Imported from ../bash-2.0.tar.gz.
[platform/upstream/bash.git] / builtins / set.def
index a97168c..d31e77e 100644 (file)
@@ -21,62 +21,79 @@ Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
 $PRODUCES set.c
 
+#include <config.h>
+
+#if defined (HAVE_UNISTD_H)
+#  include <unistd.h>
+#endif
+
 #include <stdio.h>
+
+#include "../bashansi.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 interactive;
-extern int noclobber, no_brace_expansion, posixly_correct;
+extern int noclobber, posixly_correct, ignoreeof, eof_encountered_limit;
 #if defined (READLINE)
 extern int rl_editing_mode, no_line_editing;
 #endif /* READLINE */
 
-#define USAGE_STRING "set [--abefhknotuvxldHCP] [-o option] [arg ...]"
-
 $BUILTIN set
 $FUNCTION set_builtin
-$SHORT_DOC set [--abefhknotuvxldHCP] [-o option] [arg ...]
+$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  Locate and remember function commands as functions are
-        defined.  Function commands are normally looked up when
-        the function is executed.
+    -h  Remember the location of commands as they are looked up.
     -i  Force the shell to be an "interactive" one.  Interactive shells
         always read `~/.bashrc' on startup.
-    -k  All keyword arguments are placed in the environment for a
+    -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  the shell will perform brace expansion
+            braceexpand  same as -B
 #if defined (READLINE)
             emacs        use an emacs-style line editing interface
 #endif /* READLINE */
             errexit      same as -e
+            hashall      same as -h
 #if defined (BANG_HISTORY)
             histexpand   same as -H
 #endif /* BANG_HISTORY */
             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    disallow redirection to existing files
+            noclobber    same as -C
             noexec       same as -n
             noglob       same as -f
-            nohash       same as -d
             notify       save as -b
             nounset      same as -u
-           physical     same as -P
-           posix        change the behavior of bash where the default
-                        operation differs from the 1003.2 standard to
-                        match the standard
-           privileged   same as -p
+            onecmd       same as -t
+            physical     same as -P
+            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
@@ -85,15 +102,14 @@ $SHORT_DOC set [--abefhknotuvxldHCP] [-o option] [arg ...]
     -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.
+        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.
-    -l  Save and restore the binding of the NAME in a FOR command.
-    -d  Disable the hashing of commands that are looked up for execution.
-        Normally, commands are remembered in a hash table, and once
-        found, do not have to be looked up again.
+#if defined (BRACE_EXPANSION)
+    -B  the shell will perform brace expansion
+#endif /* BRACE_EXPANSION */
 #if defined (BANG_HISTORY)
     -H  Enable ! style history substitution.  This flag is on
         by default.
@@ -110,6 +126,20 @@ parameters and are assigned, in order, to $1, $2, .. $n.  If no
 ARGs are given, all shell variables are printed.
 $END
 
+static int set_ignoreeof ();
+
+#if defined (READLINE)
+static int set_edit_mode ();
+static int get_edit_mode ();
+#endif
+
+#if defined (HISTORY)
+static int bash_set_history ();
+#endif
+
+static char *on = "on";
+static char *off = "off";
+
 /* An a-list used to match long options for set -o to the corresponding
    option letter. */
 struct {
@@ -117,201 +147,354 @@ struct {
   int letter;
 } o_options[] = {
   { "allexport",  'a' },
+#if defined (BRACE_EXPANSION)
+  { "braceexpand",'B' },
+#endif
   { "errexit",   'e' },
+  { "hashall",    'h' },
 #if defined (BANG_HISTORY)
   { "histexpand", 'H' },
 #endif /* BANG_HISTORY */
+  { "keyword",    'k' },
   { "monitor",   'm' },
+  { "noclobber",  'C' },
   { "noexec",    'n' },
   { "noglob",    'f' },
-  { "nohash",    'd' },
 #if defined (JOB_CONTROL)
   { "notify",    'b' },
 #endif /* JOB_CONTROL */
-  {"nounset",    'u' },
-  {"physical",    'P' },
-  {"privileged",  'p' },
-  {"verbose",    'v' },
-  {"xtrace",     'x' },
-  {(char *)NULL, 0},
+  { "nounset",   'u' },
+  { "onecmd",    't' },
+  { "physical",   'P' },
+  { "privileged", 'p' },
+  { "verbose",   'v' },
+  { "xtrace",    'x' },
+  {(char *)NULL, 0 },
+};
+
+struct {
+  char *name;
+  int *variable;
+  Function *set_func;
+  Function *get_func;
+} binary_o_options[] = {
+#if defined (HISTORY)
+  { "history", &remember_on_history, bash_set_history, (Function *)NULL },
+#endif
+  { "ignoreeof", &ignoreeof, set_ignoreeof, (Function *)NULL },
+  { "interactive-comments", &interactive_comments, (Function *)NULL, (Function *)NULL },
+  { "posix", &posixly_correct, (Function *)NULL, (Function *)NULL },
+#if defined (READLINE)
+  { "emacs", (int *)NULL, set_edit_mode, get_edit_mode },
+  { "vi", (int *)NULL, set_edit_mode, get_edit_mode },
+#endif
+  { (char *)NULL, (int *)NULL }
 };
 
+#define GET_BINARY_O_OPTION_VALUE(i, name) \
+  ((binary_o_options[i].get_func) ? (*binary_o_options[i].get_func) (name) \
+                                 : (*binary_o_options[i].variable))
+
+#define SET_BINARY_O_OPTION_VALUE(i, onoff, name) \
+  ((binary_o_options[i].set_func) ? (*binary_o_options[i].set_func) (onoff, name) \
+                                 : (*binary_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))
+       {
+         on_or_off = find_flag (o_options[i].letter);
+         return ((on_or_off == FLAG_UNKNOWN) ? -1 : *on_or_off);
+       }
+    }
+  for (i = 0; binary_o_options[i].name; i++)
+    {
+      if (STREQ (name, binary_o_options[i].name))
+        return (GET_BINARY_O_OPTION_VALUE (i, name));
+    }
+        
+  return (-1);
+}
+
 #define MINUS_O_FORMAT "%-15s\t%s\n"
 
 void
-list_minus_o_opts ()
+list_minus_o_opts (mode)
+     int mode;
 {
   register int i;
-  char *on = "on", *off = "off";
+  int *on_or_off, value;
 
-  printf (MINUS_O_FORMAT, "braceexpand", (no_brace_expansion == 0) ? on : off);
-  printf (MINUS_O_FORMAT, "noclobber", (noclobber == 1) ? on : off);
+  for (value = i = 0; o_options[i].name; i++)
+    {
+      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)
+       printf (MINUS_O_FORMAT, o_options[i].name, *on_or_off ? on : off);
+    }
+  for (i = 0; binary_o_options[i].name; i++)
+    {
+      value = GET_BINARY_O_OPTION_VALUE (i, binary_o_options[i].name);
+      if (mode == -1 || mode == value)
+       printf (MINUS_O_FORMAT, binary_o_options[i].name, value ? on : off);
+    }
+}
 
-  if (find_variable ("ignoreeof") || find_variable ("IGNOREEOF"))
-    printf (MINUS_O_FORMAT, "ignoreeof", on);
-  else
-    printf (MINUS_O_FORMAT, "ignoreeof", off);
+static void
+minus_o_option_commands ()
+{
+  register int i;
+  int *on_or_off, value;
 
-  printf (MINUS_O_FORMAT, "interactive-comments",
-         interactive_comments ? on : off);
+  for (value = i = 0; o_options[i].name; i++)
+    {
+      on_or_off = find_flag (o_options[i].letter);
+      if (on_or_off == FLAG_UNKNOWN)
+       on_or_off = &value;
+      printf ("set %co %s\n", *on_or_off ? '-' : '+', o_options[i].name);
+    }
+  for (i = 0; binary_o_options[i].name; i++)
+    {
+      value = GET_BINARY_O_OPTION_VALUE (i, binary_o_options[i].name);
+      printf ("set %co %s\n", value ? '-' : '+', binary_o_options[i].name);
+    }
+}
 
-  printf (MINUS_O_FORMAT, "posix", posixly_correct ? on : off);
+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"); 
+  else
+    unbind_variable ("IGNOREEOF");
+  sv_ignoreeof ("IGNOREEOF");
+  return 0;
+}
 
 #if defined (READLINE)
-  if (no_line_editing)
+/* 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)
     {
-      printf (MINUS_O_FORMAT, "emacs", off);
-      printf (MINUS_O_FORMAT, "vi", off);
+      rl_variable_bind ("editing-mode", option_name);
+
+      if (interactive)
+       with_input_from_stdin ();
+      no_line_editing = 0;
     }
   else
     {
-      /* Magic.  This code `knows' how readline handles rl_editing_mode. */
-      printf (MINUS_O_FORMAT, "emacs", (rl_editing_mode == 1) ? on : off);
-      printf (MINUS_O_FORMAT, "vi", (rl_editing_mode == 0) ? on : off);
+      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 */
 
