]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
(glob): Fix completely broken handling of GLOB_BRACE and partly borken
authorUlrich Drepper <drepper@redhat.com>
Fri, 21 Mar 1997 04:55:48 +0000 (04:55 +0000)
committerUlrich Drepper <drepper@redhat.com>
Fri, 21 Mar 1997 04:55:48 +0000 (04:55 +0000)
handling of GLOB_TILDE:

posix/glob.c

index ac26a1af217091b129702e9311cd2b10da431117..86a79b08a1bee7b833d5de428b94fdbc59f8ea79 100644 (file)
@@ -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
 #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.  */