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