2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
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)
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
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
23 /* $TOG: FileUtils.c /main/8 1998/07/28 15:37:38 mgreess $ */
24 /************************************<+>*************************************
25 ****************************************************************************
29 ** Project: File locating
31 ** Description: Locates files (volumes) accessible via the
34 ** NOTE: this file must remain free of Xt & Xm calls.
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 ************************************<+>*************************************/
54 #include <sys/param.h> /* MAXPATHLEN */
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"
70 /******** constants *********/
71 #define LANG_C_STR "C"
75 /******** types *********/
77 /******** public global variables *********/
78 const char * _DtHelpFileSuffixList[3] = {
80 DtHelpCCDF_VOL_SUFFIX,
83 /******** variables *********/
84 static const char * EmptyStr = "";
86 static const char * PeriodStr = ".";
88 static const char * DirSlashStr = "/";
89 static const char * PathSeparator = ":";
90 static const _DtHelpCeDirStruct DefCacheDir = { NULL, ENOTDIR, 0, NULL };
92 static _DtHelpCeDirStruct *CachedDirs = NULL;
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)
100 /*****************************************************************************
103 * locate the '.' of the filename extension, if present
105 *****************************************************************************/
106 static void MyFree(char * ptr)
113 /*****************************************************************************
114 * Function: GetExtension()
116 * locate the '.' of the filename extension, if present
118 *****************************************************************************/
119 static char * GetExtension(
123 if (_DtHelpCeStrrchr(filename,PeriodStr,MB_CUR_MAX,&ext) == 0 ) return ext;
124 else return ""; /* do NOT return a NULL*/
129 /*****************************************************************************
130 * Function: SpecialStrcmp()
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.
137 *****************************************************************************/
138 static int SpecialStrcmp(
144 if(NULL == str2) return 0; /* str1 == str2 */
145 return -1; /* str1 < str2 */
147 if(NULL == str2) return 1; /* str1 > str2 */
148 return(strcmp(str1,str2)); /* str1 ? str2 */
155 /************************************************************************
156 * Function: _DtHelpFileTraceLinks (pathName)
158 * Purpose: Traces pathname through all symbolic links
159 * until a real file is found or the link is
160 * found to be invalid.
162 * Returns: True if file found at end of link, False if not
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 ***********************************************************************/
171 _DtHelpFileTraceLinks (
178 char buf [2][MAXPATHLEN+2]; /* 2K+ bytes on stack */
180 if ( NULL == *pPathName ) return False; /* RETURN */
183 strcpy(buf[0],*pPathName);
184 linkPath = buf[0]; /* will be assigned to filePath below */
185 curBuf = 1; /* next valid buf */
187 /* find out if this path is a symbolic link */
188 while ( result >= 0 )
190 /* exchange buffer ptrs and toggle index */
192 linkPath = buf[curBuf % 2];
195 /* get the link info */
196 result = readlink (filePath, linkPath, MAXPATHLEN);
198 /* check for the result of the readlink call */
201 /* if newPath is not a symbolic link, errno != EINVAL */
203 return False; /* RETURN */
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 */
210 * pPathName had memory allocated before this function was called.
211 * only increase the memory if needed.
213 if ( strlen (*pPathName) < strlen(filePath) )
214 *pPathName = (char *)realloc((void *)*pPathName, (sizeof(char)
215 * (strlen(filePath) +1)));
217 strcpy(*pPathName, filePath);
219 /* printf("actual is: %s\n", filePath); ** DBG */
220 return True; /* RETURN */
223 { /* no error--handle the link */
225 /* if the path is absolute, just take it as such */
226 linkPath [result] = EOS; /* for safety */
228 /* is path relative to current directory? */
229 if ( linkPath[0] != DIR_SLASH )
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 */
239 } /* if path is relative */
240 /* printf("traced to: %s\n", linkPath); ** DBG */
242 } /* while result >= 0 */
243 return False; /* RETURN */
249 /************************************************************************
250 * Function: _DtHelpFileTraceToFile (pathName, accessMode, foundPath)
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.
259 * False: file not found or error
260 ***********************************************************************/
262 _DtHelpFileTraceToFile (
268 char * pathName = *pPathName; /* avoid indirection */
271 if ( pathName == NULL || pathName[0] == EOS )
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 */
279 /* trace any links */
280 if ( _DtHelpFileTraceLinks(pPathName) == False )
282 /* don't free pPathName here */
283 return False; /* RETURN: no file */
285 pathName = *pPathName;
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 */
294 /* point foundPath at the path */
295 *pFoundPath = pathName;
296 return True; /* RETURN: its a file */
297 } /* if a valid path */
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),
317 /******************************************************************************
318 * Function: int _DtHelpFileGetSearchPaths ()
321 * paths: caller array size _DtHELP_FILE_NUM_PATHS in which
322 * to store ptrs to the private path strings
323 * searchHomeDir: boolean flag
326 * the memory pointed to by the array is NOT owned by the
327 * caller and should not be freed or modified
329 * Purpose: make the search paths available
331 *****************************************************************************/
332 void _DtHelpFileGetSearchPaths(
334 Boolean searchHomeDir)
336 static char * pathsSet[_DtHELP_FILE_NUM_PATHS];
337 char tmpPath[MAXPATHLEN + 2];
339 /* get user's home directory; is used in _DtHELP_FILE_USER_PATH as well */
340 if (NULL == pathsSet[_DtHELP_FILE_HOME_PATH])
342 _DtHelpOSGetHomeDirName(tmpPath, sizeof(tmpPath));
343 pathsSet[_DtHELP_FILE_HOME_PATH] = strdup(tmpPath);
346 paths[_DtHELP_FILE_HOME_PATH] = pathsSet[_DtHELP_FILE_HOME_PATH];
348 paths[_DtHELP_FILE_HOME_PATH] = NULL;
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];
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];
363 /******************************************************************************
364 * Function: char * _DtHelpFileLocate ()
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)
373 * Returns: malloc'd path of the located file or NULL if none located
378 * Purpose: Scans all paths of given type looking for a matching file
379 * If file contains a valid absolute path, that is also
382 * FIX: merge _DtHelpFileLocate() and _DtHelpFileListScanPaths()
383 *****************************************************************************/
384 char * _DtHelpFileLocate (
387 const char * suffixList[],
388 Boolean searchCurDir,
398 const char empty = 0;
399 const char * sufList[2];
401 _DtSubstitutionRec bugFixSubs [NUM_BUGS];
402 char * paths[_DtHELP_FILE_NUM_PATHS];
403 char tmpPath[MAXPATHLEN + 2];
404 const char * * pSuffix;
409 if (NULL == filespec) return NULL;
411 /* init suffix list to empty if not specified */
412 if (suffixList == NULL)
416 suffixList = sufList; /* override initial argument setting */
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 / */
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 */
433 /* test all suffixes */
434 eos = pathName + strlen(pathName);
435 for ( pSuffix = suffixList; NULL != *pSuffix; pSuffix++ )
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 */
446 /*** second, check if its relative to the current directory ***/
447 /* if filespec begins with . or .. then stop after the cwd path */
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 ***/
455 /* get user's current working directory */
456 /* JET - CERT VU#575804 */
457 if (getcwd(tmpPath, MAXPATHLEN - 1) == NULL) return NULL; /* RETURN: error */
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; }
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);
471 /* cat on the relative path */
472 strcat(pathName,filespec);
474 /* compress out any relative paths */
475 _DtHelpCeCompressPathname(pathName);
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 */
482 /* test all suffixes */
483 eos = pathName + strlen(pathName);
484 for ( pSuffix = suffixList; NULL != *pSuffix; pSuffix++ )
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 */
492 return NULL; /* RETURN: error */
493 } /* filespec is a relative path or search cur dir */
495 /*** third look in search path directories ***/
497 /* get the search paths */
498 _DtHelpFileGetSearchPaths( paths, False );
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 */
506 if ( _DtHelpCeStrrchr(filespec,DirSlashStr,MB_CUR_MAX,&ptr) == 0 )
507 base = ++ptr; /* begin past the slash */
509 /* Have to support %H explicitly */
510 bugFixSubs[0].match = 'H';
511 bugFixSubs[0].substitution = base;
513 /* get the LANG value */
514 loc = _DtHelpGetLocale();
515 if (NULL == loc || EOS == loc[0]) loc = strdup(LANG_C_STR);
517 /* outer loop is once for each path */
519 for ( curPathIndex = 0;
520 curPathIndex < _DtHELP_FILE_NUM_PATHS && NULL == foundPath;
523 curPath = paths[curPathIndex];
524 if (NULL == curPath) continue; /* continue */
526 /* look for the file in that path */
527 if (NULL != curPath) do
529 /* look for next subpath separator and insert and EOS if found */
530 if (_DtHelpCeStrchr(curPath,PathSeparator,MB_CUR_MAX,&ptr)==0)
533 /* compress that path */
534 /* JET - CERT VU#575804 */
535 strncpy(tmpPath, curPath, MAXPATHLEN);
537 _DtHelpCeCompressPathname(tmpPath);
539 /* test all suffixes */
540 for ( pSuffix = suffixList, foundPath = NULL;
541 NULL == foundPath && NULL != *pSuffix;
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);
549 if ( _DtHelpFileTraceToFile(&pathName,accessMode,&foundPath)==False
553 } /* for all suffixes */
555 /* restore the subpath separator and advance past it */
556 if (ptr) *ptr++ = *PathSeparator;
559 } while (curPath && *curPath && NULL == foundPath);
560 /* do while more subpaths */
562 } /* for all paths */
568 /******************************************************************************
569 * Function: int _DtHelpCeCheckAndCacheDir (char *dir)
572 * dir Specifies the directory to test.
574 * Returns: 0 if the directory exists.
575 * ENOTDIR if the directory is invalid.
577 * Purpose: To check a directory only once and remember the result.
579 *****************************************************************************/
581 _DtHelpCeCheckAndCacheDir (char *dir)
583 int result = ENOTDIR;
584 _DtHelpCeDirStruct *curDir = CachedDirs;
585 _DtHelpCeDirStruct *prevDir = NULL;
588 _DtHelpProcessLock();
590 if (dir == NULL || *dir == '\0')
594 * search the cached directories
596 while (curDir != NULL && strcmp(curDir->dir_name, dir))
599 curDir = curDir->next_dir;
603 * was the directory found in the cache? If so, return the type.
606 result = curDir->type;
610 * new directory - malloc room for this entry.
613 curDir = (_DtHelpCeDirStruct *) malloc(sizeof(_DtHelpCeDirStruct));
617 * initialize the new entry. I.E. type starts out ENOTDIR.
619 *curDir = DefCacheDir;
620 curDir->dir_name = strdup(dir);
621 if (curDir->dir_name != NULL)
624 * put this entry in the list
627 prevDir->next_dir = curDir;
632 * is this a directory?
634 if (access(dir, R_OK) == 0 &&
635 stat(dir, &buf) == 0 && S_ISDIR(buf.st_mode))
639 * return the result of the tests.
641 result = curDir->type;
649 * This should never happen, but just in case the directory
650 * can't be cached, go ahead and check it anyway.
652 if (result == ENOMEM && access(dir, R_OK) == 0 &&
653 stat(dir, &buf) == 0 && S_ISDIR(buf.st_mode))
656 _DtHelpProcessUnlock();
661 /******************************************************************************
662 * Function: _DtHelpCeDirStruct *_DtHelpCeGetCachedDirs (void)
666 * Returns: A pointer to the cached directories.
668 * Purpose: To allow access to the cached directories.
670 *****************************************************************************/
672 _DtHelpCeGetCachedDirs (void)
676 #endif /* not_done */