FileUtils.c: fix CERT VU#575804
[oweals/cde.git] / cde / lib / DtHelp / FileUtils.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $TOG: FileUtils.c /main/8 1998/07/28 15:37:38 mgreess $ */
24 /************************************<+>*************************************
25  ****************************************************************************
26  **
27  **   File:     FileUtils.c
28  **
29  **   Project:     File locating
30  **
31  **   Description: Locates files (volumes) accessible via the 
32  **             known paths
33  **
34  **   NOTE: this file must remain free of Xt & Xm calls.
35  **
36  **  (c) Copyright 1993, 1994 Hewlett-Packard Company
37  **  (c) Copyright 1993, 1994 International Business Machines Corp.
38  **  (c) Copyright 1993, 1994 Sun Microsystems, Inc.
39  **  (c) Copyright 1993, 1994 Novell, Inc.
40  ****************************************************************************
41  ************************************<+>*************************************/
42
43 /*
44  * system includes
45  */
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <stdlib.h>
49 #include <stdio.h>
50 #include <string.h>
51 #include <unistd.h>
52 #include <dirent.h>
53 #include <sys/stat.h>
54 #include <sys/param.h> /* MAXPATHLEN */
55
56 #include <Dt/Help.h>
57
58 /*
59  * private includes
60  */
61 #include "bufioI.h"  /* for AccessI.h */
62 #include "Access.h"     /* CompressPathname */
63 #include "AccessI.h"    /* ExpandPathname */
64 #include "HelpP.h"      /* for DtDEFAULT_xxx */
65 #include "HelposI.h"    /* for search path access */
66 #include "StringFuncsI.h"
67 #include "FileUtilsI.h"
68 #include "Lock.h"
69
70 /******** constants *********/
71 #define LANG_C_STR     "C"
72 #define DIR_SLASH      '/'
73 #define EOS            '\0'
74
75 /******** types *********/
76
77 /******** public global variables *********/
78 const char * _DtHelpFileSuffixList[3] = {
79                     DtHelpSDL_VOL_SUFFIX,
80                     DtHelpCCDF_VOL_SUFFIX,
81                     NULL };
82
83 /******** variables *********/
84 static const char * EmptyStr = "";
85 #ifdef  not_used
86 static const char * PeriodStr = ".";
87 #endif
88 static const char * DirSlashStr = "/";
89 static const char * PathSeparator = ":";
90 static const _DtHelpCeDirStruct DefCacheDir = { NULL, ENOTDIR, 0, NULL };
91
92 static  _DtHelpCeDirStruct *CachedDirs = NULL;
93
94 /******** functions *********/
95 #define MyNewString(s) (NULL != s ? strdup((char *)s) : NULL)
96 #define MyRealloc(p,s) (NULL != (p) ? realloc((char *)p,s) : malloc(s) )
97 #define MyMalloc(s)    malloc(s)
98
99 \f
100 /*****************************************************************************
101  * Function:   MyFree()
102  *
103  *  locate the '.' of the filename extension, if present
104  *
105  *****************************************************************************/
106 static void MyFree(char * ptr)
107 {
108    if(ptr) free(ptr);
109 }
110
111 \f
112 #ifdef  not_used
113 /*****************************************************************************
114  * Function:   GetExtension()
115  *
116  *  locate the '.' of the filename extension, if present
117  *
118  *****************************************************************************/
119 static char * GetExtension(
120             char *         filename)
121 {
122     char * ext;
123     if (_DtHelpCeStrrchr(filename,PeriodStr,MB_CUR_MAX,&ext) == 0 ) return ext;
124     else return "";          /* do NOT return a NULL*/
125 }
126
127
128
129 /*****************************************************************************
130  * Function:   SpecialStrcmp()
131  *
132  * Tests the args for NULL pointers. If both are NULL or if
133  * both aren't NULL and are the same string, then returns 0.
134  * If one arg is NULL and other isn't, or if strings are
135  * different, returns -1 or +1.
136  *
137  *****************************************************************************/
138 static int SpecialStrcmp(
139     const char *    str1,
140     const char *    str2)
141 {
142            if(NULL == str1)
143            {
144               if(NULL == str2) return 0;   /* str1 == str2 */
145               return -1;                   /* str1 < str2 */
146            }
147            if(NULL == str2) return 1;      /* str1 > str2 */
148            return(strcmp(str1,str2));      /* str1 ? str2 */
149 }
150 #endif
151
152
153
154 \f
155 /************************************************************************
156  * Function: _DtHelpFileTraceLinks (pathName)
157  *
158  * Purpose:  Traces pathname through all symbolic links
159  *           until a real file is found or the link is
160  *           found to be invalid.
161  *
162  * Returns:  True if file found at end of link, False if not
163  *
164  * Memory:  pathName must point to a malloc'd string.  The string
165  *          may be freed by the function and pathName assigned a pointer
166  *          to a different string specifying a different path to the same file.
167  *          If a file is found, foundPath is set to the same pointer
168  *          as pathName, otherwise it is set to NULL.
169  ***********************************************************************/
170 Boolean 
171 _DtHelpFileTraceLinks (
172              char * *  pPathName)
173 {
174    int    result = 0;
175    char   curBuf;
176    char * linkPath;
177    char * filePath;
178    char   buf [2][MAXPATHLEN+2];      /* 2K+ bytes on stack */
179
180    if ( NULL == *pPathName ) return False;         /* RETURN */
181
182    /* init */
183    strcpy(buf[0],*pPathName);
184    linkPath = buf[0];        /* will be assigned to filePath below */
185    curBuf = 1;               /* next valid buf */
186
187    /* find out if this path is a symbolic link */
188    while ( result >= 0 )
189    {
190       /* exchange buffer ptrs and toggle index */
191       filePath = linkPath;
192       linkPath = buf[curBuf % 2];
193       curBuf++;
194
195       /* get the link info */
196       result = readlink (filePath, linkPath, MAXPATHLEN);
197
198      /* check for the result of the readlink call */
199      if (result == -1)
200      {
201         /* if newPath is not a symbolic link, errno != EINVAL */
202         if (errno != EINVAL)
203            return False;                    /* RETURN */
204
205         /* filePath is not a sym link ==> a real file or directory */
206         /* so return filePath in caller-owned memory */
207         if ( curBuf != 1 )  /* curBuf == 1 when pPathName is a file */
208         {
209            /*
210             * pPathName had memory allocated before this function was called.
211             * only increase the memory if needed.
212             */
213            if ( strlen (*pPathName) < strlen(filePath) )
214                 *pPathName = (char *)realloc((void *)*pPathName, (sizeof(char)
215                                 * (strlen(filePath) +1)));
216
217            strcpy(*pPathName, filePath);
218         }
219         /* printf("actual is: %s\n", filePath);  ** DBG */
220         return True;                        /* RETURN */
221      }  /* if an error */
222      else
223      {  /* no error--handle the link */
224
225         /* if the path is absolute, just take it as such */
226         linkPath [result] = EOS;       /* for safety */
227
228         /* is path relative to current directory? */
229         if ( linkPath[0] != DIR_SLASH )
230         {
231            char * slash = NULL;
232
233            /* get last slash in the current file path */
234            if(_DtHelpCeStrrchr(filePath,DirSlashStr,MB_CUR_MAX,&slash) == 0)
235            { /* there is a path comonent in filePath; use it with linkPath */
236               strcpy(++slash,linkPath);
237               strcpy(linkPath,filePath);       /* leave result in linkPath */
238            }
239         } /* if path is relative */
240         /* printf("traced to: %s\n", linkPath);  ** DBG */
241      }  /* if no error */
242    }  /* while result >= 0 */
243    return False;                             /* RETURN */
244 }
245
246
247
248 \f
249 /************************************************************************
250  * Function: _DtHelpFileTraceToFile (pathName, accessMode, foundPath)
251  *
252  * Memory:  pPathName must point to a malloc'd string.  The string
253  *          may be freed by the function and pathName assigned a pointer
254  *          to a different string specifying a different path to the same file.
255  *          If a file is found, foundPath is set to the same pointer
256  *          as pathName, otherwise it is set to NULL.
257  * Returns:
258  *   True:  file found
259  *   False: file not found or error
260  ***********************************************************************/
261 Boolean 
262 _DtHelpFileTraceToFile (
263              char * *  pPathName,
264              int       accessMode,
265              char * *  pFoundPath)
266 {
267    struct stat status;
268    char * pathName = *pPathName;  /* avoid indirection */
269
270    *pFoundPath = NULL;
271    if ( pathName == NULL || pathName[0] == EOS )
272       return False;
273  
274    /* if it's a file, trace its links */
275    if (   access (pathName, accessMode) == 0 
276        && stat (pathName, &status) == 0
277        && S_ISREG(status.st_mode) )    /* a file */
278    {
279       /* trace any links */
280       if ( _DtHelpFileTraceLinks(pPathName) == False ) 
281       {
282           /* don't free pPathName here */
283           return False;                           /* RETURN: no file */
284       }
285       pathName = *pPathName;
286       
287       /* find out if its an accessible file */
288       if (   pathName != NULL 
289           && pathName[0] != EOS
290           && access (pathName, accessMode) == 0 
291           && stat (pathName, &status) == 0
292                    && S_ISREG(status.st_mode))    /* a file */
293       {
294          /* point foundPath at the path */
295          *pFoundPath = pathName;     
296          return True;               /* RETURN:  its a file */
297       }  /* if a valid path */
298    } /* if a path */
299
300 #if 0
301    printf("Unknown file: %s\n", pathName);
302    printf("Access: %d, stat: %d, IS_REG: %d, mode: %x\n",
303              access (pathName, accessMode),
304              stat (pathName, &status),
305              S_ISREG(status.st_mode),
306              status.st_mode);
307 #endif
308
309    /* its not a file */
310    *pFoundPath = NULL;
311    return False;
312 }
313
314
315
316 \f
317 /******************************************************************************
318  * Function: int _DtHelpFileGetSearchPaths ()
319  *
320  * Parameters:
321  *        paths:          caller array size _DtHELP_FILE_NUM_PATHS in which
322  *                        to store ptrs to the private path strings
323  *        searchHomeDir:  boolean flag
324  *
325  * Memory:
326  *        the memory pointed to by the array is NOT owned by the
327  *        caller and should not be freed or modified
328  *
329  * Purpose:     make the search paths available
330  * 
331  *****************************************************************************/
332 void _DtHelpFileGetSearchPaths(
333          char *  paths[],
334          Boolean searchHomeDir)
335 {
336 static char * pathsSet[_DtHELP_FILE_NUM_PATHS];
337     char tmpPath[MAXPATHLEN + 2];
338
339     /* get user's home directory; is used in _DtHELP_FILE_USER_PATH as well */
340     if (NULL == pathsSet[_DtHELP_FILE_HOME_PATH])
341     {
342        _DtHelpOSGetHomeDirName(tmpPath, sizeof(tmpPath));
343        pathsSet[_DtHELP_FILE_HOME_PATH] = strdup(tmpPath);
344     }
345     if (searchHomeDir) 
346        paths[_DtHELP_FILE_HOME_PATH] = pathsSet[_DtHELP_FILE_HOME_PATH];
347     else 
348        paths[_DtHELP_FILE_HOME_PATH] = NULL;
349
350     /* generate the user path */
351     if (NULL == pathsSet[_DtHELP_FILE_USER_PATH])
352        pathsSet[_DtHELP_FILE_USER_PATH] = _DtHelpGetUserSearchPath();
353     paths[_DtHELP_FILE_USER_PATH] = pathsSet[_DtHELP_FILE_USER_PATH];
354
355     /* get the system search path */
356     if (NULL == pathsSet[_DtHELP_FILE_SYS_PATH])
357        pathsSet[_DtHELP_FILE_SYS_PATH] = _DtHelpGetSystemSearchPath();
358     paths[_DtHELP_FILE_SYS_PATH] = pathsSet[_DtHELP_FILE_SYS_PATH];
359 }
360
361
362 \f
363 /******************************************************************************
364  * Function: char * _DtHelpFileLocate ()
365  *
366  * Parameters:
367  *        type:           subdirectories to search (%T)
368  *        base:           basename of the file
369  *        suffix:         extension of the file to find (%S)
370  *        searchCurDir:   boolean flag
371  *        accessMode:     constant value from access(2)
372  *
373  * Returns:     malloc'd path of the located file or NULL if none located
374  *
375  * errno Values:
376  *              EINVAL
377  *
378  * Purpose:     Scans all paths of given type looking for a matching file
379  *              If file contains a valid absolute path, that is also
380  *              acceptible.
381  * 
382  * FIX: merge _DtHelpFileLocate() and _DtHelpFileListScanPaths()
383  *****************************************************************************/
384 char * _DtHelpFileLocate (
385         char *        type,
386         char *        filespec,
387         const char *  suffixList[],
388         Boolean       searchCurDir,
389         int           accessMode)
390 {
391     char * loc;
392     char * ptr;
393     char * pathName;
394     char * curPath;
395     char * base;
396     int    curPathIndex;
397     char * foundPath;
398     const char empty = 0;
399     const char * sufList[2];
400 #define NUM_BUGS 1
401     _DtSubstitutionRec bugFixSubs [NUM_BUGS];
402     char * paths[_DtHELP_FILE_NUM_PATHS];
403     char tmpPath[MAXPATHLEN + 2];
404     const char * * pSuffix;
405     char * eos = NULL;
406     char * slash = NULL;
407
408     /* test args */
409     if (NULL == filespec) return NULL;
410
411     /* init suffix list to empty if not specified */
412     if (suffixList == NULL) 
413     { 
414        sufList[0] = &empty; 
415        sufList[1] = NULL;
416        suffixList = sufList; /* override initial argument setting */
417     }
418
419     /*** first look for file as specified ***/
420     /* if filespec begins with . or .. then stop after the cwd path */
421     if (   (   MB_CUR_MAX == 1 
422             || mblen(filespec, MB_CUR_MAX) == 1)        /* 1st char is 1 byte */
423          && *filespec == '/')                   /* and its a / */
424     {
425        /* _DtHelpFileTraceToFile() needs a malloc'd string */
426        /* 10: leaves room for add'l suffixes */
427        pathName = MyMalloc(sizeof(char) * (strlen(filespec)+10)); 
428        pathName = strcpy(pathName,filespec);
429        _DtHelpCeCompressPathname(pathName); /* compress out relative paths */
430        if ( _DtHelpFileTraceToFile(&pathName,accessMode,&foundPath) )
431           return foundPath;                         /* RETURN */
432
433        /* test all suffixes */
434        eos = pathName + strlen(pathName);
435        for ( pSuffix = suffixList; NULL != *pSuffix; pSuffix++ )
436        {
437           strcpy(eos,(char *) *pSuffix);
438           /*recall: _DtHelpFileTraceToFile() requires pathName to be malloc'd*/
439           if ( _DtHelpFileTraceToFile(&pathName,accessMode,&foundPath) )
440              return foundPath;                           /* RETURN: found */
441        } /* for all suffixes */
442
443        MyFree(pathName);
444     }
445
446     /*** second, check if its relative to the current directory ***/
447     /* if filespec begins with . or .. then stop after the cwd path */
448     if (    searchCurDir
449          || (      MB_CUR_MAX == 1 
450                 || mblen(filespec, MB_CUR_MAX) == 1)  /* 1st char is 1 byte */
451              && *filespec == '.')                     /* and its a . */
452     {     /* we're looking at a cwd-relative path; ignore others */
453        /*** this is monstrously inefficient--but it shouldn't get called often ***/
454
455        /* get user's current working directory */
456        /* JET - CERT VU#575804 */
457        if (getcwd(tmpPath, MAXPATHLEN - 1) == NULL) return NULL; /* RETURN: error */
458        
459        /* make path end in a slash */
460        eos = tmpPath + strlen(tmpPath);
461        _DtHelpCeStrrchr(tmpPath,DirSlashStr,MB_CUR_MAX,&slash);
462        if ( slash != (eos - 1) ) { *eos++ = DIR_SLASH; *eos = EOS; }
463
464        /* make a malloc'd copy of the path with room to grow */
465        slash = filespec + strlen(filespec);
466        pathName = malloc(sizeof(char) * 
467                     ((eos-tmpPath) + (slash-filespec) + 50) ); /* 50: arbitrary */
468        if (NULL == pathName) return NULL;               /* RETURN: error */
469        strcpy(pathName,tmpPath);
470
471        /* cat on the relative path */
472        strcat(pathName,filespec);
473
474        /* compress out any relative paths */
475        _DtHelpCeCompressPathname(pathName);
476
477        /* see if we find the file now */
478        /* recall: _DtHelpFileTraceToFile() requires pathName to be malloc'd */
479        if ( _DtHelpFileTraceToFile(&pathName,accessMode,&foundPath) )
480           return foundPath;                             /* RETURN: found */
481
482        /* test all suffixes */
483        eos = pathName + strlen(pathName);
484        for ( pSuffix = suffixList; NULL != *pSuffix; pSuffix++ )
485        {
486          strcpy(eos,(char *) *pSuffix);
487          /* recall: _DtHelpFileTraceToFile() requires pathName to be malloc'd */
488          if ( _DtHelpFileTraceToFile(&pathName,accessMode,&foundPath) )
489             return foundPath;                           /* RETURN: found */
490        } /* for all suffixes */
491        MyFree(pathName);
492        return NULL;                                     /* RETURN: error */
493     }  /* filespec is a relative path or search cur dir */
494
495     /*** third look in search path directories ***/
496
497     /* get the search paths */
498     _DtHelpFileGetSearchPaths( paths, False );
499
500     /*** prep variables to pass through the path search loop ***/
501     /* we're not looking at a cwd-relative path and
502        we know that 'filespec' isn't a valid path to a volume
503        (from _DtHelpFileTraceToFile), so just pick off the 
504        basename of the spec */
505     base = filespec;
506     if ( _DtHelpCeStrrchr(filespec,DirSlashStr,MB_CUR_MAX,&ptr) == 0 )
507         base = ++ptr;      /* begin past the slash */
508
509     /* Have to support %H explicitly */
510     bugFixSubs[0].match = 'H';
511     bugFixSubs[0].substitution = base;
512
513     /* get the LANG value */
514     loc = _DtHelpGetLocale();
515     if (NULL == loc || EOS == loc[0]) loc = strdup(LANG_C_STR);
516
517     /* outer loop is once for each path */
518     foundPath = NULL;
519     for ( curPathIndex = 0;
520           curPathIndex < _DtHELP_FILE_NUM_PATHS && NULL == foundPath; 
521           curPathIndex++ )
522     {
523        curPath = paths[curPathIndex];
524        if (NULL == curPath) continue;                   /* continue */
525     
526        /* look for the file in that path */
527        if (NULL != curPath) do
528        {
529           /* look for next subpath separator and insert and EOS if found */
530           if (_DtHelpCeStrchr(curPath,PathSeparator,MB_CUR_MAX,&ptr)==0)
531               *ptr = EOS;
532
533           /* compress that path */
534           /* JET - CERT VU#575804 */
535           strncpy(tmpPath, curPath, MAXPATHLEN);
536
537           _DtHelpCeCompressPathname(tmpPath);
538
539           /* test all suffixes */
540           for ( pSuffix = suffixList, foundPath = NULL;
541                 NULL == foundPath && NULL != *pSuffix; 
542                 pSuffix++ )
543           {
544              /* generate the (directory) path using all the variables and fix 
545                 it up to remove the unwanted stuff involving the filename */
546              pathName = _DtHelpCeExpandPathname (curPath, base, type, 
547                             (char *) *pSuffix, loc, bugFixSubs, NUM_BUGS);
548   
549              if (   _DtHelpFileTraceToFile(&pathName,accessMode,&foundPath)==False
550                  && NULL != pathName)
551                 free(pathName);
552
553           } /* for all suffixes */
554  
555           /* restore the subpath separator and advance past it */
556           if (ptr) *ptr++ = *PathSeparator;
557
558           curPath = ptr;
559        } while (curPath && *curPath && NULL == foundPath); 
560         /* do while more subpaths */
561
562    }  /* for all paths */
563
564    MyFree(loc);
565    return foundPath;
566 }
567
568 /******************************************************************************
569  * Function: int _DtHelpCeCheckAndCacheDir (char *dir)
570  *
571  * Parameters:
572  *        dir   Specifies the directory to test.
573  *
574  * Returns:     0       if the directory exists.
575  *              ENOTDIR if the directory is invalid.
576  *
577  * Purpose:     To check a directory only once and remember the result.
578  * 
579  *****************************************************************************/
580 int
581 _DtHelpCeCheckAndCacheDir (char *dir)
582 {
583     int                  result  = ENOTDIR;
584     _DtHelpCeDirStruct  *curDir  = CachedDirs;
585     _DtHelpCeDirStruct  *prevDir = NULL;
586     struct stat          buf;
587
588     _DtHelpProcessLock();
589
590     if (dir == NULL || *dir == '\0')
591         return ENOTDIR;
592
593     /*
594      * search the cached directories
595      */
596     while (curDir != NULL && strcmp(curDir->dir_name, dir))
597       {
598         prevDir = curDir;
599         curDir  = curDir->next_dir;
600       }
601
602     /*
603      * was the directory found in the cache? If so, return the type.
604      */
605     if (curDir != NULL)
606         result = curDir->type;
607     else
608       {
609         /*
610          * new directory - malloc room for this entry.
611          */
612         result = ENOMEM;
613         curDir = (_DtHelpCeDirStruct *) malloc(sizeof(_DtHelpCeDirStruct));
614         if (curDir != NULL)
615           {
616             /*
617              * initialize the new entry. I.E. type starts out ENOTDIR.
618              */
619             *curDir = DefCacheDir;
620             curDir->dir_name = strdup(dir);
621             if (curDir->dir_name != NULL)
622               {
623                 /*
624                  * put this entry in the list
625                  */
626                 if (prevDir != NULL)
627                     prevDir->next_dir = curDir;
628                 else
629                     CachedDirs = curDir;
630
631                 /*
632                  * is this a directory?
633                  */
634                 if (access(dir, R_OK) == 0 && 
635                                 stat(dir, &buf) == 0 && S_ISDIR(buf.st_mode))
636                     curDir->type = 0;
637
638                 /*
639                  * return the result of the tests.
640                  */
641                 result = curDir->type;
642               }
643             else
644                 free(curDir);
645           }
646       }
647
648     /*
649      * This should never happen, but just in case the directory
650      * can't be cached, go ahead and check it anyway.
651      */
652     if (result == ENOMEM && access(dir, R_OK) == 0 && 
653                                 stat(dir, &buf) == 0 && S_ISDIR(buf.st_mode))
654         result = 0;
655
656     _DtHelpProcessUnlock();
657     return result;
658 }
659
660 #ifdef  not_done
661 /******************************************************************************
662  * Function: _DtHelpCeDirStruct *_DtHelpCeGetCachedDirs (void)
663  *
664  * Parameters:  none.
665  *
666  * Returns:     A pointer to the cached directories.
667  *
668  * Purpose:     To allow access to the cached directories.
669  * 
670  *****************************************************************************/
671 _DtHelpCeDirStruct *
672 _DtHelpCeGetCachedDirs (void)
673 {
674     return CachedDirs;
675 }
676 #endif /* not_done */