]> git.ipfire.org Git - thirdparty/bash.git/commitdiff
commit bash-20121130 snapshot
authorChet Ramey <chet.ramey@case.edu>
Fri, 7 Dec 2012 16:07:50 +0000 (11:07 -0500)
committerChet Ramey <chet.ramey@case.edu>
Fri, 7 Dec 2012 16:07:50 +0000 (11:07 -0500)
25 files changed:
CWRU/CWRU.chlog
CWRU/CWRU.chlog~
builtins/declare.def
builtins/mapfile.def
builtins/read.def
builtins/read.def~ [new file with mode: 0644]
builtins/wait.def
doc/bash.1
doc/bashref.texi
doc/version.texi
examples/loadables/sleep.c
execute_cmd.c
jobs.c
jobs.h
lib/malloc/table.c
redir.c
subst.c
subst.c~
test.c
test.c~ [new file with mode: 0644]
tests/dollar-star6.sub
tests/dollar-star6.sub~ [new file with mode: 0644]
tests/vredir.right
trap.c
variables.c~

index 5bfb07f4cadb8fcff3514c4a754ae4c33bb4317d..4d4978ea8eb3ea2246bd7acbfc46da12e5612284 100644 (file)
@@ -3890,3 +3890,78 @@ lib/malloc/{malloc.c,imalloc.h}
 lib/readline/bind.c
        - sv_histsize: if argument evaluates to a value < 0, unstifle the
          history
+
+                                  11/22
+                                  -----
+redir.c
+       - do_redirection_internal: if we have REDIR_VARASSIGN set in the
+         redirection flags and we set up `redirector' using fcntl or dup2,
+         don't add a redirect to make sure it stays open.  Let the
+         script programmer manage the file handle.  Fixes bug reported by
+         Sam Liddicott <sam@liddicott.com>
+
+                                  11/24
+                                  -----
+jobs.c
+       - wait_for_any_job: new function, waits for an unspecified background
+         job to exit and returns its exit status.  Returns -1 on no background
+         jobs or no children or other errors.  Calls wait_for with new
+         sentinel value ANY_PID
+       - wait_for: changes to handle argument of ANY_PID: don't look up or
+         try to modify the child struct, only go through the wait loop once.
+         Return -1 if waitpid returns no children
+
+jobs.h
+       - ANY_PID: new define
+
+builtins/wait.def
+       - new option: -n. Means to wait for the next job and return its exit
+         status.  Returns 127 if there are no background jobs (or no
+         children).  Feature most recently requested by Elliott Forney
+         <idfah@cs.colostate.edu>
+
+doc/{bash.1,bashref.texi}
+       - document new `wait -n' option
+
+execute_cmd.c
+       - execute_command_internal: save make_command_string () result in a
+         temp variable before calling savestring() on it; avoids evaluating
+         make_command_string() result twice.  Fix from John E. Malmberg
+         <wb8tyw@qsl.net>
+
+                                  11/28
+                                  -----
+
+builtins/declare.def
+       - declare_internal: if an array variable is declared using `declare -a'
+         or `declare -A', but not assigned a value, set the `invisible'
+         attribute so the variable does not show up as set.  Fix for bug
+         about variable initialization reported by Tim Friske <me@timfriske.com>
+
+builtins/{mapfile,read}.def
+       - after calling find_or_make_array_variable, make sure the invisible
+         flag is turned off, in case the variable was declared previously
+         using `declare -a' or `declare -A'.  Side effect of above change to
+         declare_internal
+
+subst.c
+       - shell_expand_word_list: handle the W_ASSNGLOBAL flag and put -g into
+         the list of options passed to make_internal_declare as appropriate.
+         Fix for bug reported by Tim Friske <me@timfriske.com>
+
+                                  11/30
+                                  -----
+test.c
+       - unary_op: make sure -v and -n check that the variable is not marked
+         as invisible before calling var_isset.  Fix for bug reported by Tim
+         Friske <me@timfriske.com>
+
+                                  12/2
+                                  ----
+subst.c
+       - process_substitute: turn off the `expanding_redir' flag, which
+         controls whether or not variables.c:find_variable_internal uses the
+         temporary environment to find variables.  We want to use the
+         temp environment, since we don't have to worry about order of
+         evaluation in a subshell.  Fixes bug reported by Andrey Borzenkov
+         <arvidjaar@gmail.com>
index f2d1e4b77789f03c0452de11da2da290d71bf526..1c37a2354c28e01311b9b5a402fceae72ce58201 100644 (file)
@@ -3827,3 +3827,131 @@ execute_cmd.c
          last element of a pipeline (or not in a pipeline), rather than for
          every child.  Fixes difference in behavior between /dev/fd and
          FIFOs reported by Zev Weiss <zev@bewilderbeest.net>
+       - execute_null_command: do the same thing in the parent branch after
+         make_child
+
+                                  11/14
+                                  -----
+subst.c
+       - parameter_brace_expand: a variable is null if it's special ($@, $*),
+         the expansion occurs within double quotes, and the expansion turns
+         into a quoted null.  Fixes debian bug 692447 reported by
+         Matrosov Dmitriy <sgf.dma@gmail.com>
+
+jobs.c
+       - run_sigchld_trap: make sure `running_trap' sentinel is set
+         appropriately
+       - waitchld: only run the sigchld trap if we're not in a signal
+         handler, not running a trap, and executing the wait builtin.
+         Otherwise, queue for later handling.  We still run one instance
+         of the trap handler per exited child.  Bulk of fix for bug
+         reported by Elliott Forney <idfah@cs.colostate.edu>
+
+trap.c
+       - queue_sigchld_trap: set catch_flag so run_pending_traps notices,
+         and set trapped_signal_received for completeness.  Rest of fix
+         for bug reported by Elliott Forney <idfah@cs.colostate.edu>
+
+lib/malloc/malloc.c
+       - block_signals: renamed to _malloc_block_signals, made public
+       - unblock_signals: renamed to _malloc_unblock_signals, made public
+
+lib/malloc/imalloc.h
+       - extern declarations for _malloc_{un,}block_signals
+
+lib/malloc/table.c
+       - mregister_alloc, mregister_free: block signals around table
+         manipulation
+
+                                  11/15
+                                  -----
+trap.c
+       - run_pending_traps: set SIG_INPROGRESS flag around calls to
+         run_sigchld_handler so other parts of the shell know that the
+         SIGCHLD trap handler is executing
+       - run_pending_traps: if we get a situation where we are looking at
+         running a SIGCHLD trap but the trap string is IMPOSSIBLE_TRAP_HANDLER
+         and the SIG_INPROGRESS flag is set, just skip it.  This is possible
+         if run_pending_traps is called from a SIGCHLD trap handler run by
+         run_sigchld_trap
+
+doc/bash.1,lib/readline/doc/{rluser.texi,readline.3}
+       - corrected description of the effect of `set history-size 0'.  Report
+         from Vesa-Matti J Kari <vmkari@cc.helsinki.fi>
+
+include/stdc.h
+       - CPP_STRING: new define, replaces __STRING
+
+lib/malloc/{malloc.c,imalloc.h}
+       - replace __STRING with CPP_STRING
+
+                                  11/16
+                                  -----
+lib/readline/bind.c
+       - sv_histsize: if argument evaluates to a value < 0, unstifle the
+         history
+
+                                  11/22
+                                  -----
+redir.c
+       - do_redirection_internal: if we have REDIR_VARASSIGN set in the
+         redirection flags and we set up `redirector' using fcntl or dup2,
+         don't add a redirect to make sure it stays open.  Let the
+         script programmer manage the file handle.  Fixes bug reported by
+         Sam Liddicott <sam@liddicott.com>
+
+                                  11/24
+                                  -----
+jobs.c
+       - wait_for_any_job: new function, waits for an unspecified background
+         job to exit and returns its exit status.  Returns -1 on no background
+         jobs or no children or other errors.  Calls wait_for with new
+         sentinel value ANY_PID
+       - wait_for: changes to handle argument of ANY_PID: don't look up or
+         try to modify the child struct, only go through the wait loop once.
+         Return -1 if waitpid returns no children
+
+jobs.h
+       - ANY_PID: new define
+
+builtins/wait.def
+       - new option: -n. Means to wait for the next job and return its exit
+         status.  Returns 127 if there are no background jobs (or no
+         children).  Feature most recently requested by Elliott Forney
+         <idfah@cs.colostate.edu>
+
+doc/{bash.1,bashref.texi}
+       - document new `wait -n' option
+
+execute_cmd.c
+       - execute_command_internal: save make_command_string () result in a
+         temp variable before calling savestring() on it; avoids evaluating
+         make_command_string() result twice.  Fix from John E. Malmberg
+         <wb8tyw@qsl.net>
+
+                                  11/28
+                                  -----
+
+builtins/declare.def
+       - declare_internal: if an array variable is declared using `declare -a'
+         or `declare -A', but not assigned a value, set the `invisible'
+         attribute so the variable does not show up as set.  Fix for bug
+         about variable initialization reported by Tim Friske <me@timfriske.com>
+
+builtins/{mapfile,read}.def
+       - after calling find_or_make_array_variable, make sure the invisible
+         flag is turned off, in case the variable was declared previously
+         using `declare -a' or `declare -A'.  Side effect of above change to
+         declare_internal
+
+subst.c
+       - shell_expand_word_list: handle the W_ASSNGLOBAL flag and put -g into
+         the list of options passed to make_internal_declare as appropriate.
+         Fix for bug reported by Tim Friske <me@timfriske.com>
+
+                                  11/30
+                                  -----
+test.c
+       - unary_op: make sure -v and -n check that the variable is not marked
+         as invisible before calling var_isset.  Fix for bug reported by Tim
+         Friske <me@timfriske.com>
index 2fec6066f116f5897b5b255f3ce5d59a2b783f8e..260c947af3f8dd5d010cae3cf49930f16364b48d 100644 (file)
@@ -497,9 +497,17 @@ declare_internal (list, local_var)
            {
 #if defined (ARRAY_VARS)
              if (flags_on & att_assoc)
-               var = make_new_assoc_variable (name);
+               {
+                 var = make_new_assoc_variable (name);
+                 if (offset == 0)
+                   VSETATTR (var, att_invisible);
+               }
              else if ((flags_on & att_array) || making_array_special)
-               var = make_new_array_variable (name);
+               {
+                 var = make_new_array_variable (name);
+                 if (offset == 0)
+                   VSETATTR (var, att_invisible);
+               }
              else
 #endif
 
index c11a0dea2c654dcd97c876c54f1b038f719af363..8de8a873fd1dbdad45139487398f8ca65d2a840e 100644 (file)
@@ -172,6 +172,8 @@ mapfile (fd, line_count_goal, origin, nskip, callback_quantum, callback, array_n
       builtin_error (_("%s: not an indexed array"), array_name);
       return (EXECUTION_FAILURE);
     }
+  else if (invisible_p (entry))
+    VUNSETATTR (entry, att_invisible); /* no longer invisible */
       
   if (flags & MAPF_CLEARARRAY)
     array_flush (array_cell (entry));
index 18fdf81d2d17328482a51f8f44094edeb9f09832..b6aed3115e200e7f1ab41aefd65a5ded63577bb5 100644 (file)
@@ -710,6 +710,8 @@ assign_vars:
          xfree (input_string);
          return EXECUTION_FAILURE;     /* existing associative array */
        }
+      else if (invisible_p (var))
+       VUNSETATTR (var, att_invisible);
       array_flush (array_cell (var));
 
       alist = list_string (input_string, ifs_chars, 0);
