/* * CDE - Common Desktop Environment * * Copyright (c) 1993-2012, The Open Group. All rights reserved. * * These libraries and programs are free software; you can * redistribute them and/or modify them under the terms of the GNU * Lesser General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * * These libraries and programs are distributed in the hope that * they will be useful, but WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public * License along with these libraries and programs; if not, write * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth * Floor, Boston, MA 02110-1301 USA */ /* $TOG: FileUtils.c /main/8 1998/07/28 15:37:38 mgreess $ */ /************************************<+>************************************* **************************************************************************** ** ** File: FileUtils.c ** ** Project: File locating ** ** Description: Locates files (volumes) accessible via the ** known paths ** ** NOTE: this file must remain free of Xt & Xm calls. ** ** (c) Copyright 1993, 1994 Hewlett-Packard Company ** (c) Copyright 1993, 1994 International Business Machines Corp. ** (c) Copyright 1993, 1994 Sun Microsystems, Inc. ** (c) Copyright 1993, 1994 Novell, Inc. **************************************************************************** ************************************<+>*************************************/ /* * system includes */ #include #include #include #include #include #include #include #include #include /* MAXPATHLEN */ #include
/* * private includes */ #include "bufioI.h" /* for AccessI.h */ #include "Access.h" /* CompressPathname */ #include "AccessI.h" /* ExpandPathname */ #include "HelpP.h" /* for DtDEFAULT_xxx */ #include "HelposI.h" /* for search path access */ #include "StringFuncsI.h" #include "FileUtilsI.h" #include "Lock.h" /******** constants *********/ #define LANG_C_STR "C" #define DIR_SLASH '/' #define EOS '\0' /******** types *********/ /******** public global variables *********/ const char * _DtHelpFileSuffixList[3] = { DtHelpSDL_VOL_SUFFIX, DtHelpCCDF_VOL_SUFFIX, NULL }; /******** variables *********/ #ifdef not_used static const char * PeriodStr = "."; #endif static const char * DirSlashStr = "/"; static const char * PathSeparator = ":"; static const _DtHelpCeDirStruct DefCacheDir = { NULL, ENOTDIR, 0, NULL }; static _DtHelpCeDirStruct *CachedDirs = NULL; /******** functions *********/ #define MyNewString(s) (NULL != s ? strdup((char *)s) : NULL) #define MyRealloc(p,s) (NULL != (p) ? realloc((char *)p,s) : malloc(s) ) #define MyMalloc(s) malloc(s) /***************************************************************************** * Function: MyFree() * * locate the '.' of the filename extension, if present * *****************************************************************************/ static void MyFree(char * ptr) { if(ptr) free(ptr); } #ifdef not_used /***************************************************************************** * Function: GetExtension() * * locate the '.' of the filename extension, if present * *****************************************************************************/ static char * GetExtension( char * filename) { char * ext; if (_DtHelpCeStrrchr(filename,PeriodStr,MB_CUR_MAX,&ext) == 0 ) return ext; else return ""; /* do NOT return a NULL*/ } /***************************************************************************** * Function: SpecialStrcmp() * * Tests the args for NULL pointers. If both are NULL or if * both aren't NULL and are the same string, then returns 0. * If one arg is NULL and other isn't, or if strings are * different, returns -1 or +1. * *****************************************************************************/ static int SpecialStrcmp( const char * str1, const char * str2) { if(NULL == str1) { if(NULL == str2) return 0; /* str1 == str2 */ return -1; /* str1 < str2 */ } if(NULL == str2) return 1; /* str1 > str2 */ return(strcmp(str1,str2)); /* str1 ? str2 */ } #endif /************************************************************************ * Function: _DtHelpFileTraceLinks (pathName) * * Purpose: Traces pathname through all symbolic links * until a real file is found or the link is * found to be invalid. * * Returns: True if file found at end of link, False if not * * Memory: pathName must point to a malloc'd string. The string * may be freed by the function and pathName assigned a pointer * to a different string specifying a different path to the same file. * If a file is found, foundPath is set to the same pointer * as pathName, otherwise it is set to NULL. ***********************************************************************/ Boolean _DtHelpFileTraceLinks ( char * * pPathName) { int result = 0; char curBuf; char * linkPath; char * filePath; char buf [2][MAXPATHLEN+2]; /* 2K+ bytes on stack */ if ( NULL == *pPathName ) return False; /* RETURN */ /* init */ snprintf(buf[0], sizeof(buf[0]), "%s", *pPathName); linkPath = buf[0]; /* will be assigned to filePath below */ curBuf = 1; /* next valid buf */ /* find out if this path is a symbolic link */ while ( result >= 0 ) { /* exchange buffer ptrs and toggle index */ filePath = linkPath; linkPath = buf[curBuf % 2]; curBuf++; /* get the link info */ result = readlink (filePath, linkPath, MAXPATHLEN); /* check for the result of the readlink call */ if (result == -1) { /* if newPath is not a symbolic link, errno != EINVAL */ if (errno != EINVAL) return False; /* RETURN */ /* filePath is not a sym link ==> a real file or directory */ /* so return filePath in caller-owned memory */ if ( curBuf != 1 ) /* curBuf == 1 when pPathName is a file */ { /* * pPathName had memory allocated before this function was called. * only increase the memory if needed. */ if ( strlen (*pPathName) < strlen(filePath) ) *pPathName = (char *)realloc((void *)*pPathName, (sizeof(char) * (strlen(filePath) +1))); strcpy(*pPathName, filePath); } /* printf("actual is: %s\n", filePath); ** DBG */ return True; /* RETURN */ } /* if an error */ else { /* no error--handle the link */ /* if the path is absolute, just take it as such */ linkPath [result] = EOS; /* for safety */ /* is path relative to current directory? */ if ( linkPath[0] != DIR_SLASH ) { char * slash = NULL; /* get last slash in the current file path */ if(_DtHelpCeStrrchr(filePath,DirSlashStr,MB_CUR_MAX,&slash) == 0) { /* there is a path component in filePath; use it with linkPath */ strcpy(++slash,linkPath); strcpy(linkPath,filePath); /* leave result in linkPath */ } } /* if path is relative */ /* printf("traced to: %s\n", linkPath); ** DBG */ } /* if no error */ } /* while result >= 0 */ return False; /* RETURN */ } /************************************************************************ * Function: _DtHelpFileTraceToFile (pathName, accessMode, foundPath) * * Memory: pPathName must point to a malloc'd string. The string * may be freed by the function and pathName assigned a pointer * to a different string specifying a different path to the same file. * If a file is found, foundPath is set to the same pointer * as pathName, otherwise it is set to NULL. * Returns: * True: file found * False: file not found or error ***********************************************************************/ Boolean _DtHelpFileTraceToFile ( char * * pPathName, int accessMode, char * * pFoundPath) { struct stat status; char * pathName = *pPathName; /* avoid indirection */ *pFoundPath = NULL; if ( pathName == NULL || pathName[0] == EOS ) return False; /* if it's a file, trace its links */ if ( access (pathName, accessMode) == 0 && stat (pathName, &status) == 0 && S_ISREG(status.st_mode) ) /* a file */ { /* trace any links */ if ( _DtHelpFileTraceLinks(pPathName) == False ) { /* don't free pPathName here */ return False; /* RETURN: no file */ } pathName = *pPathName; /* find out if its an accessible file */ if ( pathName != NULL && pathName[0] != EOS && access (pathName, accessMode) == 0 && stat (pathName, &status) == 0 && S_ISREG(status.st_mode)) /* a file */ { /* point foundPath at the path */ *pFoundPath = pathName; return True; /* RETURN: its a file */ } /* if a valid path */ } /* if a path */ #if 0 printf("Unknown file: %s\n", pathName); printf("Access: %d, stat: %d, IS_REG: %d, mode: %x\n", access (pathName, accessMode), stat (pathName, &status), S_ISREG(status.st_mode), status.st_mode); #endif /* its not a file */ *pFoundPath = NULL; return False; } /****************************************************************************** * Function: int _DtHelpFileGetSearchPaths () * * Parameters: * paths: caller array size _DtHELP_FILE_NUM_PATHS in which * to store ptrs to the private path strings * searchHomeDir: boolean flag * * Memory: * the memory pointed to by the array is NOT owned by the * caller and should not be freed or modified * * Purpose: make the search paths available * *****************************************************************************/ void _DtHelpFileGetSearchPaths( char * paths[], Boolean searchHomeDir) { static char * pathsSet[_DtHELP_FILE_NUM_PATHS]; char tmpPath[MAXPATHLEN + 2]; /* get user's home directory; is used in _DtHELP_FILE_USER_PATH as well */ if (NULL == pathsSet[_DtHELP_FILE_HOME_PATH]) { _DtHelpOSGetHomeDirName(tmpPath, sizeof(tmpPath)); pathsSet[_DtHELP_FILE_HOME_PATH] = strdup(tmpPath); } if (searchHomeDir) paths[_DtHELP_FILE_HOME_PATH] = pathsSet[_DtHELP_FILE_HOME_PATH]; else paths[_DtHELP_FILE_HOME_PATH] = NULL; /* generate the user path */ if (NULL == pathsSet[_DtHELP_FILE_USER_PATH]) pathsSet[_DtHELP_FILE_USER_PATH] = _DtHelpGetUserSearchPath(); paths[_DtHELP_FILE_USER_PATH] = pathsSet[_DtHELP_FILE_USER_PATH]; /* get the system search path */ if (NULL == pathsSet[_DtHELP_FILE_SYS_PATH]) pathsSet[_DtHELP_FILE_SYS_PATH] = _DtHelpGetSystemSearchPath(); paths[_DtHELP_FILE_SYS_PATH] = pathsSet[_DtHELP_FILE_SYS_PATH]; } /****************************************************************************** * Function: char * _DtHelpFileLocate () * * Parameters: * type: subdirectories to search (%T) * base: basename of the file * suffix: extension of the file to find (%S) * searchCurDir: boolean flag * accessMode: constant value from access(2) * * Returns: malloc'd path of the located file or NULL if none located * * errno Values: * EINVAL * * Purpose: Scans all paths of given type looking for a matching file * If file contains a valid absolute path, that is also * acceptable. * * FIX: merge _DtHelpFileLocate() and _DtHelpFileListScanPaths() *****************************************************************************/ char * _DtHelpFileLocate ( char * type, char * filespec, const char * suffixList[], Boolean searchCurDir, int accessMode) { char * loc; char * ptr; char * pathName; char * curPath; char * base; int curPathIndex; char * foundPath; const char empty = 0; const char * sufList[2]; #define NUM_BUGS 1 _DtSubstitutionRec bugFixSubs [NUM_BUGS]; char * paths[_DtHELP_FILE_NUM_PATHS]; char tmpPath[MAXPATHLEN + 2]; const char * * pSuffix; char * eos = NULL; char * slash = NULL; /* test args */ if (NULL == filespec) return NULL; /* init suffix list to empty if not specified */ if (suffixList == NULL) { sufList[0] = ∅ sufList[1] = NULL; suffixList = sufList; /* override initial argument setting */ } /*** first look for file as specified ***/ /* if filespec begins with . or .. then stop after the cwd path */ if ( ( MB_CUR_MAX == 1 || mblen(filespec, MB_CUR_MAX) == 1) /* 1st char is 1 byte */ && *filespec == '/') /* and its a / */ { /* _DtHelpFileTraceToFile() needs a malloc'd string */ /* 10: leaves room for add'l suffixes */ pathName = MyMalloc(sizeof(char) * (strlen(filespec)+10)); pathName = strcpy(pathName,filespec); _DtHelpCeCompressPathname(pathName); /* compress out relative paths */ if ( _DtHelpFileTraceToFile(&pathName,accessMode,&foundPath) ) return foundPath; /* RETURN */ /* test all suffixes */ eos = pathName + strlen(pathName); for ( pSuffix = suffixList; NULL != *pSuffix; pSuffix++ ) { strcpy(eos,(char *) *pSuffix); /*recall: _DtHelpFileTraceToFile() requires pathName to be malloc'd*/ if ( _DtHelpFileTraceToFile(&pathName,accessMode,&foundPath) ) return foundPath; /* RETURN: found */ } /* for all suffixes */ MyFree(pathName); } /*** second, check if its relative to the current directory ***/ /* if filespec begins with . or .. then stop after the cwd path */ if ( searchCurDir || ( MB_CUR_MAX == 1 || mblen(filespec, MB_CUR_MAX) == 1) /* 1st char is 1 byte */ && *filespec == '.') /* and its a . */ { /* we're looking at a cwd-relative path; ignore others */ /*** this is monstrously inefficient--but it shouldn't get called often ***/ /* get user's current working directory */ /* JET - CERT VU#575804 */ if (getcwd(tmpPath, MAXPATHLEN - 1) == NULL) return NULL; /* RETURN: error */ /* make path end in a slash */ eos = tmpPath + strlen(tmpPath); _DtHelpCeStrrchr(tmpPath,DirSlashStr,MB_CUR_MAX,&slash); if ( slash != (eos - 1) ) { *eos++ = DIR_SLASH; *eos = EOS; } /* make a malloc'd copy of the path with room to grow */ slash = filespec + strlen(filespec); pathName = malloc(sizeof(char) * ((eos-tmpPath) + (slash-filespec) + 50) ); /* 50: arbitrary */ if (NULL == pathName) return NULL; /* RETURN: error */ strcpy(pathName,tmpPath); /* cat on the relative path */ strcat(pathName,filespec); /* compress out any relative paths */ _DtHelpCeCompressPathname(pathName); /* see if we find the file now */ /* recall: _DtHelpFileTraceToFile() requires pathName to be malloc'd */ if ( _DtHelpFileTraceToFile(&pathName,accessMode,&foundPath) ) return foundPath; /* RETURN: found */ /* test all suffixes */ eos = pathName + strlen(pathName); for ( pSuffix = suffixList; NULL != *pSuffix; pSuffix++ ) { strcpy(eos,(char *) *pSuffix); /* recall: _DtHelpFileTraceToFile() requires pathName to be malloc'd */ if ( _DtHelpFileTraceToFile(&pathName,accessMode,&foundPath) ) return foundPath; /* RETURN: found */ } /* for all suffixes */ MyFree(pathName); return NULL; /* RETURN: error */ } /* filespec is a relative path or search cur dir */ /*** third look in search path directories ***/ /* get the search paths */ _DtHelpFileGetSearchPaths( paths, False ); /*** prep variables to pass through the path search loop ***/ /* we're not looking at a cwd-relative path and we know that 'filespec' isn't a valid path to a volume (from _DtHelpFileTraceToFile), so just pick off the basename of the spec */ base = filespec; if ( _DtHelpCeStrrchr(filespec,DirSlashStr,MB_CUR_MAX,&ptr) == 0 ) base = ++ptr; /* begin past the slash */ /* Have to support %H explicitly */ bugFixSubs[0].match = 'H'; bugFixSubs[0].substitution = base; /* get the LANG value */ loc = _DtHelpGetLocale(); if (NULL == loc || EOS == loc[0]) loc = strdup(LANG_C_STR); /* outer loop is once for each path */ foundPath = NULL; for ( curPathIndex = 0; curPathIndex < _DtHELP_FILE_NUM_PATHS && NULL == foundPath; curPathIndex++ ) { curPath = paths[curPathIndex]; if (NULL == curPath) continue; /* continue */ /* look for the file in that path */ if (NULL != curPath) do { /* look for next subpath separator and insert and EOS if found */ if (_DtHelpCeStrchr(curPath,PathSeparator,MB_CUR_MAX,&ptr)==0) *ptr = EOS; /* compress that path */ /* JET - CERT VU#575804 */ strncpy(tmpPath, curPath, MAXPATHLEN); _DtHelpCeCompressPathname(tmpPath); /* test all suffixes */ for ( pSuffix = suffixList, foundPath = NULL; NULL == foundPath && NULL != *pSuffix; pSuffix++ ) { /* generate the (directory) path using all the variables and fix it up to remove the unwanted stuff involving the filename */ pathName = _DtHelpCeExpandPathname (curPath, base, type, (char *) *pSuffix, loc, bugFixSubs, NUM_BUGS); if ( _DtHelpFileTraceToFile(&pathName,accessMode,&foundPath)==False && NULL != pathName) free(pathName); } /* for all suffixes */ /* restore the subpath separator and advance past it */ if (ptr) *ptr++ = *PathSeparator; curPath = ptr; } while (curPath && *curPath && NULL == foundPath); /* do while more subpaths */ } /* for all paths */ MyFree(loc); return foundPath; } /****************************************************************************** * Function: int _DtHelpCeCheckAndCacheDir (char *dir) * * Parameters: * dir Specifies the directory to test. * * Returns: 0 if the directory exists. * ENOTDIR if the directory is invalid. * * Purpose: To check a directory only once and remember the result. * *****************************************************************************/ int _DtHelpCeCheckAndCacheDir (char *dir) { int result = ENOTDIR; _DtHelpCeDirStruct *curDir = CachedDirs; _DtHelpCeDirStruct *prevDir = NULL; struct stat buf; _DtHelpProcessLock(); if (dir == NULL || *dir == '\0') return ENOTDIR; /* * search the cached directories */ while (curDir != NULL && strcmp(curDir->dir_name, dir)) { prevDir = curDir; curDir = curDir->next_dir; } /* * was the directory found in the cache? If so, return the type. */ if (curDir != NULL) result = curDir->type; else { /* * new directory - malloc room for this entry. */ result = ENOMEM; curDir = (_DtHelpCeDirStruct *) malloc(sizeof(_DtHelpCeDirStruct)); if (curDir != NULL) { /* * initialize the new entry. I.E. type starts out ENOTDIR. */ *curDir = DefCacheDir; curDir->dir_name = strdup(dir); if (curDir->dir_name != NULL) { /* * put this entry in the list */ if (prevDir != NULL) prevDir->next_dir = curDir; else CachedDirs = curDir; /* * is this a directory? */ if (access(dir, R_OK) == 0 && stat(dir, &buf) == 0 && S_ISDIR(buf.st_mode)) curDir->type = 0; /* * return the result of the tests. */ result = curDir->type; } else free(curDir); } } /* * This should never happen, but just in case the directory * can't be cached, go ahead and check it anyway. */ if (result == ENOMEM && access(dir, R_OK) == 0 && stat(dir, &buf) == 0 && S_ISDIR(buf.st_mode)) result = 0; _DtHelpProcessUnlock(); return result; } #ifdef not_done /****************************************************************************** * Function: _DtHelpCeDirStruct *_DtHelpCeGetCachedDirs (void) * * Parameters: none. * * Returns: A pointer to the cached directories. * * Purpose: To allow access to the cached directories. * *****************************************************************************/ _DtHelpCeDirStruct * _DtHelpCeGetCachedDirs (void) { return CachedDirs; } #endif /* not_done */