]> git.ipfire.org Git - thirdparty/bash.git/commitdiff
more ANSI/ISO C cleanups
authorChet Ramey <chet.ramey@case.edu>
Tue, 28 Mar 2023 20:34:56 +0000 (16:34 -0400)
committerChet Ramey <chet.ramey@case.edu>
Tue, 28 Mar 2023 20:34:56 +0000 (16:34 -0400)
38 files changed:
CWRU/CWRU.chlog
CWRU/old/PLATFORMS [new file with mode: 0644]
CWRU/old/execute_cmd.c.lastpipe-first [new file with mode: 0644]
aclocal.m4
array.c
array2.c
builtins/common.c
builtins/enable.def
builtins/gen-helpfiles.c
builtins/help.def
builtins/printf.def
config.h.in
configure
configure.ac
examples/loadables/TODO
examples/loadables/getconf.c
execute_cmd.c
externs.h
general.h
hashlib.h
include/ansi_stdlib.h
include/ocache.h
lib/glob/collsyms.h
lib/glob/glob.c
lib/readline/history.h
lib/readline/xmalloc.h
lib/sh/stringlist.c
lib/sh/stringvec.c
lib/sh/strtrans.c
lib/tilde/tilde.c
mksyntax.c
pcomplete.c
subst.c
support/endian.c
tests/history.right
tests/history7.sub
xmalloc.c
xmalloc.h

index 89194192ba3f9cfd4e5dfe07e0466a8400361a2e..44d61616de33f0d357321e92483372b3a34dd4ba 100644 (file)
@@ -5848,6 +5848,7 @@ support/printenv.c,support/signames.c,support/bashversion.c
 support/memtest.c,support/endian.c
 lib/glob/gm_loop.c
        - some cleanups for function prototypes and other stdc requirements
+         From Paul Eggert <eggert@cs.ucla.edu>
 
 lib/readline/doc/{rltech.texi,hstech.texi}
        - add prototypes and stdc requirements to the examples
@@ -5857,3 +5858,30 @@ builtins/complete.def
          filename completions are treated in case someone uses compgen in
          the foreground.
          From a report by Grisha Levit <grishalevit@gmail.com>
