about summary refs log tree commit diff
path: root/posix
diff options
context:
space:
mode:
Diffstat (limited to 'posix')
-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.  */