Imported from ../bash-3.0.16.tar.gz.
[platform/upstream/bash.git] / braces.c
index a5558b4..4d229ca 100644 (file)
--- a/braces.c
+++ b/braces.c
@@ -1,6 +1,6 @@
 /* braces.c -- code for doing word expansion in curly braces. */
 
-/* Copyright (C) 1987,1991 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2003 Free Software Foundation, Inc.
 
    This file is part of GNU Bash, the Bourne Again SHell.
 
 #endif /* SHELL */
 
 #include "general.h"
+#include "shmbutil.h"
+#include "chartypes.h"
+
 #define brace_whitespace(c) (!(c) || (c) == ' ' || (c) == '\t' || (c) == '\n')
 
-#if defined (SHELL)
-extern char *extract_command_subst ();
-#endif
+#define BRACE_SEQ_SPECIFIER    ".."
 
 /* Basic idea:
 
@@ -56,8 +57,19 @@ extern char *extract_command_subst ();
 /* The character which is used to separate arguments. */
 int brace_arg_separator = ',';
 
+#if defined (__P)
+static int brace_gobbler __P((char *, size_t, int *, int));
+static char **expand_amble __P((char *, size_t, int));
+static char **expand_seqterm __P((char *, size_t));
+static char **mkseq __P((int, int, int));
+static char **array_concat __P((char **, char **));
+#else
 static int brace_gobbler ();
-static char **expand_amble (), **array_concat ();
+static char **expand_amble ();
+static char **expand_seqterm ();
+static char **mkseq();
+static char **array_concat ();
+#endif
 
 /* Return an array of strings; the brace expansion of TEXT. */
 char **
