diff options
Diffstat (limited to 'posix/glob.c')
-rw-r--r-- | posix/glob.c | 294 |
1 files changed, 187 insertions, 107 deletions
diff --git a/posix/glob.c b/posix/glob.c index ac26a1af21..86a79b08a1 100644 --- a/posix/glob.c +++ b/posix/glob.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1991, 92, 93, 94, 95, 96 Free Software Foundation, Inc. +/* Copyright (C) 1991, 92, 93, 94, 95, 96, 97 Free Software Foundation, Inc. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as @@ -33,6 +33,10 @@ #include <sys/types.h> #include <sys/stat.h> +/* Outcomment the following line for production quality code. */ +/* #define NDEBUG 1 */ +#include <assert.h> + /* Comment out all this code if we are using the GNU C Library, and are not actually compiling the library itself. This code is part of the GNU C @@ -159,7 +163,7 @@ extern void bcopy (); ((void) ((better_be_zero) == 0 ? (bzero((s), (n)), 0) : (abort(), 0))) #endif /* Not ANSI_STRING. */ -#ifndef HAVE_STRCOLL +#if !defined HAVE_STRCOLL && !defined _LIBC #define strcoll strcmp #endif @@ -254,6 +258,51 @@ static int glob_in_dir __P ((const char *pattern, const char *directory, static int prefix_array __P ((const char *prefix, char **array, size_t n)); static int collated_compare __P ((const __ptr_t, const __ptr_t)); + +/* Find the end of the sub-pattern in a brace expression. We define + this as an inline function if the compiler permits. */ +static +#if __GNUC__ - 0 >= 2 +inline +#endif +const char * +next_brace_sub (const char *begin) +{ + unsigned int depth = 0; + const char *cp = begin; + + while (1) + { + if (depth == 0) + { + if (*cp != ',' && *cp != '}' && *cp != '\0') + { + if (*cp == '{') + ++depth; + ++cp; + continue; + } + } + else + { + while (*cp != '\0' && (*cp != '}' || depth > 0)) + { + if (*cp == '}') + ++depth; + ++cp; + } + if (*cp == '\0') + /* An incorrectly terminated brace expression. */ + return NULL; + + continue; + } + break; + } + + return cp; +} + /* Do glob searching for PATTERN, placing results in PGLOB. The bits defined above may be set in FLAGS. If a directory cannot be opened or read and ERRFUNC is not nil, @@ -286,38 +335,59 @@ glob (pattern, flags, errfunc, pglob) const char *begin = strchr (pattern, '{'); if (begin != NULL) { + /* Allocate working buffer large enough for our work. Note that + we have at least an opening and closing brace. */ int firstc; - size_t restlen; - const char *p, *end, *next; - unsigned int depth = 0; - - /* Find the end of the brace expression, by counting braces. - While we're at it, notice the first comma at top brace level. */ - end = begin + 1; - next = NULL; - while (1) + char *alt_start; + const char *p; + const char *next; + const char *rest; + size_t rest_len; +#ifdef __GNUC__ + char onealt[strlen (pattern) - 1]; +#else + char *onealt = (char *) malloc (strlen (pattern) - 1); + if (onealt == NULL) { - switch (*end++) + if (!(flags & GLOB_APPEND)) + globfree (pglob); + return GLOB_NOSPACE; + } +#endif + + /* We know the prefix for all sub-patterns. */ + memcpy (onealt, pattern, begin - pattern); + alt_start = &onealt[begin - pattern]; + + /* Find the first sub-pattern and at the same time find the + rest after the closing brace. */ + next = next_brace_sub (begin + 1); + if (next == NULL) + { + /* It is an illegal expression. */ +#ifndef __GNUC__ + free (onealt); +#endif + return glob (pattern, flags & ~GLOB_BRACE, errfunc, pglob); + } + + /* Now find the end of the whole brace expression. */ + rest = next; + while (*rest != '}') + { + rest = next_brace_sub (rest + 1); + if (rest == NULL) { - case ',': - if (depth == 0 && next == NULL) - next = end; - continue; - case '{': - ++depth; - continue; - case '}': - if (depth-- == 0) - break; - continue; - case '\0': - return glob (pattern, flags &~ GLOB_BRACE, errfunc, pglob); + /* It is an illegal expression. */ +#ifndef __GNUC__ + free (onealt); +#endif + return glob (pattern, flags & ~GLOB_BRACE, errfunc, pglob); } - break; } - restlen = strlen (end) + 1; - if (next == NULL) - next = end; + /* Please note that we now can be sure the brace expression + is well-formed. */ + rest_len = strlen (++rest) + 1; /* We have a brace expression. BEGIN points to the opening {, NEXT points past the terminator of the first element, and END @@ -334,72 +404,47 @@ glob (pattern, flags, errfunc, pglob) } firstc = pglob->gl_pathc; - /* In this loop P points to the beginning of the current element - and NEXT points past its terminator. */ p = begin + 1; while (1) { - /* Construct a whole name that is one of the brace - alternatives in a temporary buffer. */ int result; - size_t bufsz = (begin - pattern) + (next - 1 - p) + restlen; -#ifdef __GNUC__ - char onealt[bufsz]; -#else - char *onealt = malloc (bufsz); - if (onealt == NULL) - { - if (!(flags & GLOB_APPEND)) - globfree (pglob); - return GLOB_NOSPACE; - } -#endif - memcpy (onealt, pattern, begin - pattern); - memcpy (&onealt[begin - pattern], p, next - 1 - p); - memcpy (&onealt[(begin - pattern) + (next - 1 - p)], - end, restlen); + + /* Construct the new glob expression. */ + memcpy (alt_start, p, next - p); + memcpy (&alt_start[next - p], rest, rest_len); + result = glob (onealt, - ((flags & ~(GLOB_NOCHECK|GLOB_NOMAGIC)) | - GLOB_APPEND), errfunc, pglob); -#ifndef __GNUC__ - free (onealt); -#endif + ((flags & ~(GLOB_NOCHECK|GLOB_NOMAGIC)) + | GLOB_APPEND), errfunc, pglob); /* If we got an error, return it. */ if (result && result != GLOB_NOMATCH) { +#ifndef __GNUC__ + free (onealt); +#endif if (!(flags & GLOB_APPEND)) globfree (pglob); return result; } - /* Advance past this alternative and process the next. */ - p = next; - depth = 0; - scan: - switch (*p++) - { - case ',': - if (depth == 0) - { - /* Found the next alternative. Loop to glob it. */ - next = p; - continue; - } - goto scan; - case '{': - ++depth; - goto scan; - case '}': - if (depth-- == 0) - /* End of the brace expression. Break out of the loop. */ - break; - goto scan; - } + if (*next == '}') + /* We saw the last entry. */ + break; + + p = next + 1; + next = next_brace_sub (p); + assert (next != NULL); } - if (pglob->gl_pathc == firstc && - !(flags & (GLOB_NOCHECK|GLOB_NOMAGIC))) +#ifndef __GNUC__ + free (onealt); +#endif + + if (pglob->gl_pathc != firstc) + /* We found some entries. */ + return 0; + else if (!(flags & (GLOB_NOCHECK|GLOB_NOMAGIC))) return GLOB_NOMATCH; } } @@ -452,19 +497,19 @@ glob (pattern, flags, errfunc, pglob) #ifndef VMS if ((flags & GLOB_TILDE) && dirname[0] == '~') { - if (dirname[1] == '\0') + if (dirname[1] == '\0' || dirname[1] == '/') { /* Look up home directory. */ - dirname = getenv ("HOME"); + char *home_dir = getenv ("HOME"); #ifdef _AMIGA - if (dirname == NULL || dirname[0] == '\0') - dirname = "SYS:"; + if (home_dir == NULL || home_dir[0] == '\0') + home_dir = "SYS:"; #else #ifdef WIN32 - if (dirname == NULL || dirname[0] == '\0') - dirname = "c:/users/default"; /* poor default */ + if (home_dir == NULL || home_dir[0] == '\0') + home_dir = "c:/users/default"; /* poor default */ #else - if (dirname == NULL || dirname[0] == '\0') + if (home_dir == NULL || home_dir[0] == '\0') { extern char *getlogin __P ((void)); extern int getlogin_r __P ((char *, size_t)); @@ -501,39 +546,74 @@ glob (pattern, flags, errfunc, pglob) success = p != NULL; #endif if (success) - dirname = p->pw_dir; + home_dir = p->pw_dir; } } - if (dirname == NULL || dirname[0] == '\0') - dirname = (char *) "~"; /* No luck. */ + if (home_dir == NULL || home_dir[0] == '\0') + home_dir = (char *) "~"; /* No luck. */ #endif /* WIN32 */ #endif + /* Now construct the full directory. */ + if (dirname[1] == '\0') + dirname = home_dir; + else + { + char *newp; + size_t home_len = strlen (home_dir); + newp = __alloca (home_len + dirlen); + memcpy (newp, home_dir, home_len); + memcpy (&newp[home_len], &dirname[1], dirlen); + dirname = newp; + } } +#if !defined _AMIGA && !defined WIN32 else { -#ifdef _AMIGA - if (dirname == NULL || dirname[0] == '\0') - dirname = "SYS:"; -#else -#ifdef WIN32 - if (dirname == NULL || dirname[0] == '\0') - dirname = "c:/users/default"; /* poor default */ -#else + char *end_name = strchr (dirname, '/'); + char *user_name; + char *home_dir; + + if (end_name == NULL) + user_name = dirname + 1; + else + { + user_name = __alloca (end_name - dirname); + memcpy (user_name, dirname + 1, end_name - dirname); + user_name[end_name - dirname - 1] = '\0'; + } + /* Look up specific user's home directory. */ + { #if defined HAVE_GETPWNAM_R || defined _LIBC - size_t buflen = sysconf (_SC_GETPW_R_SIZE_MAX); - char *pwtmpbuf = __alloca (buflen); - struct passwd pwbuf, *p; - if (__getpwnam_r (dirname + 1, &pwbuf, pwtmpbuf, buflen, &p) >= 0) - dirname = p->pw_dir; + size_t buflen = sysconf (_SC_GETPW_R_SIZE_MAX); + char *pwtmpbuf = __alloca (buflen); + struct passwd pwbuf, *p; + if (__getpwnam_r (user_name, &pwbuf, pwtmpbuf, buflen, &p) >= 0) + home_dir = p->pw_dir; + else + home_dir = NULL; #else - struct passwd *p = getpwnam (dirname + 1); - if (p != NULL) - dirname = p->pw_dir; -#endif -#endif /* WIN32 */ + struct passwd *p = getpwnam (user_name); + if (p != NULL) + home_dir = p->pw_dir; + else + home_dir = NULL; #endif + } + /* If we found a home directory use this. */ + if (home_dir != NULL) + { + char *newp; + size_t home_len = strlen (home_dir); + size_t rest_len = end_name == NULL ? 0 : strlen (end_name); + newp = __alloca (home_len + rest_len + 1); + memcpy (newp, home_dir, home_len); + memcpy (&newp[home_len], end_name, rest_len); + newp[home_len + rest_len] = '\0'; + dirname = newp; + } } +#endif /* Not Amiga && not Win32. */ } #endif /* Not VMS. */ |