aboutsummaryrefslogtreecommitdiff
path: root/posix/glob.c
diff options
context:
space:
mode:
Diffstat (limited to 'posix/glob.c')
-rw-r--r--posix/glob.c294
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. */