+
+                                  3/28
+                                  ----
+aclocal.m4
+builtins/enable.def,builtins/gen-helpfiles.c,builtins/help.def,builtins/printf.def
+general.h,hashlib.h
+include/ansi_stdlib.h,include/ocache.h
+lib/glob/collsyms.h,lib/glob/glob.c
+lib/readline/history.h,lib/readline/xmalloc.h
+xmalloc.c,xmalloc.h
+lib/sh/strtrans.c
+lib/tilde/tilde.c
+mksyntax.c
+support/endian.c
+       - don't bother with __STDC__ if we are just using C89 features like
+         const, function prototypes, etc.
+         From Paul Eggert <eggert@cs.ucla.edu>
+
+externs.h
+       - prototype the list functions that take GENERIC_LIST *
+         From Paul Eggert <eggert@cs.ucla.edu>
+
+builtins/common.c,examples/loadables/getconf.c
+execute_cmd.c,pcomplete.c,subst.c
+lib/sh/stringlist.c,lib/sh/stringvec.c
+       - changes and casts now that the generic list functions are prototyped
+         From Paul Eggert <eggert@cs.ucla.edu>
diff --git a/CWRU/old/PLATFORMS b/CWRU/old/PLATFORMS
new file mode 100644 (file)
index 0000000..f05caff
--- /dev/null
@@ -0,0 +1,31 @@
+The version of bash in this directory has been compiled on the
+following systems:
+
+By chet:
+
+SunOS 4.1.4
+SunOS 5.5
+BSDI BSD/OS 2.1
+FreeBSD 2.2
+NetBSD 1.2
+AIX 4.2
+AIX 4.1.4
+HP/UX 9.05, 10.01, 10.10, 10.20
+Linux 2.0.29 (libc 5.3.12)
+Linux 2.0.4 (libc 5.3.12)
+
+By other testers:
+
+SCO ODT 2.0
+SCO 3.2v5.0, 3.2v4.2
+SunOS 5.3
+SunOS 5.5
+BSD/OS 2.1
+FreeBSD 2.2
+SunOS 4.1.3
+Irix 5.3
+Irix 6.2
+Linux 2.0 (unknown distribution)
+Digital OSF/1 3.2
+GNU Hurd 0.1
+SVR4.2
diff --git a/CWRU/old/execute_cmd.c.lastpipe-first b/CWRU/old/execute_cmd.c.lastpipe-first
new file mode 100644 (file)
index 0000000..6b7b533
--- /dev/null
@@ -0,0 +1,3176 @@
+/* execute_command.c -- Execute a COMMAND structure. */
+
+/* Copyright (C) 1987,1991 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 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. */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include "posixstat.h"
+#include "filecntl.h"
+#include <signal.h>
+
+#if !defined (SIGABRT)
+#define SIGABRT SIGIOT
+#endif
+
+#include <sys/param.h>
+#include <errno.h>
+
+#include "shell.h"
+#include "y.tab.h"
+#include "flags.h"
+#include "hash.h"
+#include "jobs.h"
+
+#include "sysdefs.h"
+#include <glob/fnmatch.h>
+
+#if defined (BUFFERED_INPUT)
+#  include "input.h"
+#endif
+
+#define CMD_NO_WAIT    0x40    /* XXX move to command.h */
+#define CMD_LAST_PIPE  0x80    /* XXX ditto */
+
+#if !defined (errno)
+extern int errno;
+#endif
+
+extern int breaking, continuing, loop_level;
+extern int interactive, login_shell;
+
+#if defined (JOB_CONTROL)
+extern int job_control;
+extern int set_job_control ();
+#endif /* JOB_CONTROL */
+
+extern int getdtablesize ();
+extern int close ();
+extern char *strerror ();
+extern char *string_list ();
+
+#if defined (USG)
+extern pid_t last_made_pid;
+#endif
+
+struct stat SB;
+
+extern WORD_LIST *expand_words (), *expand_word ();
+extern WORD_LIST *expand_word_leave_quoted ();
+extern char *make_command_string ();
+
+extern Function *find_shell_builtin (), *builtin_address ();
+extern SigHandler *set_sigint_handler ();
+
+#if defined (PROCESS_SUBSTITUTION)
+void close_all_files ();
+#endif /* PROCESS_SUBSTITUTION */
+
+/* Static functions defined and used in this file. */
+static void close_pipes (), do_piping (), execute_disk_command ();
+static void execute_subshell_builtin_or_function ();
+static void cleanup_redirects (), cleanup_func_redirects (), bind_lastarg ();
+static void add_undo_close_redirect ();
+static int do_redirection_internal (), do_redirections ();
+static int expandable_redirection_filename (), execute_shell_script ();
+static int execute_builtin_or_function (), add_undo_redirect ();
+static char *find_user_command_internal (), *find_user_command_in_path ();
+
+/* The value returned by the last synchronous command. */
+int last_command_exit_value = 0;
+
+/* The list of redirections to preform which will undo the redirections
+   that I made in the shell. */
+REDIRECT *redirection_undo_list = (REDIRECT *)NULL;
+
+/* Have we just forked, and are we now running in a subshell environment? */
+int subshell_environment = 0;
+
+/* Use this as the function to call when adding unwind protects so we
+   don't need to know what free() returns. */
+void
+vfree (string)
+     char *string;
+{
+  free (string);
+}
+
+#define FD_BITMAP_DEFAULT_SIZE 32
+/* Functions to allocate and deallocate the structures used to pass
+   information from the shell to its children about file descriptors
+   to close. */
+struct fd_bitmap *
+new_fd_bitmap (size)
+     long size;
+{
+  struct fd_bitmap *ret;
+
+  ret = (struct fd_bitmap *)xmalloc (sizeof (struct fd_bitmap));
+
+  ret->size = size;
+
+  if (size)
+    {
+      ret->bitmap = (char *)xmalloc (size);
+      bzero (ret->bitmap, size);
+    }
+  else
+    ret->bitmap = (char *)NULL;
+  return (ret);
+}
+
+void
+dispose_fd_bitmap (fdbp)
+     struct fd_bitmap *fdbp;
+{
+  if (fdbp->bitmap)
+    free (fdbp->bitmap);
+
+  free (fdbp);
+}
+
+void
+close_fd_bitmap (fdbp)
+     struct fd_bitmap *fdbp;
+{
+  register int i;
+
+  if (fdbp)
+    {
+      for (i = 0; i < fdbp->size; i++)
+       if (fdbp->bitmap[i])
+         {
+           close (i);
+           fdbp->bitmap[i] = 0;
+         }
+    }
+}
+
+/* Execute the command passed in COMMAND.  COMMAND is exactly what
+   read_command () places into GLOBAL_COMMAND.  See "command.h" for the
+   details of the command structure.
+
+   EXECUTION_SUCCESS or EXECUTION_FAILURE are the only possible
+   return values.  Executing a command with nothing in it returns
+   EXECUTION_SUCCESS. */
+execute_command (command)
+     COMMAND *command;
+{
+  struct fd_bitmap *bitmap;
+  int result;
+
+  bitmap = new_fd_bitmap (FD_BITMAP_DEFAULT_SIZE);
+
+  /* Just do the command, but not asynchronously. */
+  result = execute_command_internal (command, 0, NO_PIPE, NO_PIPE, bitmap);
+
+  dispose_fd_bitmap (bitmap);
+
+#if defined (PROCESS_SUBSTITUTION)
+  unlink_fifo_list ();
+#endif /* PROCESS_SUBSTITUTION */
+
+  return (result);
+}
+
+/* Return 1 if TYPE is a shell control structure type. */
+int
+shell_control_structure (type)
+     enum command_type type;
+{
+  switch (type)
+    {
+    case cm_for:
+    case cm_case:
+    case cm_while:
+    case cm_until:
+    case cm_if:
+    case cm_group:
+      return (1);
+
+    default:
+      return (0);
+    }
+}
+
+/* A function to use to unwind_protect the redirection undo list
+   for loops. */
+static void
+cleanup_redirects (list)
+     REDIRECT *list;
+{
+  do_redirections (list, 1, 0, 0);
+  dispose_redirects (list);
+}
+
+/* Function to unwind_protect the redirections for functions and builtins. */
+static void
+cleanup_func_redirects (list)
+     REDIRECT *list;
+{
+  do_redirections (list, 1, 0, 0);
+}
+
+#if defined (JOB_CONTROL)
+/* A function to restore the signal mask to its proper value when the shell
+   is interrupted or errors occur while creating a pipeline. */
+static int
+restore_signal_mask (set)
+     sigset_t set;
+{
+  return (sigprocmask (SIG_SETMASK, &set, (sigset_t *)NULL));
+}
+#endif /* JOB_CONTROL */
+
+/* A debugging function that can be called from gdb, for instance. */
+open_files ()
+{
+  register int i;
+  int f, fd_table_size;
+
+  fd_table_size = getdtablesize ();
+
+  fprintf (stderr, "pid %d open files:", getpid ());
+  for (i = 3; i < fd_table_size; i++)
+    {
+      if ((f = fcntl (i, F_GETFD, 0)) != -1)
+       fprintf (stderr, " %d (%s)", i, f ? "close" : "open");
+    }
+  fprintf (stderr, "\n");
+}
+
+execute_command_internal (command, asynchronous, pipe_in, pipe_out, 
+                         fds_to_close)
+     COMMAND *command;
+     int asynchronous;
+     int pipe_in, pipe_out;
+     struct fd_bitmap *fds_to_close;
+{
+  int exec_result = EXECUTION_SUCCESS;
+  int invert, ignore_return;
+  REDIRECT *my_undo_list;
+
+  if (!command || breaking || continuing)
+    return (EXECUTION_SUCCESS);
+
+  run_pending_traps ();
+
+  invert = (command->flags & CMD_INVERT_RETURN) != 0;
+
+  /* If a command was being explicitly run in a subshell, or if it is
+     a shell control-structure, and it has a pipe, then we do the command
+     in a subshell. */
+
+  if ((command->flags & CMD_WANT_SUBSHELL) ||
+      (command->flags & CMD_FORCE_SUBSHELL)  ||
+      (shell_control_structure (command->type) &&
+       (pipe_out != NO_PIPE || pipe_in != NO_PIPE || asynchronous)))
+    {
+      pid_t paren_pid;
+
+      /* Fork a subshell, turn off the subshell bit, turn off job
+        control and call execute_command () on the command again. */
+      paren_pid = make_child (savestring (make_command_string (command)),
+                             asynchronous);
+      if (paren_pid == 0)
+       {
+         int user_subshell, return_code;
+
+#if defined (JOB_CONTROL)
+         set_sigchld_handler ();
+#endif /* JOB_CONTROL */
+
+         set_sigint_handler ();
+
+         user_subshell = (command->flags & CMD_WANT_SUBSHELL) != 0;
+         command->flags &= ~(CMD_FORCE_SUBSHELL | CMD_WANT_SUBSHELL);
+
+         /* If a command is asynchronous in a subshell (like ( foo ) & or
+            the special case of an asynchronous GROUP command where the
+            the subshell bit is turned on down in case cm_group: below), 
+            turn off `asynchronous', so that two subshells aren't spawned.
+
+            This seems semantically correct to me.  For example, 
+            ( foo ) & seems to say ``do the command `foo' in a subshell
+            environment, but don't wait for that subshell to finish'',
+            and "{ foo ; bar } &" seems to me to be like functions or
+            builtins in the background, which executed in a subshell
+            environment.  I just don't see the need to fork two subshells. */
+
+         /* Don't fork again, we are already in a subshell. */
+         asynchronous = 0;
+
+         /* Subshells are neither login nor interactive. */
+         login_shell = interactive = 0;
+
+         subshell_environment = 1;
+
+#if defined (JOB_CONTROL)
+         /* Delete all traces that there were any jobs running.  This is
+            only for subshells. */
+         without_job_control ();
+#endif /* JOB_CONTROL */
+         do_piping (pipe_in, pipe_out);
+
+         if (fds_to_close)
+           close_fd_bitmap (fds_to_close);
+
+         /* Do redirections, then dispose of them before recursive call. */
+         if (command->redirects)
+           {
+             if (do_redirections (command->redirects, 1, 0, 0) != 0)
+               exit (EXECUTION_FAILURE);
+
+             dispose_redirects (command->redirects);
+             command->redirects = (REDIRECT *)NULL;
+           }
+
+         return_code = execute_command_internal
+           (command, asynchronous, NO_PIPE, NO_PIPE, fds_to_close);
+
+         /* If we were explicitly placed in a subshell with (), we need
+            to do the `shell cleanup' things, such as running traps[0]. */
+         if (user_subshell)
+           run_exit_trap ();
+
+         exit (return_code);
+       }
+      else
+       {
+         close_pipes (pipe_in, pipe_out);
+
+         /* If we are part of a pipeline, and not the end of the pipeline,
+            then we should simply return and let the last command in the
+            pipe be waited for.  If we are not in a pipeline, or are the
+            last command in the pipeline, then we wait for the subshell 
+            and return its exit status as usual. */
+         if (pipe_out != NO_PIPE)
+           return (EXECUTION_SUCCESS);
+
+         if (command->flags & CMD_NO_WAIT)
+           return (EXECUTION_SUCCESS);
+
+         stop_pipeline (asynchronous, (COMMAND *)NULL);
+
+         if (!asynchronous)
+           {
+             last_command_exit_value = wait_for (paren_pid);
+
+             /* If we have to, invert the return value. */
+             if (invert)
+               {
+                 if (last_command_exit_value == EXECUTION_SUCCESS)
+                   return (EXECUTION_FAILURE);
+                 else
+                   return (EXECUTION_SUCCESS);
+               }
+             else
+               return (last_command_exit_value);
+           }
+         else
+           {
+             if (interactive)
+               describe_pid (paren_pid);
+
+             run_pending_traps ();
+
+             return (EXECUTION_SUCCESS);
+           }
+       }
+    }
+
+  /* Handle WHILE FOR CASE etc. with redirections.  (Also '&' input
+     redirection.)  */
+  if (do_redirections (command->redirects, 1, 1, 0) != 0)
+    return (EXECUTION_FAILURE);
+
+  my_undo_list = (REDIRECT *)copy_redirects (redirection_undo_list);
+
+  begin_unwind_frame ("loop_redirections");
+
+  if (my_undo_list)
+    add_unwind_protect ((Function *)cleanup_redirects, my_undo_list);
+
+  ignore_return = (command->flags & CMD_IGNORE_RETURN) != 0;
+
+  switch (command->type)
+    {
+    case cm_for:
+      if (ignore_return)
+       command->value.For->flags |= CMD_IGNORE_RETURN;
+      exec_result = execute_for_command (command->value.For);
+      break;
+
+    case cm_case:
+      if (ignore_return)
+       command->value.Case->flags |= CMD_IGNORE_RETURN;
+      exec_result = execute_case_command (command->value.Case);
+      break;
+
+    case cm_while:
+      if (ignore_return)
+       command->value.While->flags |= CMD_IGNORE_RETURN;
+      exec_result = execute_while_command (command->value.While);
+      break;
+
+    case cm_until:
+      if (ignore_return)
+       command->value.While->flags |= CMD_IGNORE_RETURN;
+      exec_result = execute_until_command (command->value.While);
+      break;
+
+    case cm_if:
+      if (ignore_return)
+       command->value.If->flags |= CMD_IGNORE_RETURN;
+      exec_result = execute_if_command (command->value.If);
+      break;
+
+    case cm_group:
+
+      /* This code can be executed from either of two paths: an explicit
+        '{}' command, or via a function call.  If we are executed via a
+        function call, we have already taken care of the function being
+        executed in the background (down there in execute_simple_command ()),
+        and this command should *not* be marked as asynchronous.  If we
+        are executing a regular '{}' group command, and asynchronous == 1,
+        we must want to execute the whole command in the background, so we
+        need a subshell, and we want the stuff executed in that subshell
+        (this group command) to be executed in the foreground of that
+        subshell (i.e. there will not be *another* subshell forked).
+
+        What we do is to force a subshell if asynchronous, and then call
+        execute_command_internal again with asynchronous still set to 1,
+        but with the original group command, so the printed command will
+        look right.
+
+        The code above that handles forking off subshells will note that
+        both subshell and async are on, and turn off async in the child
+        after forking the subshell (but leave async set in the parent, so
+        the normal call to describe_pid is made).  This turning off
+        async is *crucial*; if it is not done, this will fall into an
+        infinite loop of executions through this spot in subshell after
+        subshell until the process limit is exhausted. */
+
+      if (asynchronous)
+       {
+         command->flags |= CMD_FORCE_SUBSHELL;
+         exec_result =
+           execute_command_internal (command, 1, pipe_in, pipe_out,
+                                     fds_to_close);
+       }
+      else
+       {
+         if (ignore_return && command->value.Group->command)
+           command->value.Group->command->flags |= CMD_IGNORE_RETURN;
+         exec_result =
+           execute_command_internal (command->value.Group->command,
+                                     asynchronous, pipe_in, pipe_out,
+                                     fds_to_close);
+       }
+      break;
+
+    case cm_simple:
+      {
+       pid_t last_pid = last_made_pid;
+
+#if defined (JOB_CONTROL)
+       extern int already_making_children;
+#endif /* JOB_CONTROL */
+       if (ignore_return && command->value.Simple)
+         command->value.Simple->flags |= CMD_IGNORE_RETURN;
+       exec_result =
+         execute_simple_command (command->value.Simple, pipe_in, pipe_out,
+                                 asynchronous, fds_to_close);
+
+       /* The temporary environment should be used for only the simple
+          command immediately following its definition. */
+       dispose_used_env_vars ();
+
+#if (defined (Ultrix) && defined (mips)) || !defined (HAVE_ALLOCA)
+       /* Reclaim memory allocated with alloca () on machines which
+          may be using the alloca emulation code. */
+       (void) alloca (0);
+#endif /* (Ultrix && mips) || !HAVE_ALLOCA */
+
+       if (command->flags & CMD_NO_WAIT)
+         break;
+
+       /* If we forked to do the command, then we must wait_for ()
+          the child. */
+#if defined (JOB_CONTROL)
+       if (already_making_children && pipe_out == NO_PIPE)
+#else
+       if (pipe_out == NO_PIPE)
+#endif /* JOB_CONTROL */
+         {
+           if (last_pid != last_made_pid)
+             {
+               stop_pipeline (asynchronous, (COMMAND *)NULL);
+
+               if (asynchronous)
+                 {
+                   if (interactive)
+                     describe_pid (last_made_pid);
+                 }
+               else
+#if !defined (JOB_CONTROL)
+                 /* Do not wait for asynchronous processes started from
+                    startup files. */
+                 if (last_made_pid != last_asynchronous_pid)
+#endif
+                   /* When executing a shell function that executes other
+                      commands, this causes the last simple command in
+                      the function to be waited for twice. */
+                   exec_result = wait_for (last_made_pid);
+             }
+         }
+      }
+      if (!ignore_return && exit_immediately_on_error && !invert &&
+         (exec_result != EXECUTION_SUCCESS))
+       {
+         last_command_exit_value = exec_result;
+         run_pending_traps ();
+         longjmp (top_level, EXITPROG);
+       }
+
+      break;
+
+    case cm_connection:
+      switch (command->value.Connection->connector)
+       {
+         /* Do the first command asynchronously. */
+       case '&':
+         {
+           COMMAND *tc = command->value.Connection->first;
+           REDIRECT *rp = tc->redirects;
+
+           if (ignore_return && tc)
+             tc->flags |= CMD_IGNORE_RETURN;
+
+           /* If this shell was compiled without job control, or if job
+              control is not active (e.g., if the shell is not interactive),
+              the standard input for an asynchronous command is /dev/null. */
+#if defined (JOB_CONTROL)
+           if (!interactive || !job_control)
+#endif /* JOB_CONTROL */
+           {
+             REDIRECT *tr = 
+               make_redirection (0, r_inputa_direction,
+                                 make_word ("/dev/null"));
+             tr->next = tc->redirects;
+             tc->redirects = tr;
+           }
+
+           exec_result = execute_command_internal (tc, 1, pipe_in, pipe_out,
+                                                   fds_to_close);
+
+#if defined (JOB_CONTROL)
+           if (!interactive || !job_control)
+#endif /* JOB_CONTROL */
+           {
+             /* Remove the redirection we added above.  It matters,
+                especially for loops, which call execute_command ()
+                multiple times with the same command. */
+             REDIRECT *tr, *tl;
+
+             tr = tc->redirects;
+             do
+               {
+                 tl = tc->redirects;
+                 tc->redirects = tc->redirects->next;
+               }
+             while (tc->redirects && tc->redirects != rp);
+
+             tl->next = (REDIRECT *)NULL;
+             dispose_redirects (tr);
+           }
+
+           {
+             register COMMAND *second;
+
+             second = command->value.Connection->second;
+
+             if (second)
+               {
+                 if (ignore_return)
+                   second->flags |= CMD_IGNORE_RETURN;
+
+                 exec_result = execute_command_internal
+                   (second, asynchronous, pipe_in, pipe_out, fds_to_close);
+               }
+           }
+         }
+         break;
+
+       case ';':
+         /* Just call execute command on both of them. */
+         if (ignore_return)
+           {
+             if (command->value.Connection->first)
+               command->value.Connection->first->flags |= CMD_IGNORE_RETURN;
+             QUIT;
+             if (command->value.Connection->second)
+               command->value.Connection->second->flags |= CMD_IGNORE_RETURN;
+           }
+         execute_command (command->value.Connection->first);
+         QUIT;
+         exec_result =
+           execute_command_internal (command->value.Connection->second,
+                                     asynchronous, pipe_in, pipe_out,
+                                     fds_to_close);
+         break;
+
+       case '|':
+         {
+           int prev, fildes[2], new_bitmap_size, dummyfd;
+           COMMAND *cmd;
+           int outpipe;
+           struct fd_bitmap *fd_bitmap;
+
+#if defined (JOB_CONTROL)
+           COMMAND *lastcmd;
+           int lastpipe[2];
+           pid_t lastpipe_pid;
+
+           sigset_t set, oset;
+           BLOCK_CHILD (set, oset);
+#endif /* JOB_CONTROL */
+
+#if defined (JOB_CONTROL)
+           /* Fork the last command in the pipeline first, then the rest of
+              the commands, to avoid synchronization problems. */
+           /* Find the last pipeline element */
+           lastcmd = command;
+           while (lastcmd && lastcmd->type == cm_connection &&
+                  lastcmd->value.Connection &&
+                  lastcmd->value.Connection->connector == '|')
+             lastcmd = lastcmd->value.Connection->second;
+
+           if (pipe (lastpipe) < 0)
+             {
+               report_error ("pipe error: %s", strerror (errno));
+               terminate_current_pipeline ();
+               kill_current_pipeline ();
+               last_command_exit_value = EXECUTION_FAILURE;
+               throw_to_top_level ();
+             }
+
+           /* Now execute the rightmost command in the pipeline, but do
+              not wait for it. */
+           if (ignore_return && lastcmd)
+             lastcmd->flags |= CMD_IGNORE_RETURN;
+           lastcmd->flags |= (CMD_NO_WAIT | CMD_LAST_PIPE);
+           fds_to_close->bitmap[lastpipe[1]] = 1;
+           (void) execute_command_internal
+             (lastcmd, asynchronous, lastpipe[0], pipe_out, fds_to_close);
+           lastpipe_pid = last_made_pid;
+#endif /* JOB_CONTROL */
+
+           prev = pipe_in;
+           cmd = command;
+
+           while (cmd &&
+                  cmd->type == cm_connection &&
+                  cmd->value.Connection &&
+                  cmd->value.Connection->connector == '|')
+             {
+               /* Make a pipeline between the two commands. */
+               if (pipe (fildes) < 0)
+                 {
+                   report_error ("pipe error: %s", strerror (errno));
+#if defined (JOB_CONTROL)
+                   terminate_current_pipeline ();
+                   kill_current_pipeline ();
+#endif /* JOB_CONTROL */
+                   last_command_exit_value = EXECUTION_FAILURE;
+                   /* The unwind-protects installed below will take care
+                      of closing all of the open file descriptors. */
+                   throw_to_top_level ();
+                 }
+               else
+                 {
+                   /* Here is a problem: with the new file close-on-exec
+                      code, the read end of the pipe (fildes[0]) stays open
+                      in the first process, so that process will never get a
+                      SIGPIPE.  There is no way to signal the first process
+                      that it should close fildes[0] after forking, so it
+                      remains open.  No SIGPIPE is ever sent because there
+                      is still a file descriptor open for reading connected
+                      to the pipe.  We take care of that here.  This passes
+                      around a bitmap of file descriptors that must be
+                      closed after making a child process in
+                      execute_simple_command. */
+
+                   /* We need fd_bitmap to be at least as big as fildes[0].
+                      If fildes[0] is less than fds_to_close->size, then
+                      use fds_to_close->size. */
+
+                   if (fildes[0] < fds_to_close->size)
+                     new_bitmap_size = fds_to_close->size;
+                   else
+                     new_bitmap_size = fildes[0] + 8;
+
+                   fd_bitmap = new_fd_bitmap (new_bitmap_size);
+
+                   /* Now copy the old information into the new bitmap. */
+                   bcopy (fds_to_close->bitmap, fd_bitmap->bitmap,
+                          fds_to_close->size);
+
+                   /* And mark the pipe file descriptors to be closed. */
+                   fd_bitmap->bitmap[fildes[0]] = 1;
+
+                   /* In case there are pipe or out-of-processes errors, we
+                      want all these file descriptors to be closed when
+                      unwind-protects are run, and the storage used for the
+                      bitmaps freed up. */
+                   begin_unwind_frame ("pipe-file-descriptors");
+                   add_unwind_protect (dispose_fd_bitmap, fd_bitmap);
+                   add_unwind_protect (close_fd_bitmap, fd_bitmap);
+                   if (prev >= 0)
+                     add_unwind_protect (close, prev);
+                   dummyfd = fildes[1];
+                   add_unwind_protect (close, dummyfd);
+
+#if defined (JOB_CONTROL)
+                   add_unwind_protect (restore_signal_mask, oset);
+#endif /* JOB_CONTROL */
+
+                   if (ignore_return && cmd->value.Connection->first)
+                     cmd->value.Connection->first->flags |=
+                       CMD_IGNORE_RETURN;
+
+#if defined (JOB_CONTROL)
+                   if (cmd->value.Connection->second == lastcmd)
+                     {
+                       outpipe = lastpipe[1];
+                       close (fildes[1]);
+                     }
+                   else
+#endif /* JOB_CONTROL */
+                     outpipe = fildes[1];
+
+                   execute_command_internal
+                     (cmd->value.Connection->first, asynchronous, prev,
+                      outpipe, fd_bitmap);
+
+                   if (prev >= 0)
+                     close (prev);
+                   
+                   prev = fildes[0];
+                   close (outpipe);
+
+                   dispose_fd_bitmap (fd_bitmap);
+                   discard_unwind_frame ("pipe-file-descriptors");
+                 }
+               cmd = cmd->value.Connection->second;
+#if defined (JOB_CONTROL)
+               if (cmd == lastcmd)
+                 break;
+#endif
+             }
+
+#if !defined (JOB_CONTROL)
+           /* Now execute the rightmost command in the pipeline.  */
+           if (ignore_return && cmd)
+             cmd->flags |= CMD_IGNORE_RETURN;
+           exec_result =
+             execute_command_internal
+               (cmd, asynchronous, prev, pipe_out, fds_to_close);
+#endif /* !JOB_CONTROL */
+
+           if (prev >= 0)
+             close (prev);
+
+#if defined (JOB_CONTROL)
+           UNBLOCK_CHILD (oset);
+#endif
+
+#if defined (JOB_CONTROL)
+           /* Because we created the processes out of order, we have to
+              reorder the pipeline slightly. */
+           rotate_the_pipeline ();
+           stop_pipeline (asynchronous, (COMMAND *)NULL);
+           if (asynchronous && interactive)
+             describe_pid (lastpipe_pid);
+           exec_result = wait_for (lastpipe_pid);
+           if (!ignore_return && exit_immediately_on_error && !invert &&
+               (exec_result != EXECUTION_SUCCESS))
+             {
+               last_command_exit_value = exec_result;
+               run_pending_traps ();
+               longjmp (top_level, EXITPROG);
+             }
+#endif /* JOB_CONTROL */
+         }
+         break;
+
+       case AND_AND:
+         if (asynchronous)
+           {
+             /* If we have something like `a && b &', run the && stuff in a
+                subshell.  Force a subshell and just call
+                execute_command_internal again.  Leave asynchronous on
+                so that we get a report from the parent shell about the
+                background job. */
+             command->flags |= CMD_FORCE_SUBSHELL;
+             exec_result = execute_command_internal (command, 1, pipe_in,
+                             pipe_out, fds_to_close);
+             break;
+           }
+
+         /* Execute the first command.  If the result of that is successful,
+            then execute the second command, otherwise return. */
+
+         if (command->value.Connection->first)
+           command->value.Connection->first->flags |= CMD_IGNORE_RETURN;
+
+         exec_result = execute_command (command->value.Connection->first);
+         QUIT;
+         if (exec_result == EXECUTION_SUCCESS)
+           {
+             if (ignore_return && command->value.Connection->second)
+               command->value.Connection->second->flags |=
+                 CMD_IGNORE_RETURN;
+
+             exec_result =
+               execute_command (command->value.Connection->second);
+           }
+         break;
+
+       case OR_OR:
+         if (asynchronous)
+           {
+             /* If we have something like `a || b &', run the || stuff in a
+                subshell.  Force a subshell and just call
+                execute_command_internal again.  Leave asynchronous on
+                so that we get a report from the parent shell about the
+                background job. */
+             command->flags |= CMD_FORCE_SUBSHELL;
+             exec_result = execute_command_internal (command, 1, pipe_in,
+                             pipe_out, fds_to_close);
+             break;
+           }
+
+         /* Execute the first command.  If the result of that is successful,
+            then return, otherwise execute the second command. */
+
+         if (command->value.Connection->first)
+           command->value.Connection->first->flags |= CMD_IGNORE_RETURN;
+
+         exec_result = execute_command (command->value.Connection->first);
+         QUIT;
+         if (exec_result != EXECUTION_SUCCESS)
+           {
+             if (ignore_return && command->value.Connection->second)
+               command->value.Connection->second->flags |=
+                 CMD_IGNORE_RETURN;
+
+             exec_result =
+               execute_command (command->value.Connection->second);
+           }
+
+         break;
+
+       default:
+         programming_error ("Bad connector `%d'!",
+                            command->value.Connection->connector);
+         longjmp (top_level, DISCARD);
+         break;
+       }
+      break;
+
+    case cm_function_def:
+      exec_result = intern_function (command->value.Function_def->name,
+                                    command->value.Function_def->command);
+      break;
+
+    default:
+      programming_error
+       ("execute_command: Bad command type `%d'!", command->type);
+    }
+
+  if (my_undo_list)
+    {
+      do_redirections (my_undo_list, 1, 0, 0);
+      dispose_redirects (my_undo_list);
+    }
+
+  discard_unwind_frame ("loop_redirections");
+
+  /* Invert the return value if we have to */
+  if (invert)
+    {
+      if (exec_result == EXECUTION_SUCCESS)
+       exec_result = EXECUTION_FAILURE;
+      else
+       exec_result = EXECUTION_SUCCESS;
+    }
+
+  last_command_exit_value = exec_result;
+  run_pending_traps ();
+  return (last_command_exit_value);
+}
+
+/* Execute a FOR command.  The syntax is: FOR word_desc IN word_list;
+   DO command; DONE */
+execute_for_command (for_command)
+     FOR_COM *for_command;
+{
+  /* I just noticed that the Bourne shell leaves word_desc bound to the
+     last name in word_list after the FOR statement is done.  This seems
+     wrong to me; I thought that the variable binding should be lexically
+     scoped, i.e., only would last the duration of the FOR command.  This
+     behaviour can be gotten by turning on the lexical_scoping switch. */
+
+  register WORD_LIST *releaser, *list;
+  WORD_DESC *temp = for_command->name;
+  char *identifier;
+  SHELL_VAR *old_value = (SHELL_VAR *)NULL; /* Remember the old value of x. */
+  int retval = EXECUTION_SUCCESS;
+  extern int dispose_words ();
+  extern int dispose_variable ();
+
+  if (!check_identifier (temp))
+    return (EXECUTION_FAILURE);
+
+  loop_level++;
+  identifier = temp->word;
+
+  list = releaser = expand_words (for_command->map_list, 0);
+
+  begin_unwind_frame ("for");
+  add_unwind_protect (dispose_words, releaser);
+
+  if (lexical_scoping)
+    {
+      old_value = copy_variable (find_variable (identifier));
+      if (old_value)
+       add_unwind_protect (dispose_variable, old_value);
+    }
+
+  while (list)
+    {
+      QUIT;
+      bind_variable (identifier, list->word->word);
+      if (for_command->flags & CMD_IGNORE_RETURN)
+       for_command->action->flags |= CMD_IGNORE_RETURN;
+      execute_command (for_command->action);
+      retval = last_command_exit_value;
+      QUIT;
+
+      if (breaking)
+       {
+         breaking--; 
+         break;
+       }
+
+      if (continuing)
+       {
+         continuing--;
+         if (continuing)
+           break;
+       }
+
+      list = list->next;
+    }
+
+  loop_level--;
+
+  if (lexical_scoping)
+    {
+      if (!old_value)
+       makunbound (identifier, shell_variables);
+      else
+       {
+         SHELL_VAR *new_value;
+
+         new_value = bind_variable (identifier, value_cell(old_value));
+         new_value->attributes = old_value->attributes;
+       }
+    }
+
+  run_unwind_frame ("for");
+  return (retval);
+}
+
+/* Execute a CASE command.  The syntax is: CASE word_desc IN pattern_list ESAC.
+   The pattern_list is a linked list of pattern clauses; each clause contains
+   some patterns to compare word_desc against, and an associated command to
+   execute. */
+execute_case_command (case_command)
+     CASE_COM *case_command;
+{
+  extern int dispose_words ();
+  extern char *tilde_expand ();
+  register WORD_LIST *list;
+  WORD_LIST *wlist;
+  PATTERN_LIST *clauses;
+  char *word;
+  int retval;
+
+  /* Posix.2 Draft 11.2 says that the word is tilde expanded. */
+  if (member ('~', case_command->word->word))
+    {
+      word = tilde_expand (case_command->word->word);
+      free (case_command->word->word);
+      case_command->word->word = word;
+    }
+  wlist = expand_word (case_command->word, 0);
+  clauses = case_command->clauses;
+  word = (wlist) ? string_list (wlist) : savestring ("");
+  retval = EXECUTION_SUCCESS;
+
+  begin_unwind_frame ("case");
+  add_unwind_protect (dispose_words, wlist);
+  add_unwind_protect ((Function *)vfree, word);
+
+  while (clauses)
+    {
+      QUIT;
+      list = clauses->patterns;
+      while (list)
+       {
+         extern char *quote_string_for_globbing ();
+         char *t, *pattern;
+         WORD_LIST *es;
+         int match, freepat;
+
+         /* Posix.2 draft 11.3 says to do tilde expansion on each member
+            of the pattern list. */
+         if (member ('~', list->word->word))
+           {
+             t = tilde_expand (list->word->word);
+             free (list->word->word);
+             list->word->word = t;
+           }
+
+         es = expand_word_leave_quoted (list->word, 0);
+         if (es && es->word && es->word->word && *(es->word->word))
+           {
+             pattern = quote_string_for_globbing (es->word->word, 1);
+             freepat = 1;
+           }
+         else
+           {
+             pattern = "";
+             freepat = 0;
+           }
+
+         /* Since the pattern does not undergo quote removal according to
+            Posix.2 section 3.9.4.3, the fnmatch() call must be able to
+            recognize backslashes as escape characters. */
+         match = fnmatch (pattern, word, 0) != FNM_NOMATCH;
+         if (freepat)
+           free (pattern);
+         dispose_words (es);
+
+         if (match)
+           {
+             if (clauses->action && 
+                 (case_command->flags & CMD_IGNORE_RETURN))
+               clauses->action->flags |= CMD_IGNORE_RETURN;
+             execute_command (clauses->action);
+             retval = last_command_exit_value;
+             goto exit_command;
+           }
+
+         list = list->next;
+         QUIT;
+       }
+      clauses = clauses->next;
+    }
+ exit_command:
+  run_unwind_frame ("case");
+  return (retval);
+}
+
+#define CMD_WHILE 0
+#define CMD_UNTIL 1
+
+/* The WHILE command.  Syntax: WHILE test DO action; DONE.
+   Repeatedly execute action while executing test produces
+   EXECUTION_SUCCESS. */
+execute_while_command (while_command)
+     WHILE_COM *while_command;
+{
+  return (execute_while_or_until (while_command, CMD_WHILE));
+}
+
+/* UNTIL is just like WHILE except that the test result is negated. */
+execute_until_command (while_command)
+     WHILE_COM *while_command;
+{
+  return (execute_while_or_until (while_command, CMD_UNTIL));
+}
+
+/* The body for both while and until.  The only difference between the
+   two is that the test value is treated differently.  TYPE is
+   CMD_WHILE or CMD_UNTIL.  The return value for both commands should
+   be EXECUTION_SUCCESS if no commands in the body are executed, and
+   the status of the last command executed in the body otherwise. */
+execute_while_or_until (while_command, type)
+     WHILE_COM *while_command;
+     int type;
+{
+  extern int breaking;
+  extern int continuing;
+  int commands_executed = 0;
+  int return_value, body_status = EXECUTION_SUCCESS;
+
+  loop_level++;
+  while_command->test->flags |= CMD_IGNORE_RETURN;
+
+  while (1)
+    {
+      return_value = execute_command (while_command->test);
+
+      if (type == CMD_WHILE && return_value != EXECUTION_SUCCESS)
+       break;
+      if (type == CMD_UNTIL && return_value == EXECUTION_SUCCESS)
+       break;
+
+      QUIT;
+      commands_executed = 1;
+
+      if (while_command->flags & CMD_IGNORE_RETURN)
+       while_command->action->flags |= CMD_IGNORE_RETURN;
+      body_status = execute_command (while_command->action);
+
+      QUIT;
+
+      if (breaking)
+       {
+         breaking--;
+         break;
+       }
+
+      if (continuing)
+       {
+         continuing--;
+         if (continuing)
+           break;
+       }
+    }
+  loop_level--;
+
+  return (body_status);
+}
+
+/* IF test THEN command [ELSE command].
+   IF also allows ELIF in the place of ELSE IF, but
+   the parser makes *that* stupidity transparent. */
+execute_if_command (if_command)
+     IF_COM *if_command;
+{
+  int return_value;
+
+  if_command->test->flags |= CMD_IGNORE_RETURN;
+  return_value = execute_command (if_command->test);
+
+  if (return_value == EXECUTION_SUCCESS)
+    {
+      QUIT;
+      if (if_command->true_case && (if_command->flags & CMD_IGNORE_RETURN))
+         if_command->true_case->flags |= CMD_IGNORE_RETURN;
+      return (execute_command (if_command->true_case));
+    }
+  else
+    {
+      QUIT;
+
+      if (if_command->false_case &&
+         (if_command->flags & CMD_IGNORE_RETURN))
+       {
+         if_command->false_case->flags |= CMD_IGNORE_RETURN;
+       }
+
+      return (execute_command (if_command->false_case));
+    }
+}
+
+/* The name of the command that is currently being executed.
+   `test' needs this, for example. */
+char *this_command_name;
+
+static void
+bind_lastarg (arg)
+     char *arg;
+{
+  SHELL_VAR *var;
+
+  if (!arg)
+    arg = "";
+  var = bind_variable ("_", arg);
+  var->attributes &= ~att_exported;
+}
+
+/* For catching RETURN in a function. */
+int return_catch_flag = 0;
+int return_catch_value;
+jmp_buf return_catch;
+
+/* The meaty part of all the executions.  We have to start hacking the
+   real execution of commands here.  Fork a process, set things up,
+   execute the command. */
+execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close)
+     SIMPLE_COM *simple_command;
+     int pipe_in, pipe_out;
+     struct fd_bitmap *fds_to_close;
+{
+  extern int command_string_index, variable_context, line_number;
+  extern char *the_printed_command;
+  extern pid_t last_command_subst_pid;
+  WORD_LIST *expand_words (), *copy_word_list ();
+  WORD_LIST *words, *lastword;
+  char *command_line, *lastarg;
+  int first_word_quoted, result;
+  pid_t old_last_command_subst_pid;
+
+  result = EXECUTION_SUCCESS;
+
+  /* If we're in a function, update the pseudo-line-number information. */
+  if (variable_context)
+    line_number++;
+
+  /* Remember what this command line looks like at invocation. */
+  command_string_index = 0;
+  print_simple_command (simple_command);
+  command_line = (char *)alloca (1 + strlen (the_printed_command));
+  strcpy (command_line, the_printed_command);
+
+  first_word_quoted =
+    simple_command->words ? simple_command->words->word->quoted : 0;
+
+  old_last_command_subst_pid = last_command_subst_pid;
+
+  /* If we are re-running this as the result of executing the `command'
+     builtin, do not expand the command words a second time. */
+  if ((simple_command->flags & CMD_INHIBIT_EXPANSION) == 0)
+    words = expand_words (simple_command->words);
+  else
+    words = copy_word_list (simple_command->words);
+
+  lastarg = (char *)NULL;
+  begin_unwind_frame ("simple-command");
+
+  /* It is possible for WORDS not to have anything left in it.
+     Perhaps all the words consisted of `$foo', and there was
+     no variable `$foo'. */
+  if (words)
+    {
+      extern int dispose_words ();
+      extern Function *last_shell_builtin, *this_shell_builtin;
+      Function *builtin;
+      SHELL_VAR *func;
+
+      if (echo_command_at_execute)
+       {
+         extern char *indirection_level_string ();
+         char *line = string_list (words);
+
+         if (line && *line)
+           fprintf (stderr, "%s%s\n", indirection_level_string (), line);
+
+         if (line)
+           free (line);
+       }
+
+      if (simple_command->flags & CMD_NO_FUNCTIONS)
+       func = (SHELL_VAR *)NULL;
+      else
+       func = find_function (words->word->word);
+
+      add_unwind_protect (dispose_words, words);
+
+      QUIT;
+
+      /* Bind the last word in this command to "$_" after execution. */
+      for (lastword = words; lastword->next; lastword = lastword->next);
+      lastarg = lastword->word->word;
+
+#if defined (JOB_CONTROL)
+      /* Is this command a job control related thing? */
+      if (words->word->word[0] == '%')
+       {
+         int result;
+
+         if (async)
+           this_command_name = "bg";
+         else
+           this_command_name = "fg";
+
+         last_shell_builtin = this_shell_builtin;
+         this_shell_builtin = builtin_address (this_command_name);
+         result = (*this_shell_builtin) (words);
+         goto return_result;
+       }
+
+      /* One other possiblilty.  The user may want to resume an existing job.
+        If they do, find out whether this word is a candidate for a running
+        job. */
+      {
+       char *auto_resume_value;
+
+       if ((auto_resume_value = get_string_value ("auto_resume")) &&
+           !first_word_quoted &&
+           !words->next &&
+           words->word->word[0] &&
+           !simple_command->redirects &&
+           pipe_in == NO_PIPE &&
+           pipe_out == NO_PIPE &&
+           !async)
+         {
+           char *word = words->word->word;
+           register int i, wl = strlen (word), exact;
+
+           exact = strcmp (auto_resume_value, "exact") == 0;
+           for (i = job_slots - 1; i > -1; i--)
+             {
+               if (jobs[i])
+                 {
+                   register PROCESS *p = jobs[i]->pipe;
+                   do
+                     {
+                       if ((JOBSTATE (i) == JSTOPPED) &&
+                           (strncmp (p->command, word,
+                                     exact ? strlen (p->command) : wl) == 0))
+                         {
+                           int started_status;
+
+                           run_unwind_frame ("simple-command");
+                           last_shell_builtin = this_shell_builtin;
+                           this_shell_builtin = builtin_address ("fg");
+
+                           started_status = start_job (i, 1);
+
+                           if (started_status < 0)
+                             return (EXECUTION_FAILURE);
+                           else
+                             return (started_status);
+                         }
+                       p = p->next;
+                     }
+                   while (p != jobs[i]->pipe);
+                 }
+             }
+         }
+      }
+#endif /* JOB_CONTROL */
+
+      /* Remember the name of this command globally. */
+      this_command_name = words->word->word;
+
+      QUIT;
+
+      /* Not a running job.  Do normal command processing. */
+      maybe_make_export_env ();
+
+      /* This command could be a shell builtin or a user-defined function.
+        If so, and we have pipes, then fork a subshell in here.  Else, just
+        do the command. */
+
+      if (func)
+       builtin = (Function *)NULL;
+      else
+       builtin = find_shell_builtin (this_command_name);
+
+      last_shell_builtin = this_shell_builtin;
+      this_shell_builtin = builtin;
+
+      if (builtin || func)
+       {
+         put_command_name_into_env (this_command_name);
+         if ((pipe_in != NO_PIPE) || (pipe_out != NO_PIPE) || async)
+           {
+             if (make_child (savestring (command_line), async) == 0)
+               {
+                 execute_subshell_builtin_or_function
+                   (words, simple_command->redirects, builtin, func,
+                    pipe_in, pipe_out, async, fds_to_close,
+                    simple_command->flags);
+               }
+             else
+               {
+                 close_pipes (pipe_in, pipe_out);
+                 goto return_result;
+               }
+           }
+         else
+           {
+             result = execute_builtin_or_function
+               (words, builtin, func, simple_command->redirects, fds_to_close,
+                simple_command->flags);
+
+             goto return_result;
+           }
+       }
+
+      execute_disk_command (words, simple_command->redirects, command_line,
+                           pipe_in, pipe_out, async, fds_to_close);
+
+      goto return_result;
+    }
+  else if (pipe_in != NO_PIPE || pipe_out != NO_PIPE || async)
+    {
+      /* We have a null command, but we really want a subshell to take
+        care of it.  Just fork, do piping and redirections, and exit. */
+      if (make_child (savestring (""), async) == 0)
+       {
+         do_piping (pipe_in, pipe_out);
+
+         subshell_environment = 1;
+
+         if (do_redirections (simple_command->redirects, 1, 0, 0) == 0)
+           exit (EXECUTION_SUCCESS);
+         else
+           exit (EXECUTION_FAILURE);
+       }
+      else
+       {
+         close_pipes (pipe_in, pipe_out);
+         result = EXECUTION_SUCCESS;
+         goto return_result;
+       }
+    }
+  else
+    {
+      /* Even if there aren't any command names, pretend to do the
+        redirections that are specified.  The user expects the side
+        effects to take place.  If the redirections fail, then return
+        failure.  Otherwise, if a command substitution took place while
+        expanding the command or a redirection, return the value of that
+        substitution.  Otherwise, return EXECUTION_SUCCESS. */
+
+      if (do_redirections (simple_command->redirects, 0, 0, 0) != 0)
+       result = EXECUTION_FAILURE;
+      else if (old_last_command_subst_pid != last_command_subst_pid)
+       result = last_command_exit_value;
+      else
+       result = EXECUTION_SUCCESS;
+    }
+
+ return_result:
+  bind_lastarg (lastarg);
+  run_unwind_frame ("simple-command");
+  return (result);
+}
+
+/* Execute a shell builtin or function in a subshell environment.  This
+   routine does not return; it only calls exit().  If BUILTIN is non-null,
+   it points to a function to call to execute a shell builtin; otherwise
+   VAR points at the body of a function to execute.  WORDS is the arguments
+   to the command, REDIRECTS specifies redirections to perform before the
+   command is executed. */
+static void
+execute_subshell_builtin_or_function (words, redirects, builtin, var,
+                                     pipe_in, pipe_out, async, fds_to_close,
+                                     flags)
+     WORD_LIST *words;
+     REDIRECT *redirects;
+     Function *builtin;
+     SHELL_VAR *var;
+     int pipe_in, pipe_out, async;
+     struct fd_bitmap *fds_to_close;
+     int flags;
+{
+  extern char **temporary_env, **function_env, **copy_array ();
+  extern int login_shell, interactive;
+#if defined (JOB_CONTROL)
+  extern int jobs_builtin ();
+#endif /* JOB_CONTROL */
+
+  /* A subshell is neither a login shell nor interactive. */
+  login_shell = interactive = 0;
+
+  subshell_environment = 1;
+
+#if defined (JOB_CONTROL)
+  /* Eradicate all traces of job control after we fork the subshell, so
+     all jobs begun by this subshell are in the same process group as
+     the shell itself. */
+
+  /* Allow the output of `jobs' to be piped. */
+  if (builtin == jobs_builtin && !async &&
+      (pipe_out != NO_PIPE || pipe_in != NO_PIPE))
+    kill_current_pipeline ();
+  else
+    without_job_control ();
+
+  set_sigchld_handler ();
+#endif /* JOB_CONTROL */
+
+  set_sigint_handler ();
+
+  do_piping (pipe_in, pipe_out);
+
+  if (fds_to_close)
+    close_fd_bitmap (fds_to_close);
+
+  if (do_redirections (redirects, 1, 0, 0) != 0)
+    exit (EXECUTION_FAILURE);
+
+  if (builtin)
+    {
+      extern jmp_buf top_level;
+      int result;
+
+      /* Give builtins a place to jump back to on failure,
+        so we don't go back up to main(). */
+      result = setjmp (top_level);
+
+      if (result == EXITPROG)
+       exit (last_command_exit_value);
+      else if (result)
+       exit (EXECUTION_FAILURE);
+      else
+       exit ((*builtin) (words->next));
+    }
+  else
+    {
+      extern int variable_context, line_number;
+      extern void dispose_command (), dispose_function_env ();
+      COMMAND *fc, *tc;
+      int result, return_val;
+
+      tc = (COMMAND *)function_cell (var);
+      fc = (COMMAND *)NULL;
+
+      remember_args (words->next, 1);
+      line_number = 0;
+#if defined (JOB_CONTROL)
+      stop_pipeline (async, (COMMAND *)NULL);
+#endif
+
+      begin_unwind_frame ("subshell_function_calling");
+      unwind_protect_int (variable_context);
+      unwind_protect_int (return_catch_flag);
+      unwind_protect_jmp_buf (return_catch);
+      add_unwind_protect (dispose_command, fc);
+
+      /* The temporary environment for a function is supposed to apply to
+        all commands executed in the function.  If we have a temporary
+        environment, copy it to the special `function environment' and
+        get rid of the temporary environment. */
+      if (temporary_env)
+       {
+         function_env = copy_array (temporary_env);
+         add_unwind_protect (dispose_function_env, (char *)NULL);
+         dispose_used_env_vars ();
+       }
+      else
+       function_env = (char **)NULL;
+
+      /* We can do this because function bodies are always guaranteed to
+        be group commands, according to the grammar in parse.y.  If we
+        don't do this now, execute_command_internal will graciously fork
+        another subshell for us, and we'll lose contact with the rest of
+        the pipeline and fail to get any SIGPIPE that might be sent. */
+
+      if (tc->type == cm_group)
+       fc = (COMMAND *)copy_command (tc->value.Group->command);
+      else
+       fc = (COMMAND *)copy_command (tc);
+
+      if (fc && (flags & CMD_IGNORE_RETURN))
+       fc->flags |= CMD_IGNORE_RETURN;
+
+      /* result = execute_command (fc); doesn't work.
+        We need to explicitly specify the pipes in and out so that they
+        are closed in all the processes that rely on their being closed.
+        If they are not, it is possible to not get the SIGPIPE that we
+        need to kill all the processes sharing the pipe. */
+
+      variable_context++;
+      return_catch_flag++;
+      return_val = setjmp (return_catch);
+
+      if (return_val)
+       result = return_catch_value;
+      else
+       result =
+         execute_command_internal (fc, 0, NO_PIPE, NO_PIPE, fds_to_close);
+
+      run_unwind_frame ("subshell_function_calling");
+
+      exit (result);
+    }
+}
+
+/* Execute a builtin or function in the current shell context.  If BUILTIN
+   is non-null, it is the builtin command to execute, otherwise VAR points
+   to the body of a function.  WORDS are the command's arguments, REDIRECTS
+   are the redirections to perform.  FDS_TO_CLOSE is the usual bitmap of
+   file descriptors to close.
+
+   If BUILTIN is exec_builtin, the redirections specified in REDIRECTS are
+   not undone before this function returns. */
+static int
+execute_builtin_or_function (words, builtin, var, redirects,
+                            fds_to_close, flags)
+     WORD_LIST *words;
+     Function *builtin;
+     SHELL_VAR *var;
+     REDIRECT *redirects;
+     struct fd_bitmap *fds_to_close;
+     int flags;
+{
+  extern int exec_builtin (), eval_builtin ();
+  extern char **temporary_env, **function_env, **copy_array ();
+  int result = EXECUTION_FAILURE;
+  REDIRECT *saved_undo_list;
+
+  if (do_redirections (redirects, 1, 1, 0) != 0)
+    return (EXECUTION_FAILURE);
+
+  saved_undo_list = redirection_undo_list;
+
+  /* Calling the "exec" builtin changes redirections forever. */
+  if (builtin == exec_builtin)
+    {
+      dispose_redirects (saved_undo_list);
+      saved_undo_list = (REDIRECT *)NULL;
+    }
+  else
+    {
+      begin_unwind_frame ("saved redirects");
+      add_unwind_protect (cleanup_func_redirects, (char *)saved_undo_list);
+    }
+
+  redirection_undo_list = (REDIRECT *)NULL;
+
+  if (builtin)
+    {
+      int old_e_flag = exit_immediately_on_error;
+
+      /* The eval builtin calls parse_and_execute, which does not know about
+        the setting of flags, and always calls the execution functions with
+        flags that will exit the shell on an error if -e is set.  If the
+        eval builtin is being called, and we're supposed to ignore the exit
+        value of the command, we turn the -e flag off ourselves, then
+        restore it when the command completes. */
+      if ((builtin == eval_builtin) && (flags & CMD_IGNORE_RETURN))
+       {
+         begin_unwind_frame ("eval_builtin");
+         unwind_protect_int (exit_immediately_on_error);
+         exit_immediately_on_error = 0;
+       }
+
+      result = ((*builtin) (words->next));
+
+      if ((builtin == eval_builtin) && (flags & CMD_IGNORE_RETURN))
+       {
+         exit_immediately_on_error += old_e_flag;
+         discard_unwind_frame ("eval_builtin");
+       }
+    }
+  else
+    {
+      extern void dispose_command (), dispose_function_env ();
+      extern int pop_context ();
+      extern int line_number;
+      int return_val;
+      COMMAND *tc;
+
+      tc = (COMMAND *)copy_command (function_cell (var));
+      if (tc && (flags & CMD_IGNORE_RETURN))
+       tc->flags |= CMD_IGNORE_RETURN;
+
+      begin_unwind_frame ("function_calling");
+      push_context ();
+      add_unwind_protect (pop_context, (char *)NULL);
+      add_unwind_protect (dispose_command, (char *)tc);
+      unwind_protect_int (return_catch_flag);
+      unwind_protect_int (line_number);
+      unwind_protect_jmp_buf (return_catch);
+
+      /* The temporary environment for a function is supposed to apply to
+        all commands executed in the function.  If we have a temporary
+        environment, copy it to the special `function environment' and
+        get rid of the temporary environment. */
+      if (temporary_env)
+       {
+         function_env = copy_array (temporary_env);
+         add_unwind_protect (dispose_function_env, (char *)NULL);
+         dispose_used_env_vars ();
+       }
+      else
+       function_env = (char **)NULL;
+
+      /* Note the second argument of "1", meaning that we discard
+        the current value of "$*"!  This is apparently the right thing. */
+      remember_args (words->next, 1);
+
+      line_number = 0;
+      return_catch_flag++;
+      return_val =  setjmp (return_catch);
+
+      if (return_val)
+       result = return_catch_value;
+      else
+       result =
+         execute_command_internal (tc, 0, NO_PIPE, NO_PIPE, fds_to_close);
+
+      run_unwind_frame ("function_calling");
+    }
+
+  redirection_undo_list = saved_undo_list;
+  if (builtin != exec_builtin)
+    discard_unwind_frame ("saved redirects");
+  do_redirections (redirection_undo_list, 1, 0, 0);
+
+  return (result);
+}
+
+/* Execute a simple command that is hopefully defined in a disk file
+   somewhere.
+
+   1) fork ()
+   2) connect pipes
+   3) look up the command
+   4) do redirections
+   5) execve ()
+   6) If the execve failed, see if the file has executable mode set.
+   If so, and it isn't a directory, then execute its contents as
+   a shell script.
+
+   Note that the filename hashing stuff has to take place up here,
+   in the parent.  This is probably why the Bourne style shells
+   don't handle it, since that would require them to go through
+   this gnarly hair, for no good reason.  */
+static void
+execute_disk_command (words, redirects, command_line, pipe_in, pipe_out,
+                     async, fds_to_close)
+     WORD_LIST *words;
+     REDIRECT *redirects;
+     char *command_line;
+     int pipe_in, pipe_out, async;
+     struct fd_bitmap *fds_to_close;
+{
+  char **make_word_array (), *find_user_command (), *find_hashed_filename ();
+  char *hashed_file, *command, **args;
+
+#if defined (RESTRICTED_SHELL)
+  extern int restricted;
+
+  if (restricted && strchr (words->word->word, '/'))
+    {
+      report_error ("%s: restricted: cannot specify `/' in command names",
+                   words->word->word);
+      last_command_exit_value = EXECUTION_FAILURE;
+      return;
+    }
+#endif /* RESTRICTED_SHELL */
+
+  hashed_file = command = (char *)NULL;
+
+  /* Don't waste time trying to find hashed data for a pathname
+     that is already completely specified. */
+
+  if (!absolute_program (words->word->word))
+    hashed_file = find_hashed_filename (words->word->word);
+
+  /* XXX - this could be a big performance hit... */
+  /* If a command found in the hash table no longer exists, we need to
+     look for it in $PATH. */
+  if (hashed_file)
+    {
+      int st;
+
+      st = file_status (hashed_file);
+      if ((st & (FS_EXISTS | FS_EXECABLE)) != (FS_EXISTS | FS_EXECABLE))
+       {
+         remove_hashed_filename (words->word->word);
+         hashed_file = (char *) NULL;
+       }
+    }
+    
+  if (hashed_file)
+    command = savestring (hashed_file);
+  else
+    {
+      /* A command containing a slash is not looked up in PATH. */
+      if (absolute_program (words->word->word))
+       command = savestring (words->word->word);
+      else
+       {
+         command = find_user_command (words->word->word);
+
+         /* A command name containing a slash is not saved in the
+            hash table. */
+         if (command && !hashing_disabled)
+           {
+             extern int dot_found_in_search;
+
+             remember_filename
+               (words->word->word, command, dot_found_in_search);
+
+             /* Increase the number of hits to 1. */
+             find_hashed_filename (words->word->word);
+           }
+       }
+    }
+
+  if (command)
+    put_command_name_into_env (command);
+
+  /* We have to make the child before we check for the non-existance
+     of COMMAND, since we want the error messages to be redirected. */
+  if (make_child (savestring (command_line), async) == 0)
+    {
+      do_piping (pipe_in, pipe_out);
+
+      /* Execve expects the command name to be in args[0].  So we
+        leave it there, in the same format that the user used to
+        type it in. */
+      args = make_word_array (words);
+
+      if (async)
+       {
+         begin_unwind_frame ("async_redirections");
+         unwind_protect_int (interactive);
+         interactive = 0;
+       }
+
+      subshell_environment = 1;
+
+      if (do_redirections (redirects, 1, 0, 0) != 0)
+       {
+#if defined (PROCESS_SUBSTITUTION)
+         /* Try to remove named pipes that may have been created as the
+            result of redirections. */
+         unlink_fifo_list ();
+#endif /* PROCESS_SUBSTITUTION */
+         exit (EXECUTION_FAILURE);
+       }
+
+      if (async)
+       run_unwind_frame ("async_redirections");
+
+      if (!command)
+       {
+         report_error ("%s: command not found", args[0]);
+         exit (EXECUTION_FAILURE);
+       }
+
+      /* This functionality is now provided by close-on-exec of the
+        file descriptors manipulated by redirection and piping.
+        Some file descriptors still need to be closed in all children
+        because of the way bash does pipes; fds_to_close is a 
+        bitmap of all such file descriptors. */
+      if (fds_to_close)
+       close_fd_bitmap (fds_to_close);
+
+      exit (shell_execve (command, args, export_env));
+    }
+  else
+    {
+      /* Make sure that the pipes are closed in the parent. */
+      close_pipes (pipe_in, pipe_out);
+      if (command)
+       free (command);
+    }
+}
+
+/* If the operating system on which we're running does not handle
+   the #! executable format, then help out.  SAMPLE is the text read
+   from the file, SAMPLE_LEN characters.  COMMAND is the name of
+   the script; it and ARGS, the arguments given by the user, will
+   become arguments to the specified interpreter.  ENV is the environment
+   to pass to the interpreter.
+
+   The word immediately following the #! is the interpreter to execute.
+   A single argument to the interpreter is allowed. */
+static int
+execute_shell_script (sample, sample_len, command, args, env)
+     unsigned char *sample;
+     int sample_len;
+     char *command;
+     char **args, **env;
+{
+  extern char *shell_name;
+  register int i;
+  char *execname, *firstarg;
+  int start, size_increment, larry;
+
+  /* Find the name of the interpreter to exec. */
+  for (i = 2; whitespace (sample[i]) && i < sample_len; i++)
+    ;
+
+  for (start = i;
+       !whitespace (sample[i]) && sample[i] != '\n' && i < sample_len;
+       i++)
+    ;
+
+  execname = (char *)xmalloc (1 + (i - start));
+  strncpy (execname, sample + start, i - start);
+  execname[i - start] = '\0';
+  size_increment = 1;
+
+  /* Now the argument, if any. */
+  firstarg = (char *)NULL;
+  for (start = i;
+       whitespace (sample[i]) && sample[i] != '\n' && i < sample_len;
+       i++)
+    ;
+
+  /* If there is more text on the line, then it is an argument for the
+     interpreter. */
+  if (i < sample_len && sample[i] != '\n' && !whitespace (sample[i]))
+    {
+      for (start = i;
+          !whitespace (sample[i]) && sample[i] != '\n' && i < sample_len;
+          i++)
+       ;
+      firstarg = (char *)xmalloc (1 + (i - start));
+      strncpy (firstarg, sample + start, i - start);
+      firstarg[i - start] = '\0';
+
+      size_increment = 2;
+    }
+
+  larry = array_len (args) + size_increment;
+
+  args = (char **)xrealloc (args, (1 + larry) * sizeof (char *));
+
+  for (i = larry - 1; i; i--)
+    args[i] = args[i - size_increment];
+
+  args[0] = execname;
+  if (firstarg)
+    {
+      args[1] = firstarg;
+      args[2] = command;
+    }
+  else
+    args[1] = command;
+
+  args[larry] = (char *)NULL;
+
+  return (shell_execve (execname, args, env));
+}
+
+/* Call execve (), handling interpreting shell scripts, and handling
+   exec failures. */
+int
+shell_execve (command, args, env)
+     char *command;
+     char **args, **env;
+{
+#if defined (isc386) && defined (_POSIX_SOURCE)
+  __setostype (0);             /* Turn on USGr3 semantics. */
+  execve (command, args, env);
+  __setostype (1);             /* Turn the POSIX semantics back on. */
+#else
+  execve (command, args, env);
+#endif /* !(isc386 && _POSIX_SOURCE) */
+
+  /* If we get to this point, then start checking out the file.
+     Maybe it is something we can hack ourselves. */
+  {
+    struct stat finfo;
+
+    if (errno != ENOEXEC)
+      {
+       if ((stat (command, &finfo) == 0) &&
+           (S_ISDIR (finfo.st_mode)))
+         report_error ("%s: is a directory", args[0]);
+       else
+         file_error (command);
+
+       return (EXECUTION_FAILURE);
+      }
+    else
+      {
+       /* This file is executable.
+          If it begins with #!, then help out people with losing operating
+          systems.  Otherwise, check to see if it is a binary file by seeing
+          if the first line (or up to 30 characters) are in the ASCII set.
+          Execute the contents as shell commands. */
+       extern char *shell_name;
+       int larray = array_len (args) + 1;
+       int i, should_exec = 0;
+
+       {
+         int fd = open (command, O_RDONLY);
+         if (fd != -1)
+           {
+             unsigned char sample[80];
+             int sample_len = read (fd, &sample[0], 80);
+
+             close (fd);
+
+             if (sample_len == 0)
+               return (EXECUTION_SUCCESS);
+
+             /* Is this supposed to be an executable script? */
+             /* If so, the format of the line is "#! interpreter [argument]".
+                A single argument is allowed.  The BSD kernel restricts
+                the length of the entire line to 32 characters (32 bytes
+                being the size of the BSD exec header), but we allow 80
+                characters. */
+
+             if (sample_len > 0 && sample[0] == '#' && sample[1] == '!')
+               return (execute_shell_script (sample, sample_len, command,
+                                             args, env));
+#if defined (NOTDEF)
+#if defined (HAVE_CSH) && ( defined (Bsd) || defined (Ultrix) )
+             /* If this system has Csh, then keep the old
+                BSD semantics. */
+             else if (sample_len > 0 && sample[0] == '#')
+               {
+                 /* Scripts starting with a # are for Csh. */
+                 shell_name = savestring ("/bin/csh");
+                 should_exec = 1;
+               }
+#endif /* HAVE_CSH */
+#endif /* NOTDEF */
+             else if ((sample_len != -1) &&
+                       check_binary_file (sample, sample_len))
+               {
+                 report_error ("%s: cannot execute binary file", command);
+                 return (EX_BINARY_FILE);
+               }
+           }
+       }
+#if defined (JOB_CONTROL)
+       /* Forget about the way that job control was working. We are
+          in a subshell. */
+       without_job_control ();
+#endif /* JOB_CONTROL */
+#if defined (ALIAS)
+       /* Forget about any aliases that we knew of.  We are in a subshell. */
+       delete_all_aliases ();
+#endif /* ALIAS */
+
+#if defined (JOB_CONTROL)
+       set_sigchld_handler ();
+#endif /* JOB_CONTROL */
+        set_sigint_handler ();
+
+       /* Insert the name of this shell into the argument list. */
+       args = (char **)xrealloc (args, (1 + larray) * sizeof (char *));
+
+       for (i = larray - 1; i; i--)
+         args[i] = args[i - 1];
+
+       args[0] = shell_name;
+       args[1] = command;
+       args[larray] = (char *)NULL;
+
+       if (args[0][0] == '-')
+         args[0]++;
+
+       if (should_exec)
+         {
+           struct stat finfo;
+
+#if defined (isc386) && defined (_POSIX_SOURCE)
+           __setostype (0);    /* Turn on USGr3 semantics. */
+           execve (shell_name, args, env);
+           __setostype (1);    /* Turn the POSIX semantics back on. */
+#else
+           execve (shell_name, args, env);
+#endif /* isc386 && _POSIX_SOURCE */
+
+           /* Oh, no!  We couldn't even exec this! */
+           if ((stat (args[0], &finfo) == 0) && (S_ISDIR (finfo.st_mode)))
+             report_error ("%s: is a directory", args[0]);
+           else
+             file_error (args[0]);
+
+           return (EXECUTION_FAILURE);
+         }
+       else
+         {
+           extern jmp_buf subshell_top_level;
+           extern int subshell_argc;
+           extern char **subshell_argv;
+           extern char **subshell_envp;
+
+           subshell_argc = larray;
+           subshell_argv = args;
+           subshell_envp = env;
+           longjmp (subshell_top_level, 1);
+         }
+      }
+  }
+}
+
+#if defined (PROCESS_SUBSTITUTION)
+void
+close_all_files ()
+{
+  register int i, fd_table_size;
+
+  fd_table_size = getdtablesize ();
+
+  for (i = 3; i < fd_table_size; i++)
+    close (i);
+}
+#endif /* PROCESS_SUBSTITUTION */
+
+static void
+close_pipes (in, out)
+     int in, out;
+{
+  if (in >= 0) close (in);
+  if (out >= 0) close (out);
+}
+
+/* Redirect input and output to be from and to the specified pipes.
+   NO_PIPE and REDIRECT_BOTH are handled correctly. */
+static void
+do_piping (pipe_in, pipe_out)
+     int pipe_in, pipe_out;
+{
+  if (pipe_in != NO_PIPE)
+    {
+      dup2 (pipe_in, 0);
+      close (pipe_in);
+    }
+  if (pipe_out != NO_PIPE)
+    {
+      dup2 (pipe_out, 1);
+      close (pipe_out);
+
+      if (pipe_out == REDIRECT_BOTH)
+       dup2 (1, 2);
+    }
+}
+
+/* Defined in flags.c.  Non-zero means don't overwrite existing files. */
+extern int noclobber;
+
+#define AMBIGUOUS_REDIRECT  -1
+#define NOCLOBBER_REDIRECT  -2
+#define RESTRICTED_REDIRECT -3 /* Only can happen in restricted shells. */
+
+/* Perform the redirections on LIST.  If FOR_REAL, then actually make
+   input and output file descriptors, otherwise just do whatever is
+   neccessary for side effecting.  INTERNAL says to remember how to
+   undo the redirections later, if non-zero.  If SET_CLEXEC is non-zero,
+   file descriptors opened in do_redirection () have their close-on-exec
+   flag set. */
+static int
+do_redirections (list, for_real, internal, set_clexec)
+     REDIRECT *list;
+     int for_real, internal;
+{
+  register int error;
+  register REDIRECT *temp = list;
+
+  if (internal && redirection_undo_list)
+    {
+      dispose_redirects (redirection_undo_list);
+      redirection_undo_list = (REDIRECT *)NULL;
+    }
+
+  while (temp)
+    {
+      extern char *strerror ();
+
+      error = do_redirection_internal (temp, for_real, internal, set_clexec);
+
+      if (error)
+       {
+         char *redirection_expand (), *itos ();
+         char *filename;
+
+         if (expandable_redirection_filename (temp))
+           {
+             filename = redirection_expand (temp->redirectee.filename);
+             if (!filename)
+               filename = savestring ("");
+           }
+         else
+           filename = itos (temp->redirectee.dest);
+
+         switch (error)
+           {
+           case AMBIGUOUS_REDIRECT:
+             report_error ("%s: Ambiguous redirect", filename);
+             break;
+
+           case NOCLOBBER_REDIRECT:
+             report_error ("%s: Cannot clobber existing file", filename);
+             break;
+
+#if defined (RESTRICTED_SHELL)
+           case RESTRICTED_REDIRECT:
+             report_error ("%s: output redirection restricted", filename);
+             break;
+#endif /* RESTRICTED_SHELL */
+
+           default:
+             report_error ("%s: %s", filename, strerror (error));
+             break;
+           }
+
+         free (filename);
+         return (error);
+       }
+
+      temp = temp->next;
+    }
+  return (0);
+}
+
+/* Return non-zero if the redirection pointed to by REDIRECT has a
+   redirectee.filename that can be expanded. */
+static int
+expandable_redirection_filename (redirect)
+     REDIRECT *redirect;
+{
+  int result;
+
+  switch (redirect->instruction)
+    {
+    case r_output_direction:
+    case r_appending_to:
+    case r_input_direction:
+    case r_inputa_direction:
+    case r_err_and_out:
+    case r_input_output:
+    case r_output_force:
+    case r_duplicating_input_word:
+    case r_duplicating_output_word:
+      result = 1;
+      break;
+
+    default:
+      result = 0;
+    }
+  return (result);
+}
+\f
+/* Expand the word in WORD returning a string.  If WORD expands to
+   multiple words (or no words), then return NULL. */
+char *
+redirection_expand (word)
+     WORD_DESC *word;
+{
+  char *result;
+  WORD_LIST *make_word_list (), *expand_words_no_vars ();
+  WORD_LIST *tlist1, *tlist2;
+
+  tlist1 = make_word_list (copy_word (word), (WORD_LIST *)NULL);
+  tlist2 = expand_words_no_vars (tlist1);
+  dispose_words (tlist1);
+
+  if (!tlist2 || tlist2->next)
+    {
+      /* We expanded to no words, or to more than a single word.
+        Dispose of the word list and return NULL. */
+      if (tlist2)
+       dispose_words (tlist2);
+      return ((char *)NULL);
+    }
+  result = string_list (tlist2);
+  dispose_words (tlist2);
+
+  return (result);
+}
+
+/* Do the specific redirection requested.  Returns errno in case of error.
+   If FOR_REAL is zero, then just do whatever is neccessary to produce the
+   appropriate side effects.   REMEMBERING, if non-zero, says to remember
+   how to undo each redirection.  If SET_CLEXEC is non-zero, then
+   we set all file descriptors > 2 that we open to be close-on-exec.  */
+static int
+do_redirection_internal (redirect, for_real, remembering, set_clexec)
+     REDIRECT *redirect;
+     int for_real, remembering;
+{
+  WORD_DESC *redirectee = redirect->redirectee.filename;
+  int fd, redirector = redirect->redirector;
+  char *redirectee_word;
+  enum r_instruction ri = redirect->instruction;
+  REDIRECT *new_redirect;
+
+  if (ri == r_duplicating_input_word || ri == r_duplicating_output_word)
+    {
+      /* We have [N]>&WORD or [N]<&WORD.  Expand WORD, then translate
+        the redirection into a new one and continue. */
+      redirectee_word = redirection_expand (redirectee);
+
+      if (redirectee_word[0] == '-' && redirectee_word[1] == '\0')
+       {
+         new_redirect = make_redirection (redirector, r_close_this, 0);
+       }
+      else if (all_digits (redirectee_word))
+       {
+         if (ri == r_duplicating_input_word)
+           {
+             new_redirect = make_redirection
+               (redirector, r_duplicating_input, atoi (redirectee_word));
+           }
+         else
+           {
+             new_redirect = make_redirection
+               (redirector, r_duplicating_output, atoi (redirectee_word));
+           }
+       }
+      else if (ri == r_duplicating_output_word && redirector == 1)
+       {
+         new_redirect = make_redirection
+           (1, r_err_and_out, make_word (redirectee_word));
+       }
+      else
+       {
+         free (redirectee_word);
+         return (AMBIGUOUS_REDIRECT);
+       }
+
+      free (redirectee_word);
+
+      /* Set up the variables needed by the rest of the function from the
+        new redirection. */
+      if (new_redirect->instruction == r_err_and_out)
+       {
+         char *alloca_hack;
+
+         /* Copy the word without allocating any memory that must be
+            explicitly freed. */
+         redirectee = (WORD_DESC *)alloca (sizeof (WORD_DESC));
+         bcopy (new_redirect->redirectee.filename,
+                redirectee, sizeof (WORD_DESC));
+
+         alloca_hack = (char *)
+           alloca (1 + strlen (new_redirect->redirectee.filename->word));
+         redirectee->word = alloca_hack;
+         strcpy (redirectee->word, new_redirect->redirectee.filename->word);
+       }
+      else
+       /* It's guaranteed to be an integer, and shouldn't be freed. */
+       redirectee = new_redirect->redirectee.filename;
+
+      redirector = new_redirect->redirector;
+      ri = new_redirect->instruction;
+
+      /* Overwrite the flags element of the old redirect with the new value. */
+      redirect->flags = new_redirect->flags;
+      dispose_redirects (new_redirect);
+    }
+
+  switch (ri)
+    {
+    case r_output_direction:
+    case r_appending_to:
+    case r_input_direction:
+    case r_inputa_direction:
+    case r_err_and_out:                /* command &>filename */
+    case r_input_output:
+    case r_output_force:
+
+      if (!(redirectee_word = redirection_expand (redirectee)))
+       return (AMBIGUOUS_REDIRECT);
+
+#if defined (RESTRICTED_SHELL)
+      if (restricted && (ri == r_output_direction ||
+                        ri == r_input_output ||
+                        ri == r_err_and_out ||
+                        ri == r_appending_to ||
+                        ri == r_output_force))
+       {
+         free(redirectee_word);
+         return (RESTRICTED_REDIRECT);
+       }
+#endif /* RESTRICTED_SHELL */
+
+      /* If we are in noclobber mode, you are not allowed to overwrite
+        existing files.  Check first. */
+      if (noclobber && (ri == r_output_direction ||
+                     ri == r_input_output ||
+                     ri == r_err_and_out))
+      {
+       struct stat buf;
+       int stat_result;
+
+       stat_result = stat (redirectee_word, &buf);
+       if ((stat_result == 0) && (S_ISREG (buf.st_mode)))
+         {
+           free (redirectee_word);
+           return (NOCLOBBER_REDIRECT);
+         }
+       /* If the file was not there, make sure we use exclusive open so
+          that if it's created before we open it, our open will fail. */
+       if (stat_result != 0)
+         redirect->flags |= O_EXCL;
+       fd = open (redirectee_word, redirect->flags, 0666);
+       if (fd < 0 && errno == EEXIST)
+         {
+           free (redirectee_word);
+           return (NOCLOBBER_REDIRECT);
+         }
+      }
+    else
+      {
+       fd = open (redirectee_word, redirect->flags, 0666);
+#if defined (AFS_CREATE_BUG)
+       if (fd < 0 && errno == EACCES)
+         fd = open (redirectee_word, (redirect->flags & ~O_CREAT), 0666);
+#endif /* AFS_CREATE_BUG */
+      }
+
+    free (redirectee_word);
+
+    if (fd < 0 )
+      return (errno);
+
+      if (for_real)
+       {
+         if (remembering)
+           /* Only setup to undo it if the thing to undo is active. */
+           if ((fd != redirector) && (fcntl (redirector, F_GETFD, 0) != -1))
+             add_undo_redirect (redirector);
+           else
+             add_undo_close_redirect (redirector);
+
+         if ((fd != redirector) && (dup2 (fd, redirector) < 0))
+           return (errno);
+
+#if defined (BUFFERED_INPUT)
+         if (ri == r_input_direction || ri == r_inputa_direction ||
+             ri == r_input_output)
+           duplicate_buffered_stream (fd, redirector);
+#endif /* BUFFERED_INPUT */
+
+         /*
+          * If we're remembering, then this is the result of a while, for
+          * or until loop with a loop redirection, or a function/builtin
+          * executing in the parent shell with a redirection.  In the
+          * function/builtin case, we want to set all file descriptors > 2
+          * to be close-on-exec to duplicate the effect of the old
+          * for i = 3 to NOFILE close(i) loop.  In the case of the loops,
+          * both sh and ksh leave the file descriptors open across execs.
+          * The Posix standard mentions only the exec builtin.
+          */
+         if (set_clexec && (redirector > 2))
+           SET_CLOSE_ON_EXEC (redirector);
+       }
+
+      if (fd != redirector)
+       {
+#if defined (BUFFERED_INPUT)
+         if (ri == r_input_direction || ri == r_inputa_direction ||
+             ri == r_input_output)
+           close_buffered_fd (fd);
+         else
+#endif /* !BUFFERED_INPUT */
+           close (fd);         /* Don't close what we just opened! */
+       }
+
+      /* If we are hacking both stdout and stderr, do the stderr
+        redirection here. */
+      if (ri == r_err_and_out)
+       {
+         if (for_real)
+           {
+             if (remembering)
+               add_undo_redirect (2);
+             if (dup2 (1, 2) < 0)
+               return (errno);
+           }
+       }
+      break;
+
+    case r_reading_until:
+    case r_deblank_reading_until:
+      {
+       /* REDIRECTEE is a pointer to a WORD_DESC containing the text of
+          the new input.  Place it in a temporary file. */
+       int document_index = 0;
+       char *document = (char *)NULL;
+
+       /* Expand the text if the word that was specified had no quoting.
+          Note that the text that we expand is treated exactly as if it
+          were surrounded by double-quotes.  */
+
+       if (!redirectee)
+         document = savestring ("");
+       else
+         {
+           if (!redirectee->quoted)
+             {
+               WORD_LIST *temp_word_list =
+                 (WORD_LIST *)expand_string (redirectee->word,
+                                             Q_HERE_DOCUMENT);
+
+               document = string_list (temp_word_list);
+               if (!document)
+                 document = savestring ("");
+               dispose_words (temp_word_list);
+             }
+           else
+             {
+               document = redirectee->word;
+             }
+           document_index = strlen (document);
+
+           {
+             char filename[40];
+             pid_t pid = getpid ();
+
+             /* Make the filename for the temp file. */
+             sprintf (filename, "/tmp/t%d-sh", pid);
+
+             fd = open (filename, O_TRUNC | O_WRONLY | O_CREAT, 0666);
+             if (fd < 0)
+               {
+                 if (!redirectee->quoted)
+                   free (document);
+                 return (errno);
+               }
+
+             write (fd, document, document_index);
+             close (fd);
+
+             if (!redirectee->quoted)
+               free (document);
+
+             /* Make the document really temporary.  Also make it the
+                input. */
+             fd = open (filename, O_RDONLY, 0666);
+
+             if (unlink (filename) < 0 || fd < 0)
+               return (errno);
+
+             if (for_real)
+               {
+                 if (remembering)
+                   /* Only setup to undo it if the thing to undo is active. */
+                   if ((fd != redirector) &&
+                       (fcntl (redirector, F_GETFD, 0) != -1))
+                     add_undo_redirect (redirector);
+                   else
+                     add_undo_close_redirect (redirector);
+
+                 if (dup2 (fd, redirector) < 0)
+                   return (errno);
+
+#if defined (BUFFERED_INPUT)
+                 duplicate_buffered_stream (fd, redirector);
+#endif
+
+                 if (set_clexec && (redirector > 2))
+                   SET_CLOSE_ON_EXEC (redirector);
+               }
+
+#if defined (BUFFERED_INPUT)
+             close_buffered_fd (fd);
+#else
+             close (fd);
+#endif
+           }
+         }
+      }
+      break;
+
+    case r_duplicating_input:
+    case r_duplicating_output:
+      if (for_real && ((int)redirectee != redirector))
+       {
+         if (remembering)
+           /* Only setup to undo it if the thing to undo is active. */
+           if (fcntl (redirector, F_GETFD, 0) != -1)
+             add_undo_redirect (redirector);
+           else
+             add_undo_close_redirect (redirector);
+
+         /* This is correct.  2>&1 means dup2 (1, 2); */
+         if (dup2 ((int)redirectee, redirector) < 0)
+           return (errno);
+
+#if defined (BUFFERED_INPUT)
+         if (ri == r_duplicating_input)
+           duplicate_buffered_stream ((int)redirectee, redirector);
+#endif /* BUFFERED_INPUT */
+
+         /* First duplicate the close-on-exec state of redirectee.  dup2
+            leaves the flag unset on the new descriptor, which means it
+            stays open.  Only set the close-on-exec bit for file descriptors
+            greater than 2 in any case, since 0-2 should always be open
+            unless closed by something like `exec 2<&-'. */
+         /* if ((already_set || set_unconditionally) && (ok_to_set))
+               set_it () */
+         if (((fcntl ((int)redirectee, F_GETFD, 0) == 1) || set_clexec) &&
+              (redirector > 2))
+           SET_CLOSE_ON_EXEC (redirector);
+       }
+      break;
+
+    case r_close_this:
+      if (for_real)
+       {
+         if (remembering && (fcntl (redirector, F_GETFD, 0) != -1))
+           add_undo_redirect (redirector);
+
+#if defined (BUFFERED_INPUT)
+         close_buffered_fd (redirector);
+#else /* !BUFFERED_INPUT */
+         close (redirector);
+#endif /* !BUFFERED_INPUT */
+       }
+      break;
+    }
+  return (0);
+}
+
+#define SHELL_FD_BASE  10
+
+/* Remember the file descriptor associated with the slot FD,
+   on REDIRECTION_UNDO_LIST.  Note that the list will be reversed
+   before it is executed. */
+static int
+add_undo_redirect (fd)
+     int fd;
+{
+  int new_fd, clexec_flag;
+  REDIRECT *new_redirect, *closer;
+
+  new_fd = fcntl (fd, F_DUPFD, SHELL_FD_BASE);
+
+  if (new_fd < 0)
+    {
+      file_error ("redirection error");
+      return (-1);
+    }
+  else
+    {
+      clexec_flag = fcntl (fd, F_GETFD, 0);
+      closer = make_redirection (new_fd, r_close_this, 0);
+      new_redirect = make_redirection (fd, r_duplicating_input, new_fd);
+      new_redirect->next = closer;
+      closer->next = redirection_undo_list;
+      redirection_undo_list = new_redirect;
+      /*
+       * File descriptors used only for saving others should always be
+       * marked close-on-exec.  Unfortunately, we have to preserve the
+       * close-on-exec state of the file descriptor we are saving, since
+       * fcntl (F_DUPFD) sets the new file descriptor to remain open
+       * across execs.  If, however, the file descriptor whose state we
+       * are saving is <= 2, we can just set the close-on-exec flag,
+       * because file descriptors 0-2 should always be open-on-exec,
+       * and the restore above in do_redirection() will take care of it.
+       */
+      if (clexec_flag || fd < 3)
+       SET_CLOSE_ON_EXEC (new_fd);
+    }
+  return (0);
+}
+
+/* Set up to close FD when we are finished with the current command
+   and its redirections. */
+static void
+add_undo_close_redirect (fd)
+     int fd;
+{
+  REDIRECT *closer;
+
+  closer = make_redirection (fd, r_close_this, 0);
+  closer->next = redirection_undo_list;
+  redirection_undo_list = closer;
+}
+
+intern_function (name, function)
+     WORD_DESC *name;
+     COMMAND *function;
+{
+  SHELL_VAR *var;
+
+  if (!check_identifier (name))
+    return (EXECUTION_FAILURE);
+
+  var = find_function (name->word);
+  if (var && readonly_p (var))
+    {
+      report_error ("%s: readonly function", var->name);
+      return (EXECUTION_FAILURE);
+    }
+
+  bind_function (name->word, function);
+  return (EXECUTION_SUCCESS);
+}
+
+/* Make sure that identifier is a valid shell identifier, i.e.
+   does not contain a dollar sign, nor is quoted in any way.  Nor
+   does it consist of all digits. */
+check_identifier (word)
+     WORD_DESC *word;
+{
+  if (word->dollar_present || word->quoted || all_digits (word->word))
+    {
+      report_error ("`%s' is not a valid identifier", word->word);
+      return (0);
+    }
+  else
+    return (1);
+}
+
+#define u_mode_bits(x) (((x) & 0000700) >> 6)
+#define g_mode_bits(x) (((x) & 0000070) >> 3)
+#define o_mode_bits(x) (((x) & 0000007) >> 0)
+#define X_BIT(x) (x & 1)
+
+/* Return some flags based on information about this file.
+   The EXISTS bit is non-zero if the file is found.
+   The EXECABLE bit is non-zero the file is executble.
+   Zero is returned if the file is not found. */
+int
+file_status (name)
+     char *name;
+{
+  struct stat finfo;
+  static int user_id = -1;
+
+  /* Determine whether this file exists or not. */
+  if (stat (name, &finfo) < 0)
+    return (0);
+
+  /* If the file is a directory, then it is not "executable" in the
+     sense of the shell. */
+  if (S_ISDIR (finfo.st_mode))
+    return (FS_EXISTS);
+
+  /* Find out if the file is actually executable.  By definition, the
+     only other criteria is that the file has an execute bit set that
+     we can use. */
+  if (user_id == -1)
+    user_id = geteuid ();
+
+  /* Root only requires execute permission for any of owner, group or
+     others to be able to exec a file. */
+  if (user_id == 0)
+    {
+      int bits;
+
+      bits = (u_mode_bits (finfo.st_mode) |
+             g_mode_bits (finfo.st_mode) |
+             o_mode_bits (finfo.st_mode));
+
+      if (X_BIT (bits))
+       return (FS_EXISTS | FS_EXECABLE);
+    }
+
+  /* If we are the owner of the file, the owner execute bit applies. */
+  if (user_id == finfo.st_uid && X_BIT (u_mode_bits (finfo.st_mode)))
+    return (FS_EXISTS | FS_EXECABLE);
+
+  /* If we are in the owning group, the group permissions apply. */
+  if (group_member (finfo.st_gid) && X_BIT (g_mode_bits (finfo.st_mode)))
+    return (FS_EXISTS | FS_EXECABLE);
+
+  /* If `others' have execute permission to the file, then so do we,
+     since we are also `others'. */
+  if (X_BIT (o_mode_bits (finfo.st_mode)))
+    return (FS_EXISTS | FS_EXECABLE);
+  else
+    return (FS_EXISTS);
+}
+
+/* Return non-zero if FILE exists and is executable.
+   Note that this function is the definition of what an
+   executable file is; do not change this unless YOU know
+   what an executable file is. */
+int
+executable_file (file)
+     char *file;
+{
+  if (file_status (file) & FS_EXECABLE)
+    return (1);
+  else
+    return (0);
+}
+
+/* DOT_FOUND_IN_SEARCH becomes non-zero when find_user_command ()
+   encounters a `.' as the directory pathname while scanning the
+   list of possible pathnames; i.e., if `.' comes before the directory
+   containing the file of interest. */
+int dot_found_in_search = 0;
+
+/* Locate the executable file referenced by NAME, searching along
+   the contents of the shell PATH variable.  Return a new string
+   which is the full pathname to the file, or NULL if the file
+   couldn't be found.  If a file is found that isn't executable,
+   and that is the only match, then return that. */
+char *
+find_user_command (name)
+     char *name;
+{
+  return (find_user_command_internal (name, FS_EXEC_PREFERRED));
+}
+
+/* Locate the file referenced by NAME, searching along the contents
+   of the shell PATH variable.  Return a new string which is the full
+   pathname to the file, or NULL if the file couldn't be found.  This
+   returns the first file found. */
+char *
+find_path_file (name)
+     char *name;
+{
+  return (find_user_command_internal (name, FS_EXISTS));
+}
+
+static char *
+find_user_command_internal (name, flags)
+     char *name;
+     int flags;
+{
+  char *path_list = (char *)NULL;
+  SHELL_VAR *var;
+
+  /* Search for the value of PATH in both the temporary environment, and
+     in the regular list of variables. */
+  if (var = find_variable_internal ("PATH", 1))
+    path_list = value_cell (var);
+
+  if (!path_list)
+    return (savestring (name));
+
+  return (find_user_command_in_path (name, path_list, flags));
+}
+
+/* Return the next element from PATH_LIST, a colon separated list of
+   paths.  PATH_INDEX_POINTER is the address of an index into PATH_LIST;
+   the index is modified by this function.
+   Return the next element of PATH_LIST or NULL if there are no more. */
+static char *
+get_next_path_element (path_list, path_index_pointer)
+     char *path_list;
+     int *path_index_pointer;
+{
+  extern char *extract_colon_unit ();
+  char *path;
+
+  path = extract_colon_unit (path_list, path_index_pointer);
+
+  if (!path)
+    return (path);
+
+  if (!*path)
+    {
+      free (path);
+      path = savestring (".");
+    }
+
+  return (path);
+}
+
+char *
+user_command_matches (name, flags, state)
+     char *name;
+     int flags, state;
+{
+  register int i;
+  char *path_list;
+  int  path_index;
+  char *path_element;
+  char *match;
+  static char **match_list = NULL;
+  static int match_list_size = 0;
+  static int match_index = 0;
+
+  if (!state)
+    {
+      /* Create the list of matches. */
+      if (!match_list)
+       {
+         match_list =
+           (char **) xmalloc ((match_list_size = 5) * sizeof(char *));
+
+         for (i = 0; i < match_list_size; i++)
+           match_list[i] = 0;
+       }
+
+      /* Clear out the old match list. */
+      for (i = 0; i < match_list_size; i++)
+       match_list[i] = NULL;
+
+      /* We haven't found any files yet. */
+      match_index = 0;
+
+      path_list = get_string_value ("PATH");
+      path_index = 0;
+
+      while (path_list && path_list[path_index])
+       {
+         char *find_user_command_in_path ();
+
+         path_element = get_next_path_element (path_list, &path_index);
+
+         if (!path_element)
+           break;
+
+         match = find_user_command_in_path (name, path_element, flags);
+
+         free (path_element);
+
+         if (!match)
+           continue;
+
+         if (match_index + 1 == match_list_size)
+           match_list = (char **)xrealloc
+             (match_list, ((match_list_size += 10) + 1) * sizeof (char *));
+         match_list[match_index++] = match;
+         match_list[match_index] = (char *)NULL;
+       }
+
+      /* We haven't returned any strings yet. */
+      match_index = 0;
+    }
+
+  match = match_list[match_index];
+
+  if (match)
+    match_index++;
+
+  return (match);
+}
+
+/* Return 1 if PATH1 and PATH2 are the same file.  This is kind of
+   expensive.  If non-NULL STP1 and STP2 point to stat structures
+   corresponding to PATH1 and PATH2, respectively. */
+int
+same_file (path1, path2, stp1, stp2)
+     char *path1, *path2;
+     struct stat *stp1, *stp2;
+{
+  struct stat st1, st2;
+
+  if (stp1 == NULL)
+    {
+      if (stat (path1, &st1) != 0)
+       return (0);
+      stp1 = &st1;
+    }
+
+  if (stp2 == NULL)
+    {
+      if (stat (path2, &st2) != 0)
+       return (0);
+      stp2 = &st2;
+    }
+
+  return ((stp1->st_dev == stp2->st_dev) && (stp1->st_ino == stp2->st_ino));
+}
+
+/* This does the dirty work for find_path_file () and find_user_command ().
+   NAME is the name of the file to search for.
+   PATH_LIST is a colon separated list of directories to search.
+   FLAGS contains bit fields which control the files which are eligible.
+   Some values are:
+      FS_EXEC_ONLY:            The file must be an executable to be found.
+      FS_EXEC_PREFERRED:       If we can't find an executable, then the
+                               the first file matching NAME will do.
+      FS_EXISTS:               The first file found will do.
+*/
+static char *
+find_user_command_in_path (name, path_list, flags)
+     char *name;
+     char *path_list;
+     int flags;
+{
+  char *full_path, *path, *file_to_lose_on;
+  int status, path_index, name_len;
+  struct stat finfo;
+
+  name_len = strlen (name);
+
+  /* The file name which we would try to execute, except that it isn't
+     possible to execute it.  This is the first file that matches the
+     name that we are looking for while we are searching $PATH for a
+     suitable one to execute.  If we cannot find a suitable executable
+     file, then we use this one. */
+  file_to_lose_on = (char *)NULL;
+
+  /* We haven't started looking, so we certainly haven't seen
+     a `.' as the directory path yet. */
+  dot_found_in_search = 0;
+
+  if (absolute_program (name))
+    {
+      full_path = (char *)xmalloc (1 + name_len);
+      strcpy (full_path, name);
+
+      status = file_status (full_path);
+
+      if (!(status & FS_EXISTS))
+       return (0);
+
+      if ((flags & FS_EXEC_ONLY) && (status & FS_EXECABLE))
+       return (full_path);
+      else
+       {
+         free (full_path);
+         return ((char *)NULL);
+       }
+    }
+
+  /* Find out the location of the current working directory. */
+  stat (".", &finfo);
+
+  path_index = 0;
+  while (path_list && path_list[path_index])
+    {
+      /* Allow the user to interrupt out of a lengthy path search. */
+      QUIT;
+
+      path = get_next_path_element (path_list, &path_index);
+
+      if (!path)
+       break;
+
+      if (*path == '~')
+       {
+         char *tilde_expand ();
+         char *t = tilde_expand (path);
+         free (path);
+         path = t;
+       }
+
+      /* Remember the location of "." in the path, in all its forms
+        (as long as they begin with a `.', e.g. `./.') */
+      if ((*path == '.') &&
+         same_file (".", path, &finfo, (struct stat *)NULL))
+       dot_found_in_search = 1;
+
+      full_path = (char *)xmalloc (2 + strlen (path) + name_len);
+      sprintf (full_path, "%s/%s", path, name);
+      free (path);
+
+      status = file_status (full_path);
+
+      if (!(status & FS_EXISTS))
+       goto next_file;
+
+      /* The file exists.  If the caller simply wants the first file,
+        here it is. */
+      if (flags & FS_EXISTS)
+       return (full_path);
+
+       /* If the file is executable, then it satisfies the cases of
+         EXEC_ONLY and EXEC_PREFERRED.  Return this file unconditionally. */
+      if (status & FS_EXECABLE)
+       {
+         if (file_to_lose_on)
+           free (file_to_lose_on);
+
+         return (full_path);
+       }
+
+      /* The file is not executable, but it does exist.  If we prefer
+        an executable, then remember this one if it is the first one
+        we have found. */
+      if (flags & FS_EXEC_PREFERRED)
+       {
+         if (!file_to_lose_on)
+           file_to_lose_on = savestring (full_path);
+       }
+
+    next_file:
+      free (full_path);
+    }
+
+  /* We didn't find exactly what the user was looking for.  Return
+     the contents of FILE_TO_LOSE_ON which is NULL when the search
+     required an executable, or non-NULL if a file was found and the
+     search would accept a non-executable as a last resort. */
+  return (file_to_lose_on);
+}
+
+/* Given a string containing units of information separated by colons,
+   return the next one pointed to by INDX, or NULL if there are no more.
+   Advance INDX to the character after the colon. */
+char *
+extract_colon_unit (string, indx)
+     char *string;
+     int *indx;
+{
+  int i, start;
+
+  i = *indx;
+
+  if (!string || (i >= strlen (string)))
+    return ((char *)NULL);
+
+  /* Each call to this routine leaves the index pointing at a colon if
+     there is more to the path.  If I is > 0, then increment past the
+     `:'.  If I is 0, then the path has a leading colon.  Trailing colons
+     are handled OK by the `else' part of the if statement; an empty
+     string is returned in that case. */
+  if (i && string[i] == ':')
+    i++;
+
+  start = i;
+
+  while (string[i] && string[i] != ':') i++;
+
+  *indx = i;
+
+  if (i == start)
+    {
+      if (string[i])
+       (*indx)++;
+
+      /* Return "" in the case of a trailing `:'. */
+      return (savestring (""));
+    }
+  else
+    {
+      char *value;
+
+      value = (char *)xmalloc (1 + (i - start));
+      strncpy (value, &string[start], (i - start));
+      value [i - start] = '\0';
+
+      return (value);
+    }
+}
+
+/* Return non-zero if the characters from SAMPLE are not all valid
+   characters to be found in the first line of a shell script.  We
+   check up to the first newline, or SAMPLE_LEN, whichever comes first.
+   All of the characters must be printable or whitespace. */
+
+#if !defined (isspace)
+#define isspace(c) ((c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\f')
+#endif
+
+#if !defined (isprint)
+#define isprint(c) (isletter(c) || digit(c) || ispunct(c))
+#endif
+
+int
+check_binary_file (sample, sample_len)
+     unsigned char *sample;
+     int sample_len;
+{
+  register int i;
+
+  for (i = 0; i < sample_len; i++)
+    {
+      if (sample[i] == '\n')
+       break;
+
+      if (!isspace (sample[i]) && !isprint (sample[i]))
+       return (1);
+    }
+  return (0);
+}
index 7ace9c23deb474a0a2ca807f8350b321a7367ab9..9387a1172b421fdf115b7e8263de38ac87244813 100644 (file)
@@ -563,18 +563,8 @@ AC_CACHE_VAL(bash_cv_getenv_redef,
 #  include <unistd.h>
 #endif
 #include <stdlib.h>
-#ifndef __STDC__
-#  ifndef const
-#    define const
-#  endif
-#endif
 char *
-getenv (name)
-#if defined (__linux__) || defined (__bsdi__) || defined (convex)
-     const char *name;
-#else
-     char const *name;
-#endif /* !__linux__ && !__bsdi__ && !convex */
+getenv (const char *name)
 {
 return "42";
 }
@@ -613,16 +603,7 @@ AC_CACHE_CHECK([for standard-conformant putenv declaration], bash_cv_std_putenv,
 #if HAVE_STDDEF_H
 #include <stddef.h>
 #endif
-#ifndef __STDC__
-#  ifndef const
-#    define const
-#  endif
-#endif
-#ifdef PROTOTYPES
 extern int putenv (char *);
-#else
-extern int putenv ();
-#endif
 ]], [[return (putenv == 0);]] )],
 [bash_cv_std_putenv=yes], [bash_cv_std_putenv=no]
 )])
