Imported from ../bash-2.0.tar.gz.
[platform/upstream/bash.git] / builtins / common.c
index ff940b5..2936b88 100644 (file)
    with Bash; see the file COPYING.  If not, write to the Free Software
    Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
 
+#include <config.h>
+
+#if defined (HAVE_UNISTD_H)
+#  include <unistd.h>
+#endif
+
 #include <stdio.h>
 #include <sys/types.h>
 #include "../posixstat.h"
-#if defined (HAVE_VFPRINTF)
-#include <varargs.h>
-#endif /* VFPRINTF */
+#include <signal.h>
+
+#if defined (PREFER_STDARG)
+#  include <stdarg.h>
+#else
+#  if defined (PREFER_VARARGS)
+#    include <varargs.h>
+#  endif
+#endif
 
-#if defined (HAVE_STRING_H)
-#  include <string.h>
-#else /* !HAVE_STRING_H */
-#  include <strings.h>
-#endif /* !HAVE_STRING_H */
+#include "../bashansi.h"
 
 #include "../shell.h"
-#include "../unwind_prot.h"
 #include "../maxpath.h"
+#include "../flags.h"
 #include "../jobs.h"
 #include "../builtins.h"
 #include "../input.h"
 #include "../execute_cmd.h"
+#include "../trap.h"
 #include "hashcom.h"
+#include "bashgetopt.h"
 #include "common.h"
 #include <tilde/tilde.h>
 
 #endif
 
 extern int no_symbolic_links, interactive, interactive_shell;
-extern int indirection_level, startup_state;
+extern int indirection_level, startup_state, subshell_environment;
+extern int line_number;
 extern int last_command_exit_value;
-extern int hashing_disabled;
+extern int running_trap;
+extern int hashing_enabled;
 extern int variable_context;
+extern int posixly_correct;
 extern char *this_command_name, *shell_name;
 extern COMMAND *global_command;
 extern HASH_TABLE *hashed_filenames;
+extern char *bash_getcwd_errstr;
 
-/* Read a numeric arg for this_command_name, the name of the shell builtin
-   that wants it.  LIST is the word list that the arg is to come from. */
-int
-get_numeric_arg (list)
-     WORD_LIST *list;
-{
-  int count = 1;
-
-  if (list)
-    {
-      register char *arg;
-      int sign = 1;
-
-      arg = list->word->word;
-      if (!arg)
-       goto bad_number;
-
-      /* Skip optional leading white space. */
-      while (whitespace (*arg))
-       arg++;
-
-      if (!*arg)
-        goto bad_number;
-
-      /* We allow leading `-' or `+'. */
-      if (*arg == '-' || *arg == '+')
-       {
-         if (!digit (arg[1]))
-           goto bad_number;
-
-         if (*arg == '-')
-           sign = -1;
-
-         arg++;
-       }
-
-      for (count = 0; digit (*arg); arg++)
-       count = (count * 10) + digit_value (*arg);
-
-      /* Skip trailing whitespace, if any. */
-      while (whitespace (*arg))
-        arg++;
-
-      if (!*arg)
-        count = count * sign;
-      else
-       {
-  bad_number:
-         builtin_error ("bad non-numeric arg `%s'", list->word->word);
-         throw_to_top_level ();
-       }
-      no_args (list->next);
-    }
-  return (count);
-}
+/* **************************************************************** */
+/*                                                                 */
+/*          Error reporting, usage, and option processing          */
+/*                                                                 */
+/* **************************************************************** */
 
 /* This is a lot like report_error (), but it is for shell builtins
    instead of shell control structures, and it won't ever exit the
    shell. */
-#if defined (HAVE_VFPRINTF)
+#if defined (USE_VARARGS)
 void
-builtin_error (va_alist)
+#if defined (PREFER_STDARG)
+builtin_error (const char *format, ...)
+#else
+builtin_error (format, va_alist)
+     const char *format;
      va_dcl
+#endif
 {
-  char *format;
   va_list args;
+  char *name;
+
+  name = get_name_for_error ();
+  fprintf (stderr, "%s: ", name);
 
   if (this_command_name && *this_command_name)
     fprintf (stderr, "%s: ", this_command_name);
 
+#if defined (PREFER_STDARG)
+  va_start (args, format);
+#else
   va_start (args);
-  format = va_arg (args, char *);
+#endif
+
   vfprintf (stderr, format, args);
   va_end (args);
   fprintf (stderr, "\n");
 }
