diff options
Diffstat (limited to 'util/find.c')
-rw-r--r-- | util/find.c | 456 |
1 files changed, 456 insertions, 0 deletions
diff --git a/util/find.c b/util/find.c new file mode 100644 index 0000000..cf4d9ca --- /dev/null +++ b/util/find.c @@ -0,0 +1,456 @@ +/* + * Copyright (c) 1999-2003 Smithsonian Astrophysical Observatory + */ + +/* + * + * Find.c -- find files via the path environment variable + * (and related routines) + * + */ +#include <find.h> + +/* + * + * private routines + * + */ + +#define MAXBUFSIZE 8192 + +#ifndef HAVE_UNISTD_H +#define F_OK 0 /* does file exist */ +#define X_OK 1 /* is it executable by caller */ +#define W_OK 2 /* is it writable by caller */ +#define R_OK 4 /* is it readable by caller */ +#endif + +/* not part of unistd.h but we need to differentiate directories */ +#ifdef D_OK +#undef D_OK +#endif +#define D_OK 256 /* is it a directory */ + +#ifdef ANSI_FUNC +static int +amparse (char *mode) +#else +static int amparse(mode) + char *mode; +#endif +{ + int xmode = 0; + + xmode |= ( strpbrk(mode, "r") != NULL ? R_OK : 0 ); + xmode |= ( strpbrk(mode, "w") != NULL ? W_OK : 0 ); + xmode |= ( strpbrk(mode, "x") != NULL ? X_OK : 0 ); + xmode |= ( strpbrk(mode, "f") != NULL ? F_OK : 0 ); + xmode |= ( strpbrk(mode, "d") != NULL ? D_OK : 0 ); + + return xmode; +} + +#ifdef ANSI_FUNC +static char * +findpath (char *name, char *mode, char *path) +#else +static char *findpath(name, mode, path) + char *name; + char *mode; + char *path; +#endif +{ + char pathbuff[MAXBUFSIZE]; + char namebuff[MAXBUFSIZE]; + char tempbuff[MAXBUFSIZE]; + char backmode[MAXBUFSIZE]; + char *here, *found; + int len; + int mark = 0; + int skip = strpbrk(mode, ">") != NULL; + int pick = strpbrk(mode, "<") != NULL; + + if ( skip && pick ) return NULL; + + if ( (path==NULL) || ( name[0] == '.' && name[1] == '/' ) || name[0] == '/' ) + return Access(name, mode); + + strncpy(pathbuff, path, MAXBUFSIZE-1); + pathbuff[MAXBUFSIZE-1] = '\0'; + path = pathbuff; + + if ( (here = strpbrk(pathbuff, ":;")) ) { + mark = *here; + *here++ = '\0'; + } + while ( path ) { + /* if there is an environment variable ... */ + if ( strchr(path, '$') ) { + /* exand it */ + ExpandEnv(path, tempbuff, MAXBUFSIZE); + /* make sure we could expand it (otherwise we get an infinite loop) */ + if( !strchr(tempbuff, '$') ){ + if ( (found = findpath(name, mode, tempbuff)) ) + return found; + } + } else { + if ( !skip ) { + if ( !strcmp(".", path) ) path[0] = '\0'; + + strncpy(namebuff, path, MAXBUFSIZE-1); + namebuff[MAXBUFSIZE-1] = '\0'; + len = strlen(namebuff); + if ( namebuff[0] && namebuff[len-1] != '/' ){ + if( (len+1) <= (MAXBUFSIZE-1) ){ + strcat(namebuff, "/"); + len++; + } + /* filename is too large, so we can't find it */ + else + return NULL; + } + if( len+strlen(name) <= MAXBUFSIZE-1 ) + strcat(namebuff, name); + /* filename is too large, so we can't find it */ + else + return NULL; + + if ( (found = Access(namebuff, mode)) ) + return found; + } + } + + if ( mark == ';' ) { + if ( skip ) { + skip = 0; + /* Knock down the skip mode to select all + * directories in path after the first ";" + */ + strncpy(backmode, mode, MAXBUFSIZE-1); + backmode[MAXBUFSIZE-1] = '\0'; + mode = backmode; + } + if ( pick ) return NULL; + } + + path = here; + if ( here && (here = strpbrk(here, ":;")) ) { + mark = *here; + *here++ = '\0'; + } + } + + return NULL; +} + + +/* + * + * public routines + * + */ + +/* + * + * ResolvePath -- resolve the path to remove . and .. entries + * + */ +#ifdef ANSI_FUNC +char * +ResolvePath (char *ibuf, char *obuf, int maxlen) +#else +char *ResolvePath(ibuf, obuf, maxlen) + char *ibuf; + char *obuf; + int maxlen; +#endif +{ + char path[MAXBUFSIZE]; + char *part[MAXBUFSIZE]; + char *tbuf; + int i, j; + int len; + int npart=0; + + /* if we have no path separators, we really don't have much to do! */ + if( strchr(ibuf, '/') == NULL ){ + strncpy(obuf, ibuf, maxlen-1); + obuf[maxlen-1] = '\0'; + return(obuf); + } + + /* if its just "/" or "/.", its easy */ + if( !strcmp(ibuf, "/") || !strcmp(ibuf, "/.") ){ + strncpy(obuf, "/", maxlen-1); + obuf[maxlen-1] = '\0'; + return(obuf); + } + + /* if we have a relative path to deal with, get current directory */ + if( (*ibuf == '.') || ( (strchr(ibuf, '/') != NULL) && (*ibuf != '/') ) ){ + getcwd(path, MAXBUFSIZE); + } + else{ + *path = '\0'; + } + + /* construct the total string we have to deal with */ + len = strlen(path) + strlen(ibuf) + 1; + tbuf = (char *)xmalloc(len+1); + if( *path ){ + strcpy(tbuf, path); + strcat(tbuf, "/"); + strcat(tbuf, ibuf); + } + else{ + strcpy(tbuf, ibuf); + } + + /* construct the parts array from this string, removing / characters + and null-terminating each part */ + for(i=0; i<len; i++){ + if( tbuf[i] == '/' ){ + tbuf[i] = '\0'; + /* skip adjacent slashes */ + if( tbuf[i+1] == '/' ) continue; + part[npart] = &tbuf[i+1]; + npart++; + } + } + + /* loop through the parts array and resolve the . and .. entries */ + for(i=0; i<npart; i++){ + /* for ".", just remove it */ + if( !strcmp(part[i], ".") ){ + part[i] = NULL; + } + /* for "..", also remove the previous part -- if possible */ + else if( !strcmp(part[i], "..") ){ + part[i] = NULL; + for(j=i-1; j>=0; j--){ + if( part[j] ){ + part[j] = NULL; + break; + } + } + } + } + + /* construct a new string from the remaining parts */ + *obuf = '\0'; + len = 0; + for(i=0; i<npart; i++){ + if( part[i] != NULL ){ + if( len+(int)strlen(part[i])+1 <= maxlen-1 ){ + strcat(obuf, "/"); + strcat(obuf, part[i]); + len += strlen(part[i])+1; + } + else{ + break; + } + } + } + + /* free up buffer space */ + if( tbuf ) free(tbuf); + + /* return the string */ + return(obuf); +} + +#ifdef ANSI_FUNC +void +ExpandEnv (char *name, char *envname, int maxlen) +#else +void ExpandEnv(name, envname, maxlen) + char *name; + char *envname; + int maxlen; +#endif +{ + char brace[2]; + char tbuf[MAXBUFSIZE]; + char *fullname=NULL; + char *mip; + char *ip; + char *s; + int len; + int i=0, j=0; + + /* allocate temp working buffer (so dest can be same as source) */ + if( !(fullname=(char *)xcalloc(maxlen, sizeof(char))) ) return; + + /* process each character */ + for(ip=name; *ip; ip++){ + /* if its not beginning of an env, just store and loop */ + if( *ip != '$' ){ + fullname[i++] = *ip; + fullname[i] = '\0'; + } + else{ + mip = ip; + /* skip past '$' */ + ip++; + /* skip past brace, if necessary */ + if( *ip == '{' ){ + brace[0] = '{'; + ip++; + } + else if( *ip == '(' ){ + brace[0] = '('; + ip++; + } + else + brace[0] = '\0'; + /* get variable up to next white space */ + for(*tbuf='\0', j=0; + (!isspace((int)*ip)) && (*ip != '"') && (*ip != '\'') && (*ip); + ip++){ + /* look for trailing brace, if necessary */ + if( *brace && *ip == (*brace == '(' ? ')' : '}') ){ + ip++; + break; + } + /* a "/" will end the environment variable as well */ + if( *ip == '/' ){ + break; + } + tbuf[j++] = *ip; + tbuf[j] = '\0'; + } + /* back up so we can process the white space in the outer loop */ + ip--; + if( (s = (char *)getenv(tbuf)) != NULL ){ + i += strlen(s); + if( i <= maxlen ) + strcat(fullname, s); + } + /* if we don't recognize this macro, put it back onto the string */ + else{ + len = ip - mip + 1; + i += len; + if( i <= maxlen ) + strncat(fullname, mip, len); + } + } + } + + /* transfer to output buffer */ + strncpy(envname, fullname, maxlen); + + /* free up temp space */ + if( fullname ) xfree(fullname); +} + +#ifdef ANSI_FUNC +char * +Access (char *name, char *mode) +#else +char *Access (name, mode) + char *name; + char *mode; +#endif +{ + struct stat info; + char fullname[MAXBUFSIZE]; + char AccessName[MAXBUFSIZE]; + + + ExpandEnv(name, fullname, MAXBUFSIZE); + if ( stat(fullname, &info) !=0 ) return NULL; + +#if HAVE_MINGW32==0 && HAVE_CYGWIN==0 + if ( mode ) { + int m = amparse(mode); + + /* distinguish between directories and files */ + if ( (m & D_OK) && !(info.st_mode & S_IFDIR) ) return NULL; + if ( !(m & D_OK) && (info.st_mode & S_IFDIR) ) return NULL; + + if ( getuid() == info.st_uid ) { + if ( m & R_OK && !(info.st_mode & S_IRUSR) ) return NULL; + if ( m & W_OK && !(info.st_mode & S_IWUSR) ) return NULL; + if ( m & X_OK && !(info.st_mode & S_IXUSR) ) return NULL; + } else + if ( getgid() == info.st_gid ) { + if ( m & R_OK && !(info.st_mode & S_IRGRP) ) return NULL; + if ( m & W_OK && !(info.st_mode & S_IWGRP) ) return NULL; + if ( m & X_OK && !(info.st_mode & S_IXGRP) ) return NULL; + } else { + if ( m & R_OK && !(info.st_mode & S_IROTH) ) return NULL; + if ( m & W_OK && !(info.st_mode & S_IWOTH) ) return NULL; + if ( m & X_OK && !(info.st_mode & S_IXOTH) ) return NULL; + } + } +#endif + + ResolvePath(fullname, AccessName, MAXBUFSIZE); + return(xstrdup(AccessName)); +} + +#ifdef ANSI_FUNC +char * +Find (char *name, char *mode, char *exten, char *path) +#else +char *Find (name, mode, exten, path) + char *name; + char *mode; + char *exten; + char *path; +#endif +{ + char extenbuff[MAXBUFSIZE]; + char namebuff[MAXBUFSIZE]; + char *here, *found; + int len; + + /* sanity check */ + if( !name || !*name ) + return NULL; + + /* if its a WWW file, we just say 'yes' */ + if( !strncmp(name, "ftp://", 6) || + !strncmp(name, "http://", 7) ){ + return(xstrdup(name)); + } + + if ( exten == NULL ) + return findpath(name, mode, path); + + strncpy(extenbuff, exten, MAXBUFSIZE-1); + extenbuff[MAXBUFSIZE-1] = '\0'; + exten = extenbuff; + + if ( (here = strpbrk(extenbuff, ":;")) ) *here++ = '\0'; + + while ( exten ) { + if ( exten[0] == '$' ) { + if ( (exten = (char *)getenv(&exten[1])) ) + if ( (found = Find(name, mode, exten, path)) ) + return found; + } else { + char *e = strstr(name, exten); + + strncpy(namebuff, name, MAXBUFSIZE-1); + namebuff[MAXBUFSIZE-1] = '\0'; + len = strlen(namebuff); + if ( (e==NULL) || ( e && *(e + len)) ){ + if( len+strlen(exten) <= MAXBUFSIZE-1 ) + strcat(namebuff, exten); + /* filename is too large, so we can't find it */ + else + return NULL; + } + + if ( (found = findpath(namebuff, mode, path)) ) + return found; + + } + + exten = here; + if ( here && (here = strpbrk(here, ":;")) ) *here++ = '\0'; + } + + return NULL; +} |