-  for (i = 0; o_options[i].name; i++)
+#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)
     {
-      int *on_or_off, zero = 0;
-
-      on_or_off = find_flag (o_options[i].letter);
-      if (on_or_off == FLAG_UNKNOWN)
-       on_or_off = &zero;
-      printf (MINUS_O_FORMAT, o_options[i].name, (*on_or_off == 1) ? on : off);
+      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;
 {
-  int option_char = -1;
+  int option_char;
+  VFunction *set_func;
+  register int i;
 
-  if (STREQ (option_name, "braceexpand"))
+  for (i = 0; binary_o_options[i].name; i++)
     {
-      if (on_or_off == FLAG_ON)
-       no_brace_expansion = 0;
-      else
-       no_brace_expansion = 1;
+      if (STREQ (option_name, binary_o_options[i].name))
+       {
+         SET_BINARY_O_OPTION_VALUE (i, on_or_off, option_name);
+         return (EXECUTION_SUCCESS);
+       }
     }
-  else if (STREQ (option_name, "noclobber"))
+
+  for (i = 0, option_char = -1, set_func = 0; o_options[i].name; i++)
     {
-      if (on_or_off == FLAG_ON)
-       bind_variable ("noclobber", "");
-      else
-       unbind_variable ("noclobber");
-      stupidly_hack_special_variables ("noclobber");
+      if (STREQ (option_name, o_options[i].name))
+        {
+          option_char = o_options[i].letter;
+          break;
+        }
     }
-  else if (STREQ (option_name, "ignoreeof"))
+  if (option_char == -1)
     {
-      unbind_variable ("ignoreeof");
-      unbind_variable ("IGNOREEOF");
-      if (on_or_off == FLAG_ON)
-       bind_variable ("IGNOREEOF", "10");
-      stupidly_hack_special_variables ("IGNOREEOF");
+      builtin_error ("%s: unknown option name", option_name);
+      return (EXECUTION_FAILURE);
     }
-  
-#if defined (READLINE)
-  else if ((STREQ (option_name, "emacs")) || (STREQ (option_name, "vi")))
+ if (change_flag (option_char, on_or_off) == FLAG_ERROR)
     {
-      if (on_or_off == FLAG_ON)
-       {
-         rl_variable_bind ("editing-mode", option_name);
+      bad_option (option_name);
+      return (EXECUTION_FAILURE);
+    }
+  return (EXECUTION_SUCCESS);
+}
 
-         if (interactive)
-           with_input_from_stdin ();
-         no_line_editing = 0;
-       }
-      else
-       {
-         int isemacs = (rl_editing_mode == 1);
-         if ((isemacs && STREQ (option_name, "emacs")) ||
-             (!isemacs && STREQ (option_name, "vi")))
-           {
-             if (interactive)
-               with_input_from_stream (stdin, "stdin");
-             no_line_editing = 1;
-           }
-         else
-           builtin_error ("not in %s editing mode", option_name);
-       }
+static void
+print_all_shell_variables ()
+{
+  SHELL_VAR **vars;
+
+  vars = all_shell_variables ();
+  if (vars)
+    {
+      print_var_list (vars);
+      free (vars);
     }
-#endif /* READLINE */
-  else if (STREQ (option_name, "interactive-comments"))
-    interactive_comments = (on_or_off == FLAG_ON);
-  else if (STREQ (option_name, "posix"))
+
+  vars = all_shell_functions ();
+  if (vars)
     {
-      posixly_correct = (on_or_off == FLAG_ON);
-      unbind_variable ("POSIXLY_CORRECT");
-      unbind_variable ("POSIX_PEDANTIC");
-      if (on_or_off == FLAG_ON)
-       {
-         bind_variable ("POSIXLY_CORRECT", "");
-         stupidly_hack_special_variables ("POSIXLY_CORRECT");
-       }
+      print_var_list (vars);
+      free (vars);
     }
-  else
+}
+
+void
+set_shellopts ()
+{
+  char *value;
+  int vsize, i, vptr, *ip;
+  SHELL_VAR *v;
+
+  for (vsize = i = 0; o_options[i].name; i++)
     {
-      register int i;
-      for (i = 0; o_options[i].name; i++)
-       {
-         if (STREQ (option_name, o_options[i].name))
-           {
-             option_char = o_options[i].letter;
-             break;
-           }
-       }
-      if (option_char == -1)
-       {
-         builtin_error ("%s: unknown option name", option_name);
-         return (EXECUTION_FAILURE);
-       }
-      if (change_flag (option_char, on_or_off) == FLAG_ERROR)
+      ip = find_flag (o_options[i].letter);
+      if (ip && *ip)
+       vsize += strlen (o_options[i].name) + 1;
+    }
+  for (i = 0; binary_o_options[i].name; i++)
+    if (GET_BINARY_O_OPTION_VALUE (i, binary_o_options[i].name))
+      vsize += strlen (binary_o_options[i].name) + 1;
+
+  value = xmalloc (vsize + 1);
+
+  for (i = vptr = 0; o_options[i].name; i++)
+    {
+      ip = find_flag (o_options[i].letter);
+      if (ip && *ip)
        {
-         bad_option (option_name);
-         return (EXECUTION_FAILURE);
+         strcpy (value + vptr, o_options[i].name);
+         vptr += strlen (o_options[i].name);
+         value[vptr++] = ':';
        }
     }
-  return (EXECUTION_SUCCESS);
+  for (i = 0; binary_o_options[i].name; i++)
+    if (GET_BINARY_O_OPTION_VALUE (i, binary_o_options[i].name))
+      {
+       strcpy (value + vptr, binary_o_options[i].name);
+       vptr += strlen (binary_o_options[i].name);
+       value[vptr++] = ':';
+      }
+  value[--vptr] = '\0';                /* cut off trailing colon */
+
+  v = find_variable ("SHELLOPTS");
+  if (v)
+    v->attributes &= ~att_readonly;
+  v = bind_variable ("SHELLOPTS", value);
+  v->attributes |= att_readonly;
+
+  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 ()
+{
+  char *temp;
+
+  /* set up any shell options we may have inherited. */
+  if (temp = get_string_value ("SHELLOPTS"))
+    parse_shellopts (temp);
+
+  /* Set up the $SHELLOPTS variable. */
+  set_shellopts ();
 }
 
 /* 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 = 0;
+  int on_or_off, flag_name, force_assignment, opts_changed;
+  WORD_LIST *l;
+  register char *arg;
 
-  if (!list)
+  if (list == 0)
     {
-      SHELL_VAR **vars;
-
-      vars = all_shell_variables ();
-      if (vars)
-       {
-         print_var_list (vars);
-         free (vars);
-       }
-
-      vars = all_shell_functions ();
-      if (vars)
-       {
-         print_var_list (vars);
-         free (vars);
-       }
-
+      print_all_shell_variables ();
       return (EXECUTION_SUCCESS);
     }
 
   /* Check validity of flag arguments. */
   if (*list->word->word == '-' || *list->word->word == '+')
     {
-      register char *arg;
-      WORD_LIST *save_list = list;
-
-      while (list && (arg = list->word->word))
+      for (l = list; l && (arg = l->word->word); l = l->next)
        {
          char c;
 
@@ -319,8 +502,7 @@ set_builtin (list)
            break;
 
          /* `-' or `--' signifies end of flag arguments. */
-         if (arg[0] == '-' &&
-             (!arg[1] || (arg[1] == '-' && !arg[2])))
+         if (arg[0] == '-' && (!arg[1] || (arg[1] == '-' && !arg[2])))
            break;
 
          while (c = *++arg)
@@ -331,30 +513,28 @@ set_builtin (list)
                  s[0] = c; s[1] = '\0';
                  bad_option (s);
                  if (c == '?')
-                   printf ("usage: %s\n", USAGE_STRING);
+                   builtin_usage ();
                  return (c == '?' ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
                }
            }
-         list = list->next;
        }
-      list = save_list;
     }
 
   /* Do the set command.  While the list consists of words starting with
      '-' or '+' treat them as flags, otherwise, start assigning them to
      $1 ... $n. */
-  while (list)
+  for (force_assignment = opts_changed = 0; list; )
     {
-      char *string = list->word->word;
+      arg = list->word->word;
 
       /* If the argument is `--' or `-' then signal the end of the list
         and remember the remaining arguments. */
-      if (string[0] == '-' && (!string[1] || (string[1] == '-' && !string[2])))
+      if (arg[0] == '-' && (!arg[1] || (arg[1] == '-' && !arg[2])))
        {
          list = list->next;
 
          /* `set --' unsets the positional parameters. */
-         if (string[1] == '-')
+         if (arg[1] == '-')
            force_assignment = 1;
 
          /* Until told differently, the old shell behaviour of
@@ -364,20 +544,19 @@ set_builtin (list)
            {
              change_flag ('x', '+');
              change_flag ('v', '+');
+             opts_changed = 1;
            }
 
          break;
        }
 
-      if ((on_or_off = *string) &&
-         (on_or_off == '-' || on_or_off == '+'))
+      if ((on_or_off = *arg) && (on_or_off == '-' || on_or_off == '+'))
        {
-         int i = 1;
-         while (flag_name = string[i++])
+         while (flag_name = *++arg)
            {
              if (flag_name == '?')
                {
-                 printf ("usage: %s\n", USAGE_STRING);
+                 builtin_usage ();
                  return (EXECUTION_SUCCESS);
                }
              else if (flag_name == 'o') /* -+o option-name */
@@ -387,36 +566,47 @@ set_builtin (list)
 
                  opt = list->next;
 
-                 if (!opt)
+                 if (opt == 0)
                    {
-                     list_minus_o_opts ();
+                     if (on_or_off == '-')
+                       list_minus_o_opts (-1);
+                     else
+                       minus_o_option_commands ();
                      continue;
                    }
 
                  option_name = opt->word->word;
 
-                 if (!option_name || !*option_name || (*option_name == '-'))
+                 if (option_name == 0 || *option_name == '\0' ||
+                     *option_name == '-' || *option_name == '+')
                    {
-                     list_minus_o_opts ();
+                     if (on_or_off == '-')
+                       list_minus_o_opts (-1);
+                     else
+                       minus_o_option_commands ();
                      continue;
                    }
                  list = list->next; /* Skip over option name. */
 
+                 opts_changed = 1;
                  if (set_minus_o_option (on_or_off, option_name) != EXECUTION_SUCCESS)
-                   return (EXECUTION_FAILURE);
-               }
-             else
-               {
-                 if (change_flag (flag_name, on_or_off) == FLAG_ERROR)
                    {
-                     char opt[3];
-                     opt[0] = on_or_off;
-                     opt[1] = flag_name;
-                     opt[2] = '\0';
-                     bad_option (opt);
+                     set_shellopts ();
                      return (EXECUTION_FAILURE);
                    }
                }
+             else if (change_flag (flag_name, on_or_off) == FLAG_ERROR)
+               {
+                 char opt[3];
+                 opt[0] = on_or_off;
+                 opt[1] = flag_name;
+                 opt[2] = '\0';
+                 bad_option (opt);
+                 builtin_usage ();
+                 set_shellopts ();
+                 return (EXECUTION_FAILURE);
+               }
+             opts_changed = 1;
            }
        }
       else
