]> git.ipfire.org Git - thirdparty/bash.git/blobdiff - builtins/setattr.def
bash-4.3-rc2 overlay
[thirdparty/bash.git] / builtins / setattr.def
index 2340e1a8420db4cdbbe8664634d1fe5492cc491e..3be31896ef272f8e4fbac76acaa4bfc0b68a164e 100644 (file)
@@ -1,43 +1,71 @@
 This file is setattr.def, from which is created setattr.c.
 It implements the builtins "export" and "readonly", in Bash.
 
-Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
+Copyright (C) 1987-2012 Free Software Foundation, Inc.
 
 This file is part of GNU Bash, the Bourne Again SHell.
 
-Bash is free software; you can redistribute it and/or modify it under
-the terms of the GNU General Public License as published by the Free
-Software Foundation; either version 1, or (at your option) any later
-version.
+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 3 of the License, 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.
+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, 675 Mass Ave, Cambridge, MA 02139, USA.
+You should have received a copy of the GNU General Public License
+along with Bash.  If not, see <http://www.gnu.org/licenses/>.
 
 $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 [-n] [-f] [name ...] 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.
+$SHORT_DOC export [-fn] [name[=value] ...] or export -p
+Set export attribute for shell variables.
+
+Marks each NAME for automatic export to the environment of subsequently
+executed commands.  If VALUE is supplied, assign VALUE before exporting.
+
+Options:
+  -f   refer to shell functions
+  -n   remove the export property from each NAME
+  -p   display a list of all exported variables and functions
+
+An argument of `--' disables further option processing.
+
+Exit Status:
+Returns success unless an invalid option is given or NAME is invalid.
 $END
 
 /* For each variable name in LIST, make that variable appear in the
@@ -45,46 +73,73 @@ $END
    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));
+  return (set_or_show_attributes (list, att_exported, 0));
 }
 
 $BUILTIN readonly
 $FUNCTION readonly_builtin
-$SHORT_DOC readonly [-n] [-f] [name ...] 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.  An argument of `-n' says to remove the readonly property
-from subsequent NAMEs.  An argument of `--' disables further option
-processing.
+$SHORT_DOC readonly [-aAf] [name[=value] ...] or readonly -p
+Mark shell variables as unchangeable.
+
+Mark each NAME as read-only; the values of these NAMEs may not be
+changed by subsequent assignment.  If VALUE is supplied, assign VALUE
+before marking as read-only.
+
+Options:
+  -a   refer to indexed array variables
+  -A   refer to associative array variables
+  -f   refer to shell functions
+  -p   display a list of all readonly variables or functions, depending on
+        whether or not the -f option is given
+
+An argument of `--' disables further option processing.
+
+Exit Status:
+Returns success unless an invalid option is given or NAME is invalid.
 $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));
+  return (set_or_show_attributes (list, att_readonly, 0));
 }
 
