]> git.ipfire.org Git - thirdparty/bash.git/commitdiff
commit bash-20041104 snapshot
authorChet Ramey <chet.ramey@case.edu>
Sat, 3 Dec 2011 18:39:12 +0000 (13:39 -0500)
committerChet Ramey <chet.ramey@case.edu>
Sat, 3 Dec 2011 18:39:12 +0000 (13:39 -0500)
66 files changed:
CHANGES
COMPAT
CWRU/CWRU.chlog
CWRU/CWRU.chlog~
MANIFEST
array.c~
arrayfunc.c
arrayfunc.c.save1 [new file with mode: 0644]
arrayfunc.c~
arrayfunc.h
arrayfunc.h~ [new file with mode: 0644]
builtins/cd.def
builtins/cd.def~
builtins/command.def
builtins/command.def~
builtins/declare.def
builtins/declare.def~ [new file with mode: 0644]
builtins/getopts.def
builtins/getopts.def~ [new file with mode: 0644]
builtins/pushd.def
builtins/pushd.def~
builtins/read.def
builtins/read.def~ [new file with mode: 0644]
builtins/set.def
builtins/set.def~ [new file with mode: 0644]
builtins/setattr.def
builtins/setattr.def~ [new file with mode: 0644]
doc/bash.1
doc/bash.1~
doc/bashref.texi
doc/bashref.texi~
doc/version.texi
doc/version.texi~
execute_cmd.c
execute_cmd.c~
general.c
general.c~
lib/readline/display.c
mailcheck.c
mailcheck.c~
parse.y
parse.y~
pcomplete.c
pcomplete.c~
shell.c
shell.c~
subst.c
subst.c~
subst.h
subst.h~
tests/RUN-ONE-TEST
tests/array.right
tests/array.tests
tests/array3.sub [new file with mode: 0644]
tests/extglob.right
tests/extglob.tests
tests/nquote.right
tests/nquote.tests
tests/rhs-exp.right
tests/rhs-exp.tests
tests/varenv.right
tests/varenv.sh
variables.c
variables.c~
variables.h
variables.h~

diff --git a/CHANGES b/CHANGES
index 159da675635e60f7cb4b36872b500e3f14ff9a26..e71aedc5e2503d14fed48995420752628e25e8e4 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -184,7 +184,7 @@ n.  Fixed the value of errno set by the pathname canonicalization functions.
 o.  Changed the grammar so that `time' alone on a line times a null command
     rather than being a syntax error.
 
-p.  The pattern substitution coded no longer performs quote removal on the
+p.  The pattern substitution code no longer performs quote removal on the
     pattern before trying to match it, as the pattern removal functions do.
 
 q.  Fixed a bug that could cause core dumps when checking whether a quoted
diff --git a/COMPAT b/COMPAT
index 6858ce7c3a4edec9ff72dda2b36e80d19b80ed21..56e960dbaf747a6e5624692232dfe23ef153b498 100644 (file)
--- a/COMPAT
+++ b/COMPAT
@@ -212,3 +212,14 @@ and bash-2.0 were significant.)
 
 18.  Bash no longer requires that the body of a function be a group command;
      any compound command is accepted.
+
+19.  As of bash-3.0, the pattern substitution operators no longer perform
+     quote removal on the pattern before attempting the match.  This is the
+     way the pattern removal functions behave, and is more consistent.
+
+20.  After bash-3.0 was released, I reimplemented tilde expansion, incorporating
+     it into the mainline word expansion code.  This fixes the bug that caused
+     the results of tilde expansion to be re-expanded.  There is one
+     incompatibility:  a ${paramOPword} expansion within double quotes will not
+     perform tilde expansion on WORD.  This is consistent with the other
+     expansions, and what POSIX specifies.
index 93dc12594e2c57f477bf904f60944f653a551609..7c235d220c38f98dd879748d300074e14f3e3a7e 100644 (file)
@@ -10475,3 +10475,79 @@ lib/readline/display.c
          locale, it's used as an absolute cursor position; when not using
          multibyte characters, it's a buffer offset.  I should have caught
          this when the multibyte character support was donated
+
+                                  11/5
+                                  ----
+general.c
+       - change `assignment()' to accept `+=' assignment operator
+
+arrayfunc.[ch]
+       - bind_array_variable and assign_array_element both take a new `flags'
+         argument
+       - assign_array_var_from_string, assign_array_from_string, and
+         assign_array_var_from_word_list now all take a new `flags' argument
+       - change assign_array_var_from_word_list to understand how to append
+         to an array variable
+       - change assign_array_var_from_string to understand how to append
+         to an array variable.  It does not unset the previous value if
+         appending, allowing both old values to be changed and new ones to
+         be added
+
+subst.h
+       - new flag #defines to use for evaluating assignment statements
+
+{subst,variables}.c, builtins/{declare,read}.def
+       - change callers of assign_array_element and bind_array_variable
+       - change do_compound_assignment to understand assignment flags
+       - change do_assignment_internal to set assignment flags and pass them
+         to underlying functions
+
+pcomplete.c,builtins/{declare,read}.def
+       - fix callers of assign_array_var_from_string, assign_array_var_from_word_list
+
+variables.[ch]
+       - make_variable_value now takes a new `flags' argument
+       - make_variable_value now understands how to append to a particular
+         variable, using the old value
+       - bind_variable_value now takes a new `flags' argument
+       - change make_variable_value to understand ASS_APPEND flag
+       - bind_variable now takes a new `flags' argument
+       - bind_variable_internal now takes a new `flags' argument
+
+arrayfunc.c
+       - change callers of make_variable_value to add flags arg
+
+builtins/declare.def
+       - change callers of bind_variable_value to add flags arg
+
+{execute_cmd,mailcheck,pcomplete,shell,subst,variables}.c,parse.y
+builtins/{cd,command,declare,getopts,read,set,setattr}.def
+       - change callers of bind_variable to add flags arg
+
+variables.c
+       - change callers of bind_variable_internal
+       - change bind_variable_internal to pass assignment flags on to
+         make_variable_value
+       - change assign_in_env to treat `var+=value' like `var=value'
+
+arrayfunc.c
+       - break code that actually constructs the new value and assigns it
+         to a particular array index out into a new functions:
+         bind_array_var_internal.  This fakes out make_variable_value by
+         passing a dummy SHELL_VAR * so it can do proper appending and other
+         += processing
+       - changes to assign_array_var_from_string to accept and process as if
+         they were `standalone' assignment statements array assignment words
+         of the form [ind]+=val
+
+                                  11/7
+                                  ----
+builtins/declare.def
+       - added support for `declare [flags] var+=value'.  `Flags' are applied
+         before the assignment is performed, which has implications for things
+         like `-i' -- if -i is supplied, arithmetic evaluation and increment
+         will be performed
+
+builtins/setattr.def
+       - add support for `+=' assignment for rest of `assignment builtins':
+         export, readonly
index b3ba6ee56e414820870032309b83e1b057ae1ca4..d65cdd00d3b66b9fdb4ab56af07b993c2546f50f 100644 (file)
@@ -10463,3 +10463,87 @@ shell.c
 lib/readline/display.c
        - disable `fast redisplay' at the end of the line if in a locale that
          supports multibyte characters (from SUSE, but not sent in)
+
+lib/readline/histexpand.c
+       - fix a problem with finding the delimiter of a `?' substring when
+         compiled for multibyte characters (from SUSE, but not sent in)
+
+                                  11/1
+                                  ----
+lib/readline/display.c
+       - correct some assignments to _rl_last_c_pos:  when in a multibyte
+         locale, it's used as an absolute cursor position; when not using
+         multibyte characters, it's a buffer offset.  I should have caught
+         this when the multibyte character support was donated
+
+                                  11/5
+                                  ----
+general.c
+       - change `assignment()' to accept `+=' assignment operator
+
+arrayfunc.[ch]
+       - bind_array_variable and assign_array_element both take a new `flags'
+         argument
+       - assign_array_var_from_string, assign_array_from_string, and
+         assign_array_var_from_word_list now all take a new `flags' argument
+       - change assign_array_var_from_word_list to understand how to append
+         to an array variable
+       - change assign_array_var_from_string to understand how to append
+         to an array variable.  It does not unset the previous value if
+         appending, allowing both old values to be changed and new ones to
+         be added
+
+subst.h
+       - new flag #defines to use for evaluating assignment statements
+
+{subst,variables}.c, builtins/{declare,read}.def
+       - change callers of assign_array_element and bind_array_variable
+       - change do_compound_assignment to understand assignment flags
+       - change do_assignment_internal to set assignment flags and pass them
+         to underlying functions
+
+pcomplete.c,builtins/{declare,read}.def
+       - fix callers of assign_array_var_from_string, assign_array_var_from_word_list
+
+variables.[ch]
+       - make_variable_value now takes a new `flags' argument
+       - make_variable_value now understands how to append to a particular
+         variable, using the old value
+       - bind_variable_value now takes a new `flags' argument
+       - change make_variable_value to understand ASS_APPEND flag
+       - bind_variable now takes a new `flags' argument
+       - bind_variable_internal now takes a new `flags' argument
+
+arrayfunc.c
+       - change callers of make_variable_value to add flags arg
+
+builtins/declare.def
+       - change callers of bind_variable_value to add flags arg
+
+{execute_cmd,mailcheck,pcomplete,shell,subst,variables}.c,parse.y
+builtins/{cd,command,declare,getopts,read,set,setattr}.def
+       - change callers of bind_variable to add flags arg
+
+variables.c
+       - change callers of bind_variable_internal
+       - change bind_variable_internal to pass assignment flags on to
+         make_variable_value
+       - change assign_in_env to treat `var+=value' like `var=value'
+
+arrayfunc.c
+       - break code that actually constructs the new value and assigns it
+         to a particular array index out into a new functions:
+         bind_array_var_internal.  This fakes out make_variable_value by
+         passing a dummy SHELL_VAR * so it can do proper appending and other
+         += processing
+       - changes to assign_array_var_from_string to accept and process as if
+         they were `standalone' assignment statements array assignment words
+         of the form [ind]+=val
+
+                                  11/7
+                                  ----
+builtins/declare.def
+       - added support for `declare [flags] var+=value'.  `Flags' are applied
+         before the assignment is performed, which has implications for things
+         like `-i' -- if -i is supplied, arithmetic evaluation and increment
+         will be performed
index 0207ba522d2ce927eb3b664dcd055a54a73b65af..6edc5c73bd19152dc7be394c54b2f832b5826ad8 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -685,6 +685,7 @@ tests/array.tests   f
 tests/array.right      f
 tests/array1.sub       f
 tests/array2.sub       f
+tests/array3.sub       f
 tests/array-at-star    f
 tests/array2.right     f
 tests/braces.tests     f
index 66df4ab8dc468d93c94953126b6af766132582ec..2b362c678c5c0242eb2e186f7a8992eeec2fcd58 100644 (file)
--- a/array.c~
+++ b/array.c~
@@ -243,9 +243,7 @@ char        *s;
 {
        register ARRAY_ELEMENT  *ae, *new;
 
-       if (a == 0)
-               return 0;
-       else if (array_empty(a) && s == 0)
+       if (a == 0 || (array_empty(a) && s == 0))
                return 0;
        else if (n <= 0)
                return (a->num_elements);
@@ -558,6 +556,7 @@ ARRAY       *a;
        return (REVERSE_LIST(list, WORD_LIST *));
 }
 
+/* XXX - changes needed to support `+=' */
 ARRAY *
 array_assign_list (array, list)
 ARRAY  *array;
index 9e85594c301783267b6db66e29b7f100fb19198e..48c59d790d088d6934009376073aec2cef4f86da 100644 (file)
@@ -1,6 +1,6 @@
 /* arrayfunc.c -- High-level array functions used by other parts of the shell. */
 
-/* Copyright (C) 2001-2003 Free Software Foundation, Inc.
+/* Copyright (C) 2001-2004 Free Software Foundation, Inc.
 
    This file is part of GNU Bash, the Bourne Again SHell.
 
@@ -39,6 +39,8 @@ extern char *this_command_name;
 extern int last_command_exit_value;
 extern int array_needs_making;
 
+static SHELL_VAR *bind_array_var_internal __P((SHELL_VAR *, arrayind_t, char *, int));
+
 static void quote_array_assignment_chars __P((WORD_LIST *));
 static char *array_value_internal __P((char *, int, int, int *));
 
@@ -82,6 +84,49 @@ convert_var_to_array (var)
   return var;
 }
 
+static SHELL_VAR *
+bind_array_var_internal (entry, ind, value, flags)
+     SHELL_VAR *entry;
+     arrayind_t ind;
+     char *value;
+     int flags;
+{
+  SHELL_VAR *dentry;
+  char *newval;
+
+  /* If we're appending, we need the old value of the array reference, so
+     fake out make_variable_value with a dummy SHELL_VAR */
+  if (flags & ASS_APPEND)
+    {
+      dentry = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
+      dentry->name = savestring (entry->name);
+      newval = array_reference (array_cell (entry), ind);
+      if (newval)
+       dentry->value = savestring (newval);
+      else
+       {
+         dentry->value = (char *)xmalloc (1);
+         dentry->value[0] = '\0';
+       }
+      dentry->exportstr = 0;
+      dentry->attributes = entry->attributes & ~(att_array|att_exported);
+      /* Leave the rest of the members uninitialized; the code doesn't look
+        at them. */
+      newval = make_variable_value (dentry, value, flags);      
+      dispose_variable (dentry);
+    }
+  else
+    newval = make_variable_value (entry, value, flags);
+
+  if (entry->assign_func)
+    (*entry->assign_func) (entry, newval, ind);
+  else
+    array_insert (array_cell (entry), ind, newval);
+  FREE (newval);
+
+  return (entry);
+}
+
 /* Perform an array assignment name[ind]=value.  If NAME already exists and
    is not an array, and IND is 0, perform name=value instead.  If NAME exists
    and is not an array, and IND is not 0, convert it into an array with the
@@ -90,13 +135,13 @@ convert_var_to_array (var)
    If NAME does not exist, just create an array variable, no matter what
    IND's value may be. */
 SHELL_VAR *
-bind_array_variable (name, ind, value)
+bind_array_variable (name, ind, value, flags)
      char *name;
      arrayind_t ind;
      char *value;
+     int flags;
 {
   SHELL_VAR *entry;
-  char *newval;
 
   entry = var_lookup (name, shell_variables);
 
@@ -112,21 +157,15 @@ bind_array_variable (name, ind, value)
     entry = convert_var_to_array (entry);
 
   /* ENTRY is an array variable, and ARRAY points to the value. */
-  newval = make_variable_value (entry, value);
-  if (entry->assign_func)
-    (*entry->assign_func) (entry, newval, ind);
-  else
-    array_insert (array_cell (entry), ind, newval);
-  FREE (newval);
-
-  return (entry);
+  return (bind_array_var_internal (entry, ind, value, flags));
 }
 
 /* Parse NAME, a lhs of an assignment statement of the form v[s], and
    assign VALUE to that array element by calling bind_array_variable(). */
 SHELL_VAR *
-assign_array_element (name, value)
+assign_array_element (name, value, flags)
      char *name, *value;
+     int flags;
 {
   char *sub, *vname;
   arrayind_t ind;
@@ -153,7 +192,7 @@ assign_array_element (name, value)
       return ((SHELL_VAR *)NULL);
     }
 
-  entry = bind_array_variable (vname, ind, value);
+  entry = bind_array_variable (vname, ind, value, flags);
 
   free (vname);
   return (entry);
@@ -190,8 +229,9 @@ find_or_make_array_variable (name, check_flags)
 /* Perform a compound assignment statement for array NAME, where VALUE is
    the text between the parens:  NAME=( VALUE ) */
 SHELL_VAR *
-assign_array_from_string (name, value)
+assign_array_from_string (name, value, flags)
      char *name, *value;
+     int flags;
 {
   SHELL_VAR *var;
 
@@ -199,21 +239,25 @@ assign_array_from_string (name, value)
   if (var == 0)
     return ((SHELL_VAR *)NULL);
 
-  return (assign_array_var_from_string (var, value));
+  return (assign_array_var_from_string (var, value, flags));
 }
 
 /* Sequentially assign the indices of indexed array variable VAR from the
    words in LIST. */
 SHELL_VAR *
-assign_array_var_from_word_list (var, list)
+assign_array_var_from_word_list (var, list, flags)
      SHELL_VAR *var;
      WORD_LIST *list;