@@ -429,6 +619,9 @@ set_builtin (list)
   /* 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);
 }
 
@@ -443,13 +636,17 @@ function.  Some variables (such as PATH and IFS) 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 = 0, unset_variable = 0, opt;
-  int any_failed = 0;
+  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)
     {
@@ -462,7 +659,8 @@ unset_builtin (list)
          unset_variable = 1;
          break;
        default:
-         return (EXECUTION_FAILURE);
+         builtin_usage ();
+         return (EX_USAGE);
        }
     }
 
@@ -476,53 +674,69 @@ unset_builtin (list)
 
   while (list)
     {
+      SHELL_VAR *var;
+      int tem;
+#if defined (ARRAY_VARS)
+      char *t;
+#endif
+
       name = list->word->word;
 
-      if (!unset_function &&
-         find_name_in_list (name, non_unsettable_vars) > -1)
+#if defined (ARRAY_VARS)
+      if (!unset_function && valid_array_reference (name))
        {
-         builtin_error ("%s: cannot unset", name);
-         any_failed++;
+         t = strchr (name, '[');
+         *t++ = '\0';
+         unset_array++;
        }
-      else
+#endif
+
+      var = unset_function ? find_function (name) : find_variable (name);
+
+      if (var && !unset_function && non_unsettable_p (var))
        {
-         SHELL_VAR *var;
-         int tem;
+         builtin_error ("%s: cannot unset", name);
+         NEXT_VARIABLE ();
+       }
 
-         var = unset_function ? find_function (name) : find_variable (name);
+      /* 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 ();
+       }
 
-         /* Posix.2 says that unsetting readonly variables is an error. */
-         if (var && readonly_p (var))
+      /* 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: cannot unset: readonly %s",
-                            name, unset_function ? "function" : "variable");
-             any_failed++;
-             list = list->next;
-             continue;
+             builtin_error ("%s: not an array variable", name);
+             NEXT_VARIABLE ();
            }
-
-         /* Unless the -f option is supplied, the name refers to a
-            variable. */
-         tem = makunbound
-           (name, unset_function ? shell_functions : shell_variables);
-
-         /* 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 = makunbound (name, shell_functions);
-
-         if (tem == -1)
-           any_failed++;
-         else if (!unset_function)
-           stupidly_hack_special_variables (name);
+         else
+           tem = unbind_array_element (var, t);
        }
+      else
+#endif /* ARRAY_VARS */
+      tem = makunbound (name, unset_function ? shell_functions : shell_variables);
+
+      /* 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 = makunbound (name, shell_functions);
+
+      if (tem == -1)
+       any_failed++;
+      else if (!unset_function)
+       stupidly_hack_special_variables (name);
+
       list = list->next;
     }
 
-  if (any_failed)
-    return (EXECUTION_FAILURE);
-  else
-    return (EXECUTION_SUCCESS);
+  return (any_failed ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
 }