-#else /* !HAVE_VFPRINTF */
+#else /* !USE_VARARGS */
 void
 builtin_error (format, arg1, arg2, arg3, arg4, arg5)
      char *format, *arg1, *arg2, *arg3, *arg4, *arg5;
@@ -141,7 +117,75 @@ builtin_error (format, arg1, arg2, arg3, arg4, arg5)
   fprintf (stderr, "\n");
   fflush (stderr);
 }
-#endif /* !HAVE_VFPRINTF */
+#endif /* !USE_VARARGS */
+
+/* Print a usage summary for the currently-executing builtin command. */
+void
+builtin_usage ()
+{
+  if (this_command_name && *this_command_name)
+    fprintf (stderr, "%s: usage: ", this_command_name);
+  fprintf (stderr, "%s\n", current_builtin->short_doc);
+  fflush (stderr);
+}
+
+/* Return if LIST is NULL else barf and jump to top_level.  Used by some
+   builtins that do not accept arguments. */
+void
+no_args (list)
+     WORD_LIST *list;
+{
+  if (list)
+    {
+      builtin_error ("too many arguments");
+      jump_to_top_level (DISCARD);
+    }
+}
+
+/* Function called when one of the builtin commands detects a bad
+   option. */
+void
+bad_option (s)
+     char *s;
+{
+  builtin_error ("unknown option: %s", s);
+}
+
+/* Check that no options were given to the currently-executing builtin,
+   and return 0 if there were options. */
+int
+no_options (list)
+     WORD_LIST *list;
+{
+  reset_internal_getopt ();
+  if (internal_getopt (list, "") != -1)
+    {
+      builtin_usage ();
+      return (1);
+    }
+  return (0);
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*          Shell positional parameter manipulation                */
+/*                                                                 */
+/* **************************************************************** */
+
+/* Convert a WORD_LIST into a C-style argv.  Return the number of elements
+   in the list in *IP, if IP is non-null.  A convenience function for
+   loadable builtins; also used by `test'. */
+char **
+make_builtin_argv (list, ip)
+     WORD_LIST *list;
+     int *ip;
+{
+  char **argv;
+
+  argv = word_list_to_argv (list, 0, 1, ip);
+  argv[0] = this_command_name;
+  return argv;
+}
 
 /* Remember LIST in $0 ... $9, and REST_OF_ARGS.  If DESTRUCTIVE is
    non-zero, then discard whatever the existing arguments are, else
@@ -155,7 +199,7 @@ remember_args (list, destructive)
 
   for (i = 1; i < 10; i++)
     {
-      if (destructive && dollar_vars[i])
+      if ((destructive || list) && dollar_vars[i])
        {
          free (dollar_vars[i]);
          dollar_vars[i] = (char *)NULL;
@@ -163,9 +207,6 @@ remember_args (list, destructive)
 
       if (list)
        {
-         if (!destructive && dollar_vars[i])
-           free (dollar_vars[i]);
-
          dollar_vars[i] = savestring (list->word->word);
          list = list->next;
        }
@@ -184,142 +225,15 @@ remember_args (list, destructive)
     set_dollar_vars_changed ();
 }
 
-/* Return if LIST is NULL else barf and jump to top_level. */
-void
-no_args (list)
-     WORD_LIST *list;
-{
-  if (list)
-    {
-      builtin_error ("extra arguments");
-      longjmp (top_level, DISCARD);
-    }
-}
-
-/* Return the octal number parsed from STRING, or -1 to indicate
-   that the string contained a bad number. */
-int
-read_octal (string)
-     char *string;
-{
-  int result = 0;
-  int digits = 0;
-
-  while (*string && *string >= '0' && *string < '8')
-    {
-      digits++;
-      result = (result * 8) + *string++ - '0';
-    }
-
-  if (!digits || result > 0777 || *string)
-    result = -1;
-
-  return (result);
-}
-
-/* Temporary static. */
-static char *dotted_filename = (char *)NULL;
-
-/* Return the full pathname that FILENAME hashes to.  If FILENAME
-   is hashed, but data->check_dot is non-zero, check ./FILENAME
-   and return that if it is executable. */
-char *
-find_hashed_filename (filename)
-     char *filename;
-{
-  register BUCKET_CONTENTS *item;
-
-  if (hashing_disabled)
-    return ((char *)NULL);
-
-  item = find_hash_item (filename, hashed_filenames);
-
-  if (item)
-    {
-      /* If this filename is hashed, but `.' comes before it in the path,
-        then see if `./filename' is an executable. */
-      if (pathdata(item)->check_dot)
-       {
-         if (dotted_filename)
-           free (dotted_filename);
-
-         dotted_filename = (char *)xmalloc (3 + strlen (filename));
-         strcpy (dotted_filename, "./");
-         strcat (dotted_filename, filename);
-
-         if (executable_file (dotted_filename))
-           return (dotted_filename);
-
-         /* Watch out.  If this file was hashed to "./filename", and
-            "./filename" is not executable, then return NULL. */
-
-         /* Since we already know "./filename" is not executable, what
-            we're really interested in is whether or not the `path'
-            portion of the hashed filename is equivalent to the current
-            directory, but only if it starts with a `.'.  (This catches
-            ./. and so on.)  same_file () is in execute_cmd.c; it tests
-            general Unix file equivalence -- same device and inode. */
-         {
-           char *path = pathdata (item)->path;
-
-           if (*path == '.')
-             {
-               int same = 0;
-               char *tail;
-
-               tail = (char *) strrchr (path, '/');
-
-               if (tail)
-                 {
-                   *tail = '\0';
-                   same = same_file
-                     (".", path, (struct stat *)NULL, (struct stat *)NULL);
-                   *tail = '/';
-                 }
-               if (same)
-                 return ((char *)NULL);
-             }
-         }
-       }
-      return (pathdata (item)->path);
-    }
-  else
-    return ((char *)NULL);
-}
-
-/* Remove FILENAME from the table of hashed commands. */
-void
-remove_hashed_filename (filename)
-     char *filename;
-{
-  register BUCKET_CONTENTS *item;
-
-  if (hashing_disabled)
-    return;
-
-  item = remove_hash_item (filename, hashed_filenames);
-  if (item)
-    {
-      if (item->data)
-        {
-         free (pathdata(item)->path);
-         free (item->data);
-        }
-      if (item->key)
-       free (item->key);
-      free (item);
-    }
-}
-
 /* **************************************************************** */
 /*                                                                 */
-/*                 Pushing and Popping a Context                   */
+/*              Pushing and Popping variable contexts              */
 /*                                                                 */
 /* **************************************************************** */
 
 static WORD_LIST **dollar_arg_stack = (WORD_LIST **)NULL;
-static int dollar_arg_stack_slots = 0;
-static int dollar_arg_stack_index = 0;
+static int dollar_arg_stack_slots;
+static int dollar_arg_stack_index;
 
 void
 push_context ()
@@ -346,15 +260,15 @@ push_dollar_vars ()
        xrealloc (dollar_arg_stack, (dollar_arg_stack_slots += 10)
                  * sizeof (WORD_LIST **));
     }
