/* -*- 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 Description APY 12/99 Created APY 14/03/00 fixpath's,vpath2 Check for abs path prior to scanning vpath. APY 30/11/00 - VPATH, wild arguments -vlib() 11/10/04 vlint vlib, 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 #define NAMLEN(dirent) strlen((dirent)->d_name) #elif defined(HAVE_DIRENT_H) || \ defined(sun) || defined(linux) || defined(WIN32) #include #define NAMLEN(dirent) strlen((dirent)->d_name) #else # define dirent direct # define NAMLEN(dirent) (dirent)->d_namlen # if defined(HAVE_SYS_NDIR_H) # include # endif # if defined(HAVE_SYS_DIR_H) # include # endif # if defined(HAVE_NDIR_H) # include # endif #endif #include #include #include "cmdfile.h" #ifdef MSDOS #include /* For intdos() */ #endif typedef struct { #define FICASE 0x0001 int gl_argc; char **gl_argv; } Glob_t; #define HWIN32 0x01 #define HUNIX 0x02 static 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); #endif static 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 ... #endif int 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); } int vpath( 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); } int do_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); } int do_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 int x_exists( const char *buf ) { int ret; ret = access( buf, F_OK ); verbose( " access(%s) : %d", buf, ret ); return (ret);; } static int file_access( char sep, const char *name, char *buf ) { (void) fixpath( name, buf, sep ); return x_exists( buf ); } static int lib_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 int lint_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 int glob_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 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 void fixpath( 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 int is_term(int c) { return c == '/' || c == '\\' || c == '\0'; } static int is_slash(int c) { if (c == '/' || c == '\\') return 1; return 0; } #if defined(MSDOS) static int GetDisk(void) { #ifdef __WATCOMC__ unsigned drive; _dos_getdrive(&drive); return drive - 1; #else union 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 int GetDisk(void) { char path[ PATH_MAX ]; getcwd(path, PATH_MAX); return (toupper(path[0])-'A'); /* A=1, B=2 etc */ } #endif static 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; #else regs.x.si = (unsigned long) cwd; #endif (void) intdos(®s, ®s); #ifdef __WATCOMC__ if (regs.w.cflag != 0) { errno = regs.w.ax; return out; } #else if (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); #else out[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 names into 'pGlob->gl_argc' and a pointer to a list of pointers to 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 returned number of matched paths is set to zero, and the contents of 'pGlob->gl_argv' is undefined. The argument 'pPath' is a pointer to a path name to be searched, using the argument 'pPattern' to be expanded. The glob function matches all accessible path names against this pattern and develops a list of all path names that match. In order to have access to a path name, glob requires search permission on every component of a path except the last, and read permission on each directory of any filename component of pattern that contains any of the following special characters: * ? [ It is the caller's responsibility to create the structure pointed to by pGlob. The glob function allocates other space as needed, including the memory pointed to by gl_pathv. The GlobFree() function frees any space associated with pGlob from a previous call to Glob. Parameters: pPath, pGlob, pPattern Returns: The following values are returned by glob(): Example: Glob_t globbuf; Glob(".", &globbuf, "*.c"); Print(&globbuf); GlobFree(&globuf); */ static int Glob(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 void GlobPrint( 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 void GlobFree( 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. It can be any character in the supported character set except for NUL, and the following three special pattern characters. When unquoted and outside a bracket expression, the following three 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 will matches a single collating element contained in the non-empty set of collating elements. The following rules and definitions apply to bracket expressions: 1. A bracket expression is either a matching list expression or a non-matching list expression. It consists of one or more expressions. 2. A matching list expression specifies a list that matches any one of the expressions represented in the list. The first character in the list must not be the circumflex (^). For example, [abc] is a pattern that 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 expressions represented in the list after the leading circumflex. For example, [^abc] is a pattern that matches any character or collating element except the characters a, b or c. The circumflex will have this special meaning only when it occurs first in the list, immediately following the left-bracket. 4. A range expression represents the set of collating elements that fall between two elements in the current collation sequence, inclusively. It is expressed as the starting point and the ending point separated by a hyphen (-). For example, [a-z] is a pattern that matches any of the characters a to z inclusive. The hyphen character will be treated as itself if it occurs first (after an initial ^, if any) or last in the list, or as an ending range point in a range expression. As examples, the expressions [-ac] and [ac-] are equivalent and match any of the characters a, c, or -; [^-ac] and [^ac-] are equivalent and match any characters except a, c, or -; the expression [%--] matches any of the characters between % and - inclusive; the expression [--@] matches any of the characters between - and @ inclusive. + a bracket expression followed by this means one or more time. { introduces a pattern set expression, that will match one or the colon seperated patterns. *.........................................................................*/ static int Match(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 __TOLOWER if (*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 ** ----------------------------------------------------------------------------*/ int from_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); }