@@ -881,6 +883,7 @@ bind_read_variable (name, value)
      char *name, *value;
 {
   SHELL_VAR *v;
+
 #if defined (ARRAY_VARS)
   if (valid_array_reference (name) == 0)
     v = bind_variable (name, value, 0);
diff --git a/builtins/read.def~ b/builtins/read.def~
new file mode 100644 (file)
index 0000000..026899f
--- /dev/null
@@ -0,0 +1,1068 @@
+This file is read.def, from which is created read.c.
+It implements the builtin "read" in Bash.
+
+Copyright (C) 1987-2012 Free Software Foundation, Inc.
+
+This file is part of GNU Bash, the Bourne Again SHell.
+
+Bash is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Bash is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Bash.  If not, see <http://www.gnu.org/licenses/>.
+
+$PRODUCES read.c
+
+$BUILTIN read
+$FUNCTION read_builtin
+$SHORT_DOC read [-ers] [-a array] [-d delim] [-i text] [-n nchars] [-N nchars] [-p prompt] [-t timeout] [-u fd] [name ...]
+Read a line from the standard input and split it into fields.
+
+Reads a single line from the standard input, or from file descriptor FD
+if the -u option is supplied.  The line is split into fields as with word
+splitting, and the first word is assigned to the first NAME, the second
+word to the second NAME, and so on, with any leftover words assigned to
+the last NAME.  Only the characters found in $IFS are recognized as word
+delimiters.
+
+If no NAMEs are supplied, the line read is stored in the REPLY variable.
+
+Options:
+  -a array     assign the words read to sequential indices of the array
+               variable ARRAY, starting at zero
+  -d delim     continue until the first character of DELIM is read, rather
+               than newline
+  -e           use Readline to obtain the line in an interactive shell
+  -i text      Use TEXT as the initial text for Readline
+  -n nchars    return after reading NCHARS characters rather than waiting
+               for a newline, but honor a delimiter if fewer than NCHARS
+               characters are read before the delimiter
+  -N nchars    return only after reading exactly NCHARS characters, unless
+               EOF is encountered or read times out, ignoring any delimiter
+  -p prompt    output the string PROMPT without a trailing newline before
+               attempting to read
+  -r           do not allow backslashes to escape any characters
+  -s           do not echo input coming from a terminal
+  -t timeout   time out and return failure if a complete line of input is
+               not read within TIMEOUT seconds.  The value of the TMOUT
+               variable is the default timeout.  TIMEOUT may be a
+               fractional number.  If TIMEOUT is 0, read returns immediately,
+               without trying to read any data, returning success only if
+               input is available on the specified file descriptor.  The
+               exit status is greater than 128 if the timeout is exceeded
+  -u fd                read from file descriptor FD instead of the standard input
+
+Exit Status:
+The return code is zero, unless end-of-file is encountered, read times out
+(in which case it's greater than 128), a variable assignment error occurs,
+or an invalid file descriptor is supplied as the argument to -u.
+$END
+
+#include <config.h>
+
+#include "bashtypes.h"
+#include "posixstat.h"
+
+#include <stdio.h>
+
+#include "bashansi.h"
+
+#if defined (HAVE_UNISTD_H)
+#  include <unistd.h>
+#endif
+
+#include <signal.h>
+#include <errno.h>
+
+#ifdef __CYGWIN__
+#  include <fcntl.h>
+#  include <io.h>
+#endif
+
+#include "../bashintl.h"
+
+#include "../shell.h"
+#include "common.h"
+#include "bashgetopt.h"
+
+#include <shtty.h>
+
+#if defined (READLINE)
+#include "../bashline.h"
+#include <readline/readline.h>
+#endif
+
+#if defined (BUFFERED_INPUT)
+#  include "input.h"
+#endif
+
+#include "shmbutil.h"
+
+#if !defined(errno)
+extern int errno;
+#endif
+
+extern void run_pending_traps __P((void));
+
+extern int posixly_correct;
+extern int trapped_signal_received;
+
+struct ttsave
+{
+  int fd;
+  TTYSTRUCT *attrs;
+};
+
+#if defined (READLINE)
+static void reset_attempted_completion_function __P((char *));
+static int set_itext __P((void));
+static char *edit_line __P((char *, char *));
+static void set_eol_delim __P((int));
+static void reset_eol_delim __P((char *));
+#endif
+static SHELL_VAR *bind_read_variable __P((char *, char *));
+#if defined (HANDLE_MULTIBYTE)
+static int read_mbchar __P((int, char *, int, int, int));
+#endif
+static void ttyrestore __P((struct ttsave *));
+
+static sighandler sigalrm __P((int));
+static void reset_alarm __P((void));
+
+static procenv_t alrmbuf;
+static int sigalrm_seen, reading;
+static SigHandler *old_alrm;
+static unsigned char delim;
+
+/* In most cases, SIGALRM just sets a flag that we check periodically.  This
+   avoids problems with the semi-tricky stuff we do with the xfree of
+   input_string at the top of the unwind-protect list (see below). */
+#define CHECK_ALRM \
+  do { \
+    if (sigalrm_seen) \
+      longjmp (alrmbuf, 1); \
+  } while (0)
+
+static sighandler
+sigalrm (s)
+     int s;
+{
+  sigalrm_seen = 1;
+  if (reading)         /* do the longjmp if we get SIGALRM while in read() */
+    longjmp (alrmbuf, 1);
+}
+
+static void
+reset_alarm ()
+{
+  set_signal_handler (SIGALRM, old_alrm);
+  falarm (0, 0);
+}
+
+/* Read the value of the shell variables whose names follow.
+   The reading is done from the current input stream, whatever
+   that may be.  Successive words of the input line are assigned
+   to the variables mentioned in LIST.  The last variable in LIST
+   gets the remainder of the words on the line.  If no variables
+   are mentioned in LIST, then the default variable is $REPLY. */
+int
+read_builtin (list)
+     WORD_LIST *list;
+{
+  register char *varname;
+  int size, i, nr, pass_next, saw_escape, eof, opt, retval, code, print_ps2;
+  int input_is_tty, input_is_pipe, unbuffered_read, skip_ctlesc, skip_ctlnul;
+  int raw, edit, nchars, silent, have_timeout, ignore_delim, fd, lastsig, t_errno;
+  unsigned int tmsec, tmusec;
+  long ival, uval;
+  intmax_t intval;
+  char c;
+  char *input_string, *orig_input_string, *ifs_chars, *prompt, *arrayname;
+  char *e, *t, *t1, *ps2, *tofree;
+  struct stat tsb;
+  SHELL_VAR *var;
+  TTYSTRUCT ttattrs, ttset;
+  struct ttsave termsave;
+#if defined (ARRAY_VARS)
+  WORD_LIST *alist;
+#endif
+#if defined (READLINE)
+  char *rlbuf, *itext;
+  int rlind;
+#endif
+
+  USE_VAR(size);
+  USE_VAR(i);
+  USE_VAR(pass_next);
+  USE_VAR(print_ps2);
+  USE_VAR(saw_escape);
+  USE_VAR(input_is_pipe);
+/*  USE_VAR(raw); */
+  USE_VAR(edit);
+  USE_VAR(tmsec);
+  USE_VAR(tmusec);
+  USE_VAR(nchars);
+  USE_VAR(silent);
+  USE_VAR(ifs_chars);
+  USE_VAR(prompt);
+  USE_VAR(arrayname);
+#if defined (READLINE)
+  USE_VAR(rlbuf);
+  USE_VAR(rlind);
+  USE_VAR(itext);
+#endif
+  USE_VAR(list);
+  USE_VAR(ps2);
+  USE_VAR(lastsig);
+
+  sigalrm_seen = reading = 0;
+
+  i = 0;               /* Index into the string that we are reading. */
+  raw = edit = 0;      /* Not reading raw input by default. */
+  silent = 0;
+  arrayname = prompt = (char *)NULL;
+  fd = 0;              /* file descriptor to read from */
+
+#if defined (READLINE)
+  rlbuf = itext = (char *)0;
+  rlind = 0;
+#endif
+
+  tmsec = tmusec = 0;          /* no timeout */
+  nr = nchars = input_is_tty = input_is_pipe = unbuffered_read = have_timeout = 0;
+  delim = '\n';                /* read until newline */
+  ignore_delim = 0;
+
+  reset_internal_getopt ();
+  while ((opt = internal_getopt (list, "ersa:d:i:n:p:t:u:N:")) != -1)
+    {
+      switch (opt)
+       {
+       case 'r':
+         raw = 1;
+         break;
+       case 'p':
+         prompt = list_optarg;
+         break;
+       case 's':
+         silent = 1;
+         break;
+       case 'e':
+#if defined (READLINE)
+         edit = 1;
+#endif
+         break;
+       case 'i':
+#if defined (READLINE)
+         itext = list_optarg;
+#endif
+         break;
+#if defined (ARRAY_VARS)
+       case 'a':
+         arrayname = list_optarg;
+         break;
+#endif
+       case 't':
+         code = uconvert (list_optarg, &ival, &uval);
+         if (code == 0 || ival < 0 || uval < 0)
+           {
+             builtin_error (_("%s: invalid timeout specification"), list_optarg);
+             return (EXECUTION_FAILURE);
+           }
+         else
+           {
+             have_timeout = 1;
+             tmsec = ival;
+             tmusec = uval;
+           }
+         break;
+       case 'N':
+         ignore_delim = 1;
+         delim = -1;
+       case 'n':
+         code = legal_number (list_optarg, &intval);
+         if (code == 0 || intval < 0 || intval != (int)intval)
+           {
+             sh_invalidnum (list_optarg);
+             return (EXECUTION_FAILURE);
+           }
+         else
+           nchars = intval;
+         break;
+       case 'u':
+         code = legal_number (list_optarg, &intval);
+         if (code == 0 || intval < 0 || intval != (int)intval)
+           {
+             builtin_error (_("%s: invalid file descriptor specification"), list_optarg);
+             return (EXECUTION_FAILURE);
+           }
+         else
+           fd = intval;
+         if (sh_validfd (fd) == 0)
+           {
+             builtin_error (_("%d: invalid file descriptor: %s"), fd, strerror (errno));
+             return (EXECUTION_FAILURE);
+           }
+         break;
+       case 'd':
+         delim = *list_optarg;
+         break;
+       default:
+         builtin_usage ();
+         return (EX_USAGE);
+       }
+    }
+  list = loptend;
+
+  /* `read -t 0 var' tests whether input is available with select/FIONREAD,
+     and fails if those are unavailable */
+  if (have_timeout && tmsec == 0 && tmusec == 0)
+#if 0
+    return (EXECUTION_FAILURE);
+#else
+    return (input_avail (fd) ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
+#endif
+
+  /* If we're asked to ignore the delimiter, make sure we do. */
+  if (ignore_delim)
+    delim = -1;
+
+  /* IF IFS is unset, we use the default of " \t\n". */
+  ifs_chars = getifs ();
+  if (ifs_chars == 0)          /* XXX - shouldn't happen */
+    ifs_chars = "";
+  /* If we want to read exactly NCHARS chars, don't split on IFS */
+  if (ignore_delim)
+    ifs_chars = "";
+  for (skip_ctlesc = skip_ctlnul = 0, e = ifs_chars; *e; e++)
+    skip_ctlesc |= *e == CTLESC, skip_ctlnul |= *e == CTLNUL;
+
+  input_string = (char *)xmalloc (size = 112); /* XXX was 128 */
+  input_string[0] = '\0';
+
+  /* $TMOUT, if set, is the default timeout for read. */
+  if (have_timeout == 0 && (e = get_string_value ("TMOUT")))
+    {
+      code = uconvert (e, &ival, &uval);
+      if (code == 0 || ival < 0 || uval < 0)
+       tmsec = tmusec = 0;
+      else
+       {
+         tmsec = ival;
+         tmusec = uval;
+       }
+    }
+
+  begin_unwind_frame ("read_builtin");
+
+#if defined (BUFFERED_INPUT)
+  if (interactive == 0 && default_buffered_input >= 0 && fd_is_bash_input (fd))
+    sync_buffered_stream (default_buffered_input);
+#endif
+
+  input_is_tty = isatty (fd);
+  if (input_is_tty == 0)
+#ifndef __CYGWIN__
+    input_is_pipe = (lseek (fd, 0L, SEEK_CUR) < 0) && (errno == ESPIPE);
+#else
+    input_is_pipe = 1;
+#endif
+
+  /* If the -p, -e or -s flags were given, but input is not coming from the
+     terminal, turn them off. */
+  if ((prompt || edit || silent) && input_is_tty == 0)
+    {
+      prompt = (char *)NULL;
+#if defined (READLINE)
+      itext = (char *)NULL;
+#endif
+      edit = silent = 0;
+    }
+
+#if defined (READLINE)
+  if (edit)
+    add_unwind_protect (xfree, rlbuf);
+#endif
+
+  pass_next = 0;       /* Non-zero signifies last char was backslash. */
+  saw_escape = 0;      /* Non-zero signifies that we saw an escape char */
+
+  if (tmsec > 0 || tmusec > 0)
+    {
+      /* Turn off the timeout if stdin is a regular file (e.g. from
+        input redirection). */
+      if ((fstat (fd, &tsb) < 0) || S_ISREG (tsb.st_mode))
+       tmsec = tmusec = 0;
+    }
+
+  if (tmsec > 0 || tmusec > 0)
+    {
+      code = setjmp (alrmbuf);
+      if (code)
+       {
+         sigalrm_seen = 0;
+         /* Tricky.  The top of the unwind-protect stack is the free of
+            input_string.  We want to run all the rest and use input_string,
+            so we have to remove it from the stack. */
+         orig_input_string = 0;
+
+         remove_unwind_protect ();
+         run_unwind_frame ("read_builtin");
+         input_string[i] = '\0';       /* make sure it's terminated */
+         retval = 128+SIGALRM;
+         goto assign_vars;
+       }
+      old_alrm = set_signal_handler (SIGALRM, sigalrm);
+      add_unwind_protect (reset_alarm, (char *)NULL);
+#if defined (READLINE)
+      if (edit)
+       add_unwind_protect (reset_attempted_completion_function, (char *)NULL);
+#endif
+      falarm (tmsec, tmusec);
+    }
+
+  /* If we've been asked to read only NCHARS chars, or we're using some
+     character other than newline to terminate the line, do the right
+     thing to readline or the tty. */
+  if (nchars > 0 || delim != '\n')
+    {
+#if defined (READLINE)
+      if (edit)
+       {
+         if (nchars > 0)
+           {
+             unwind_protect_int (rl_num_chars_to_read);
+             rl_num_chars_to_read = nchars;
+           }
+         if (delim != '\n')
+           {
+             set_eol_delim (delim);
+             add_unwind_protect (reset_eol_delim, (char *)NULL);
+           }
+       }
+      else
+#endif
+      if (input_is_tty)
+       {
+         /* ttsave() */
+         termsave.fd = fd;
+         ttgetattr (fd, &ttattrs);
+         termsave.attrs = &ttattrs;
+
+         ttset = ttattrs;        
+         i = silent ? ttfd_cbreak (fd, &ttset) : ttfd_onechar (fd, &ttset);
+         if (i < 0)
+           sh_ttyerror (1);
+         add_unwind_protect ((Function *)ttyrestore, (char *)&termsave);
+       }
+    }
+  else if (silent)     /* turn off echo but leave term in canonical mode */
+    {
+      /* ttsave (); */
+      termsave.fd = fd;
+      ttgetattr (fd, &ttattrs);
+      termsave.attrs = &ttattrs;
+
+      ttset = ttattrs;
+      i = ttfd_noecho (fd, &ttset);                    /* ttnoecho (); */
+      if (i < 0)
+       sh_ttyerror (1);
+
+      add_unwind_protect ((Function *)ttyrestore, (char *)&termsave);
+    }
+
+  /* This *must* be the top unwind-protect on the stack, so the manipulation
+     of the unwind-protect stack after the realloc() works right. */
+  add_unwind_protect (xfree, input_string);
+
+  CHECK_ALRM;
+  unbuffered_read = (nchars > 0) || (delim != '\n') || input_is_pipe;
+
+  if (prompt && edit == 0)
+    {
+      fprintf (stderr, "%s", prompt);
+      fflush (stderr);
+    }
+
+#if defined (__CYGWIN__) && defined (O_TEXT)
+  setmode (0, O_TEXT);
+#endif
+
+  ps2 = 0;
+  for (print_ps2 = eof = retval = 0;;)
+    {
+      CHECK_ALRM;
+
+#if defined (READLINE)
+      if (edit)
+       {
+         if (rlbuf && rlbuf[rlind] == '\0')
+           {
+             xfree (rlbuf);
+             rlbuf = (char *)0;
+           }
+         if (rlbuf == 0)
+           {
+             reading = 1;
+             rlbuf = edit_line (prompt ? prompt : "", itext);
+             reading = 0;
+             rlind = 0;
+           }
+         if (rlbuf == 0)
+           {
+             eof = 1;
+             break;
+           }
+         c = rlbuf[rlind++];
+       }
+      else
+       {
+#endif
+
+      if (print_ps2)
+       {
+         if (ps2 == 0)
+           ps2 = get_string_value ("PS2");
+         fprintf (stderr, "%s", ps2 ? ps2 : "");
+         fflush (stderr);
+         print_ps2 = 0;
+       }
+
+#if 0
+      if (posixly_correct == 0)
+       interrupt_immediately++;
+#endif
+      reading = 1;
+      if (unbuffered_read)
+       retval = posixly_correct ? zreadintr (fd, &c, 1) : zread (fd, &c, 1);
+      else
+       retval = posixly_correct ? zreadcintr (fd, &c) : zreadc (fd, &c);
+      reading = 0;
+#if 0
+      if (posixly_correct == 0)
+       interrupt_immediately--;
+#endif
+
+      if (retval <= 0)
+       {
+         if (retval < 0 && errno == EINTR)
+           {
+             lastsig = LASTSIG();
+             if (lastsig == 0)
+               lastsig = trapped_signal_received;
+             run_pending_traps ();     /* because interrupt_immediately is not set */
+           }
+         else
+           lastsig = 0;
+         CHECK_TERMSIG;
+         eof = 1;
+         break;
+       }
+
+      CHECK_ALRM;
+
+#if defined (READLINE)
+       }
+#endif
+
+      CHECK_ALRM;
+      if (i + 4 >= size)       /* XXX was i + 2; use i + 4 for multibyte/read_mbchar */
+       {
+         char *t;
+         t = (char *)xrealloc (input_string, size += 128);
+
+         /* Only need to change unwind-protect if input_string changes */
+         if (t != input_string)
+           {
+             input_string = t;
+             remove_unwind_protect ();
+             add_unwind_protect (xfree, input_string);
+           }
+       }
+
+      /* If the next character is to be accepted verbatim, a backslash
+        newline pair still disappears from the input. */
+      if (pass_next)
+       {
+         pass_next = 0;
+         if (c == '\n')
+           {
+             i--;              /* back up over the CTLESC */
+             if (interactive && input_is_tty && raw == 0)
+               print_ps2 = 1;
+           }
+         else
+           goto add_char;
+         continue;
+       }
+
+      /* This may cause problems if IFS contains CTLESC */
+      if (c == '\\' && raw == 0)
+       {
+         pass_next++;
+         if (skip_ctlesc == 0)
+           {
+             saw_escape++;
+             input_string[i++] = CTLESC;
+           }
+         continue;
+       }
+
+      if ((unsigned char)c == delim)
+       break;
+
+      if (c == '\0' && delim != '\0')
+       continue;               /* skip NUL bytes in input */
+
+      if ((skip_ctlesc == 0 && c == CTLESC) || (skip_ctlnul == 0 && c == CTLNUL))
+       {
+         saw_escape++;
+         input_string[i++] = CTLESC;
+       }
+
+add_char:
+      input_string[i++] = c;
+      CHECK_ALRM;
+
+#if defined (HANDLE_MULTIBYTE)
+      if (nchars > 0 && MB_CUR_MAX > 1 && is_basic (c) == 0)
+       {
+         input_string[i] = '\0';       /* for simplicity and debugging */
+         i += read_mbchar (fd, input_string, i, c, unbuffered_read);
+       }
+#endif
+
+      nr++;
+
+      if (nchars > 0 && nr >= nchars)
+       break;
+    }
+  input_string[i] = '\0';
+  CHECK_ALRM;
+
+  if (retval < 0)
+    {
+      t_errno = errno;
+      if (errno != EINTR)
+       builtin_error (_("read error: %d: %s"), fd, strerror (errno));
+      run_unwind_frame ("read_builtin");
+      return ((t_errno != EINTR) ? EXECUTION_FAILURE : 128+lastsig);
+    }
+
+  if (tmsec > 0 || tmusec > 0)
+    reset_alarm ();
+
+  if (nchars > 0 || delim != '\n')
+    {
+#if defined (READLINE)
+      if (edit)
+       {
+         if (nchars > 0)
+           rl_num_chars_to_read = 0;
+         if (delim != '\n')
+           reset_eol_delim ((char *)NULL);
+       }
+      else
+#endif
+      if (input_is_tty)
+       ttyrestore (&termsave);
+    }
+  else if (silent)
+    ttyrestore (&termsave);
+
+  if (unbuffered_read == 0)
+    zsyncfd (fd);
+
+  discard_unwind_frame ("read_builtin");
+
+  retval = eof ? EXECUTION_FAILURE : EXECUTION_SUCCESS;
+
+assign_vars:
+
+#if defined (ARRAY_VARS)
+  /* If -a was given, take the string read, break it into a list of words,
+     an assign them to `arrayname' in turn. */
+  if (arrayname)
+    {
+      if (legal_identifier (arrayname) == 0)
+       {
+         sh_invalidid (arrayname);
+         xfree (input_string);
+         return (EXECUTION_FAILURE);
+       }
+
+      var = find_or_make_array_variable (arrayname, 1);
+      if (var == 0)
+       {
+         xfree (input_string);
+         return EXECUTION_FAILURE;     /* readonly or noassign */
+       }
+      if (assoc_p (var))
+       {
+          builtin_error (_("%s: cannot convert associative to indexed array"), arrayname);
+         xfree (input_string);
+         return EXECUTION_FAILURE;     /* existing associative array */
+       }
+      else if (invisible_p (var))
+       VUNSETATTR (var, att_invisible);
+      array_flush (array_cell (var));
+
+      alist = list_string (input_string, ifs_chars, 0);
+      if (alist)
+       {
+         if (saw_escape)
+           dequote_list (alist);
+         else
+           word_list_remove_quoted_nulls (alist);
+         assign_array_var_from_word_list (var, alist, 0);
+         dispose_words (alist);
+       }
+      xfree (input_string);
+      return (retval);
+    }
+#endif /* ARRAY_VARS */ 
+
+  /* If there are no variables, save the text of the line read to the
+     variable $REPLY.  ksh93 strips leading and trailing IFS whitespace,
+     so that `read x ; echo "$x"' and `read ; echo "$REPLY"' behave the
+     same way, but I believe that the difference in behaviors is useful
+     enough to not do it.  Without the bash behavior, there is no way
+     to read a line completely without interpretation or modification
+     unless you mess with $IFS (e.g., setting it to the empty string).
+     If you disagree, change the occurrences of `#if 0' to `#if 1' below. */
+  if (list == 0)
+    {
+#if 0
+      orig_input_string = input_string;
+      for (t = input_string; ifs_chars && *ifs_chars && spctabnl(*t) && isifs(*t); t++)
+       ;
+      input_string = t;
+      input_string = strip_trailing_ifs_whitespace (input_string, ifs_chars, saw_escape);
+#endif
+
+      if (saw_escape)
+       {
+         t = dequote_string (input_string);
+         var = bind_variable ("REPLY", t, 0);
+         free (t);
+       }
+      else
+       var = bind_variable ("REPLY", input_string, 0);
+      VUNSETATTR (var, att_invisible);
+
+      xfree (input_string);
+      return (retval);
+    }
+
+  /* This code implements the Posix.2 spec for splitting the words
+     read and assigning them to variables. */
+  orig_input_string = input_string;
+
+  /* Remove IFS white space at the beginning of the input string.  If
+     $IFS is null, no field splitting is performed. */
+  for (t = input_string; ifs_chars && *ifs_chars && spctabnl(*t) && isifs(*t); t++)
+    ;
+  input_string = t;
+  for (; list->next; list = list->next)
+    {
+      varname = list->word->word;
+#if defined (ARRAY_VARS)
+      if (legal_identifier (varname) == 0 && valid_array_reference (varname) == 0)
+#else
+      if (legal_identifier (varname) == 0)
+#endif
+       {
+         sh_invalidid (varname);
+         xfree (orig_input_string);
+         return (EXECUTION_FAILURE);
+       }
+
+      /* If there are more variables than words read from the input,
+        the remaining variables are set to the empty string. */
+      if (*input_string)
+       {
+         /* This call updates INPUT_STRING. */
+         t = get_word_from_string (&input_string, ifs_chars, &e);
+         if (t)
+           *e = '\0';
+         /* Don't bother to remove the CTLESC unless we added one
+            somewhere while reading the string. */
+         if (t && saw_escape)
+           {
+             t1 = dequote_string (t);
+             var = bind_read_variable (varname, t1);
+             xfree (t1);
+           }
+         else
+           var = bind_read_variable (varname, t ? t : "");
+       }
+      else
+       {
+         t = (char *)0;
+         var = bind_read_variable (varname, "");
+       }
+
+      FREE (t);
+      if (var == 0)
+       {
+         xfree (orig_input_string);
+         return (EXECUTION_FAILURE);
+       }
+
+      stupidly_hack_special_variables (varname);
+      VUNSETATTR (var, att_invisible);
+    }
+
+  /* Now assign the rest of the line to the last variable argument. */
+#if defined (ARRAY_VARS)
+  if (legal_identifier (list->word->word) == 0 && valid_array_reference (list->word->word) == 0)
+#else
+  if (legal_identifier (list->word->word) == 0)
+#endif
+    {
+      sh_invalidid (list->word->word);
+      xfree (orig_input_string);
+      return (EXECUTION_FAILURE);
+    }
+
+#if 0
+  /* This has to be done this way rather than using string_list
+     and list_string because Posix.2 says that the last variable gets the
+     remaining words and their intervening separators. */
+  input_string = strip_trailing_ifs_whitespace (input_string, ifs_chars, saw_escape);
+#else
+  /* Check whether or not the number of fields is exactly the same as the
+     number of variables. */
+  tofree = NULL;
+  if (*input_string)
+    {
+      t1 = input_string;
+      t = get_word_from_string (&input_string, ifs_chars, &e);
+      if (*input_string == 0)
+       tofree = input_string = t;
+      else
+       {
+         input_string = strip_trailing_ifs_whitespace (t1, ifs_chars, saw_escape);
+         tofree = t;
+       }
+    }
+#endif
+
+  if (saw_escape && input_string && *input_string)
+    {
+      t = dequote_string (input_string);
+      var = bind_read_variable (list->word->word, t);
+      xfree (t);
+    }
+  else
+    var = bind_read_variable (list->word->word, input_string ? input_string : "");
+
+  if (var)
+    {
+      stupidly_hack_special_variables (list->word->word);
+      VUNSETATTR (var, att_invisible);
+    }
+  else
+    retval = EXECUTION_FAILURE;
+
+  FREE (tofree);
+  xfree (orig_input_string);
+
+  return (retval);
+}
+
+static SHELL_VAR *
+bind_read_variable (name, value)
+     char *name, *value;
+{
+  SHELL_VAR *v;
+
+itrace("bind_read_variable: name = %s value = %s", name, value);
+#if defined (ARRAY_VARS)
+  if (valid_array_reference (name) == 0)
+    v = bind_variable (name, value, 0);
+  else
+    v = assign_array_element (name, value, 0);
+#else /* !ARRAY_VARS */
+  v = bind_variable (name, value, 0);
+#endif /* !ARRAY_VARS */
+  return (v == 0 ? v
+                : ((readonly_p (v) || noassign_p (v)) ? (SHELL_VAR *)NULL : v));
+}
+
+#if defined (HANDLE_MULTIBYTE)
+static int
+read_mbchar (fd, string, ind, ch, unbuffered)
+     int fd;
+     char *string;
+     int ind, ch, unbuffered;
+{
+  char mbchar[MB_LEN_MAX + 1];
+  int i, n, r;
+  char c;
+  size_t ret;
+  mbstate_t ps, ps_back;
+  wchar_t wc;
+
+  memset (&ps, '\0', sizeof (mbstate_t));
+  memset (&ps_back, '\0', sizeof (mbstate_t));
+  
+  mbchar[0] = ch;
+  i = 1;
+  for (n = 0; n <= MB_LEN_MAX; n++)
+    {
+      ps_back = ps;
+      ret = mbrtowc (&wc, mbchar, i, &ps);
+      if (ret == (size_t)-2)
+       {
+         ps = ps_back;
+         /* We don't want to be interrupted during a multibyte char read */
+         if (unbuffered)
+           r = zread (fd, &c, 1);
+         else
+           r = zreadc (fd, &c);
+         if (r < 0)
+           goto mbchar_return;
+         mbchar[i++] = c;      
+         continue;
+       }
+      else if (ret == (size_t)-1 || ret == (size_t)0 || ret > (size_t)0)
+       break;
+    }
+
+mbchar_return:
+  if (i > 1)   /* read a multibyte char */
+    /* mbchar[0] is already string[ind-1] */
+    for (r = 1; r < i; r++)
+      string[ind+r-1] = mbchar[r];
+  return i - 1;
+}
+#endif
+
+
+static void
+ttyrestore (ttp)
+     struct ttsave *ttp;
+{
+  ttsetattr (ttp->fd, ttp->attrs);
+}
+
+#if defined (READLINE)
+static rl_completion_func_t *old_attempted_completion_function = 0;
+static rl_hook_func_t *old_startup_hook;
+static char *deftext;
+
+static void
+reset_attempted_completion_function (cp)
+     char *cp;
+{
+  if (rl_attempted_completion_function == 0 && old_attempted_completion_function)
+    rl_attempted_completion_function = old_attempted_completion_function;
+}
+
+static int
+set_itext ()
+{
+  int r1, r2;
+
+  r1 = r2 = 0;
+  if (old_startup_hook)
+    r1 = (*old_startup_hook) ();
+  if (deftext)
+    {
+      r2 = rl_insert_text (deftext);
+      deftext = (char *)NULL;
+      rl_startup_hook = old_startup_hook;
+      old_startup_hook = (rl_hook_func_t *)NULL;
+    }
+  return (r1 || r2);
+}
+
+static char *
+edit_line (p, itext)
+     char *p;
+     char *itext;
+{
+  char *ret;
+  int len;
+
+  if (bash_readline_initialized == 0)
+    initialize_readline ();
+
+  old_attempted_completion_function = rl_attempted_completion_function;
+  rl_attempted_completion_function = (rl_completion_func_t *)NULL;
+  if (itext)
+    {
+      old_startup_hook = rl_startup_hook;
+      rl_startup_hook = set_itext;
+      deftext = itext;
+    }
+
+  ret = readline (p);
+
+  rl_attempted_completion_function = old_attempted_completion_function;
+  old_attempted_completion_function = (rl_completion_func_t *)NULL;
+
+  if (ret == 0)
+    return ret;
+  len = strlen (ret);
+  ret = (char *)xrealloc (ret, len + 2);
+  ret[len++] = delim;
+  ret[len] = '\0';
+  return ret;
+}
+
+static int old_delim_ctype;
+static rl_command_func_t *old_delim_func;
+static int old_newline_ctype;
+static rl_command_func_t *old_newline_func;
+
+static unsigned char delim_char;
+
+static void
+set_eol_delim (c)
+     int c;
+{
+  Keymap cmap;
+
+  if (bash_readline_initialized == 0)
+    initialize_readline ();
+  cmap = rl_get_keymap ();
+
+  /* Change newline to self-insert */
+  old_newline_ctype = cmap[RETURN].type;
+  old_newline_func =  cmap[RETURN].function;
+  cmap[RETURN].type = ISFUNC;
+  cmap[RETURN].function = rl_insert;
+
+  /* Bind the delimiter character to accept-line. */
+  old_delim_ctype = cmap[c].type;
+  old_delim_func = cmap[c].function;
+  cmap[c].type = ISFUNC;
+  cmap[c].function = rl_newline;
+
+  delim_char = c;
+}
+
+static void
+reset_eol_delim (cp)
+     char *cp;
+{
+  Keymap cmap;
+
+  cmap = rl_get_keymap ();
+
+  cmap[RETURN].type = old_newline_ctype;
+  cmap[RETURN].function = old_newline_func;
+
+  cmap[delim_char].type = old_delim_ctype;
+  cmap[delim_char].function = old_delim_func;
+}
+#endif
index 807b7f050ac81d399665b288116d9a99bfe9812e..f207084ae4a2a60a984e03751a8f29aa04163a67 100644 (file)
@@ -22,7 +22,7 @@ $BUILTIN wait
 $FUNCTION wait_builtin
 $DEPENDS_ON JOB_CONTROL
 $PRODUCES wait.c
-$SHORT_DOC wait [id ...]
+$SHORT_DOC wait [-n] [id ...]
 Wait for job completion and return exit status.
 
 Waits for each process identified by an ID, which may be a process ID or a
@@ -31,6 +31,9 @@ given, waits for all currently active child processes, and the return
 status is zero.  If ID is a a job specification, waits for all processes
 in that job's pipeline.
 
+If the -n option is supplied, waits for the next job to terminate and
+returns its exit status.
+
 Exit Status:
 Returns the status of the last ID; fails if ID is invalid or an invalid
 option is given.
@@ -91,13 +94,27 @@ int
 wait_builtin (list)
      WORD_LIST *list;
 {
-  int status, code;
+  int status, code, opt, nflag;
   volatile int old_interrupt_immediately;
 
   USE_VAR(list);
 
-  if (no_options (list))
-    return (EX_USAGE);
+  nflag = 0;
+  reset_internal_getopt ();
+  while ((opt = internal_getopt (list, "n")) != -1)
+    {
+      switch (opt)
+       {
+#if defined (JOB_CONTROL)
+       case 'n':
+         nflag = 1;
+         break;
+#endif
+       default:
+         builtin_usage ();
+         return (EXECUTION_FAILURE);
+       }
+    }
   list = loptend;
 
   old_interrupt_immediately = interrupt_immediately;
@@ -123,6 +140,16 @@ wait_builtin (list)
   /* We support jobs or pids.
      wait <pid-or-job> [pid-or-job ...] */
 
+#if defined (JOB_CONTROL)
+  if (nflag)
+    {
+      status = wait_for_any_job ();
+      if (status < 0)
+       status = 127;
+      WAIT_RETURN (status);
+    }
+#endif
+      
   /* But wait without any arguments means to wait for all of the shell's
      currently active background processes. */
   if (list == 0)
index f6461c8cca39b50413c745abbc4571ea41f15e17..b1f9fff609a7a42c53ea90ca29d0134ead94c346 100644 (file)
@@ -5,12 +5,12 @@
 .\"    Case Western Reserve University
 .\"    chet@po.cwru.edu
 .\"
-.\"    Last Change: Thu Nov 15 21:03:47 EST 2012
+.\"    Last Change: Sat Nov 24 15:07:12 EST 2012
 .\"
 .\" bash_builtins, strip all but Built-Ins section
 .if \n(zZ=1 .ig zZ
 .if \n(zY=1 .ig zY
-.TH BASH 1 "2012 November 15" "GNU Bash 4.2"
+.TH BASH 1 "2012 November 24" "GNU Bash 4.2"
 .\"
 .\" There's some problem with having a `@'
 .\" in a tagged paragraph with the BSD man macros.
@@ -10006,7 +10006,7 @@ subsequently reset.  The exit status is true unless a
 .I name
 is readonly.
 .TP
-\fBwait\fP [\fIn ...\fP]
+\fBwait\fP [\fB\--n\fP] [\fIn ...\fP]
 Wait for each specified process and return its termination status.
 Each
 .I n
@@ -10015,7 +10015,10 @@ ID or a job specification; if a job spec is given, all processes
 in that job's pipeline are waited for.  If
 .I n
 is not given, all currently active child processes
-are waited for, and the return status is zero.  If
+are waited for, and the return status is zero.
+If the \fB\--n\fP option is supplied, \fBwait\fP waits for any job to
+terminate and returns its exit status.
+If
 .I n
 specifies a non-existent process or job, the return status is
 127.  Otherwise, the return status is the exit status of the last
index 026d59af6d8365ede2ad42b28e780adac39d59f6..1d3ea1f2a4f25f308c2a8b1506eeeaedab8c7d31 100644 (file)
@@ -7505,6 +7505,8 @@ last command waited for.
 If a job spec is given, all processes in the job are waited for.
 If no arguments are given, all currently active child processes are
 waited for, and the return status is zero.
+If the @option{-n} option is supplied, @code{wait} waits for any job to
+terminate and returns its exit status.
 If neither @var{jobspec} nor @var{pid} specifies an active child process
 of the shell, the return status is 127.
 
index b8e56a6b877c478426442fde825cbcb8acf4af67..40a779860a54367f0b30992a57dea5858b71d5a7 100644 (file)
@@ -2,9 +2,9 @@
 Copyright (C) 1988-2012 Free Software Foundation, Inc.
 @end ignore
 
-@set LASTCHANGE Sun Oct  7 17:58:01 EDT 2012
+@set LASTCHANGE Sat Nov 24 15:06:49 EST 2012
 
 @set EDITION 4.2
 @set VERSION 4.2
-@set UPDATED 7 October 2012
-@set UPDATED-MONTH October 2012
+@set UPDATED 24 November 2012
+@set UPDATED-MONTH November 2012
index a9bd36f3ccd83212782249eb38d8e481b0f634ac..736c8af1a7757629bbbf8b24406c553e378e8bf9 100644 (file)
@@ -66,6 +66,10 @@ WORD_LIST    *list;
                return(EX_USAGE);
        }
 
+       /* Skip over `--' */
+       if (list->word && ISOPTION (list->word->word, '-'))
+               list = list->next;
+
        if (*list->word->word == '-' || list->next) {
                builtin_usage ();
                return (EX_USAGE);
index a56493c04e4002c45a189fa433c91024d3f1e272..70a37451aa1bfb99be0ef9515de6ef95ee12601d 100644 (file)
@@ -532,6 +532,7 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
 {
   int exec_result, user_subshell, invert, ignore_return, was_error_trap;
   REDIRECT *my_undo_list, *exec_undo_list;
+  char *tcmd;
   volatile int last_pid;
   volatile int save_line_number;
 #if defined (PROCESS_SUBSTITUTION)
@@ -586,8 +587,8 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
       /* Fork a subshell, turn off the subshell bit, turn off job
         control and call execute_command () on the command again. */
       line_number_for_err_trap = line_number;
-      paren_pid = make_child (savestring (make_command_string (command)),
-                             asynchronous);
+      tcmd = make_command_string (command);
+      paren_pid = make_child (savestring (tcmd), asynchronous);
 
       if (user_subshell && signal_is_trapped (ERROR_TRAP) && 
          signal_in_progress (DEBUG_TRAP) == 0 && running_trap == 0)
@@ -2199,6 +2200,7 @@ execute_coproc (command, pipe_in, pipe_out, fds_to_close)
   close (rpipe[1]);
   close (wpipe[0]);
 
+  /* XXX - possibly run Coproc->name through word expansion? */
   cp = coproc_alloc (command->value.Coproc->name, coproc_pid);
   cp->c_rfd = rpipe[0];
   cp->c_wfd = wpipe[1];
diff --git a/jobs.c b/jobs.c
index a786d7048870dc217dc82a9448a861179f7d24f0..d1345c9537380e625c86b382103887170bcecb32 100644 (file)
--- a/jobs.c
+++ b/jobs.c
@@ -933,12 +933,10 @@ realloc_jobs_list ()
       }
 
 #if defined (DEBUG)
-#  if 0
   itrace ("realloc_jobs_list: resize jobs list from %d to %d", js.j_jobslots, nsize);
   itrace ("realloc_jobs_list: j_lastj changed from %d to %d", js.j_lastj, (j > 0) ? j - 1 : 0);
   itrace ("realloc_jobs_list: j_njobs changed from %d to %d", js.j_njobs, j);
   itrace ("realloc_jobs_list: js.j_ndead %d js.c_reaped %d", js.j_ndead, js.c_reaped);
-#  endif
 #endif
 
   js.j_firstj = 0;
@@ -966,7 +964,7 @@ realloc_jobs_list ()
     reset_current ();
 
 #ifdef DEBUG
-/*  itrace ("realloc_jobs_list: reset js.j_current (%d) and js.j_previous (%d)", js.j_current, js.j_previous);*/
+  itrace ("realloc_jobs_list: reset js.j_current (%d) and js.j_previous (%d)", js.j_current, js.j_previous);
 #endif
 
   UNBLOCK_CHILD (oset);
@@ -989,7 +987,7 @@ compact_jobs_list (flags)
   realloc_jobs_list ();
 
 #ifdef DEBUG
-/*  itrace("compact_jobs_list: returning %d", (js.j_lastj || jobs[js.j_lastj]) ? js.j_lastj + 1 : 0); */
+  itrace("compact_jobs_list: returning %d", (js.j_lastj || jobs[js.j_lastj]) ? js.j_lastj + 1 : 0);
 #endif
 
   return ((js.j_lastj || jobs[js.j_lastj]) ? js.j_lastj + 1 : 0);
@@ -2396,6 +2394,7 @@ wait_for (pid)
   /* In the case that this code is interrupted, and we longjmp () out of it,
      we are relying on the code in throw_to_top_level () to restore the
      top-level signal mask. */
+  child = 0;
   BLOCK_CHILD (set, oset);
 
   /* Ignore interrupts while waiting for a job run without job control
@@ -2433,7 +2432,8 @@ wait_for (pid)
   job = NO_JOB;
   do
     {
-      FIND_CHILD (pid, child);
+      if (pid != ANY_PID)
+       FIND_CHILD (pid, child);
 
       /* If this child is part of a job, then we are really waiting for the
         job to finish.  Otherwise, we are waiting for the child to finish.
@@ -2446,7 +2446,7 @@ wait_for (pid)
         has already exited before this is called, sigchld_handler will have
         called waitchld and the state will be set to JDEAD. */
 
-      if (PRUNNING(child) || (job != NO_JOB && RUNNING (job)))
+      if (pid == ANY_PID || PRUNNING(child) || (job != NO_JOB && RUNNING (job)))
        {
 #if defined (WAITPID_BROKEN)    /* SCOv4 */
          sigset_t suspend_set;
@@ -2468,7 +2468,7 @@ wait_for (pid)
 #  endif
          queue_sigchld = 1;
          waiting_for_child++;
-         r = waitchld (pid, 1);
+         r = waitchld (pid, 1);        /* XXX */
          waiting_for_child--;
 #  if defined (MUST_UNBLOCK_CHLD)
          sigaction (SIGCHLD, &oact, (struct sigaction *)NULL);
@@ -2478,6 +2478,7 @@ wait_for (pid)
          if (r == -1 && errno == ECHILD && this_shell_builtin == wait_builtin)
            {
              termination_state = -1;
+             /* XXX - restore sigint handler here? */
              goto wait_for_return;
            }
 
@@ -2487,8 +2488,11 @@ wait_for (pid)
             if it exists, as JDEAD. */
          if (r == -1 && errno == ECHILD)
            {
-             child->running = PS_DONE;
-             WSTATUS (child->status) = 0;      /* XXX -- can't find true status */
+             if (child)
+               {
+                 child->running = PS_DONE;
+                 WSTATUS (child->status) = 0;  /* XXX -- can't find true status */
+               }
              js.c_living = 0;          /* no living child processes */
              if (job != NO_JOB)
                {
@@ -2496,6 +2500,11 @@ wait_for (pid)
                  js.c_reaped++;
                  js.j_ndead++;
                }
+             if (pid == ANY_PID)
+               {
+                 termination_state = -1;
+                 break;
+               }
            }
 #endif /* WAITPID_BROKEN */
        }
@@ -2511,6 +2520,11 @@ wait_for (pid)
 
       /* Check for a trapped signal interrupting the wait builtin and jump out */
       CHECK_WAIT_INTR;
+
+      if (pid == ANY_PID)
+        /* XXX - could set child but we don't have a handle on what waitchld
+          reaps.  Leave termination_state alone. */
+       goto wait_for_return;
     }
   while (PRUNNING (child) || (job != NO_JOB && RUNNING (job)));
 
@@ -2676,6 +2690,76 @@ wait_for_job (job)
   return r;
 }
 
+/* Wait for any background job started by this shell to finish.  Very
+   similar to wait_for_background_pids().  Returns the exit status of
+   the next exiting job, -1 if there are no background jobs.  The caller
+   is responsible for translating -1 into the right return value. */
+int
+wait_for_any_job ()
+{
+  pid_t pid;
+  int i, r, waited_for;
+  sigset_t set, oset;
+
+  if (jobs_list_frozen)
+    return -1;
+
+  /* First see if there are any unnotified dead jobs that we can report on */
+  BLOCK_CHILD (set, oset);
+  for (i = 0; i < js.j_jobslots; i++)
+    {
+      if (jobs[i] && DEADJOB (i) && IS_NOTIFIED (i) == 0)
+       {
+return_job:
+         r = job_exit_status (i);
+         notify_of_job_status ();              /* XXX */
+         delete_job (i, 0);
+#if defined (COPROCESS_SUPPORT)
+         coproc_reap ();
+#endif
+         UNBLOCK_CHILD (oset);
+         return r;
+       }
+    }
+  UNBLOCK_CHILD (oset);
+
+  /* At this point, we have no dead jobs in the jobs table.  Wait until we
+     get one, even if it takes multiple pids exiting. */
+  for (waited_for = 0;;)
+    {
+      /* Make sure there is a background job to wait for */
+      BLOCK_CHILD (set, oset);
+      for (i = 0; i < js.j_jobslots; i++)
+        if (jobs[i] && RUNNING (i) && IS_FOREGROUND (i) == 0)
+          break;
+      if (i == js.j_jobslots)
+       {
+         UNBLOCK_CHILD (oset);
+         return -1;
+       }
+
+      UNBLOCK_CHILD (oset);
+
+      QUIT;
+      CHECK_TERMSIG;
+      CHECK_WAIT_INTR;
+
+      errno = 0;
+      r = wait_for (ANY_PID);  /* special sentinel value for wait_for */
+      if (r == -1 && errno == ECHILD)
+       mark_all_jobs_as_dead ();
+       
+      /* Now we see if we have any dead jobs and return the first one */
+      BLOCK_CHILD (set, oset);
+      for (i = 0; i < js.j_jobslots; i++)
+       if (jobs[i] && DEADJOB (i))
+         goto return_job;
+      UNBLOCK_CHILD (oset);
+    }
+
+  return -1;
+}
+
 /* Print info about dead jobs, and then delete them from the list
    of known jobs.  This does not actually delete jobs when the
    shell is not interactive, because the dead jobs are not marked
diff --git a/jobs.h b/jobs.h
index 62e294724d16afb97b7423546fd7b10f63345913..f101f6c8856a5f6f783243fb25dc0acbaea51c0a 100644 (file)
--- a/jobs.h
+++ b/jobs.h
@@ -156,6 +156,8 @@ struct bgpids {
 /* A value which cannot be a process ID. */
 #define NO_PID (pid_t)-1
 
+#define ANY_PID (pid_t)-1
+
 /* System calls. */
 #if !defined (HAVE_UNISTD_H)
 extern pid_t fork (), getpid (), getpgrp ();
@@ -216,6 +218,7 @@ extern int wait_for_single_pid __P((pid_t));
 extern void wait_for_background_pids __P((void));
 extern int wait_for __P((pid_t));
 extern int wait_for_job __P((int));
+extern int wait_for_any_job __P((void));
 
 extern void notify_and_cleanup __P((void));
 extern void reap_dead_jobs __P((void));
index f0e2c1e31f41a55b350cb9c642f185eadf3aea17..dfa29e222db5e6ead39130c7d7ae58e3ce3f7d32 100644 (file)
@@ -192,8 +192,8 @@ mregister_alloc (tag, mem, size, file, line)
     {
       /* oops.  table is full.  punt. */
       fprintf (stderr, _("register_alloc: alloc table is full with FIND_ALLOC?\n"));
-  if (blocked_sigs)
-    _malloc_unblock_signals (&set, &oset);
+      if (blocked_sigs)
+       _malloc_unblock_signals (&set, &oset);
       return;
     }
   
@@ -246,8 +246,8 @@ mregister_free (mem, size, file, line)
 #if 0
       fprintf (stderr, "register_free: %p not in allocation table?\n", mem);
 #endif
-  if (blocked_sigs)
-    _malloc_unblock_signals (&set, &oset);
+      if (blocked_sigs)
+       _malloc_unblock_signals (&set, &oset);
       return;
     }
   if (tentry->flags & MT_FREE)
diff --git a/redir.c b/redir.c
index a00ab879866dd6ca3cf5f02800636c5337cda8c8..33ce958df53c94f146a029261cafec888aed6577 100644 (file)
--- a/redir.c
+++ b/redir.c
@@ -819,15 +819,13 @@ do_redirection_internal (redirect, flags)
              REDIRECTION_ERROR (redirector, r, fd);
            }
 
-         if (flags & RX_UNDOABLE)
+         if ((flags & RX_UNDOABLE) && (redirect->rflags & REDIR_VARASSIGN) == 0)
            {
              /* Only setup to undo it if the thing to undo is active. */
              if ((fd != redirector) && (fcntl (redirector, F_GETFD, 0) != -1))
                r = add_undo_redirect (redirector, ri, -1);
              else
                r = add_undo_close_redirect (redirector);
-             if (r < 0 && (redirect->rflags & REDIR_VARASSIGN))
-               close (redirector);
              REDIRECTION_ERROR (r, errno, fd);
            }
 
@@ -933,15 +931,13 @@ do_redirection_internal (redirect, flags)
 
          if (flags & RX_ACTIVE)
            {
-             if (flags & RX_UNDOABLE)
+             if ((flags & RX_UNDOABLE) && (redirect->rflags & REDIR_VARASSIGN) == 0)
                {
                  /* Only setup to undo it if the thing to undo is active. */
                  if ((fd != redirector) && (fcntl (redirector, F_GETFD, 0) != -1))
                    r = add_undo_redirect (redirector, ri, -1);
                  else
                    r = add_undo_close_redirect (redirector);
-                 if (r < 0 && (redirect->rflags & REDIR_VARASSIGN))
-                   close (redirector);
                  REDIRECTION_ERROR (r, errno, fd);
                }
 
@@ -996,15 +992,13 @@ do_redirection_internal (redirect, flags)
 
       if ((flags & RX_ACTIVE) && (redir_fd != redirector))
        {
-         if (flags & RX_UNDOABLE)
+         if ((flags & RX_UNDOABLE) && (redirect->rflags & REDIR_VARASSIGN) == 0)
            {
              /* Only setup to undo it if the thing to undo is active. */
              if (fcntl (redirector, F_GETFD, 0) != -1)
                r = add_undo_redirect (redirector, ri, redir_fd);
              else
                r = add_undo_close_redirect (redirector);
-             if (r < 0 && (redirect->rflags & REDIR_VARASSIGN))
-               close (redirector);
              REDIRECTION_ERROR (r, errno, -1);
            }
 #if defined (BUFFERED_INPUT)
@@ -1078,6 +1072,7 @@ do_redirection_internal (redirect, flags)
            }
 
          r = 0;
+         /* XXX - only if REDIR_VARASSIGN not set? */
          if ((flags & RX_UNDOABLE) && (fcntl (redirector, F_GETFD, 0) != -1))
            {
              r = add_undo_redirect (redirector, ri, -1);
diff --git a/subst.c b/subst.c
index 958c2619b9614ba1333b14bbdbe6e8b30c68e89e..d342d85ab7039f31abdf38c98c9379879459d98f 100644 (file)
--- a/subst.c
+++ b/subst.c
@@ -5128,6 +5128,10 @@ process_substitute (string, open_for_read_in_child)
   dev_fd_list[parent_pipe_fd] = 0;
 #endif /* HAVE_DEV_FD */
 
+  /* subshells shouldn't have this flag, which controls using the temporary
+     environment for variable lookups. */
+  expanding_redir = 0;
+
   result = parse_and_execute (string, "process substitution", (SEVAL_NONINT|SEVAL_NOHIST));
 
 #if !defined (HAVE_DEV_FD)
@@ -7270,7 +7274,7 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta
   var_is_null = check_nullness && (var_is_set == 0 || *temp == 0);
   /* XXX - this may not need to be restricted to special variables */
   if (check_nullness)
-    var_is_null |= var_is_special && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && QUOTED_NULL (temp);
+    var_is_null |= var_is_set && var_is_special && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && QUOTED_NULL (temp);
 
   /* Get the rest of the stuff inside the braces. */
   if (c && c != RBRACE)
@@ -9231,7 +9235,7 @@ brace_expand_word_list (tlist, eflags)
 
       if (tlist->word->flags & W_NOBRACE)
         {
-itrace("brace_expand_word_list: %s: W_NOBRACE", tlist->word->word);
+/*itrace("brace_expand_word_list: %s: W_NOBRACE", tlist->word->word);*/
          PREPEND_LIST (tlist, output_list);
          continue;
         }
@@ -9341,8 +9345,12 @@ shell_expand_word_list (tlist, eflags)
        {
          int t;
 
-         if (tlist->word->flags & W_ASSIGNASSOC)
+         if ((tlist->word->flags & (W_ASSIGNASSOC|W_ASSNGLOBAL)) == (W_ASSIGNASSOC|W_ASSNGLOBAL))
+           make_internal_declare (tlist->word->word, "-gA");
+         else if (tlist->word->flags & W_ASSIGNASSOC)
            make_internal_declare (tlist->word->word, "-A");
+         else if (tlist->word->flags & W_ASSNGLOBAL)
+           make_internal_declare (tlist->word->word, "-g");
 
          t = do_word_assignment (tlist->word, 0);
          if (t == 0)
index 3cd556238763d5fa691157e2ef8fd39fd1dfc67f..191733bde2e29a8e21e7959d08bf5e1d37440096 100644 (file)
--- a/subst.c~
+++ b/subst.c~
@@ -4888,7 +4888,6 @@ unlink_fifo_list ()
   if (nfds == 0)
     return;
 
-itrace("unlink_fifo_list");
   for (i = 0; nfds && i < totfds; i++)
     unlink_fifo (i);
 
@@ -5129,6 +5128,10 @@ process_substitute (string, open_for_read_in_child)
   dev_fd_list[parent_pipe_fd] = 0;
 #endif /* HAVE_DEV_FD */
 
+  /* subshells shouldn't have this flag, which controls using the temporary
+     environment for variable lookups. */
+  expanding_redir = 0;
+
   result = parse_and_execute (string, "process substitution", (SEVAL_NONINT|SEVAL_NOHIST));
 
 #if !defined (HAVE_DEV_FD)
@@ -7269,6 +7272,9 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta
 
   var_is_set = temp != (char *)0;
   var_is_null = check_nullness && (var_is_set == 0 || *temp == 0);
+  /* XXX - this may not need to be restricted to special variables */
+  if (check_nullness)
+    var_is_null |= var_is_set && var_is_special && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && QUOTED_NULL (temp);
 
   /* Get the rest of the stuff inside the braces. */
   if (c && c != RBRACE)
@@ -9339,8 +9345,12 @@ shell_expand_word_list (tlist, eflags)
        {
          int t;
 
-         if (tlist->word->flags & W_ASSIGNASSOC)
+         if ((tlist->word->flags & (W_ASSIGNASSOC|W_ASSNGLOBAL)) == (W_ASSIGNASSOC|W_ASSNGLOBAL))
+           make_internal_declare (tlist->word->word, "-gA");
+         else if (tlist->word->flags & W_ASSIGNASSOC)
            make_internal_declare (tlist->word->word, "-A");
+         else if (tlist->word->flags & W_ASSNGLOBAL)
+           make_internal_declare (tlist->word->word, "-g");
 
          t = do_word_assignment (tlist->word, 0);
          if (t == 0)
diff --git a/test.c b/test.c
index e41cb43280b0093c8d42e7974fd9084c15267b07..240a8b00a52a8a50832a23d61f7ade2f3ff0dc30 100644 (file)
--- a/test.c
+++ b/test.c
@@ -620,11 +620,11 @@ unary_test (op, arg)
 
     case 'v':
       v = find_variable (arg);
-      return (v && var_isset (v) ? TRUE : FALSE);
+      return (v && invisible_p (v) == 0 && var_isset (v) ? TRUE : FALSE);
 
     case 'R':
       v = find_variable (arg);
-      return (v && var_isset (v) && nameref_p (v) ? TRUE : FALSE);
+      return (v && invisible_p (v) == 0 && var_isset (v) && nameref_p (v) ? TRUE : FALSE);
     }
 
   /* We can't actually get here, but this shuts up gcc. */
diff --git a/test.c~ b/test.c~
new file mode 100644 (file)
index 0000000..984e891
--- /dev/null
+++ b/test.c~
@@ -0,0 +1,856 @@
+/* test.c - GNU test program (ksb and mjb) */
+
+/* Modified to run with the GNU shell Apr 25, 1988 by bfox. */
+
+/* Copyright (C) 1987-2010 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 3 of the License, or
+   (at your option) any later version.
+
+   Bash is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with Bash.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Define PATTERN_MATCHING to get the csh-like =~ and !~ pattern-matching
+   binary operators. */
+/* #define PATTERN_MATCHING */
+
+#if defined (HAVE_CONFIG_H)
+#  include <config.h>
+#endif
+
+#include <stdio.h>
+
+#include "bashtypes.h"
+
+#if !defined (HAVE_LIMITS_H) && defined (HAVE_SYS_PARAM_H)
+#  include <sys/param.h>
+#endif
+
+#if defined (HAVE_UNISTD_H)
+#  include <unistd.h>
+#endif
+
+#include <errno.h>
+#if !defined (errno)
+extern int errno;
+#endif /* !errno */
+
+#if !defined (_POSIX_VERSION) && defined (HAVE_SYS_FILE_H)
+#  include <sys/file.h>
+#endif /* !_POSIX_VERSION */
+#include "posixstat.h"
+#include "filecntl.h"
+#include "stat-time.h"
+
+#include "bashintl.h"
+
+#include "shell.h"
+#include "pathexp.h"
+#include "test.h"
+#include "builtins/common.h"
+
+#include <glob/strmatch.h>
+
+#if !defined (STRLEN)
+#  define STRLEN(s) ((s)[0] ? ((s)[1] ? ((s)[2] ? strlen(s) : 2) : 1) : 0)
+#endif
+
+#if !defined (STREQ)
+#  define STREQ(a, b) ((a)[0] == (b)[0] && strcmp ((a), (b)) == 0)
+#endif /* !STREQ */
+#define STRCOLLEQ(a, b) ((a)[0] == (b)[0] && strcoll ((a), (b)) == 0)
+
+#if !defined (R_OK)
+#define R_OK 4
+#define W_OK 2
+#define X_OK 1
+#define F_OK 0
+#endif /* R_OK */
+
+#define EQ     0
+#define NE     1
+#define LT     2
+#define GT     3
+#define LE     4
+#define GE     5
+
+#define NT     0
+#define OT     1
+#define EF     2
+
+/* The following few defines control the truth and false output of each stage.
+   TRUE and FALSE are what we use to compute the final output value.
+   SHELL_BOOLEAN is the form which returns truth or falseness in shell terms.
+   Default is TRUE = 1, FALSE = 0, SHELL_BOOLEAN = (!value). */
+#define TRUE 1
+#define FALSE 0
+#define SHELL_BOOLEAN(value) (!(value))
+
+#define TEST_ERREXIT_STATUS    2
+
+static procenv_t test_exit_buf;
+static int test_error_return;
+#define test_exit(val) \
+       do { test_error_return = val; longjmp (test_exit_buf, 1); } while (0)
+
+extern int sh_stat __P((const char *, struct stat *));
+
+static int pos;                /* The offset of the current argument in ARGV. */
+static int argc;       /* The number of arguments present in ARGV. */
+static char **argv;    /* The argument list. */
+static int noeval;
+
+static void test_syntax_error __P((char *, char *)) __attribute__((__noreturn__));
+static void beyond __P((void)) __attribute__((__noreturn__));
+static void integer_expected_error __P((char *)) __attribute__((__noreturn__));
+
+static int unary_operator __P((void));
+static int binary_operator __P((void));
+static int two_arguments __P((void));
+static int three_arguments __P((void));
+static int posixtest __P((void));
+
+static int expr __P((void));
+static int term __P((void));
+static int and __P((void));
+static int or __P((void));
+
+static int filecomp __P((char *, char *, int));
+static int arithcomp __P((char *, char *, int, int));
+static int patcomp __P((char *, char *, int));
+
+static void
+test_syntax_error (format, arg)
+     char *format, *arg;
+{
+  builtin_error (format, arg);
+  test_exit (TEST_ERREXIT_STATUS);
+}
+
+/*
+ * beyond - call when we're beyond the end of the argument list (an
+ *     error condition)
+ */
+static void
+beyond ()
+{
+  test_syntax_error (_("argument expected"), (char *)NULL);
+}
+
+/* Syntax error for when an integer argument was expected, but
+   something else was found. */
+static void
+integer_expected_error (pch)
+     char *pch;
+{
+  test_syntax_error (_("%s: integer expression expected"), pch);
+}
+
+/* Increment our position in the argument list.  Check that we're not
+   past the end of the argument list.  This check is supressed if the
+   argument is FALSE.  Made a macro for efficiency. */
+#define advance(f) do { ++pos; if (f && pos >= argc) beyond (); } while (0)
+#define unary_advance() do { advance (1); ++pos; } while (0)
+
+/*
+ * expr:
+ *     or
+ */
+static int
+expr ()
+{
+  if (pos >= argc)
+    beyond ();
+
+  return (FALSE ^ or ());              /* Same with this. */
+}
+
+/*
+ * or:
+ *     and
+ *     and '-o' or
+ */
+static int
+or ()
+{
+  int value, v2;
+
+  value = and ();
+  if (pos < argc && argv[pos][0] == '-' && argv[pos][1] == 'o' && !argv[pos][2])
+    {
+      advance (0);
+      v2 = or ();
+      return (value || v2);
+    }
+
+  return (value);
+}
+
+/*
+ * and:
+ *     term
+ *     term '-a' and
+ */
+static int
+and ()
+{
+  int value, v2;
+
+  value = term ();
+  if (pos < argc && argv[pos][0] == '-' && argv[pos][1] == 'a' && !argv[pos][2])
+    {
+      advance (0);
+      v2 = and ();
+      return (value && v2);
+    }
+  return (value);
+}
+
+/*
+ * term - parse a term and return 1 or 0 depending on whether the term
+ *     evaluates to true or false, respectively.
+ *
+ * term ::=
+ *     '-'('a'|'b'|'c'|'d'|'e'|'f'|'g'|'h'|'k'|'p'|'r'|'s'|'u'|'w'|'x') filename
+ *     '-'('G'|'L'|'O'|'S'|'N') filename
+ *     '-t' [int]
+ *     '-'('z'|'n') string
+ *     '-o' option
+ *     string
+ *     string ('!='|'='|'==') string
+ *     <int> '-'(eq|ne|le|lt|ge|gt) <int>
+ *     file '-'(nt|ot|ef) file
+ *     '(' <expr> ')'
+ * int ::=
+ *     positive and negative integers
+ */
+static int
+term ()
+{
+  int value;
+
+  if (pos >= argc)
+    beyond ();
+
+  /* Deal with leading `not's. */
+  if (argv[pos][0] == '!' && argv[pos][1] == '\0')
+    {
+      value = 0;
+      while (pos < argc && argv[pos][0] == '!' && argv[pos][1] == '\0')
+       {
+         advance (1);
+         value = 1 - value;
+       }
+
+      return (value ? !term() : term());
+    }
+
+  /* A paren-bracketed argument. */
+  if (argv[pos][0] == '(' && argv[pos][1] == '\0') /* ) */
+    {
+      advance (1);
+      value = expr ();
+      if (argv[pos] == 0) /* ( */
+       test_syntax_error (_("`)' expected"), (char *)NULL);
+      else if (argv[pos][0] != ')' || argv[pos][1]) /* ( */
+       test_syntax_error (_("`)' expected, found %s"), argv[pos]);
+      advance (0);
+      return (value);
+    }
+
+  /* are there enough arguments left that this could be dyadic? */
+  if ((pos + 3 <= argc) && test_binop (argv[pos + 1]))
+    value = binary_operator ();
+
+  /* Might be a switch type argument */
+  else if (argv[pos][0] == '-' && argv[pos][2] == '\0')
+    {
+      if (test_unop (argv[pos]))
+       value = unary_operator ();
+      else
+       test_syntax_error (_("%s: unary operator expected"), argv[pos]);
+    }
+  else
+    {
+      value = argv[pos][0] != '\0';
+      advance (0);
+    }
+
+  return (value);
+}
+
+static int
+stat_mtime (fn, st, ts)
+     char *fn;
+     struct stat *st;
+     struct timespec *ts;
+{
+  int r;
+
+  r = sh_stat (fn, st);
+  if (r < 0)
+    return r;
+  *ts = get_stat_mtime (st);
+  return 0;
+}
+
+static int
+filecomp (s, t, op)
+     char *s, *t;
+     int op;
+{
+  struct stat st1, st2;
+  struct timespec ts1, ts2;
+  int r1, r2;
+
+  if ((r1 = stat_mtime (s, &st1, &ts1)) < 0)
+    {
+      if (op == EF)
+       return (FALSE);
+    }
+  if ((r2 = stat_mtime (t, &st2, &ts2)) < 0)
+    {
+      if (op == EF)
+       return (FALSE);
+    }
+  
+  switch (op)
+    {
+    case OT: return (r1 < r2 || (r2 == 0 && timespec_cmp (ts1, ts2) < 0));
+    case NT: return (r1 > r2 || (r1 == 0 && timespec_cmp (ts1, ts2) > 0));
+    case EF: return (same_file (s, t, &st1, &st2));
+    }
+  return (FALSE);
+}
+
+static int
+arithcomp (s, t, op, flags)
+     char *s, *t;
+     int op, flags;
+{
+  intmax_t l, r;
+  int expok;
+
+  if (flags & TEST_ARITHEXP)
+    {
+      l = evalexp (s, &expok);
+      if (expok == 0)
+       return (FALSE);         /* should probably longjmp here */
+      r = evalexp (t, &expok);
+      if (expok == 0)
+       return (FALSE);         /* ditto */
+    }
+  else
+    {
+      if (legal_number (s, &l) == 0)
+       integer_expected_error (s);
+      if (legal_number (t, &r) == 0)
+       integer_expected_error (t);
+    }
+
+  switch (op)
+    {
+    case EQ: return (l == r);
+    case NE: return (l != r);
+    case LT: return (l < r);
+    case GT: return (l > r);
+    case LE: return (l <= r);
+    case GE: return (l >= r);
+    }
+
+  return (FALSE);
+}
+
+static int
+patcomp (string, pat, op)
+     char *string, *pat;
+     int op;
+{
+  int m;
+
+  m = strmatch (pat, string, FNMATCH_EXTFLAG|FNMATCH_IGNCASE);
+  return ((op == EQ) ? (m == 0) : (m != 0));
+}
+
+int
+binary_test (op, arg1, arg2, flags)
+     char *op, *arg1, *arg2;
+     int flags;
+{
+  int patmatch;
+
+  patmatch = (flags & TEST_PATMATCH);
+
+  if (op[0] == '=' && (op[1] == '\0' || (op[1] == '=' && op[2] == '\0')))
+    return (patmatch ? patcomp (arg1, arg2, EQ) : STREQ (arg1, arg2));
+  else if ((op[0] == '>' || op[0] == '<') && op[1] == '\0')
+    {
+      if (shell_compatibility_level > 40 && flags & TEST_LOCALE)
+       return ((op[0] == '>') ? (strcoll (arg1, arg2) > 0) : (strcoll (arg1, arg2) < 0));
+      else
+       return ((op[0] == '>') ? (strcmp (arg1, arg2) > 0) : (strcmp (arg1, arg2) < 0));
+    }
+  else if (op[0] == '!' && op[1] == '=' && op[2] == '\0')
+    return (patmatch ? patcomp (arg1, arg2, NE) : (STREQ (arg1, arg2) == 0));
+    
+
+  else if (op[2] == 't')
+    {
+      switch (op[1])
+       {
+       case 'n': return (filecomp (arg1, arg2, NT));           /* -nt */
+       case 'o': return (filecomp (arg1, arg2, OT));           /* -ot */
+       case 'l': return (arithcomp (arg1, arg2, LT, flags));   /* -lt */
+       case 'g': return (arithcomp (arg1, arg2, GT, flags));   /* -gt */
+       }
+    }
+  else if (op[1] == 'e')
+    {
+      switch (op[2])
+       {
+       case 'f': return (filecomp (arg1, arg2, EF));           /* -ef */
+       case 'q': return (arithcomp (arg1, arg2, EQ, flags));   /* -eq */
+       }
+    }
+  else if (op[2] == 'e')
+    {
+      switch (op[1])
+       {
+       case 'n': return (arithcomp (arg1, arg2, NE, flags));   /* -ne */
+       case 'g': return (arithcomp (arg1, arg2, GE, flags));   /* -ge */
+       case 'l': return (arithcomp (arg1, arg2, LE, flags));   /* -le */
+       }
+    }
+
+  return (FALSE);      /* should never get here */
+}
+
+
+static int
+binary_operator ()
+{
+  int value;
+  char *w;
+
+  w = argv[pos + 1];
+  if ((w[0] == '=' && (w[1] == '\0' || (w[1] == '=' && w[2] == '\0'))) || /* =, == */
+      ((w[0] == '>' || w[0] == '<') && w[1] == '\0') ||                /* <, > */
+      (w[0] == '!' && w[1] == '=' && w[2] == '\0'))            /* != */
+    {
+      value = binary_test (w, argv[pos], argv[pos + 2], 0);
+      pos += 3;
+      return (value);
+    }
+
+#if defined (PATTERN_MATCHING)
+  if ((w[0] == '=' || w[0] == '!') && w[1] == '~' && w[2] == '\0')
+    {
+      value = patcomp (argv[pos], argv[pos + 2], w[0] == '=' ? EQ : NE);
+      pos += 3;
+      return (value);
+    }
+#endif
+
+  if ((w[0] != '-' || w[3] != '\0') || test_binop (w) == 0)
+    {
+      test_syntax_error (_("%s: binary operator expected"), w);
+      /* NOTREACHED */
+      return (FALSE);
+    }
+
+  value = binary_test (w, argv[pos], argv[pos + 2], 0);
+  pos += 3;
+  return value;
+}
+
+static int
+unary_operator ()
+{
+  char *op;
+  intmax_t r;
+
+  op = argv[pos];
+  if (test_unop (op) == 0)
+    return (FALSE);
+
+  /* the only tricky case is `-t', which may or may not take an argument. */
+  if (op[1] == 't')
+    {
+      advance (0);
+      if (pos < argc)
+       {
+         if (legal_number (argv[pos], &r))
+           {
+             advance (0);
+             return (unary_test (op, argv[pos - 1]));
+           }
+         else
+           return (FALSE);
+       }
+      else
+       return (unary_test (op, "1"));
+    }
+
+  /* All of the unary operators take an argument, so we first call
+     unary_advance (), which checks to make sure that there is an
+     argument, and then advances pos right past it.  This means that
+     pos - 1 is the location of the argument. */
+  unary_advance ();
+  return (unary_test (op, argv[pos - 1]));
+}
+
+int
+unary_test (op, arg)
+     char *op, *arg;
+{
+  intmax_t r;
+  struct stat stat_buf;
+  SHELL_VAR *v;
+     
+  switch (op[1])
+    {
+    case 'a':                  /* file exists in the file system? */
+    case 'e':
+      return (sh_stat (arg, &stat_buf) == 0);
+
+    case 'r':                  /* file is readable? */
+      return (sh_eaccess (arg, R_OK) == 0);
+
+    case 'w':                  /* File is writeable? */
+      return (sh_eaccess (arg, W_OK) == 0);
+
+    case 'x':                  /* File is executable? */
+      return (sh_eaccess (arg, X_OK) == 0);
+
+    case 'O':                  /* File is owned by you? */
+      return (sh_stat (arg, &stat_buf) == 0 &&
+             (uid_t) current_user.euid == (uid_t) stat_buf.st_uid);
+
+    case 'G':                  /* File is owned by your group? */
+      return (sh_stat (arg, &stat_buf) == 0 &&
+             (gid_t) current_user.egid == (gid_t) stat_buf.st_gid);
+
+    case 'N':
+      return (sh_stat (arg, &stat_buf) == 0 &&
+             stat_buf.st_atime <= stat_buf.st_mtime);
+
+    case 'f':                  /* File is a file? */
+      if (sh_stat (arg, &stat_buf) < 0)
+       return (FALSE);
+
+      /* -f is true if the given file exists and is a regular file. */
+#if defined (S_IFMT)
+      return (S_ISREG (stat_buf.st_mode) || (stat_buf.st_mode & S_IFMT) == 0);
+#else
+      return (S_ISREG (stat_buf.st_mode));
+#endif /* !S_IFMT */
+
+    case 'd':                  /* File is a directory? */
+      return (sh_stat (arg, &stat_buf) == 0 && (S_ISDIR (stat_buf.st_mode)));
+
+    case 's':                  /* File has something in it? */
+      return (sh_stat (arg, &stat_buf) == 0 && stat_buf.st_size > (off_t) 0);
+
+    case 'S':                  /* File is a socket? */
+#if !defined (S_ISSOCK)
+      return (FALSE);
+#else
+      return (sh_stat (arg, &stat_buf) == 0 && S_ISSOCK (stat_buf.st_mode));
+#endif /* S_ISSOCK */
+
+    case 'c':                  /* File is character special? */
+      return (sh_stat (arg, &stat_buf) == 0 && S_ISCHR (stat_buf.st_mode));
+
+    case 'b':                  /* File is block special? */
+      return (sh_stat (arg, &stat_buf) == 0 && S_ISBLK (stat_buf.st_mode));
+
+    case 'p':                  /* File is a named pipe? */
+#ifndef S_ISFIFO
+      return (FALSE);
+#else
+      return (sh_stat (arg, &stat_buf) == 0 && S_ISFIFO (stat_buf.st_mode));
+#endif /* S_ISFIFO */
+
+    case 'L':                  /* Same as -h  */
+    case 'h':                  /* File is a symbolic link? */
+#if !defined (S_ISLNK) || !defined (HAVE_LSTAT)
+      return (FALSE);
+#else
+      return ((arg[0] != '\0') &&
+             (lstat (arg, &stat_buf) == 0) && S_ISLNK (stat_buf.st_mode));
+#endif /* S_IFLNK && HAVE_LSTAT */
+
+    case 'u':                  /* File is setuid? */
+      return (sh_stat (arg, &stat_buf) == 0 && (stat_buf.st_mode & S_ISUID) != 0);
+
+    case 'g':                  /* File is setgid? */
+      return (sh_stat (arg, &stat_buf) == 0 && (stat_buf.st_mode & S_ISGID) != 0);
+
+    case 'k':                  /* File has sticky bit set? */
+#if !defined (S_ISVTX)
+      /* This is not Posix, and is not defined on some Posix systems. */
+      return (FALSE);
+#else
+      return (sh_stat (arg, &stat_buf) == 0 && (stat_buf.st_mode & S_ISVTX) != 0);
+#endif
+
+    case 't':  /* File fd is a terminal? */
+      if (legal_number (arg, &r) == 0)
+       return (FALSE);
+      return ((r == (int)r) && isatty ((int)r));
+
+    case 'n':                  /* True if arg has some length. */
+      return (arg[0] != '\0');
+
+    case 'z':                  /* True if arg has no length. */
+      return (arg[0] == '\0');
+
+    case 'o':                  /* True if option `arg' is set. */
+      return (minus_o_option_value (arg) == 1);
+
+    case 'v':
+      v = find_variable (arg);
+      return (v && invisible_p (v) == 0 && var_isset (v) ? TRUE : FALSE);
+
+    case 'R':
+      v = find_variable (arg);
+      return (v && var_isset (v) && nameref_p (v) ? TRUE : FALSE);
+    }
+
+  /* We can't actually get here, but this shuts up gcc. */
+  return (FALSE);
+}
+
+/* Return TRUE if OP is one of the test command's binary operators. */
+int
+test_binop (op)
+     char *op;
+{
+  if (op[0] == '=' && op[1] == '\0')
+    return (1);                /* '=' */
+  else if ((op[0] == '<' || op[0] == '>') && op[1] == '\0')  /* string <, > */
+    return (1);
+  else if ((op[0] == '=' || op[0] == '!') && op[1] == '=' && op[2] == '\0')
+    return (1);                /* `==' and `!=' */
+#if defined (PATTERN_MATCHING)
+  else if (op[2] == '\0' && op[1] == '~' && (op[0] == '=' || op[0] == '!'))
+    return (1);
+#endif
+  else if (op[0] != '-' || op[2] == '\0' || op[3] != '\0')
+    return (0);
+  else
+    {
+      if (op[2] == 't')
+       switch (op[1])
+         {
+         case 'n':             /* -nt */
+         case 'o':             /* -ot */
+         case 'l':             /* -lt */
+         case 'g':             /* -gt */
+           return (1);
+         default:
+           return (0);
+         }
+      else if (op[1] == 'e')
+       switch (op[2])
+         {
+         case 'q':             /* -eq */
+         case 'f':             /* -ef */
+           return (1);
+         default:
+           return (0);
+         }
+      else if (op[2] == 'e')
+       switch (op[1])
+         {
+         case 'n':             /* -ne */
+         case 'g':             /* -ge */
+         case 'l':             /* -le */
+           return (1);
+         default:
+           return (0);
+         }
+      else
+       return (0);
+    }
+}
+
+/* Return non-zero if OP is one of the test command's unary operators. */
+int
+test_unop (op)
+     char *op;
+{
+  if (op[0] != '-' || op[2] != 0)
+    return (0);
+
+  switch (op[1])
+    {
+    case 'a': case 'b': case 'c': case 'd': case 'e':
+    case 'f': case 'g': case 'h': case 'k': case 'n':
+    case 'o': case 'p': case 'r': case 's': case 't':
+    case 'u': case 'v': case 'w': case 'x': case 'z':
+    case 'G': case 'L': case 'O': case 'S': case 'N':
+      return (1);
+    }
+
+  return (0);
+}
+
+static int
+two_arguments ()
+{
+  if (argv[pos][0] == '!' && argv[pos][1] == '\0')
+    return (argv[pos + 1][0] == '\0');
+  else if (argv[pos][0] == '-' && argv[pos][2] == '\0')
+    {
+      if (test_unop (argv[pos]))
+       return (unary_operator ());
+      else
+       test_syntax_error (_("%s: unary operator expected"), argv[pos]);
+    }
+  else
+    test_syntax_error (_("%s: unary operator expected"), argv[pos]);
+
+  return (0);
+}
+
+#define ANDOR(s)  (s[0] == '-' && !s[2] && (s[1] == 'a' || s[1] == 'o'))
+
+/* This could be augmented to handle `-t' as equivalent to `-t 1', but
+   POSIX requires that `-t' be given an argument. */
+#define ONE_ARG_TEST(s)                ((s)[0] != '\0')
+
+static int
+three_arguments ()
+{
+  int value;
+
+  if (test_binop (argv[pos+1]))
+    {
+      value = binary_operator ();
+      pos = argc;
+    }
+  else if (ANDOR (argv[pos+1]))
+    {
+      if (argv[pos+1][1] == 'a')
+       value = ONE_ARG_TEST(argv[pos]) && ONE_ARG_TEST(argv[pos+2]);
+      else
+       value = ONE_ARG_TEST(argv[pos]) || ONE_ARG_TEST(argv[pos+2]);
+      pos = argc;
+    }
+  else if (argv[pos][0] == '!' && argv[pos][1] == '\0')
+    {
+      advance (1);
+      value = !two_arguments ();
+    }
+  else if (argv[pos][0] == '(' && argv[pos+2][0] == ')')
+    {
+      value = ONE_ARG_TEST(argv[pos+1]);
+      pos = argc;
+    }
+  else
+    test_syntax_error (_("%s: binary operator expected"), argv[pos+1]);
+
+  return (value);
+}
+
+/* This is an implementation of a Posix.2 proposal by David Korn. */
+static int
+posixtest ()
+{
+  int value;
+
+  switch (argc - 1)    /* one extra passed in */
+    {
+      case 0:
+       value = FALSE;
+       pos = argc;
+       break;
+
+      case 1:
+       value = ONE_ARG_TEST(argv[1]);
+       pos = argc;
+       break;
+
+      case 2:
+       value = two_arguments ();
+       pos = argc;
+       break;
+
+      case 3:
+       value = three_arguments ();
+       break;
+
+      case 4:
+       if (argv[pos][0] == '!' && argv[pos][1] == '\0')
+         {
+           advance (1);
+           value = !three_arguments ();
+           break;
+         }
+       /* FALLTHROUGH */
+      default:
+       value = expr ();
+    }
+
+  return (value);
+}
+
+/*
+ * [:
+ *     '[' expr ']'
+ * test:
+ *     test expr
+ */
+int
+test_command (margc, margv)
+     int margc;
+     char **margv;
+{
+  int value;
+  int code;
+
+  USE_VAR(margc);
+
+  code = setjmp (test_exit_buf);
+
+  if (code)
+    return (test_error_return);
+
+  argv = margv;
+
+  if (margv[0] && margv[0][0] == '[' && margv[0][1] == '\0')
+    {
+      --margc;
+
+      if (margv[margc] && (margv[margc][0] != ']' || margv[margc][1]))
+       test_syntax_error (_("missing `]'"), (char *)NULL);
+
+      if (margc < 2)
+       test_exit (SHELL_BOOLEAN (FALSE));
+    }
+
+  argc = margc;
+  pos = 1;
+
+  if (pos >= argc)
+    test_exit (SHELL_BOOLEAN (FALSE));
+
+  noeval = 0;
+  value = posixtest ();
+
+  if (pos != argc)
+    test_syntax_error (_("too many arguments"), (char *)NULL);
+
+  test_exit (SHELL_BOOLEAN (value));
+}
index 5f8dea91568942870fa6cacf09680fd539ecc748..96d47e52072d2f513613dc9f5485359298622e51 100644 (file)
@@ -1,3 +1,7 @@
+recho "A${*:-w}R"
+recho "A${*-w}R"
+recho "A${*}R"
+
 set -- ""
 
 recho "A${*:-w}R"
diff --git a/tests/dollar-star6.sub~ b/tests/dollar-star6.sub~
new file mode 100644 (file)
index 0000000..5f8dea9
--- /dev/null
@@ -0,0 +1,5 @@
+set -- ""
+
+recho "A${*:-w}R"
+recho "A${*-w}R"
+recho "A${*}R"
index 4b0c0b90f1578853d71e2fa38366fb9ef81cfc55..07e02a7acefdebaf0ee612ad0715a0c52603d79e 100644 (file)
@@ -20,7 +20,7 @@ bar ()
     exec {v}> $TMPFILE;
     echo $v
 }
-11
+10
 line 1
 line 2
 line 3
@@ -35,11 +35,11 @@ EOF
 
     echo $v
 }
-11
+10
 foo 1
 foo 2
 foo 3
-11
+10
 /bin/bash
 /bin/csh
 /bin/ksh
@@ -64,7 +64,7 @@ iclosev ()
 /bin/zsh
 ./vredir3.sub: line 4: v: ambiguous redirect
 after
-11 12
+10 11
 a
 a
 swizzle is a function
@@ -75,7 +75,7 @@ swizzle ()
     exec {stdin}<&$fd0;
     exec {stdout}>&$fd1
 }
-13 11
+12 10
 a
 a
 swizzle is a function
diff --git a/trap.c b/trap.c
index a127d2182789a0b9302a128526df7eb03f198467..9b290458454517abc44aa53ea33ae108634d2426 100644 (file)
--- a/trap.c
+++ b/trap.c
@@ -414,10 +414,7 @@ trap_handler (sig)
        {
          wait_signal_received = sig;
          if (interrupt_immediately)
-{
-itrace("trap_handler: interrupt_immediately = 1: calling longjmp to wait_intr_buf: sig = %d", sig);
            longjmp (wait_intr_buf, 1);
-}
        }
 
 #if defined (READLINE)
index 21700998aee1043ec9a7371ba682f32dfe5111f4..f8530e8e4848f0c2a5c2abf61856e2dea4077a54 100644 (file)
@@ -2192,7 +2192,6 @@ make_local_variable (name)
       return ((SHELL_VAR *)NULL);
     }
 
-itrace("make_local_variable: name = %s old_var = %p variable_context = %d", name, old_var, variable_context);
   if (old_var == 0)
     new_var = make_new_variable (name, vc->table);
   else
@@ -2855,6 +2854,9 @@ assign_in_env (word, flags)
     setifs (var);
 else
 #endif
+if (ifsname (name))
+  itrace("assign_in_env: name = %s value = <%s> flags = %d context = %d", name, value, flags, variable_context);
+
   if (flags)
     stupidly_hack_special_variables (name);
 
@@ -4365,7 +4367,6 @@ push_context (name, is_subshell, tempvars)
      int is_subshell;
      HASH_TABLE *tempvars;
 {
-itrace("push_context: %s: subshell = %d variable_context = %d", name, is_subshell, variable_context);
   if (is_subshell == 0)
     push_dollar_vars ();
   variable_context++;
@@ -4377,7 +4378,6 @@ itrace("push_context: %s: subshell = %d variable_context = %d", name, is_subshel
 void
 pop_context ()
 {
-itrace("pop_context: variable_context = %d", variable_context);
   pop_dollar_vars ();
   variable_context--;
   pop_var_context ();