Blame | Last modification | View Log | RSS feed
/* -*- mode: c; tabs: 4; -*- ************************************************* Module name : vpath.c* Module type : CMDFILE source file* Environment(s): n/a** Description:*vpath --- dependencies search path support** Version Who Date DescriptionAPY 12/99 CreatedAPY 14/03/00 fixpath's,vpath2Check for abs path prior to scanning vpath.APY 30/11/00 - VPATH, wild arguments-vlib()11/10/04 vlintvlib, host specification** $Source: /cvsroot/device/DEVL/UTILS/CMDFILE/vpath.c,v $* $Revision: 1.7 $ $Date: 2004/10/11 08:59:27 $ $State: Exp $* $Author: ayoung $ $Locker: $*.........................................................................*/#if defined(HAVE_DIRECT_H)#include <direct.h>#define NAMLEN(dirent) strlen((dirent)->d_name)#elif defined(HAVE_DIRENT_H) || \defined(sun) || defined(linux) || defined(WIN32)#include <dirent.h>#define NAMLEN(dirent) strlen((dirent)->d_name)#else# define dirent direct# define NAMLEN(dirent) (dirent)->d_namlen# if defined(HAVE_SYS_NDIR_H)# include <sys/ndir.h># endif# if defined(HAVE_SYS_DIR_H)# include <sys/dir.h># endif# if defined(HAVE_NDIR_H)# include <ndir.h># endif#endif#include <unistd.h>#include <ctype.h>#include "cmdfile.h"#ifdef MSDOS#include <dos.h> /* For intdos() */#endiftypedef struct {#define FICASE 0x0001int gl_argc;char **gl_argv;} Glob_t;#define HWIN32 0x01#define HUNIX 0x02static const char *find_on_vpath(int flags, const char *name,const char *vpath, char *buf);static const char *find_on_vpath2(int flags, const char *name,const char *vpath, char *buf);static const char *next_path(const char *path, char sep,const char *name, char *dir, char *buf);static void fixpath(const char *in, char *out, char sep);static int is_term(int c);static int is_slash(int c);#if defined(MSDOS) || defined(WIN32)static int GetDisk(void);#endifstatic char *GetCwd(char *out, int drive, char sep);static int Glob(int flags, const char *pPath,Glob_t *pGlob, const char *pPattern);static void GlobFree(Glob_t *pGlob);static void GlobPrint(Glob_t *pGlob);const char * vpath_lib_default = NULL;char vpath_sep = ';'; /*default*/#if defined(unix)int vpath_host = HUNIX;#elif defined(WIN32) || defined(MSDOS)int vpath_host = HWIN32;#else#error vpath: Unknown host ...#endifint do_clean_sep ( String_t *str, const char *s ){char gsep[2];char name[ MACRO_MAX ];int len = 0, len2;char *i;if (expected(s++, ',') > 0 &&(len2 = pathargument(s, gsep, 2)) > 0){len += 1+len2, s += len2;if (expected(s++, ',') > 0 &&(len2 = pathargument(s, name, MACRO_MAX)) > 0){len += 1+len2, s += len2;/* Step through the input path */for (i = name; *i; i++){if (is_slash(*i))*i = gsep[0];}StringCat( str, name );return (len);}}return (-1);}intvpath( String_t *str, const char *s, int flags ){char name[ PATH_MAX ], vpath[ MACRO_MAX ], result[ PATH_MAX ];char gsep[2];int len = 0, len2;const char *p;if (expected(s++, ',') > 0 &&(len2 = pathargument(s, name, PATH_MAX)) > 0){len = 1+len2, s += len2;if (expected(s++, ',') > 0 &&(len2 = pathargument(s, vpath, MACRO_MAX)) > 0){len += 1+len2, s += len2;gsep[0] = 0;if ( *s++ == ',' && (len2 = pathargument(s, gsep, 2)) > 0){len += 1+len2, s += len2;}if (vflg){const char *type =(flags & VFLAG_LINT ? "vlint" :(flags & VFLAG_LIBRARY ? "vlib" :(flags & VFLAG_GLOB ? "vglob" : "vpath" )));verbose( " %s%s(%s,%s)", type,(flags & VFLAG_OPTIONAL ? "3":(flags & VFLAG_MUSTRESOLVE ? "2": "")),name, vpath );}if ((p = find_on_vpath(flags, name, vpath, result)) == NULL){if (flags & VFLAG_MUSTRESOLVE){if (strchr(vpath, '/') || strchr(vpath,'\\') ||strchr(vpath, '.'))p = NULL;else{p = getenv(vpath);}/*** Generate a readable error message** Display each element of the path on a new line*/{int len;const char *ptr;message ( "ERROR: Unable to locate the file '%s' " \"using the path:\n", name);p = (p ? p : vpath);for ( ptr = p; *p ; p++ ){if ( *p == vpath_sep ){len = p - ptr;if ( len ){message ( " %-*.*s\n", len, len, ptr );len = 0;}ptr = p+1;}}len = p - ptr;if ( len ){message ( " %-*.*s\n", len, len, ptr );}fatalerr( "Unable to resolve '%s'\n", name );}}else if ((flags & VFLAG_OPTIONAL) == 0){p = name; /* quote path */}}else{verbose( " path = %s", p );}if ( gsep[0] )fixpath( p, (char *)p, gsep[0]);StringPath( str, p );return (len);}}return (-1);}intdo_vhost( const char *s ){char host[ 32 ];int len = 0, len2;if (expected(s++, ',') > 0 &&(len2 = argument(s, host, sizeof(host))) > 0){len = 1+len2, s += len2;if (strcasecmp(host, "unix") == 0)vpath_host = HUNIX;else if (strcasecmp(host, "win32") == 0)vpath_host = HWIN32;else{fatalerr( "Unknown host '%s'\n", host );}}return (len);}intdo_vsep( const char *s ){char sep[ 32 ];int len = 0, len2;if (expected(s++, ',') > 0 &&(len2 = argument(s, sep, sizeof(sep))) > 0){len = 1+len2, s += len2;if (!strcasecmp(sep, ":") || !strcasecmp(sep, "colon"))vpath_sep = ':';else if (!strcasecmp(sep, ";") || !strcasecmp(sep, "semicolon"))vpath_sep = ';';else{fatalerr( "Unknown seperator '%s'\n", sep );}}return (len);}static char *fcat(char *buf, const char *dir, char sep, const char *name){char tbuf[ PATH_MAX ];char *s = tbuf;int fsize;(void) strncpy(tbuf, dir, PATH_MAX);s += (fsize = strlen(dir));if (fsize + strlen(name) + 1 >= PATH_MAX)fatalerr( "PATH_MAX(%d) exceeded on dir '%s'\n", PATH_MAX, buf );if ((tbuf != s) && !is_slash(s[-1]) && (fsize++ < PATH_MAX))*s++ = sep;*s = '\0';(void) strncpy(s, name, PATH_MAX-fsize);tbuf[ PATH_MAX-1 ] = '\0';(void) fixpath((const char *)tbuf, buf, sep);return (buf);}static intx_exists( const char *buf ){int ret;ret = access( buf, F_OK );verbose( " access(%s) : %d", buf, ret );return (ret);;}static intfile_access( char sep, const char *name, char *buf ){(void) fixpath( name, buf, sep );return x_exists( buf );}static intlib_access( const char *dir, char sep, const char *name, char *buf ){char *s;int fsize;(void) fcat(buf, dir, sep, name);verbose( " libsearch(%s%c%s=%s)", dir, sep, name, buf );s = buf + (fsize = strlen(buf));if (vpath_host == HUNIX){ /* shared library */(void) strncpy(s, ".so", PATH_MAX-fsize);buf[ PATH_MAX-1 ] = '\0';if (x_exists( buf ) == 0)return (0);/* static library */(void) strncpy(s, ".a", PATH_MAX-fsize);buf[ PATH_MAX-1 ] = '\0';if (x_exists( buf ) == 0)return (0);}else if (vpath_host == HWIN32){ /* static/implib */(void) strncpy(s, ".lib", PATH_MAX-fsize);buf[ PATH_MAX-1 ] = '\0';if (x_exists( buf ) == 0)return (0);}return (ENOENT);}static intlint_access( const char *dir, char sep, const char *name, char *buf ){char *s;int fsize;(void) fcat(buf, dir, sep, name);verbose( " lintsearch(%s%c%s=%s)", dir, sep, name, buf );s = buf + (fsize = strlen(buf));(void) strncpy(s, ".lob", PATH_MAX-fsize); /* lint object */buf[ PATH_MAX-1 ] = '\0';if (x_exists( buf ) == 0)return (0);(void) strncpy(s, ".lnt", PATH_MAX-fsize); /* lint specification */buf[ PATH_MAX-1 ] = '\0';if (x_exists( buf ) == 0)return (0);return (ENOENT);}static intglob_access(const char *dir, char sep, const char *name, char *buf){Glob_t glob;verbose( " globbing(%s, %s)", dir, name );if (Glob(0, dir, &glob, name) == 0){int i;for (i=0; i<glob.gl_argc; i++){fcat(buf, dir, sep, glob.gl_argv[i]);verbose( " testing access(%s)", buf );if (access(buf, F_OK) == 0){(void) GlobFree(&glob);return (0);}}(void) GlobFree(&glob);}return (ENOENT);}static const char *find_on_vpath(int flags, const char *name, const char *vpath, char *buf ){const char *ret;/* specified path */if ((ret = find_on_vpath2( flags, name, vpath, buf )) == NULL){if (flags & VFLAG_LIBRARY) /* default LIB path, if any */if (vpath_lib_default != NULL){ret = find_on_vpath2( flags, name, vpath_lib_default, buf );}}return (ret);}static const char *find_on_vpath2(int flags, const char *name, const char *vpath, char *buf ){char dir[ PATH_MAX ];const char *sp;char sep;if (strchr(vpath, '/') || strchr(vpath, '\\') || strchr(vpath, '.'))sp = vpath;else if ((sp = getenv(vpath)) == NULL)sp = "";sep = (strchr(sp, '/') ? '/' : '\\');verbose( " vpath(%s), sep=%c", sp, sep );if (file_access( sep, name, buf ) == 0 ) /* is path already absolute */return name;do { /* scan vpath */sp = next_path( sp, sep, name, dir, buf );if (dir[0] == '\0')continue; /* empty specification */if (x_exists( buf ) == 0) /* abs match ? */return buf;if ((flags & VFLAG_LINT) && /* lint search */lint_access(dir, sep, name, buf) == 0)return (buf);if ((flags & VFLAG_LIBRARY) && /* library search */lib_access(dir, sep, name, buf) == 0)return (buf);if ((flags & VFLAG_GLOB) && /* globbing */glob_access(dir, sep, name, buf) == 0)return buf;} while (sp != (char *)NULL);errno = ENOENT;return (NULL); /* no match */}/** Build next path*/static const char *next_path(const char *path, char sep, const char *name, char *dir, char *buf){char *d = dir;int fsize = 0;/* retrieve path */while (*path && *path != vpath_sep) {if (*path != ' ' || d != dir) /* trim leading spaces */if (fsize < PATH_MAX) {*d++ = *path, fsize++;}path++;}while (fsize && d[-1] == ' ') /* trim trailing spaces */d--, fsize--;if (fsize > 2 && dir[0] == '"' && d[-1] == '"') {fsize -= 2; /* strip quoted path */memmove( dir, dir+1, fsize );d = dir + fsize;}/* add seperator */if (fsize && !is_slash(d[-1]) && (++fsize < PATH_MAX))*d++ = sep;*d = '\0'; /* terminator *//* build fullname */(void) fcat( buf, dir, sep, name );verbose( " search(%s,%s=%s)", dir, name, buf );return (*path ? ++path : (char *)NULL);}/* fixpath ---** Takes as input an arbitrary path. Fixes up the path by:* 1. Convert relative path to absolute* 2. Removing consecutive slashes* 3. Removing trailing slashes* 4. Making the path absolute if it wasn't already* 5. Removing "." in the path* 6. Removing ".." entries in the path (and the directory above them)*/static voidfixpath( const char *in, char *out, char sep ){register const char *i = in;register char *o = out;/* Convert relative path to absolute */#if defined(MSDOS) || defined(WIN32){int drive;if (*(i+1) == ':' &&((*i >= 'a' && *i <= 'z') || (*i >= 'A' && *i <= 'Z'))) {if (*i >= 'a' && *i <= 'z')drive = *i - 'a';else drive = *i - 'A';*o++ = *i++;*o++ = *i++;} else {drive = GetDisk();*o++ = drive + 'A';*o++ = ':';}if (!is_slash(*i))o = GetCwd(o, drive, sep);}#else /*unix*/if (!is_slash(*i))o = GetCwd(o, -1, sep);#endif/* Step through the input path */while (*i != '\0'){/* Skip input slashes */if (is_slash(*i)){i++;continue;}/* Skip "." and output nothing */if (*i == '.' && is_term(*(i + 1))){i++; continue;}/* Skip ".." and remove previous output directory */if (*i == '.' && *(i + 1) == '.' && is_term(*(i + 2))){/* Don't back up over drive spec or consume ../.. */if (o > out+2 && strncmp(o-2, "..", 2) != 0){/* Consume .. and parent directory */i += 2;while (--o > out+2 && !is_slash(*o));continue;}}/* Copy path component from in to p */*o++ = sep;/* Copy next filename/directory */while (!is_term(*i))*o++ = *i++;}*o = '\0';}static intis_term(int c){return c == '/' || c == '\\' || c == '\0';}static intis_slash(int c){if (c == '/' || c == '\\')return 1;return 0;}#if defined(MSDOS)static intGetDisk(void){#ifdef __WATCOMC__unsigned drive;_dos_getdrive(&drive);return drive - 1;#elseunion REGS regs;regs.h.ah = 0x19; /* DOS Get Default Drive call */regs.h.al = 0;(void) intdos(®s, ®s);return regs.h.al;#endif}#elif defined(WIN32)static intGetDisk(void){char path[ PATH_MAX ];getcwd(path, PATH_MAX);return (toupper(path[0])-'A'); /* A=1, B=2 etc */}#endifstatic char *GetCwd(char *out, int drive, char sep){#if defined(MSDOS)static char cwd[ PATH_MAX ];register char *p, *o;/* Get current work directory */if (*cwd == '\0'){union REGS regs;regs.h.ah = 0x47;regs.h.dl = drive + 1;#ifdef __386__regs.x.esi = (unsigned long) cwd;#elseregs.x.si = (unsigned long) cwd;#endif(void) intdos(®s, ®s);#ifdef __WATCOMC__if (regs.w.cflag != 0) {errno = regs.w.ax;return out;}#elseif (regs.x.cflag != 0) {errno = regs.x.ax;return out;}#endif}/* Copy and convert path */for (p = cwd, o = out + 1; *p; p++)*o++ = is_slash(*p) ? sep : *p;*o = '\0';/* Root path, don't insert "/", it'll be added later */if (*(out+1) != '\0')*out = sep;else *out = '\0';return (out);#elif defined(WIN32)int len;out[0] = '\0';(void) _getdcwd(drive + 1, out, PATH_MAX);if ((len = strlen(out)) >= 2 && out[1] == ':'){memmove(out, (const char *)(out+2), len-1);len -= 2;}return (out + len);#elseout[0] = '\0';(void) getcwd(out, PATH_MAX);return (out + strlen(out));#endif}/*-------------------------------------------------------------- */static int Match(int, const char *, const char *);/*----------------------------------------------------------------------------Synopsis:static int Glob(int flags,const char *pPath,Glob_t *pGlob,const char *pPattern)static GlobFree(Glob_t *pGlob)MT-Level:Safe (only one thread may own the Glob_t structure)Purpose:The Glob function stores the number of matched path namesinto 'pGlob->gl_argc' and a pointer to a list of pointersto path names into 'pGlob->gl_argv'.typedef struct {int gl_argc;char **gl_argv;} Glob_t;The path names are sorted as encountered within the directory.The first pointer after the last path name is a NULL pointer.If the pattern does not match any path names, the returnednumber of matched paths is set to zero, and the contentsof 'pGlob->gl_argv' is undefined.The argument 'pPath' is a pointer to a path name to besearched, using the argument 'pPattern' to be expanded. Theglob function matches all accessible path names againstthis pattern and develops a list of all path names thatmatch. In order to have access to a path name, glob requiressearch permission on every component of a path except thelast, and read permission on each directory of any filenamecomponent of pattern that contains any of the followingspecial characters:* ? [It is the caller's responsibility to create the structurepointed to by pGlob. The glob function allocates other spaceas needed, including the memory pointed to by gl_pathv. TheGlobFree() function frees any space associated with pGlobfrom a previous call to Glob.Parameters:pPath,pGlob,pPatternReturns:The following values are returned by glob():Example:Glob_t globbuf;Glob(".", &globbuf, "*.c");Print(&globbuf);GlobFree(&globuf);*/static intGlob(int flags, const char *pPath, Glob_t *pGlob, const char *pPattern){struct dirent *dp;char **ppNames;int total, cnt;DIR *pDir;char dir_path[ PATH_MAX ];/*** Remove trailing / from pPath*/strcpy ( dir_path, pPath );cnt = strlen(dir_path);if ( is_slash( dir_path[cnt-1]) )dir_path[cnt-1] = 0;if ((pDir = opendir(dir_path)) == (DIR *)NULL){return (errno);}ppNames = NULL;for (total=0, cnt=0; (dp = readdir(pDir)) != NULL;){if (pPattern == NULL || Match(flags, pPattern, dp->d_name) == 0){/* (re)alloc name storage */if (cnt >= total){total += 256;ppNames = (char **)realloc(ppNames, total*sizeof(ppNames[0]));if (ppNames == NULL)goto nomem;}/* Store directory details */verbose( " .. matched(%s)", dp->d_name );if ((ppNames[cnt] = (char *)strdup(dp->d_name)) == (char *)NULL)goto nomem;cnt++;}}(void) closedir(pDir);if (cnt)ppNames[cnt] = NULL;pGlob->gl_argv = ppNames;pGlob->gl_argc = cnt;return (0);nomem:fatalerr("Memory error globing %s", pPattern);pGlob->gl_argv = ppNames;pGlob->gl_argc = cnt;GlobFree(pGlob);pGlob->gl_argc = 0;return (ENOMEM);}/** Dump the glob list*/static voidGlobPrint(Glob_t *pGlob){if (pGlob && pGlob->gl_argc > 0){register int cnt = pGlob->gl_argc;register int i;for(i=0; i < cnt; i++)printf("[%2d]: %s", i, pGlob->gl_argv[i]);}}/** Free the list of files:* The Globfree() function frees any memory allocated by* Glob() associated with 'pGlob'.*/static voidGlobFree(Glob_t *pGlob){if (pGlob && pGlob->gl_argc > 0){register int cnt = pGlob->gl_argc;while (cnt > 0)free(pGlob->gl_argv[--cnt]);free(pGlob->gl_argv);}}/* Match ---* Pattern matching* Usage:*An ordinary character is a pattern that matches itself. Itcan be any character in the supported character set exceptfor NUL, and the following three special pattern characters.When unquoted and outside a bracket expression, the followingthree characters will have special meaning:\ quotes the next character.? is a pattern that will match any character.* is a pattern that will match multiple characters.[ introduces a pattern bracket expression, that willmatches a single collating element contained in thenon-empty set of collating elements.The following rules and definitions apply to bracketexpressions:1. A bracket expression is either a matching listexpression or a non-matching list expression. Itconsists of one or more expressions.2. A matching list expression specifies a list thatmatches any one of the expressions represented in thelist. The first character in the list must not bethe circumflex (^). For example, [abc] is a patternthat matches any of the characters a, b or c.3. A non-matching list expression begins with a circum-flex (^), and specifies a list that matches any char-acter or collating element except for the expressionsrepresented in the list after the leading circumflex.For example, [^abc] is a pattern that matches anycharacter or collating element except the charactersa, b or c. The circumflex will have this specialmeaning only when it occurs first in the list,immediately following the left-bracket.4. A range expression represents the set of collatingelements that fall between two elements in thecurrent collation sequence, inclusively. It isexpressed as the starting point and the ending pointseparated by a hyphen (-). For example, [a-z] is apattern that matches any of the characters a to zinclusive.The hyphen character will be treated as itself ifit occurs first (after an initial ^, if any) orlast in the list, or as an ending range point in arange expression. As examples, the expressions[-ac] and [ac-] are equivalent and match any of thecharacters a, c, or -; [^-ac] and [^ac-] areequivalent and match any characters except a, c, or -;the expression [%--] matches any of the charactersbetween % and - inclusive; the expression [--@]matches any of the characters between - and @inclusive.+ a bracket expression followed by this means one ormore time.{ introduces a pattern set expression, that willmatch one or the colon seperated patterns.*.........................................................................*/static intMatch(int flags, const char *pPattern, const char *pString){register const char *p = pPattern;register const char *n = pString;register char ch;#define __TOLOWER(c) ((flags & FICASE) && isupper(c)?tolower(c):c)while ((ch = *p++) != '\0'){ch = __TOLOWER(ch);switch (ch){case '?': /* Match next character */if (*n == '\0')return -1;break;case '*': /* Match next [n] characters */for (ch = *p++; ch == '?' || ch == '*'; ch = *p++, n++){if (ch == '?' && *n == '\0')return -1;}if (ch == '\0')return 0;{char c1 = (char)ch;c1 = __TOLOWER(c1);for (--p; *n != '\0'; ++n)if ((ch == '[' || __TOLOWER(*n) == c1) &&Match(flags, p, n) == 0)return 0;return -1;}case '[': /* Match one of the given set */{const char *start;int xnot, cnt=0;start = p; /* Start of set */if (*n == '\0')return -1;more:;xnot = (*p == '!' || *p == '^');if (xnot)++p;ch = (int)*p++;for (;;){register char cstart = ch, cend = ch;cstart = cend = __TOLOWER(cstart);if (ch == '\0') /* [... (unterminated) loses. */return -1;ch = *p++;ch = __TOLOWER(ch);if (ch == '-' && *p != ']'){cend = *p++;if (cend == '\0') /* [... (unterminated) loses. */return -1;cend = __TOLOWER(cend);ch = *p++;}if (__TOLOWER(*n) >= cstart && __TOLOWER(*n) <= cend)goto matched;if (ch == ']')break;}/* nomatch */if (!xnot){/* Match expected, at least one previous match required */if (!cnt)return -1;if (*p == '+')p++; /* remove attribute */n--; /* push terminator */}else{/* No match expected, loop if required otherwise success */if (*p == '+'){cnt++;if (n[1]){ /* Additional characters to scan */n++, p = start;goto more;}p++; /* remove attribute */}}break;matched:;/* Skip the rest of the [...] that already matched */while (ch != ']'){if (ch == '\0') /* [... (unterminated) loses. */return -1;ch = *p++;}if (xnot){/* No match excepted, if [!x]+ previous match required */if (*p == '+'){if (!cnt)return -1;p++; /* remove attribute */}n--; /* push terminator */}else{/* Match expected, loop if required otherwise success */if (*p == '+'){cnt++;if (n[1]){ /* Additional characters to scan */n++, p = start;goto more;}p++; /* remove attribute */}}}break;case '{': /* Set */{const char *se = p;char buf[256]; /* XXX, max set size */char *bp= buf;int i;while (*se != /*{*/ '}'){if (*se++ == '\0')return -1;}se++;while (*p != /*{*/ '}'){for (i=0, bp=buf; i < sizeof(buf)-1 &&*p != '|' && *p != '}'; i++, *bp++ = *p++)/*LINTED*/;*bp = '\0';if (*se != '\0') {strncpy(bp, se, sizeof(buf)-1);buf[ sizeof(buf)-1 ] = '\0';}if (Match(flags, buf, n) == 0)return 0;if (*p == '|') /* treat '|' as sep */p++;}return -1;}break;case '\\': /* Quote */if ((ch = *p++) == '\0') /* \... (unterminated) loses. */return -1;default:if (ch != __TOLOWER(*n))return -1;}n++;}#undef __TOLOWERif (*n == '\0')return 0;return -1;}/*----------------------------------------------------------------------------** FUNCTION : from_env**** DESCRIPTION : Get data from the users environment****** INPUTS : str - output string** s - input arguments**** RETURNS : length of arguments consumed**----------------------------------------------------------------------------*/intfrom_env(String_t *str, const char *s){char varname[ 32 ];int len = 0, len2;char *sp;if (expected(s++, ',') > 0 &&(len2 = argument(s, varname, sizeof(varname))) > 0){len = 1+len2, s += len2;sp = getenv(varname);if ( ! sp )fatalerr( "Environment Variable not found: '%s'\n", varname );verbose( " EnvVar %s = %s", varname, sp );StringCat(str, sp);}return (len);}