summary refs log tree commit diff
path: root/posix/glob.c
diff options
context:
space:
mode:
Diffstat (limited to 'posix/glob.c')
-rw-r--r--posix/glob.c139
1 files changed, 106 insertions, 33 deletions
diff --git a/posix/glob.c b/posix/glob.c
index 8646bba570..eea126d800 100644
--- a/posix/glob.c
+++ b/posix/glob.c
@@ -268,48 +268,121 @@ glob (pattern, flags, errfunc, pglob)
       const char *begin = strchr (pattern, '{');
       if (begin != NULL)
 	{
-	  const char *end = strchr (begin + 1, '}');
-	  if (end != NULL && end != begin + 1)
+	  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)
 	    {
-	      size_t restlen = strlen (end + 1) + 1;
-	      const char *p, *comma;
-	      char *buf;
-	      size_t bufsz = 0;
-	      int firstc;
-	      if (!(flags & GLOB_APPEND))
+	      switch (*end++)
 		{
-		  pglob->gl_pathc = 0;
-		  pglob->gl_pathv = 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);
 		}
-	      firstc = pglob->gl_pathc;
-	      for (p = begin + 1;; p = comma + 1)
+	      break;
+	    }
+	  restlen = strlen (end) + 1;
+	  if (next == NULL)
+	    next = end;
+
+	  /* We have a brace expression.  BEGIN points to the opening {,
+	     NEXT points past the terminator of the first element, and END
+	     points past the final }.  We will accumulate result names from
+	     recursive runs for each brace alternative in the buffer using
+	     GLOB_APPEND.  */
+
+	  if (!(flags & GLOB_APPEND))
+	    {
+	      /* This call is to set a new vector, so clear out the
+		 vector so we can append to it.  */
+	      pglob->gl_pathc = 0;
+	      pglob->gl_pathv = NULL;
+	    }
+	  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)
 		{
-		  int result;
-		  comma = strchr (p, ',');
-		  if (comma == NULL)
-		    comma = strchr (p, '\0');
-		  if ((begin - pattern) + (comma - p) + 1 > bufsz)
+		  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);
+	      result = glob (onealt,
+			     ((flags & ~(GLOB_NOCHECK|GLOB_NOMAGIC)) |
+			      GLOB_APPEND), errfunc, pglob);
+#ifndef __GNUC__
+	      free (onealt);
+#endif
+
+	      /* If we got an error, return it.  */
+	      if (result && result != GLOB_NOMATCH)
+		{
+		  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)
 		    {
-		      if (bufsz * 2 < comma - p + 1)
-			bufsz *= 2;
-		      else
-			bufsz = comma - p + 1;
-		      buf = __alloca (bufsz);
+		      /* Found the next alternative.  Loop to glob it.  */
+		      next = p;
+		      continue;
 		    }
-		  memcpy (buf, pattern, begin - pattern);
-		  memcpy (buf + (begin - pattern), p, comma - p);
-		  memcpy (buf + (begin - pattern) + (comma - p), end, restlen);
-		  result = glob (buf, ((flags & ~(GLOB_NOCHECK|GLOB_NOMAGIC)) |
-				       GLOB_APPEND), errfunc, pglob);
-		  if (result && result != GLOB_NOMATCH)
-		    return result;
-		  if (*comma == '\0')
+		  goto scan;
+		case '{':
+		  ++depth;
+		  goto scan;
+		case '}':
+		  if (depth-- == 0)
+		    /* End of the brace expression.  Break out of the loop.  */
 		    break;
+		  goto scan;
 		}
-	      if (pglob->gl_pathc == firstc &&
-		  !(flags & (GLOB_NOCHECK|GLOB_NOMAGIC)))
-		return GLOB_NOMATCH;
 	    }
+
+	  if (pglob->gl_pathc == firstc &&
+	      !(flags & (GLOB_NOCHECK|GLOB_NOMAGIC)))
+	    return GLOB_NOMATCH;
 	}
     }