+#if defined (ARRAY_VARS)
+#  define ATTROPTS     "aAfnp"
+#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. */
+   remaining names in LIST (doesn't work for readonly). */
 int
-set_or_show_attributes (list, attribute)
+set_or_show_attributes (list, attribute, nodefs)
      register WORD_LIST *list;
-     int attribute;
+     int attribute, nodefs;
 {
   register SHELL_VAR *var;
-  int assign, undo = 0, functions_only = 0, any_failed = 0, opt;
-
+  int assign, undo, any_failed, assign_error, opt;
+  int functions_only, arrays_only, assoc_only;
+  int aflags;
+  char *name;
+#if defined (ARRAY_VARS)
+  WORD_LIST *nlist, *tlist;
+  WORD_DESC *w;
+#endif
+
+  functions_only = arrays_only = assoc_only = 0;
+  undo = any_failed = assign_error = 0;
   /* Read arguments from the front of the list. */
   reset_internal_getopt ();
-  while ((opt = internal_getopt (list, "nfp")) != -1)
+  while ((opt = internal_getopt (list, ATTROPTS)) != -1)
     {
       switch (opt)
        {
@@ -94,10 +149,18 @@ set_or_show_attributes (list, attribute)
          case 'f':
            functions_only = 1;
            break;
+#if defined (ARRAY_VARS)
+         case 'a':
+           arrays_only = 1;
+           break;
+         case 'A':
+           assoc_only = 1;
+           break;
+#endif
          case 'p':
            break;
          default:
-           builtin_error ("usage: %s [-nfp] [varname]", this_command_name);
+           builtin_usage ();
            return (EX_USAGE);
        }
     }
@@ -108,85 +171,89 @@ set_or_show_attributes (list, attribute)
       if (attribute & att_exported)
        array_needs_making = 1;
 
-      /* Cannot undo readonly status. */
+      /* Cannot undo readonly status, silently disallowed. */
       if (undo && (attribute & att_readonly))
        attribute &= ~att_readonly;
 
       while (list)
        {
-         register char *name = list->word->word;
+         name = list->word->word;
 
-         if (functions_only)
+         if (functions_only)           /* xxx -f name */
            {
              var = find_function (name);
-             if (!var)
+             if (var == 0)
                {
-                 builtin_error ("%s: not a function", name);
+                 builtin_error (_("%s: not a function"), name);
                  any_failed++;
                }
              else
-               {
-                 if (undo)
-                   var->attributes &= ~attribute;
-                 else
-                   var->attributes |= attribute;
-               }
-             list = list->next;
-             if (attribute == att_exported)
-               array_needs_making++;
-             continue;
-           }
+               SETVARATTR (var, attribute, undo);
 
-         assign = assignment (name);
-
-          if (assign)
-           name[assign] = '\0';
-         if (legal_identifier (name) == 0)
-           {
-             builtin_error ("%s: not a legal variable name", name);
-             any_failed++;
              list = list->next;
              continue;
            }
 
+         /* xxx [-np] name[=value] */
+         assign = assignment (name, 0);
+
+         aflags = 0;
          if (assign)
            {
-             name[assign] = '=';
-             /* 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. */
-             do_assignment_no_expand (name);
              name[assign] = '\0';
+             if (name[assign - 1] == '+')
+               {
+                 aflags |= ASS_APPEND;
+                 name[assign - 1] = '\0';
+               }
            }
 
-         if (undo)
+         if (legal_identifier (name) == 0)
            {
-             var = find_variable (name);
-             if (var)
-               var->attributes &= ~attribute;
+             sh_invalidid (name);
+             if (assign)
+               assign_error++;
+             else
+               any_failed++;
+             list = list->next;
+             continue;
            }
-         else
-           {
-             SHELL_VAR *find_tempenv_variable (), *tv;
 
-             if (tv = find_tempenv_variable (name))
+         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 || assoc_only)
                {
-                 var = bind_variable (tv->name, tv->value);
-                 dispose_variable (tv);
+                 tlist = list->next;
+                 list->next = (WORD_LIST *)NULL;
+                 w = arrays_only ? make_word ("-ra") : 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
-               var = find_variable (name);
-
-             if (!var)
-               {
-                 var = bind_variable (name, (char *)NULL);
-                 var->attributes |= att_invisible;
-               }
-
-             var->attributes |= attribute;
+#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';
            }
 
-         array_needs_making++; /* XXX */
+         set_var_attribute (name, attribute, undo);
          list = list->next;
        }
     }
@@ -204,50 +271,281 @@ set_or_show_attributes (list, attribute)
       else
        variable_list = all_shell_variables ();
 
+#if defined (ARRAY_VARS)
+      if (attribute & att_array)
+       {
+         arrays_only++;
+         if (attribute != att_array)
+           attribute &= ~att_array;
+       }
+      else if (attribute & att_assoc)
+       {
+         assoc_only++;
+         if (attribute != att_assoc)
+           attribute &= ~att_assoc;
+       }
+#endif
+
       if (variable_list)
        {
          for (i = 0; var = variable_list[i]; i++)
            {
-             if ((var->attributes & attribute) && !invisible_p (var))
+#if defined (ARRAY_VARS)
+             if (arrays_only && array_p (var) == 0)
+               continue;
+             else if (assoc_only && assoc_p (var) == 0)
+               continue;
+#endif
+             if ((var->attributes & attribute))
                {
-                 char flags[6];
+                 show_var_attributes (var, READONLY_OR_EXPORT, nodefs);
+                 if (any_failed = sh_chkwrite (any_failed))
+                   break;
+               }
+           }
+         free (variable_list);
+       }
+    }
+
+  return (assign_error ? EX_BADASSIGN
+                      : ((any_failed == 0) ? EXECUTION_SUCCESS
+                                           : EXECUTION_FAILURE));
+}
+
+/* Show all variable variables (v == 1) or functions (v == 0) with
+   attributes. */
+int
+show_all_var_attributes (v, nodefs)
+     int v, nodefs;
+{
+  SHELL_VAR **variable_list, *var;
+  int any_failed;
+  register int i;
+
+  variable_list = v ? all_shell_variables () : all_shell_functions ();
+  if (variable_list == 0)  
+    return (EXECUTION_SUCCESS);
+
+  for (i = any_failed = 0; var = variable_list[i]; i++)
+    {
+      show_var_attributes (var, READONLY_OR_EXPORT, nodefs);
+      if (any_failed = sh_chkwrite (any_failed))
+        break;
+    }
+  free (variable_list);
+  return (any_failed == 0 ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
+}
 
-                 flags[0] = '\0';
+/* 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[16], *x;
+  int i;
 
-                 if (exported_p (var))
-                   strcat (flags, "x");
+  i = 0;
 
-                 if (readonly_p (var))
-                   strcat (flags, "r");
+  /* 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';
 
-                 if (function_p (var))
-                   strcat (flags, "f");
+      if (assoc_p (var))
+       flags[i++] = 'A';
+#endif
 
-                 if (integer_p (var))
-                   strcat (flags, "i");
+      if (function_p (var))
+       flags[i++] = 'f';
 
-                 if (flags[0])
-                   {
-                     printf ("declare -%s ", flags);
+      if (integer_p (var))
+       flags[i++] = 'i';
 
-                     if (!function_p (var))
-                       {
-                         char *x = double_quote (value_cell (var));
-                         printf ("%s=%s\n", var->name, x);
-                         free (x);
-                       }
-                     else
-                       {
-                         char *named_function_string ();
+      if (nameref_p (var))
+       flags[i++] = 'n';
 
-                         printf ("%s\n", named_function_string
-                                 (var->name, function_cell (var), 1));
-                       }
-                   }
-               }
+      if (readonly_p (var))
+       flags[i++] = 'r';
+
+      if (trace_p (var))
+       flags[i++] = 't';
+
+      if (exported_p (var))
+       flags[i++] = 'x';
+
+      if (capcase_p (var))
+       flags[i++] = 'c';
+
+      if (lowercase_p (var))
+       flags[i++] = 'l';
+
+      if (uppercase_p (var))
+       flags[i++] = 'u';
+    }
+  else
+    {
+#if defined (ARRAY_VARS)
+      if (array_p (var))
+       flags[i++] = 'a';
+
+      if (assoc_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), FUNC_MULTILINE|FUNC_EXTERNAL));
+      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 if (assoc_p (var))
+    print_assoc_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), FUNC_MULTILINE|FUNC_EXTERNAL));
+  else if (invisible_p (var) || var_isset (var) == 0)
+    printf ("%s\n", var->name);
+  else
+    {
+      x = sh_double_quote (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;
+
+#if 0
+  var = find_variable_tempenv (name);
+#else
+  var = find_variable_noref (name);
+#endif
+
+  if (var && invisible_p (var) == 0)
+    {
+      show_var_attributes (var, READONLY_OR_EXPORT, nodefs);
+      return (0);
+    }
+  else
+    return (1);
+}
+
+int
+show_func_attributes (name, nodefs)
+     char *name;
+     int nodefs;
+{
+  SHELL_VAR *var;
+
+  var = find_function (name);
+
+  if (var)
+    {
+      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, *v;
+  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;
+         /* This avoids an error message when propagating a read-only var
+            later on. */
+         if (var->context == 0 && (attribute & att_readonly))
+           {
+             /* Don't bother to set the `propagate to the global variables
+                table' flag if we've just bound the variable in that table */
+             v = find_global_variable (tv->name);
+             if (v != var)
+               VSETATTR (tv, att_propagate);
            }
-         free (variable_list);
+         else
+           VSETATTR (tv, att_propagate);
+         if (var->context != 0)
+           VSETATTR (var, att_propagate);
+         SETVARATTR (tv, attribute, undo);     /* XXX */
+
+         stupidly_hack_special_variables (tv->name);
+
+         free (tvalue);
+       }
+      else
+       {
+         var = find_variable_notempenv (name);
+         if (var == 0)
+           {
+             var = bind_variable (name, (char *)NULL, 0);
+             VSETATTR (var, att_invisible);
+           }
+         else if (var->context != 0)
+           VSETATTR (var, att_propagate);
        }
     }
-  return (any_failed == 0 ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
+
+  if (var)
+    SETVARATTR (var, attribute, undo);
+
+  if (var && (exported_p (var) || (attribute & att_exported)))
+    array_needs_making++;      /* XXX */
 }