+     int flags;
 {
   register arrayind_t i;
   register WORD_LIST *l;
   ARRAY *a;
 
-  for (a = array_cell (var), l = list, i = 0; l; l = l->next, i++)
+  a = array_cell (var);
+  i = (flags & ASS_APPEND) ? array_max_index (a) + 1 : 0;
+
+  for (l = list; l; l = l->next, i++)
     if (var->assign_func)
       (*var->assign_func) (var, l->word->word, i);
     else
@@ -224,9 +268,10 @@ assign_array_var_from_word_list (var, list)
 /* Perform a compound array assignment:  VAR->name=( VALUE ).  The
    VALUE has already had the parentheses stripped. */
 SHELL_VAR *
-assign_array_var_from_string (var, value)
+assign_array_var_from_string (var, value, flags)
      SHELL_VAR *var;
      char *value;
+     int flags;
 {
   ARRAY *a;
   WORD_LIST *list, *nlist;
@@ -274,10 +319,11 @@ assign_array_var_from_string (var, value)
 
   /* Now that we are ready to assign values to the array, kill the existing
      value. */
-  if (a)
+  if (a && (flags & ASS_APPEND) == 0)
     array_flush (a);
+  last_ind = (flags & ASS_APPEND) ? array_max_index (a) + 1 : 0;
 
-  for (last_ind = 0, list = nlist; list; list = list->next)
+  for (list = nlist; list; list = list->next)
     {
       w = list->word->word;
 
@@ -286,9 +332,14 @@ assign_array_var_from_string (var, value)
        {
          len = skipsubscript (w, 0);
 
+#if 1
+         /* XXX - changes for `+=' */
+         if (w[len] != ']' || (w[len+1] != '=' && (w[len+1] != '+' || w[len+2] != '=')))
+#else
          if (w[len] != ']' || w[len+1] != '=')
+#endif
            {
-             nval = make_variable_value (var, w);
+             nval = make_variable_value (var, w, flags);
              if (var->assign_func)
                (*var->assign_func) (var, nval, last_ind);
              else
@@ -317,7 +368,14 @@ assign_array_var_from_string (var, value)
              continue;
            }
          last_ind = ind;
-         val = w + len + 2;
+         /* XXX - changes for `+=' */
+         if (w[len + 1] == '+' && w[len + 2] == '=')
+           {
+             flags |= ASS_APPEND;
+             val = w + len + 3;
+           }
+         else
+           val = w + len + 2;
        }
       else             /* No [ind]=value, just a stray `=' */
        {
@@ -327,12 +385,7 @@ assign_array_var_from_string (var, value)
 
       if (integer_p (var))
        this_command_name = (char *)NULL;       /* no command name for errors */
-      nval = make_variable_value (var, val);
-      if (var->assign_func)
-       (*var->assign_func) (var, nval, ind);
-      else
-       array_insert (a, ind, nval);
-      FREE (nval);
+      bind_array_var_internal (var, ind, val, flags);
       last_ind++;
     }
 
diff --git a/arrayfunc.c.save1 b/arrayfunc.c.save1
new file mode 100644 (file)
index 0000000..2f980d9
--- /dev/null
@@ -0,0 +1,803 @@
+/* arrayfunc.c -- High-level array functions used by other parts of the shell. */
+
+/* Copyright (C) 2001-2004 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 2, or (at your option) any later
+   version.
+
+   Bash is distributed in the hope that it will be useful, but WITHOUT ANY
+   WARRANTY; without even the implied warranty of MERCHANTABILITY or
+   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+   for more details.
+
+   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, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+
+#include "config.h"
+
+#if defined (ARRAY_VARS)
+
+#if defined (HAVE_UNISTD_H)
+#  include <unistd.h>
+#endif
+#include <stdio.h>
+
+#include "bashintl.h"
+
+#include "shell.h"
+
+#include "shmbutil.h"
+
+#include "builtins/common.h"
+
+extern char *this_command_name;
+extern int last_command_exit_value;
+extern int array_needs_making;
+
+static void quote_array_assignment_chars __P((WORD_LIST *));
+static char *array_value_internal __P((char *, int, int, int *));
+
+/* Standard error message to use when encountering an invalid array subscript */
+char *bash_badsub_errmsg = N_("bad array subscript");
+
+/* **************************************************************** */
+/*                                                                 */
+/*  Functions to manipulate array variables and perform assignments */
+/*                                                                 */
+/* **************************************************************** */
+
+/* Convert a shell variable to an array variable.  The original value is
+   saved as array[0]. */
+SHELL_VAR *
+convert_var_to_array (var)
+     SHELL_VAR *var;
+{
+  char *oldval;
+  ARRAY *array;
+
+  oldval = value_cell (var);
+  array = array_create ();
+  if (oldval)
+    array_insert (array, 0, oldval);
+
+  FREE (value_cell (var));
+  var_setarray (var, array);
+
+  /* these aren't valid anymore */
+  var->dynamic_value = (sh_var_value_func_t *)NULL;
+  var->assign_func = (sh_var_assign_func_t *)NULL;
+
+  INVALIDATE_EXPORTSTR (var);
+  if (exported_p (var))
+    array_needs_making++;
+
+  VSETATTR (var, att_array);
+  VUNSETATTR (var, att_invisible);
+
+  return var;
+}
+
+/* Perform an array assignment name[ind]=value.  If NAME already exists and
+   is not an array, and IND is 0, perform name=value instead.  If NAME exists
+   and is not an array, and IND is not 0, convert it into an array with the
+   existing value as name[0].
+
+   If NAME does not exist, just create an array variable, no matter what
+   IND's value may be. */
+SHELL_VAR *
+bind_array_variable (name, ind, value, flags)
+     char *name;
+     arrayind_t ind;
+     char *value;
+     int flags;
+{
+  SHELL_VAR *entry, *dentry;
+  char *newval;
+
+  entry = var_lookup (name, shell_variables);
+
+  if (entry == (SHELL_VAR *) 0)
+    entry = make_new_array_variable (name);
+  else if (readonly_p (entry) || noassign_p (entry))
+    {
+      if (readonly_p (entry))
+       err_readonly (name);
+      return (entry);
+    }
+  else if (array_p (entry) == 0)
+    entry = convert_var_to_array (entry);
+
+  /* ENTRY is an array variable, and ARRAY points to the value. */
+
+  /* If we're appending, we need the old value of the array reference, so
+     fake out make_variable_value with a dummy SHELL_VAR */
+  if (flags & ASS_APPEND)
+    {
+      dentry = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
+      dentry->name = savestring (entry->name);
+      newval = array_reference (array_cell (entry), ind);
+      if (newval)
+       dentry->value = savestring (newval);
+      else
+       {
+         dentry->value = (char *)xmalloc (1);
+         dentry->value[0] = '\0';
+       }
+      dentry->exportstr = 0;
+      dentry->attributes = entry->attributes & ~(att_array|att_exported);
+      /* Leave the rest of the members uninitialized; the code doesn't look
+        at them. */
+      newval = make_variable_value (dentry, value, flags);      
+      dispose_variable (dentry);
+    }
+  else
+    newval = make_variable_value (entry, value, flags);
+
+  if (entry->assign_func)
+    (*entry->assign_func) (entry, newval, ind);
+  else
+    array_insert (array_cell (entry), ind, newval);
+  FREE (newval);
+
+  return (entry);
+}
+
+/* Parse NAME, a lhs of an assignment statement of the form v[s], and
+   assign VALUE to that array element by calling bind_array_variable(). */
+SHELL_VAR *
+assign_array_element (name, value, flags)
+     char *name, *value;
+     int flags;
+{
+  char *sub, *vname;
+  arrayind_t ind;
+  int sublen;
+  SHELL_VAR *entry;
+
+  vname = array_variable_name (name, &sub, &sublen);
+
+  if (vname == 0)
+    return ((SHELL_VAR *)NULL);
+
+  if ((ALL_ELEMENT_SUB (sub[0]) && sub[1] == ']') || (sublen <= 1))
+    {
+      free (vname);
+      err_badarraysub (name);
+      return ((SHELL_VAR *)NULL);
+    }
+
+  ind = array_expand_index (sub, sublen);
+  if (ind < 0)
+    {
+      free (vname);
+      err_badarraysub (name);
+      return ((SHELL_VAR *)NULL);
+    }
+
+  entry = bind_array_variable (vname, ind, value, flags);
+
+  free (vname);
+  return (entry);
+}
+
+/* Find the array variable corresponding to NAME.  If there is no variable,
+   create a new array variable.  If the variable exists but is not an array,
+   convert it to an indexed array.  If CHECK_FLAGS is non-zero, an existing
+   variable is checked for the readonly or noassign attribute in preparation
+   for assignment (e.g., by the `read' builtin). */
+SHELL_VAR *
+find_or_make_array_variable (name, check_flags)
+     char *name;
+     int check_flags;
+{
+  SHELL_VAR *var;
+
+  var = find_variable (name);
+
+  if (var == 0)
+    var = make_new_array_variable (name);
+  else if (check_flags && (readonly_p (var) || noassign_p (var)))
+    {
+      if (readonly_p (var))
+       err_readonly (name);
+      return ((SHELL_VAR *)NULL);
+    }
+  else if (array_p (var) == 0)
+    var = convert_var_to_array (var);
+
+  return (var);
+}
+  
+/* Perform a compound assignment statement for array NAME, where VALUE is
+   the text between the parens:  NAME=( VALUE ) */
+SHELL_VAR *
+assign_array_from_string (name, value, flags)
+     char *name, *value;
+     int flags;
+{
+  SHELL_VAR *var;
+
+  var = find_or_make_array_variable (name, 1);
+  if (var == 0)
+    return ((SHELL_VAR *)NULL);
+
+  return (assign_array_var_from_string (var, value, flags));
+}
+
+/* Sequentially assign the indices of indexed array variable VAR from the
+   words in LIST. */
+SHELL_VAR *
+assign_array_var_from_word_list (var, list, flags)
+     SHELL_VAR *var;
+     WORD_LIST *list;
+     int flags;
+{
+  register arrayind_t i;
+  register WORD_LIST *l;
+  ARRAY *a;
+
+  a = array_cell (var);
+  i = (flags & ASS_APPEND) ? array_max_index (a) + 1 : 0;
+
+  for (l = list; l; l = l->next, i++)
+    if (var->assign_func)
+      (*var->assign_func) (var, l->word->word, i);
+    else
+      array_insert (a, i, l->word->word);
+  return var;
+}
+
+/* Perform a compound array assignment:  VAR->name=( VALUE ).  The
+   VALUE has already had the parentheses stripped. */
+SHELL_VAR *
+assign_array_var_from_string (var, value, flags)
+     SHELL_VAR *var;
+     char *value;
+     int flags;
+{
+  ARRAY *a;
+  WORD_LIST *list, *nlist;
+  char *w, *val, *nval;
+  int ni, len;
+  arrayind_t ind, last_ind;
+
+  if (value == 0)
+    return var;
+
+  /* If this is called from declare_builtin, value[0] == '(' and
+     xstrchr(value, ')') != 0.  In this case, we need to extract
+     the value from between the parens before going on. */
+  if (*value == '(')   /*)*/
+    {
+      ni = 1;
+      val = extract_array_assignment_list (value, &ni);
+      if (val == 0)
+       return var;
+    }
+  else
+    val = value;
+
+  /* Expand the value string into a list of words, performing all the
+     shell expansions including pathname generation and word splitting. */
+  /* First we split the string on whitespace, using the shell parser
+     (ksh93 seems to do this). */
+  list = parse_string_to_word_list (val, 1, "array assign");
+
+  /* If we're using [subscript]=value, we need to quote each [ and ] to
+     prevent unwanted filename expansion. */
+  if (list)
+    quote_array_assignment_chars (list);
+
+  /* Now that we've split it, perform the shell expansions on each
+     word in the list. */
+  nlist = list ? expand_words_no_vars (list) : (WORD_LIST *)NULL;
+
+  dispose_words (list);
+
+  if (val != value)
+    free (val);
+
+  a = array_cell (var);
+
+  /* Now that we are ready to assign values to the array, kill the existing
+     value. */
+  if (a && (flags & ASS_APPEND) == 0)
+    array_flush (a);
+  last_ind = (flags & ASS_APPEND) ? array_max_index (a) + 1 : 0;
+
+  for (list = nlist; list; list = list->next)
+    {
+      w = list->word->word;
+
+      /* We have a word of the form [ind]=value */
+      if ((list->word->flags & W_ASSIGNMENT) && w[0] == '[')
+       {
+         len = skipsubscript (w, 0);
+
+#if 1
+         if (! (w[len] == ']' && (w[len+1] == '=' || (w[len+1] == '+' && w[len+2] == '='))))
+#else
+         if (w[len] != ']' || w[len+1] != '=')
+#endif
+           {
+             nval = make_variable_value (var, w, flags);
+             if (var->assign_func)
+               (*var->assign_func) (var, nval, last_ind);
+             else
+               array_insert (a, last_ind, nval);
+             FREE (nval);
+             last_ind++;
+             continue;
+           }
+
+         if (len == 1)
+           {
+             err_badarraysub (w);
+             continue;
+           }
+
+         if (ALL_ELEMENT_SUB (w[1]) && len == 2)
+           {
+             report_error (_("%s: cannot assign to non-numeric index"), w);
+             continue;
+           }
+
+         ind = array_expand_index (w + 1, len);
+         if (ind < 0)
+           {
+             err_badarraysub (w);
+             continue;
+           }
+         last_ind = ind;
+         val = w + len + 2;
+       }
+      else             /* No [ind]=value, just a stray `=' */
+       {
+         ind = last_ind;
+         val = w;
+       }
+
+      if (integer_p (var))
+       this_command_name = (char *)NULL;       /* no command name for errors */
+      nval = make_variable_value (var, val, flags);
+      if (var->assign_func)
+       (*var->assign_func) (var, nval, ind);
+      else
+       array_insert (a, ind, nval);
+      FREE (nval);
+      last_ind++;
+    }
+
+  dispose_words (nlist);
+  return (var);
+}
+
+/* For each word in a compound array assignment, if the word looks like
+   [ind]=value, quote the `[' and `]' before the `=' to protect them from
+   unwanted filename expansion. */
+static void
+quote_array_assignment_chars (list)
+     WORD_LIST *list;
+{
+  char *s, *t, *nword;
+  int saw_eq;
+  WORD_LIST *l;
+
+  for (l = list; l; l = l->next)
+    {
+      if (l->word == 0 || l->word->word == 0 || l->word->word[0] == '\0')
+       continue;       /* should not happen, but just in case... */
+      /* Don't bother if it doesn't look like [ind]=value */
+      if (l->word->word[0] != '[' || xstrchr (l->word->word, '=') == 0) /* ] */
+       continue;
+      s = nword = (char *)xmalloc (strlen (l->word->word) * 2 + 1);
+      saw_eq = 0;
+      for (t = l->word->word; *t; )
+       {
+         if (*t == '=')
+           saw_eq = 1;
+         if (saw_eq == 0 && (*t == '[' || *t == ']'))
+           *s++ = '\\';
+         *s++ = *t++;
+       }
+      *s = '\0';
+      free (l->word->word);
+      l->word->word = nword;
+    }
+}
+
+/* This function assumes s[i] == '['; returns with s[ret] == ']' if
+   an array subscript is correctly parsed. */
+int
+skipsubscript (s, i)
+     const char *s;
+     int i;
+{
+  int count, c;
+#if defined (HANDLE_MULTIBYTE)
+  mbstate_t state, state_bak;
+  size_t slength, mblength;
+  size_t mb_cur_max;
+#endif
+
+#if defined (HANDLE_MULTIBYTE)
+  memset (&state, '\0', sizeof (mbstate_t));
+  slength = strlen (s + i);
+  mb_cur_max = MB_CUR_MAX;
+#endif
+  
+  count = 1;
+  while (count)
+    {
+      /* Advance one (possibly multibyte) character in S starting at I. */
+#if defined (HANDLE_MULTIBYTE)
+      if (mb_cur_max > 1)
+       {
+         state_bak = state;
+         mblength = mbrlen (s + i, slength, &state);
+
+         if (MB_INVALIDCH (mblength))
+           {
+             state = state_bak;
+             i++;
+             slength--;
+           }
+         else if (MB_NULLWCH (mblength))
+           return i;
+         else
+           {
+             i += mblength;
+             slength -= mblength;
+           }
+       }
+      else
+#endif
+      ++i;
+
+      c = s[i];
+
+      if (c == 0)
+        break;
+      else if (c == '[')
+       count++;
+      else if (c == ']')
+       count--;
+    }
+
+  return i;
+}
+
+/* This function is called with SUB pointing to just after the beginning
+   `[' of an array subscript and removes the array element to which SUB
+   expands from array VAR.  A subscript of `*' or `@' unsets the array. */
+int
+unbind_array_element (var, sub)
+     SHELL_VAR *var;
+     char *sub;
+{
+  int len;
+  arrayind_t ind;
+  ARRAY_ELEMENT *ae;
+
+  len = skipsubscript (sub, 0);
+  if (sub[len] != ']' || len == 0)
+    {
+      builtin_error ("%s[%s: %s", var->name, sub, _(bash_badsub_errmsg));
+      return -1;
+    }
+  sub[len] = '\0';
+
+  if (ALL_ELEMENT_SUB (sub[0]) && sub[1] == 0)
+    {
+      unbind_variable (var->name);
+      return (0);
+    }
+  ind = array_expand_index (sub, len+1);
+  if (ind < 0)
+    {
+      builtin_error ("[%s]: %s", sub, _(bash_badsub_errmsg));
+      return -1;
+    }
+  ae = array_remove (array_cell (var), ind);
+  if (ae)
+    array_dispose_element (ae);
+  return 0;
+}
+
+/* Format and output an array assignment in compound form VAR=(VALUES),
+   suitable for re-use as input. */
+void
+print_array_assignment (var, quoted)
+     SHELL_VAR *var;
+     int quoted;
+{
+  char *vstr;
+
+  vstr = array_to_assign (array_cell (var), quoted);
+
+  if (vstr == 0)
+    printf ("%s=%s\n", var->name, quoted ? "'()'" : "()");
+  else
+    {
+      printf ("%s=%s\n", var->name, vstr);
+      free (vstr);
+    }
+}
+
+/***********************************************************************/
+/*                                                                    */
+/* Utility functions to manage arrays and their contents for expansion */
+/*                                                                    */
+/***********************************************************************/
+
+/* Return 1 if NAME is a properly-formed array reference v[sub]. */
+int
+valid_array_reference (name)
+     char *name;
+{
+  char *t;
+  int r, len;
+
+  t = xstrchr (name, '[');     /* ] */
+  if (t)
+    {
+      *t = '\0';
+      r = legal_identifier (name);
+      *t = '[';
+      if (r == 0)
+       return 0;
+      /* Check for a properly-terminated non-blank subscript. */
+      len = skipsubscript (t, 0);
+      if (t[len] != ']' || len == 1)
+       return 0;
+      for (r = 1; r < len; r++)
+       if (whitespace (t[r]) == 0)
+         return 1;
+      return 0;
+    }
+  return 0;
+}
+
+/* Expand the array index beginning at S and extending LEN characters. */
+arrayind_t
+array_expand_index (s, len)
+     char *s;
+     int len;
+{
+  char *exp, *t;
+  int expok;
+  arrayind_t val;
+
+  exp = (char *)xmalloc (len);
+  strncpy (exp, s, len - 1);
+  exp[len - 1] = '\0';
+  t = expand_string_to_string (exp, 0);
+  this_command_name = (char *)NULL;
+  val = evalexp (t, &expok);
+  free (t);
+  free (exp);
+  if (expok == 0)
+    {
+      last_command_exit_value = EXECUTION_FAILURE;
+      jump_to_top_level (DISCARD);
+    }
+  return val;
+}
+
+/* Return the name of the variable specified by S without any subscript.
+   If SUBP is non-null, return a pointer to the start of the subscript
+   in *SUBP. If LENP is non-null, the length of the subscript is returned
+   in *LENP.  This returns newly-allocated memory. */
+char *
+array_variable_name (s, subp, lenp)
+     char *s, **subp;
+     int *lenp;
+{
+  char *t, *ret;
+  int ind, ni;
+
+  t = xstrchr (s, '[');
+  if (t == 0)
+    {
+      if (subp)
+       *subp = t;
+      if (lenp)
+       *lenp = 0;
+      return ((char *)NULL);
+    }
+  ind = t - s;
+  ni = skipsubscript (s, ind);
+  if (ni <= ind + 1 || s[ni] != ']')
+    {
+      err_badarraysub (s);
+      if (subp)
+       *subp = t;
+      if (lenp)
+       *lenp = 0;
+      return ((char *)NULL);
+    }
+
+  *t = '\0';
+  ret = savestring (s);
+  *t++ = '[';          /* ] */
+
+  if (subp)
+    *subp = t;
+  if (lenp)
+    *lenp = ni - ind;
+
+  return ret;
+}
+
+/* Return the variable specified by S without any subscript.  If SUBP is
+   non-null, return a pointer to the start of the subscript in *SUBP.
+   If LENP is non-null, the length of the subscript is returned in *LENP. */
+SHELL_VAR *
+array_variable_part (s, subp, lenp)
+     char *s, **subp;
+     int *lenp;
+{
+  char *t;
+  SHELL_VAR *var;
+
+  t = array_variable_name (s, subp, lenp);
+  if (t == 0)
+    return ((SHELL_VAR *)NULL);
+  var = find_variable (t);
+
+  free (t);
+  return (var == 0 || invisible_p (var)) ? (SHELL_VAR *)0 : var;
+}
+
+/* Return a string containing the elements in the array and subscript
+   described by S.  If the subscript is * or @, obeys quoting rules akin
+   to the expansion of $* and $@ including double quoting.  If RTYPE
+   is non-null it gets 1 if the array reference is name[@] or name[*]
+   and 0 otherwise. */
+static char *
+array_value_internal (s, quoted, allow_all, rtype)
+     char *s;
+     int quoted, allow_all, *rtype;
+{
+  int len;
+  arrayind_t ind;
+  char *retval, *t, *temp;
+  WORD_LIST *l;
+  SHELL_VAR *var;
+
+  var = array_variable_part (s, &t, &len);
+
+  /* Expand the index, even if the variable doesn't exist, in case side
+     effects are needed, like ${w[i++]} where w is unset. */
+#if 0
+  if (var == 0)
+    return (char *)NULL;
+#endif
+
+  if (len == 0)
+    return ((char *)NULL);     /* error message already printed */
+
+  /* [ */
+  if (ALL_ELEMENT_SUB (t[0]) && t[1] == ']')
+    {
+      if (rtype)
+       *rtype = 1;
+      if (allow_all == 0)
+       {
+         err_badarraysub (s);
+         return ((char *)NULL);
+       }
+      else if (var == 0)
+       return ((char *)NULL);
+      else if (array_p (var) == 0)
+       l = add_string_to_list (value_cell (var), (WORD_LIST *)NULL);
+      else
+       {
+         l = array_to_word_list (array_cell (var));
+         if (l == (WORD_LIST *)NULL)
+           return ((char *) NULL);
+       }
+
+      if (t[0] == '*' && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
+       {
+         temp = string_list_dollar_star (l);
+         retval = quote_string (temp);
+         free (temp);
+       }
+      else     /* ${name[@]} or unquoted ${name[*]} */
+       retval = string_list_dollar_at (l, quoted);
+
+      dispose_words (l);
+    }
+  else
+    {
+      if (rtype)
+       *rtype = 0;
+      ind = array_expand_index (t, len);
+      if (ind < 0)
+       {
+         if (var)
+           err_badarraysub (var->name);
+         else
+           {
+             t[-1] = '\0';
+             err_badarraysub (s);
+             t[-1] = '[';      /* ] */
+           }
+         return ((char *)NULL);
+       }
+      if (var == 0)
+       return ((char *)NULL);
+      if (array_p (var) == 0)
+       return (ind == 0 ? value_cell (var) : (char *)NULL);
+      retval = array_reference (array_cell (var), ind);
+    }
+
+  return retval;
+}
+
+/* Return a string containing the elements described by the array and
+   subscript contained in S, obeying quoting for subscripts * and @. */
+char *
+array_value (s, quoted, rtype)
+     char *s;
+     int quoted, *rtype;
+{
+  return (array_value_internal (s, quoted, 1, rtype));
+}
+
+/* Return the value of the array indexing expression S as a single string.
+   If ALLOW_ALL is 0, do not allow `@' and `*' subscripts.  This is used
+   by other parts of the shell such as the arithmetic expression evaluator
+   in expr.c. */
+char *
+get_array_value (s, allow_all, rtype)
+     char *s;
+     int allow_all, *rtype;
+{
+  return (array_value_internal (s, 0, allow_all, rtype));
+}
+
+char *
+array_keys (s, quoted)
+     char *s;
+     int quoted;
+{
+  int len;
+  char *retval, *t, *temp;
+  WORD_LIST *l;
+  SHELL_VAR *var;
+
+  var = array_variable_part (s, &t, &len);
+
+  /* [ */
+  if (var == 0 || ALL_ELEMENT_SUB (t[0]) == 0 || t[1] != ']')
+    return (char *)NULL;
+
+  if (array_p (var) == 0)
+    l = add_string_to_list ("0", (WORD_LIST *)NULL);
+  else
+    {
+      l = array_keys_to_word_list (array_cell (var));
+      if (l == (WORD_LIST *)NULL)
+        return ((char *) NULL);
+    }
+
+  if (t[0] == '*' && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
+    {
+      temp = string_list_dollar_star (l);
+      retval = quote_string (temp);
+      free (temp);
+    }
+  else /* ${!name[@]} or unquoted ${!name[*]} */
+    retval = string_list_dollar_at (l, quoted);
+
+  dispose_words (l);
+  return retval;
+}
+#endif /* ARRAY_VARS */
index a173865017a67bf66380e969c1e047cade15df6e..fc4adb5bcda24df5e65ae1f69b764b7a4691de5f 100644 (file)
@@ -1,6 +1,6 @@
 /* arrayfunc.c -- High-level array functions used by other parts of the shell. */
 
-/* Copyright (C) 2001-2003 Free Software Foundation, Inc.
+/* Copyright (C) 2001-2004 Free Software Foundation, Inc.
 
    This file is part of GNU Bash, the Bourne Again SHell.
 
@@ -37,6 +37,9 @@
 
 extern char *this_command_name;
 extern int last_command_exit_value;
+extern int array_needs_making;
+
+static SHELL_VAR *bind_array_var_internal __P((SHELL_VAR *, arrayind_t, char *, int));
 
 static void quote_array_assignment_chars __P((WORD_LIST *));
 static char *array_value_internal __P((char *, int, int, int *));
@@ -72,6 +75,8 @@ convert_var_to_array (var)
   var->assign_func = (sh_var_assign_func_t *)NULL;
 
   INVALIDATE_EXPORTSTR (var);
+  if (exported_p (var))
+    array_needs_making++;
 
   VSETATTR (var, att_array);
   VUNSETATTR (var, att_invisible);
@@ -79,6 +84,49 @@ convert_var_to_array (var)
   return var;
 }
 
+static SHELL_VAR *
+bind_array_var_internal (entry, ind, value, flags)
+     SHELL_VAR *entry;
+     arrayind_t ind;
+     char *value;
+     int flags;
+{
+  SHELL_VAR *dentry;
+  char *newval;
+
+  /* If we're appending, we need the old value of the array reference, so
+     fake out make_variable_value with a dummy SHELL_VAR */
+  if (flags & ASS_APPEND)
+    {
+      dentry = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
+      dentry->name = savestring (entry->name);
+      newval = array_reference (array_cell (entry), ind);
+      if (newval)
+       dentry->value = savestring (newval);
+      else
+       {
+         dentry->value = (char *)xmalloc (1);
+         dentry->value[0] = '\0';
+       }
+      dentry->exportstr = 0;
+      dentry->attributes = entry->attributes & ~(att_array|att_exported);
+      /* Leave the rest of the members uninitialized; the code doesn't look
+        at them. */
+      newval = make_variable_value (dentry, value, flags);      
+      dispose_variable (dentry);
+    }
+  else
+    newval = make_variable_value (entry, value, flags);
+
+  if (entry->assign_func)
+    (*entry->assign_func) (entry, newval, ind);
+  else
+    array_insert (array_cell (entry), ind, newval);
+  FREE (newval);
+
+  return (entry);
+}
+
 /* Perform an array assignment name[ind]=value.  If NAME already exists and
    is not an array, and IND is 0, perform name=value instead.  If NAME exists
    and is not an array, and IND is not 0, convert it into an array with the
@@ -87,13 +135,13 @@ convert_var_to_array (var)
    If NAME does not exist, just create an array variable, no matter what
    IND's value may be. */
 SHELL_VAR *
-bind_array_variable (name, ind, value)
+bind_array_variable (name, ind, value, flags)
      char *name;
      arrayind_t ind;
      char *value;
+     int flags;
 {
   SHELL_VAR *entry;
-  char *newval;
 
   entry = var_lookup (name, shell_variables);
 
@@ -109,21 +157,15 @@ bind_array_variable (name, ind, value)
     entry = convert_var_to_array (entry);
 
   /* ENTRY is an array variable, and ARRAY points to the value. */
-  newval = make_variable_value (entry, value);
-  if (entry->assign_func)
-    (*entry->assign_func) (entry, newval, ind);
-  else
-    array_insert (array_cell (entry), ind, newval);
-  FREE (newval);
-
-  return (entry);
+  return (bind_array_var_internal (entry, ind, value, flags));
 }
 
 /* Parse NAME, a lhs of an assignment statement of the form v[s], and
    assign VALUE to that array element by calling bind_array_variable(). */
 SHELL_VAR *
-assign_array_element (name, value)
+assign_array_element (name, value, flags)
      char *name, *value;
+     int flags;
 {
   char *sub, *vname;
   arrayind_t ind;
@@ -150,7 +192,7 @@ assign_array_element (name, value)
       return ((SHELL_VAR *)NULL);
     }
 
-  entry = bind_array_variable (vname, ind, value);
+  entry = bind_array_variable (vname, ind, value, flags);
 
   free (vname);
   return (entry);
@@ -187,8 +229,9 @@ find_or_make_array_variable (name, check_flags)
 /* Perform a compound assignment statement for array NAME, where VALUE is
    the text between the parens:  NAME=( VALUE ) */
 SHELL_VAR *
-assign_array_from_string (name, value)
+assign_array_from_string (name, value, flags)
      char *name, *value;
+     int flags;
 {
   SHELL_VAR *var;
 
@@ -196,21 +239,25 @@ assign_array_from_string (name, value)
   if (var == 0)
     return ((SHELL_VAR *)NULL);
 
-  return (assign_array_var_from_string (var, value));
+  return (assign_array_var_from_string (var, value, flags));
 }
 
 /* Sequentially assign the indices of indexed array variable VAR from the
    words in LIST. */
 SHELL_VAR *
-assign_array_var_from_word_list (var, list)
+assign_array_var_from_word_list (var, list, flags)
      SHELL_VAR *var;
      WORD_LIST *list;
+     int flags;
 {
   register arrayind_t i;
   register WORD_LIST *l;
   ARRAY *a;
 
-  for (a = array_cell (var), l = list, i = 0; l; l = l->next, i++)
+  a = array_cell (var);
+  i = (flags & ASS_APPEND) ? array_max_index (a) + 1 : 0;
+
+  for (l = list; l; l = l->next, i++)
     if (var->assign_func)
       (*var->assign_func) (var, l->word->word, i);
     else
@@ -221,9 +268,10 @@ assign_array_var_from_word_list (var, list)
 /* Perform a compound array assignment:  VAR->name=( VALUE ).  The
    VALUE has already had the parentheses stripped. */
 SHELL_VAR *
-assign_array_var_from_string (var, value)
+assign_array_var_from_string (var, value, flags)
      SHELL_VAR *var;
      char *value;
+     int flags;
 {
   ARRAY *a;
   WORD_LIST *list, *nlist;
@@ -271,10 +319,11 @@ assign_array_var_from_string (var, value)
 
   /* Now that we are ready to assign values to the array, kill the existing
      value. */
-  if (a)
+  if (a && (flags & ASS_APPEND) == 0)
     array_flush (a);
+  last_ind = (flags & ASS_APPEND) ? array_max_index (a) + 1 : 0;
 
-  for (last_ind = 0, list = nlist; list; list = list->next)
+  for (list = nlist; list; list = list->next)
     {
       w = list->word->word;
 
@@ -283,9 +332,14 @@ assign_array_var_from_string (var, value)
        {
          len = skipsubscript (w, 0);
 
+#if 1
+         /* XXX - changes for `+=' */
+         if (w[len] != ']' || (w[len+1] != '=' && (w[len+1] != '+' || w[len+2] != '=')))
+#else
          if (w[len] != ']' || w[len+1] != '=')
+#endif
            {
-             nval = make_variable_value (var, w);
+             nval = make_variable_value (var, w, flags);
              if (var->assign_func)
                (*var->assign_func) (var, nval, last_ind);
              else
@@ -314,7 +368,14 @@ assign_array_var_from_string (var, value)
              continue;
            }
          last_ind = ind;
-         val = w + len + 2;
+         /* XXX - changes for `+=' */
+         if (w[len + 1] == '+' && w[len + 2] == '=')
+           {
+             flags |= ASS_APPEND;
+             val = w + len + 3;
+           }
+         else
+           val = w + len + 2;
        }
       else             /* No [ind]=value, just a stray `=' */
        {
@@ -324,12 +385,16 @@ assign_array_var_from_string (var, value)
 
       if (integer_p (var))
        this_command_name = (char *)NULL;       /* no command name for errors */
-      nval = make_variable_value (var, val);
+#if 0
+      nval = make_variable_value (var, val, flags);
       if (var->assign_func)
        (*var->assign_func) (var, nval, ind);
       else
        array_insert (a, ind, nval);
       FREE (nval);
+#else
+      bind_array_var_internal (var, ind, val, flags);
+#endif
       last_ind++;
     }
 
@@ -611,11 +676,7 @@ array_variable_part (s, subp, lenp)
   var = find_variable (t);
 
   free (t);
-#if 1
   return (var == 0 || invisible_p (var)) ? (SHELL_VAR *)0 : var;
-#else
-  return var;
-#endif
 }
 
 /* Return a string containing the elements in the array and subscript
index 375f6f37e0815b4bb7454be55e4d9756ab09bf68..3c4f9a0749ecd900822346afb75616e4641f48dd 100644 (file)
@@ -1,6 +1,6 @@
 /* arrayfunc.h -- declarations for miscellaneous array functions in arrayfunc.c */
 
-/* Copyright (C) 2001 Free Software Foundation, Inc.
+/* Copyright (C) 2001-2004 Free Software Foundation, Inc.
 
    This file is part of GNU Bash, the Bourne Again SHell.
 
 
 extern SHELL_VAR *convert_var_to_array __P((SHELL_VAR *));
 
-extern SHELL_VAR *bind_array_variable __P((char *, arrayind_t, char *));
-extern SHELL_VAR *assign_array_element __P((char *, char *));
+extern SHELL_VAR *bind_array_variable __P((char *, arrayind_t, char *, int));
+extern SHELL_VAR *assign_array_element __P((char *, char *, int));
 
 extern SHELL_VAR *find_or_make_array_variable __P((char *, int));
 
-extern SHELL_VAR *assign_array_from_string  __P((char *, char *));
-extern SHELL_VAR *assign_array_var_from_word_list __P((SHELL_VAR *, WORD_LIST *));
-extern SHELL_VAR *assign_array_var_from_string __P((SHELL_VAR *, char *));
+extern SHELL_VAR *assign_array_from_string  __P((char *, char *, int));
+extern SHELL_VAR *assign_array_var_from_word_list __P((SHELL_VAR *, WORD_LIST *, int));
+extern SHELL_VAR *assign_array_var_from_string __P((SHELL_VAR *, char *, int));
 
 extern int unbind_array_element __P((SHELL_VAR *, char *));
 extern int skipsubscript __P((const char *, int));
diff --git a/arrayfunc.h~ b/arrayfunc.h~
new file mode 100644 (file)
index 0000000..5094744
--- /dev/null
@@ -0,0 +1,55 @@
+/* arrayfunc.h -- declarations for miscellaneous array functions in arrayfunc.c */
+
+/* Copyright (C) 2001 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 2, or (at your option)
+   any later version.
+
+   Bash is distributed in the hope that it will be useful, but WITHOUT
+   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
+   License for more details.
+
+   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, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+
+#if !defined (_ARRAYFUNC_H_)
+#define _ARRAYFUNC_H_
+
+/* Must include variables.h before including this file. */
+
+#if defined (ARRAY_VARS)
+
+extern SHELL_VAR *convert_var_to_array __P((SHELL_VAR *));
+
+extern SHELL_VAR *bind_array_variable __P((char *, arrayind_t, char *, int));
+extern SHELL_VAR *assign_array_element __P((char *, char *, int));
+
+extern SHELL_VAR *find_or_make_array_variable __P((char *, int));
+
+extern SHELL_VAR *assign_array_from_string  __P((char *, char *, int));
+extern SHELL_VAR *assign_array_var_from_word_list __P((SHELL_VAR *, WORD_LIST *, int));
+extern SHELL_VAR *assign_array_var_from_string __P((SHELL_VAR *, char *, int));
+
+extern int unbind_array_element __P((SHELL_VAR *, char *));
+extern int skipsubscript __P((const char *, int));
+extern void print_array_assignment __P((SHELL_VAR *, int));
+
+extern arrayind_t array_expand_index __P((char *, int));
+extern int valid_array_reference __P((char *));
+extern char *array_value __P((char *, int, int *));
+extern char *get_array_value __P((char *, int, int *));
+
+extern char *array_keys __P((char *, int));
+
+extern char *array_variable_name __P((char *, char **, int *));
+extern SHELL_VAR *array_variable_part __P((char *, char **, int *));
+
+#endif
+
+#endif /* !_ARRAYFUNC_H_ */
index 39afbebeafddbede4d3a56cc12f6b09affec5a98..8bf41158cf18baa57e4e7e3b33165e3eab5ed46a 100644 (file)
@@ -1,7 +1,7 @@
 This file is cd.def, from which is created cd.c.  It implements the
 builtins "cd" and "pwd" in Bash.
 
-Copyright (C) 1987-2003 Free Software Foundation, Inc.
+Copyright (C) 1987-2004 Free Software Foundation, Inc.
 
 This file is part of GNU Bash, the Bourne Again SHell.
 
@@ -100,14 +100,14 @@ bindpwd (no_symlinks)
   old_anm = array_needs_making;
   pwdvar = get_string_value ("PWD");
 
-  tvar = bind_variable ("OLDPWD", pwdvar);
+  tvar = bind_variable ("OLDPWD", pwdvar, 0);
   if (old_anm == 0 && array_needs_making && exported_p (tvar))
     {
       update_export_env_inplace ("OLDPWD=", 7, pwdvar);
       array_needs_making = 0;
     }
 
-  tvar = bind_variable ("PWD", dirname ? dirname : "");
+  tvar = bind_variable ("PWD", dirname ? dirname : "", 0);
   if (old_anm == 0 && array_needs_making && exported_p (tvar))
     {
       update_export_env_inplace ("PWD=", 4, dirname ? dirname : "");
index 2be86d11fd9006dc4d92c5bac67689f4ce9a6ac5..f949e6d13ca20b5e2de744847df7a163a34de053 100644 (file)
@@ -100,14 +100,14 @@ bindpwd (no_symlinks)
   old_anm = array_needs_making;
   pwdvar = get_string_value ("PWD");
 
-  tvar = bind_variable ("OLDPWD", pwdvar);
+  tvar = bind_variable ("OLDPWD", pwdvar, 0);
   if (old_anm == 0 && array_needs_making && exported_p (tvar))
     {
       update_export_env_inplace ("OLDPWD=", 7, pwdvar);
       array_needs_making = 0;
     }
 
-  tvar = bind_variable ("PWD", dirname ? dirname : "");
+  tvar = bind_variable ("PWD", dirname ? dirname : "", 0);
   if (old_anm == 0 && array_needs_making && exported_p (tvar))
     {
       update_export_env_inplace ("PWD=", 4, dirname ? dirname : "");
@@ -355,7 +355,7 @@ pwd_builtin (list)
       fflush (stdout);
       if (ferror (stdout))
        {
-         builtin_error (_("write error: %s"), strerror (errno));
+         sh_wrerror ();
          clearerr (stdout);
          return (EXECUTION_FAILURE);
        }
index 757fc280105c27e15a3d8fa02eeaa92ea8dae61b..dbc1e9a14a1e44b3f066a89583ed4af07fcabf85 100644 (file)
@@ -1,7 +1,7 @@
 This file is command.def, from which is created command.c.
 It implements the builtin "command" in Bash.
 
-Copyright (C) 1987-2002 Free Software Foundation, Inc.
+Copyright (C) 1987-2004 Free Software Foundation, Inc.
 
 This file is part of GNU Bash, the Bourne Again SHell.
 
@@ -131,7 +131,7 @@ command_builtin (list)
       add_unwind_protect ((Function *)restore_path, old_path);
 
       standard_path = get_standard_path ();
-      bind_variable ("PATH", standard_path ? standard_path : "");
+      bind_variable ("PATH", standard_path ? standard_path : "", 0);
       FREE (standard_path);
     }
 
@@ -170,7 +170,7 @@ restore_path (var)
 {
   if (var)
     {
-      bind_variable ("PATH", var);
+      bind_variable ("PATH", var, 0);
       free (var);
     }
   else
index 73d395277876e7eed4d37062e82b5f5493930d57..de901075a3eef1facff7f49440353b7ee44ddffc 100644 (file)
@@ -78,7 +78,7 @@ command_builtin (list)
          use_standard_path = 1;
          break;
        case 'V':
-         verbose = CDESC_SHORTDESC;    /* look in common.h for constants */
+         verbose = CDESC_SHORTDESC|CDESC_ABSPATH;      /* look in common.h for constants */
          break;
        case 'v':
          verbose = CDESC_REUSABLE;     /* ditto */
@@ -131,7 +131,7 @@ command_builtin (list)
       add_unwind_protect ((Function *)restore_path, old_path);
 
       standard_path = get_standard_path ();
-      bind_variable ("PATH", standard_path ? standard_path : "");
+      bind_variable ("PATH", standard_path ? standard_path : "", 0);
       FREE (standard_path);
     }
 
@@ -170,7 +170,7 @@ restore_path (var)
 {
   if (var)
     {
-      bind_variable ("PATH", var);
+      bind_variable ("PATH", var, 0);
       free (var);
     }
   else
index 3afed551842e8912100c3bf06a7baa66f5ebf620..d94118f5b739085aec2f02df32df6eea89e248d1 100644 (file)
@@ -1,7 +1,7 @@
 This file is declare.def, from which is created declare.c.
 It implements the builtins "declare" and "local" in Bash.
 
-Copyright (C) 1987-2003 Free Software Foundation, Inc.
+Copyright (C) 1987-2004 Free Software Foundation, Inc.
 
 This file is part of GNU Bash, the Bourne Again SHell.
 
@@ -222,18 +222,24 @@ declare_internal (list, local_var)
   while (list)         /* declare [-afFirx] name [name ...] */
     {
       char *value, *name;
-      int offset;
+      int offset, aflags;
 #if defined (ARRAY_VARS)
       int making_array_special, compound_array_assign, simple_array_assign;
 #endif
 
       name = savestring (list->word->word);
       offset = assignment (name, 0);
+      aflags = 0;
 
       if (offset)      /* declare [-afFirx] name=value */
        {
          name[offset] = '\0';
          value = name + offset + 1;
+         if (name[offset - 1] == '+')
+           {
+             aflags |= ASS_APPEND;
+             name[offset - 1] = '\0';
+           }
        }
       else
        value = "";
@@ -353,7 +359,7 @@ declare_internal (list, local_var)
                var = make_new_array_variable (name);
              else
 #endif
-             var = bind_variable (name, "");
+             var = bind_variable (name, "", 0);
            }
 
          /* Cannot use declare +r to turn off readonly attribute. */ 
@@ -407,23 +413,23 @@ declare_internal (list, local_var)
 
 #if defined (ARRAY_VARS)
          if (offset && compound_array_assign)
-           assign_array_var_from_string (var, value);
+           assign_array_var_from_string (var, value, aflags);
          else if (simple_array_assign && subscript_start)
            {
              /* declare [-a] name[N]=value */
              *subscript_start = '[';   /* ] */
-             var = assign_array_element (name, value);
+             var = assign_array_element (name, value, 0);      /* XXX - not aflags */
              *subscript_start = '\0';
            }
          else if (simple_array_assign)
            /* let bind_array_variable take care of this. */
-           bind_array_variable (name, 0, value);
+           bind_array_variable (name, 0, value, aflags);
          else
 #endif
          /* bind_variable_value duplicates the essential internals of
             bind_variable() */
          if (offset)
-           bind_variable_value (var, value);
+           bind_variable_value (var, value, aflags);
 
          /* If we found this variable in the temporary environment, as with
             `var=value declare -x var', make sure it is treated identically
@@ -443,7 +449,7 @@ declare_internal (list, local_var)
              if (tv)
                {
                  tvalue = var_isset (var) ? savestring (value_cell (var)) : savestring ("");
-                 tv = bind_variable (var->name, tvalue);
+                 tv = bind_variable (var->name, tvalue, 0);
                  tv->attributes |= var->attributes & ~att_tempvar;
                  if (tv->context > 0)
                    VSETATTR (tv, att_propagate);
diff --git a/builtins/declare.def~ b/builtins/declare.def~
new file mode 100644 (file)
index 0000000..b871863
--- /dev/null
@@ -0,0 +1,470 @@
+This file is declare.def, from which is created declare.c.
+It implements the builtins "declare" and "local" in Bash.
+
+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 2, or (at your option) any later
+version.
+
+Bash is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+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, 59 Temple Place, Suite 330, Boston, MA 02111 USA.
+
+$PRODUCES declare.c
+
+$BUILTIN declare
+$FUNCTION declare_builtin
+$SHORT_DOC declare [-afFirtx] [-p] [name[=value] ...]
+Declare variables and/or give them attributes.  If no NAMEs are
+given, then display the values of variables instead.  The -p option
+will display the attributes and values of each NAME.
+
+The flags are:
+
+  -a   to make NAMEs arrays (if supported)
+  -f   to select from among function names only
+  -F   to display function names (and line number and source file name if
+       debugging) without definitions 
+  -i   to make NAMEs have the `integer' attribute
+  -r   to make NAMEs readonly
+  -t   to make NAMEs have the `trace' attribute
+  -x   to make NAMEs export
+
+Variables with the integer attribute have arithmetic evaluation (see
+`let') done when the variable is assigned to.
+
+When displaying values of variables, -f displays a function's name
+and definition.  The -F option restricts the display to function
+name only.
+
+Using `+' instead of `-' turns off the given attribute instead.  When
+used in a function, makes NAMEs local, as with the `local' command.
+$END
+
+$BUILTIN typeset
+$FUNCTION declare_builtin
+$SHORT_DOC typeset [-afFirtx] [-p] name[=value] ...
+Obsolete.  See `declare'.
+$END
+
+#include <config.h>
+
+#if defined (HAVE_UNISTD_H)
+#  ifdef _MINIX
+#    include <sys/types.h>
+#  endif
+#  include <unistd.h>
+#endif
+
+#include <stdio.h>
+
+#include "../bashansi.h"
+#include "../bashintl.h"
+
+#include "../shell.h"
+#include "common.h"
+#include "builtext.h"
+#include "bashgetopt.h"
+
+extern int array_needs_making;
+
+static int declare_internal __P((register WORD_LIST *, int));
+
+/* Declare or change variable attributes. */
+int
+declare_builtin (list)
+     register WORD_LIST *list;
+{
+  return (declare_internal (list, 0));
+}
+
+$BUILTIN local
+$FUNCTION local_builtin
+$SHORT_DOC local name[=value] ...
+Create a local variable called NAME, and give it VALUE.  LOCAL
+can only be used within a function; it makes the variable NAME
+have a visible scope restricted to that function and its children.
+$END
+int
+local_builtin (list)
+     register WORD_LIST *list;
+{
+  if (variable_context)
+    return (declare_internal (list, 1));
+  else
+    {
+      builtin_error (_("can only be used in a function"));
+      return (EXECUTION_FAILURE);
+    }
+}
+
+#if defined (ARRAY_VARS)
+#  define DECLARE_OPTS "+afiprtxF"
+#else
+#  define DECLARE_OPTS "+fiprtxF"
+#endif
+
+/* The workhorse function. */
+static int
+declare_internal (list, local_var)
+     register WORD_LIST *list;
+     int local_var;
+{
+  int flags_on, flags_off, *flags, any_failed, assign_error, pflag, nodefs, opt;
+  char *t, *subscript_start;
+  SHELL_VAR *var;
+  FUNCTION_DEF *shell_fn;
+
+  flags_on = flags_off = any_failed = assign_error = pflag = nodefs = 0;
+  reset_internal_getopt ();
+  while ((opt = internal_getopt (list, DECLARE_OPTS)) != EOF)
+    {
+      flags = list_opttype == '+' ? &flags_off : &flags_on;
+
+      switch (opt)
+       {
+       case 'a':
+#if defined (ARRAY_VARS)
+         *flags |= att_array;
+#endif
+         break;
+       case 'p':
+         if (local_var == 0)
+           pflag++;
+         break;
+        case 'F':
+         nodefs++;
+         *flags |= att_function;
+         break;
+       case 'f':
+         *flags |= att_function;
+         break;
+       case 'i':
+         *flags |= att_integer;
+         break;
+       case 'r':
+         *flags |= att_readonly;
+         break;
+       case 't':
+         *flags |= att_trace;
+         break;
+       case 'x':
+         *flags |= att_exported;
+         array_needs_making = 1;
+         break;
+       default:
+         builtin_usage ();
+         return (EX_USAGE);
+       }
+    }
+
+  list = loptend;
+
+  /* If there are no more arguments left, then we just want to show
+     some variables. */
+  if (list == 0)       /* declare -[afFirtx] */
+    {
+      /* Show local variables defined at this context level if this is
+        the `local' builtin. */
+      if (local_var)
+       {
+         register SHELL_VAR **vlist;
+         register int i;
+
+         vlist = all_local_variables ();
+
+         if (vlist)
+           {
+             for (i = 0; vlist[i]; i++)
+               print_assignment (vlist[i]);
+
+             free (vlist);
+           }
+       }
+      else
+       {
+         if (flags_on == 0)
+           set_builtin ((WORD_LIST *)NULL);
+         else
+           set_or_show_attributes ((WORD_LIST *)NULL, flags_on, nodefs);
+       }
+
+      fflush (stdout);
+      return (EXECUTION_SUCCESS);
+    }
+
+  if (pflag)   /* declare -p [-afFirtx] name [name...] */
+    {
+      for (any_failed = 0; list; list = list->next)
+       {
+         pflag = show_name_attributes (list->word->word, nodefs);
+         if (pflag)
+           {
+             sh_notfound (list->word->word);
+             any_failed++;
+           }
+       }
+      return (any_failed ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
+    }
+
+#define NEXT_VARIABLE() free (name); list = list->next; continue
+
+  /* There are arguments left, so we are making variables. */
+  while (list)         /* declare [-afFirx] name [name ...] */
+    {
+      char *value, *name;
+      int offset, aflags;
+#if defined (ARRAY_VARS)
+      int making_array_special, compound_array_assign, simple_array_assign;
+#endif
+
+      name = savestring (list->word->word);
+      offset = assignment (name, 0);
+      aflags = 0;
+
+      if (offset)      /* declare [-afFirx] name=value */
+       {
+         name[offset] = '\0';
+         value = name + offset + 1;
+         if (name[offset - 1] == '+')
+           {
+             aflags |= ASS_APPEND;
+             name[offset - 1] = '\0';
+           }
+       }
+      else
+       value = "";
+
+#if defined (ARRAY_VARS)
+      compound_array_assign = simple_array_assign = 0;
+      subscript_start = (char *)NULL;
+      if (t = strchr (name, '['))      /* ] */
+       {
+         subscript_start = t;
+         *t = '\0';
+         making_array_special = 1;
+       }
+      else
+       making_array_special = 0;
+#endif
+       
+      if (legal_identifier (name) == 0)
+       {
+         sh_invalidid (name);
+         assign_error++;
+         NEXT_VARIABLE ();
+       }
+
+      /* If VARIABLE_CONTEXT has a non-zero value, then we are executing
+        inside of a function.  This means we should make local variables,
+        not global ones. */
+
+      /* XXX - this has consequences when we're making a local copy of a
+              variable that was in the temporary environment.  Watch out
+              for this. */
+      if (variable_context && ((flags_on & att_function) == 0))
+       {
+#if defined (ARRAY_VARS)
+         if ((flags_on & att_array) || making_array_special)
+           var = make_local_array_variable (name);
+         else
+#endif
+         var = make_local_variable (name);
+         if (var == 0)
+           {
+             any_failed++;
+             NEXT_VARIABLE ();
+           }
+       }
+      else
+       var = (SHELL_VAR *)NULL;
+
+      /* If we are declaring a function, then complain about it in some way.
+        We don't let people make functions by saying `typeset -f foo=bar'. */
+
+      /* There should be a way, however, to let people look at a particular
+        function definition by saying `typeset -f foo'. */
+
+      if (flags_on & att_function)
+       {
+         if (offset)   /* declare -f [-rix] foo=bar */
+           {
+             builtin_error (_("cannot use `-f' to make functions"));
+             free (name);
+             return (EXECUTION_FAILURE);
+           }
+         else          /* declare -f [-rx] name [name...] */
+           {
+             var = find_function (name);
+
+             if (var)
+               {
+                 if (readonly_p (var) && (flags_off & att_readonly))
+                   {
+                     builtin_error (_("%s: readonly function"), name);
+                     any_failed++;
+                     NEXT_VARIABLE ();
+                   }
+
+                 /* declare -[Ff] name [name...] */
+                 if (flags_on == att_function && flags_off == 0)
+                   {
+#if defined (DEBUGGER)
+                     if (nodefs && debugging_mode)
+                       {
+                         shell_fn = find_function_def (var->name);
+                         if (shell_fn)
+                           printf ("%s %d %s\n", var->name, shell_fn->line, shell_fn->source_file);
+                         else
+                           printf ("%s\n", var->name);
+                       }
+                     else
+#endif /* DEBUGGER */
+                       {       
+                         t = nodefs ? var->name
+                                    : named_function_string (name, function_cell (var), 1);
+                         printf ("%s\n", t);
+                       }
+                   }
+                 else          /* declare -[fF] -[rx] name [name...] */
+                   {
+                     VSETATTR (var, flags_on);
+                     VUNSETATTR (var, flags_off);
+                   }
+               }
+             else
+               any_failed++;
+             NEXT_VARIABLE ();
+           }
+       }
+      else             /* declare -[airx] name [name...] */
+       {
+         /* Non-null if we just created or fetched a local variable. */
+         if (var == 0)
+           var = find_variable (name);
+
+         if (var == 0)
+           {
+#if defined (ARRAY_VARS)
+             if ((flags_on & att_array) || making_array_special)
+               var = make_new_array_variable (name);
+             else
+#endif
+             var = bind_variable (name, "", 0);
+           }
+
+         /* Cannot use declare +r to turn off readonly attribute. */ 
+         if (readonly_p (var) && (flags_off & att_readonly))
+           {
+             sh_readonly (name);
+             any_failed++;
+             NEXT_VARIABLE ();
+           }
+
+         /* Cannot use declare to assign value to readonly or noassign
+            variable. */
+         if ((readonly_p (var) || noassign_p (var)) && offset)
+           {
+             if (readonly_p (var))
+               sh_readonly (name);
+             assign_error++;
+             NEXT_VARIABLE ();
+           }
+
+#if defined (ARRAY_VARS)
+         if ((making_array_special || (flags_on & att_array) || array_p (var)) && offset)
+           {
+             int vlen;
+             vlen = STRLEN (value);
+#if 0
+             if (value[0] == '(' && strchr (value, ')'))
+#else
+             if (value[0] == '(' && value[vlen-1] == ')')
+#endif
+               compound_array_assign = 1;
+             else
+               simple_array_assign = 1;
+           }
+
+         /* Cannot use declare +a name to remove an array variable. */
+         if ((flags_off & att_array) && array_p (var))
+           {
+             builtin_error (_("%s: cannot destroy array variables in this way"), name);
+             any_failed++;
+             NEXT_VARIABLE ();
+           }
+
+         /* declare -a name makes name an array variable. */
+         if ((making_array_special || (flags_on & att_array)) && array_p (var) == 0)
+           var = convert_var_to_array (var);
+#endif /* ARRAY_VARS */
+
+         VSETATTR (var, flags_on);
+         VUNSETATTR (var, flags_off);
+
+#if defined (ARRAY_VARS)
+         if (offset && compound_array_assign)
+           assign_array_var_from_string (var, value, aflags);
+         else if (simple_array_assign && subscript_start)
+           {
+             /* declare [-a] name[N]=value */
+             *subscript_start = '[';   /* ] */
+             var = assign_array_element (name, value, 0);      /* XXX - not aflags */
+             *subscript_start = '\0';
+           }
+         else if (simple_array_assign)
+           /* let bind_array_variable take care of this. */
+           bind_array_variable (name, 0, value, aflags);
+         else
+#endif
+         /* bind_variable_value duplicates the essential internals of
+            bind_variable() */
+         if (offset)
+           bind_variable_value (var, value, aflags);
+
+         /* If we found this variable in the temporary environment, as with
+            `var=value declare -x var', make sure it is treated identically
+            to `var=value export var'.  Do the same for `declare -r' and
+            `readonly'.  Preserve the attributes, except for att_tempvar. */
+         /* XXX -- should this create a variable in the global scope, or
+            modify the local variable flags?  ksh93 has it modify the
+            global scope.
+            Need to handle case like in set_var_attribute where a temporary
+            variable is in the same table as the function local vars. */
+         if ((flags_on & (att_exported|att_readonly)) && tempvar_p (var))
+           {
+             SHELL_VAR *tv;
+             char *tvalue;
+
+             tv = find_tempenv_variable (var->name);
+             if (tv)
+               {
+                 tvalue = var_isset (var) ? savestring (value_cell (var)) : savestring ("");
+                 tv = bind_variable (var->name, tvalue, 0);
+                 tv->attributes |= var->attributes & ~att_tempvar;
+                 if (tv->context > 0)
+                   VSETATTR (tv, att_propagate);
+                 free (tvalue);
+               }
+             VSETATTR (var, att_propagate);
+           }
+       }
+
+      stupidly_hack_special_variables (name);
+
+      NEXT_VARIABLE ();
+    }
+
+  return (assign_error ? EX_BADASSIGN
+                      : ((any_failed == 0) ? EXECUTION_SUCCESS
+                                           : EXECUTION_FAILURE));
+}
index eb0a31d14cf4f2464b97a70a6de038d6472e17fa..a9aad62bd57a881e525fe910677d55324aa7f497 100644 (file)
@@ -1,7 +1,7 @@
 This file is getopts.def, from which is created getopts.c.
 It implements the builtin "getopts" in Bash.
 
-Copyright (C) 1987-2002 Free Software Foundation, Inc.
+Copyright (C) 1987-2004 Free Software Foundation, Inc.
 
 This file is part of GNU Bash, the Bourne Again SHell.
 
@@ -101,7 +101,7 @@ getopts_bind_variable (name, value)
 
   if (legal_identifier (name))
     {
-      v = bind_variable (name, value);
+      v = bind_variable (name, value, 0);
       return (v && (readonly_p (v) == 0)) ? EXECUTION_SUCCESS : EXECUTION_FAILURE;
     }
   else
@@ -228,7 +228,7 @@ dogetopts (argc, argv)
        }
       while (n /= 10);
     }
-  bind_variable ("OPTIND", numval + i);
+  bind_variable ("OPTIND", numval + i, 0);
 
   /* If an error occurred, decide which one it is and set the return
      code appropriately.  In all cases, the option character in error
@@ -259,7 +259,7 @@ dogetopts (argc, argv)
        {
          strval[0] = (char)sh_optopt;
          strval[1] = '\0';
-         bind_variable ("OPTARG", strval);
+         bind_variable ("OPTARG", strval, 0);
        }
       else
        unbind_variable ("OPTARG");
@@ -276,7 +276,7 @@ dogetopts (argc, argv)
 
          strval[0] = (char)sh_optopt;
          strval[1] = '\0';
-         bind_variable ("OPTARG", strval);
+         bind_variable ("OPTARG", strval, 0);
        }
       else
        {
@@ -286,7 +286,7 @@ dogetopts (argc, argv)
       return (ret);
     }                  
 
-  bind_variable ("OPTARG", sh_optarg);
+  bind_variable ("OPTARG", sh_optarg, 0);
 
   strval[0] = (char) ret;
   strval[1] = '\0';
diff --git a/builtins/getopts.def~ b/builtins/getopts.def~
new file mode 100644 (file)
index 0000000..9e612fe
--- /dev/null
@@ -0,0 +1,323 @@
+This file is getopts.def, from which is created getopts.c.
+It implements the builtin "getopts" in Bash.
+
+Copyright (C) 1987-2002 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 2, or (at your option) any later
+version.
+
+Bash is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+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, 59 Temple Place, Suite 330, Boston, MA 02111 USA.
+
+$PRODUCES getopts.c
+
+$BUILTIN getopts
+$FUNCTION getopts_builtin
+$SHORT_DOC getopts optstring name [arg]
+Getopts is used by shell procedures to parse positional parameters.
+
+OPTSTRING contains the option letters to be recognized; if a letter
+is followed by a colon, the option is expected to have an argument,
+which should be separated from it by white space.
+
+Each time it is invoked, getopts will place the next option in the
+shell variable $name, initializing name if it does not exist, and
+the index of the next argument to be processed into the shell
+variable OPTIND.  OPTIND is initialized to 1 each time the shell or
+a shell script is invoked.  When an option requires an argument,
+getopts places that argument into the shell variable OPTARG.
+
+getopts reports errors in one of two ways.  If the first character
+of OPTSTRING is a colon, getopts uses silent error reporting.  In
+this mode, no error messages are printed.  If an invalid option is
+seen, getopts places the option character found into OPTARG.  If a
+required argument is not found, getopts places a ':' into NAME and
+sets OPTARG to the option character found.  If getopts is not in
+silent mode, and an invalid option is seen, getopts places '?' into
+NAME and unsets OPTARG.  If a required argument is not found, a '?'
+is placed in NAME, OPTARG is unset, and a diagnostic message is
+printed.
+
+If the shell variable OPTERR has the value 0, getopts disables the
+printing of error messages, even if the first character of
+OPTSTRING is not a colon.  OPTERR has the value 1 by default.
+
+Getopts normally parses the positional parameters ($0 - $9), but if
+more arguments are given, they are parsed instead.
+$END
+
+#include <config.h>
+
+#include <stdio.h>
+
+#if defined (HAVE_UNISTD_H)
+#  ifdef _MINIX
+#    include <sys/types.h>
+#  endif
+#  include <unistd.h>
+#endif
+
+#include "../bashansi.h"
+
+#include "../shell.h"
+#include "common.h"
+#include "bashgetopt.h"
+#include "getopt.h"
+
+#define G_EOF          -1
+#define G_INVALID_OPT  -2
+#define G_ARG_MISSING  -3
+
+extern char *this_command_name;
+
+static int getopts_bind_variable __P((char *, char *));
+static int dogetopts __P((int, char **));
+
+/* getopts_reset is magic code for when OPTIND is reset.  N is the
+   value that has just been assigned to OPTIND. */
+void
+getopts_reset (newind)
+     int newind;
+{
+  sh_optind = newind;
+  sh_badopt = 0;
+}
+
+static int
+getopts_bind_variable (name, value)
+     char *name, *value;
+{
+  SHELL_VAR *v;
+
+  if (legal_identifier (name))
+    {
+      v = bind_variable (name, value, 0);
+      return (v && (readonly_p (v) == 0)) ? EXECUTION_SUCCESS : EXECUTION_FAILURE;
+    }
+  else
+    {
+      sh_invalidid (name);
+      return (EXECUTION_FAILURE);
+    }
+}
+
+/* Error handling is now performed as specified by Posix.2, draft 11
+   (identical to that of ksh-88).  The special handling is enabled if
+   the first character of the option string is a colon; this handling
+   disables diagnostic messages concerning missing option arguments
+   and invalid option characters.  The handling is as follows.
+
+   INVALID OPTIONS:
+        name -> "?"
+        if (special_error) then
+                OPTARG = option character found
+                no error output
+        else
+                OPTARG unset
+                diagnostic message
+        fi
+  MISSING OPTION ARGUMENT;
+        if (special_error) then
+                name -> ":"
+                OPTARG = option character found
+        else
+                name -> "?"
+                OPTARG unset
+                diagnostic message
+        fi
+ */
+
+static int
+dogetopts (argc, argv)
+     int argc;
+     char **argv;
+{
+  int ret, special_error, old_opterr, i, n;
+  char strval[2], numval[16];
+  char *optstr;                        /* list of options */
+  char *name;                  /* variable to get flag val */
+  char *t;
+
+  if (argc < 3)
+    {
+      builtin_usage ();
+      return (EX_USAGE);
+    }
+
+  /* argv[0] is "getopts". */
+
+  optstr = argv[1];
+  name = argv[2];
+  argc -= 2;
+  argv += 2;
+
+  special_error = optstr[0] == ':';
+
+  if (special_error)
+    {
+      old_opterr = sh_opterr;
+      optstr++;
+      sh_opterr = 0;           /* suppress diagnostic messages */
+    }
+
+  if (argc > 1)
+    {
+      sh_getopt_restore_state (argv);
+      t = argv[0];
+      argv[0] = dollar_vars[0];
+      ret = sh_getopt (argc, argv, optstr);
+      argv[0] = t;
+    }
+  else if (rest_of_args == (WORD_LIST *)NULL)
+    {
+      for (i = 0; i < 10 && dollar_vars[i]; i++)
+       ;
+
+      sh_getopt_restore_state (dollar_vars);
+      ret = sh_getopt (i, dollar_vars, optstr);
+    }
+  else
+    {
+      register WORD_LIST *words;
+      char **v;
+
+      for (i = 0; i < 10 && dollar_vars[i]; i++)
+       ;
+      for (words = rest_of_args; words; words = words->next, i++)
+       ;
+      v = strvec_create (i + 1);
+      for (i = 0; i < 10 && dollar_vars[i]; i++)
+       v[i] = dollar_vars[i];
+      for (words = rest_of_args; words; words = words->next, i++)
+       v[i] = words->word->word;
+      v[i] = (char *)NULL;
+      sh_getopt_restore_state (v);
+      ret = sh_getopt (i, v, optstr);
+      free (v);
+    }
+
+  if (special_error)
+    sh_opterr = old_opterr;
+
+  /* Set the OPTIND variable in any case, to handle "--" skipping.  It's
+     highly unlikely that 14 digits will be too few. */
+  if (sh_optind < 10)
+    {
+      numval[14] = sh_optind + '0';
+      numval[15] = '\0';
+      i = 14;
+    }
+  else
+    {
+      numval[i = 15] = '\0';
+      n = sh_optind;
+      do
+       {
+         numval[--i] = (n % 10) + '0';
+       }
+      while (n /= 10);
+    }
+  bind_variable ("OPTIND", numval + i, 0);
+
+  /* If an error occurred, decide which one it is and set the return
+     code appropriately.  In all cases, the option character in error
+     is in OPTOPT.  If an invalid option was encountered, OPTARG is
+     NULL.  If a required option argument was missing, OPTARG points
+     to a NULL string (that is, sh_optarg[0] == 0). */
+  if (ret == '?')
+    {
+      if (sh_optarg == NULL)
+       ret = G_INVALID_OPT;
+      else if (sh_optarg[0] == '\0')
+       ret = G_ARG_MISSING;
+    }
+           
+  if (ret == G_EOF)
+    {
+      unbind_variable ("OPTARG");
+      getopts_bind_variable (name, "?");
+      return (EXECUTION_FAILURE);
+    }
+
+  if (ret == G_INVALID_OPT)
+    {
+      /* Invalid option encountered. */
+      ret = getopts_bind_variable (name, "?");
+
+      if (special_error)
+       {
+         strval[0] = (char)sh_optopt;
+         strval[1] = '\0';
+         bind_variable ("OPTARG", strval, 0);
+       }
+      else
+       unbind_variable ("OPTARG");
+
+      return (ret);
+    }
+
+  if (ret == G_ARG_MISSING)
+    {
+      /* Required argument missing. */
+      if (special_error)
+       {
+         ret = getopts_bind_variable (name, ":");
+
+         strval[0] = (char)sh_optopt;
+         strval[1] = '\0';
+         bind_variable ("OPTARG", strval, 0);
+       }
+      else
+       {
+         ret = getopts_bind_variable (name, "?");
+         unbind_variable ("OPTARG");
+       }
+      return (ret);
+    }                  
+
+  bind_variable ("OPTARG", sh_optarg, 0);
+
+  strval[0] = (char) ret;
+  strval[1] = '\0';
+  return (getopts_bind_variable (name, strval));
+}
+
+/* The getopts builtin.  Build an argv, and call dogetopts with it. */
+int
+getopts_builtin (list)
+     WORD_LIST *list;
+{
+  char **av;
+  int ac, ret;
+
+  if (list == 0)
+    {
+      builtin_usage ();
+      return EX_USAGE;
+    }
+
+  reset_internal_getopt ();
+  if (internal_getopt (list, "") != -1)
+    {
+      builtin_usage ();
+      return (EX_USAGE);
+    }
+  list = loptend;
+
+  av = make_builtin_argv (list, &ac);
+  ret = dogetopts (ac, av);
+  free ((char *)av);
+
+  return (ret);
+}
index 1db11e955382f0a49622504ad637107cae827b9a..bc5504c751c68f17d449dbf7a61a8a75f0168a62 100644 (file)
@@ -1,7 +1,7 @@
 This file is pushd.def, from which is created pushd.c.  It implements the
 builtins "pushd", "popd", and "dirs" in Bash.
 
-Copyright (C) 1987-2003 Free Software Foundation, Inc.
+Copyright (C) 1987-2004 Free Software Foundation, Inc.
 
 This file is part of GNU Bash, the Bourne Again SHell.
 
index 83b69c44a9b0f74ec91d39cb392d8be6a3bd307a..1db11e955382f0a49622504ad637107cae827b9a 100644 (file)
@@ -152,12 +152,17 @@ pushd_builtin (list)
      WORD_LIST *list;
 {
   char *temp, *current_directory, *top;
-  int j, flags;
+  int j, flags, skipopt;
   intmax_t num;
   char direction;
 
   if (list && list->word && ISOPTION (list->word->word, '-'))
-    list = list->next;
+    {
+      list = list->next;
+      skipopt = 1;
+    }
+  else
+    skipopt = 0;
 
   /* If there is no argument list then switch current and
      top of list. */
@@ -181,7 +186,7 @@ pushd_builtin (list)
       return j;
     }
 
-  for (flags = 0; list; list = list->next)
+  for (flags = 0; skipopt == 0 && list; list = list->next)
     {
       if (ISOPTION (list->word->word, 'n'))
        {
index cdac9c4b1fbceb5c34923dd3ea018387b2c7b311..e28b8cde5d4c024041b7c7907b2549f3f811c9cb 100644 (file)
@@ -515,7 +515,7 @@ read_builtin (list)
       if (alist)
        {
          word_list_remove_quoted_nulls (alist);
-         assign_array_var_from_word_list (var, alist);
+         assign_array_var_from_word_list (var, alist, 0);
          dispose_words (alist);
        }
       xfree (input_string);
@@ -544,11 +544,11 @@ read_builtin (list)
       if (saw_escape)
        {
          t = dequote_string (input_string);
-         var = bind_variable ("REPLY", t);
+         var = bind_variable ("REPLY", t, 0);
          free (t);
        }
       else
-       var = bind_variable ("REPLY", input_string);
+       var = bind_variable ("REPLY", input_string, 0);
       VUNSETATTR (var, att_invisible);
 
       free (input_string);
@@ -654,11 +654,11 @@ bind_read_variable (name, value)
 {
 #if defined (ARRAY_VARS)
   if (valid_array_reference (name) == 0)
-    return (bind_variable (name, value));
+    return (bind_variable (name, value, 0));
   else
-    return (assign_array_element (name, value));
+    return (assign_array_element (name, value, 0));
 #else /* !ARRAY_VARS */
-  return bind_variable (name, value);
+  return bind_variable (name, value, 0);
 #endif /* !ARRAY_VARS */
 }
 
diff --git a/builtins/read.def~ b/builtins/read.def~
new file mode 100644 (file)
index 0000000..0446d48
--- /dev/null
@@ -0,0 +1,736 @@
+This file is read.def, from which is created read.c.
+It implements the builtin "read" in Bash.
+
+Copyright (C) 1987-2004 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 2, or (at your option) any later
+version.
+
+Bash is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+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, 59 Temple Place, Suite 330, Boston, MA 02111 USA.
+
+$PRODUCES read.c
+
+$BUILTIN read
+$FUNCTION read_builtin
+$SHORT_DOC read [-ers] [-u fd] [-t timeout] [-p prompt] [-a array] [-n nchars] [-d delim] [name ...]
+One line is read from the standard input, or from file descriptor FD if the
+-u option is supplied, and the first word is assigned to the first NAME,
+the second word to the second NAME, and so on, with leftover words assigned
+to the last NAME.  Only the characters found in $IFS are recognized as word
+delimiters.  If no NAMEs are supplied, the line read is stored in the REPLY
+variable.  If the -r option is given, this signifies `raw' input, and
+backslash escaping is disabled.  The -d option causes read to continue
+until the first character of DELIM is read, rather than newline.  If the -p
+option is supplied, the string PROMPT is output without a trailing newline
+before attempting to read.  If -a is supplied, the words read are assigned
+to sequential indices of ARRAY, starting at zero.  If -e is supplied and
+the shell is interactive, readline is used to obtain the line.  If -n is
+supplied with a non-zero NCHARS argument, read returns after NCHARS
+characters have been read.  The -s option causes input coming from a
+terminal to not be echoed.
+
+The -t option causes read to time out and return failure if a complete line
+of input is not read within TIMEOUT seconds.  If the TMOUT variable is set,
+its value is the default timeout.  The return code is zero, unless end-of-file
+is encountered, read times out, or an invalid file descriptor is supplied as
+the argument to -u.
+$END
+
+#include <config.h>
+
+#include "bashtypes.h"
+#include "posixstat.h"
+
+#include <stdio.h>
+
+#if defined (HAVE_UNISTD_H)
+#  include <unistd.h>
+#endif
+
+#include <signal.h>
+#include <errno.h>
+
+#ifdef __CYGWIN__
+#  include <fcntl.h>
+#  include <io.h>
+#endif
+
+#include "../bashintl.h"
+
+#include "../shell.h"
+#include "common.h"
+#include "bashgetopt.h"
+
+#include <shtty.h>
+
+#if defined (READLINE)
+#include "../bashline.h"
+#include <readline/readline.h>
+#endif
+
+#if !defined(errno)
+extern int errno;
+#endif
+
+extern int interrupt_immediately;
+
+#if defined (READLINE)
+static char *edit_line __P((char *));
+static void set_eol_delim __P((int));
+static void reset_eol_delim __P((char *));
+#endif
+static SHELL_VAR *bind_read_variable __P((char *, char *));
+
+static sighandler sigalrm __P((int));
+static void reset_alarm __P((void));
+
+static procenv_t alrmbuf;
+static SigHandler *old_alrm;
+static unsigned char delim;
+
+static sighandler
+sigalrm (s)
+     int s;
+{
+  longjmp (alrmbuf, 1);
+}
+
+static void
+reset_alarm ()
+{
+  set_signal_handler (SIGALRM, old_alrm);
+  alarm (0);
+}
+
+/* Read the value of the shell variables whose names follow.
+   The reading is done from the current input stream, whatever
+   that may be.  Successive words of the input line are assigned
+   to the variables mentioned in LIST.  The last variable in LIST
+   gets the remainder of the words on the line.  If no variables
+   are mentioned in LIST, then the default variable is $REPLY. */
+int
+read_builtin (list)
+     WORD_LIST *list;
+{
+  register char *varname;
+  int size, i, pass_next, saw_escape, eof, opt, retval, code;
+  int input_is_tty, input_is_pipe, unbuffered_read;
+  int raw, edit, nchars, silent, have_timeout, fd;
+  unsigned int tmout;
+  intmax_t intval;
+  char c;
+  char *input_string, *orig_input_string, *ifs_chars, *prompt, *arrayname;
+  char *e, *t, *t1;
+  struct stat tsb;
+  SHELL_VAR *var;
+#if defined (ARRAY_VARS)
+  WORD_LIST *alist;
+#endif
+#if defined (READLINE)
+  char *rlbuf;
+  int rlind;
+#endif
+
+  USE_VAR(size);
+  USE_VAR(i);
+  USE_VAR(pass_next);
+  USE_VAR(saw_escape);
+  USE_VAR(input_is_pipe);
+/*  USE_VAR(raw); */
+  USE_VAR(edit);
+  USE_VAR(tmout);
+  USE_VAR(nchars);
+  USE_VAR(silent);
+  USE_VAR(ifs_chars);
+  USE_VAR(prompt);
+  USE_VAR(arrayname);
+#if defined (READLINE)
+  USE_VAR(rlbuf);
+  USE_VAR(rlind);
+#endif
+  USE_VAR(list);
+
+  i = 0;               /* Index into the string that we are reading. */
+  raw = edit = 0;      /* Not reading raw input by default. */
+  silent = 0;
+  arrayname = prompt = (char *)NULL;
+  fd = 0;              /* file descriptor to read from */
+
+#if defined (READLINE)
+  rlbuf = (char *)0;
+  rlind = 0;
+#endif
+
+  tmout = 0;           /* no timeout */
+  nchars = input_is_tty = input_is_pipe = unbuffered_read = have_timeout = 0;
+  delim = '\n';                /* read until newline */
+
+  reset_internal_getopt ();
+  while ((opt = internal_getopt (list, "ersa:d:n:p:t:u:")) != -1)
+    {
+      switch (opt)
+       {
+       case 'r':
+         raw = 1;
+         break;
+       case 'p':
+         prompt = list_optarg;
+         break;
+       case 's':
+         silent = 1;
+         break;
+       case 'e':
+#if defined (READLINE)
+         edit = 1;
+#endif
+         break;
+#if defined (ARRAY_VARS)
+       case 'a':
+         arrayname = list_optarg;
+         break;
+#endif
+       case 't':
+         code = legal_number (list_optarg, &intval);
+         if (code == 0 || intval < 0 || intval != (unsigned int)intval)
+           {
+             builtin_error (_("%s: invalid timeout specification"), list_optarg);
+             return (EXECUTION_FAILURE);
+           }
+         else
+           {
+             have_timeout = 1;
+             tmout = intval;
+           }
+         break;
+       case 'n':
+         code = legal_number (list_optarg, &intval);
+         if (code == 0 || intval < 0 || intval != (int)intval)
+           {
+             sh_invalidnum (list_optarg);
+             return (EXECUTION_FAILURE);
+           }
+         else
+           nchars = intval;
+         break;
+       case 'u':
+         code = legal_number (list_optarg, &intval);
+         if (code == 0 || intval < 0 || intval != (int)intval)
+           {
+             builtin_error (_("%s: invalid file descriptor specification"), list_optarg);
+             return (EXECUTION_FAILURE);
+           }
+         else
+           fd = intval;
+         if (sh_validfd (fd) == 0)
+           {
+             builtin_error (_("%d: invalid file descriptor: %s"), fd, strerror (errno));
+             return (EXECUTION_FAILURE);
+           }
+         break;
+       case 'd':
+         delim = *list_optarg;
+         break;
+       default:
+         builtin_usage ();
+         return (EX_USAGE);
+       }
+    }
+  list = loptend;
+
+  /* `read -t 0 var' returns failure immediately.  XXX - should it test
+     whether input is available with select/FIONREAD, and fail if those
+     are unavailable? */
+  if (have_timeout && tmout == 0)
+    return (EXECUTION_FAILURE);
+
+  /* IF IFS is unset, we use the default of " \t\n". */
+  ifs_chars = getifs ();
+  if (ifs_chars == 0)          /* XXX - shouldn't happen */
+    ifs_chars = "";
+
+  input_string = (char *)xmalloc (size = 112); /* XXX was 128 */
+
+  /* $TMOUT, if set, is the default timeout for read. */
+  if (have_timeout == 0 && (e = get_string_value ("TMOUT")))
+    {
+      code = legal_number (e, &intval);
+      if (code == 0 || intval < 0 || intval != (unsigned int)intval)
+       tmout = 0;
+      else
+       tmout = intval;
+    }
+
+  begin_unwind_frame ("read_builtin");
+
+  input_is_tty = isatty (fd);
+  if (input_is_tty == 0)
+#ifndef __CYGWIN__
+    input_is_pipe = (lseek (0, 0L, SEEK_CUR) < 0) && (errno == ESPIPE);
+#else
+    input_is_pipe = 1;
+#endif
+
+  /* If the -p, -e or -s flags were given, but input is not coming from the
+     terminal, turn them off. */
+  if ((prompt || edit || silent) && input_is_tty == 0)
+    {
+      prompt = (char *)NULL;
+      edit = silent = 0;
+    }
+
+#if defined (READLINE)
+  if (edit)
+    add_unwind_protect (xfree, rlbuf);
+#endif
+
+  if (prompt && edit == 0)
+    {
+      fprintf (stderr, "%s", prompt);
+      fflush (stderr);
+    }
+
+  pass_next = 0;       /* Non-zero signifies last char was backslash. */
+  saw_escape = 0;      /* Non-zero signifies that we saw an escape char */
+
+  if (tmout > 0)
+    {
+      /* Turn off the timeout if stdin is a regular file (e.g. from
+        input redirection). */
+      if ((fstat (fd, &tsb) < 0) || S_ISREG (tsb.st_mode))
+       tmout = 0;
+    }
+
+  if (tmout > 0)
+    {
+      code = setjmp (alrmbuf);
+      if (code)
+       {
+         run_unwind_frame ("read_builtin");
+         return (EXECUTION_FAILURE);
+       }
+      old_alrm = set_signal_handler (SIGALRM, sigalrm);
+      add_unwind_protect (reset_alarm, (char *)NULL);
+      alarm (tmout);
+    }
+
+  /* If we've been asked to read only NCHARS chars, or we're using some
+     character other than newline to terminate the line, do the right
+     thing to readline or the tty. */
+  if (nchars > 0 || delim != '\n')
+    {
+#if defined (READLINE)
+      if (edit)
+       {
+         if (nchars > 0)
+           {
+             unwind_protect_int (rl_num_chars_to_read);
+             rl_num_chars_to_read = nchars;
+           }
+         if (delim != '\n')
+           {
+             set_eol_delim (delim);
+             add_unwind_protect (reset_eol_delim, (char *)NULL);
+           }
+       }
+      else
+#endif
+      if (input_is_tty)
+       {
+         ttsave ();
+         if (silent)
+           ttcbreak ();
+         else
+           ttonechar ();
+         add_unwind_protect ((Function *)ttrestore, (char *)NULL);
+       }
+    }
+  else if (silent)     /* turn off echo but leave term in canonical mode */
+    {
+      ttsave ();
+      ttnoecho ();
+      add_unwind_protect ((Function *)ttrestore, (char *)NULL);
+    }
+
+  /* This *must* be the top unwind-protect on the stack, so the manipulation
+     of the unwind-protect stack after the realloc() works right. */
+  add_unwind_protect (xfree, input_string);
+  interrupt_immediately++;
+
+  unbuffered_read = (nchars > 0) || (delim != '\n') || input_is_pipe;
+
+#if defined (__CYGWIN__) && defined (O_TEXT)
+  setmode (0, O_TEXT);
+#endif
+
+  for (eof = retval = 0;;)
+    {
+#if defined (READLINE)
+      if (edit)
+       {
+         if (rlbuf && rlbuf[rlind] == '\0')
+           {
+             xfree (rlbuf);
+             rlbuf = (char *)0;
+           }
+         if (rlbuf == 0)
+           {
+             rlbuf = edit_line (prompt ? prompt : "");
+             rlind = 0;
+           }
+         if (rlbuf == 0)
+           {
+             eof = 1;
+             break;
+           }
+         c = rlbuf[rlind++];
+       }
+      else
+       {
+#endif
+
+      if (unbuffered_read)
+       retval = zread (fd, &c, 1);
+      else
+       retval = zreadc (fd, &c);
+
+      if (retval <= 0)
+       {
+         eof = 1;
+         break;
+       }
+
+#if defined (READLINE)
+       }
+#endif
+
+      if (i + 2 >= size)
+       {
+         input_string = (char *)xrealloc (input_string, size += 128);
+         remove_unwind_protect ();
+         add_unwind_protect (xfree, input_string);
+       }
+
+      /* If the next character is to be accepted verbatim, a backslash
+        newline pair still disappears from the input. */
+      if (pass_next)
+       {
+         if (c == '\n')
+           i--;                /* back up over the CTLESC */
+         else
+           input_string[i++] = c;
+         pass_next = 0;
+         continue;
+       }
+
+      if (c == '\\' && raw == 0)
+       {
+         pass_next++;
+         saw_escape++;
+         input_string[i++] = CTLESC;
+         continue;
+       }
+
+      if ((unsigned char)c == delim)
+       break;
+
+      if (c == CTLESC || c == CTLNUL)
+       {
+         saw_escape++;
+         input_string[i++] = CTLESC;
+       }
+
+      input_string[i++] = c;
+
+      if (nchars > 0 && i >= nchars)
+       break;
+    }
+  input_string[i] = '\0';
+
+#if 1
+  if (retval < 0)
+    {
+      builtin_error (_("read error: %d: %s"), fd, strerror (errno));
+      return (EXECUTION_FAILURE);
+    }
+#endif
+
+  if (tmout > 0)
+    reset_alarm ();
+
+  if (nchars > 0 || delim != '\n')
+    {
+#if defined (READLINE)
+      if (edit)
+       {
+         if (nchars > 0)
+           rl_num_chars_to_read = 0;
+         if (delim != '\n')
+           reset_eol_delim ((char *)NULL);
+       }
+      else
+#endif
+      if (input_is_tty)
+       ttrestore ();
+    }
+  else if (silent)
+    ttrestore ();
+
+  if (unbuffered_read == 0)
+    zsyncfd (fd);
+
+  interrupt_immediately--;
+  discard_unwind_frame ("read_builtin");
+
+  retval = eof ? EXECUTION_FAILURE : EXECUTION_SUCCESS;
+
+#if defined (ARRAY_VARS)
+  /* If -a was given, take the string read, break it into a list of words,
+     an assign them to `arrayname' in turn. */
+  if (arrayname)
+    {
+      if (legal_identifier (arrayname) == 0)
+       {
+         sh_invalidid (arrayname);
+         xfree (input_string);
+         return (EXECUTION_FAILURE);
+       }
+
+      var = find_or_make_array_variable (arrayname, 1);
+      if (var == 0)
+       return EXECUTION_FAILURE;       /* readonly or noassign */
+      array_flush (array_cell (var));
+
+      alist = list_string (input_string, ifs_chars, 0);
+      if (alist)
+       {
+         word_list_remove_quoted_nulls (alist);
+         assign_array_var_from_word_list (var, alist, 0);
+         dispose_words (alist);
+       }
+      xfree (input_string);
+      return (retval);
+    }
+#endif /* ARRAY_VARS */ 
+
+  /* If there are no variables, save the text of the line read to the
+     variable $REPLY.  ksh93 strips leading and trailing IFS whitespace,
+     so that `read x ; echo "$x"' and `read ; echo "$REPLY"' behave the
+     same way, but I believe that the difference in behaviors is useful
+     enough to not do it.  Without the bash behavior, there is no way
+     to read a line completely without interpretation or modification
+     unless you mess with $IFS (e.g., setting it to the empty string).
+     If you disagree, change the occurrences of `#if 0' to `#if 1' below. */
+  if (list == 0)
+    {
+#if 0
+      orig_input_string = input_string;
+      for (t = input_string; ifs_chars && *ifs_chars && spctabnl(*t) && isifs(*t); t++)
+       ;
+      input_string = t;
+      input_string = strip_trailing_ifs_whitespace (input_string, ifs_chars, saw_escape);
+#endif
+
+      if (saw_escape)
+       {
+         t = dequote_string (input_string);
+         var = bind_variable ("REPLY", t);
+         free (t);
+       }
+      else
+       var = bind_variable ("REPLY", input_string);
+      VUNSETATTR (var, att_invisible);
+
+      free (input_string);
+      return (retval);
+    }
+
+  /* This code implements the Posix.2 spec for splitting the words
+     read and assigning them to variables. */
+  orig_input_string = input_string;
+
+  /* Remove IFS white space at the beginning of the input string.  If
+     $IFS is null, no field splitting is performed. */
+  for (t = input_string; ifs_chars && *ifs_chars && spctabnl(*t) && isifs(*t); t++)
+    ;
+  input_string = t;
+
+  for (; list->next; list = list->next)
+    {
+      varname = list->word->word;
+#if defined (ARRAY_VARS)
+      if (legal_identifier (varname) == 0 && valid_array_reference (varname) == 0)
+#else
+      if (legal_identifier (varname) == 0)
+#endif
+       {
+         sh_invalidid (varname);
+         xfree (orig_input_string);
+         return (EXECUTION_FAILURE);
+       }
+
+      /* If there are more variables than words read from the input,
+        the remaining variables are set to the empty string. */
+      if (*input_string)
+       {
+         /* This call updates INPUT_STRING. */
+         t = get_word_from_string (&input_string, ifs_chars, &e);
+         if (t)
+           *e = '\0';
+         /* Don't bother to remove the CTLESC unless we added one
+            somewhere while reading the string. */
+         if (t && saw_escape)
+           {
+             t1 = dequote_string (t);
+             var = bind_read_variable (varname, t1);
+             xfree (t1);
+           }
+         else
+           var = bind_read_variable (varname, t);
+       }
+      else
+       {
+         t = (char *)0;
+         var = bind_read_variable (varname, "");
+       }
+
+      FREE (t);
+      if (var == 0)
+       {
+         xfree (orig_input_string);
+         return (EXECUTION_FAILURE);
+       }
+
+      stupidly_hack_special_variables (varname);
+      VUNSETATTR (var, att_invisible);
+    }
+
+  /* Now assign the rest of the line to the last variable argument. */
+#if defined (ARRAY_VARS)
+  if (legal_identifier (list->word->word) == 0 && valid_array_reference (list->word->word) == 0)
+#else
+  if (legal_identifier (list->word->word) == 0)
+#endif
+    {
+      sh_invalidid (list->word->word);
+      xfree (orig_input_string);
+      return (EXECUTION_FAILURE);
+    }
+
+  /* This has to be done this way rather than using string_list
+     and list_string because Posix.2 says that the last variable gets the
+     remaining words and their intervening separators. */
+  input_string = strip_trailing_ifs_whitespace (input_string, ifs_chars, saw_escape);
+
+  if (saw_escape)
+    {
+      t = dequote_string (input_string);
+      var = bind_read_variable (list->word->word, t);
+      xfree (t);
+    }
+  else
+    var = bind_read_variable (list->word->word, input_string);
+  stupidly_hack_special_variables (list->word->word);
+  if (var)
+    VUNSETATTR (var, att_invisible);
+  xfree (orig_input_string);
+
+  return (retval);
+}
+
+static SHELL_VAR *
+bind_read_variable (name, value)
+     char *name, *value;
+{
+#if defined (ARRAY_VARS)
+  if (valid_array_reference (name) == 0)
+    return (bind_variable (name, value));
+  else
+    return (assign_array_element (name, value, 0));
+#else /* !ARRAY_VARS */
+  return bind_variable (name, value);
+#endif /* !ARRAY_VARS */
+}
+
+#if defined (READLINE)
+static rl_completion_func_t *old_attempted_completion_function;
+
+static char *
+edit_line (p)
+     char *p;
+{
+  char *ret;
+  int len;
+
+  if (!bash_readline_initialized)
+    initialize_readline ();
+  old_attempted_completion_function = rl_attempted_completion_function;
+  rl_attempted_completion_function = (rl_completion_func_t *)NULL;
+  ret = readline (p);
+  rl_attempted_completion_function = old_attempted_completion_function;
+  if (ret == 0)
+    return ret;
+  len = strlen (ret);
+  ret = (char *)xrealloc (ret, len + 2);
+  ret[len++] = delim;
+  ret[len] = '\0';
+  return ret;
+}
+
+static int old_delim_ctype;
+static rl_command_func_t *old_delim_func;
+static int old_newline_ctype;
+static rl_command_func_t *old_newline_func;
+
+static unsigned char delim_char;
+
+static void
+set_eol_delim (c)
+     int c;
+{
+  Keymap cmap;
+
+  if (bash_readline_initialized == 0)
+    initialize_readline ();
+  cmap = rl_get_keymap ();
+
+  /* Change newline to self-insert */
+  old_newline_ctype = cmap[RETURN].type;
+  old_newline_func =  cmap[RETURN].function;
+  cmap[RETURN].type = ISFUNC;
+  cmap[RETURN].function = rl_insert;
+
+  /* Bind the delimiter character to accept-line. */
+  old_delim_ctype = cmap[c].type;
+  old_delim_func = cmap[c].function;
+  cmap[c].type = ISFUNC;
+  cmap[c].function = rl_newline;
+
+  delim_char = c;
+}
+
+static void
+reset_eol_delim (cp)
+     char *cp;
+{
+  Keymap cmap;
+
+  cmap = rl_get_keymap ();
+
+  cmap[RETURN].type = old_newline_ctype;
+  cmap[RETURN].function = old_newline_func;
+
+  cmap[delim_char].type = old_delim_ctype;
+  cmap[delim_char].function = old_delim_func;
+}
+#endif
index 02cc16a133d18490efb93aea67f07a27184a3040..a6046d979f8ceffe7fcd1e259d21f87b01dcb546 100644 (file)
@@ -1,7 +1,7 @@
 This file is set.def, from which is created set.c.
 It implements the "set" and "unset" builtins in Bash.
 
-Copyright (C) 1987-2002 Free Software Foundation, Inc.
+Copyright (C) 1987-2004 Free Software Foundation, Inc.
 
 This file is part of GNU Bash, the Bourne Again SHell.
 
@@ -311,7 +311,7 @@ set_ignoreeof (on_or_off, option_name)
   ignoreeof = on_or_off == FLAG_ON;
   unbind_variable ("ignoreeof");
   if (ignoreeof)
-    bind_variable ("IGNOREEOF", "10"); 
+    bind_variable ("IGNOREEOF", "10", 0); 
   else
     unbind_variable ("IGNOREEOF");
   sv_ignoreeof ("IGNOREEOF");
@@ -327,7 +327,7 @@ set_posix_mode (on_or_off, option_name)
   if (posixly_correct == 0)
     unbind_variable ("POSIXLY_CORRECT");
   else
-    bind_variable ("POSIXLY_CORRECT", "y");
+    bind_variable ("POSIXLY_CORRECT", "y", 0);
   sv_strict_posix ("POSIXLY_CORRECT");
   return (0);
 }
@@ -503,7 +503,7 @@ set_shellopts ()
   else
     exported = 0;
 
-  v = bind_variable ("SHELLOPTS", value);
+  v = bind_variable ("SHELLOPTS", value, 0);
 
   /* Turn the read-only attribute back on, and turn off the export attribute
      if it was set implicitly by mark_modified_vars and SHELLOPTS was not
diff --git a/builtins/set.def~ b/builtins/set.def~
new file mode 100644 (file)
index 0000000..d4e94ed
--- /dev/null
@@ -0,0 +1,829 @@
+This file is set.def, from which is created set.c.
+It implements the "set" and "unset" builtins in Bash.
+
+Copyright (C) 1987-2002 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 2, or (at your option) any later
+version.
+
+Bash is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+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, 59 Temple Place, Suite 330, Boston, MA 02111 USA.
+
+$PRODUCES set.c
+
+#include <config.h>
+
+#if defined (HAVE_UNISTD_H)
+#  ifdef _MINIX
+#    include <sys/types.h>
+#  endif
+#  include <unistd.h>
+#endif
+
+#include <stdio.h>
+
+#include "../bashansi.h"
+#include "../bashintl.h"
+
+#include "../shell.h"
+#include "../flags.h"
+#include "common.h"
+#include "bashgetopt.h"
+
+#if defined (READLINE)
+#  include "../input.h"
+#  include "../bashline.h"
+#  include <readline/readline.h>
+#endif
+
+#if defined (HISTORY)
+#  include "../bashhist.h"
+#endif
+
+extern int posixly_correct, ignoreeof, eof_encountered_limit;
+#if defined (HISTORY)
+extern int dont_save_function_defs;
+#endif
+#if defined (READLINE)
+extern int no_line_editing;
+#endif /* READLINE */
+
+$BUILTIN set
+$FUNCTION set_builtin
+$SHORT_DOC set [--abefhkmnptuvxBCHP] [-o option] [arg ...]
+    -a  Mark variables which are modified or created for export.
+    -b  Notify of job termination immediately.
+    -e  Exit immediately if a command exits with a non-zero status.
+    -f  Disable file name generation (globbing).
+    -h  Remember the location of commands as they are looked up.
+    -k  All assignment arguments are placed in the environment for a
+        command, not just those that precede the command name.
+    -m  Job control is enabled.
+    -n  Read commands but do not execute them.
+    -o option-name
+        Set the variable corresponding to option-name:
+            allexport    same as -a
+            braceexpand  same as -B
+#if defined (READLINE)
+            emacs        use an emacs-style line editing interface
+#endif /* READLINE */
+            errexit      same as -e
+            errtrace     same as -E
+            functrace    same as -T
+            hashall      same as -h
+#if defined (BANG_HISTORY)
+            histexpand   same as -H
+#endif /* BANG_HISTORY */
+#if defined (HISTORY)
+            history      enable command history
+#endif
+            ignoreeof    the shell will not exit upon reading EOF
+            interactive-comments
+                         allow comments to appear in interactive commands
+            keyword      same as -k
+            monitor      same as -m
+            noclobber    same as -C
+            noexec       same as -n
+            noglob       same as -f
+            nolog        currently accepted but ignored
+            notify       same as -b
+            nounset      same as -u
+            onecmd       same as -t
+            physical     same as -P
+            pipefail     the return value of a pipeline is the status of
+                         the last command to exit with a non-zero status,
+                         or zero if no command exited with a non-zero status
+            posix        change the behavior of bash where the default
+                         operation differs from the 1003.2 standard to
+                         match the standard
+            privileged   same as -p
+            verbose      same as -v
+#if defined (READLINE)
+            vi           use a vi-style line editing interface
+#endif /* READLINE */
+            xtrace       same as -x
+    -p  Turned on whenever the real and effective user ids do not match.
+        Disables processing of the $ENV file and importing of shell
+        functions.  Turning this option off causes the effective uid and
+        gid to be set to the real uid and gid.
+    -t  Exit after reading and executing one command.
+    -u  Treat unset variables as an error when substituting.
+    -v  Print shell input lines as they are read.
+    -x  Print commands and their arguments as they are executed.
+#if defined (BRACE_EXPANSION)
+    -B  the shell will perform brace expansion
+#endif /* BRACE_EXPANSION */
+    -C  If set, disallow existing regular files to be overwritten
+        by redirection of output.
+    -E  If set, the ERR trap is inherited by shell functions.
+#if defined (BANG_HISTORY)
+    -H  Enable ! style history substitution.  This flag is on
+        by default.
+#endif /* BANG_HISTORY */
+    -P  If set, do not follow symbolic links when executing commands
+        such as cd which change the current directory.
+    -T  If set, the DEBUG trap is inherited by shell functions.
+
+Using + rather than - causes these flags to be turned off.  The
+flags can also be used upon invocation of the shell.  The current
+set of flags may be found in $-.  The remaining n ARGs are positional
+parameters and are assigned, in order, to $1, $2, .. $n.  If no
+ARGs are given, all shell variables are printed.
+$END
+
+typedef int setopt_set_func_t __P((int, char *));
+typedef int setopt_get_func_t __P((char *));
+
+static void print_minus_o_option __P((char *, int, int));
+static void print_all_shell_variables __P((void));
+
+static int set_ignoreeof __P((int, char *));
+static int set_posix_mode __P((int, char *));
+
+#if defined (READLINE)
+static int set_edit_mode __P((int, char *));
+static int get_edit_mode __P((char *));
+#endif
+
+#if defined (HISTORY)
+static int bash_set_history __P((int, char *));
+#endif
+
+static char *on = "on";
+static char *off = "off";
+
+/* A struct used to match long options for set -o to the corresponding
+   option letter or internal variable.  The functions can be called to
+   dynamically generate values. */
+struct {
+  char *name;
+  int letter;
+  int *variable;
+  setopt_set_func_t *set_func;
+  setopt_get_func_t *get_func;
+} o_options[] = {
+  { "allexport",  'a', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL },
+#if defined (BRACE_EXPANSION)
+  { "braceexpand",'B', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
+#endif
+#if defined (READLINE)
+  { "emacs",     '\0', (int *)NULL, set_edit_mode, get_edit_mode },
+#endif
+  { "errexit",   'e', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
+  { "errtrace",          'E', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
+  { "functrace",  'T', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
+  { "hashall",    'h', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
+#if defined (BANG_HISTORY)
+  { "histexpand", 'H', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
+#endif /* BANG_HISTORY */
+#if defined (HISTORY)
+  { "history",   '\0', &remember_on_history, bash_set_history, (setopt_get_func_t *)NULL },
+#endif
+  { "ignoreeof", '\0', &ignoreeof, set_ignoreeof, (setopt_get_func_t *)NULL },
+  { "interactive-comments", '\0', &interactive_comments, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL },
+  { "keyword",    'k', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
+  { "monitor",   'm', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
+  { "noclobber",  'C', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
+  { "noexec",    'n', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
+  { "noglob",    'f', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
+#if defined (HISTORY)
+  { "nolog",     '\0', &dont_save_function_defs, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL },
+#endif
+#if defined (JOB_CONTROL)
+  { "notify",    'b', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
+#endif /* JOB_CONTROL */
+  { "nounset",   'u', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
+  { "onecmd",    't', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL },
+  { "physical",   'P', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
+  { "pipefail",  '\0', &pipefail_opt, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
+  { "posix",     '\0', &posixly_correct, set_posix_mode, (setopt_get_func_t *)NULL },
+  { "privileged", 'p', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
+  { "verbose",   'v', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
+#if defined (READLINE)
+  { "vi",        '\0', (int *)NULL, set_edit_mode, get_edit_mode },
+#endif
+  { "xtrace",    'x', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
+  {(char *)NULL, 0 , (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL },
+};
+
+#define N_O_OPTIONS    (sizeof (o_options) / sizeof (o_options[0]))
+
+#define GET_BINARY_O_OPTION_VALUE(i, name) \
+  ((o_options[i].get_func) ? (*o_options[i].get_func) (name) \
+                          : (*o_options[i].variable))
+
+#define SET_BINARY_O_OPTION_VALUE(i, onoff, name) \
+  ((o_options[i].set_func) ? (*o_options[i].set_func) (onoff, name) \
+                          : (*o_options[i].variable = (onoff == FLAG_ON)))
+
+int
+minus_o_option_value (name)
+     char *name;
+{
+  register int i;
+  int *on_or_off;
+
+  for (i = 0; o_options[i].name; i++)
+    {
+      if (STREQ (name, o_options[i].name))
+       {
+         if (o_options[i].letter)
+           {
+             on_or_off = find_flag (o_options[i].letter);
+             return ((on_or_off == FLAG_UNKNOWN) ? -1 : *on_or_off);
+           }
+         else
+           return (GET_BINARY_O_OPTION_VALUE (i, name));
+       }
+    }
+
+  return (-1);
+}
+
+#define MINUS_O_FORMAT "%-15s\t%s\n"
+
+static void
+print_minus_o_option (name, value, pflag)
+     char *name;
+     int value, pflag;
+{
+  if (pflag == 0)
+    printf (MINUS_O_FORMAT, name, value ? on : off);
+  else
+    printf ("set %co %s\n", value ? '-' : '+', name);
+}
+
+void
+list_minus_o_opts (mode, reusable)
+     int mode, reusable;
+{
+  register int i;
+  int *on_or_off, value;
+
+  for (i = 0; o_options[i].name; i++)
+    {
+      if (o_options[i].letter)
+       {
+         value = 0;
+         on_or_off = find_flag (o_options[i].letter);
+         if (on_or_off == FLAG_UNKNOWN)
+           on_or_off = &value;
+         if (mode == -1 || mode == *on_or_off)
+           print_minus_o_option (o_options[i].name, *on_or_off, reusable);
+       }
+      else
+       {
+         value = GET_BINARY_O_OPTION_VALUE (i, o_options[i].name);
+         if (mode == -1 || mode == value)
+           print_minus_o_option (o_options[i].name, value, reusable);
+       }
+    }
+}
+
+char **
+get_minus_o_opts ()
+{
+  char **ret;
+  int i;
+
+  ret = strvec_create (N_O_OPTIONS + 1);
+  for (i = 0; o_options[i].name; i++)
+    ret[i] = o_options[i].name;
+  ret[i] = (char *)NULL;
+  return ret;
+}
+
+static int
+set_ignoreeof (on_or_off, option_name)
+     int on_or_off;
+     char *option_name;
+{
+  ignoreeof = on_or_off == FLAG_ON;
+  unbind_variable ("ignoreeof");
+  if (ignoreeof)
+    bind_variable ("IGNOREEOF", "10", 0); 
+  else
+    unbind_variable ("IGNOREEOF");
+  sv_ignoreeof ("IGNOREEOF");
+  return 0;
+}
+
+static int
+set_posix_mode (on_or_off, option_name)
+     int on_or_off;
+     char *option_name;
+{
+  posixly_correct = on_or_off == FLAG_ON;
+  if (posixly_correct == 0)
+    unbind_variable ("POSIXLY_CORRECT");
+  else
+    bind_variable ("POSIXLY_CORRECT", "y", 0);
+  sv_strict_posix ("POSIXLY_CORRECT");
+  return (0);
+}
+
+#if defined (READLINE)
+/* Magic.  This code `knows' how readline handles rl_editing_mode. */
+static int
+set_edit_mode (on_or_off, option_name)
+     int on_or_off;
+     char *option_name;
+{
+  int isemacs;
+
+  if (on_or_off == FLAG_ON)
+    {
+      rl_variable_bind ("editing-mode", option_name);
+
+      if (interactive)
+       with_input_from_stdin ();
+      no_line_editing = 0;
+    }
+  else
+    {
+      isemacs = rl_editing_mode == 1;
+      if ((isemacs && *option_name == 'e') || (!isemacs && *option_name == 'v'))
+       {
+         if (interactive)
+           with_input_from_stream (stdin, "stdin");
+         no_line_editing = 1;
+       }
+    }
+  return 1-no_line_editing;
+}
+
+static int
+get_edit_mode (name)
+     char *name;
+{
+  return (*name == 'e' ? no_line_editing == 0 && rl_editing_mode == 1
+                      : no_line_editing == 0 && rl_editing_mode == 0);
+}
+#endif /* READLINE */
+
+#if defined (HISTORY)
+static int
+bash_set_history (on_or_off, option_name)
+     int on_or_off;
+     char *option_name;
+{
+  if (on_or_off == FLAG_ON)
+    {
+      bash_history_enable ();
+      if (history_lines_this_session == 0)
+       load_history ();
+    }
+  else
+    bash_history_disable ();
+  return (1 - remember_on_history);
+}
+#endif
+
+int
+set_minus_o_option (on_or_off, option_name)
+     int on_or_off;
+     char *option_name;
+{
+  register int i;
+
+  for (i = 0; o_options[i].name; i++)
+    {
+      if (STREQ (option_name, o_options[i].name))
+       {
+         if (o_options[i].letter == 0)
+           {
+             SET_BINARY_O_OPTION_VALUE (i, on_or_off, option_name);
+             return (EXECUTION_SUCCESS);
+           }
+         else
+           {
+             if (change_flag (o_options[i].letter, on_or_off) == FLAG_ERROR)
+               {
+                 sh_invalidoptname (option_name);
+                 return (EXECUTION_FAILURE);
+               }
+             else
+               return (EXECUTION_SUCCESS);
+           }
+
+       }
+    }
+
+  sh_invalidoptname (option_name);
+  return (EXECUTION_FAILURE);
+}
+
+static void
+print_all_shell_variables ()
+{
+  SHELL_VAR **vars;
+
+  vars = all_shell_variables ();
+  if (vars)
+    {
+      print_var_list (vars);
+      free (vars);
+    }
+
+  /* POSIX.2 does not allow function names and definitions to be output when
+     `set' is invoked without options (PASC Interp #202). */
+  if (posixly_correct == 0)
+    {
+      vars = all_shell_functions ();
+      if (vars)
+       {
+         print_func_list (vars);
+         free (vars);
+       }
+    }
+}
+
+void
+set_shellopts ()
+{
+  char *value;
+  char tflag[N_O_OPTIONS];
+  int vsize, i, vptr, *ip, exported;
+  SHELL_VAR *v;
+
+  for (vsize = i = 0; o_options[i].name; i++)
+    {
+      tflag[i] = 0;
+      if (o_options[i].letter)
+       {
+         ip = find_flag (o_options[i].letter);
+         if (ip && *ip)
+           {
+             vsize += strlen (o_options[i].name) + 1;
+             tflag[i] = 1;
+           }
+       }
+      else if (GET_BINARY_O_OPTION_VALUE (i, o_options[i].name))
+       {
+         vsize += strlen (o_options[i].name) + 1;
+         tflag[i] = 1;
+       }
+    }
+
+  value = (char *)xmalloc (vsize + 1);
+
+  for (i = vptr = 0; o_options[i].name; i++)
+    {
+      if (tflag[i])
+       {
+         strcpy (value + vptr, o_options[i].name);
+         vptr += strlen (o_options[i].name);
+         value[vptr++] = ':';
+       }
+    }
+
+  if (vptr)
+    vptr--;                    /* cut off trailing colon */
+  value[vptr] = '\0';
+
+  v = find_variable ("SHELLOPTS");
+
+  /* Turn off the read-only attribute so we can bind the new value, and
+     note whether or not the variable was exported. */
+  if (v)
+    {
+      VUNSETATTR (v, att_readonly);
+      exported = exported_p (v);
+    }
+  else
+    exported = 0;
+
+  v = bind_variable ("SHELLOPTS", value, 0);
+
+  /* Turn the read-only attribute back on, and turn off the export attribute
+     if it was set implicitly by mark_modified_vars and SHELLOPTS was not
+     exported before we bound the new value. */
+  VSETATTR (v, att_readonly);
+  if (mark_modified_vars && exported == 0 && exported_p (v))
+    VUNSETATTR (v, att_exported);
+
+  free (value);
+}
+
+void
+parse_shellopts (value)
+     char *value;
+{
+  char *vname;
+  int vptr;
+
+  vptr = 0;
+  while (vname = extract_colon_unit (value, &vptr))
+    {
+      set_minus_o_option (FLAG_ON, vname);
+      free (vname);
+    }
+}
+
+void
+initialize_shell_options (no_shellopts)
+     int no_shellopts;
+{
+  char *temp;
+  SHELL_VAR *var;
+
+  if (no_shellopts == 0)
+    {
+      var = find_variable ("SHELLOPTS");
+      /* set up any shell options we may have inherited. */
+      if (var && imported_p (var))
+       {
+         temp = (array_p (var)) ? (char *)NULL : savestring (value_cell (var));
+         if (temp)
+           {
+             parse_shellopts (temp);
+             free (temp);
+           }
+       }
+    }
+
+  /* Set up the $SHELLOPTS variable. */
+  set_shellopts ();
+}
+
+/* Reset the values of the -o options that are not also shell flags.  This is
+   called from execute_cmd.c:initialize_subshell() when setting up a subshell
+   to run an executable shell script without a leading `#!'. */
+void
+reset_shell_options ()
+{
+#if defined (HISTORY)
+  remember_on_history = 1;
+#endif
+  ignoreeof = 0;
+}
+
+/* Set some flags from the word values in the input list.  If LIST is empty,
+   then print out the values of the variables instead.  If LIST contains
+   non-flags, then set $1 - $9 to the successive words of LIST. */
+int
+set_builtin (list)
+     WORD_LIST *list;
+{
+  int on_or_off, flag_name, force_assignment, opts_changed;
+  WORD_LIST *l;
+  register char *arg;
+  char s[3];
+
+  if (list == 0)
+    {
+      print_all_shell_variables ();
+      return (EXECUTION_SUCCESS);
+    }
+
+  /* Check validity of flag arguments. */
+  reset_internal_getopt ();
+  while ((flag_name = internal_getopt (list, optflags)) != -1)
+    {
+      switch (flag_name)
+       {
+         case '?':
+           builtin_usage ();
+           return (list_optopt == '?' ? EXECUTION_SUCCESS : EX_USAGE);
+         default:
+           break;
+       }
+    }
+    
+  /* Do the set command.  While the list consists of words starting with
+     '-' or '+' treat them as flags, otherwise, start assigning them to
+     $1 ... $n. */
+  for (force_assignment = opts_changed = 0; list; )
+    {
+      arg = list->word->word;
+
+      /* If the argument is `--' or `-' then signal the end of the list
+        and remember the remaining arguments. */
+      if (arg[0] == '-' && (!arg[1] || (arg[1] == '-' && !arg[2])))
+       {
+         list = list->next;
+
+         /* `set --' unsets the positional parameters. */
+         if (arg[1] == '-')
+           force_assignment = 1;
+
+         /* Until told differently, the old shell behaviour of
+            `set - [arg ...]' being equivalent to `set +xv [arg ...]'
+            stands.  Posix.2 says the behaviour is marked as obsolescent. */
+         else
+           {
+             change_flag ('x', '+');
+             change_flag ('v', '+');
+             opts_changed = 1;
+           }
+
+         break;
+       }
+
+      if ((on_or_off = *arg) && (on_or_off == '-' || on_or_off == '+'))
+       {
+         while (flag_name = *++arg)
+           {
+             if (flag_name == '?')
+               {
+                 builtin_usage ();
+                 return (EXECUTION_SUCCESS);
+               }
+             else if (flag_name == 'o') /* -+o option-name */
+               {
+                 char *option_name;
+                 WORD_LIST *opt;
+
+                 opt = list->next;
+
+                 if (opt == 0)
+                   {
+                     list_minus_o_opts (-1, (on_or_off == '+'));
+                     continue;
+                   }
+
+                 option_name = opt->word->word;
+
+                 if (option_name == 0 || *option_name == '\0' ||
+                     *option_name == '-' || *option_name == '+')
+                   {
+                     list_minus_o_opts (-1, (on_or_off == '+'));
+                     continue;
+                   }
+                 list = list->next; /* Skip over option name. */
+
+                 opts_changed = 1;
+                 if (set_minus_o_option (on_or_off, option_name) != EXECUTION_SUCCESS)
+                   {
+                     set_shellopts ();
+                     return (EXECUTION_FAILURE);
+                   }
+               }
+             else if (change_flag (flag_name, on_or_off) == FLAG_ERROR)
+               {
+                 s[0] = on_or_off;
+                 s[1] = flag_name;
+                 s[2] = '\0';
+                 sh_invalidopt (s);
+                 builtin_usage ();
+                 set_shellopts ();
+                 return (EXECUTION_FAILURE);
+               }
+             opts_changed = 1;
+           }
+       }
+      else
+       {
+         break;
+       }
+      list = list->next;
+    }
+
+  /* Assigning $1 ... $n */
+  if (list || force_assignment)
+    remember_args (list, 1);
+  /* Set up new value of $SHELLOPTS */
+  if (opts_changed)
+    set_shellopts ();
+  return (EXECUTION_SUCCESS);
+}
+
+$BUILTIN unset
+$FUNCTION unset_builtin
+$SHORT_DOC unset [-f] [-v] [name ...]
+For each NAME, remove the corresponding variable or function.  Given
+the `-v', unset will only act on variables.  Given the `-f' flag,
+unset will only act on functions.  With neither flag, unset first
+tries to unset a variable, and if that fails, then tries to unset a
+function.  Some variables cannot be unset; also see readonly.
+$END
+
+#define NEXT_VARIABLE()        any_failed++; list = list->next; continue;
+
+int
+unset_builtin (list)
+  WORD_LIST *list;
+{
+  int unset_function, unset_variable, unset_array, opt, any_failed;
+  char *name;
+
+  unset_function = unset_variable = unset_array = any_failed = 0;
+
+  reset_internal_getopt ();
+  while ((opt = internal_getopt (list, "fv")) != -1)
+    {
+      switch (opt)
+       {
+       case 'f':
+         unset_function = 1;
+         break;
+       case 'v':
+         unset_variable = 1;
+         break;
+       default:
+         builtin_usage ();
+         return (EX_USAGE);
+       }
+    }
+
+  list = loptend;
+
+  if (unset_function && unset_variable)
+    {
+      builtin_error (_("cannot simultaneously unset a function and a variable"));
+      return (EXECUTION_FAILURE);
+    }
+
+  while (list)
+    {
+      SHELL_VAR *var;
+      int tem;
+#if defined (ARRAY_VARS)
+      char *t;
+#endif
+
+      name = list->word->word;
+
+#if defined (ARRAY_VARS)
+      unset_array = 0;
+      if (!unset_function && valid_array_reference (name))
+       {
+         t = strchr (name, '[');
+         *t++ = '\0';
+         unset_array++;
+       }
+#endif
+
+      /* Bash allows functions with names which are not valid identifiers
+        to be created when not in posix mode, so check only when in posix
+        mode when unsetting a function. */
+      if (((unset_function && posixly_correct) || !unset_function) && legal_identifier (name) == 0)
+       {
+         sh_invalidid (name);
+         NEXT_VARIABLE ();
+       }
+
+      var = unset_function ? find_function (name) : find_variable (name);
+
+      if (var && !unset_function && non_unsettable_p (var))
+       {
+         builtin_error (_("%s: cannot unset"), name);
+         NEXT_VARIABLE ();
+       }
+
+      /* Posix.2 says that unsetting readonly variables is an error. */
+      if (var && readonly_p (var))
+       {
+         builtin_error (_("%s: cannot unset: readonly %s"),
+                        name, unset_function ? "function" : "variable");
+         NEXT_VARIABLE ();
+       }
+
+      /* Unless the -f option is supplied, the name refers to a variable. */
+#if defined (ARRAY_VARS)
+      if (var && unset_array)
+       {
+         if (array_p (var) == 0)
+           {
+             builtin_error (_("%s: not an array variable"), name);
+             NEXT_VARIABLE ();
+           }
+         else
+           {
+             tem = unbind_array_element (var, t);
+             if (tem == -1)
+               any_failed++;
+           }
+       }
+      else
+#endif /* ARRAY_VARS */
+      tem = unset_function ? unbind_func (name) : unbind_variable (name);
+
+      /* This is what Posix.2 draft 11+ says.  ``If neither -f nor -v
+        is specified, the name refers to a variable; if a variable by
+        that name does not exist, a function by that name, if any,
+        shall be unset.'' */
+      if (tem == -1 && !unset_function && !unset_variable)
+       tem = unbind_func (name);
+
+      /* SUSv3, POSIX.1-2001 say:  ``Unsetting a variable or function that
+        was not previously set shall not be considered an error.'' */
+
+      if (unset_function == 0)
+       stupidly_hack_special_variables (name);
+
+      list = list->next;
+    }
+
+  return (any_failed ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
+}
index d211dbc444f0194a128674541f7e39a488d02330..a98622c59b1eddab5c3eb8ad681da5c24de338d3 100644 (file)
@@ -1,7 +1,7 @@
 This file is setattr.def, from which is created setattr.c.
 It implements the builtins "export" and "readonly", in Bash.
 
-Copyright (C) 1987-2003 Free Software Foundation, Inc.
+Copyright (C) 1987-2004 Free Software Foundation, Inc.
 
 This file is part of GNU Bash, the Bourne Again SHell.
 
@@ -111,6 +111,7 @@ set_or_show_attributes (list, attribute, nodefs)
 {
   register SHELL_VAR *var;
   int assign, undo, functions_only, arrays_only, any_failed, assign_error, opt;
+  int aflags;
   char *name;
 #if defined (ARRAY_VARS)
   WORD_LIST *nlist, *tlist;
@@ -175,8 +176,16 @@ set_or_show_attributes (list, attribute, nodefs)
          /* xxx [-np] name[=value] */
          assign = assignment (name, 0);
 
+         aflags = 0;
          if (assign)
-           name[assign] = '\0';
+           {
+             name[assign] = '\0';
+             if (name[assign - 1] == '+')
+               {
+                 aflags |= ASS_APPEND;
+                 name[assign - 1] = '\0';
+               }
+           }
 
          if (legal_identifier (name) == 0)
            {
@@ -192,6 +201,8 @@ set_or_show_attributes (list, attribute, nodefs)
          if (assign)   /* xxx [-np] name=value */
            {
              name[assign] = '=';
+             if (aflags & ASS_APPEND)
+               name[assign - 1] = '+';
 #if defined (ARRAY_VARS)
              /* Let's try something here.  Turn readonly -a xxx=yyy into
                 declare -ra xxx=yyy and see what that gets us. */
@@ -217,6 +228,8 @@ set_or_show_attributes (list, attribute, nodefs)
              if (do_assignment_no_expand (name) == 0)
                assign_error++;
              name[assign] = '\0';
+             if (aflags & ASS_APPEND)
+               name[assign - 1] = '\0';
            }
 
          set_var_attribute (name, attribute, undo);
@@ -396,7 +409,7 @@ set_var_attribute (name, attribute, undo)
        {
          tvalue = var_isset (tv) ? savestring (value_cell (tv)) : savestring ("");
 
-         var = bind_variable (tv->name, tvalue);
+         var = bind_variable (tv->name, tvalue, 0);
          var->attributes |= tv->attributes & ~att_tempvar;
          VSETATTR (tv, att_propagate);
          if (var->context != 0)
@@ -410,7 +423,7 @@ set_var_attribute (name, attribute, undo)
          var = find_variable_internal (name, 0);
          if (var == 0)
            {
-             var = bind_variable (name, (char *)NULL);
+             var = bind_variable (name, (char *)NULL, 0);
              VSETATTR (var, att_invisible);
            }
          else if (var->context != 0)
diff --git a/builtins/setattr.def~ b/builtins/setattr.def~
new file mode 100644 (file)
index 0000000..46fd8a9
--- /dev/null
@@ -0,0 +1,439 @@
+This file is setattr.def, from which is created setattr.c.
+It implements the builtins "export" and "readonly", in Bash.
+
+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 2, or (at your option) any later
+version.
+
+Bash is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+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, 59 Temple Place, Suite 330, Boston, MA 02111 USA.
+
+$PRODUCES setattr.c
+
+#include <config.h>
+
+#if defined (HAVE_UNISTD_H)
+#  ifdef _MINIX
+#    include <sys/types.h>
+#  endif
+#  include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include "../bashansi.h"
+#include "../bashintl.h"
+
+#include "../shell.h"
+#include "common.h"
+#include "bashgetopt.h"
+
+extern int posixly_correct;
+extern int array_needs_making;
+extern char *this_command_name;
+extern sh_builtin_func_t *this_shell_builtin;
+
+#ifdef ARRAY_VARS
+extern int declare_builtin __P((WORD_LIST *));
+#endif
+
+#define READONLY_OR_EXPORT \
+  (this_shell_builtin == readonly_builtin || this_shell_builtin == export_builtin)
+
+$BUILTIN export
+$FUNCTION export_builtin
+$SHORT_DOC export [-nf] [name[=value] ...] or export -p
+NAMEs are marked for automatic export to the environment of
+subsequently executed commands.  If the -f option is given,
+the NAMEs refer to functions.  If no NAMEs are given, or if `-p'
+is given, a list of all names that are exported in this shell is
+printed.  An argument of `-n' says to remove the export property
+from subsequent NAMEs.  An argument of `--' disables further option
+processing.
+$END
+
+/* For each variable name in LIST, make that variable appear in the
+   environment passed to simple commands.  If there is no LIST, then
+   print all such variables.  An argument of `-n' says to remove the
+   exported attribute from variables named in LIST.  An argument of
+  -f indicates that the names present in LIST refer to functions. */
+int
+export_builtin (list)
+     register WORD_LIST *list;
+{
+  return (set_or_show_attributes (list, att_exported, 0));
+}
+
+$BUILTIN readonly
+$FUNCTION readonly_builtin
+$SHORT_DOC readonly [-af] [name[=value] ...] or readonly -p
+The given NAMEs are marked readonly and the values of these NAMEs may
+not be changed by subsequent assignment.  If the -f option is given,
+then functions corresponding to the NAMEs are so marked.  If no
+arguments are given, or if `-p' is given, a list of all readonly names
+is printed.  The `-a' option means to treat each NAME as
+an array variable.  An argument of `--' disables further option
+processing.
+$END
+
+/* For each variable name in LIST, make that variable readonly.  Given an
+   empty LIST, print out all existing readonly variables. */
+int
+readonly_builtin (list)
+     register WORD_LIST *list;
+{
+  return (set_or_show_attributes (list, att_readonly, 0));
+}
+
+#if defined (ARRAY_VARS)
+#  define ATTROPTS     "afnp"
+#else
+#  define ATTROPTS     "fnp"
+#endif
+
+/* For each variable name in LIST, make that variable have the specified
+   ATTRIBUTE.  An arg of `-n' says to remove the attribute from the the
+   remaining names in LIST (doesn't work for readonly). */
+int
+set_or_show_attributes (list, attribute, nodefs)
+     register WORD_LIST *list;
+     int attribute, nodefs;
+{
+  register SHELL_VAR *var;
+  int assign, undo, functions_only, arrays_only, any_failed, assign_error, opt;
+  int aflags;
+  char *name;
+#if defined (ARRAY_VARS)
+  WORD_LIST *nlist, *tlist;
+  WORD_DESC *w;
+#endif
+
+  undo = functions_only = arrays_only = any_failed = assign_error = 0;
+  /* Read arguments from the front of the list. */
+  reset_internal_getopt ();
+  while ((opt = internal_getopt (list, ATTROPTS)) != -1)
+    {
+      switch (opt)
+       {
+         case 'n':
+           undo = 1;
+           break;
+         case 'f':
+           functions_only = 1;
+           break;
+#if defined (ARRAY_VARS)
+         case 'a':
+            arrays_only = 1;
+            break;
+#endif
+         case 'p':
+           break;
+         default:
+           builtin_usage ();
+           return (EX_USAGE);
+       }
+    }
+  list = loptend;
+
+  if (list)
+    {
+      if (attribute & att_exported)
+       array_needs_making = 1;
+
+      /* Cannot undo readonly status, silently disallowed. */
+      if (undo && (attribute & att_readonly))
+       attribute &= ~att_readonly;
+
+      while (list)
+       {
+         name = list->word->word;
+
+         if (functions_only)           /* xxx -f name */
+           {
+             var = find_function (name);
+             if (var == 0)
+               {
+                 builtin_error (_("%s: not a function"), name);
+                 any_failed++;
+               }
+             else
+               SETVARATTR (var, attribute, undo);
+
+             list = list->next;
+             continue;
+           }
+
+         /* xxx [-np] name[=value] */
+         assign = assignment (name, 0);
+
+         aflags = 0;
+         if (assign)
+           {
+             name[assign] = '\0';
+             if (name[assign - 1] == '+')
+               {
+                 aflags |= ASS_APPEND;
+                 name[assign - 1] = '\0';
+               }
+           }
+
+         if (legal_identifier (name) == 0)
+           {
+             sh_invalidid (name);
+             if (assign)
+               assign_error++;
+             else
+               any_failed++;
+             list = list->next;
+             continue;
+           }
+
+         if (assign)   /* xxx [-np] name=value */
+           {
+             name[assign] = '=';
+             if (aflags & ASS_APPEND)
+               name[assign - 1] = '+';
+#if defined (ARRAY_VARS)
+             /* Let's try something here.  Turn readonly -a xxx=yyy into
+                declare -ra xxx=yyy and see what that gets us. */
+             if (arrays_only)
+               {
+                 tlist = list->next;
+                 list->next = (WORD_LIST *)NULL;
+                 w = make_word ("-ra");
+                 nlist = make_word_list (w, list);
+                 opt = declare_builtin (nlist);
+                 if (opt != EXECUTION_SUCCESS)
+                   assign_error++;
+                 list->next = tlist;
+                 dispose_word (w);
+                 free (nlist);
+               }
+             else
+#endif
+             /* This word has already been expanded once with command
+                and parameter expansion.  Call do_assignment_no_expand (),
+                which does not do command or parameter substitution.  If
+                the assignment is not performed correctly, flag an error. */
+             if (do_assignment_no_expand (name) == 0)
+               assign_error++;
+             name[assign] = '\0';
+             if (aflags & ASS_APPEND)
+               name[assign - 1] = '\0';
+           }
+
+         set_var_attribute (name, attribute, undo);
+         list = list->next;
+       }
+    }
+  else
+    {
+      SHELL_VAR **variable_list;
+      register int i;
+
+      if ((attribute & att_function) || functions_only)
+       {
+         variable_list = all_shell_functions ();
+         if (attribute != att_function)
+           attribute &= ~att_function; /* so declare -xf works, for example */
+       }
+      else
+       variable_list = all_shell_variables ();
+
+#if defined (ARRAY_VARS)
+      if (attribute & att_array)
+       {
+         arrays_only++;
+         if (attribute != att_array)
+           attribute &= ~att_array;
+       }
+#endif
+
+      if (variable_list)
+       {
+         for (i = 0; var = variable_list[i]; i++)
+           {
+#if defined (ARRAY_VARS)
+             if (arrays_only && array_p (var) == 0)
+               continue;
+#endif
+             if ((var->attributes & attribute))
+               show_var_attributes (var, READONLY_OR_EXPORT, nodefs);
+           }
+         free (variable_list);
+       }
+    }
+
+  return (assign_error ? EX_BADASSIGN
+                      : ((any_failed == 0) ? EXECUTION_SUCCESS
+                                           : EXECUTION_FAILURE));
+}
+
+/* Show the attributes for shell variable VAR.  If NODEFS is non-zero,
+   don't show function definitions along with the name.  If PATTR is
+   non-zero, it indicates we're being called from `export' or `readonly'.
+   In POSIX mode, this prints the name of the calling builtin (`export'
+   or `readonly') instead of `declare', and doesn't print function defs
+   when called by `export' or `readonly'. */
+int
+show_var_attributes (var, pattr, nodefs)
+     SHELL_VAR *var;
+     int pattr, nodefs;
+{
+  char flags[8], *x;
+  int i;
+
+  i = 0;
+
+  /* pattr == 0 means we are called from `declare'. */
+  if (pattr == 0 || posixly_correct == 0)
+    {
+#if defined (ARRAY_VARS)
+      if (array_p (var))
+       flags[i++] = 'a';
+#endif
+
+      if (function_p (var))
+       flags[i++] = 'f';
+
+      if (integer_p (var))
+       flags[i++] = 'i';
+
+      if (readonly_p (var))
+       flags[i++] = 'r';
+
+      if (trace_p (var))
+       flags[i++] = 't';
+
+      if (exported_p (var))
+       flags[i++] = 'x';
+    }
+  else
+    {
+#if defined (ARRAY_VARS)
+      if (array_p (var))
+       flags[i++] = 'a';
+#endif
+
+      if (function_p (var))
+       flags[i++] = 'f';
+    }
+
+  flags[i] = '\0';
+
+  /* If we're printing functions with definitions, print the function def
+     first, then the attributes, instead of printing output that can't be
+     reused as input to recreate the current state. */
+  if (function_p (var) && nodefs == 0 && (pattr == 0 || posixly_correct == 0))
+    {
+      printf ("%s\n", named_function_string (var->name, function_cell (var), 1));
+      nodefs++;
+      if (pattr == 0 && i == 1 && flags[0] == 'f')
+       return 0;               /* don't print `declare -f name' */
+    }
+
+  if (pattr == 0 || posixly_correct == 0)
+    printf ("declare -%s ", i ? flags : "-");
+  else if (i)
+    printf ("%s -%s ", this_command_name, flags);
+  else
+    printf ("%s ", this_command_name);
+
+#if defined (ARRAY_VARS)
+ if (array_p (var))
+    print_array_assignment (var, 1);
+  else
+#endif
+  /* force `readonly' and `export' to not print out function definitions
+     when in POSIX mode. */
+  if (nodefs || (function_p (var) && pattr != 0 && posixly_correct))
+    printf ("%s\n", var->name);
+  else if (function_p (var))
+    printf ("%s\n", named_function_string (var->name, function_cell (var), 1));
+  else if (invisible_p (var))
+    printf ("%s\n", var->name);
+  else
+    {
+      x = sh_double_quote (var_isset (var) ? value_cell (var) : "");
+      printf ("%s=%s\n", var->name, x);
+      free (x);
+    }
+  return (0);
+}
+
+int
+show_name_attributes (name, nodefs)
+     char *name;
+     int nodefs;
+{
+  SHELL_VAR *var;
+
+  var = find_variable_internal (name, 1);
+
+  if (var && invisible_p (var) == 0)
+    {
+      show_var_attributes (var, READONLY_OR_EXPORT, nodefs);
+      return (0);
+    }
+  else
+    return (1);
+}
+
+void
+set_var_attribute (name, attribute, undo)
+     char *name;
+     int attribute, undo;
+{
+  SHELL_VAR *var, *tv;
+  char *tvalue;
+
+  if (undo)
+    var = find_variable (name);
+  else
+    {
+      tv = find_tempenv_variable (name);
+      /* XXX -- need to handle case where tv is a temp variable in a
+        function-scope context, since function_env has been merged into
+        the local variables table. */
+      if (tv && tempvar_p (tv))
+       {
+         tvalue = var_isset (tv) ? savestring (value_cell (tv)) : savestring ("");
+
+         var = bind_variable (tv->name, tvalue, 0);
+         var->attributes |= tv->attributes & ~att_tempvar;
+         VSETATTR (tv, att_propagate);
+         if (var->context != 0)
+           VSETATTR (var, att_propagate);
+         SETVARATTR (tv, attribute, undo);     /* XXX */
+
+         free (tvalue);
+       }
+      else
+       {
+         var = find_variable_internal (name, 0);
+         if (var == 0)
+           {
+             var = bind_variable (name, (char *)NULL, 0);
+             VSETATTR (var, att_invisible);
+           }
+         else if (var->context != 0)
+           VSETATTR (var, att_propagate);
+       }
+    }
+
+  if (var)
+    SETVARATTR (var, attribute, undo);
+
+  if (var && (exported_p (var) || (attribute & att_exported)))
+    array_needs_making++;      /* XXX */
+}
index e607d98bce1bbc18cc6ac2a738ac25100e49ec5b..6bc0005e42af17d815a19802c28d020a7495c84b 100644 (file)
@@ -6,12 +6,12 @@
 .\"    Case Western Reserve University
 .\"    chet@po.CWRU.Edu
 .\"
-.\"    Last Change: Sat Oct 30 22:24:07 EDT 2004
+.\"    Last Change: Sun Nov  7 14:53:18 EST 2004
 .\"
 .\" bash_builtins, strip all but Built-Ins section
 .if \n(zZ=1 .ig zZ
 .if \n(zY=1 .ig zY
-.TH BASH 1 "2004 Oct 30" "GNU Bash-3.1-devel"
+.TH BASH 1 "2004 Nov 7" "GNU Bash-3.1-devel"
 .\"
 .\" There's some problem with having a `@'
 .\" in a tagged paragraph with the BSD man macros.
@@ -1076,6 +1076,20 @@ Assignment statements may also appear as arguments to the
 and
 .B local
 builtin commands.
+.PP
+In the context where an assignment statement is assigning a value
+to a shell variable or array index, the += operator can be used to
+append to or add to the variable's previous value.
+When += is applied to a variable for which the integer attribute has been
+set, \fIvalue\fP is evaluated as an arithmetic expression and added to the
+variable's current value, which is also evaluated.
+When += is applied to an array variable using compound assignment (see
+.B Arrays
+below), the
+variable's value is not unset (as it is when using =), and new values are
+appended to the array beginning at one greater than the array's maximum index.
+When applied to a string-valued variable, \fIvalue\fP is expanded and
+appended to the variable's value.
 .SS Positional Parameters
 .PP
 A
index 86504ef94c29a0f574c4bcb79ced3700b20d2d17..e607d98bce1bbc18cc6ac2a738ac25100e49ec5b 100644 (file)
@@ -6,12 +6,12 @@
 .\"    Case Western Reserve University
 .\"    chet@po.CWRU.Edu
 .\"
-.\"    Last Change: Sat Oct  2 18:05:57 EDT 2004
+.\"    Last Change: Sat Oct 30 22:24:07 EDT 2004
 .\"
 .\" bash_builtins, strip all but Built-Ins section
 .if \n(zZ=1 .ig zZ
 .if \n(zY=1 .ig zY
-.TH BASH 1 "2004 Oct 2" "GNU Bash-3.1-devel"
+.TH BASH 1 "2004 Oct 30" "GNU Bash-3.1-devel"
 .\"
 .\" There's some problem with having a `@'
 .\" in a tagged paragraph with the BSD man macros.
@@ -8541,9 +8541,9 @@ subsequently reset.  The exit status is true unless a
 .I name
 is readonly.
 .TP
-\fBwait\fP [\fIn\fP]
-Wait for the specified process and return its termination
-status.
+\fBwait\fP [\fIn ...\fP]
+Wait for each specified process and return its termination status.
+Each
 .I n
 may be a process
 ID or a job specification; if a job spec is given, all processes
index b581aaabcc1941884bf9c336fda13bfaa0d675fd..ef24755cf9dae175e81a873066abb91e8cdc9ebe 100644 (file)
@@ -1208,6 +1208,21 @@ Assignment statements may also appear as arguments to the
 @code{declare}, @code{typeset}, @code{export}, @code{readonly},
 and @code{local} builtin commands.
 
+In the context where an assignment statement is assigning a value  
+to a shell variable or array index (@pxref{Arrays}), the @samp{+=}
+operator can be used to   
+append to or add to the variable's previous value.
+When @samp{+=} is applied to a variable for which the integer attribute
+has been set, @var{value} is evaluated as an arithmetic expression and
+added to the variable's current value, which is also evaluated.
+When @samp{+=} is applied to an array variable using compound assignment
+(@pxref{Arrays}), the
+variable's value is not unset (as it is when using @samp{=}), and new
+values are appended to the array beginning at one greater than the array's
+maximum index.
+When applied to a string-valued variable, @var{value} is expanded and
+appended to the variable's value.
+
 @node Positional Parameters
 @subsection Positional Parameters
 @cindex parameters, positional
index 94814d67675f65c06988b0601cadcaa93250f1d4..b581aaabcc1941884bf9c336fda13bfaa0d675fd 100644 (file)
@@ -6252,11 +6252,11 @@ or non-zero if an error occurs or an invalid option is encountered.
 @item wait
 @btindex wait
 @example
-wait [@var{jobspec} or @var{pid}]
+wait [@var{jobspec} or @var{pid} ...]
 @end example
-Wait until the child process specified by process @sc{id} @var{pid} or job
-specification @var{jobspec} exits and return the exit status of the last
-command waited for.
+Wait until the child process specified by each process @sc{id} @var{pid}
+or job specification @var{jobspec} exits and return the exit status of the
+last command waited for.
 If a job spec is given, all processes in the job are waited for.
 If no arguments are given, all currently active child processes are
 waited for, and the return status is zero.
index d26a6fa05406c387cf136336690e5f2556997e8b..3c858bd4f5409684225f9c0ab46af57d55feaff6 100644 (file)
@@ -4,7 +4,7 @@ Copyright (C) 1988-2004 Free Software Foundation, Inc.
 
 @set EDITION 3.1-devel
 @set VERSION 3.1-devel
-@set UPDATED 30 October 2004
-@set UPDATED-MONTH October 2004
+@set UPDATED 7 November 2004
+@set UPDATED-MONTH November 2004
 
-@set LASTCHANGE Sat Oct 30 22:24:26 EDT 2004
+@set LASTCHANGE Sun Nov  7 15:09:53 EST 2004
index 0490f6a51fd43f64621074c52732bdcf93e65221..8f7b82092c1c44f42063acf7ec5b0dbdf205a965 100644 (file)
@@ -4,7 +4,7 @@ Copyright (C) 1988-2004 Free Software Foundation, Inc.
 
 @set EDITION 3.1-devel
 @set VERSION 3.1-devel
-@set UPDATED 20 October 2004
-@set UPDATED-MONTH October 2004
+@set UPDATED 7 November 2004
+@set UPDATED-MONTH November 2004
 
-@set LASTCHANGE Wed Oct 20 09:54:44 EDT 2004
+@set LASTCHANGE 
index 96887a97f770389386d7fe895487eaf69d268daf..b434938c6612008c2c460ee773dad45cfe80c7e7 100644 (file)
@@ -1,6 +1,6 @@
 /* execute_command.c -- Execute a COMMAND structure. */
 
-/* Copyright (C) 1987-2003 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2004 Free Software Foundation, Inc.
 
    This file is part of GNU Bash, the Bourne Again SHell.
 
@@ -1635,7 +1635,7 @@ execute_for_command (for_command)
 #endif
 
       this_command_name = (char *)NULL;
-      v = bind_variable (identifier, list->word->word);
+      v = bind_variable (identifier, list->word->word, 0);
       if (readonly_p (v) || noassign_p (v))
        {
          line_number = save_line_number;
@@ -1682,7 +1682,7 @@ execute_for_command (for_command)
        {
          SHELL_VAR *new_value;
 
-         new_value = bind_variable (identifier, value_cell(old_value));
+         new_value = bind_variable (identifier, value_cell(old_value), 0);
          new_value->attributes = old_value->attributes;
          dispose_variable (old_value);
        }
@@ -2089,7 +2089,7 @@ execute_select_command (select_command)
          break;
        }
 
-      v = bind_variable (identifier, selection);
+      v = bind_variable (identifier, selection, 0);
       if (readonly_p (v) || noassign_p (v))
        {
          if (readonly_p (v) && interactive_shell == 0 && posixly_correct)
@@ -2566,7 +2566,7 @@ bind_lastarg (arg)
 
   if (arg == 0)
     arg = "";
-  var = bind_variable ("_", arg);
+  var = bind_variable ("_", arg, 0);
   VUNSETATTR (var, att_exported);
 }
 
index cd9b083229c9d263fe81275e94c6e4d53ba78eb1..96887a97f770389386d7fe895487eaf69d268daf 100644 (file)
@@ -2281,7 +2281,6 @@ execute_while_or_until (while_command, type)
       return_value = execute_command (while_command->test);
       REAP ();
 
-itrace("execute_while_or_until: test returns %d", return_value);
       /* Need to handle `break' in the test when we would break out of the
          loop.  The job control code will set `breaking' to loop_level
          when a job in a loop is stopped with SIGTSTP.  If the stopped job
@@ -2304,7 +2303,6 @@ itrace("execute_while_or_until: test returns %d", return_value);
       body_status = execute_command (while_command->action);
       QUIT;
 
-itrace("execute_while_or_until: body returns %d", body_status);
       if (breaking)
        {
          breaking--;
index 46a959d90a1b97398bd5d7eb5f1b28cbb61d585d..d399f06b8a4d2e9941ed1f9ef0cfc81d76023913 100644 (file)
--- a/general.c
+++ b/general.c
@@ -288,10 +288,16 @@ assignment (string, flags)
          newi = skipsubscript (string, indx);
          if (string[newi++] != ']')
            return (0);
+         if (string[newi] == '+' && string[newi+1] == '=')
+           return (newi + 1);
          return ((string[newi] == '=') ? newi : 0);
        }
 #endif /* ARRAY_VARS */
 
+      /* Check for `+=' */
+      if (c == '+' && string[indx+1] == '=')
+       return (indx + 1);
+
       /* Variable names in assignment statements may contain only letters,
         digits, and `_'. */
       if (legal_variable_char (c) == 0)
index 9531a1e02db221a74d8618912e0902bedd47384c..7b97ea907f1b1a345ecf85e518e69e12c543f347 100644 (file)
@@ -269,7 +269,7 @@ assignment (string, flags)
   c = string[indx = 0];
 
 #if defined (ARRAY_VARS)
-  if ((legal_variable_starter (c) == 0) && (flags && c != '[')) /* ] */
+  if ((legal_variable_starter (c) == 0) && (flags == 0 || c != '[')) /* ] */
 #else
   if (legal_variable_starter (c) == 0)
 #endif
@@ -288,10 +288,16 @@ assignment (string, flags)
          newi = skipsubscript (string, indx);
          if (string[newi++] != ']')
            return (0);
+         if (string[newi] == '+' && string[newi+1] == '=')
+           return (flags ? 0 : (newi + 1));
          return ((string[newi] == '=') ? newi : 0);
        }
 #endif /* ARRAY_VARS */
 
+      /* Check for `+=' */
+      if (c == '+' && string[indx+1] == '=')
+       return (indx + 1);
+
       /* Variable names in assignment statements may contain only letters,
         digits, and `_'. */
       if (legal_variable_char (c) == 0)
@@ -802,6 +808,10 @@ bash_tilde_find_word (s, flags, lenp)
 
   for (r = s; *r && *r != '/'; r++)
     {
+      /* Short-circuit immediately if we see a quote character.  Even though
+        POSIX says that `the first unquoted slash' (or `:') terminates the
+        tilde-prefix, in practice, any quoted portion of the tilde prefix
+        will cause it to not be expanded. */
       if (*r == '\\' || *r == '\'' || *r == '"')  
        {
          ret = savestring (s);
index 294a64687ae4938db2578a6e2fe6847f77b59200..4b6e5985b72185fe048df8d78382d9e54dd83e38 100644 (file)
@@ -964,8 +964,11 @@ rl_redisplay ()
                tx = _rl_col_width (&visible_line[pos], 0, nleft);
              else
                tx = nleft;
-             _rl_backspace (_rl_last_c_pos - tx);      /* XXX */
-             _rl_last_c_pos = tx;
+             if (_rl_last_c_pos != tx)
+               {
+                 _rl_backspace (_rl_last_c_pos - tx);  /* XXX */
+                 _rl_last_c_pos = tx;
+               }
            }
 
          if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
index fd50401ea36c9f78acd3aa61b218e74902ec6463..4b7e2073f2aa23df73a1a62a3c6bafae02de7406 100644 (file)
@@ -1,6 +1,6 @@
 /* mailcheck.c -- The check is in the mail... */
 
-/* Copyright (C) 1987-2002 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2004 Free Software Foundation, Inc.
 
 This file is part of GNU Bash, the Bourne Again SHell.
 
@@ -385,7 +385,7 @@ check_mail ()
          use_user_notification = mailfiles[i]->msg != (char *)NULL;
          message = mailfiles[i]->msg ? mailfiles[i]->msg : _("You have mail in $_");
 
-         bind_variable ("_", current_mail_file);
+         bind_variable ("_", current_mail_file, 0);
 
 #define atime mailfiles[i]->access_time
 #define mtime mailfiles[i]->mod_time
@@ -430,7 +430,7 @@ check_mail ()
 
   if (dollar_underscore)
     {
-      bind_variable ("_", dollar_underscore);
+      bind_variable ("_", dollar_underscore, 0);
       free (dollar_underscore);
     }
   else
index bd6f05289756a14ffa5be92d553489741fcf494d..fd50401ea36c9f78acd3aa61b218e74902ec6463 100644 (file)
@@ -184,9 +184,7 @@ reset_mail_files ()
   register int i;
 
   for (i = 0; i < mailfiles_count; i++)
-    {
-      RESET_MAIL_FILE (i);
-    }
+    RESET_MAIL_FILE (i);
 }
 
 /* Free the information that we have about the remembered mail files. */
diff --git a/parse.y b/parse.y
index c03d19a9364938b39f7084c717f965cd974f2959..1585d18ea17f69a0f4762c8da9956906d0f7f467 100644 (file)
--- a/parse.y
+++ b/parse.y
@@ -2145,7 +2145,7 @@ execute_prompt_command (command)
   parse_and_execute (savestring (command), "PROMPT_COMMAND", SEVAL_NONINT|SEVAL_NOHIST);
 
   restore_parser_state (&ps);
-  bind_variable ("_", last_lastarg);
+  bind_variable ("_", last_lastarg, 0);
   FREE (last_lastarg);
 
   if (token_to_read == '\n')   /* reset_parser was called */
@@ -3252,6 +3252,7 @@ token_is_assignment (t, i)
   return r;
 }
 
+/* XXX - possible changes here for `+=' */
 static int
 token_is_ident (t, i)
      char *t;
index 68d66dfede17ed9c78f3338a96b4f77735ca068d..641f4e7eef52ada4118c09dbaf8886a19390fcbf 100644 (file)
--- a/parse.y~
+++ b/parse.y~
@@ -962,22 +962,26 @@ pipeline_command: pipeline
                        { $$ = $1; }
        |       BANG pipeline
                        {
-                         $2->flags |= CMD_INVERT_RETURN;
+                         if ($2)
+                           $2->flags |= CMD_INVERT_RETURN;
                          $$ = $2;
                        }
        |       timespec pipeline
                        {
-                         $2->flags |= $1;
+                         if ($2)
+                           $2->flags |= $1;
                          $$ = $2;
                        }
        |       timespec BANG pipeline
                        {
-                         $3->flags |= $1|CMD_INVERT_RETURN;
+                         if ($3)
+                           $3->flags |= $1|CMD_INVERT_RETURN;
                          $$ = $3;
                        }
        |       BANG timespec pipeline
                        {
-                         $3->flags |= $2|CMD_INVERT_RETURN;
+                         if ($3)
+                           $3->flags |= $2|CMD_INVERT_RETURN;
                          $$ = $3;
                        }
        |       timespec list_terminator
@@ -2141,7 +2145,7 @@ execute_prompt_command (command)
   parse_and_execute (savestring (command), "PROMPT_COMMAND", SEVAL_NONINT|SEVAL_NOHIST);
 
   restore_parser_state (&ps);
-  bind_variable ("_", last_lastarg);
+  bind_variable ("_", last_lastarg, 0);
   FREE (last_lastarg);
 
   if (token_to_read == '\n')   /* reset_parser was called */
@@ -2726,7 +2730,11 @@ parse_matched_pair (qc, open, close, lenp, flags)
   start_lineno = line_number;
   while (count)
     {
+#if 0
       ch = shell_getc ((qc != '\'' || (flags & P_ALLOWESC)) && pass_next_character == 0);
+#else
+      ch = shell_getc (qc != '\'' && pass_next_character == 0);
+#endif
       if (ch == EOF)
        {
          free (ret);
@@ -2806,6 +2814,7 @@ parse_matched_pair (qc, open, close, lenp, flags)
                  /* Translate $'...' here. */
                  ttrans = ansiexpand (nestret, 0, nestlen - 1, &ttranslen);
                  xfree (nestret);
+
                  nestret = sh_single_quote (ttrans);
                  free (ttrans);
                  nestlen = strlen (nestret);
@@ -2816,13 +2825,10 @@ parse_matched_pair (qc, open, close, lenp, flags)
                  /* Locale expand $"..." here. */
                  ttrans = localeexpand (nestret, 0, nestlen - 1, start_lineno, &ttranslen);
                  xfree (nestret);
-                 nestret = (char *)xmalloc (ttranslen + 3);
-                 nestret[0] = '"';
-                 strcpy (nestret + 1, ttrans);
-                 nestret[ttranslen + 1] = '"';
-                 nestret[ttranslen += 2] = '\0';
+
+                 nestret = sh_mkdoublequoted (ttrans, ttranslen, 0);
                  free (ttrans);
-                 nestlen = ttranslen;
+                 nestlen = ttranslen + 2;
                  retind -= 2;          /* back up before the $" */
                }
 
@@ -2921,19 +2927,12 @@ parse_dparen (c)
   if (reserved_word_acceptable (last_read_token))
     {
       sline = line_number;
-#if 0
-      cmdtyp = parse_arith_cmd (&wval, 1);
-#else
+
       cmdtyp = parse_arith_cmd (&wval, 0);
-#endif
       if (cmdtyp == 1) /* arithmetic command */
        {
          wd = make_word (wval);
-#if 0
-         wd->flags = W_QUOTED;
-#else
          wd->flags = W_QUOTED|W_NOSPLIT|W_NOGLOB;
-#endif
          yylval.word_list = make_word_list (wd, (WORD_LIST *)NULL);
          free (wval);  /* make_word copies it */
          return (ARITH_CMD);
@@ -3253,6 +3252,7 @@ token_is_assignment (t, i)
   return r;
 }
 
+/* XXX - possible changes here for `+=' */
 static int
 token_is_ident (t, i)
      char *t;
@@ -3285,6 +3285,10 @@ read_token_word (character)
   /* DOLLAR_PRESENT becomes non-zero if we see a `$'. */
   int dollar_present;
 
+  /* COMPOUND_ASSIGNMENT becomes non-zero if we are parsing a compound
+     assignment. */
+  int compound_assignment;
+
   /* QUOTED becomes non-zero if we see one of ("), ('), (`), or (\). */
   int quoted;
 
@@ -3304,7 +3308,7 @@ read_token_word (character)
 
   token_index = 0;
   all_digit_token = DIGIT (character);
-  dollar_present = quoted = pass_next_character = 0;
+  dollar_present = quoted = pass_next_character = compound_assignment = 0;
 
   for (;;)
     {
@@ -3449,27 +3453,25 @@ read_token_word (character)
                {
                  ttrans = ansiexpand (ttok, 0, ttoklen - 1, &ttranslen);
                  free (ttok);
+
                  /* Insert the single quotes and correctly quote any
                     embedded single quotes (allowed because P_ALLOWESC was
                     passed to parse_matched_pair). */
                  ttok = sh_single_quote (ttrans);
                  free (ttrans);
+                 ttranslen = strlen (ttok);
                  ttrans = ttok;
-                 ttranslen = strlen (ttrans);
                }
              else
                {
-                 /* Try to locale-expand the converted string. */
+                 /* Try to locale)-expand the converted string. */
                  ttrans = localeexpand (ttok, 0, ttoklen - 1, first_line, &ttranslen);
                  free (ttok);
 
                  /* Add the double quotes back */
-                 ttok = (char *)xmalloc (ttranslen + 3);
-                 ttok[0] = '"';
-                 strcpy (ttok + 1, ttrans);
-                 ttok[ttranslen + 1] = '"';
-                 ttok[ttranslen += 2] = '\0';
+                 ttok = sh_mkdoublequoted (ttrans, ttranslen, 0);
                  free (ttrans);
+                 ttranslen += 2;
                  ttrans = ttok;
                }
 
@@ -3522,6 +3524,7 @@ read_token_word (character)
          goto next_character;
         }
       /* Identify possible compound array variable assignment. */
+      /* XXX - changes here for `+=' */
       else if MBTEST(character == '=' && token_index > 0 && (assignment_acceptable (last_read_token) || (parser_state & PST_ASSIGNOK)) && token_is_assignment (token, token_index))
        {
          peek_char = shell_getc (1);
@@ -3543,7 +3546,12 @@ read_token_word (character)
              token[token_index++] = ')';
              FREE (ttok);
              all_digit_token = 0;
+             compound_assignment = 1;
+#if 0
              goto next_character;
+#else
+             goto got_token;           /* ksh93 seems to do this */
+#endif
            }
          else
            shell_ungetc (peek_char);
@@ -3638,6 +3646,8 @@ got_token:
     the_word->flags |= W_HASDOLLAR;
   if (quoted)
     the_word->flags |= W_QUOTED;
+  if (compound_assignment)
+    the_word->flags |= W_COMPASSIGN;
   /* A word is an assignment if it appears at the beginning of a
      simple command, or after another assignment word.  This is
      context-dependent, so it cannot be handled in the grammar. */
@@ -3814,6 +3824,8 @@ history_delimiting_chars ()
        return " ";
       return ";";
     }
+  else if (two_tokens_ago == CASE && token_before_that == WORD && (parser_state & PST_CASESTMT))
+    return " ";
 
   for (i = 0; no_semi_successors[i]; i++)
     {
index 2462bd6c814344b9953b7c2cd460ed04b5ac8b4f..371f0842a048631468adec0aabfdb54c38c3b8eb 100644 (file)
@@ -862,7 +862,7 @@ bind_comp_words (lwords)
     VUNSETATTR (v, att_readonly);
   if (array_p (v) == 0)
     v = convert_var_to_array (v);
-  v = assign_array_var_from_word_list (v, lwords);
+  v = assign_array_var_from_word_list (v, lwords, 0);
 
   VUNSETATTR (v, att_invisible);
   return v;
@@ -882,7 +882,7 @@ bind_compfunc_variables (line, ind, lwords, cw, exported)
 
   /* Set the variables that the function expects while it executes.  Maybe
      these should be in the function environment (temporary_env). */
-  v = bind_variable ("COMP_LINE", line);
+  v = bind_variable ("COMP_LINE", line, 0);
   if (v && exported)
     VSETATTR(v, att_exported);
 
index 7f5ac54c99de8debd2716b5b105699d197af10d3..ccac5c28538c76ee4946025140fb399fc433d059 100644 (file)
@@ -862,7 +862,9 @@ bind_comp_words (lwords)
     VUNSETATTR (v, att_readonly);
   if (array_p (v) == 0)
     v = convert_var_to_array (v);
-  v = assign_array_var_from_word_list (v, lwords);
+  v = assign_array_var_from_word_list (v, lwords, 0);
+
+  VUNSETATTR (v, att_invisible);
   return v;
 }
 #endif /* ARRAY_VARS */
@@ -1022,6 +1024,8 @@ gen_shell_function_matches (cs, text, line, ind, lwords, nw, cw)
   if (array_p (v) == 0)
     v = convert_var_to_array (v);
 
+  VUNSETATTR (v, att_invisible);
+
   a = array_cell (v);
   if (a == 0 || array_empty (a))
     sl = (STRINGLIST *)NULL;
diff --git a/shell.c b/shell.c
index 651ce1d201b66531c6ff2b85c28470a0eef8e52c..0f81e9dbbab9476fb38434498f7c813c0463c3ad 100644 (file)
--- a/shell.c
+++ b/shell.c
@@ -511,7 +511,7 @@ main (argc, argv, env)
      alias expansion in non-interactive shells, and other Posix.2 things. */
   if (posixly_correct)
     {
-      bind_variable ("POSIXLY_CORRECT", "y");
+      bind_variable ("POSIXLY_CORRECT", "y", 0);
       sv_strict_posix ("POSIXLY_CORRECT");
     }
 
@@ -610,7 +610,7 @@ main (argc, argv, env)
   /* If we are invoked as `sh', turn on Posix mode. */
   if (act_like_sh)
     {
-      bind_variable ("POSIXLY_CORRECT", "y");
+      bind_variable ("POSIXLY_CORRECT", "y", 0);
       sv_strict_posix ("POSIXLY_CORRECT");
     }
 
index 2e17baac3f855e70049b013c82106b483d999160..651ce1d201b66531c6ff2b85c28470a0eef8e52c 100644 (file)
--- a/shell.c~
+++ b/shell.c~
@@ -1089,6 +1089,8 @@ shell_is_restricted (name)
   if (restricted)
     return 1;
   temp = base_pathname (name);
+  if (*temp == '-')
+    temp++;
   return (STREQ (temp, RESTRICTED_SHELL_NAME));
 }
 
diff --git a/subst.c b/subst.c
index c72b8a6d2098f994fa6cd867dcc3d55791e08c33..a6dc8b10fce11d9796f6bb9680692ce69875a6a6 100644 (file)
--- a/subst.c
+++ b/subst.c
@@ -2143,22 +2143,24 @@ list_string_with_quotes (string)
 
 #if defined (ARRAY_VARS)
 static SHELL_VAR *
-do_compound_assignment (name, value, mklocal)
+do_compound_assignment (name, value, flags)
      char *name, *value;
-     int mklocal;
+     int flags;
 {
   SHELL_VAR *v;
-  int off;
+  int off, mklocal;
+
+  mklocal = flags & ASS_MKLOCAL;
 
   if (mklocal && variable_context)
     {
       v = find_variable (name);
       if (v == 0 || array_p (v) == 0)
         v = make_local_array_variable (name);
-      v = assign_array_var_from_string (v, value);
+      v = assign_array_var_from_string (v, value, flags);
     }
   else
-    v = assign_array_from_string (name, value);
+    v = assign_array_from_string (name, value, flags);
 
   return (v);
 }
@@ -2174,19 +2176,19 @@ do_assignment_internal (word, expand)
      const WORD_DESC *word;
      int expand;
 {
-  int offset, tlen;
-  char *name, *value;
+  int offset, tlen, appendop, assign_list, aflags;
+  char *name, *value, *ovalue, *nvalue;
   SHELL_VAR *entry;
 #if defined (ARRAY_VARS)
   char *t;
   int ni;
 #endif
-  int assign_list = 0;
   const char *string;
 
   if (word == 0 || word->word == 0)
     return 0;
 
+  appendop = assign_list = aflags = 0;
   string = word->word;
   offset = assignment (string, 0);
   name = savestring (string);
@@ -2196,7 +2198,13 @@ do_assignment_internal (word, expand)
     {
       char *temp;
 
-      name[offset] = 0;
+      if (name[offset - 1] == '+')
+       {
+         appendop = 1;
+         name[offset - 1] = '\0';
+       }
+
+      name[offset] = 0;                /* might need this set later */
       temp = name + offset + 1;
       tlen = STRLEN (temp);
 
@@ -2226,10 +2234,19 @@ do_assignment_internal (word, expand)
     }
 
   if (echo_command_at_execute)
-     xtrace_print_assignment (name, value, assign_list, 1);
+    {
+      if (appendop)
+       name[offset - 1] = '+';
+      xtrace_print_assignment (name, value, assign_list, 1);
+      if (appendop)
+       name[offset - 1] = '\0';
+    }
 
 #define ASSIGN_RETURN(r)       do { FREE (value); free (name); return (r); } while (0)
 
+  if (appendop)
+    aflags |= ASS_APPEND;
+
 #if defined (ARRAY_VARS)
   if (t = xstrchr (name, '[')) /*]*/
     {
@@ -2238,15 +2255,19 @@ do_assignment_internal (word, expand)
          report_error (_("%s: cannot assign list to array member"), name);
          ASSIGN_RETURN (0);
        }
-      entry = assign_array_element (name, value);
+      entry = assign_array_element (name, value, aflags);
       if (entry == 0)
        ASSIGN_RETURN (0);
     }
   else if (assign_list)
-    entry = do_compound_assignment (name, value, (word->flags & W_ASSIGNARG));
+    {
+      if (word->flags & W_ASSIGNARG)
+       aflags |= ASS_MKLOCAL;
+      entry = do_compound_assignment (name, value, aflags);
+    }
   else
 #endif /* ARRAY_VARS */
-  entry = bind_variable (name, value);
+  entry = bind_variable (name, value, aflags);
 
   stupidly_hack_special_variables (name);
 
@@ -4854,10 +4875,10 @@ parameter_brace_expand_rhs (name, value, c, quoted, qdollaratp, hasdollarat)
   free (t);
 #if defined (ARRAY_VARS)
   if (valid_array_reference (name))
-    assign_array_element (name, t1);
+    assign_array_element (name, t1, 0);
   else
 #endif /* ARRAY_VARS */
-  bind_variable (name, t1);
+  bind_variable (name, t1, 0);
   free (t1);
   return (temp);
 }
index 85393c653787b39bf071ca292a9098a2809a4008..879649c84c659856318c137ba7b777875b748934 100644 (file)
--- a/subst.c~
+++ b/subst.c~
@@ -2143,22 +2143,24 @@ list_string_with_quotes (string)
 
 #if defined (ARRAY_VARS)
 static SHELL_VAR *
-do_compound_assignment (name, value, mklocal)
+do_compound_assignment (name, value, flags)
      char *name, *value;
-     int mklocal;
+     int flags;
 {
   SHELL_VAR *v;
-  int off;
+  int off, mklocal;
+
+  mklocal = flags & ASS_MKLOCAL;
 
   if (mklocal && variable_context)
     {
       v = find_variable (name);
       if (v == 0 || array_p (v) == 0)
         v = make_local_array_variable (name);
-      v = assign_array_var_from_string (v, value);
+      v = assign_array_var_from_string (v, value, flags);
     }
   else
-    v = assign_array_from_string (name, value);
+    v = assign_array_from_string (name, value, flags);
 
   return (v);
 }
@@ -2174,19 +2176,19 @@ do_assignment_internal (word, expand)
      const WORD_DESC *word;
      int expand;
 {
-  int offset, tlen;
-  char *name, *value;
+  int offset, tlen, appendop, assign_list, aflags;
+  char *name, *value, *ovalue, *nvalue;
   SHELL_VAR *entry;
 #if defined (ARRAY_VARS)
   char *t;
   int ni;
 #endif
-  int assign_list = 0;
   const char *string;
 
   if (word == 0 || word->word == 0)
     return 0;
 
+  appendop = assign_list = aflags = 0;
   string = word->word;
   offset = assignment (string, 0);
   name = savestring (string);
@@ -2196,7 +2198,13 @@ do_assignment_internal (word, expand)
     {
       char *temp;
 
-      name[offset] = 0;
+      if (name[offset - 1] == '+')
+       {
+         appendop = 1;
+         name[offset - 1] = 0;
+       }
+
+      name[offset] = 0;                /* might need this set later */
       temp = name + offset + 1;
       tlen = STRLEN (temp);
 
@@ -2226,10 +2234,19 @@ do_assignment_internal (word, expand)
     }
 
   if (echo_command_at_execute)
-     xtrace_print_assignment (name, value, assign_list, 1);
+    {
+      if (appendop)
+       name[offset - 1] = '+';
+      xtrace_print_assignment (name, value, assign_list, 1);
+      if (appendop)
+       name[offset - 1] = '+';
+    }
 
 #define ASSIGN_RETURN(r)       do { FREE (value); free (name); return (r); } while (0)
 
+  if (appendop)
+    aflags |= ASS_APPEND;
+
 #if defined (ARRAY_VARS)
   if (t = xstrchr (name, '[')) /*]*/
     {
@@ -2238,15 +2255,19 @@ do_assignment_internal (word, expand)
          report_error (_("%s: cannot assign list to array member"), name);
          ASSIGN_RETURN (0);
        }
-      entry = assign_array_element (name, value);
+      entry = assign_array_element (name, value, aflags);
       if (entry == 0)
        ASSIGN_RETURN (0);
     }
   else if (assign_list)
-    entry = do_compound_assignment (name, value, (word->flags & W_ASSIGNARG));
+    {
+      if (word->flags & W_ASSIGNARG)
+       aflags |= ASS_MKLOCAL;
+      entry = do_compound_assignment (name, value, aflags);
+    }
   else
 #endif /* ARRAY_VARS */
-  entry = bind_variable (name, value);
+  entry = bind_variable (name, value, aflags);
 
   stupidly_hack_special_variables (name);
 
@@ -4854,10 +4875,10 @@ parameter_brace_expand_rhs (name, value, c, quoted, qdollaratp, hasdollarat)
   free (t);
 #if defined (ARRAY_VARS)
   if (valid_array_reference (name))
-    assign_array_element (name, t1);
+    assign_array_element (name, t1, 0);
   else
 #endif /* ARRAY_VARS */
-  bind_variable (name, t1);
+  bind_variable (name, t1, 0);
   free (t1);
   return (temp);
 }
@@ -6503,10 +6524,12 @@ add_string:
            assignoff = sindex;
          if (sindex == assignoff && string[sindex+1] == '~')   /* XXX */
            word->flags |= W_ITILDE;
+#if 0
          else if ((word->flags & W_ASSIGNMENT) &&
                   (posixly_correct == 0 || (word->flags & W_TILDEEXP)) &&
                   string[sindex+1] == '~')
            word->flags |= W_ITILDE;
+#endif
          goto add_character;
 
        case ':':
diff --git a/subst.h b/subst.h
index 1832c64f6cb0ca3b94d071accfea1e1989995a8e..33d3f49e421460f64dcab9c40acf154ead99232f 100644 (file)
--- a/subst.h
+++ b/subst.h
@@ -1,6 +1,6 @@
 /* subst.h -- Names of externally visible functions in subst.c. */
 
-/* Copyright (C) 1993-2002 Free Software Foundation, Inc.
+/* Copyright (C) 1993-2004 Free Software Foundation, Inc.
 
    This file is part of GNU Bash, the Bourne Again SHell.
 
    to unconditionally retain the backslash.  Q_PATQUOTE means that we're
    expanding a pattern ${var%#[#%]pattern} in an expansion surrounded
    by double quotes. */
-#define Q_DOUBLE_QUOTES  0x1
-#define Q_HERE_DOCUMENT  0x2
-#define Q_KEEP_BACKSLASH 0x4
-#define Q_PATQUOTE      0x8
+#define Q_DOUBLE_QUOTES  0x01
+#define Q_HERE_DOCUMENT  0x02
+#define Q_KEEP_BACKSLASH 0x04
+#define Q_PATQUOTE      0x08
 #define Q_QUOTED        0x10
 #define Q_ADDEDQUOTES   0x20
 #define Q_QUOTEDNULL    0x40
 
+/* Flag values controlling how assignment statements are treated. */
+#define ASS_APPEND     0x01
+#define ASS_MKLOCAL    0x02
+
 /* Remove backslashes which are quoting backquotes from STRING.  Modifies
    STRING, and returns a pointer to it. */
 extern char * de_backslash __P((char *));
index 350e82aa7088d92d8b13fccf480401232a47f708..0b0553bebe8e99f6c890bf9f1d21d96dca24d239 100644 (file)
--- a/subst.h~
+++ b/subst.h~
    to unconditionally retain the backslash.  Q_PATQUOTE means that we're
    expanding a pattern ${var%#[#%]pattern} in an expansion surrounded
    by double quotes. */
-#define Q_DOUBLE_QUOTES  0x1
-#define Q_HERE_DOCUMENT  0x2
-#define Q_KEEP_BACKSLASH 0x4
-#define Q_PATQUOTE      0x8
+#define Q_DOUBLE_QUOTES  0x01
+#define Q_HERE_DOCUMENT  0x02
+#define Q_KEEP_BACKSLASH 0x04
+#define Q_PATQUOTE      0x08
 #define Q_QUOTED        0x10
 #define Q_ADDEDQUOTES   0x20
 #define Q_QUOTEDNULL    0x40
 
+/* Flag values controlling how assignment statements are treated. */
+#define ASS_APPEND     0x01
+#define ASS_MKLOCAL    0x02
+
 /* Remove backslashes which are quoting backquotes from STRING.  Modifies
    STRING, and returns a pointer to it. */
 extern char * de_backslash __P((char *));
@@ -98,6 +102,7 @@ extern char *strip_trailing_ifs_whitespace __P((char *, char *, int));
    splitting on the result of expansion. */
 extern int do_assignment __P((char *));
 extern int do_assignment_no_expand __P((char *));
+extern int do_word_assignment __P((WORD_DESC *));
 
 /* Append SOURCE to TARGET at INDEX.  SIZE is the current amount
    of space allocated to TARGET.  SOURCE can be NULL, in which
index 3efcf32d68e9722024b6ca9d67f9e81b2aa5ac04..72ec06a2c1fd8dde92acea5e8ac773e35f1d061b 100755 (executable)
@@ -1,4 +1,4 @@
-BUILD_DIR=/usr/local/build/chet/bash/bash-current
+BUILD_DIR=/usr/local/build/bash/bash-current
 THIS_SH=$BUILD_DIR/bash
 PATH=$PATH:$BUILD_DIR
 
index 4047025e3e9303785067f1118584339c449ad972..488b4e46a5a741bd803074d580c441c36a971ffb 100644 (file)
@@ -132,6 +132,11 @@ value = new1 new2 new3
 ./array1.sub: line 1: `printf "%s\n" -a a=(a 'b  c')'
 ./array2.sub: line 1: syntax error near unexpected token `('
 ./array2.sub: line 1: `declare -a ''=(a 'b c')'
+9
+9
+
+
+7 8 9
 
 a b c d e f g
 for case if then else
@@ -139,10 +144,10 @@ for case if then else
 12 14 16 18 20
 4414758999202
 aaa bbb
-./array.tests: line 280: syntax error near unexpected token `<>'
-./array.tests: line 280: `metas=( <> < > ! )'
-./array.tests: line 281: syntax error near unexpected token `<>'
-./array.tests: line 281: `metas=( [1]=<> [2]=< [3]=> [4]=! )'
+./array.tests: line 282: syntax error near unexpected token `<>'
+./array.tests: line 282: `metas=( <> < > ! )'
+./array.tests: line 283: syntax error near unexpected token `<>'
+./array.tests: line 283: `metas=( [1]=<> [2]=< [3]=> [4]=! )'
 abc 3
 case 4
 abc case if then else 5
index 20eaf20c577a927a2bdf4c2908cb6be0e1d50d86..5aca26510c20a8ba90f70f57c43fc545684b3219 100644 (file)
@@ -240,6 +240,8 @@ ${THIS_SH} ./array1.sub
 ${THIS_SH} ./array2.sub
 
 # some old bugs and ksh93 compatibility tests
+${THIS_SH} ./array3.sub
+
 set +u
 cd /tmp
 
@@ -394,3 +396,4 @@ unset x[2]
 x[9]='9'
 
 echo ${x[*]: -1}
+
diff --git a/tests/array3.sub b/tests/array3.sub
new file mode 100644 (file)
index 0000000..579b42b
--- /dev/null
@@ -0,0 +1,9 @@
+a=(0 1 2 3 4 5 6 7 8 9)
+
+echo ${a[@]: -1}
+
+echo ${a[@]:9}
+echo ${a[@]:10}
+echo ${a[@]:11}
+
+echo ${a[@]:7:3}
index 154a969a833f8e4851e7e9d41742f7e45e6cffcb..f063b26178ef605af778b9ab349036c3b9498c8d 100644 (file)
@@ -77,3 +77,4 @@ a b a,b a-b a:b a;b a_b
 a b a,b a-b a.b a:b a;b a_b
 a b a,b a-b a.b a:b a;b a_b
 a b a,b a-b a.b a:b a;b a_b
+argv[1] = <ef>
index 1a123d8bbcd1019aa137dbc87ebd2ef88218ecef..2d938500e7504b1ae4fa51e78259c9c1ed31fb2f 100644 (file)
@@ -353,6 +353,9 @@ echo a@(.|[^[:alnum:]])b
 builtin cd /
 rm -rf $TESTDIR
 
+x=abcdef
+recho "${x#*(a|b)cd}"
+
 # this is for the benefit of pure coverage, so it writes the pcv file
 # in the right place
 builtin cd $MYDIR
index 35bf19199fafb341a06e8455d3a9c5b95eb2aa73..904467b5ca935d47079af7b874e1e20bc57ea5a2 100644 (file)
@@ -20,3 +20,13 @@ ok
 'abcd'
 \'abcd\'
 \'abcd\'
+argv[1] = <A\CB>
+argv[1] = <A\CB>
+argv[1] = <ab$cde>
+A\CB
+A\CB
+A\CB
+argv[1] = <hello, $"world">
+argv[1] = <hello, \$"world">
+argv[1] = <hello, $"world">
+argv[1] = <hello, $world>
index 05709f458f6570c98d4e94e39561bfb162b9461d..b25fbe3171d8b9310ca940dc0883d254527f1220 100644 (file)
@@ -77,4 +77,26 @@ printf "\'abcd\'\n"
 echo -e "\'abcd\'"
 echo -e "\\'abcd\\'"
 
+# and what do we do about unrecognized escape sequences?
 
+shopt -s xpg_echo
+
+recho $'A\CB'
+
+recho "A\CB"
+
+cde=c
+recho $'ab$cde'
+
+printf "%b\n" 'A\CB'
+printf 'A\CB\n'
+
+echo 'A\CB'
+
+world=chet
+
+recho $'hello, $"world"'
+recho $'hello, \$"world"'
+recho $'hello, $\"world"'
+
+recho "hello, $"world""
index 87b9e43ac0beedba388daea4d0d44c7cdcf01f96..c5dca4288e19b3130aff91942888642d44e35fb9 100644 (file)
@@ -66,3 +66,9 @@ argv[3] = <-DSELECT_VECS='>
 argv[1] = <TDEFAULTS>
 argv[2] = <=>
 argv[3] = <-DSELECT_VECS=\'>
+a*b
+ab
+a?b
+ab
+a/b
+ab
index 9aecb8298e0ac21d2e5be046ecd1ad2ea7c4d4db..d457198d99267e03ae0a4bf8489a6ca4809f732a 100644 (file)
@@ -36,3 +36,14 @@ recho TDEFAULTS = ${selvecs:+-DSELECT_VECS="\\"}
 recho TDEFAULTS = ${selvecs:+-DSELECT_VECS=\\}
 recho TDEFAULTS = ${selvecs:+-DSELECT_VECS=\'}
 recho TDEFAULTS = ${selvecs:+-DSELECT_VECS="\'"}
+
+# more tests for bash-3.0 behavior
+
+var="a*b" ; echo "${var//\\*/}"
+var="a*b" ; echo "${var//\*/}"
+
+var="a?b" ; echo "${var//\\?/}"
+var="a?b" ; echo "${var//\?/}"
+
+var="a/b" ; echo "${var//\\//}"
+var="a/b" ; echo "${var//\//}"
index c458b184396da07e1b1e40200324e92a3df73dc7..df8086dc6289608a6ed2f40bd5861537d2e2a92f 100644 (file)
@@ -51,3 +51,6 @@ after fff3: x=4
 |0|12|
 |y|
 |y|
+a=z
+a=b
+a=z
index d6d763d8433a86f5d42e81d9c44b64f4e537cdc1..77776f9b152adbc33c463d2bf699123e463f09aa 100644 (file)
@@ -201,3 +201,6 @@ $THIS_SH ./varenv1.sub
 
 # more tests; bugs in bash up to version 2.05a
 $THIS_SH ./varenv2.sub
+
+# make sure variable scoping is done right
+tt() { typeset a=b;echo a=$a; };a=z;echo a=$a;tt;echo a=$a
index 5c0eba93d3bae809e14b204fab06589460734f63..f75a06a0b5139b3016124842fe42c7e752524813 100644 (file)
@@ -199,7 +199,7 @@ static void initialize_dynamic_variables __P((void));
 static SHELL_VAR *hash_lookup __P((const char *, HASH_TABLE *));
 static SHELL_VAR *new_shell_variable __P((const char *));
 static SHELL_VAR *make_new_variable __P((const char *, HASH_TABLE *));
-static SHELL_VAR *bind_variable_internal __P((const char *, char *, HASH_TABLE *, int));
+static SHELL_VAR *bind_variable_internal __P((const char *, char *, HASH_TABLE *, int, int));
 
 static void free_variable_hash_data __P((PTR_T));
 
@@ -333,7 +333,7 @@ initialize_shell_variables (env, privmode)
 #endif
       else
        {
-         temp_var = bind_variable (name, string);
+         temp_var = bind_variable (name, string, 0);
          VSETATTR (temp_var, (att_exported | att_imported));
          array_needs_making = 1;
        }
@@ -350,7 +350,7 @@ initialize_shell_variables (env, privmode)
   set_pwd ();
 
   /* Set up initial value of $_ */
-  temp_var = bind_variable ("_", dollar_vars[0]);
+  temp_var = bind_variable ("_", dollar_vars[0], 0);
 
   /* Remember this pid. */
   dollar_dollar_pid = getpid ();
@@ -372,7 +372,7 @@ initialize_shell_variables (env, privmode)
   {
     char node_name[22];
     qnx_nidtostr (getnid (), node_name, sizeof (node_name));
-    temp_var = bind_variable ("NODE", node_name);
+    temp_var = bind_variable ("NODE", node_name, 0);
     set_auto_export (temp_var);
   }
 #endif
@@ -392,7 +392,7 @@ initialize_shell_variables (env, privmode)
   set_if_not ("PS4", "+ ");
 
   /* Don't allow IFS to be imported from the environment. */
-  temp_var = bind_variable ("IFS", " \t\n");
+  temp_var = bind_variable ("IFS", " \t\n", 0);
   setifs (temp_var);
 
   /* Magic machine types.  Pretty convenient. */
@@ -411,9 +411,9 @@ initialize_shell_variables (env, privmode)
   set_ppid ();
 
   /* Initialize the `getopts' stuff. */
-  bind_variable ("OPTIND", "1");
+  bind_variable ("OPTIND", "1", 0);
   getopts_reset (0);
-  bind_variable ("OPTERR", "1");
+  bind_variable ("OPTERR", "1", 0);
   sh_opterr = 1;
 
   if (login_shell == 1)
@@ -422,7 +422,7 @@ initialize_shell_variables (env, privmode)
   /* Get the full pathname to THIS shell, and set the BASH variable
      to it. */
   name = get_bash_name ();
-  temp_var = bind_variable ("BASH", name);
+  temp_var = bind_variable ("BASH", name, 0);
   free (name);
 
   /* Make the exported environment variable SHELL be the user's login
@@ -432,13 +432,13 @@ initialize_shell_variables (env, privmode)
   set_shell_var ();
 
   /* Make a variable called BASH_VERSION which contains the version info. */
-  bind_variable ("BASH_VERSION", shell_version_string ());
+  bind_variable ("BASH_VERSION", shell_version_string (), 0);
 #if defined (ARRAY_VARS)
   make_vers_array ();
 #endif
 
   if (command_execution_string)
-    bind_variable ("BASH_EXECUTION_STRING", command_execution_string);
+    bind_variable ("BASH_EXECUTION_STRING", command_execution_string, 0);
 
   /* Find out if we're supposed to be in Posix.2 mode via an
      environment variable. */
@@ -556,7 +556,7 @@ set_home_var ()
 
   temp_var = find_variable ("HOME");
   if (temp_var == 0)
-    temp_var = bind_variable ("HOME", sh_get_home_dir ());
+    temp_var = bind_variable ("HOME", sh_get_home_dir (), 0);
 #if 0
   VSETATTR (temp_var, att_exported);
 #endif
@@ -574,7 +574,7 @@ set_shell_var ()
     {
       if (current_user.shell == 0)
        get_current_user_info ();
-      temp_var = bind_variable ("SHELL", current_user.shell);
+      temp_var = bind_variable ("SHELL", current_user.shell, 0);
     }
 #if 0
   VSETATTR (temp_var, att_exported);
@@ -696,7 +696,7 @@ adjust_shell_level (change)
       new_level[3] = '\0';
     }
 
-  temp_var = bind_variable ("SHLVL", new_level);
+  temp_var = bind_variable ("SHLVL", new_level, 0);
   set_auto_export (temp_var);
 }
 
@@ -731,7 +731,7 @@ set_pwd ()
           same_file (home_string, ".", (struct stat *)NULL, (struct stat *)NULL))
     {
       set_working_directory (home_string);
-      temp_var = bind_variable ("PWD", home_string);
+      temp_var = bind_variable ("PWD", home_string, 0);
       set_auto_export (temp_var);
     }
   else
@@ -739,7 +739,7 @@ set_pwd ()
       temp_string = get_working_directory ("shell-init");
       if (temp_string)
        {
-         temp_var = bind_variable ("PWD", temp_string);
+         temp_var = bind_variable ("PWD", temp_string, 0);
          set_auto_export (temp_var);
          free (temp_string);
        }
@@ -748,7 +748,7 @@ set_pwd ()
   /* According to the Single Unix Specification, v2, $OLDPWD is an
      `environment variable' and therefore should be auto-exported.
      Make a dummy invisible variable for OLDPWD, and mark it as exported. */
-  temp_var = bind_variable ("OLDPWD", (char *)NULL);
+  temp_var = bind_variable ("OLDPWD", (char *)NULL, 0);
   VSETATTR (temp_var, (att_exported | att_invisible));
 }
 
@@ -763,7 +763,7 @@ set_ppid ()
   temp_var = find_variable ("PPID");
   if (temp_var)
     VUNSETATTR (temp_var, (att_readonly | att_exported));
-  temp_var = bind_variable ("PPID", name);
+  temp_var = bind_variable ("PPID", name, 0);
   VSETATTR (temp_var, (att_readonly | att_integer));
 }
 
@@ -777,7 +777,7 @@ uidset ()
   v = find_variable ("UID");
   if (v == 0)
     {
-      v = bind_variable ("UID", b);
+      v = bind_variable ("UID", b, 0);
       VSETATTR (v, (att_readonly | att_integer));
     }
 
@@ -787,7 +787,7 @@ uidset ()
   v = find_variable ("EUID");
   if (v == 0)
     {
-      v = bind_variable ("EUID", b);
+      v = bind_variable ("EUID", b, 0);
       VSETATTR (v, (att_readonly | att_integer));
     }
 }
@@ -830,10 +830,10 @@ sh_set_lines_and_columns (lines, cols)
   char val[INT_STRLEN_BOUND(int) + 1], *v;
 
   v = inttostr (lines, val, sizeof (val));
-  bind_variable ("LINES", v);
+  bind_variable ("LINES", v, 0);
 
   v = inttostr (cols, val, sizeof (val));
-  bind_variable ("COLUMNS", v);
+  bind_variable ("COLUMNS", v, 0);
 }
 
 /* **************************************************************** */
@@ -983,7 +983,7 @@ print_var_function (var)
 #define INIT_DYNAMIC_VAR(var, val, gfunc, afunc) \
   do \
     { \
-      v = bind_variable (var, (val)); \
+      v = bind_variable (var, (val), 0); \
       v->dynamic_value = gfunc; \
       v->assign_func = afunc; \
     } \
@@ -1589,7 +1589,7 @@ set_if_not (name, value)
 
   v = find_variable (name);
   if (v == 0)
-    v = bind_variable_internal (name, value, global_variables->table, HASH_NOSRCH);
+    v = bind_variable_internal (name, value, global_variables->table, HASH_NOSRCH, 0);
   return (v);
 }
 
@@ -1643,7 +1643,7 @@ make_local_variable (name)
     }
 
   if (old_var == 0)
-    new_var = bind_variable_internal (name, "", vc->table, HASH_NOSRCH);
+    new_var = bind_variable_internal (name, "", vc->table, HASH_NOSRCH, 0);
   else
     {
       new_var = make_new_variable (name, vc->table);
@@ -1758,13 +1758,14 @@ make_new_array_variable (name)
 #endif
 
 char *
-make_variable_value (var, value)
+make_variable_value (var, value, flags)
      SHELL_VAR *var;
      char *value;
+     int flags;
 {
-  char *retval;
-  intmax_t lval;
-  int expok;
+  char *retval, *oval;
+  intmax_t lval, rval;
+  int expok, olen;
 
   /* If this variable has had its type set to integer (via `declare -i'),
      then do expression evaluation on it and store the result.  The
@@ -1773,14 +1774,34 @@ make_variable_value (var, value)
      evaluation done. */
   if (integer_p (var))
     {
-      lval = evalexp (value, &expok);
+      if (flags & ASS_APPEND)
+       {
+         oval = value_cell (var);
+         lval = evalexp (oval, &expok);        /* ksh93 seems to do this */
+         if (expok == 0)
+           jump_to_top_level (DISCARD);
+       }
+      rval = evalexp (value, &expok);
       if (expok == 0)
        jump_to_top_level (DISCARD);
-      retval = itos (lval);
+      if (flags & ASS_APPEND)
+       rval += lval;
+      retval = itos (rval);
     }
   else if (value)
     {
-      if (*value)
+      if (flags & ASS_APPEND)
+       {
+         oval = value_cell (var);
+         if (oval == 0)        /* paranoia */
+           oval = "";
+         olen = STRLEN (oval);
+         retval = (char *)xmalloc (olen + (value ? STRLEN (value) : 0) + 1);
+         strcpy (retval, oval);
+         if (value)
+           strcpy (retval+olen, value);
+       }
+      else if (*value)
        retval = savestring (value);
       else
        {
@@ -1797,11 +1818,11 @@ make_variable_value (var, value)
 /* Bind a variable NAME to VALUE in the HASH_TABLE TABLE, which may be the
    temporary environment (but usually is not). */
 static SHELL_VAR *
-bind_variable_internal (name, value, table, hflags)
+bind_variable_internal (name, value, table, hflags, aflags)
      const char *name;
      char *value;
      HASH_TABLE *table;
-     int hflags;
+     int hflags, aflags;
 {
   char *newval;
   SHELL_VAR *entry;
@@ -1811,7 +1832,7 @@ bind_variable_internal (name, value, table, hflags)
   if (entry == 0)
     {
       entry = make_new_variable (name, table);
-      var_setvalue (entry, make_variable_value (entry, value));
+      var_setvalue (entry, make_variable_value (entry, value, 0)); /* XXX */
     }
   else if (entry->assign_func) /* array vars have assign functions now */
     {
@@ -1830,7 +1851,7 @@ bind_variable_internal (name, value, table, hflags)
       /* Variables which are bound are visible. */
       VUNSETATTR (entry, att_invisible);
 
-      newval = make_variable_value (entry, value);
+      newval = make_variable_value (entry, value, aflags);     /* XXX */
 
       /* Invalidate any cached export string */
       INVALIDATE_EXPORTSTR (entry);
@@ -1867,9 +1888,10 @@ bind_variable_internal (name, value, table, hflags)
    first, then we bind into shell_variables. */
 
 SHELL_VAR *
-bind_variable (name, value)
+bind_variable (name, value, flags)
      const char *name;
      char *value;
+     int flags;
 {
   SHELL_VAR *v;
   VAR_CONTEXT *vc;
@@ -1896,10 +1918,10 @@ bind_variable (name, value)
         {
           v = hash_lookup (name, vc->table);
           if (v)
-           return (bind_variable_internal (name, value, vc->table, 0));
+           return (bind_variable_internal (name, value, vc->table, 0, flags));
         }
     }
-  return (bind_variable_internal (name, value, global_variables->table, 0));
+  return (bind_variable_internal (name, value, global_variables->table, 0, flags));
 }
 
 /* Make VAR, a simple shell variable, have value VALUE.  Once assigned a
@@ -1908,15 +1930,16 @@ bind_variable (name, value)
    all modified variables should be exported, mark the variable for export
    and note that the export environment needs to be recreated. */
 SHELL_VAR *
-bind_variable_value (var, value)
+bind_variable_value (var, value, aflags)
      SHELL_VAR *var;
      char *value;
+     int aflags;
 {
   char *t;
 
   VUNSETATTR (var, att_invisible);
 
-  t = make_variable_value (var, value);
+  t = make_variable_value (var, value, aflags);
   FREE (value_cell (var));
   var_setvalue (var, t);
 
@@ -1972,10 +1995,10 @@ bind_int_variable (lhs, rhs)
 
 #if defined (ARRAY_VARS)
   if (isarr)
-    v = assign_array_element (lhs, rhs);
+    v = assign_array_element (lhs, rhs, 0);
   else
 #endif
-    v = bind_variable (lhs, rhs);
+    v = bind_variable (lhs, rhs, 0);
 
   if (isint)
     VSETATTR (v, att_integer);
@@ -2092,6 +2115,10 @@ assign_in_env (word)
     {
       name[offset] = 0;
 
+      /* ignore the `+' when assigning temporary environment */
+      if (name[offset - 1] == '+')
+       name[offset - 1] = '\0';
+
       var = find_variable (name);
       if (var && (readonly_p (var) || noassign_p (var)))
        {
@@ -2407,7 +2434,7 @@ delete_all_variables (hashed_vars)
       entry = find_variable (name); \
       if (!entry) \
        { \
-         entry = bind_variable (name, ""); \
+         entry = bind_variable (name, "", 0); \
          if (!no_invisible_vars) entry->attributes |= att_invisible; \
        } \
     } \
@@ -2849,7 +2876,7 @@ push_temp_var (data)
        binding_table = shell_variables->table = hash_create (TEMPENV_HASH_BUCKETS);
     }
 
-  v = bind_variable_internal (var->name, value_cell (var), binding_table, 0);
+  v = bind_variable_internal (var->name, value_cell (var), binding_table, 0, 0);
 
   /* XXX - should we set the context here?  It shouldn't matter because of how
      assign_in_env works, but might want to check. */
@@ -3367,7 +3394,7 @@ push_func_var (data)
   if (tempvar_p (var) && (posixly_correct || (var->attributes & att_propagate)))
     {
       /* XXX - should we set v->context here? */
-      v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0);
+      v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0, 0);
       if (shell_variables == global_variables)
        var->attributes &= ~(att_tempvar|att_propagate);
       else
@@ -3455,7 +3482,7 @@ push_exported_var (data)
 #endif
     {
       var->attributes &= ~att_tempvar;         /* XXX */
-      v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0);
+      v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0, 0);
       if (shell_variables == global_variables)
        var->attributes &= ~att_propagate;
       v->attributes |= var->attributes;
index cdf1a195b98750a1da047d500dc06393d0d7f4de..7c916df446d4ef1d53c341cdd3cf8920a40dac72 100644 (file)
@@ -199,7 +199,7 @@ static void initialize_dynamic_variables __P((void));
 static SHELL_VAR *hash_lookup __P((const char *, HASH_TABLE *));
 static SHELL_VAR *new_shell_variable __P((const char *));
 static SHELL_VAR *make_new_variable __P((const char *, HASH_TABLE *));
-static SHELL_VAR *bind_variable_internal __P((const char *, char *, HASH_TABLE *, int));
+static SHELL_VAR *bind_variable_internal __P((const char *, char *, HASH_TABLE *, int, int));
 
 static void free_variable_hash_data __P((PTR_T));
 
@@ -320,7 +320,7 @@ initialize_shell_variables (env, privmode)
 #if defined (ARRAY_VARS)
 #  if 0
       /* Array variables may not yet be exported. */
-      else if (*string == '(' && string[1] == '[' && xstrchr (string, ')'))
+      else if (*string == '(' && string[1] == '[' && string[strlen (string) - 1] == ')')
        {
          string_length = 1;
          temp_string = extract_array_assignment_list (string, &string_length);
@@ -333,7 +333,7 @@ initialize_shell_variables (env, privmode)
 #endif
       else
        {
-         temp_var = bind_variable (name, string);
+         temp_var = bind_variable (name, string, 0);
          VSETATTR (temp_var, (att_exported | att_imported));
          array_needs_making = 1;
        }
@@ -350,7 +350,7 @@ initialize_shell_variables (env, privmode)
   set_pwd ();
 
   /* Set up initial value of $_ */
-  temp_var = bind_variable ("_", dollar_vars[0]);
+  temp_var = bind_variable ("_", dollar_vars[0], 0);
 
   /* Remember this pid. */
   dollar_dollar_pid = getpid ();
@@ -372,7 +372,7 @@ initialize_shell_variables (env, privmode)
   {
     char node_name[22];
     qnx_nidtostr (getnid (), node_name, sizeof (node_name));
-    temp_var = bind_variable ("NODE", node_name);
+    temp_var = bind_variable ("NODE", node_name, 0);
     set_auto_export (temp_var);
   }
 #endif
@@ -392,7 +392,7 @@ initialize_shell_variables (env, privmode)
   set_if_not ("PS4", "+ ");
 
   /* Don't allow IFS to be imported from the environment. */
-  temp_var = bind_variable ("IFS", " \t\n");
+  temp_var = bind_variable ("IFS", " \t\n", 0);
   setifs (temp_var);
 
   /* Magic machine types.  Pretty convenient. */
@@ -411,9 +411,9 @@ initialize_shell_variables (env, privmode)
   set_ppid ();
 
   /* Initialize the `getopts' stuff. */
-  bind_variable ("OPTIND", "1");
+  bind_variable ("OPTIND", "1", 0);
   getopts_reset (0);
-  bind_variable ("OPTERR", "1");
+  bind_variable ("OPTERR", "1", 0);
   sh_opterr = 1;
 
   if (login_shell == 1)
@@ -422,7 +422,7 @@ initialize_shell_variables (env, privmode)
   /* Get the full pathname to THIS shell, and set the BASH variable
      to it. */
   name = get_bash_name ();
-  temp_var = bind_variable ("BASH", name);
+  temp_var = bind_variable ("BASH", name, 0);
   free (name);
 
   /* Make the exported environment variable SHELL be the user's login
@@ -432,13 +432,13 @@ initialize_shell_variables (env, privmode)
   set_shell_var ();
 
   /* Make a variable called BASH_VERSION which contains the version info. */
-  bind_variable ("BASH_VERSION", shell_version_string ());
+  bind_variable ("BASH_VERSION", shell_version_string (), 0);
 #if defined (ARRAY_VARS)
   make_vers_array ();
 #endif
 
   if (command_execution_string)
-    bind_variable ("BASH_EXECUTION_STRING", command_execution_string);
+    bind_variable ("BASH_EXECUTION_STRING", command_execution_string, 0);
 
   /* Find out if we're supposed to be in Posix.2 mode via an
      environment variable. */
@@ -556,7 +556,7 @@ set_home_var ()
 
   temp_var = find_variable ("HOME");
   if (temp_var == 0)
-    temp_var = bind_variable ("HOME", sh_get_home_dir ());
+    temp_var = bind_variable ("HOME", sh_get_home_dir (), 0);
 #if 0
   VSETATTR (temp_var, att_exported);
 #endif
@@ -574,7 +574,7 @@ set_shell_var ()
     {
       if (current_user.shell == 0)
        get_current_user_info ();
-      temp_var = bind_variable ("SHELL", current_user.shell);
+      temp_var = bind_variable ("SHELL", current_user.shell, 0);
     }
 #if 0
   VSETATTR (temp_var, att_exported);
@@ -696,7 +696,7 @@ adjust_shell_level (change)
       new_level[3] = '\0';
     }
 
-  temp_var = bind_variable ("SHLVL", new_level);
+  temp_var = bind_variable ("SHLVL", new_level, 0);
   set_auto_export (temp_var);
 }
 
@@ -731,7 +731,7 @@ set_pwd ()
           same_file (home_string, ".", (struct stat *)NULL, (struct stat *)NULL))
     {
       set_working_directory (home_string);
-      temp_var = bind_variable ("PWD", home_string);
+      temp_var = bind_variable ("PWD", home_string, 0);
       set_auto_export (temp_var);
     }
   else
@@ -739,7 +739,7 @@ set_pwd ()
       temp_string = get_working_directory ("shell-init");
       if (temp_string)
        {
-         temp_var = bind_variable ("PWD", temp_string);
+         temp_var = bind_variable ("PWD", temp_string, 0);
          set_auto_export (temp_var);
          free (temp_string);
        }
@@ -748,7 +748,7 @@ set_pwd ()
   /* According to the Single Unix Specification, v2, $OLDPWD is an
      `environment variable' and therefore should be auto-exported.
      Make a dummy invisible variable for OLDPWD, and mark it as exported. */
-  temp_var = bind_variable ("OLDPWD", (char *)NULL);
+  temp_var = bind_variable ("OLDPWD", (char *)NULL, 0);
   VSETATTR (temp_var, (att_exported | att_invisible));
 }
 
@@ -763,7 +763,7 @@ set_ppid ()
   temp_var = find_variable ("PPID");
   if (temp_var)
     VUNSETATTR (temp_var, (att_readonly | att_exported));
-  temp_var = bind_variable ("PPID", name);
+  temp_var = bind_variable ("PPID", name, 0);
   VSETATTR (temp_var, (att_readonly | att_integer));
 }
 
@@ -777,7 +777,7 @@ uidset ()
   v = find_variable ("UID");
   if (v == 0)
     {
-      v = bind_variable ("UID", b);
+      v = bind_variable ("UID", b, 0);
       VSETATTR (v, (att_readonly | att_integer));
     }
 
@@ -787,7 +787,7 @@ uidset ()
   v = find_variable ("EUID");
   if (v == 0)
     {
-      v = bind_variable ("EUID", b);
+      v = bind_variable ("EUID", b, 0);
       VSETATTR (v, (att_readonly | att_integer));
     }
 }
@@ -830,10 +830,10 @@ sh_set_lines_and_columns (lines, cols)
   char val[INT_STRLEN_BOUND(int) + 1], *v;
 
   v = inttostr (lines, val, sizeof (val));
-  bind_variable ("LINES", v);
+  bind_variable ("LINES", v, 0);
 
   v = inttostr (cols, val, sizeof (val));
-  bind_variable ("COLUMNS", v);
+  bind_variable ("COLUMNS", v, 0);
 }
 
 /* **************************************************************** */
@@ -983,7 +983,7 @@ print_var_function (var)
 #define INIT_DYNAMIC_VAR(var, val, gfunc, afunc) \
   do \
     { \
-      v = bind_variable (var, (val)); \
+      v = bind_variable (var, (val), 0); \
       v->dynamic_value = gfunc; \
       v->assign_func = afunc; \
     } \
@@ -1589,7 +1589,7 @@ set_if_not (name, value)
 
   v = find_variable (name);
   if (v == 0)
-    v = bind_variable_internal (name, value, global_variables->table, HASH_NOSRCH);
+    v = bind_variable_internal (name, value, global_variables->table, HASH_NOSRCH, 0);
   return (v);
 }
 
@@ -1643,7 +1643,7 @@ make_local_variable (name)
     }
 
   if (old_var == 0)
-    new_var = bind_variable_internal (name, "", vc->table, HASH_NOSRCH);
+    new_var = bind_variable_internal (name, "", vc->table, HASH_NOSRCH, 0);
   else
     {
       new_var = make_new_variable (name, vc->table);
@@ -1758,13 +1758,14 @@ make_new_array_variable (name)
 #endif
 
 char *
-make_variable_value (var, value)
+make_variable_value (var, value, flags)
      SHELL_VAR *var;
      char *value;
+     int flags;
 {
-  char *retval;
-  intmax_t lval;
-  int expok;
+  char *retval, *oval;
+  intmax_t lval, rval;
+  int expok, olen;
 
   /* If this variable has had its type set to integer (via `declare -i'),
      then do expression evaluation on it and store the result.  The
@@ -1773,14 +1774,34 @@ make_variable_value (var, value)
      evaluation done. */
   if (integer_p (var))
     {
-      lval = evalexp (value, &expok);
+      if (flags & ASS_APPEND)
+       {
+         oval = value_cell (var);
+         lval = evalexp (oval, &expok);        /* ksh93 seems to do this */
+         if (expok == 0)
+           jump_to_top_level (DISCARD);
+       }
+      rval = evalexp (value, &expok);
       if (expok == 0)
        jump_to_top_level (DISCARD);
-      retval = itos (lval);
+      if (flags & ASS_APPEND)
+       rval += lval;
+      retval = itos (rval);
     }
   else if (value)
     {
-      if (*value)
+      if (flags & ASS_APPEND)
+       {
+         oval = value_cell (var);
+         if (oval == 0)        /* paranoia */
+           oval = "";
+         olen = STRLEN (oval);
+         retval = (char *)xmalloc (olen + (value ? STRLEN (value) : 0) + 1);
+         strcpy (retval, oval);
+         if (value)
+           strcpy (retval+olen, value);
+       }
+      else if (*value)
        retval = savestring (value);
       else
        {
@@ -1797,11 +1818,11 @@ make_variable_value (var, value)
 /* Bind a variable NAME to VALUE in the HASH_TABLE TABLE, which may be the
    temporary environment (but usually is not). */
 static SHELL_VAR *
-bind_variable_internal (name, value, table, hflags)
+bind_variable_internal (name, value, table, hflags, aflags)
      const char *name;
      char *value;
      HASH_TABLE *table;
-     int hflags;
+     int hflags, aflags;
 {
   char *newval;
   SHELL_VAR *entry;
@@ -1811,7 +1832,7 @@ bind_variable_internal (name, value, table, hflags)
   if (entry == 0)
     {
       entry = make_new_variable (name, table);
-      var_setvalue (entry, make_variable_value (entry, value));
+      var_setvalue (entry, make_variable_value (entry, value, 0)); /* XXX */
     }
   else if (entry->assign_func) /* array vars have assign functions now */
     {
@@ -1830,7 +1851,7 @@ bind_variable_internal (name, value, table, hflags)
       /* Variables which are bound are visible. */
       VUNSETATTR (entry, att_invisible);
 
-      newval = make_variable_value (entry, value);
+      newval = make_variable_value (entry, value, aflags);     /* XXX */
 
       /* Invalidate any cached export string */
       INVALIDATE_EXPORTSTR (entry);
@@ -1867,9 +1888,10 @@ bind_variable_internal (name, value, table, hflags)
    first, then we bind into shell_variables. */
 
 SHELL_VAR *
-bind_variable (name, value)
+bind_variable (name, value, flags)
      const char *name;
      char *value;
+     int flags;
 {
   SHELL_VAR *v;
   VAR_CONTEXT *vc;
@@ -1896,10 +1918,10 @@ bind_variable (name, value)
         {
           v = hash_lookup (name, vc->table);
           if (v)
-           return (bind_variable_internal (name, value, vc->table, 0));
+           return (bind_variable_internal (name, value, vc->table, 0, flags));
         }
     }
-  return (bind_variable_internal (name, value, global_variables->table, 0));
+  return (bind_variable_internal (name, value, global_variables->table, 0, flags));
 }
 
 /* Make VAR, a simple shell variable, have value VALUE.  Once assigned a
@@ -1908,15 +1930,16 @@ bind_variable (name, value)
    all modified variables should be exported, mark the variable for export
    and note that the export environment needs to be recreated. */
 SHELL_VAR *
-bind_variable_value (var, value)
+bind_variable_value (var, value, aflags)
      SHELL_VAR *var;
      char *value;
+     int aflags;
 {
   char *t;
 
   VUNSETATTR (var, att_invisible);
 
-  t = make_variable_value (var, value);
+  t = make_variable_value (var, value, aflags);
   FREE (value_cell (var));
   var_setvalue (var, t);
 
@@ -1972,10 +1995,10 @@ bind_int_variable (lhs, rhs)
 
 #if defined (ARRAY_VARS)
   if (isarr)
-    v = assign_array_element (lhs, rhs);
+    v = assign_array_element (lhs, rhs, 0);
   else
 #endif
-    v = bind_variable (lhs, rhs);
+    v = bind_variable (lhs, rhs, 0);
 
   if (isint)
     VSETATTR (v, att_integer);
@@ -2073,6 +2096,8 @@ bind_function_def (name, value)
    HASH_TABLE (temporary_env).  The functions in execute_cmd.c are
    responsible for moving the main temporary env to one of the other
    temporary environments.  The expansion code in subst.c calls this. */
+
+   /* XXX - changes needed for `+=' */
 int
 assign_in_env (word)
      WORD_DESC *word;
@@ -2092,6 +2117,10 @@ assign_in_env (word)
     {
       name[offset] = 0;
 
+      /* ignore the `+' when assigning temporary environment */
+      if (name[offset - 1] == '+')
+       name[offset - 1] = '\0';
+
       var = find_variable (name);
       if (var && (readonly_p (var) || noassign_p (var)))
        {
@@ -2407,7 +2436,7 @@ delete_all_variables (hashed_vars)
       entry = find_variable (name); \
       if (!entry) \
        { \
-         entry = bind_variable (name, ""); \
+         entry = bind_variable (name, "", 0); \
          if (!no_invisible_vars) entry->attributes |= att_invisible; \
        } \
     } \
@@ -2849,7 +2878,7 @@ push_temp_var (data)
        binding_table = shell_variables->table = hash_create (TEMPENV_HASH_BUCKETS);
     }
 
-  v = bind_variable_internal (var->name, value_cell (var), binding_table, 0);
+  v = bind_variable_internal (var->name, value_cell (var), binding_table, 0, 0);
 
   /* XXX - should we set the context here?  It shouldn't matter because of how
      assign_in_env works, but might want to check. */
@@ -3367,7 +3396,7 @@ push_func_var (data)
   if (tempvar_p (var) && (posixly_correct || (var->attributes & att_propagate)))
     {
       /* XXX - should we set v->context here? */
-      v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0);
+      v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0, 0);
       if (shell_variables == global_variables)
        var->attributes &= ~(att_tempvar|att_propagate);
       else
@@ -3446,10 +3475,16 @@ push_exported_var (data)
 
   /* If a temp var had its export attribute set, or it's marked to be
      propagated, bind it in the previous scope before disposing it. */
+  /* XXX - This isn't exactly right, because all tempenv variables have the
+    export attribute set. */
+#if 0
   if (exported_p (var) || (var->attributes & att_propagate))
+#else
+  if (tempvar_p (var) && exported_p (var) && (var->attributes & att_propagate))
+#endif
     {
       var->attributes &= ~att_tempvar;         /* XXX */
-      v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0);
+      v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0, 0);
       if (shell_variables == global_variables)
        var->attributes &= ~att_propagate;
       v->attributes |= var->attributes;
index 08db0f5e3b042a00737f957edde23e147291a84f..0974c72f5ca5bc9724d9928d6bf85e3c26bf4dfb 100644 (file)
@@ -1,6 +1,6 @@
 /* variables.h -- data structures for shell variables. */
 
-/* Copyright (C) 1987-2002 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2004 Free Software Foundation, Inc.
 
    This file is part of GNU Bash, the Bourne Again SHell.
 
@@ -224,7 +224,7 @@ extern SHELL_VAR *find_variable_internal __P((const char *, int));
 extern SHELL_VAR *find_tempenv_variable __P((const char *));
 extern SHELL_VAR *copy_variable __P((SHELL_VAR *));
 extern SHELL_VAR *make_local_variable __P((const char *));
-extern SHELL_VAR *bind_variable __P((const char *, char *));
+extern SHELL_VAR *bind_variable __P((const char *, char *, int));
 extern SHELL_VAR *bind_function __P((const char *, COMMAND *));
 
 extern void bind_function_def __P((const char *, FUNCTION_DEF *));
@@ -250,9 +250,9 @@ extern char **add_or_supercede_exported_var __P((char *, int));
 extern char *get_variable_value __P((SHELL_VAR *));
 extern char *get_string_value __P((const char *));
 extern char *sh_get_env_value __P((const char *));
-extern char *make_variable_value __P((SHELL_VAR *, char *));
+extern char *make_variable_value __P((SHELL_VAR *, char *, int));
 
-extern SHELL_VAR *bind_variable_value __P((SHELL_VAR *, char *));
+extern SHELL_VAR *bind_variable_value __P((SHELL_VAR *, char *, int));
 extern SHELL_VAR *bind_int_variable __P((char *, char *));
 extern SHELL_VAR *bind_var_to_int __P((char *, intmax_t));
 
index cd11fd97c8961fa6b5d56c79e623d1f718738099..d514cd8f8a0e3c019b1f601754b0ae2342ed3f89 100644 (file)
@@ -224,7 +224,7 @@ extern SHELL_VAR *find_variable_internal __P((const char *, int));
 extern SHELL_VAR *find_tempenv_variable __P((const char *));
 extern SHELL_VAR *copy_variable __P((SHELL_VAR *));
 extern SHELL_VAR *make_local_variable __P((const char *));
-extern SHELL_VAR *bind_variable __P((const char *, char *));
+extern SHELL_VAR *bind_variable __P((const char *, char *, int));
 extern SHELL_VAR *bind_function __P((const char *, COMMAND *));
 
 extern void bind_function_def __P((const char *, FUNCTION_DEF *));
@@ -250,13 +250,14 @@ extern char **add_or_supercede_exported_var __P((char *, int));
 extern char *get_variable_value __P((SHELL_VAR *));
 extern char *get_string_value __P((const char *));
 extern char *sh_get_env_value __P((const char *));
-extern char *make_variable_value __P((SHELL_VAR *, char *));
+extern char *make_variable_value __P((SHELL_VAR *, char *, int));
 
-extern SHELL_VAR *bind_variable_value __P((SHELL_VAR *, char *));
+extern SHELL_VAR *bind_variable_value __P((SHELL_VAR *, char *, int));
 extern SHELL_VAR *bind_int_variable __P((char *, char *));
 extern SHELL_VAR *bind_var_to_int __P((char *, intmax_t));
 
-extern int assign_in_env __P((const char *));
+extern int assign_in_env __P((WORD_DESC *));
+
 extern int unbind_variable __P((const char *));
 extern int unbind_func __P((const char *));
 extern int unbind_function_def __P((const char *));