/* * Copyright (c) 1990 Rene' Seindal * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ /* * Generalised to allow multiple types of directory structures * by Philip G. Richards 1992; all these modifications are copyright * Philip G. Richards, 1992. All rights reserved. * * Both redistribution, use, and warranty are as above. */ #include "client.h" #include #include #ifdef TEST #define ffprintf (void)fprintf static FILE *STDDBG = STDERR; #endif static char *real_getname(VOIDDIRENT *dp); static VOIDDIR *real_opendir(char *dirname); static void real_closedir(VOIDDIR *dirp); static VOIDDIRENT *real_readdir(VOIDDIR *dirp); static int real_stat(const char *buf, struct stat *s); static VOIDDIR *(*OPENDIR)(char *) = real_opendir; static void (*CLOSEDIR)(VOIDDIR *) = real_closedir; static VOIDDIRENT *(*READDIR)(VOIDDIR *) = real_readdir; static char *(*GETNAME)(VOIDDIRENT *) = real_getname; static int (*GLOBSTAT)(const char *, struct stat *) = real_stat; static void matchdir(char *path_end, char **gpat); /* non-simple globbing */ static void do_glob(char *path_end, char **gpat); /* do simple globbing, on segment at a time */ static void add_name(void); /* add a name to namelist */ static int split_pat(char *pattern, char **table); /* split pattern into segments */ /* glob_match matches pattern against string according to the normal * rules for shell globbing. It returns SUCCES if string matches * pattern, FAIL if it doesn't, and ERROR if pattern is illformed. * * To be more specific, a pattern can be: * ? which matches any single character * * which matches zero or more character * [...] which matches any single character in ... * [^...] which matches any single character not in ... * \x where x is any character, matches that character literally * x where x is any character except ? * [ and \, matches x * * Character within [...] can include single characters and ranges of * the form x-y, which matches any characters in the ranges x - y * inclusive. It is considered an error is y < x. An ] can be included * as the very first character in ..., and a - as the first (after a * possibly [) or last character in ... */ #define PSUCCES 2 #define SUCCES 1 #define FAIL 0 #define ERROR 0 #define NEXTP {if ( (p = *++pattern) == '\0') return ERROR;} int glob_match(const char *pattern, char *string) { char p; char seenstar = '\0'; for ( ; (p = *pattern); pattern++ ) { if ( p == '\\' ) { NEXTP; /* matches next char literally (but not \0) */ if ( p != *string++ ) return FAIL; /* string too short */ continue; } else if ( p == '?' ) { if ( *string++ ) /* matches any character */ continue; else return FAIL; /* string too short */ } else if ( p == '*' ) { seenstar = '\1'; continue; } else { if ( seenstar ) { int tmp; while ( *string ) { tmp = glob_match( pattern, string ); if ( tmp != FAIL ) return tmp; string++; } return FAIL; /* NOTREACHED */ } if ( p == '\0' ) return FAIL; if ( p == '[' ) { char s = *string++; char reverse = '\0'; char first, last; char gotcha = '\0'; NEXTP; if ( p == '^' ) { reverse = '\1'; NEXTP; } if ( p == ']' ) { /* special case */ gotcha = (s==p); NEXTP; } while ( p != ']' && !gotcha ) { first = p; NEXTP; if ( p == '-' && pattern[1] != ']' ) { NEXTP; last = p; NEXTP; } else last = first; if ( first > last ) return ERROR; gotcha = (first <= s && s <= last ); } while ( p != ']' ) NEXTP; if ( reverse ? gotcha : !gotcha ) return FAIL; } else if ( p != *string ) return FAIL; else string++; } } if ( seenstar ) return SUCCES; if ( *string ) return FAIL; return SUCCES; } static char *main_path; /* ptr to scratchpad */ static int offset; /* no of leading char in main_path to ignore */ static char **namelist; /* name list buildup */ static int nnames; /* no of names found */ static int left; /* no of slots allocated but not used yet */ #define GLOBMAXSEG 50 /* max segments in pattern */ #define GLOBCHUNK 20 /* no of slots to allocate at a time */ #ifndef MAXNAMLEN #define MAXNAMLEN 256 #endif int glob_path(const char *pattern, char ***names) { char mpath[ MAXPATHLEN + MAXNAMLEN + 1 ]; char *gpat[GLOBMAXSEG]; char *pat; if (pattern == 0) return -1; if ((pat = strdup(pattern)) == NULL) return -1; if (split_pat(pat, gpat) < 0) { (void)free(pat); return -1; } main_path = mpath; /* initalisation of static storage */ namelist = 0; nnames = left = 0; if ( *gpat && **gpat == '/' ) { main_path[0] = '/'; main_path[1] = '\0'; offset = 0; do_glob(main_path, gpat + 1); } else { main_path[0] = '.'; main_path[1] = '\0'; offset = 2; do_glob(main_path + 1, gpat); } (void)free(pat); if (namelist == 0) *names = (char **)malloc(sizeof(char *)); else *names = (char **)realloc(namelist, (nnames+1) * sizeof(char *)); if (*names == 0) return -1; (*names)[nnames] = 0; return nnames; } static int split_pat(char *pattern, char **table) { char *pp = pattern; int size = GLOBMAXSEG; if (*pattern == '/') { *table++ = "/"; --size; } do { while (*pp == '/') *pp++ = '\0'; if (*pp == '\0') break; if (--size < 0) return -1; *table++ = pp; while (*pp && *pp != '/') pp++; } while (*pp); *table = 0; return 0; } #define ISGLOB(x) ((x)=='*' || (x)=='?' || (x)=='[') static int no_glob(char *pat) { while (*pat && !ISGLOB(*pat)) pat++; return (*pat == '\0'); } static void do_glob(char *path_end, char **gpat) /* ptr to the end of main_path */ /* the rest of the pattern segments */ { char *saved_end = path_end; /* saved to be resored */ char *pat; /* current pattern segment */ struct stat st; /* to check if file exists */ #ifdef GLOBDEBUG ffprintf(STDDBG,"do_glob: path = '%s', pat = '%s'\n", main_path, *gpat ); #endif for ( ; (pat = *gpat) != 0 && no_glob(pat); gpat++ ) { #ifdef GLOBDEBUG ffprintf(STDDBG,"no_glob: path = '%s', pat = '%s'\n", main_path, pat ); #endif *path_end = '/'; (void)strcpy(path_end+1, pat); path_end += strlen(pat) + 1; if (GLOBSTAT(main_path, &st) != 0 ) { *saved_end = '\0'; return; } } if (pat) matchdir(path_end, gpat); else add_name(); *saved_end = '\0'; return; } static void matchdir(char *path_end, char **gpat) /* ptr to end of main_path */ /* the rest of the pattern segments */ { char *x; /* scratch */ VOIDDIR *dirp; /* for directory reading */ VOIDDIRENT *dp; /* directory entry */ struct stat st; /* to determine files type */ #ifdef GLOBDEBUG ffprintf(STDDBG,"matchdir: path = '%s', pat = '%s'\n", main_path, *gpat ); #endif if ((dirp = OPENDIR(main_path)) == NULL) return; *path_end = '/'; while ((dp = READDIR(dirp)) != NULL) { char *dirname; x = dirname = GETNAME(dp); /* was dp->d_name */ if (*x == '.' && (*++x == '\0' || (*x == '.' && *++x == '\0'))) continue; if (*dirname == '.' && **gpat != '.') continue; (void)strcpy(path_end + 1, dirname); if (glob_match(*gpat, dirname)) { /* this is a match */ if ( *(gpat+1) == 0 ) { /* and it is the last */ add_name(); /* so eat it */ continue; } if (GLOBSTAT(main_path, &st) == 0 /* else not the last */ && (st.st_mode & S_IFMT) == S_IFDIR) do_glob(path_end + strlen(dirname) + 1, gpat + 1); } } (void)CLOSEDIR(dirp); *path_end = '\0'; } static void add_name(void) { char *np; #ifdef GLOBDEBUG ffprintf(STDDBG,"Globbed: %s\n", main_path+offset); #endif if (--left <= 0) { if (namelist == 0) namelist = (char**)malloc(GLOBCHUNK * sizeof(char*)); else namelist = (char**)realloc(namelist, (nnames+GLOBCHUNK)*sizeof(char*)); if (namelist == NULL) return; left = GLOBCHUNK; } if ((np = strdup(main_path + offset)) == 0) return; namelist[nnames++] = np; } /* the following is not general purpose code. */ const char *globerr; char globerr_buf[1024]; char *home = 0; /* extern int qsort(); */ static int name_cmp(char **s1, char **s2) { return strcmp(*s1, *s2); } char * expand_tilde(char *pat) { static char buf[BUFSIZ]; struct passwd *pw; char *tmp; char *bp; if (*pat != '~') return 0; pat++; if (*pat && *pat != '/') { bp = buf; for (tmp = pat; *tmp && *tmp != '/'; ) *bp++ = *tmp++; *bp = '\0'; pw = getpwnam(buf); if (pw == 0) { (void)sprintf(globerr_buf, "%s: Unknown user.", pat); globerr = globerr_buf; return 0; } pat = tmp ? tmp : ""; tmp = pw->pw_dir; } else { if (*pat) pat++; tmp = (char *)getenv("HOME"); } for (bp = buf; *tmp; ) *bp++ = *tmp++; *bp++ = '/'; while (*pat) *bp++ = *pat++; *bp = '\0'; return buf; } const char ** glob(const char *pat) { char **names; int nnames; if (*pat == '~') { pat = expand_tilde(pat); if (pat == 0) return 0; } nnames = glob_path(pat, &names); switch (nnames) { case -1: globerr = strerror(errno); return 0; case 0: globerr = "No match."; return 0; default: qsort(names, nnames, sizeof(char *), name_cmp); return names; } } void free_glob(char **argv) { char **a; if (argv == (char**)0) return; for (a = argv; *a; a++) (void)free(*a); (void)free((char*)argv); } void set_glob_routines(VOIDDIR *(*dopen)(char *), void (*dclose)(VOIDDIR *), VOIDDIRENT *(*dread)(VOIDDIR *), char *(*dgetname)(VOIDDIRENT *), int (*dstat)(const char *, struct stat *)) { OPENDIR = dopen; CLOSEDIR = dclose; GETNAME = dgetname; READDIR = dread; GLOBSTAT = dstat; } static char *real_getname(VOIDDIRENT *dp) { return ((struct dirent *)dp)->d_name; } /***************************************************************************** * wrappers for the real functions *****************************************************************************/ static VOIDDIR * real_opendir(char *dirname) { return (VOIDDIR*)opendir(dirname); } static void real_closedir(VOIDDIR *dirp) { (void)closedir((DIR*)dirp); } static VOIDDIRENT * real_readdir(VOIDDIR *dirp) { return (VOIDDIRENT*)readdir((DIR*)dirp); } static int real_stat(const char *buf, struct stat *s) { return stat(buf, s); } void local_glob_routines(void) { set_glob_routines(real_opendir, real_closedir, real_readdir, real_getname, real_stat); } #ifndef TEST /***************************************************************************** * wrappers for the FSP remote functions *****************************************************************************/ static char * my_getname(VOIDDIRENT *dp) { return ((struct rdirent *)dp)->rd_name; } static VOIDDIR * my_opendir(char *dirname) { return (VOIDDIR*)util_opendir(dirname); } static void my_closedir(VOIDDIR *dirp) { (void)util_closedir((RDIR*)dirp); } static VOIDDIRENT * my_readdir(VOIDDIR *dirp) { return (VOIDDIRENT*)util_readdir((RDIR*)dirp); } void remote_glob_routines(void) { set_glob_routines(my_opendir,my_closedir,my_readdir,my_getname,util_stat); } #endif #ifdef TEST int main(int argc, char *argv[]) { char **names; globerr = 0; home = getenv("HOME"); names = glob(argv[1]); if (names == 0) { ffprintf(STDERR, "glob error: %s\n", globerr); return 1; } while (*names) ffprintf(STDOUT,"%s\n", *names++); return 0; } #endif