Imported from ../bash-3.0.16.tar.gz.
[platform/upstream/bash.git] / braces.c
index a7d9db1..4d229ca 100644 (file)
--- a/braces.c
+++ b/braces.c
@@ -1,12 +1,12 @@
 /* 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.
 
    Bash is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 1, or (at your option)
+   the Free Software Foundation; either version 2, or (at your option)
    any later version.
 
    Bash is distributed in the hope that it will be useful, but WITHOUT
@@ -16,7 +16,7 @@
 
    You should have received a copy of the GNU General Public License
    along with Bash; see the file COPYING.  If not, write to the Free
-   Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+   Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
 
 /* Stuff in curly braces gets expanded before all other shell expansions. */
 
@@ -25,6 +25,9 @@
 #if defined (BRACE_EXPANSION)
 
 #if defined (HAVE_UNISTD_H)
+#  ifdef _MINIX
+#    include <sys/types.h>
+#  endif
 #  include <unistd.h>
 #endif
 
 #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:
 
@@ -53,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 **
@@ -62,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);
@@ -85,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)
@@ -93,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. */
@@ -114,43 +137,63 @@ brace_expand (text)
       return (result);
     }
 
+#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);
 }
@@ -160,21 +203,31 @@ 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
       tem = (char *)xmalloc (1 + (i - start));
       strncpy (tem, &text[start], (i - start));
       tem[i- start] = '\0';
+#endif
 
       partial = brace_expand (tem);
 
@@ -182,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;
+
+         lr = strvec_len (result);
+         lp = strvec_len (partial);
 
-         result = (char **)xrealloc (result, (1 + lp + lr) * sizeof (char *));
+         result = strvec_resize (result, lp + lr + 1);
 
          for (j = 0; j < lp; j++)
            result[lr + j] = partial[j];
@@ -195,17 +249,122 @@ 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);
+}
+
 /* Start at INDEX, and skip characters in TEXT. Set INDEX to the
    index of the character matching SATISFY.  This understands about
    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;
 {
@@ -214,35 +373,53 @@ 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;
        }
 
       /* A backslash escapes the next character.  This allows backslash to
         escape the quote character in a double-quoted string. */
       if (c == '\\' && (quoted == 0 || quoted == '"' || quoted == '`'))
-        {
-          pass_next = 1;
-          continue;
-        }
+       {
+         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;
        }
 
@@ -254,6 +431,7 @@ brace_gobbler (text, indx, satisfy)
          t = extract_command_subst (text, &si);
          i = si;
          free (t);
+         i++;
          continue;
        }
 #endif
@@ -266,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;
        }
 
@@ -279,6 +456,8 @@ brace_gobbler (text, indx, satisfy)
        level++;
       else if (c == '}' && level)
        level--;
+
+      ADVANCE_CHAR (text, tlen, i);
     }
 
   *indx = i;
@@ -298,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 *));
 
@@ -315,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++;