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 libraries 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 *********/
85 static const char * PeriodStr = ".";
87 static const char * DirSlashStr = "/";
88 static const char * PathSeparator = ":";
89 static const _DtHelpCeDirStruct DefCacheDir = { NULL, ENOTDIR, 0, NULL };
91 static _DtHelpCeDirStruct *CachedDirs = NULL;
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)
99 /*****************************************************************************
102 * locate the '.' of the filename extension, if present
104 *****************************************************************************/
105 static void MyFree(char * ptr)
112 /*****************************************************************************
113 * Function: GetExtension()
115 * locate the '.' of the filename extension, if present
117 *****************************************************************************/
118 static char * GetExtension(
122 if (_DtHelpCeStrrchr(filename,PeriodStr,MB_CUR_MAX,&ext) == 0 ) return ext;
123 else return ""; /* do NOT return a NULL*/
128 /*****************************************************************************
129 * Function: SpecialStrcmp()
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.
136 *****************************************************************************/
137 static int SpecialStrcmp(
143 if(NULL == str2) return 0; /* str1 == str2 */
144 return -1; /* str1 < str2 */
146 if(NULL == str2) return 1; /* str1 > str2 */
147 return(strcmp(str1,str2)); /* str1 ? str2 */
154 /************************************************************************
155 * Function: _DtHelpFileTraceLinks (pathName)
157 * Purpose: Traces pathname through all symbolic links
158 * until a real file is found or the link is
159 * found to be invalid.
161 * Returns: True if file found at end of link, False if not
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 ***********************************************************************/
170 _DtHelpFileTraceLinks (
177 char buf [2][MAXPATHLEN+2]; /* 2K+ bytes on stack */
179 if ( NULL == *pPathName ) return False; /* RETURN */
182 snprintf(buf[0], sizeof(buf[0]), "%s", *pPathName);
183 linkPath = buf[0]; /* will be assigned to filePath below */
184 curBuf = 1; /* next valid buf */
186 /* find out if this path is a symbolic link */
187 while ( result >= 0 )
189 /* exchange buffer ptrs and toggle index */
191 linkPath = buf[curBuf % 2];
194 /* get the link info */
195 result = readlink (filePath, linkPath, MAXPATHLEN);
197 /* check for the result of the readlink call */
200 /* if newPath is not a symbolic link, errno != EINVAL */
202 return False; /* RETURN */
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 */
209 * pPathName had memory allocated before this function was called.
210 * only increase the memory if needed.
212 if ( strlen (*pPathName) < strlen(filePath) )
213 *pPathName = (char *)realloc((void *)*pPathName, (sizeof(char)
214 * (strlen(filePath) +1)));
216 strcpy(*pPathName, filePath);
218 /* printf("actual is: %s\n", filePath); ** DBG */
219 return True; /* RETURN */
222 { /* no error--handle the link */
224 /* if the path is absolute, just take it as such */
225 linkPath [result] = EOS; /* for safety */
227 /* is path relative to current directory? */
228 if ( linkPath[0] != DIR_SLASH )
232 /* get last slash in the current file path */
233 if(_DtHelpCeStrrchr(filePath,DirSlashStr,MB_CUR_MAX,&slash) == 0)
234 { /* there is a path component in filePath; use it with linkPath */
235 strcpy(++slash,linkPath);
236 strcpy(linkPath,filePath); /* leave result in linkPath */
238 } /* if path is relative */
239 /* printf("traced to: %s\n", linkPath); ** DBG */
241 } /* while result >= 0 */
242 return False; /* RETURN */
248 /************************************************************************
249 * Function: _DtHelpFileTraceToFile (pathName, accessMode, foundPath)
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.
258 * False: file not found or error
259 ***********************************************************************/
261 _DtHelpFileTraceToFile (
267 char * pathName = *pPathName; /* avoid indirection */
270 if ( pathName == NULL || pathName[0] == EOS )
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 */
278 /* trace any links */
279 if ( _DtHelpFileTraceLinks(pPathName) == False )
281 /* don't free pPathName here */
282 return False; /* RETURN: no file */
284 pathName = *pPathName;
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 */
293 /* point foundPath at the path */
294 *pFoundPath = pathName;
295 return True; /* RETURN: its a file */
296 } /* if a valid path */
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),
316 /******************************************************************************
317 * Function: int _DtHelpFileGetSearchPaths ()
320 * paths: caller array size _DtHELP_FILE_NUM_PATHS in which
321 * to store ptrs to the private path strings
322 * searchHomeDir: boolean flag
325 * the memory pointed to by the array is NOT owned by the
326 * caller and should not be freed or modified
328 * Purpose: make the search paths available
330 *****************************************************************************/
331 void _DtHelpFileGetSearchPaths(
333 Boolean searchHomeDir)
335 static char * pathsSet[_DtHELP_FILE_NUM_PATHS];
336 char tmpPath[MAXPATHLEN + 2];
338 /* get user's home directory; is used in _DtHELP_FILE_USER_PATH as well */
339 if (NULL == pathsSet[_DtHELP_FILE_HOME_PATH])
341 _DtHelpOSGetHomeDirName(tmpPath, sizeof(tmpPath));
342 pathsSet[_DtHELP_FILE_HOME_PATH] = strdup(tmpPath);
345 paths[_DtHELP_FILE_HOME_PATH] = pathsSet[_DtHELP_FILE_HOME_PATH];
347 paths[_DtHELP_FILE_HOME_PATH] = NULL;
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];
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];
362 /******************************************************************************
363 * Function: char * _DtHelpFileLocate ()
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)
372 * Returns: malloc'd path of the located file or NULL if none located
377 * Purpose: Scans all paths of given type looking for a matching file
378 * If file contains a valid absolute path, that is also
381 * FIX: merge _DtHelpFileLocate() and _DtHelpFileListScanPaths()
382 *****************************************************************************/
383 char * _DtHelpFileLocate (
386 const char * suffixList[],
387 Boolean searchCurDir,
397 const char empty = 0;
398 const char * sufList[2];
400 _DtSubstitutionRec bugFixSubs [NUM_BUGS];
401 char * paths[_DtHELP_FILE_NUM_PATHS];
402 char tmpPath[MAXPATHLEN + 2];
403 const char * * pSuffix;
408 if (NULL == filespec) return NULL;
410 /* init suffix list to empty if not specified */
411 if (suffixList == NULL)
415 suffixList = sufList; /* override initial argument setting */
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 / */
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 */
432 /* test all suffixes */
433 eos = pathName + strlen(pathName);
434 for ( pSuffix = suffixList; NULL != *pSuffix; pSuffix++ )
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 */
445 /*** second, check if its relative to the current directory ***/
446 /* if filespec begins with . or .. then stop after the cwd path */
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 ***/
454 /* get user's current working directory */
455 /* JET - CERT VU#575804 */
456 if (getcwd(tmpPath, MAXPATHLEN - 1) == NULL) return NULL; /* RETURN: error */
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; }
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);
470 /* cat on the relative path */
471 strcat(pathName,filespec);
473 /* compress out any relative paths */
474 _DtHelpCeCompressPathname(pathName);
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 */
481 /* test all suffixes */
482 eos = pathName + strlen(pathName);
483 for ( pSuffix = suffixList; NULL != *pSuffix; pSuffix++ )
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 */
491 return NULL; /* RETURN: error */
492 } /* filespec is a relative path or search cur dir */
494 /*** third look in search path directories ***/
496 /* get the search paths */
497 _DtHelpFileGetSearchPaths( paths, False );
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 */
505 if ( _DtHelpCeStrrchr(filespec,DirSlashStr,MB_CUR_MAX,&ptr) == 0 )
506 base = ++ptr; /* begin past the slash */
508 /* Have to support %H explicitly */
509 bugFixSubs[0].match = 'H';
510 bugFixSubs[0].substitution = base;
512 /* get the LANG value */
513 loc = _DtHelpGetLocale();
514 if (NULL == loc || EOS == loc[0]) loc = strdup(LANG_C_STR);
516 /* outer loop is once for each path */
518 for ( curPathIndex = 0;
519 curPathIndex < _DtHELP_FILE_NUM_PATHS && NULL == foundPath;
522 curPath = paths[curPathIndex];
523 if (NULL == curPath) continue; /* continue */
525 /* look for the file in that path */
526 if (NULL != curPath) do
528 /* look for next subpath separator and insert and EOS if found */
529 if (_DtHelpCeStrchr(curPath,PathSeparator,MB_CUR_MAX,&ptr)==0)
532 /* compress that path */
533 /* JET - CERT VU#575804 */
534 strncpy(tmpPath, curPath, MAXPATHLEN);
536 _DtHelpCeCompressPathname(tmpPath);
538 /* test all suffixes */
539 for ( pSuffix = suffixList, foundPath = NULL;
540 NULL == foundPath && NULL != *pSuffix;
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);
548 if ( _DtHelpFileTraceToFile(&pathName,accessMode,&foundPath)==False
552 } /* for all suffixes */
554 /* restore the subpath separator and advance past it */
555 if (ptr) *ptr++ = *PathSeparator;
558 } while (curPath && *curPath && NULL == foundPath);
559 /* do while more subpaths */
561 } /* for all paths */
567 /******************************************************************************
568 * Function: int _DtHelpCeCheckAndCacheDir (char *dir)
571 * dir Specifies the directory to test.
573 * Returns: 0 if the directory exists.
574 * ENOTDIR if the directory is invalid.
576 * Purpose: To check a directory only once and remember the result.
578 *****************************************************************************/
580 _DtHelpCeCheckAndCacheDir (char *dir)
582 int result = ENOTDIR;
583 _DtHelpCeDirStruct *curDir = CachedDirs;
584 _DtHelpCeDirStruct *prevDir = NULL;
587 _DtHelpProcessLock();
589 if (dir == NULL || *dir == '\0')
593 * search the cached directories
595 while (curDir != NULL && strcmp(curDir->dir_name, dir))
598 curDir = curDir->next_dir;
602 * was the directory found in the cache? If so, return the type.
605 result = curDir->type;
609 * new directory - malloc room for this entry.
612 curDir = (_DtHelpCeDirStruct *) malloc(sizeof(_DtHelpCeDirStruct));
616 * initialize the new entry. I.E. type starts out ENOTDIR.
618 *curDir = DefCacheDir;
619 curDir->dir_name = strdup(dir);
620 if (curDir->dir_name != NULL)
623 * put this entry in the list
626 prevDir->next_dir = curDir;
631 * is this a directory?
633 if (access(dir, R_OK) == 0 &&
634 stat(dir, &buf) == 0 && S_ISDIR(buf.st_mode))
638 * return the result of the tests.
640 result = curDir->type;
648 * This should never happen, but just in case the directory
649 * can't be cached, go ahead and check it anyway.
651 if (result == ENOMEM && access(dir, R_OK) == 0 &&
652 stat(dir, &buf) == 0 && S_ISDIR(buf.st_mode))
655 _DtHelpProcessUnlock();
660 /******************************************************************************
661 * Function: _DtHelpCeDirStruct *_DtHelpCeGetCachedDirs (void)
665 * Returns: A pointer to the cached directories.
667 * Purpose: To allow access to the cached directories.
669 *****************************************************************************/
671 _DtHelpCeGetCachedDirs (void)
675 #endif /* not_done */