cdesktopenv/cde/lib/DtHelp/FileUtils.c

676 lines
22 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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 librararies 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 <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/param.h> /* MAXPATHLEN */
#include <Dt/Help.h>
/*
* 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 */
strcpy(buf[0],*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 comonent 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
* acceptible.
*
* 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] = &empty;
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 */