-  dollar_arg_stack[dollar_arg_stack_index] = list_rest_of_args ();
-  dollar_arg_stack[++dollar_arg_stack_index] = (WORD_LIST *)NULL;
+  dollar_arg_stack[dollar_arg_stack_index++] = list_rest_of_args ();
+  dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
 }
 
 /* Restore the positional parameters from our stack. */
 void
 pop_dollar_vars ()
 {
-  if (!dollar_arg_stack || !dollar_arg_stack_index)
+  if (!dollar_arg_stack || dollar_arg_stack_index == 0)
     return;
 
   remember_args (dollar_arg_stack[--dollar_arg_stack_index], 1);
@@ -365,17 +279,18 @@ pop_dollar_vars ()
 void
 dispose_saved_dollar_vars ()
 {
-  if (!dollar_arg_stack || !dollar_arg_stack_index)
+  if (!dollar_arg_stack || dollar_arg_stack_index == 0)
     return;
 
   dispose_words (dollar_arg_stack[dollar_arg_stack_index]);
   dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
 }
 
-static int changed_dollar_vars = 0;
+static int changed_dollar_vars;
 
 /* Have the dollar variables been reset to new values since we last
    checked? */
+int
 dollar_vars_changed ()
 {
   return (changed_dollar_vars);
@@ -390,18 +305,141 @@ set_dollar_vars_unchanged ()
 void
 set_dollar_vars_changed ()
 {
-  changed_dollar_vars  = 1;
+  changed_dollar_vars = 1;
 }
 
-/* Function called when one of the builtin commands detects a bad
-   option. */
-void
-bad_option (s)
-     char *s;
+/* **************************************************************** */
+/*                                                                 */
+/*             Validating numeric input and arguments              */
+/*                                                                 */
+/* **************************************************************** */
+
+/* Read a numeric arg for this_command_name, the name of the shell builtin
+   that wants it.  LIST is the word list that the arg is to come from.
+   Accept only the numeric argument; report an error if other arguments
+   follow. */
+int
+get_numeric_arg (list)
+     WORD_LIST *list;
 {
-  builtin_error ("unknown option: %s", s);
+  long count = 1;
+
+  if (list)
+    {
+      register char *arg;
+
+      arg = list->word->word;
+      if (!arg || (legal_number (arg, &count) == 0))
+       {
+         builtin_error ("bad non-numeric arg `%s'", list->word->word);
+         throw_to_top_level ();
+       }
+      no_args (list->next);
+    }
+  return (count);
+}
+
+/* Return the octal number parsed from STRING, or -1 to indicate
+   that the string contained a bad number. */
+int
+read_octal (string)
+     char *string;
+{
+  int result, digits;
+
+  result = digits = 0;
+  while (*string && *string >= '0' && *string < '8')
+    {
+      digits++;
+      result = (result * 8) + *string++ - '0';
+    }
+
+  if (!digits || result > 0777 || *string)
+    result = -1;
+
+  return (result);
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*                     Command name hashing                        */
+/*                                                                 */
+/* **************************************************************** */
+
+/* Return the full pathname that FILENAME hashes to.  If FILENAME
+   is hashed, but (data->flags & HASH_CHKDOT) is non-zero, check
+   ./FILENAME and return that if it is executable. */
+char *
+find_hashed_filename (filename)
+     char *filename;
+{
+  register BUCKET_CONTENTS *item;
+  char *path, *dotted_filename, *tail;
+  int same;
+
+  if (hashing_enabled == 0)
+    return ((char *)NULL);
+
+  item = find_hash_item (filename, hashed_filenames);
+
+  if (item == NULL)
+    return ((char *)NULL);
+
+  /* If this filename is hashed, but `.' comes before it in the path,
+     see if ./filename is executable.  If the hashed value is not an
+     absolute pathname, see if ./`hashed-value' exists. */
+  path = pathdata(item)->path;
+  if (pathdata(item)->flags & (HASH_CHKDOT|HASH_RELPATH))
+    {
+      tail = (pathdata(item)->flags & HASH_RELPATH) ? path : filename;
+      dotted_filename = xmalloc (3 + strlen (tail));
+      dotted_filename[0] = '.'; dotted_filename[1] = '/';
+      strcpy (dotted_filename + 2, tail);
+
+      if (executable_file (dotted_filename))
+       return (dotted_filename);
+
+      free (dotted_filename);
+
+#if 0
+      if (pathdata(item)->flags & HASH_RELPATH)
+       return ((char *)NULL);
+#endif
+
+      /* Watch out.  If this file was hashed to "./filename", and
+        "./filename" is not executable, then return NULL. */
+
+      /* Since we already know "./filename" is not executable, what
+        we're really interested in is whether or not the `path'
+        portion of the hashed filename is equivalent to the current
+        directory, but only if it starts with a `.'.  (This catches
+        ./. and so on.)  same_file () tests general Unix file
+        equivalence -- same device and inode. */
+      if (*path == '.')
+       {
+         same = 0;
+         tail = (char *)strrchr (path, '/');
+
+         if (tail)
+           {
+             *tail = '\0';
+             same = same_file (".", path, (struct stat *)NULL, (struct stat *)NULL);
+             *tail = '/';
+           }
+
+         return same ? (char *)NULL : path;
+       }
+    }
+
+  return (path);
 }
 
+/* **************************************************************** */
+/*                                                                 */
+/*          Manipulating the current working directory             */
+/*                                                                 */
+/* **************************************************************** */
+
 /* Return a consed string which is the current working directory.
    FOR_WHOM is the name of the caller for error printing.  */
 char *the_current_working_directory = (char *)NULL;
@@ -410,6 +448,8 @@ char *
 get_working_directory (for_whom)
      char *for_whom;
 {
+  char *directory;
+
   if (no_symbolic_links)
     {
       if (the_current_working_directory)
@@ -418,21 +458,18 @@ get_working_directory (for_whom)
       the_current_working_directory = (char *)NULL;
     }
 
-  if (!the_current_working_directory)
+  if (the_current_working_directory == 0)
     {
-      char *directory;
-
-      the_current_working_directory = xmalloc (MAXPATHLEN);
-      directory = getwd (the_current_working_directory);
-      if (!directory)
+      the_current_working_directory = xmalloc (PATH_MAX);
+      the_current_working_directory[0] = '\0';
+      directory = getcwd (the_current_working_directory, PATH_MAX);
+      if (directory == 0)
        {
-         if (for_whom && *for_whom)
-           fprintf (stderr, "%s: ", for_whom);
-         else
-           fprintf (stderr, "%s: ", get_name_for_error ());
-
-         fprintf (stderr, "could not get current directory: %s\n",
-                  the_current_working_directory);
+         fprintf (stderr, "%s: could not get current directory: %s\n",
+                  (for_whom && *for_whom) ? for_whom : get_name_for_error (),
+                  the_current_working_directory[0]
+                       ? the_current_working_directory
+                       : bash_getcwd_errstr);
 
          free (the_current_working_directory);
          the_current_working_directory = (char *)NULL;
@@ -448,35 +485,43 @@ void
 set_working_directory (name)
      char *name;
 {
-  if (the_current_working_directory)
-    free (the_current_working_directory);
-
+  FREE (the_current_working_directory);
   the_current_working_directory = savestring (name);
 }
 
+/* **************************************************************** */
+/*                                                                 */
+/*             Job control support functions                       */
+/*                                                                 */
+/* **************************************************************** */
+
 #if defined (JOB_CONTROL)
 /* Return the job spec found in LIST. */
+int
 get_job_spec (list)
      WORD_LIST *list;
 {
   register char *word;
-  int job = NO_JOB;
-  int substring = 0;
+  int job, substring;
 
-  if (!list)
+  if (list == 0)
     return (current_job);
 
   word = list->word->word;
 
-  if (!*word)
+  if (*word == '\0')
     return (current_job);
 
   if (*word == '%')
     word++;
 
-  if (digit (*word) && (sscanf (word, "%d", &job) == 1))
-    return (job - 1);
+  if (digit (*word) && all_digits (word))
+    {
+      job = atoi (word);
+      return (job - 1);
+    }
 
+  substring = 0;
   switch (*word)
     {
     case 0:
@@ -490,21 +535,24 @@ get_job_spec (list)
     case '?':                  /* Substring search requested. */
       substring++;
       word++;
-      goto find_string;
+      /* FALLTHROUGH */
 
     default:
-    find_string:
       {
-       register int i, wl = strlen (word);
+       register int i, wl;
+
+       job = NO_JOB;
+       wl = strlen (word);
        for (i = 0; i < job_slots; i++)
          {
            if (jobs[i])
              {
-               register PROCESS *p = jobs[i]->pipe;
+               register PROCESS *p;
+               p = jobs[i]->pipe;
                do
                  {
                    if ((substring && strindex (p->command, word)) ||
-                       (strncmp (p->command, word, wl) == 0))
+                       (STREQN (p->command, word, wl)))
                      if (job != NO_JOB)
                        {
                          builtin_error ("ambigious job spec: %s", word);
@@ -524,171 +572,100 @@ get_job_spec (list)
 }
 #endif /* JOB_CONTROL */
 
-int parse_and_execute_level = 0;
-
-/* How to force parse_and_execute () to clean up after itself. */
-void
-parse_and_execute_cleanup ()
-{
-  run_unwind_frame ("parse_and_execute_top");
-}
-
-/* Parse and execute the commands in STRING.  Returns whatever
-   execute_command () returns.  This frees STRING.  INTERACT is
-   the new value for `interactive' while the commands are being
-   executed.  A value of -1 means don't change it. */
 int
-parse_and_execute (string, from_file, interact)
-     char *string;
-     char *from_file;
-     int interact;
+display_signal_list (list, forcecols)
+     WORD_LIST *list;
+     int forcecols;
 {
-  int last_result = EXECUTION_SUCCESS;
-  int code = 0, jump_to_top_level = 0;
-  char *orig_string = string;
-
-  /* Unwind protect this invocation of parse_and_execute (). */
-  begin_unwind_frame ("parse_and_execute_top");
-  unwind_protect_int (parse_and_execute_level);
-  unwind_protect_jmp_buf (top_level);
-  unwind_protect_int (indirection_level);
-  if (interact != -1 && interactive != interact)
-    unwind_protect_int (interactive);
+  register int i, column;
+  char *name;
+  int result;
+  long signum;
 
-#if defined (HISTORY)
-  if (interactive_shell)
+  result = EXECUTION_SUCCESS;
+  if (!list)
     {
-      unwind_protect_int (remember_on_history);
-#  if defined (BANG_HISTORY)
-      unwind_protect_int (history_expansion_inhibited);
-#  endif /* BANG_HISTORY */
-    }
-#endif /* HISTORY */
-
-  add_unwind_protect (pop_stream, (char *)NULL);
-  if (orig_string)
-    add_unwind_protect (xfree, orig_string);
-  end_unwind_frame ();
-
-  parse_and_execute_level++;
-  push_stream ();
-  indirection_level++;
-  if (interact != -1)
-    interactive = interact;
-
-#if defined (HISTORY)
-  /* We don't remember text read by the shell this way on
-     the history list, and we don't use !$ in shell scripts. */
-  remember_on_history = 0;
-#  if defined (BANG_HISTORY)
-  history_expansion_inhibited = 1;
-#  endif /* BANG_HISTORY */
-#endif /* HISTORY */
-
-  with_input_from_string (string, from_file);
-  {
-    COMMAND *command;
-
-    while (*(bash_input.location.string))
-      {
-       if (interrupt_state)
-         {
-           last_result = EXECUTION_FAILURE;
-           break;
-         }
-
-       /* Provide a location for functions which `longjmp (top_level)' to
-          jump to.  This prevents errors in substitution from restarting
-          the reader loop directly, for example. */
-       code = setjmp (top_level);
-
-       if (code)
-         {
-           jump_to_top_level = 0;
-           switch (code)
-             {
-             case FORCE_EOF:
-             case EXITPROG:
-               run_unwind_frame ("pe_dispose");
-               /* Remember to call longjmp (top_level) after the old
-                  value for it is restored. */
-               jump_to_top_level = 1;
-               goto out;
-
-             case DISCARD:
-               dispose_command (command);
-               run_unwind_frame ("pe_dispose");
-               last_command_exit_value = 1;
-               continue;
-
-             default:
-               programming_error ("bad jump to top_level: %d", code);
-               break;
-             }
-         }
-         
-       if (parse_command () == 0)
-         {
-           if ((command = global_command) != (COMMAND *)NULL)
-             {
-               struct fd_bitmap *bitmap;
-
-               bitmap = new_fd_bitmap (FD_BITMAP_SIZE);
-               begin_unwind_frame ("pe_dispose");
-               add_unwind_protect (dispose_fd_bitmap, bitmap);
-
-               global_command = (COMMAND *)NULL;
-
-#if defined (ONESHOT)
-               if (startup_state == 2 && *bash_input.location.string == '\0' &&
-                   command->type == cm_simple && !command->redirects &&
-                   !command->value.Simple->redirects)
-                 {
-                   command->flags |= CMD_NO_FORK;
-                   command->value.Simple->flags |= CMD_NO_FORK;
-                 }
-#endif /* ONESHOT */
-    
-               last_result = execute_command_internal
-                   (command, 0, NO_PIPE, NO_PIPE, bitmap);
-
-               dispose_command (command);
-               run_unwind_frame ("pe_dispose");
-             }
-         }
-       else
-         {
-           last_result = EXECUTION_FAILURE;
-
-           /* Since we are shell compatible, syntax errors in a script
-              abort the execution of the script.  Right? */
-           break;
-         }
-      }
-  }
+      for (i = 1, column = 0; i < NSIG; i++)
+       {
+         name = signal_name (i);
+         if (STREQN (name, "SIGJUNK", 7) || STREQN (name, "Unknown", 7))
+           continue;
 
- out:
+         if (posixly_correct && !forcecols)
+           printf ("%s%s", name, (i == NSIG - 1) ? "" : " ");
+         else
+           {
+             printf ("%2d) %s", i, name);
+
+             if (++column < 4)
+               printf ("\t");
+             else
+               {
+                 printf ("\n");
+                 column = 0;
+               }
+           }
+       }
 
-  run_unwind_frame ("parse_and_execute_top");
+      if ((posixly_correct && !forcecols) || column != 0)
+       printf ("\n");
+      return result;
+    }
 
-  if (interrupt_state && parse_and_execute_level == 0)
+  /* List individual signal names or numbers. */
+  while (list)
     {
-      /* An interrupt during non-interactive execution in an
-         interactive shell (e.g. via $PROMPT_COMMAND) should
-         not cause the shell to exit. */
-      interactive = interactive_shell;
-      throw_to_top_level ();
+      if (legal_number (list->word->word, &signum))
+       {
+         /* This is specified by Posix.2 so that exit statuses can be
+            mapped into signal numbers. */
+         if (signum > 128)
+           signum -= 128;
+         if (signum < 0 || signum >= NSIG)
+           {
+             builtin_error ("bad signal number: %s", list->word->word);
+             result = EXECUTION_FAILURE;
+             list = list->next;
+             continue;
+           }
+
+         name = signal_name (signum);
+         if (STREQN (name, "SIGJUNK", 7) || STREQN (name, "Unknown", 7))
+           {
+             list = list->next;
+             continue;
+           }
+         printf ("%s\n", name);
+       }
+      else
+       {
+         signum = decode_signal (list->word->word);
+         if (signum == NO_SIG)
+           {
+             builtin_error ("%s: not a signal specification", list->word->word);
+             result = EXECUTION_FAILURE;
+             list = list->next;
+             continue;
+           }
+         printf ("%ld\n", signum);
+       }
+      list = list->next;
     }
-
-  if (jump_to_top_level)
-    longjmp (top_level, code);
-
-  return (last_result);
+  return (result);
 }
 
-/* Return the address of the builtin named NAME.
+/* **************************************************************** */
+/*                                                                 */
+/*         Finding builtin commands and their functions            */
+/*                                                                 */
+/* **************************************************************** */
+
+/* Perform a binary search and return the address of the builtin function
+   whose name is NAME.  If the function couldn't be found, or the builtin
+   is disabled or has no function associated with it, return NULL.
+   Return the address of the builtin.
    DISABLED_OKAY means find it even if the builtin is disabled. */
-static Function *
+struct builtin *
 builtin_address_internal (name, disabled_okay)
      char *name;
      int disabled_okay;
@@ -710,40 +687,53 @@ builtin_address_internal (name, disabled_okay)
       if (j == 0)
        {
          /* It must have a function pointer.  It must be enabled, or we
-            must have explicitly allowed disabled functions to be found. */
+            must have explicitly allowed disabled functions to be found,
+            and it must not have been deleted. */
          if (shell_builtins[mid].function &&
+             ((shell_builtins[mid].flags & BUILTIN_DELETED) == 0) &&
              ((shell_builtins[mid].flags & BUILTIN_ENABLED) || disabled_okay))
-           return (shell_builtins[mid].function);
+           return (&shell_builtins[mid]);
          else
-           return ((Function *)NULL);
+           return ((struct builtin *)NULL);
        }
       if (j > 0)
        hi = mid - 1;
       else
        lo = mid + 1;
     }
-  return ((Function *)NULL);
+  return ((struct builtin *)NULL);
 }
 
-/* Perform a binary search and return the address of the builtin function
-   whose name is NAME.  If the function couldn't be found, or the builtin
-   is disabled or has no function associated with it, return NULL. */
+/* Return the pointer to the function implementing builtin command NAME. */
 Function *
 find_shell_builtin (name)
-       char *name;
+     char *name;
 {
-  return (builtin_address_internal (name, 0));
+  current_builtin = builtin_address_internal (name, 0);
+  return (current_builtin ? current_builtin->function : (Function *)NULL);
 }
 
-/* Return the address of builtin with NAME, irregardless of its state of
-   enableness. */
+/* Return the address of builtin with NAME, whether it is enabled or not. */
 Function *
 builtin_address (name)
      char *name;
 {
-  return (builtin_address_internal (name, 1));
+  current_builtin = builtin_address_internal (name, 1);
+  return (current_builtin ? current_builtin->function : (Function *)NULL);
 }
 
+/* Return the function implementing the builtin NAME, but only if it is a
+   POSIX.2 special builtin. */
+Function *
+find_special_builtin (name)
+     char *name;
+{
+  current_builtin = builtin_address_internal (name, 0);
+  return ((current_builtin && (current_builtin->flags & SPECIAL_BUILTIN)) ?
+                       current_builtin->function :
+                       (Function *)NULL);
+}
+  
 static int
 shell_builtin_compare (sbp1, sbp2)
      struct builtin *sbp1, *sbp2;
@@ -765,49 +755,56 @@ initialize_shell_builtins ()
     shell_builtin_compare);
 }
 
-/* Return a new string which is the quoted version of STRING.  This is used
-   by alias and trap. */
+/* **************************************************************** */
+/*                                                                 */
+/*      Functions for quoting strings to be re-read as input       */
+/*                                                                 */
+/* **************************************************************** */
+
+/* Return a new string which is the single-quoted version of STRING.
+   Used by alias and trap, among others. */
 char *
 single_quote (string)
      char *string;
 {
-  register int i, j, c;
-  char *result;
-
-  result = (char *)xmalloc (3 + (3 * strlen (string)));
+  register int c;
+  char *result, *r, *s;
 
-  result[0] = '\'';
+  result = (char *)xmalloc (3 + (4 * strlen (string)));
+  r = result;
+  *r++ = '\'';
 
-  for (i = 0, j = 1; string && (c = string[i]); i++)
+  for (s = string; s && (c = *s); s++)
     {
-      result[j++] = c;
+      *r++ = c;
 
       if (c == '\'')
        {
-         result[j++] = '\\';   /* insert escaped single quote */
-         result[j++] = '\'';
-         result[j++] = '\'';   /* start new quoted string */
+         *r++ = '\\';  /* insert escaped single quote */
+         *r++ = '\'';
+         *r++ = '\'';  /* start new quoted string */
        }
     }
 
-  result[j++] = '\'';
-  result[j] = '\0';
+  *r++ = '\'';
+  *r = '\0';
 
   return (result);
 }
 
+/* Quote STRING using double quotes.  Return a new string. */
 char *
 double_quote (string)
      char *string;
 {
-  register int i, j, c;
-  char *result;
-
-  result = (char *)xmalloc (3 + (3 * strlen (string)));
+  register int c;
+  char *result, *r, *s;
 
-  result[0] = '"';
+  result = (char *)xmalloc (3 + (2 * strlen (string)));
+  r = result;
+  *r++ = '"';
 
-  for (i = 0, j = 1; string && (c = string[i]); i++)
+  for (s = string; s && (c = *s); s++)
     {
       switch (c)
         {
@@ -815,15 +812,86 @@ double_quote (string)
        case '$':
        case '`':
        case '\\':
-         result[j++] = '\\';
+         *r++ = '\\';
        default:
-         result[j++] = c;
+         *r++ = c;
          break;
         }
     }
 
-  result[j++] = '"';
-  result[j] = '\0';
+  *r++ = '"';
+  *r = '\0';
 
   return (result);
 }
+
+/* Quote special characters in STRING using backslashes.  Return a new
+   string. */
+char *
+backslash_quote (string)
+     char *string;
+{
+  int c;
+  char *result, *r, *s;
+
+  result = xmalloc (2 * strlen (string) + 1);
+
+  for (r = result, s = string; s && (c = *s); s++)
+    {
+      switch (c)
+       {
+       case ' ': case '\t': case '\n':         /* IFS white space */
+       case '\'': case '"': case '\\':         /* quoting chars */
+       case '|': case '&': case ';':           /* shell metacharacters */
+       case '(': case ')': case '<': case '>':
+       case '!': case '{': case '}':           /* reserved words */
+       case '*': case '[': case '?': case ']': /* globbing chars */
+       case '^':
+       case '$': case '`':                     /* expansion chars */
+         *r++ = '\\';
+         *r++ = c;
+         break;
+       case '#':                               /* comment char */
+         if (s == string)
+           *r++ = '\\';
+         /* FALLTHROUGH */
+       default:
+         *r++ = c;
+         break;
+       }
+    }
+
+  *r = '\0';
+  return (result);
+}
+
+int
+contains_shell_metas (string)
+     char *string;
+{
+  char *s;
+
+  for (s = string; s && *s; s++)
+    {
+      switch (*s)
+       {
+       case ' ': case '\t': case '\n':         /* IFS white space */
+       case '\'': case '"': case '\\':         /* quoting chars */
+       case '|': case '&': case ';':           /* shell metacharacters */
+       case '(': case ')': case '<': case '>':
+       case '!': case '{': case '}':           /* reserved words */
+       case '*': case '[': case '?': case ']': /* globbing chars */
+       case '^':
+       case '$': case '`':                     /* expansion chars */
+         return (1);
+       case '#':
+         if (s == string)                      /* comment char */
+           return (1);
+         /* FALLTHROUGH */
+       default:
+         break;
+       }
+    }
+
+  return (0);
+}