@@ -643,16 +624,7 @@ AC_CACHE_CHECK([for standard-conformant unsetenv declaration], bash_cv_std_unset
 #if HAVE_STDDEF_H
 #include <stddef.h>
 #endif
-#ifndef __STDC__
-#  ifndef const
-#    define const
-#  endif
-#endif
-#ifdef PROTOTYPES
 extern int unsetenv (const char *);
-#else
-extern int unsetenv ();
-#endif
 ]], [[return (unsetenv == 0);]] )],
 [bash_cv_std_unsetenv=yes], [bash_cv_std_unsetenv=no]
 )])
diff --git a/array.c b/array.c
index 61894e0b89cc8e7a941d8c569da1880b783b921f..dbfe9096801ce96b991f9b50014d5a0271f16ec1 100644 (file)
--- a/array.c
+++ b/array.c
@@ -9,7 +9,7 @@
  * chet@ins.cwru.edu
  */
 
-/* Copyright (C) 1997-2022 Free Software Foundation, Inc.
+/* Copyright (C) 1997-2023 Free Software Foundation, Inc.
 
    This file is part of GNU Bash, the Bourne Again SHell.
 
@@ -1119,6 +1119,7 @@ quote_string(char *s)
        return savestring(s);
 }
 
+void
 print_element(ARRAY_ELEMENT *ae)
 {
        char    lbuf[INT_STRLEN_BOUND (intmax_t) + 1];
@@ -1128,12 +1129,14 @@ print_element(ARRAY_ELEMENT *ae)
                element_value(ae));
 }
 
+void
 print_array(ARRAY *a)
 {
        printf("\n");
        array_walk(a, print_element, (void *)NULL);
 }
 
+int
 main(int c, char **v)
 {
        ARRAY   *a, *new_a, *copy_of_a;
index 34cd3440a7fa72acf2dec562e63833ba403ee7e6..49745cc95f8aa1585e6d5d9d8915b8a35a16ab65 100644 (file)
--- a/array2.c
+++ b/array2.c
@@ -1177,6 +1177,7 @@ quote_string(char *s)
        return savestring(s);
 }
 
+void
 print_element(ARRAY_ELEMENT *ae)
 {
        char    lbuf[INT_STRLEN_BOUND (intmax_t) + 1];
@@ -1186,12 +1187,14 @@ print_element(ARRAY_ELEMENT *ae)
                element_value(ae));
 }
 
+void
 print_array(ARRAY *a)
 {
        printf("\n");
        array_walk(a, print_element, (void *)NULL);
 }
 
+int
 main(int c, char **v)
 {
        ARRAY   *a, *new_a, *copy_of_a;
index 69e3fc764f460149fe69d6299d7ecbde3f670ae9..52a1f68a156cd9979aca14405029db024130c5bf 100644 (file)
@@ -389,7 +389,7 @@ remember_args (WORD_LIST *list, int destructive)
     {
       dispose_words (rest_of_args);
       rest_of_args = copy_word_list (list);
-      posparam_count += list_length (list);
+      posparam_count += list_length ((GENERIC_LIST *)list);
     }
 
   if (destructive)
index e6948bac56c023c178797bba2009cd3d3f357493..f44820b3717e8e9899124f56e313c01d9de81ae1 100644 (file)
@@ -488,11 +488,7 @@ delete_builtin (struct builtin *b)
   struct builtin *new_shell_builtins;
 
   /* XXX - funky pointer arithmetic - XXX */