@@ -65,13 +77,18 @@ brace_expand (text)
      char *text;
 {
   register int start;
+  size_t tlen;
   char *preamble, *postamble, *amble;
+  size_t alen;
   char **tack, **result;
   int i, j, c;
 
+  DECLARE_MBSTATE;
+
   /* Find the text of the preamble. */
+  tlen = strlen (text);
   i = 0;
-  c = brace_gobbler (text, &i, '{');
+  c = brace_gobbler (text, tlen, &i, '{');
 
   preamble = (char *)xmalloc (i + 1);
   strncpy (preamble, text, i);
@@ -88,7 +105,7 @@ brace_expand (text)
 
   /* Find the amble.  This is the stuff inside this set of braces. */
   start = ++i;
-  c = brace_gobbler (text, &i, '}');
+  c = brace_gobbler (text, tlen, &i, '}');
 
   /* What if there isn't a matching close brace? */
   if (c == 0)
@@ -96,20 +113,23 @@ brace_expand (text)
 #if defined (NOTDEF)
       /* Well, if we found an unquoted BRACE_ARG_SEPARATOR between START
         and I, then this should be an error.  Otherwise, it isn't. */
-      for (j = start; j < i; j++)
+      j = start;
+      while (j < i)
        {
          if (text[j] == '\\')
            {
              j++;
+             ADVANCE_CHAR (text, tlen, j);
              continue;
            }
 
          if (text[j] == brace_arg_separator)
-           {
-             free_array (result);
-             report_error ("missing `}'");
+           {   /* { */
+             strvec_dispose (result);
+             report_error ("no closing `%c' in %s", '}', text);
              throw_to_top_level ();
            }
+         ADVANCE_CHAR (text, tlen, j);
        }
 #endif
       free (preamble);         /* Same as result[0]; see initialization. */
@@ -119,45 +139,61 @@ brace_expand (text)
 
 #if defined (SHELL)
   amble = substring (text, start, i);
+  alen = i - start;
 #else
   amble = (char *)xmalloc (1 + (i - start));
   strncpy (amble, &text[start], (i - start));
-  amble[i - start] = '\0';
+  alen = i - start;
+  amble[alen] = '\0';
 #endif
 
 #if defined (SHELL)
+  INITIALIZE_MBSTATE;
+
   /* If the amble does not contain an unquoted BRACE_ARG_SEPARATOR, then
      just return without doing any expansion.  */
-  for (j = 0; amble[j]; j++)
+  j = 0;
+  while (amble[j])
     {
       if (amble[j] == '\\')
        {
          j++;
+         ADVANCE_CHAR (amble, alen, j);
          continue;
        }
+
       if (amble[j] == brace_arg_separator)
        break;
+
+      ADVANCE_CHAR (amble, alen, j);
     }
 
-  if (!amble[j])
+  if (amble[j] == 0)
     {
-      free (amble);
-      free (preamble);
-      result[0] = savestring (text);
-      return (result);
+      tack = expand_seqterm (amble, alen);
+      if (tack)
+       goto add_tack;
+      else
+       {
+         free (amble);
+         free (preamble);
+         result[0] = savestring (text);
+         return (result);
+       }
     }
 #endif /* SHELL */
 
-  postamble = &text[i + 1];
-
-  tack = expand_amble (amble);
+  tack = expand_amble (amble, alen, 0);
+add_tack:
   result = array_concat (result, tack);
   free (amble);
-  free_array (tack);
+  strvec_dispose (tack);
+
+  postamble = text + i + 1;
 
   tack = brace_expand (postamble);
   result = array_concat (result, tack);
-  free_array (tack);
+  strvec_dispose (tack);
 
   return (result);
 }
@@ -167,18 +203,24 @@ brace_expand (text)
    expand each slot which needs it, until there are no more slots which
    need it. */
 static char **
-expand_amble (text)
+expand_amble (text, tlen, flags)
      char *text;
+     size_t tlen;
+     int flags;
 {
   char **result, **partial;
   char *tem;
   int start, i, c;
 
+  DECLARE_MBSTATE;
+
   result = (char **)NULL;
 
-  for (start = 0, i = 0, c = 1; c; start = ++i)
+  start = i = 0;
+  c = 1;
+  while (c)
     {
-      c = brace_gobbler (text, &i, brace_arg_separator);
+      c = brace_gobbler (text, tlen, &i, brace_arg_separator);
 #if defined (SHELL)
       tem = substring (text, start, i);
 #else
@@ -193,11 +235,12 @@ expand_amble (text)
        result = partial;
       else
        {
-         register int lr = array_len (result);
-         register int lp = array_len (partial);
-         register int j;
+         register int lr, lp, j;
 
-         result = (char **)xrealloc (result, (1 + lp + lr) * sizeof (char *));
+         lr = strvec_len (result);
+         lp = strvec_len (partial);
+
+         result = strvec_resize (result, lp + lr + 1);
 
          for (j = 0; j < lp; j++)
            result[lr + j] = partial[j];
@@ -206,7 +249,111 @@ expand_amble (text)
          free (partial);
        }
       free (tem);
+      ADVANCE_CHAR (text, tlen, i);
+      start = i;
+    }
+  return (result);
+}
+
+#define ST_BAD 0
+#define ST_INT 1
+#define ST_CHAR        2
+
+static char **
+mkseq (start, end, type)
+     int start, end, type;
+{
+  int n, incr, i;
+  char **result, *t;
+
+  n = abs (end - start) + 1;
+  result = strvec_create (n + 1);
+
+  incr = (start < end) ? 1 : -1;
+
+  /* Make sure we go through the loop at least once, so {3..3} prints `3' */
+  i = 0;
+  n = start;
+  do
+    {
+      if (type == ST_INT)
+       result[i++] = itos (n);
+      else
+       {
+         t = (char *)xmalloc (2);
+         t[0] = n;
+         t[1] = '\0';
+         result[i++] = t;
+       }
+      if (n == end)
+        break;
+      n += incr;
+    }
+  while (1);
+
+  result[i] = (char *)0;
+  return (result);
+}
+
+static char **
+expand_seqterm (text, tlen)
+     char *text;
+     size_t tlen;
+{
+  char *t, *lhs, *rhs;
+  int i, lhs_t, rhs_t, lhs_v, rhs_v;
+  intmax_t tl, tr;
+  char **result;
+
+  t = strstr (text, BRACE_SEQ_SPECIFIER);
+  if (t == 0)
+    return ((char **)NULL);
+
+  i = t - text;                /* index of start of BRACE_SEQ_SPECIFIER */
+  lhs = substring (text, 0, i);
+  rhs = substring (text, i + sizeof(BRACE_SEQ_SPECIFIER) - 1, tlen);
+
+  if (lhs[0] == 0 || rhs[0] == 0)
+    {
+      free (lhs);
+      free (rhs);
+      return ((char **)NULL);
+    }
+
+  /* Now figure out whether LHS and RHS are integers or letters.  Both
+     sides have to match. */
+  lhs_t = (legal_number (lhs, &tl)) ? ST_INT :
+               ((ISALPHA (lhs[0]) && lhs[1] == 0) ?  ST_CHAR : ST_BAD);
+  rhs_t = (legal_number (rhs, &tr)) ? ST_INT :
+               ((ISALPHA (rhs[0]) && rhs[1] == 0) ?  ST_CHAR : ST_BAD);
+
+  if (lhs_t != rhs_t || lhs_t == ST_BAD || rhs_t == ST_BAD)
+    {
+      free (lhs);
+      free (rhs);
+      return ((char **)NULL);
+    }
+
+  /* OK, we have something.  It's either a sequence of integers, ascending
+     or descending, or a sequence or letters, ditto.  Generate the sequence,
+     put it into a string vector, and return it. */
+  
+  if (lhs_t == ST_CHAR)
+    {
+      lhs_v = (unsigned char)lhs[0];
+      rhs_v = (unsigned char)rhs[0];
+    }
+  else
+    {
+      lhs_v = tl;              /* integer truncation */
+      rhs_v = tr;
     }
+
+  result = mkseq (lhs_v, rhs_v, lhs_t);
+
+  free (lhs);
+  free (rhs);
+
   return (result);
 }
 
@@ -215,8 +362,9 @@ expand_amble (text)
    quoting.  Return the character that caused us to stop searching;
    this is either the same as SATISFY, or 0. */
 static int
-brace_gobbler (text, indx, satisfy)
+brace_gobbler (text, tlen, indx, satisfy)
      char *text;
+     size_t tlen;
      int *indx;
      int satisfy;
 {
@@ -225,14 +373,17 @@ brace_gobbler (text, indx, satisfy)
   int si;
   char *t;
 #endif
+  DECLARE_MBSTATE;
 
   level = quoted = pass_next = 0;
 
-  for (i = *indx; c = text[i]; i++)
+  i = *indx;
+  while (c = text[i])
     {
       if (pass_next)
        {
          pass_next = 0;
+         ADVANCE_CHAR (text, tlen, i);
          continue;
        }
 
@@ -241,19 +392,34 @@ brace_gobbler (text, indx, satisfy)
       if (c == '\\' && (quoted == 0 || quoted == '"' || quoted == '`'))
        {
          pass_next = 1;
+         i++;
+         continue;
+       }
+
+#if defined (SHELL)
+      /* If compiling for the shell, treat ${...} like \{...} */
+      if (c == '$' && text[i+1] == '{' && quoted != '\'')              /* } */
+       {
+         pass_next = 1;
+         i++;
+         if (quoted == 0)
+           level++;
          continue;
        }
+#endif
 
       if (quoted)
        {
          if (c == quoted)
            quoted = 0;
+         ADVANCE_CHAR (text, tlen, i);
          continue;
        }
 
       if (c == '"' || c == '\'' || c == '`')
        {
          quoted = c;
+         i++;
          continue;
        }
 
@@ -265,6 +431,7 @@ brace_gobbler (text, indx, satisfy)
          t = extract_command_subst (text, &si);
          i = si;
          free (t);
+         i++;
          continue;
        }
 #endif
@@ -277,12 +444,11 @@ brace_gobbler (text, indx, satisfy)
          if (c == '{' &&
              ((!i || brace_whitespace (text[i - 1])) &&
               (brace_whitespace (text[i + 1]) || text[i + 1] == '}')))
-           continue;
-#if defined (SHELL)
-         /* If this is being compiled as part of bash, ignore the `{'
-            in a `${}' construct */
-         if ((c != '{') || i == 0 || (text[i - 1] != '$'))
-#endif /* SHELL */
+           {
+             i++;
+             continue;
+           }
+
            break;
        }
 
@@ -290,6 +456,8 @@ brace_gobbler (text, indx, satisfy)
        level++;
       else if (c == '}' && level)
        level--;
+
+      ADVANCE_CHAR (text, tlen, i);
     }
 
   *indx = i;
@@ -309,13 +477,13 @@ array_concat (arr1, arr2)
   register char **result;
 
   if (arr1 == 0)
-    return (copy_array (arr2));
+    return (strvec_copy (arr2));
 
   if (arr2 == 0)
-    return (copy_array (arr1));
+    return (strvec_copy (arr1));
 
-  len1 = array_len (arr1);
-  len2 = array_len (arr2);
+  len1 = strvec_len (arr1);
+  len2 = strvec_len (arr2);
 
   result = (char **)xmalloc ((1 + (len1 * len2)) * sizeof (char *));
 
@@ -326,8 +494,7 @@ array_concat (arr1, arr2)
 
       for (j = 0; j < len2; j++)
        {
-         result[len] =
-           (char *)xmalloc (1 + strlen_1 + strlen (arr2[j]));
+         result[len] = (char *)xmalloc (1 + strlen_1 + strlen (arr2[j]));
          strcpy (result[len], arr1[i]);
          strcpy (result[len] + strlen_1, arr2[j]);
          len++;