-#ifdef __STDC__
   ind = b - shell_builtins;
-#else
-  ind = ((int)b - (int)shell_builtins) / sizeof (struct builtin);
-#endif
   size = num_shell_builtins * sizeof (struct builtin);
   new_shell_builtins = (struct builtin *)xmalloc (size);
 
index 76f7c06b1145e064c8d0ae81b6157e3805b96b62..4a5ac15fccf8e30cf465ce37fb751c867a3a2180 100644 (file)
@@ -1,6 +1,6 @@
 /* gen-helpfiles - create files containing builtin help text */
 
-/* Copyright (C) 2012-2022 Free Software Foundation, Inc.
+/* Copyright (C) 2012-2023 Free Software Foundation, Inc.
 
    This file is part of GNU Bash, the Bourne Again SHell.
 
 extern int errno;
 #endif
 
-#if !defined (__STDC__) && !defined (strcpy)
-extern char *strcpy ();
-#endif /* !__STDC__ && !strcpy */
-
 #define whitespace(c) (((c) == ' ') || ((c) == '\t'))
 
 /* Flag values that builtins can have. */
index 293f23de8bc946194a2e041d9903852cf5bd1573..6195831bd0f0d47bce9f358c77017d6cbe7dc21c 100644 (file)
@@ -193,20 +193,12 @@ void
 builtin_help (void)
 {
   int ind;
-  ptrdiff_t d;
 
   current_builtin = builtin_address_internal (this_command_name, 0);
   if (current_builtin == 0)
     return;
 
-  d = current_builtin - shell_builtins;
-
-#if defined (__STDC__)
-  ind = (int)d;
-#else
-  ind = (int)d / sizeof (struct builtin);
-#endif
-
+  ind = current_builtin - shell_builtins;
   printf ("%s: %s\n", this_command_name, _(shell_builtins[ind].short_doc));
   show_longdoc (ind);  
 }
index 6a4dac075a0f156f4ecabeeac03f67679a189e77..4f8cb3b5aba7a83944bccb98571640afd851dd4b 100644 (file)
@@ -1038,11 +1038,7 @@ tescape (char *estart, char *cp, int *lenp, int *sawc)
 
   switch (c = *p++)
     {
-#if defined (__STDC__)
       case 'a': *cp = '\a'; break;
-#else
-      case 'a': *cp = '\007'; break;
-#endif
 
       case 'b': *cp = '\b'; break;
 
index ba2b9fc8412414c548c3fa69e9c793eca4ee66e9..1681f8c34c64af8caacbaa64bdcce468388867e6 100644 (file)
 #undef uintmax_t
 
 /* Define to integer type wide enough to hold a pointer if <stdint.h> doesn't define. */
+#undef intptr_t
+
+/* Define to unsigned integer type wide enough to hold a pointer if <stdint.h> doesn't define. */
 #undef uintptr_t
+
 /* Define to `int' if <sys/types.h> doesn't define.  */
 #undef uid_t
 
index 6714479ac8bcbdd222e3a15821980a7b3c2e72ce..6193d3044840773d36ee3078185ed63f1e325ef8 100755 (executable)
--- a/configure
+++ b/configure
@@ -17203,6 +17203,42 @@ printf "%s\n" "#define size_t unsigned int" >>confdefs.h
 fi
 
 
+  ac_fn_c_check_type "$LINENO" "intptr_t" "ac_cv_type_intptr_t" "$ac_includes_default"
+if test "x$ac_cv_type_intptr_t" = xyes
+then :
+
+printf "%s\n" "#define HAVE_INTPTR_T 1" >>confdefs.h
+
+else $as_nop
+  for ac_type in 'int' 'long int' 'long long int'; do
+       cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main (void)
+{
+static int test_array [1 - 2 * !(sizeof (void *) <= sizeof ($ac_type))];
+test_array [0] = 0;
+return test_array [0];
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+
+printf "%s\n" "#define intptr_t $ac_type" >>confdefs.h
+
+         ac_type=
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+       test -z "$ac_type" && break
+     done
+fi
+
+
+
   ac_fn_c_check_type "$LINENO" "uintptr_t" "ac_cv_type_uintptr_t" "$ac_includes_default"
 if test "x$ac_cv_type_uintptr_t" = xyes
 then :
@@ -20005,18 +20041,8 @@ else $as_nop
 #  include <unistd.h>
 #endif
 #include <stdlib.h>
-#ifndef __STDC__
-#  ifndef const
-#    define const
-#  endif
-#endif
 char *
-getenv (name)
-#if defined (__linux__) || defined (__bsdi__) || defined (convex)
-     const char *name;
-#else
-     char const *name;
-#endif /* !__linux__ && !__bsdi__ && !convex */
+getenv (const char *name)
 {
 return "42";
 }
@@ -20488,16 +20514,7 @@ else $as_nop
 #if HAVE_STDDEF_H
 #include <stddef.h>
 #endif
-#ifndef __STDC__
-#  ifndef const
-#    define const
-#  endif
-#endif
-#ifdef PROTOTYPES
 extern int putenv (char *);
-#else
-extern int putenv ();
-#endif
 
 int
 main (void)
@@ -20546,16 +20563,7 @@ else $as_nop
 #if HAVE_STDDEF_H
 #include <stddef.h>
 #endif
-#ifndef __STDC__
-#  ifndef const
-#    define const
-#  endif
-#endif
-#ifdef PROTOTYPES
 extern int unsetenv (const char *);
-#else
-extern int unsetenv ();
-#endif
 
 int
 main (void)
index da2bd9449d179cefe48202e8916d1d8e902d7242..a933af901ddb8e06a4460509819ced71d935e3d5 100644 (file)
@@ -965,6 +965,7 @@ AC_TYPE_MODE_T
 AC_TYPE_UID_T
 AC_TYPE_PID_T
 AC_TYPE_SIZE_T
+AC_TYPE_INTPTR_T
 AC_TYPE_UINTPTR_T
 
 AC_CHECK_TYPE(ssize_t, int)
index 1b8ca44d1945c50215e460150d71516c10126504..e13c007f9b95c5b56dcde5631c55d104add8a788 100644 (file)
@@ -1,3 +1,4 @@
 cmp
 wc
 paste
+timeout
index 7edfbc968dde6d533d3f77039434b604707ff488..0dc213be93f87cda3d4416713095ffd953f1d499 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 1991-2021,2022 Free Software Foundation, Inc.
+/* Copyright (C) 1991-2023 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    This program is free software; you can redistribute it and/or modify
@@ -1135,7 +1135,7 @@ getconf_builtin (WORD_LIST *list)
     }
 
   list = loptend;
-  if ((aflag == 0 && list == 0) || (aflag && list) || list_length(list) > 2)
+  if ((aflag == 0 && list == 0) || (aflag && list) || list_length((GENERIC_LIST *)list) > 2)
     {
       builtin_usage();
       return (EX_USAGE);
index 7a8618265b0c581d411cfff5cf4e2c3158a6b948..e135d3602b0353076034ccc2270ffa7b6abd65ce 100644 (file)
@@ -570,7 +570,7 @@ execute_command_internal (COMMAND *command, int asynchronous, int pipe_in, int p
   volatile int save_line_number;
 #if defined (PROCESS_SUBSTITUTION)
   volatile int ofifo, nfifo, osize, saved_fifo;
-  volatile void *ofifo_list;
+  void *ofifo_list;            /* void * volatile ofifo_list; */
 #endif
 
   if (breaking || continuing)
@@ -791,7 +791,7 @@ execute_command_internal (COMMAND *command, int asynchronous, int pipe_in, int p
 #if defined (PROCESS_SUBSTITUTION)
       if (saved_fifo)
        {
-         free ((void *)ofifo_list);
+         free (ofifo_list);
           discard_unwind_frame ("internal_fifos");
        }
 #endif
@@ -1104,7 +1104,7 @@ execute_command_internal (COMMAND *command, int asynchronous, int pipe_in, int p
       nfifo = num_fifos ();
       if (nfifo > ofifo)
        close_new_fifos ((void *)ofifo_list, osize);
-      free ((void *)ofifo_list);
+      free (ofifo_list);
       discard_unwind_frame ("internal_fifos");
     }
 #endif
@@ -1340,7 +1340,7 @@ time_command (COMMAND *command, int asynchronous, int pipe_in, int pipe_out, str
 #  if defined (HAVE_STRUCT_TIMEZONE)
   gettimeofday (&before, &dtz);
 #  else
-  gettimeofday (&before, (void *)NULL);
+  gettimeofday (&before, NULL);
 #  endif /* !HAVE_STRUCT_TIMEZONE */
   getrusage (RUSAGE_SELF, &selfb);
   getrusage (RUSAGE_CHILDREN, &kidsb);
@@ -1390,7 +1390,7 @@ time_command (COMMAND *command, int asynchronous, int pipe_in, int pipe_out, str
 #  if defined (HAVE_STRUCT_TIMEZONE)
   gettimeofday (&after, &dtz);
 #  else
-  gettimeofday (&after, (void *)NULL);
+  gettimeofday (&after, NULL);
 #  endif /* !HAVE_STRUCT_TIMEZONE */
   getrusage (RUSAGE_SELF, &selfa);
   getrusage (RUSAGE_CHILDREN, &kidsa);
@@ -3365,7 +3365,7 @@ execute_select_command (SELECT_COM *select_command)
   /* command and arithmetic substitution, parameter and variable expansion,
      word splitting, pathname expansion, and quote removal. */
   list = releaser = expand_words_no_vars (select_command->map_list);
-  list_len = list_length (list);
+  list_len = list_length ((GENERIC_LIST *)list);
   if (list == 0 || list_len == 0)
     {
       if (list)
index a5945060d2d6d087fec7ed91b89e94cf14d877cc..b8b3321e0b4f5e3fc0b3fbeb10800d427ca04be9 100644 (file)
--- a/externs.h
+++ b/externs.h
@@ -152,12 +152,10 @@ extern int locale_decpoint (void);
 /* Declarations for functions defined in list.c. */
 extern void list_walk (GENERIC_LIST *, sh_glist_func_t *);
 extern void wlist_walk (WORD_LIST *, sh_icpfunc_t *);
-/* type punning used to pass arbitrary list types to list_reverse, et al so
-   there are no prototypes. */
-extern GENERIC_LIST *list_reverse ();
-extern int list_length ();
-extern GENERIC_LIST *list_append ();
-extern GENERIC_LIST *list_remove ();   /* unused */
+extern GENERIC_LIST *list_reverse (GENERIC_LIST *);
+extern int list_length (GENERIC_LIST *);
+extern GENERIC_LIST *list_append (GENERIC_LIST *, GENERIC_LIST *);
+extern GENERIC_LIST *list_emove (GENERIC_LIST **, sh_gcp_func_t *, char *); /* unused */
 
 /* Declarations for functions defined in stringlib.c */
 extern int find_string_in_alist (char *, STRING_INT_ALIST *, int);
index 74b97f4297fb8a4630bb3ec7c6a1eb8b7518250c..b92c3e234a977a4a3ffadf4af48a68a33d24f223 100644 (file)
--- a/general.h
+++ b/general.h
 
 #include "xmalloc.h"
 
-/* NULL pointer type. */
-#if !defined (NULL)
-#  if defined (__STDC__)
-#    define NULL ((void *) 0)
-#  else
-#    define NULL 0x0
-#  endif /* !__STDC__ */
-#endif /* !NULL */
-
 /* Hardly used anymore */
 #define pointer_to_int(x)      (int)((char *)x - (char *)0)
 
index 623ad0aa53ccee32d972a64ebb9d2560d3dd24a8..0351ae9d1b2eba6c75248ec5ecc6a88cf255dc71 100644 (file)
--- a/hashlib.h
+++ b/hashlib.h
 #include "stdc.h"
 
 #ifndef PTR_T
-#  ifdef __STDC__
-#    define PTR_T void *
-#  else
-#    define PTR_T char *
-#  endif
+#  define PTR_T void *
 #endif
 
 typedef struct bucket_contents {
@@ -81,12 +77,4 @@ extern unsigned int hash_string (const char *);
 #define HASH_NOSRCH    0x01
 #define HASH_CREATE    0x02
 
-#if !defined (NULL)
-#  if defined (__STDC__)
-#    define NULL ((void *) 0)
-#  else
-#    define NULL 0x0
-#  endif /* !__STDC__ */
-#endif /* !NULL */
-
 #endif /* _HASHLIB_H */
index 7dc2ee0cf4990fb54083c2a7349ca4c93e9f79c8..3cb3e07174a25b79559b18b7fcbf26f3707f2951 100644 (file)
@@ -2,7 +2,7 @@
 /* A minimal stdlib.h containing extern declarations for those functions
    that bash uses. */
 
-/* Copyright (C) 1993 Free Software Foundation, Inc.
+/* Copyright (C) 1993,2023 Free Software Foundation, Inc.
 
    This file is part of GNU Bash, the Bourne Again SHell.
 
@@ -32,13 +32,7 @@ extern double strtod ();
 /* Memory allocation functions. */
 /* Generic pointer type. */
 #ifndef PTR_T
-
-#if defined (__STDC__)
 #  define PTR_T        void *
-#else
-#  define PTR_T char *
-#endif
-
 #endif /* PTR_T */
 
 extern PTR_T malloc ();
index c596c272577432951507e6328e393ac725162cfd..b25c9baa0fbac66643f00e65a7b2d46f205538f8 100644 (file)
@@ -1,6 +1,6 @@
 /* ocache.h -- a minimal object caching implementation. */
 
-/* Copyright (C) 2002 Free Software Foundation, Inc.
+/* Copyright (C) 2002,2023 Free Software Foundation, Inc.
 
    This file is part of GNU Bash, the Bourne Again SHell.
 
 #define        _OCACHE_H_ 1
 
 #ifndef PTR_T
-
-#if defined (__STDC__)
 #  define PTR_T void *
-#else
-#  define PTR_T char *
-#endif
-
 #endif /* PTR_T */
 
 #define OC_MEMSET(memp, xch, nbytes)                                   \
index d56df611387df0e8ca6eba5d33e342e2ea748d69..def15655d401a6b701d9dab057da2e2ea0205736 100644 (file)
@@ -1,7 +1,7 @@
 /* collsyms.h -- collating symbol names and their corresponding characters
                 (in ascii) as given by POSIX.2 in table 2.8. */
 
-/* Copyright (C) 1997-2002 Free Software Foundation, Inc.
+/* Copyright (C) 1997-2002,2023 Free Software Foundation, Inc.
 
    This file is part of GNU Bash, the Bourne Again SHell.
 
@@ -38,11 +38,7 @@ static __COLLSYM POSIXCOLL [] =
        {  L("EOT"),                    L('\004') },
        {  L("ENQ"),                    L('\005') },
        {  L("ACK"),                    L('\006') },
-#ifdef __STDC__
        {  L("alert"),                  L('\a') },
-#else
-       {  L("alert"),                  L('\007') },
-#endif
        {  L("BS"),                     L('\010') },
        {  L("backspace"),              L('\b') },
        {  L("HT"),                     L('\011') },
index f9a73b8092f018abec8876b4466ea372839e4342..c8027aadc39bc0fbc9e9075c27a2d90967ec9122 100644 (file)
 #  define bcopy(s, d, n) ((void) memcpy ((d), (s), (n)))
 #endif /* !HAVE_BCOPY && !bcopy */
 
-#if !defined (NULL)
-#  if defined (__STDC__)
-#    define NULL ((void *) 0)
-#  else
-#    define NULL 0x0
-#  endif /* __STDC__ */
-#endif /* !NULL */
-
 #if !defined (FREE)
 #  define FREE(x)      if (x) free (x)
 #endif
index 9c2b099041de22e61b8404faae065f3aaa92a4d4..49e1bf294b31b39d105b17d4f047d1bbf9ad34db 100644 (file)
@@ -36,11 +36,7 @@ extern "C" {
 #  include <readline/rltypedefs.h>
 #endif
 
-#ifdef __STDC__
 typedef void *histdata_t;
-#else
-typedef char *histdata_t;
-#endif
 
 /* Let's not step on anyone else's define for now, since we don't use this yet. */
 #ifndef HS_HISTORY_VERSION
index 0fb9df9c00b4973c61da6f53a5519505e4dc6ae5..c8198044eebff764f620249cbeb1111c0802eebd 100644 (file)
@@ -1,6 +1,6 @@
 /* xmalloc.h -- memory allocation that aborts on errors. */
 
-/* Copyright (C) 1999-2009,2010-2021 Free Software Foundation, Inc.
+/* Copyright (C) 1999-2009,2010-2023 Free Software Foundation, Inc.
 
    This file is part of the GNU Readline Library (Readline), a library
    for reading lines of text with interactive input and history editing.      
 #endif
 
 #ifndef PTR_T
-
-#ifdef __STDC__
-#  define PTR_T        void *
-#else
-#  define PTR_T        char *
-#endif
-
+#  define PTR_T void *
 #endif /* !PTR_T */
 
 extern PTR_T xmalloc (size_t);
index 61e41bf3358f498265cca196a0204d0513c2f159..2e61287f7a9507a08311fda7a41ed32be547f6b8 100644 (file)
@@ -253,7 +253,7 @@ strlist_from_word_list (WORD_LIST *list, int alloc, int starting_index, int *ip)
         *ip = 0;
       return ((STRINGLIST *)0);
     }
-  slen = list_length (list);
+  slen = list_length ((GENERIC_LIST *)list);
   ret = (STRINGLIST *)xmalloc (sizeof (STRINGLIST));
   ret->list = strvec_from_word_list (list, alloc, starting_index, &len);
   ret->list_size = slen + starting_index;
index 2e71fe5dda43f788eb90fe4365b7b84c26317bb3..bf57d6889f3503dbf2ae905481b5e17a46f824fc 100644 (file)
@@ -206,7 +206,7 @@ strvec_from_word_list (WORD_LIST *list, int alloc, int starting_index, int *ip)
   int count;
   char **array;
 
-  count = list_length (list);
+  count = list_length ((GENERIC_LIST *)list);
   array = (char **)xmalloc ((1 + count + starting_index) * sizeof (char *));
 
   for (count = 0; count < starting_index; count++)
index daff6a5f080e11f977236a39c79238cfcd8bae3d..d3b27f3bff49b09267e768cc44b5524281b88d37 100644 (file)
@@ -95,13 +95,8 @@ ansicstr (const char *string, size_t len, int flags, int *sawc, size_t *rlen)
        {
          switch (c = *s++)
            {
-#if defined (__STDC__)
            case 'a': c = '\a'; break;
            case 'v': c = '\v'; break;
-#else
-           case 'a': c = (int) 0x07; break;
-           case 'v': c = (int) 0x0B; break;
-#endif
            case 'b': c = '\b'; break;
            case 'e': case 'E':         /* ESC -- non-ANSI */
              c = ESC; break;
@@ -257,14 +252,8 @@ ansic_quote (const char *str, int flags, int *rlen)
       switch (c)
        {
        case ESC: c = 'E'; break;
-#ifdef __STDC__
        case '\a': c = 'a'; break;
        case '\v': c = 'v'; break;
-#else
-       case 0x07: c = 'a'; break;
-       case 0x0b: c = 'v'; break;
-#endif
-
        case '\b': c = 'b'; break;
        case '\f': c = 'f'; break;
        case '\n': c = 'n'; break;
index c31f921393441c5ab8063c08cb73435869a2c29a..734b2084621e57f23aca9262ea4dcbd15772d100 100644 (file)
@@ -68,14 +68,6 @@ extern struct passwd *getpwnam (const char *);
 #define savestring(x) strcpy ((char *)xmalloc (1 + strlen (x)), (x))
 #endif /* !savestring */
 
-#if !defined (NULL)
-#  if defined (__STDC__)
-#    define NULL ((void *) 0)
-#  else
-#    define NULL 0x0
-#  endif /* !__STDC__ */
-#endif /* !NULL */
-
 /* If being compiled as part of bash, these will be satisfied from
    variables.o.  If being compiled as part of readline, they will
    be satisfied from shell.o. */
index effbe7c8ff30acac0f54f06bb5ca5ab164363b90..dfc46fa55e8d19cc72416c3a07a82ac21fd6deac 100644 (file)
@@ -2,7 +2,7 @@
  * mksyntax.c - construct shell syntax table for fast char attribute lookup.
  */
 
-/* Copyright (C) 2000-2009,2012,2022 Free Software Foundation, Inc.
+/* Copyright (C) 2000-2009,2012,2022-2023 Free Software Foundation, Inc.
 
    This file is part of GNU Bash, the Bourne Again SHell.
 
@@ -128,13 +128,8 @@ cdesc (int i)
     
   switch (i)
     {
-#ifdef __STDC__
     case '\a': xbuf[1] = 'a'; break;
     case '\v': xbuf[1] = 'v'; break;
-#else
-    case '\007': xbuf[1] = 'a'; break;
-    case 0x0B: xbuf[1] = 'v'; break;
-#endif
     case '\b': xbuf[1] = 'b'; break;
     case '\f': xbuf[1] = 'f'; break;
     case '\n': xbuf[1] = 'n'; break;
index 9eb20eaee2ca05e6da9746c59c9bfa02929ae1b5..ecdf4475efd9d30b12cf2c10a2333690c38d2801 100644 (file)
@@ -893,7 +893,7 @@ gen_wordlist_matches (COMPSPEC *cs, const char *text)
   l2 = expand_words_shellexp (l);
   dispose_words (l);
 
-  nw = list_length (l2);
+  nw = list_length ((GENERIC_LIST *)l2);
   sl = strlist_create (nw + 1);
 
   ntxt = bash_dequote_text (text);
diff --git a/subst.c b/subst.c
index a2b719ffedda93ff144c5433f984b10455d1af89..2ff9b7c2614e1885efcae92ea0ed65bca50fb273 100644 (file)
--- a/subst.c
+++ b/subst.c
@@ -12053,7 +12053,7 @@ glob_expand_word_list (WORD_LIST *tlist, int eflags)
 
          if (glob_list)
            {
-             output_list = (WORD_LIST *)list_append (glob_list, output_list);
+             output_list = (WORD_LIST *)list_append ((GENERIC_LIST *)glob_list, (GENERIC_LIST *)output_list);
              PREPEND_LIST (tlist, disposables);
            }
          else if (fail_glob_expansion != 0)
@@ -12532,7 +12532,7 @@ shell_expand_word_list (WORD_LIST *tlist, int eflags)
        }
 
       expanded = REVERSE_LIST (temp_list, WORD_LIST *);
-      new_list = (WORD_LIST *)list_append (expanded, new_list);
+      new_list = (WORD_LIST *)list_append ((GENERIC_LIST *)expanded, (GENERIC_LIST *)new_list);
     }
 
   if (orig_list)  
index 27f5b1259506dc7cae52bcc7db69a725a30591c1..3163908702d3aadb8fbae19af3bd1308e8ae8b1c 100644 (file)
@@ -1,6 +1,6 @@
 /* endian.c -- A trick for determining the byte order of a machine. */
 
-/* Copyright (C) 1993 Free Software Foundation, Inc.
+/* Copyright (C) 1993,2023 Free Software Foundation, Inc.
 
    This file is part of GNU Bash, the Bourne Again SHell.
 
@@ -42,11 +42,7 @@ char nstring[9];
 int
 main (int argc, char **argv)
 {
-#if defined (__STDC__)
   register size_t i;
-#else
-  register int i;
-#endif /* !__STDC__ */
   FILE *stream = (FILE *)NULL;
   char *stream_name = "stdout";
   union {
index 82e576fc0524f598b9227cd08e440eb52298b62a..059b074e7da14d5da0d1ffd01a27221bd10c40d6 100644 (file)
@@ -301,23 +301,23 @@ $ 1
 $ 2
 $ 3
 $ exit
-       6
+6
 $ 1
 $ 2
 $ 3
 $ 4
 $ 5
 $ exit
-       6
+6
 $ 1
 $ 2
 $ exit
-       4
+4
 $ e 1
 $ e 2
 $ e 3
 $ exit
-       3
+3
 e 1
 e 2
 e 3
@@ -327,17 +327,17 @@ $ x 3
 $ x 4
 $ x 5
 $ exit
-       3
+3
 x 3
 x 4
 x 5
 $ y 1
 $ y 2
 $ exit
-       2
+2
 y 1
 y 2
 $ 1
 $ 2
 $ exit
-       0
+0
index ad651a03c15bae5e750e87bf5ade62f89d558f9c..9c9d27b4b5a9e07aa7d0aa04e5232b33c3161620 100644 (file)
@@ -12,6 +12,7 @@
 #   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 # test history file truncation for various values of $HISTFILESIZE
+. ./test-glue-functions
 
 : ${THIS_SH:=./bash}
 : ${TMPDIR:=/var/tmp}
@@ -25,19 +26,19 @@ rm -f $HISTFILE
 
 # exactly the number of lines
 ${THIS_SH} --norc -in <<<$'1\n2\n3' 
-wc -l < $HISTFILE
+wc -l < $HISTFILE | _cut_leading_spaces
 
 rm -f $HISTFILE
 
 # truncating to fewer lines
 ${THIS_SH} --norc -in <<<$'1\n2\n3\n4\n5' 
-wc -l < $HISTFILE
+wc -l < $HISTFILE | _cut_leading_spaces
 
 rm -f $HISTFILE
 
 # the history file contains fewer lines than $HISTFILESIZE
 ${THIS_SH} --norc -in <<<$'1\n2' 
-wc -l < $HISTFILE
+wc -l < $HISTFILE | _cut_leading_spaces
 
 rm -f $HISTFILE
 
@@ -46,21 +47,21 @@ unset HISTTIMEFORMAT
 
 # exactly the number of lines
 ${THIS_SH} --norc -in <<<$'e 1\ne 2\ne 3' 
-wc -l < $HISTFILE
+wc -l < $HISTFILE | _cut_leading_spaces
 cat $HISTFILE
 
 rm -f $HISTFILE
 
 # truncating to fewer lines
 ${THIS_SH} --norc -in <<<$'x 1\nx 2\nx 3\nx 4\nx 5' 
-wc -l < $HISTFILE
+wc -l < $HISTFILE | _cut_leading_spaces
 cat $HISTFILE
 
 rm -f $HISTFILE
 
 # the history file contains fewer lines than $HISTFILESIZE
 ${THIS_SH} --norc -in <<<$'y 1\ny 2' 
-wc -l < $HISTFILE
+wc -l < $HISTFILE | _cut_leading_spaces
 cat $HISTFILE
 
 rm -f $HISTFILE
@@ -68,6 +69,6 @@ rm -f $HISTFILE
 # we want to truncate the history file to zero length
 HISTFILESIZE=0
 ${THIS_SH} --norc -in <<<$'1\n2' 
-wc -l < $HISTFILE
+wc -l < $HISTFILE | _cut_leading_spaces
 
 rm -f $HISTFILE
index aee8a492bdb571aa6ad27491172d003bda9e6303..3777d53de19b5a76d305415367b6752a2240b5c2 100644 (file)
--- a/xmalloc.c
+++ b/xmalloc.c
@@ -1,6 +1,6 @@
 /* xmalloc.c -- safe versions of malloc and realloc */
 
-/* Copyright (C) 1991-2016 Free Software Foundation, Inc.
+/* Copyright (C) 1991-2016,2023 Free Software Foundation, Inc.
 
    This file is part of GNU Bash, the GNU Bourne Again SHell.
 
 #include "bashintl.h"
 
 #if !defined (PTR_T)
-#  if defined (__STDC__)
-#    define PTR_T void *
-#  else
-#    define PTR_T char *
-#  endif /* !__STDC__ */
+#  define PTR_T void *
 #endif /* !PTR_T */
 
 #if HAVE_SBRK && !HAVE_DECL_SBRK
index 84edcc31a3e4d7ecfe410f6d905be9e627dcc6ef..d8731305f02e7fc0cbb7410e016336e4bb2d6107 100644 (file)
--- a/xmalloc.h
+++ b/xmalloc.h
@@ -1,6 +1,6 @@
 /* xmalloc.h -- defines for the `x' memory allocation functions */
 
-/* Copyright (C) 2001-2022 Free Software Foundation, Inc.
+/* Copyright (C) 2001-2023 Free Software Foundation, Inc.
 
    This file is part of GNU Bash, the Bourne Again SHell.
 
 
 /* Generic pointer type. */
 #ifndef PTR_T
-
-#if defined (__STDC__)
 #  define PTR_T        void *
-#else
-#  define PTR_T char *
-#endif
-
 #endif /* PTR_T */
 
 /* Allocation functions in xmalloc.c */