]> git.ipfire.org Git - thirdparty/bash.git/commitdiff
commit bash-20040729 snapshot
authorChet Ramey <chet.ramey@case.edu>
Sat, 3 Dec 2011 18:33:56 +0000 (13:33 -0500)
committerChet Ramey <chet.ramey@case.edu>
Sat, 3 Dec 2011 18:33:56 +0000 (13:33 -0500)
45 files changed:
CWRU/CWRU.chlog
CWRU/CWRU.chlog~ [new file with mode: 0644]
MANIFEST
arrayfunc.c
arrayfunc.c~ [new file with mode: 0644]
bashline.c
bashline.c~ [new file with mode: 0644]
builtins/Makefile.in
builtins/Makefile.in~ [new file with mode: 0644]
builtins/command.def
builtins/command.def~ [new file with mode: 0644]
builtins/fc.def
builtins/fc.def~ [new file with mode: 0644]
builtins/hash.def
builtins/hash.def~ [new file with mode: 0644]
builtins/reserved.def
builtins/reserved.def~ [new file with mode: 0644]
builtins/trap.def
builtins/trap.def~ [new file with mode: 0644]
doc/bashref.texi
doc/bashref.texi~ [new file with mode: 0644]
doc/version.texi
doc/version.texi~ [new file with mode: 0644]
general.c
general.c~ [new file with mode: 0644]
include/shmbutil.h
include/shmbutil.h~ [new file with mode: 0644]
jobs.c
jobs.c~ [new file with mode: 0644]
lib/readline/misc.c
lib/readline/misc.c~ [new file with mode: 0644]
locale.c
locale.c~ [new file with mode: 0644]
sig.c
sig.c~ [new file with mode: 0644]
subst.c
subst.c~ [new file with mode: 0644]
tests/RUN-ONE-TEST
tests/RUN-ONE-TEST~ [new file with mode: 0755]
tests/histexp.right
tests/histexp.tests
tests/history.right
tests/history.tests
variables.c
variables.c~ [new file with mode: 0644]

index 3268ef2689e3b24cc39365ba5f245ac1ca6121e5..ecfcc0cdb95e11b8b7154da66c98637ddae0211a 100644 (file)
@@ -9675,3 +9675,91 @@ general.c
 
 externs.h
        - add declaration for zcatfd
+
+tests/{history,histexp}.tests
+       - unset HISTFILESIZE to avoid problems if a value of 0 is inherited
+         from the environment
+
+                                  7/30
+                                  ----
+bashline.c
+       - small changes to glob_expand_word to perform tilde expansion before
+         attempting globbing
+
+builtins/Makefile.in
+       - fix the install-help target to not cd into the `helpfiles'
+         subdirectory, so a value of $INSTALL_DATA containing a relative
+         pathname (e.g., .././support/install.sh) remains valid
+
+                                  7/31
+                                  ----
+subst.c
+       - new function, mbstrlen(s), returns length of a multibyte character
+         string
+
+include/shmbutil.h
+       - new macro, MB_STRLEN(s), calls mbstrlen or STRLEN as appropriate
+
+builtins/trap.def
+       - small change so that a first argument that's a valid signal number
+         (digits only -- no symbolic names) will be treated as a signal and
+         reverted back to the original handling disposition.  Fixes debian
+         complaints
+
+subst.c
+       - call MB_STRLEN instead of STRLEN where appropriate in
+         parameter_brace_expand_length to handle multibyte characters properly
+       - call MB_STRLEN instead of strlen in verify_substring_values so that
+         negative substrings of strings with multibyte chars work properly
+
+                                   8/1
+                                   ---
+jobs.c
+       - describe_pid needs to write to stderr, not stdout (POSIX)
+       - start_job, since it's only used by builtins (fg/bg), needs to write
+         its output to stdout, not stderr (POSIX)
+
+sig.c
+       - add an `orig_flags' member to struct terminating_signal so the
+         original signal handling flags (SA_RESTART, etc.) can be preserved
+         on POSIX systems
+       - make sure to preserve the signal flags state in
+         initialize_terminating_signals and reset them for child processes
+         in reset_terminating_signals
+
+builtins/fc.def
+       - fixed an off-by-one error that caused `fc -l' to list one too many
+         history entries
+       - in posix mode, `fc' should not list any indication as to whether or
+         not history lines have been modified (POSIX)
+       - when in posix mode, the default editor for `fc' should be `ed' (POSIX)
+
+doc/bashref.texi
+       - updated the description of `trap' behavior when given a first
+         argument that is a valid signal number
+       - noted that `fc -l' won't indicate whether a history entry has been
+         modified if the shell is in posix mode
+
+builtins/command.def
+       - fixed bug: `command -v' is supposed to be silent if a command is not
+         found
+
+builtins/hash.def
+       - `hash' should print its `hash table empty' message to stderr
+
+lib/readline/misc.c
+       - back out 7/7 change to _rl_maybe_save_line; it breaks emacs-mode ^P
+
+general.c
+       - changed base_pathname so that it will return reasonable results for
+         non-absolute pathnames -- this is what is intended by all of its
+         callers
+
+arrayfunc.c
+       - fix array_variable_part to return NULL if it finds an invisible
+         variable in the hash table.  Fixes seg fault caused by referring to
+         unset local variable using array notation
+
+{locale,variables}.c
+       - support LC_TIME as a special locale variable so HISTTIMEFORMAT tracks
+         the current locale
diff --git a/CWRU/CWRU.chlog~ b/CWRU/CWRU.chlog~
new file mode 100644 (file)
index 0000000..ecfcc0c
--- /dev/null
@@ -0,0 +1,9765 @@
+                                4/9/2001
+                                --------
+[bash-2.05 released]
+
+                                  4/10
+                                  ----
+redir.c
+       - check return value of fclose() in write_here_document() for error
+         returns; don't just rely on fwrite() failing
+
+support/bashbug.sh
+       - set TMPDIR to /tmp if it's null or unset
+       - use $TMPDIR in the TEMP tempfile name template
+       - fixed the call to `mktemp', if it exists, to make it more portable
+
+jobs.c
+       - if WCONTINUED is not defined, define it to 0 and add a define for
+         WIFCONTINUED(wstatus) which expands to 0
+       - add WCONTINUED to the flags passed to waitpid(2) in waitchld()
+       - don't increment children_exited if waitpid's status is WIFCONTINUED,
+         since we don't want to call a SIGCHLD trap handler in this case
+       - in waitchld(), we set child->running to 1 if WIFCONTINUED(status)
+         is non-zero
+       - make sure pretty_print_job doesn't check for the core dump bit if
+         the process has been continued; it's only valid if the job is dead
+       - in set_job_status_and_cleanup, set the job to JRUNNING if job_state
+         is non-zero and the job was previously marked as JSTOPPED
+
+configure.in
+       - add -DBROKEN_DIRENT_D_INO to interix LOCAL_CFLAGS
+
+lib/glob/glob.c
+       - if BROKEN_DIRENT_D_INO is defined, define REAL_DIR_ENTRY to 1
+
+jobs.c
+       - in kill_pid, we only need to block and unblock SIGCHLD if the
+         `group' argument is non-zero, since otherwise we just call `kill'
+         on the pid argument
+
+version.c
+       - update copyright date to 2001
+
+bashline.c
+       - prog_complete_return needs to take a `const char *' as its first
+         argument
+       - history_completion_generator needs to take a `const char *' as
+         its first argument, and `text' needs to be a `const char *'
+
+                                  4/11
+                                  ----
+redir.c
+       - fixed a weird typo in redir_special_open, case RF_DEVFD, added
+         call to all_digits before call to legal_number
+       - fixed do_redirection_internal to call legal_number instead of atol(3)
+         when translating r_duplicating_{in,out}put_word, so it handles
+         overflow better
+       - produce an error message in redirection_error for out-of-range
+         file descriptors
+       - change allocation strategy in redirection_error so we don't have to
+         malloc scratch memory if redirection_expand() fails
+
+jobs.h
+       - added defines for `running' member of a struct process
+
+general.c
+       - fix legal_number to return 0 when strtol(3) reports overflow or
+         underflow
+
+parse.y
+       - changed read_token_word to call legal_number instead of atoi(3)
+
+input.c
+       - return -1/EBADF from close_buffered_fd if fd is < 0
+
+command.h
+       - fixed bogus comment about IS_DESCRIPTOR in description of the
+         REDIRECTEE struct
+
+print_cmd.c
+       - change cprintf's 'd' modifier code to display negative numbers as
+         an out-of-range value.  We can do this only because the only use
+         of %d is to output file descriptor numbers in redirections
+
+support/mksignames.c
+       - need to include config.h to get a possible value for
+         UNUSABLE_RT_SIGNALS
+
+                                  4/16
+                                  ----
+lib/readline/doc/rluser.texinfo
+       - corrected a small error in one description of M-DEL
+
+                                  4/17
+                                  ----
+stringlib.c
+       - need to initialize `ind' before calls to RESIZE_MALLOCED_BUFFER
+         in strcreplace()
+
+support/bashversion.c
+       - new file, prints bash version information
+
+Makefile.in
+       - rules for building bashversion and linking it to version.o
+
+                                  4/24
+                                  ----
+conftypes.h
+       - new file with HOSTTYPE, OSTYPE, MACHTYPE, etc. defines from
+         variables.h
+
+variables.h, version.c
+       - include conftypes.h
+
+patchlevel.h
+       - new file, contains define for PATCHLEVEL.  Doing away with the old
+         scheme of having the information in configure.in
+
+version.c
+       - include patchlevel.h
+
+Makefile.in
+       - run bashversion -p to find patch level rather than have configure
+         substitute in a value
+       - pass -S ${top_srcdir} to support/mkversion.sh
+
+support/mkversion.sh
+       - don't put PATCHLEVEL define into version.h, but accept and ignore
+         a -p option
+       - take a new -S srcdir option
+       - find the patch level by parsing it out of patchlevel.h
+
+configure.in
+       - hard-code BASHVERS assignment instead of reading it from a file
+       - remove BASHPATCH; don't substitute it
+
+_distribution,_patchlevel
+       - removed
+
+                                  4/26
+                                  ----
+shell.c
+       - call init_noninteractive() in open_shell_script if forced_interactive
+         is non-zero (the shell was started with -i) and fd_is_tty is 0
+         (the script file is a real file, not something like /dev/stdin),
+         since it wasn't done earlier
+
+builtins/printf.def
+       - change for POSIX.2 compliance when conversion errors are encountered
+         when processing %d, %u, and floating point conversion operators
+         (print a warning message, return the value accumulated at the time
+         of the error -- which is always 0 -- and exit with a non-zero status)
+
+command.h
+       - added CMD_COMMAND_BUILTIN for use by the `command' builtin and the
+         code in execute_cmd.c
+
+builtins/command.def
+       - add CMD_COMMAND_BUILTIN to the created command's flags
+
+                                   5/1
+                                   ---
+configure.in
+       - add call to AC_C_CONST to test `const' compiler behavior
+       - add call to AC_C_INLINE to test `inline' compiler behavior
+       - add call to AC_C_STRINGIZE to test cpp #x stringizing operator
+
+config.h.in
+       - add `#undef const' for configure to substitute
+       - add `#undef inline' for configure to substitute
+       - add `#undef HAVE_STRINGIZE' for configure to substitute
+
+include/stdc.h
+       - remove code that defines or undefines `const' and `inline'
+       - change the __STRING macro to be defined depending on the value
+         of HAVE_STRINGIZE
+
+lib/malloc/malloc.c
+       - change the __STRING macro to be defined depending on the value
+         of HAVE_STRINGIZE
+
+lib/readline/{readline,rlprivate}.h
+       - moved rl_get_termcap to readline.h, making it a public function
+
+lib/readline/readline.h
+       - new #define, RL_READLINE_VERSION, hex-encoded library version
+         number, currently set to 0x0402
+       - new public int variable, rl_readline_version
+
+lib/readline/readline.c
+       - #define RL_READLINE_VERSION if it is not already defined (which it
+         should be in readline.h)
+       - initialize rl_readline_version to RL_READLINE_VERSION
+
+lib/readline/doc/rltech.texinfo
+       - documented rl_get_termcap
+       - documented rl_readline_version
+
+jobs.c
+       - job_exit_status should return an int, not a WAIT (undetected
+         before because on most POSIX-like systems a WAIT is really an int)
+
+builtins/evalfile.c
+       - added FEVAL_REGFILE (file must be a regular file) to accepted
+          _evalfile flags
+       - fc_execute_file() adds FEVAL_REGFILE to _evalfile flags.  This
+         means that startup files and files read with `.' no longer need
+         to be regular files
+
+                                   5/2
+                                   ---
+
+lib/termcap/Makefile.in
+       - fix target for installed termcap library (normally unused)
+
+lib/tilde/Makefile.in
+       - fix install target to install in $(libdir) (normally unused)
+
+Makefile.in
+       - don't make $(man3dir) since there's nothing installed there
+
+Makefile.in,doc/Makefile.in
+       - change `man1ext' to `.1', `man3ext' to `.3'
+       - change appropriate install targets to use new values of man[13]ext
+       - use `test ...' instead of `[...]'
+       - add support for DESTDIR root installation prefix, for package
+         building (installdirs, install, install-strip, uninstall targets)
+
+builtins/common.c
+       - new function int get_exitstat(WORD_LIST *list) returns an eight-bit
+         exit status value for use in return, exit, logout builtins
+
+builtins/common.h
+       - extern declaration for get_exitstat()
+
+builtins/{exit,return}.def
+       - call get_exitstat where appropriate
+
+builtins/printf.def
+       - add support for "'" flag character as posix 1003.2-200x d6 says
+       - fix core dump when user-supplied field width or precision is 0
+       - fix to printstr() to handle zero-length precision with `%b' format
+         specifier (printf '%.0b-%.0s\n' foo bar)
+       - fix to printstr() to treat a negative field width as a positive
+         field width with left-justification
+       - fix to mklong to avoid static buffers, which can always be overrun
+         by someone sufficiently motivated
+
+bashline.c
+       - change var in add_host_name to type `size_t' for passing to xrealloc
+
+                                   5/3
+                                   ---
+execute_cmd.c
+       - change restore_signal_mask to accept a sigset_t *, since a sigset_t
+         may not fit into a pointer, change call
+
+unwind_prot.c
+       - use a union UWP in restore_variable when restoring a variable whose
+         size is the same as sizeof(int), the reverse of the method used to
+         store it in unwind_protect_int
+
+builtins/printf.def
+       - use a #define LENMODS containing the length modifiers instead of
+         testing against each possible modifier character, save any mod
+         character found
+       - add support for ISO C99 length specifiers `j', `t', and `z'
+       - if `L' modifier is supplied with a floating point conversion char,
+         pass a `long double' to printf if HAVE_LONG_DOUBLE is defined
+
+configure.in,config.h.in
+       - call AC_C_LONG_DOUBLE to check for `long double'; define
+         HAVE_LONG_DOUBLE if supported
+
+bashline.c
+       - fix an inadvertantly-unclosed comment in attempt_shell_completion
+       - make set_saved_history return a value
+       - make dynamic_complete_history return a useful value
+
+{make_cmd,execute_cmd,shell,subst,trap,variables,input,unwind_prot,test,
+pcomplete}.c
+       - removed some declared-but-unused variables
+
+builtins/{cd,enable,fc,set,setattr,type,umask,printf,complete}.def
+       - removed some declared-but-unused variables
+
+lib/sh/{zread,netopen}.c
+       - removed some declared-but-unused variables
+
+execute_cmd.c
+       - in execute_arith_command, use a long variable to hold the result
+         of evalexp(), since that's what it returns
+
+builtins/evalstring.c
+       - make cat_file return -1 on a read or write error
+
+lib/sh/stringlib.c
+       - make merge_stringlists() return the right value
+
+                                   5/7
+                                   ---
+pcomplete.c
+       - remove typo that caused empty declaration (;;)
+
+parse.y
+       - fix yyerror() to accept a single string argument; fix callers
+
+trap.c
+       - cast pointer to long instead of int when printing message with
+         internal_warning() in run_pending_traps()
+
+subst.c
+       - fix process_substitute to handle stdin being closed
+
+test.c
+       - change `while' to `if' in and() and or(), since the loop isn't
+         actually performed -- there's an unconditional `return' in the
+         loop body
+       - check for integer overflow of arguments to `-t'
+
+lib/sh/netopen.c
+       - change _getserv() to reject negative port/service numbers
+
+expr.c
+       - fix strlong() to not convert the base specification from long to
+         int before checking for overflow, since truncation on machines
+         where sizeof(int) != sizeof(long) may mask errors
+
+builtins/{jobs,kill,wait}.def
+       - use legal_number instead of atoi when converting strings to pid_t;
+         check for numeric overflow
+
+input.c
+       - fix for cygwin in b_fill_buffer -- off-by-one error when checking
+         buffer for \r\n termination
+
+general.h
+       - new #define INT_STRLEN_BOUND(t), computes max length of string
+         representing integer value of type T, possibly including a sign
+         character
+       - include <limits.h> if it's present
+
+{execute_cmd,findcmd,test}.c
+       - don't include <limits.h>, since general.h does it now
+
+{execute_cmd,lib/sh/itos,pcomplete,print_cmd,subst,variables}.c
+       - use INT_STRLEN_BOUND instead of static array sizes when converting
+         various strings to integer values
+
+shell.h
+       - struct fd_bitmap now uses an `int' size, since it's bounded by
+         the number of file descriptors, which must fit into an `int'
+
+execute_cmd.c
+       - FD_BITMAP_DEFAULT_SIZE is now 32, not 32L
+       - new_fd_bitmap takes an `int' size parameter, not a `long'
+
+execute_cmd.h
+       - change prototype for new_fd_bitmap()
+
+test.c
+       - fix test_stat to check for overflow when parsing the integer file
+         descriptor number; return ENOENT instead of EBADF for files that
+         are not open
+
+hashlib.c
+       - don't discard the upper 32 bits of the random value, if present
+
+lib/readline/shell.c
+       - use the same INT_STRLEN_BOUND mechanism to decide how much space to
+         allocated in sh_set_lines_and_columns
+
+                                   5/8
+                                   ---
+aclocal.m4
+       - add check for libtinfo (termcap-specific portion of ncurses-5.2) to
+         BASH_CHECK_LIB_TERMCAP
+       - new macro, RL_LIB_READLINE_VERSION, checks version of installed
+         readline library and (optionally) writes version #defines to
+         config.h.  Bash doesn't use the version defines
+
+configure.in
+       - call RL_LIB_READLINE_VERSION instead of support/rlvers.sh
+
+execute_cmd.c
+       - fix execute_shell_script and the WHITECHAR and STRINGCHAR macros
+         to check array bounds before indexing into the sample string
+
+unwind_prot.[ch]
+       - import new versions submitted by Paul Eggert <eggert@twinsun.com>
+         with a couple of changes for backwards compatibility, so the rest
+         of the source doesn't need to be changed yet
+
+jobs.c
+       - use unwind_protect_var on last_made_pid in run_sigchld_trap
+
+builtins/bind.def
+       - use unwind_protect_var on rl_outstream
+
+general.c
+       - rework print_rlimtype to use INT_STRLEN_BOUND and handle the
+         most negative number correctly
+
+expr.c
+       - `tokval' should have been a `long', since all arithmetic is done
+         as longs
+
+builtins/history.def
+       - consolidate tests for valid history position in one block to
+         avoid duplicate code and strings
+
+builtins/ulimit.def
+       - fix check for overflow when setting limit to work when int is 32
+         bits and RLIMTYPE is 64
+
+lib/sh/tmpfile.c
+       - don't truncate the result of time(3) to int; just use time_t,
+         since it's being assigned to an `unsigned long'
+
+mailcheck.c
+       - use legal_number instead of atoi in time_to_check_mail() to catch
+         more numeric errors; consolidate error checking in one block
+       - last_time_mail_checked should be a time_t
+
+                                   5/9
+                                   ---
+builtins/set.def
+       - recognize `set [-+]o nolog' if HISTORY is defined
+
+bashline.c
+       - new variable `dont_save_function_defs', set by `set -o nolog';
+         currently ignored
+
+command.h
+       - the `dest' member of a REDIRECTEE is now an `int'
+
+parse.y,redir.c
+       - changed uses of `redir.test' (where redir is a REDIRECTEE) since
+         it's now an int
+
+lib/readline/rlstdc.h
+       - don't mess around with `const', rely on configure to supply a
+         proper definition if the compiler doesn't support it
+
+lib/tilde/tilde.h
+       - include <config.h> if HAVE_CONFIG_H is defined
+       - don't mess around with `const', rely on configure
+
+builtins/shopt.def
+       - new read-only `shopt' option, login_shell, non-zero if shell is a
+         login shell (as decided by shell.c)
+       - new function set_login_shell(), sets shopt private value of
+         login_shell
+
+builtins/common.h
+       - new extern declaration for set_login_shell
+
+shell.c
+       - call set_login_shell after setting value of login_shell (in
+         main() and set_shell_name())
+
+parse.y
+       - added new `\A' prompt string escape sequence:  time in 24-hour
+         HH:MM format
+
+configure.in, config.h.in
+       - check for <grp.h>, define HAVE_GRP_H if found
+
+builtins/complete.def
+       - add new `-A group/-g' option to complete group names
+
+pcomplete.h
+       - new define for CA_GROUP, used with group name completion
+
+pcomplete.c
+       - add code to support CA_GROUP group name completion
+
+bashline.c
+       - new function, bash_groupname_completion_function(), supports
+         programmable completion of group names
+
+bashline.h
+       - extern declaration for bash_groupname_completion_function
+
+lib/readline/bind.c
+       - new inputrc variable, `match-hidden-files', controls completion
+         matching files beginning with a `.' (on Unix)
+
+lib/readline/complete.c
+       - new variable, _rl_match_hidden_files, mirrors `match-hidden-files'
+         inputrc variable
+
+lib/readline/rlprivate.h
+       - extern declaration for _rl_match_hidden_files
+
+builtins/hash.def
+       - new `-t' option to list hash values for each filename argument
+
+builtins/read.def
+       - alarm(3) takes an `unsigned int' argument, not int
+       - check for arithmetic overflow with -t and -n options
+
+input.c
+       - check for read error before doing \r\n translation on cygwin in
+         b_fill_buffer
+       - reset bp->b_used to 0 instead of leaving it at -1 on read error
+         in b_fill_buffer
+
+builtins/shopt.def
+       - new functions, shopt_setopt(name, mode) and
+         shopt_listopt(name, mode) to give the rest of the shell an easy
+         interface
+
+builtins/common.h
+       - extern declarations for shopt_setopt and shopt_listopt
+
+shell.c
+       - new invocation options -O and +O, to list or set/unset shopt
+         options like +o/-o sets and unsets `set -o' options
+
+doc/{bash.1,bashref.texi}
+       - document `set -o nolog'
+       - document `login_shell' shopt option
+       - document new `\A' prompt string escape sequence
+       - document new `-t' option to `hash'
+       - document new `[+-]O' invocation option
+
+doc/bashref.texi
+       - add text to `Invoking Bash' section defining a login shell; text
+         taken from man page
+
+doc/bash.1, lib/readline/doc/rluser.texinfo
+       - documented new complete/compgen `-A group/-g' option
+
+lib/readline/doc/{rluser.texinfo,readline.3}, doc/bash.1
+       - documented new `match-hidden-files' inputrc variable
+
+                                  5/10
+                                  ----
+configure.in
+       - fix AC_CHECK_PROG(ar, ...)
+       - add AC_CHECK_TYPE for ssize_t
+
+config.h.in
+       - new #undef for ssize_t
+
+lib/sh/zread.c
+       - int -> ssize_t fixes to mirror modern declarations of read and write
+       - the `off' variable in zsyncfd should be an off_t since it computes
+         a file offset
+       - the local buffer `lbuf' is now char, since it's not nice to pass
+         unsigned char * to read(2), and the values from it are assigned to
+         a char anyway
+       - lind and lused are now size_t, since they index into a buffer
+       - set lused to 0 on read error
+
+lib/sh/zwrite.c
+       - change second argument to type `char *', since ISO C says you have
+         to pass a `char *' to `write'
+
+externs.h
+       - fix extern declarations of zread, zread1, zreadc, and zwrite
+       - prototype extern declaration of qsort_string_compare
+       - add extern declaration for history_delimiting_chars() from parse.y
+
+input.h
+       - b_used and b_inputp members ofr struct BSTREAM are now size_t
+
+builtins/evalstring.c
+       - the number of chars read with zread in cat_file should be assigned
+         to a variable of type ssize_t
+
+input.c
+       - the number of chars read with zread in b_fill_buffer should be
+         assigned to a variable of type ssize_t
+       - `localbuf' is now type char[], since POSIX says you shouldn't pass
+         unsigned char * to read(2)
+       - in getc_with_restart(), use a variable of type unsigned char to
+         get a value from the local buffer and return it
+       - in ungetc_with_restart, explicitly return the character arg passed
+         to avoid relying on localbuf being unsigned char
+
+subst.c
+       - the number of chars read with zread in read_comsub should be
+         assigned to a variable of type ssize_t
+
+mksyntax.c
+       - instead of casting to unsigned char * in addcstr, use a variable
+         of type unsigned char and let the compiler do the work
+
+parse.y
+       - instead of casting to unsigned char * in yy_readline_get, use a
+         variable of type unsigned char and let the compiler do the work
+       - ditto for yy_string_get and shell_getc (cast to unsigned char)
+
+subst.c
+       - instead of casting to unsigned char when assigning to ifscmap in
+         expand_word_internal, use a variable of type unsigned char and
+         let the compiler do the work
+
+lib/sh/strtrans.c
+       - instead of casting to unsigned char in ansic_quote, use a variable
+         of type unsigned char and let the compiler do the work
+
+builtins/evalstring.c
+       - remove extern declarations for zwrite and run_trap_cleanup; they're
+         in externs.h
+       - prototype cat_file forward declaration
+
+Makefile.in
+       - remove -I$(includedir) from INCLUDES and SUBDIR_INCLUDES
+
+aclocal.m4
+       - change RL_LIB_READLINE_VERSION to set RL_PREFIX, RL_LIBDIR,
+         and RL_INCLUDEDIR to what it used to test the installed readline
+         library version for use by the caller
+       - change RL_LIB_READLINE_VERSION to not compute ac_cv_rl_prefix if
+         the caller has already assigned it a value
+       - rename _rl_prefix -> ac_cv_rl_prefix, _rl_libdir -> ac_cv_rl_libdir,
+         _rl_includedir -> ac_cv_rl_includedir
+
+configure.in
+       - change testing of whether to use the value of
+         $opt_with_installed_readline to be != no, to allow the user to
+         specify a prefix where the installed readline library may be found
+       - if --with-installed-readline=PREFIX is supplied, set ac_cv_rl_prefix
+         to PREFIX before calling RL_LIB_READLINE_VERSION
+       - if --with-installed-readline[=PREFIX] is supplied, don't set
+         RL_LIBDIR and RL_INCLUDEDIR; let RL_LIB_READLINE_VERSION take care
+         of it, set RL_INCLUDE=-I${RL_INCLUDEDIR}
+       - if --with-installed-readline[=PREFIX] is supplied, and we're
+         linking with the history library, assign $RL_LIBDIR to HIST_LIBDIR
+         so we use the same version of the installed readline and history
+         libraries
+
+Makefile.in, builtins/Makefile.in
+       - have configure substitute RL_INCLUDEDIR, set RL_INCLUDEDIR variable
+
+doc/bashref.texi
+       - updated description of --with-installed-readline configure option 
+
+general.c
+       - moved QSFUNC typedef here from builtins/common.c
+
+{alias,bashline,variables,lib/sh/stringvec}.c
+       - cast fourth argument to qsort to (QSFUNC *)
+
+alias.c
+       - prototype forward declaration of qsort_alias_compare
+
+bashhist.c
+       - include <glob/glob.h> for extern declaration of glob_pattern_p
+       - remove extern declaration of history_delimiting_chars; it's now
+         in externs.h
+       - prototype forward declarations of histignore_item_func,
+         maybe_add_history, and bash_add_history
+
+bracecomp.c
+       - remove extern declaration for sh_backslash_quote; it's in externs.h
+
+braces.c
+       - remove extern declaration for extract_command_subst; it's in subst.h
+       - prototype forward declarations for expand_amble, array_concat, and
+         brace_gobbler
+
+error.c
+       - prototype extern declaration of give_terminal_to, fix bad call
+
+{execute_cmd,expr,findcmd,jobs,mailcheck,nojobs,pcomplete,print_cmd,redir,
+shell}.c
+       - prototype all static forward function declarations
+
+pcomplete.c
+       - changed some function parameters to `const char *' to avoid discarding
+         const qualifier
+
+make_cmd.c
+       - make_bare_word, make_word_flags, and make_word now take a
+         `const char *' string argument
+
+make_cmd.h
+       - changed extern declarations for make_bare_word and make_word
+
+print_cmd.c
+       - cprintf now takes a `const char *' as its first argument, like
+         xprintf and printf
+       - the conditional define for xprintf should have been HAVE_VPRINTF,
+         not HAVE_VFPRINTF
+
+shell.c
+       - in isnetconn(), the return value of sizeof() is size_t
+
+aclocal.m4
+       - add inclusion of stddef.h if STDC_HEADERS is defined to 1 in
+         BASH_CHECK_TYPE
+
+configure.in
+       - add a call to BASH_CHECK_TYPE for socklen_t (type of third argument
+         to getpeername(2))
+
+                                  5/11
+                                  ----
+lib/readline/bind.c
+       - make `useq' a char array to pass to rl_macro_bind in
+         rl_parse_and_bind
+
+lib/readline/{{bind,isearch}.c,rlprivate.h}
+       - _rl_isearch_terminators is now a char *, not unsigned char *
+
+{subst,variables,lib/sh/tmpfile}.c
+       - dollar_dollar_pid is now a `pid_t' instead of `int'
+
+variables.c
+       - sbrand() now takes an `unsigned long' to set the seed value
+       - changed last_random_value to type int, since it's always between
+         0 and 32767
+       - use strtoul to convert the value in assign_random instead of atoi
+       - take out casts in any arguments to sbrand()
+       - take out cast to int in call to inttostr in set_ppid()
+
+subst.c
+       - don't cast last_asynchronous_pid when passing to itos()
+
+{sig,subst}.c
+       - prototype all static forward function declarations
+
+                                  5/14
+                                  ----
+{test,trap,variables}.c
+       - prototype all static forward function declarations
+
+variables.c
+       - free_variable_hash_data() now takes a PTR_T, a `generic pointer'
+
+builtins/{alias,bind,break,cd,complete,declare,enable,exit,fc,fg_bg,help,
+history,jobs,pushd,read,set,trap,umask,
+       - prototype all static forward function declarations
+
+builtins/read.def
+       - reset_eol_delim now takes a `char *' arg, since that's what the
+         unwind_protect functions pass it, and it ignores its arguments
+         anyway
+
+lib/readline/{histsearch,input,kill,rltty,search,vi_mode}.c
+       - prototype all static forward function declarations
+
+lib/tilde/tilde.c
+       - prototype all static forward function declarations
+       - tilde_find_prefix, tilde_find_suffix, isolate_tilde_prefix, and
+         glue_prefix_and_suffix now take `const char *' arguments where
+         appropriate
+
+configure.in,config.h.in
+       - check for vsnprintf, define HAVE_VSNPRINTF if found
+
+lib/readline/display.c
+       - use vsnprintf() in rl_message if it's available; if we don't, at
+         least set the last character in msg_buf to 0 to avoid overrun --
+         we really can't do anything about overflow at this point.  if it's
+         available, this fixes buffer overflow problems in rl_message
+
+                                  5/15
+                                  ----
+lib/readline/histexpand.c
+       - in get_history_word_specifier, allow any character to terminate
+         a `:first-' modifier, not just `:' and null.  This is what csh
+         appears to do.  This allows things like `!:0- xyzzy' to replace the
+         last argument with xyzzy
+
+                                  5/18
+                                  ----
+configure.in, config.h.in
+       - check for <stdint.h>, define HAVE_STDINT_H if found
+       - check for intmax_t in <stdint.h>, define intmax_t as long if not
+         found
+
+                                  5/21
+                                  ----
+builtins/kill.def
+       - change to use strerror() for error message when kill(2) fails
+
+aclocal.m4
+       - new macro, BASH_C_LONG_LONG, check for `long long'
+
+configure.in, config.h.in
+       - call BASH_C_LONG_LONG, define HAVE_LONG_LONG if found
+
+lib/sh/snprintf.c
+       - new file, with implementations of snprintf, vsnprintf, asprintf,
+         and vasprintf, derived from inetutils version
+
+Makefile.in, lib/sh/Makefile.in
+       - add snprintf.c/snprintf.o
+
+configure.in, config.h.in
+       - add checks for snprintf, asprintf, vasprintf, with appropriate
+         cpp defines
+
+lib/readline/{rldefs,xmalloc}.h, lib/readline/xmalloc.c
+       - xmalloc and xrealloc now take `size_t' arguments, like their bash
+         counterparts
+
+externs.h,lib/sh/itos.c
+       - inttostr and itos now take `long' arguments
+       - inttostr takes a `size_t' argument for the buffer size
+
+{expr,lib/malloc/malloc,variables,general}.c
+       - fixed calls to itos() by removing casts, etc.
+
+subst.[ch]
+       - get_dollar_var_value now takes a long, not an int
+       - sub_append_number now takes a long, not an int
+
+subst.c
+       - in parameter_brace_expand_word, use a long and legal_number to
+         translate ${N}, to avoid overflow
+       - in parameter_brace_expand_length, use a long and legal_number to
+         translate ${#N}, to avoid overflow
+       - in do_array_element_assignment, array_expand_index,
+         array_value_internal, use arrayind_t instead of int
+       - let verify_substring_values take long * arguments for the return
+         value of evalexp()
+       - pass long * arguments to verify_substring_values in
+         parameter_brace_substring
+       - parameter_brace_expand_length now returns `long'
+       - parameter_brace_expand now uses a long variable for the return
+         value of parameter_brace_expand_length
+       - param_expand now uses a long variable for the return value from
+         evalexp
+       - array_length reference now returns an `arrayind_t', since it can
+         return the num_elements member of an array, which is of type
+         arrayind_t
+
+subst.h
+       - array_expand_index now returns an `arrayind_t'
+
+array.[ch]
+       - array_subrange now takes arrayind_t arguments, not `int'
+       - dup_array_subrange now uses arrayind_t local variable to do
+         array indexing
+       - use long to print array indices in print_element
+
+variables.c
+       - null_array_assign, assign_dirstack, bind_array_variable
+         now take arrayind_t arguments as array indices
+       - assign_array_var_from_word_list, assign_array_var_from_string,
+         unbind_array_element now use arrayind_t local variables for
+         array indexing
+
+variables.h
+       - change extern declaration of bind_array_variable
+
+builtins/common.[ch]
+       - get_numeric_arg now returns a `long', since it usually returns
+         the value of legal_number()
+
+builtins/{shift,break}.def
+       - use long variables for the return value of get_numeric_arg
+
+builtins/history.def
+       - convert string argument to int only if it's in range
+
+builtins/pushd.def
+       - set_dirstack_element and get_dirstack_element now take `long'
+         index arguments
+       - get_dirstack_index now takes a `long' index argument, since it's
+         passed the converted value from legal_number
+
+lib/sh/timeval.c
+       - in print_timeval, don't assume that the number of minutes fits into
+         an int, since it's just seconds/60.
+
+lib/sh/clock.c
+       - ditto for print_clock_t
+
+                                  5/22
+                                  ----
+shell.c
+       - since the -O option settings may possibly be overridden by the
+         normal shell initialization or posix initialization, save the
+         invocation options on an alist (with add_shopt_to_alist) and
+         process them after basic initialization (with run_shopt_alist)
+
+                                  5/23
+                                  ----
+trap.h
+       - new define, BASH_NSIG, all system signals plus special bash traps
+
+trap.c, builtins/trap.def
+       - use BASH_NSIG for array bounds and loops where appropriate
+
+trap.c
+       - change decode_signal to disallow numeric signal numbers above
+         NSIG -- this means you can only reference special traps like
+         DEBUG by name
+       - new SPECIAL_TRAP(s) macro to test whether s is one of the special
+         bash traps (currently DEBUG and EXIT)
+       - change reset_or_restore_signal_handlers so command substitution
+         doesn't inherit the debug trap (like ksh93), and child processes
+         don't have to rely on initialize_traps being run to get rid of
+         any debug trap
+
+support/mksignames.c
+       - add extra "ERR" signal name, value NSIG+1, allocate space for it
+         and write it out in signal_names[]
+
+trap.h
+       - new define: ERROR_TRAP == NSIG+1, change BASH_NSIG to NSIG+2
+       - extern declarations for set_error_trap, run_error_trap
+       - new define: TRAP_STRING(s), expands to trap_list[s] if signal S
+         is trapped and not ignored, NULL otherwise
+
+trap.c
+       - add ERROR_TRAP to SPECIAL_TRAPS define
+       - initialize ERROR_TRAP stuff in initialize_traps
+       - new function: set_error_trap(command), sets the ERR trap string
+       - new function: run_error_trap(command), runs the ERR trap string
+       - set trap string for ERROR_TRAP to NULL in free_trap_strings
+       - change reset_or_restore_signal_handlers so child processes don't
+         inherit the ERR trap
+       - add case to call run_error_trap in maybe_call_trap_handler
+
+execute_cmd.c
+       - in execute_command_internal, keep track of ERR trap and call it if
+         necessary
+       - use TRAP_STRING to get the value of debug and error traps
+       - in execute_function, arrange things so the ERR trap is not inherited
+         by shell functions, and is saved and restored like the DEBUG trap
+
+doc/{bash.1,bashref.texi}
+       - documented new ERR trap
+
+tests/{trap.{tests,right},trap2.sub,trap2a.sub}
+       - added ERR trap tests
+
+subst.c
+       - on machines without /dev/fd, change the named pipe fifo list to a
+         list of structs containing pathname and proc information
+       - change unlink_fifo_list to kill the proc in the fifo list with
+         signal 0 and not remove the fifo if the proc is still alive.  This
+         should fix the problem on those backward systems without /dev/fd
+         where fifos were removed when a job using process substitution was
+         suspended
+
+                                  5/24
+                                  ----
+examples/loadables/getconf.h
+       - new file, with basic defines needed to make getconf work minimally
+         on POSIX systems without the necessary definitions
+
+examples/loadables/getconf.c
+       - replacement functions for confstr, sysconf, pathconf for systems
+         that lack them, providing a minimal posix interface
+       - heavily augmented getconf, now supports all POSIX.1-200x,
+         POSIX.2-200x, Solaris 7, AIX 4.2 getconf variables
+
+                                  5/29
+                                  ----
+builtins/setattr.def
+       - make `readonly', `export', and `declare' print `invisible' variables
+         as just a command and variable name, without a value, when listing
+         all variables (as POSIX.2-200x d6 requires)
+
+                                  5/30
+                                  ----
+
+configure.in
+       - upgraded to autoconf-2.50 on main development machine, so require
+         autoconf-2.50 in preparation for using some if its new features
+       - call AC_C_PROTOTYPES
+       - remove call to AC_EXEEXT, which now does the wrong thing
+       - changed AC_INIT to new flavor
+       - added call to AC_CONFIG_SRCDIR
+       - AC_CONFIG_HEADER -> AC_CONFIG_HEADERS
+       - AC_RETSIGTYPE -> AC_TYPE_SIGNAL
+
+configure.in, aclocal.m4, config.h.in
+       - removed call to BASH_LARGE_FILE_SUPPORT, use AC_SYS_LARGEFILE
+         standard support, with new macros _FILE_OFFSET_BITS and
+         _LARGE_FILES
+       - removed definition of BASH_LARGE_FILE_SUPPORT
+
+doc/bashref.texi
+       - document new `--enable-largefile' configure option
+
+lib/readline/readline.c
+       - change rl_set_prompt to call rl_expand_prompt unconditionally, so
+         local_prompt and local_prompt_prefix get set correctly
+
+                                   6/6
+                                   ---
+lib/readline/complete.c
+       - don't append `/' or ` ' to a match when completing a symlink that
+         resolves to a directory, unless the match doesn't add anything
+         to the word.  This means that a tab will complete the word up to
+         the full name, but not add anything, and a subsequent tab will add
+         a slash.  Change to append_to_match; callers changed
+
+hashlib.c
+       - new function, hash_table_nentries (table), returns the number of
+         items in TABLE
+
+hashlib.h
+       - extern declaration for hash_table_nentries
+
+configure.in
+       - configure without bash malloc on openbsd; they claim it needs
+         eight-bit alignment (which the bash malloc provides, but...)
+
+                                   7/2
+                                   ---
+stringlib.c
+       - only call RESIZE_MALLOCED_BUFFER from strsub() if the replacement
+         string length is > 0, avoid possible hangs if replacement is null
+
+subst.c
+       - don't include input.h; no longer needed
+
+configure.in
+       - remove calls to AC_SYS_RESTARTABLE_SYSCALLS and
+         BASH_SYS_RESTARTABLE_SYSCALLS; the results are no longer used
+
+config.h.in
+       - remove define for HAVE_RESTARTABLE_SYSCALLS
+
+aclocal.m4
+       - removed definition of BASH_SYS_RESTARTABLE_SYSCALLS; no longer used
+
+execute_cmd.c
+       - changed select command so `return' no longer terminates the select
+         command, so it can be used to return from an enclosing function.
+         This is as ksh (88 and 93) does it
+
+lib/readline/vi_mode.c
+       - fix trivial typo in declaration of vi_motion; `t' appears twice;
+         the second instance should be `T'
+
+                                   7/3
+                                   ---
+configure.in
+       - don't add -static to LDFLAGS on Solaris 2.x.  This means that the
+         auxiliary programs will be built as dynamic executables, but that
+         should do no harm
+
+                                   7/5
+                                   ---
+lib/glob/fnmatch.c
+       - fix the code that processes **(pattern) to short-circuit if the
+         pattern is ill-formed or lacks a trailing `)'  -- this fixes the
+         segfault on **(/*)
+
+Makefile.in, builtins/Makefile.in
+       - split CCFLAGS into CCFLAGS_FOR_BUILD and CFLAGS, to aid in
+         cross-compilation
+       - build programs that use $(CC_FOR_BUILD) using $(CCFLAGS_FOR_BUILD)
+
+configure.in, config.h.in
+       - check for getaddrinfo(3), define HAVE_GETADDRINFO if found
+
+lib/sh/netopen.c
+       - implemented a version of _netopen (_netopen6) that uses
+         getaddrinfo(3) if available, use if HAVE_GETADDRINFO is defined.
+         old _netopen is _netopen4; _netopen now calls either _netopen6
+         or _netopen4 as appropriate
+
+                                   7/9
+                                   ---
+builtins/exit.def
+       - don't source ~/.bash_logout if subshell_environment is non-zero
+
+execute_command.c
+       - in execute_until_or_while, handle the case where `breaking' is
+         set in the loop test (e.g., by the job control code when a job
+         is stopped with SIGTSTP), but the return value from the test is
+         something that would cause the loop to break.  Need to decrement
+         `breaking' in this case
+
+                                  7/10
+                                  ----
+execute_cmd.c
+       - in execute_in_subshell, make sure a command of type cm_subshell
+         inherits its `enclosing' command's CMD_IGNORE_RETURN flag
+
+variables.c
+       - in maybe_make_export_env, don't allow restricted shells to put
+         exported functions in the export environment
+
+                                  7/11
+                                  ----
+lib/glob/strmatch.h
+       - renamed old fnmatch.h
+       - changed guard #ifdef to _STRMATCH_H
+       - include system <fnmatch.h> if HAVE_LIBC_FNM_EXTMATCH is defined
+
+lib/glob/strmatch.c
+       - renamed old fnmatch.c
+       - include "strmatch.h"
+       - if HAVE_LIBC_FNM_EXTMATCH is defined, define a dummy version of
+         strmatch() that just calls fnmatch(3)
+
+lib/glob/glob.c
+       - include "strmatch.h"
+       - fnmatch -> strmatch
+
+Makefile.in, lib/glob/Makefile.in
+       - fnmatch -> strmatch
+
+{bashhist,execute_cmd,pathexp,pcomplete,shell,stringlib,subst,test}.c,
+pathexp.h,builtins/help.def
+       - include <glob/strmatch.h>
+       - fnmatch -> strmatch
+
+execute_cmd.c
+       - broke the code that parses the interpreter name from a #! line
+         out from execute_shell_script to a new function, getinterp()
+       - call getinterp from execute_shell_script
+       - use return value from getinterp in error message about bad
+         #! interpreter in shell_execve
+
+                                  7/12
+                                  ----
+lib/readline/isearch.c
+       - the last isearch string is now remembered in a new static variable,
+         last_isearch_string
+       - if ^R^R is typed, readline now searches for the remembered isearch
+         string, if one exists
+
+                                  7/24
+                                  ----
+pcomplete.h
+       - extern declaration for completions_to_stringlist()
+
+                                  7/25
+                                  ----
+builtins/complete.def
+       - make compgen handle -o default option
+       - make compgen return success only if sl->list_len is non-zero,
+         indicating that there are items on the list
+
+                                  7/31
+                                  ----
+execute_cmd.c
+       - in execute_connection, force stdin to /dev/null for asynchronous
+         commands if job control is not active, not just if the shell is
+         running a shell script (since you can run `set -m' in a script)
+
+lib/readline/rltty.c
+       - make sure _rl_tty_restore_signals resets `tty_sigs_disabled' on
+         successful restoration of the terminal modes
+       - make sure _rl_tty_disable_signals turns off IXON so that ^S and
+         ^Q can be read by rl_quoted_insert
+
+                                   8/1
+                                   ---
+aclocal.m4
+       - new check for FNM_EXTMATCH being defined in <fnmatch.h>, as Ullrich
+         Drepper intends to do for new versions of GNU libc
+
+config.h.in
+       - new definition for HAVE_LIBC_FNM_EXTMATCH
+
+configure.in
+       - check for fnmatch, but don't define anything in config.h
+       - call BASH_FUNC_FNMATCH_EXTMATCH to check for FNM_EXTMATCH
+
+                                   8/2
+                                   ---
+alias.h
+       - remove bogus extern declaration for xmalloc()
+       - include "stdc.h"
+       - add prototype declarations for all extern function declarations
+
+xmalloc.c,lib/readline/xmalloc.c
+       - fix xmalloc to return a PTR_T
+       - fix xrealloc to return a PTR_T and take a PTR_T as first argument
+
+include/ansi_stdlib.h
+       - extern declarations for malloc and realloc have them return PTR_T
+
+xmalloc.h
+       - new file, with extern declarations for functions in xmalloc.c
+
+general.h
+       - removed extern declarations for functions in xmalloc.c
+       - include xmalloc.h
+
+Makefile.in,builtins/Makefile.in
+       - update dependencies to include xmalloc.h
+
+parse.y,{alias,array,bashline,bracecomp,execute_cmd,findcmd,flags,general,
+hashcmd,locale,mailcheck,make_cmd,pathexp,pcomplete,print_cmd,stringlib,
+subst,unwind_prot,variables}.c
+builtins/{common,evalfile}.c
+builtins/{cd,command,enable,exec,printf,read,set}.def
+lib/sh/{makepath,netopen,pathphys,setlinebuf,shquote,snprintf,stringlist,
+strtrans,tmpfile}.c
+lib/readline/{util,terminal,shell,readline,macro,kill,isearch,input,
+histfile,histexpand,display,complete,bind}.c
+       - make sure all calls to xmalloc are cast to the right return value
+
+siglist.c
+       - include xmalloc.h
+
+parse.y,{alias,bashline,bracecomp,expr,make_cmd,nojobs,print_cmd,subst}.c
+builtins/{fc,printf,read}.def
+lib/sh/snprintf.c, lib/tilde/tilde.c
+lib/readline/{bind,display,histexpand,isearch,macro,util,vi_mode}.c
+       - make sure all calls to xrealloc are cast to the right return value
+
+lib/sh/{netopen,setlinebuf,shquote,snprintf}.c, lib/tilde/tilde.c
+       - include xmalloc.h, remove extern declaration of xmalloc
+
+lib/readline/xmalloc.h
+       - xmalloc and xrealloc should return PTR_T
+
+lib/readline/rldefs.h
+       - don't include an extern declaration for xmalloc
+
+                                   8/7
+                                   ---
+support/shobj-conf
+       - fixed up commented-out stanzas for HP's unbundled C compiler on
+         HP/UX
+
+support/bashbug.sh
+       - force the subject to be changed from the default
+
+lib/readline/doc/{rluser.texinfo,readline.3}, doc/bash.1
+       - document that transpose-words swaps the last two words on the line
+         if point is at the end of the line
+
+                                   8/9
+                                   ---
+stringlib.c
+       - fix possible infinite recursion problem with null pattern in
+         strsub()
+
+hashlib.c
+       - new function copy_hash_table to copy a hash table using a caller-
+         supplied function to copy item data (defaults to savestring())
+
+hashlib.h
+       - new extern declaration for copy_hash_table
+
+builtins/declare.def
+       - changes so that declare [-a] var=value assigns `value' to element 0
+         of array variable `var' like ksh93
+       - change so that declare [-a] var[N]=value assigns `value' to element
+         N of array variable `var' like ksh93
+
+                                  8/13
+                                  ----
+arrayfunc.c
+       - new file, for miscellaneous array functions
+
+arrayfunc.h
+       - new file, extern declarations for functions in arrayfunc.c
+
+variables.c
+       - move convert_var_to_array, bind_array_variable,
+         assign_array_from_string, assign_array_var_from_word_list,
+         assign_array_var_from_string, quote_array_assignment_chars,
+         skipsubscript, unbind_array_element, print_array_assignment
+         to arrayfunc.c
+
+shell.h
+       - include arrayfunc.h after variables.h
+
+variables.h
+       - remove above extern function declarations moved to arrayfunc.h
+       - add extern declaration for var_lookup
+
+Makefile.in
+       - add arrayfunc.c, arrayfunc.h in appropriate places
+       - add arrayfunc.h to dependencies
+
+subst.c
+       - move valid_array_reference, array_expand_index, array_variable_part,
+         array_value_internal, array_value (now global), get_array_value,
+         do_array_element_assignment to arrayfunc.c
+
+subst.h
+       - extern declarations for functions above moved to arrayfunc.h
+
+arrayfunc.h
+       - extern declarations for above functions from subst.c
+
+subst.[ch]
+       - string_list_dollar_star and string_list_dollar_at are now global
+         functions
+       - quote_escapes is now a global function
+
+subst.c
+       - maybe_expand_string -> expand_string_if_necessary
+       - expand_string_to_string -> expand_string_to_string_internal
+       - new functions: expand_string_to_string and
+         expand_string_unsplit_to_string, which call
+         expand_string_to_string_internal with expand_string and
+         expand_string_unsplit as the FUNC arguments, respectively
+
+arrayfunc.c
+       - change array_expand_index to call expand_string_to_string instead
+         of maybe_expand_string
+
+                                  8/14
+                                  ----
+shell.c
+       - in execute_env_file, call expand_string_unsplit_to_string
+
+mailcheck.c
+       - in check_mail, call expand_string_to_string
+
+variables.c
+       - in assign_in_env, call expand_string_unsplit_to_string
+
+arrayfunc.c
+       - new function, array_variable_name, splits an array reference into
+         a name (which is returned as a new string) and subscript
+       - change array_variable_part to just call array_variable_name and
+         look up the string returned with find_variable
+       - new function, find_or_make_array_variable (name, flags) which will
+         look up an array variable and convert a string variable to an
+         array if necessary.  The FLAGS argument, if non-zero, says to
+         check the readonly and noassign attributes and fail if either is set
+
+builtins/read.def
+       - make `read -a aname' honor any readonly status of `aname'
+       - read -a now calls find_or_make_array_variable with FLAGS value 1
+
+arrayfunc.[ch], subst.c, builtins/{declare,read}.def
+       - do_array_element_assignment -> assign_array_element
+
+                                  8/20
+                                  ----
+parse.y
+       - changed `for' command grammar to allow missing word list after `IN'
+         token, like latest POSIX drafts require
+
+lib/sh/tmpfile.c
+       - in sh_mktmpname(), check for filenum == 0 and init to non-zero number
+         in this case.  it can happen on arithmetic overflow
+
+support/mkversion.sh
+       - added `[0-9].[0-9][0-9][a-z]' as an acceptable value for a
+         distribution to allow for intermediate versions, like 2.05a
+
+support/config.guess
+       - removed the addition of the output of `/usr/bin/objformat' when
+         creating the canonical name on FreeBSD machines, so the canonical
+         name is once again `freebsd4.2' instead of `freebsdelf4.2'
+
+                                  8/22
+                                  ----
+lib/readline/{rlstdc,history,keymaps,readline,rldefs,rlprivate,rlshell,
+rltypedefs,xmalloc}.h
+lib/readline/{bind,compat,complete,display,funmap,histexpand,histsearch,
+input,isearch,kill,nls,parens,readline,rltty,search,shell,signals,vi_mode
+       - changed __P to PARAMS
+
+lib/tilde/tilde.[ch]
+       - changed __P to PARAMS
+
+{Makefile,configure}.in
+       - changed the version number to 2.05a
+       - changed the release status to `alpha1'
+
+                                  8/23
+                                  ----
+support/shobj-conf
+       - support for building shared libraries on Darwin/MacOS X
+
+siglist.h
+       - extern declaration for strsignal() to compensate for lack of
+         a definition in some system include files
+
+jobs.c
+       - remove casts from strsignal() calls
+
+[bash-2.05a-alpha1 frozen]
+
+                                  8/27
+                                  ----
+[bash-2.05a-alpha1 released]
+
+                                  8/27
+                                  ----
+execute_cmd.c
+       - fix eval_arith_for_expr to handle the case where the expanded
+         word list is NULL, returning 0 in this case
+
+print_cmd.c
+       - in print_function_def, make sure that func_redirects is assigned
+         a value before being used
+
+                                  8/28
+                                  ----
+alias.c
+       - include <ctype.h> for definition of isalpha()
+
+bashhist.h
+       - add prototypes for extern function declarations
+
+flags.c
+       - include bashhist.h for extern function declarations
+
+mksyntax.c
+       - include <unistd.h> if HAVE_UNISTD_H is defined in config.h
+
+parse.y
+       - include test.h for extern function declarations
+
+externs.h
+       - change extern declaration for setlinebuf to sh_setlinebuf
+
+stringlib.c
+       - include <glob/glob.h> for extern function declarations
+
+variables.h
+       - add function prototypes for all of the sv_* functions
+
+builtins/common.h
+       - add extern declarations for set_shellopts() and parse_shellopts()
+         from builtins/set.def
+
+variables.c
+       - include "hashcmd.h" for extern declaration for flush_hashed_filenames
+       - include "pathexp.h" for extern declaration for setup_glob_ignore
+
+lib/malloc/malloc.c
+       - cast to `long' instead of `int' in memalign for 64-bit machines
+
+{pcomplete,trap}.c
+       - changed printf escape sequences used to print pointers to %p
+
+lib/readline/undo.c
+       - include "xmalloc.h" for extern function declaration
+
+input.h
+       - add function prototypes to extern declarations for getc_with_restart
+         and ungetc_with_restart
+
+variables.[ch]
+       - changed type of `function' member of `struct name_and_function' to
+         `sv_func_t', which is defined and prototyped in variables.h
+       - map_over now takes an `sh_var_map_func_t *'
+
+shell.h
+       - start of a set of function pointer typedefs like those in
+         lib/readline/rltypedefs.h
+
+hashlib.[ch]
+       - second paramter to flush_hash_table is now an `sh_free_func_t *'
+
+trap.c
+       - parameter to reset_or_restore_signal_handlers is now an
+         `sh_resetsig_func_t *'
+
+pcomplete.h, pcomplib.c
+       - function pointer argument to print_all_compspecs is now an
+         `sh_csprint_func_t *'
+       - function pointer `list_getter' element of an `ITEMLIST' is now
+         prototyped with __P((...)) instead of using `Function *'
+
+jobs.[ch]
+       - `j_cleanup' member of a JOB is now an `sh_vptrfunc_t *'
+
+alias.c
+       - map_over_aliases now takes an `sh_alias_map_func_t *'
+       - free_alias_data now takes a `PTR_T'
+
+pathexp.c
+       - function pointer argument to ignore_globbed_names is now an
+         `sh_ignore_func_t *' 
+
+bashline.c
+       - function pointer argument to _ignore_completion_names is now an
+         `sh_ignore_func_t *' 
+
+pathexp.h,{bashhist,bashline.c
+       - `item_func' member of a `struct ignorevar' is now an
+         `sh_iv_item_func_t *'
+
+builtins/evalfile.c
+       - `errfunc' is now an `sh_vmsg_func_t *'
+
+jobs.c
+       - map_over_job now takes an `sh_job_map_func_t *' as its first argument
+
+array.[ch]
+       - function pointer argument to array_walk is now an
+         `sh_ae_map_func_t *'
+
+general.c
+       - tilde_expansion_preexpansion_hook has type `tilde_hook_func_t *',
+         and so the assignment in tilde_initialize doesn't need a cast
+
+list.c
+       - map_over_words now takes an `sh_icpfunc_t *' as its second argument
+
+input.h
+       - the `getter' and `ungetter' function pointer members of a
+         BASH_INPUT are now of types `sh_cget_func_t *' and
+         `sh_cunget_func_t *' respectively
+       - init_yy_io now takes an `sh_cget_func_t *' as its first argument and
+         an `sh_cunget_func_t *' as its second
+
+parse.y
+       - init_yy_io now takes an `sh_cget_func_t *' as its first argument and
+         an `sh_cunget_func_t *' as its second
+       - initialize_bash_input casts bash_input.getter and bash_input.ungetter
+         appropriately
+
+builtins/mkbuiltins.c
+       - make the extern function definitions written to builtext.h have
+         prototypes with __P((...))
+       - include "stdc.h"
+       - change Function to mk_handler_func_t
+       - fixed comment_handler to take the right number of args
+       - prototyped all the handler functions with __P((...))
+
+builtins.h
+       - the `function' member of a struct builtin is now of type
+         `sh_builtin_func_t *'
+
+builtins/common.[ch]
+       - last_shell_builtin, this_shell_builtin are now of type
+         `sh_builtin_func_t *'
+       - find_shell_builtin, builtin_address, find_special_builtin now return
+         `sh_builtin_func_t *'
+
+builtins/exit.def, {execute_cmd,jobs,nojobs,variables}.c, parse.y
+       - changed all declarations of last_shell_builtin and this_shell_builtin
+
+execute_cmd.c
+       - execute_builtin, execute_builtin_or_function,
+         execute_subshell_builtin_or_function now take an
+         `sh_builtin_func_t *' instead of a `Function *' for argument
+       - changed appropriate variables from `Function *' to
+         `sh_builtin_func_t *'
+
+builtins/{bind,builtin,enable,read,setattr}.def
+       - replaced uses of `Function *' in variable declarations with
+         appropriate types (sh_builtin_func_t * or rl_command_func_t *)
+
+builtins/set.def
+       - set_func and get_func members of binary_o_options are now of types
+         `setopt_set_func_t *' and `setopt_get_func_t *', which are
+         prototyped
+
+builtins/shopt.def
+       - set_func member of shopt_vars is now of type `shopt_set_func_t *'
+
+bashline.c
+       - enable_hostname_completion now returns `int' (the old value of
+         perform_hostname_completion)
+
+[The only use of Function and VFunction now is for unwind-protects]
+
+                                   9/4
+                                   ---
+lib/sh/getcwd.c
+       - use const define from config.h rather than `CONST'
+       - use PTR_T define from xmalloc.h rather than `PTR'
+       - include xmalloc.h for PTR_T
+       - remove PATH_MAX define, rely on value from maxpath.h
+
+{general,mailcheck}.c, lib/sh/{pathcanon,pathphys}.c
+       - don't include maxpath.h directly; it's already included by shell.h
+
+lib/sh/mailstat.c
+       - new `mailstat()' implementation, to stat a mailbox file for
+         mail checking.  handles maildir-style mail directories with one
+         file per message and creates a dummy stat struct from them
+
+lib/sh/Makefile.in
+       - add mailstat.c and mailstat.o in the appropriate places
+
+lib/malloc/malloc.c
+       - augmented implementation with wrapper functions that pass in file
+         and line number information from cpp.  currently unused, but a
+         placeholder for future debugging and use tracking
+
+lib/malloc/shmalloc.h
+       - new file, extern declarations for allocation wrapper functions for
+         use by the shell (and others, I guess)
+
+xmalloc.[ch]
+       - wrapper functions for xmalloc, xfree, xrealloc (sh_ prefixed) that
+         pass cpp line number information through to the malloc functions,
+         if USING_BASH_MALLOC is defined
+
+                                   9/5
+                                   ---
+lib/malloc/gmalloc.c
+       - removed; no longer part of distribution
+
+lib/malloc/Makefile.in
+       - removed references to gmalloc.[co]
+
+configure.in, doc/bashref.texi
+       - removed references to `--with-glibc-malloc' configure option
+
+{configure,Makefile}.in
+       - changed the way bash malloc is configured into the Makefile, making
+         it more like how readline is configured.  If the bash malloc is
+         not configured in, nothing in lib/malloc will be built
+
+                                   9/6
+                                   ---
+lib/malloc/imalloc.h
+       - new file, some internal malloc definitions
+
+lib/malloc/mstats.h
+       - new file, definitions for malloc statistics structs and functions
+
+lib/malloc/trace.c
+       - new file, malloc tracing functions (currently just print messages
+         to stderr), code is #ifdef MALLOC_TRACE
+
+lib/malloc/stats.c
+       - new file, moved malloc stats code from malloc.c to here
+
+lib/malloc/malloc.c
+       - moved some definitions to imalloc.h
+       - moved stats code to stats.c
+       - malloc tracing calls added to internal_{malloc,realloc,free}, all
+         #ifdef MALLOC_TRACE
+
+lib/malloc/Makefile.in, Makefile.in
+       - added {imalloc,mstats}.h, {trace,stats}.c
+
+parse.y
+       - changed decode_prompt_string to save and restore $?
+         (last_command_exit_value) around calls to expand_prompt_string(),
+         so command substitutions in PS1, etc. don't change $?
+
+{array,subst}.c
+       - a couple more arrayind_t fixes from Paul Eggert
+
+configure.in
+       - remove redundant check for wait3(2)
+
+redir.h
+       - fixed a typo (stdin_redirs -> stdin_redirects)
+
+                                  9/10
+                                  ----
+execute_cmd.c
+       - remove check for \n and \r from WHITESPACE macro, since those
+         chars are not whitespace as returned by the whitespace(c) macro
+       - getinterp now takes a `char *' as first arg, not unsigned char *
+       - execute_shell_script now takes a `char *' as first arg, not
+         unsigned char *
+       - fix typo in forward declaration for `initialize_subshell'
+       
+general.[ch]
+       - check_binary_file now takes a (char *) argument, not unsigned char *
+       - pass unsigned char to isspace and isprint because of ISO C fuckup
+       - bash_tilde_expand now takes a `const char *' as its argument
+
+builtins/evalfile.c, shell.c
+       - buffer passed to check_binary_file is char, not unsigned char
+
+parse.y
+       - fix extern declaration for yyerror()
+       - yyerror now takes a `const char *' as first arg
+
+{error,jobs}.c
+       - fixes to printf-style functions to handle pids wider than an int
+
+lib/readline/{isearch,vi_mode}.c
+       - fix call to rl_message in rl_display_search (remove extra arg)
+
+variables.c
+       - fix missing argument to builtin_error in make_local_variable
+
+builtins/getopts.def
+       - since getopts takes no options, change while loop calling
+         internal_getopts to a simple `if' check
+
+builtins/printf.def
+       - since printf takes no options, change while loop calling
+         internal_getopts to a simple `if' check
+
+lib/readline/bind.c
+       - remove _SET_BELL macro, expand code inline
+
+lib/readline/input.c
+       - change _rl_input_available to use either select or FIONREAD,
+         but not both
+
+lib/readline/readline.c
+       - fix rl_digit_loop to remove unreachable code at end of loop
+
+{bashhist,bashline,expr,jobs,redir,shell}.c, builtins/fc.def, lib/sh/snprintf.c
+       - bracket unused functions with #ifdef INCLUDE_UNUSED/#endif
+       - remove some unused variables
+
+execute_cmd.c
+       - remove #ifdef'd code that allowed `return' to terminate a select
+         statement
+
+expr.c
+       - remove some extraneous tests from strlong()
+
+array.h
+       - arrayind_t is now a long, since shell arithmetic is performed as
+         longs
+       - remove second declaration of new_array_element
+
+builtins/printf.def
+       - in mklong, xrealloc cannot return NULL, so don't check for it
+       - remove some #if 0 code
+       - fix core dump triggered by a format specification with more than
+         one `*'
+       - remove `foundmod', since its value mirrors `modchar != 0'
+       - include "common.h" for builtin_{error,usage} declarations
+
+Makefile.in,builtins/Makefile.in
+       - updated some dependencies due to new include files
+
+pcomplete.c
+       - include "execute_cmd.h" for declaration of execute_shell_function
+
+arrayfunc.c
+       - include <stdio.h> for printf
+       - include "builtins/common.h" for builtin_error declaration
+
+builtins/evalstring.c
+       - include "../trap.h" for run_trap_cleanup declaration
+
+builtins/help.def
+       - include "common.h" instead of locally declaring builtin_error
+         and builtin_usage
+
+error.h
+       - add extern declaration for itrace()
+       - add prototype to extern declaration of get_name_for_error
+       - file_error now takes a `const char *' as first argument
+
+externs.h
+       - added prototype for sh_setlinebuf declaration, bracketed with
+         NEED_SH_SETLINEBUF_DECL so we don't need stdio.h everywhere
+       - add extern declaration for parse.y:return_EOF()
+
+shell.c
+       - add NEED_SH_SETLINEBUF_DECL before including shell.h
+
+lib/readline/callback.c
+       - include <stdlib.h> or "ansi_stdlib.h" for abort declaration
+
+quit.h
+       - remove declaration of throw_to_top_level
+
+subst.c
+       - remove unused extern declaration for getopts_reset
+
+lib/sh/netopen.c
+       - include <shell.h> for legal_number, etc.
+       - add prototype for inet_aton extern declaration
+
+lib/sh/clock.c
+       - include <stdc.h> for __P declaration
+       - add extern declaration for get_clk_tck
+
+support/mkversion.sh
+       - changed so that extern function declarations for functions in
+         version.c (moved from externs.h) are in the generated version.h
+
+shell.h
+       - include version.h
+
+version.c
+       - various `char *' version variables are now `const char *'
+
+general.h
+       - add prototype for same_file, bracketed with _POSIXSTAT_H
+         #ifdef, since that's what include/posixstat.h defines
+
+builtins/common.[ch]
+       - _evalfile, maybe_execute_file, source_file, and fc_execute_file
+         now take a `const char *' as their first argument
+
+eval.c
+       - removed extern declaration of yyparse; it's in externs.h
+
+parse.y
+       - added prototypes to static forward function declarations
+       - changed local `all_digits' variable in read_token_word () to
+         all_digit_token to avoid clash with all_digits() function in
+         general.c
+
+{bashhist,copy_cmd,make_cmd,hashlib,mailcheck}.c
+       - added prototypes for static function declarations
+
+shell.h
+       - add extern declarations for interactive, interactive_shell,
+         changed c files with extern declarations
+
+pcomplete.c
+       - changed it_init_aliases to avoid shadowing global variable
+         `aliases'
+
+bashline.c,pathexp.c,general.h
+       - sh_ignore_func_t is now a pointer to a function taking a
+         `const char *'; users changed
+
+configure.in
+       - test for <strings.h>
+
+config.h.in
+       - add #undef HAVE_STRINGS_H
+
+bashansi.h
+       - change like recommended in autoconf manual
+
+                                  9/11
+                                  ----
+[a date which will live in infamy.  prayers for the victims.]
+
+execute_cmd.c
+       - don't use an absolute index into abuf in mkfmt, use
+         sizeof(abuf) to compute last index
+
+builtins/common.c
+       - fix read_octal to do a better job of detecting overflow while
+         iterating through the string
+
+builtins/umask.def
+       - change octal-print mode to print 4 digits, like other shells
+       - cast umask to unsigned long to avoid problems on systems where
+         it's wider than an int (POSIX doesn't guarantee that mode_t is
+         no wider than an int, but real-world systems use int)
+
+builtins/printf.def
+       - mklong can never return NULL (it uses xrealloc), so the mainline
+         doesn't need to check for NULL returns
+       - new function, getldouble (long double *), to get long doubles
+       - mklong now takes a `char *' as its second argument, the modifier(s)
+         to use
+       - changed use of `modchar' to handle more than a single modifier
+         character
+       - changed to handle `long double' and `L' formats better, rather
+         than discarding long double information
+       - since printf now follows the POSIX.2 rules for conversion errors,
+         we can dispense with the status returns from the get* functions
+       - make the get* functions as similar in structure as possible,
+         removing type casts, etc.
+
+lib/sh/timeval.c,execute_cmd.c
+       - change some instances of `long' to `time_t', for systems where
+         a time_t is bigger than a long
+
+jobs.c
+       - include "posixtime.h" instead of <sys/time.h>
+
+config.h.in
+       - add defines for HAVE_DECL_CONFSTR, HAVE_DECL_STRTOLD,
+         HAVE_DECL_SBRK, HAVE_DECL_PRINTF
+       - remove defines for SBRK_DECLARED and PRINTF_DECLARED
+       - add _GNU_SOURCE define
+
+configure.in
+       - add AC_CHECK_DECLS for strtold, confstr, sbrk, printf
+       - remove call to BASH_FUNC_SBRK_DECLARED
+       - remove call to BASH_FUNC_PRINTF
+
+xmalloc.c, lib/malloc/malloc.c
+       - change check of SBRK_DECLARED to HAVE_SBRK_DECL
+
+print_cmd.c
+       - change PRINTF_DECLARED to HAVE_DECL_PRINTF
+
+builtins/evalstring.c, builtins/common.h
+       - parse_and_execute now takes a `const char *' as its second argument
+
+input.h,parse.y
+       - with_input_from_* functions now take a `const char *' as their
+         second argument
+       - init_yy_io now takes a `const char *' as its fourth argument
+
+parse.y,externs.h
+       - parse_string_to_word_list now takes a `const char *' as its second
+         argument
+
+tests/builtins.right
+       - change output to account for extra digit in umask output
+
+pcomplib.c
+       - free_progcomp now takes a PTR_T argument
+
+builtins/bashgetopt.h
+       - include <stdc.h>
+       - add prototypes to extern declarations
+
+builtins/shopt.def
+       - add prototypes to static function declarations
+
+builtins/{fc,umask,wait}.def, builtins/{bashgetopt,common}.c
+       - include <ctype.h> for isdigit macro (referenced by `digit(x)')
+
+lib/readline/complete.c
+       - added more static function declarations with prototypes
+
+                                  9/12
+                                  ----
+lib/sh/tmpfile.c
+       - use `^' instead of `*' in sh_mktmpname to make filenames a bit
+         more random
+
+include/stdc.h,lib/readline/rldstdc.h
+       - add __attribute__ definition 
+
+builtins/common.h
+       - add printf __attribute__ to declaration of builtin_error
+
+error.h
+       - add printf __attribute__ to declaration of programming_error,
+         report_error, parser_error, fatal_error, sys_error, internal_error,
+         internal_warning
+
+lib/readline/readline.h
+       - add printf __attribute__ to declaration of rl_message
+
+pcomplete.c
+       - add printf __attribute__ to declaration of debug_printf
+
+print_cmd.c
+       - add printf __attribute__ to declarations of cprintf, xprintf
+
+include/chartypes.h
+       - new file, includes <ctype.h> and defines macros that check for
+         safe (ascii) arguments before calling the regular ctype macros
+
+{alias,bashline,execute_cmd,expr,findcmd,general,locale,mksyntax,stringlib,subst,variables}.c
+parse.y
+builtins/{bashgetopt,common}.c
+builtins/{fc,printf,umask,wait}.def
+lib/glob/strmatch.c
+lib/sh/{oslib,pathcanon,pathphys,snprintf,strcasecmp,strindex,stringvec,strtod,strtol,strtrans}.c
+examples/loadables/{head,sleep}.c
+       - include "chartypes.h" or <chartypes.h> instead of <ctype.h>
+
+Makefile.in,{builtins,lib/{glob,sh}}/Makefile.in
+       - update dependencies to include chartypes.h
+
+lib/sh/inet_aton.c
+       - use `unsigned char' instead of `char' to pass to ctype.h functions
+
+lib/sh/netopen.c
+       - check for '0' <= host[0] <= '9' in _getaddr instead of using
+         isdigit
+
+subst.c,lib/sh/shquote.c
+       - change array subscripts into sh_syntaxtab from `char' to
+         `unsigned char'
+
+{alias,bashline,execute_cmd,expr,general,subst}.c, parse.y
+builtins/{fc,printf,umask,wait}.def builtins/{bashgetopt,common}.c
+lib/sh/{pathcanon,pathphys,snprintf,strcasecmp,strindex,strtod,strtol,strtrans}.c
+examples/loadables/{head,sleep}.c
+       - change to use some of the new macros in chartypes.h
+       - remove old local macro definitions now provided by chartypes.h
+
+general.h
+       - remove definition of isletter, ISOCTAL, digit, digit_value
+       - change legal_variable_starter and legal_variable_char to use
+         chartypes.h macros
+       - change ABSPATH to use chartypes.h macros
+
+lib/readline/util.c
+       - change to use Paul Eggert's FUNCTION_FOR_MACRO define to define
+         function replacements for macros in chardefs.h
+
+lib/readline/chardefs.h
+       - added some of the same macros as in chartypes.h
+       - change _rl_lowercase_p, _rl_uppercase_p, _rl_digit_p,
+         _rl_to_upper, _rl_to_lower to use new IS* macros
+       - added _rl_isident macro from vi_mode.c:isident
+
+lib/readline/{bind,complete,nls}.c
+       - change to use some of the new macros from chardefs.h
+
+lib/readline/vi_mode.c
+       - isident -> _rl_isident
+       - remove local defines of macros in chardefs.h
+
+lib/sh/strtol.c
+       - updated to new version, modified from glibc 2.2.4 and sh-utils-2.0.
+         This one can do strtoll and strtoull, if necessary
+
+                                  9/13
+                                  ----
+builtins/ulimit.def
+       - changed get_limit so it retrieves both hard and soft limits
+         instead of one or the other
+       - changed callers of get_limit
+       - changed getmaxvm to take soft limit, hard limit as arguments
+       - changed getmaxuprc to just take a single argument, the value
+       - changed calls to printone() to pass soft limit or hard limit
+         depending on `mode' instead of using old current_limit variable
+       - moved check for out-of-range limits in ulimit_internal into the
+         block that converts a string argument to a value of type rlim_t
+       - changed RESOURCE_LIMITS struct to break the description into a
+         description string and separate scale factor string
+       - changed print_all_limits to print a single error message if
+         get_limit fails, including limits[i].description now that the
+         scale factor has been removed from the description string
+       - removed DESCFMT define, since it's now used only in printone()
+       - changed printone to print the option character associated with a
+         particular limit if we're printing multiple limits
+       - changed calls to builtin_error to print the description associated
+         with a limit if setting or getting the limit fails
+       - added support for new POSIX 1003.1-200x rlim_t values:
+         RLIM_SAVED_CUR and RLIM_SAVED_MAX, which expand to the current
+         soft and hard limits, whatever they are
+       - changed printone to print `hard' or `soft' if the current limit is
+         RLIM_SAVED_MAX or RLIM_SAVED_CUR, respectively
+       - changed ulimit_internal to handle new `hard' and `soft' arguments
+       - changed help text do describe the special limit arguments `hard',
+         `soft', and `unlimited'
+
+doc/{bash.1,bashref.texi}
+       - documented new `hard' and `soft' limit arguments to `ulimit'
+
+hashlib.[ch]
+       - find_hash_item now takes a `const char *' is its first argument
+       - hash_string now takes a `const char *' is its first argument
+       - remove_hash_item now takes a `const char *' as its first argument
+
+pcomplib.c
+       - removed cast from first argument to find_hash_item in find_compspec
+
+general.[ch]
+       - absolute_program now takes a `const char *' as its argument
+       - absolute_pathname now takes a `const char *' as its argument
+
+lib/glob/glob.[ch]
+       - glob_pattern_p now takes a `const char *' as its argument
+
+bashline.c
+       - removed cast from first argument to absolute_program in
+         command_word_completion_function
+       - removed cast from first argument to glob_pattern_p in
+         attempt_shell_completion
+
+findcmd.[ch]
+       - find_absolute_program, find_user_command, find_path_file,
+         search_for_command, user_command_matches now take a
+         `const char *' as their first argument
+       - file_status, executable_file, is_directory, executable_or_directory
+         now take a `const char *' as their argument
+       - _find_user_command_internal, find_user_command_internal,
+         find_user_command_in_path 
+
+lib/sh/makepath.c, externs.h
+       - changed sh_makepath so it takes `const char *' for its first
+         two arguments
+
+hashcmd.[ch]
+       - find_hashed_filename now takes a `const char *' as its first arg
+       - remove_hashed_filename now takes a `const char *' as its first arg
+
+variables.[ch]
+       - new_shell_variable, var_lookup, shell_var_from_env_string,
+         find_name_in_env_array, bind_function, makunbound,
+         bind_name_in_env_array, bind_tempenv_variable, bind_variable
+         now take a `const char *' as their first arg
+       - find_function, make_new_variable, find_tempenv_variable,
+         find_variable_internal, find_variable, set_func_read_only,
+         set_func_auto_export, all_variables_matching_prefix, assign_in_env,
+         assignment, kill_local_variable, make_local_variable, unbind_variable
+         now take a `const char *' as their arg
+       - mk_env_string now takes `const char *' arguments
+
+arrayfunc.[ch]
+       - skipsubscript now takes a `const char *' as its argument
+
+                                  9/17
+                                  ----
+lib/readline/complete.c
+       - attempt to preserve case of what the user typed in
+         compute_lcd_of_matches if we're ignoring case in completion
+
+builtins/{let,pushd}.def,{execute_cmd,expr}.c
+       - change some 0L constants to 0 and let the compiler sort it out
+
+                                  9/18
+                                  ----
+lib/malloc/alloca.c
+       - alloca now takes a `size_t' argument
+
+include/memalloc.h
+       - if we're providing an extern function declaration for alloca,
+         use `void *' and prototype if __STDC__ is defined
+       - if HAVE_ALLOCA_H is defined, but C_ALLOCA is defined, don't
+         define HAVE_ALLOCA
+
+                                  9/19
+                                  ----
+subst.c
+       - do_assignment_internal, do_assignment, and do_assignment_no_expand
+         now take a `const char *' as their first argument
+
+general.h
+       - a `sh_assign_func_t' is now a function taking a `const char *' and
+         returning int
+
+hashcmd.c
+       - free_filename_data now takes a `PTR_T' argument to agree with the
+         typedef for `sh_free_func_t'
+
+lib/sh/snprintf.c
+       - use TYPE_MAXIMUM define like strtol.c instead of huge constants
+
+                                  9/20
+                                  ----
+lib/sh/snprintf.c
+       - don't bother to compile the bulk of the body unless HAVE_SNPRINTF
+         or HAVE_ASPRINTF is not defined
+
+                                  9/24
+                                  ----
+flags.c
+       - ignore `set -n' if the shell was started interactively
+
+lib/readline/readline.c
+       - initialize readline_echoing_p to 0; let the terminal-specific code
+         in rltty.c set it appropriately
+
+lib/malloc/malloc.c
+       - changed internal_memalign() slightly to avoid compiler warnings about
+         negating an unsigned variable (-alignment -> (~alignment + 1))
+
+                                  9/27
+                                  ----
+lib/readline/readline.c
+       - changed rl_newline to set _rl_history_saved_point appropriately
+         for the {previous,next}_history code
+
+lib/readline/rlprivate.h
+       - extern declaration for _rl_history_preserve_point
+
+lib/readline/bind.c
+       - new bindable variable, `history-preserve-point', sets value of
+         _rl_history_preserve_point
+
+                                  10/1
+                                  ----
+lib/malloc/table.c
+       - new file, with a map of allocated (and freed) memory for debugging
+         multiple frees, etc.  Indexed by hash on values returned by
+         malloc(); holds size, file and line number info for last alloc or
+         free and a couple of statistics pointers
+
+lib/malloc/malloc.c
+       - a few cleanups; added calls for registering allocations and frees
+         if MALLOC_REGISTER is defined
+       - replaced MALLOC_RETURN with explicit MALLOC_NOTRACE define
+       - reordered fields in `struct...minfo' in `union mhead' to restore
+         eight-byte alignment
+       - added explicit checks for underflow in free and realloc since
+         checking mh_magic2 is not sufficient to detect everything (it's
+         no longer the last field in the struct, and thus not the bytes
+         immediately preceding what's returned to the user)
+       - new function, xbotch, for printing file and line number info for
+         the failed assertion before calling botch() (programming_error())
+
+configure.in
+       - replaced call to BASH_C_LONG_LONG with call to
+         AC_CHECK_TYPES([long long])
+       - moved the C compiler tests before the tests for various
+         system types, so we can know whether we have `long long'
+         before testing for 64-bit types
+       - if we have `long long', check for sizeof(long long) and save value
+
+aclocal.m4
+       - changed BASH_TYPE_BITS64_T to check `long long' before `long', but
+         after `double'
+
+                                  10/2
+                                  ----
+lib/malloc/malloc.c
+       - made malloc and realloc both agree on the rounding for a request of
+         size N (round up to nearest multiple of 8 after adjusting for
+         malloc overhead); uses new ALLOCATED_BYTES macro
+       - realloc and free now use new IN_BUCKET macro for underflow checks
+
+execute_cmd.c
+       - fixed time_command() to use `time_t' instead of `long' to hold
+         time stamps
+
+lib/sh/clock.c
+       - clock_t_to_secs now takes a `time_t *' second argument
+       - fixed print_clock_t to call clock_t_to_secs with right arguments
+
+lib/sh/timeval.c
+       - fixed print_timeval to make `minutes' a `long' and make its
+         structure identical to print_clock_t
+
+redir.c
+       - changed redirection_error to check for EBADF and use the file
+         descriptor being redirected from in the error message if it
+         is >= 0
+
+Makefile.in
+       - changed release status to `beta1'
+
+lib/glob/collsyms.h
+       - added a few ASCII symbols to the posix_collsyms array
+
+                                  10/3
+                                  ----
+aclocal.m4
+       - fixed typo in BASH_TYPE_BITS64_T
+
+configure.in
+       - added check for unsigned chars with AC_C_CHAR_UNSIGNED
+
+config.h.in
+       - added PROTOTYPES and __CHAR_UNSIGNED__ #defines
+
+general.h
+       - if CHAR_MAX is not define by <limits.h>, provide a definition
+
+builtins/printf.def
+       - change tescape() to mask \0 and \x escape sequences with 0xFF
+       - change tescape() to process at most two hex digits after a `\x'
+
+lib/sh/strtrans.c
+       - change strtrans() to mask \0 and \x escape sequences with 0xFF
+       - change strtrans() to process at most two hex digits after a `\x'.
+         This affects `echo -e' and $'...' processing
+
+lib/readline/bind.c
+       - changed rl_translate_keyseq() to process at most two hex digits
+         after a `\x'
+
+lib/readline/doc/{rluser.texinfo,readline.3}, doc/bash.1
+       - changed documentation for key binding escape sequences to specify
+         that at most two hex digits after \x are translated
+       - changed documentation for key binding to specify that the result
+         of \nnn or \xhh escapes is an eight-bit value, not just ASCII
+
+doc/{bash.1,bashref.texi}
+       - changed documentation of $'...' to specify that at most two hex
+         digits after \x are translated
+       - changed `echo' documentation to specify that at most two hex
+         digits after \x are translated
+       - changed documentation for `echo' and $'...' to specify that the
+         result of \nnn or \xhh escapes is an eight-bit value, not just ASCII
+
+                                  10/4
+                                  ----
+lib/malloc/malloc.c
+       - changed interface for xbotch to pass memory address and error code
+         as two additional arguments
+       - call mregister_describe_mem from xbotch to get the last allocation
+         or free before the botch
+
+configure.in
+       - call AC_CHECK_DECLS([strsignal])
+
+config.h.in
+       - add HAVE_DECL_STRSIGNAL
+
+siglist.h
+       - make declaration of strsignal() dependent on !HAVE_DECL_STRSIGNAL
+
+                                  10/5
+                                  ----
+support/texi2html
+       - upgraded to version 1.64
+
+                                  10/9
+                                  ----
+aclocal.m4
+       - added check for `long long' to BASH_TYPE_PTRDIFF_T
+
+configure.in
+       - replaced call to BASH_HAVE_TIOCGWINSZ with AC_HEADER_TIOCGWINSZ
+
+aclocal.m4
+       - replaced body of BASH_STRUCT_TERMIOS_LDISC with call to
+         AC_CHECK_MEMBER(struct termios.c_line, ...)
+       - replaced body of BASH_STRUCT_TERMIO_LDISC with call to
+         AC_CHECK_MEMBER(struct termios.c_line, ...)
+
+[bash-2.05a-beta1 frozen]
+
+                                  10/10
+                                  -----
+lib/sh/snprintf.c
+       - fixed exponent() to not smash the trailing zeros in the fraction
+         when using %g or %G with an `alternate form'
+       - fixed exponent() to handle the optional precision with %g and %G
+         correctly (number of significant digits before the exponent)
+
+                                  10/11
+                                  -----
+expr.c
+       - fixed strlong() to correct the values of `@' and `_' when
+         translating base-64 constants (64#@ == 62 and 64#_ == 64), for
+         compatibility with ksh
+
+lib/sh/itos.c
+       - added a slightly more flexible fmtlong() function that takes a
+         base argument and flags (for future use)
+       - rewrote itos and inttostr in terms of fmtlong
+
+lib/sh/fmtulong.c
+       - new file, converts unsigned long to string.  hooks for `unsigned
+         long long' in the future.  unused as yet
+
+                                  10/15
+                                  -----
+lib/readline/rltty.c
+       - change the SET_SPECIAL macro to avoid possible (but highly
+         unlikely) negative array subscripts
+
+error.h
+       - add __attribute__ to extern declaration of itrace (even though the
+         function isn't defined in released versions of bash)
+
+bashansi.h
+       - include <strings.h> if HAVE_STRINGS_H is defined, to get any extra
+         function declarations provided therein
+
+copy_cmd.c
+       - fix typo in forward declaration for copy_arith_for_command
+
+lib/malloc/stats.c
+       - make the accumulators in _print_malloc_stats be `unsigned long'
+         instead of `int'
+
+externs.h, sig.h
+       - add `__noreturn__' gcc attribute to exit_shell and jump_to_top_level
+         declarations
+
+lib/sh/mailstat.c, support/bashversion.c
+       - include <bashansi.h> for some string function declarations
+
+lib/malloc/shmalloc.h
+       - added extern declarations of functions that do malloc debugging
+
+lib/readline/{isearch,readline,vi_mode}.c
+       - make sure we index into _rl_keymap with a non-negative index
+
+parse.y
+       - make sure we index into sh_syntaxtab with a non-negative index
+
+lib/readline/vi_mode.c
+       - bound the vi_mark_chars array with the number of characters between
+         'a' and 'z' rather than using a fixed amount
+       - don't use _rl_lowercase_p when deciding whether the char read by
+         rl_vi_set_mark is a valid mark; just use 'a' <= char <= 'z'
+
+lib/readline/chardefs.h
+       - conditionally include memory.h and strings.h as in general.h
+       - replace ISASCII with IN_CTYPE_DOMAIN like other GNU software
+       - add defines for ISPRINT(c), ISLOWER(c) and ISUPPER(c)
+       - fix defines for _rl_lowercase_p, _rl_uppercase_p, _rl_digit_p,
+         _rl_pure_alphabetic, ALPHABETIC, _rl_to_upper, _rl_to_lower,
+         and _rl_isident to work on systems with signed chars
+
+include/chartypes.h
+       - replace ISASCII with IN_CTYPE_DOMAIN like other GNU software
+
+lib/sh/{strcasecmp,strtod,strtol}.c
+       - don't pass possibly-negative characters to tolower() or toupper()
+
+lib/glob/strmatch.c
+       - don't bother testing for isupper in FOLD; rely on TOLOWER macro
+         from <chartypes.h> to do it
+       - don't use local definitions of isblank, et al.; rely on macros
+         from <chartypes.h>
+
+lib/readline/{display,readline}.c, mksyntax.c
+       - use new ISPRINT macro instead of isprint()
+
+builtins/{kill.def,mkbuiltins.c},{error,execute_cmd,jobs,nojobs,subst}.c
+       - don't assume that a pid_t fits into an int for printing and other
+         uses
+
+variables.[ch]
+       - the unused put_gnu_argv_flags_into_env now takes a `long' pid
+         argument
+
+configure.in, config.h.in
+       - call AC_STRUCT_ST_BLOCKS, define HAVE_STRUCT_STAT_ST_BLOCKS if found
+       - check for strtoull(), define HAVE_STRTOULL if found
+       - check for uintmax_t, define to `unsigned long' if not found
+
+lib/sh/mailstat.c
+       - don't use st_blocks member of struct stat unless
+         HAVE_STRUCT_STAT_ST_BLOCKS is defined; otherwise use the st_nlink
+         field to return the total number of messages in a maildir-style
+         mail directory
+
+general.h,{alias,expr,general,subst,variables}.c
+builtins/{printf,read}.def
+lib/readline/{bind,complete,nls}.c
+lib/sh/{pathcanon,pathphys,shquote,snprintf,strindex,strtod,strtol,strtrans}.c
+       - cast args to ctype macros to unsigned char for systems with signed
+         chars; other fixes for signed chars
+
+lib/sh/{fmtullong,strtoull.c}
+       - new files, more support for `long long'
+
+Makefile.in, lib/sh/Makefile.in
+       - make fmtullong.o and strtoull.o part of libsh
+
+lib/sh/itos.c
+       - remove local copy of fmtlong; use fmtulong instead
+       - new functions: uitos, uinttostr work on `unsigned long'
+
+lib/sh/snprintf.c
+       - fixes to make `unsigned long long' work (%llu)
+       - fixes to make unsigned formats not print the sign when given
+         an unsigned long that is greater than LONG_MAX
+
+externs.h
+       - extern declarations for fmtulong, fmtulloing, strtoull
+       - extern declarations for uitos, uinttostr
+
+                                  10/16
+                                  -----
+configure.in
+       - move header checks before function checks
+       - move c compiler tests before header checks
+       - check for <inttypes.h> with BASH_HEADER_INTTYPES
+       - change type checks for intmax_t, uintmax_t to not attempt to
+         include <stdint.h>
+       - check for strtoimax, strtoumax, strtoll, strtol, strtoull, strtoul
+         with BASH_CHECK_DECL (for declarations in header files) and
+         AC_REPLACE_FUNCS (for availability and LIBOBJS substitution)
+       - remove check for have_long_long around sizeof check for long long
+         (since autoconf will give it a size of 0 if the type isn't found)
+
+config.h.in
+       - add a define for HAVE_INTTYPES_H
+       - add a define for HAVE_UNSIGNED_LONG_LONG
+       - add defines for HAVE_STRTOIMAX, HAVE_STRTOUMAX, HAVE_STRTOLL
+
+aclocal.m4
+       - new func, BASH_HEADER_INTTYPES, which just calls AC_CHECK_HEADERS
+         on <inttypes.h>; separate so it can be AC_REQUIREd
+       - AC_REQUIRE([BASH_HEADER_INTTYPES]) in BASH_CHECK_TYPE
+       - include <inttypes.h> in BASH_CHECK_TYPE if HAVE_INTTYPES_H is
+         defined
+       - change AC_DEFINE to AC_DEFINE_UNQUOTED in BASH_CHECK_TYPE
+       - new `long long' checking macros:  BASH_TYPE_LONG_LONG and
+         BASH_TYPE_UNSIGNED_LONG_LONG
+       - new BASH_CHECK_DECL 
+
+lib/sh/{strto[iu]max,strtoll}.c, lib/sh/Makefile.in, Makefile.in
+       - new files
+
+externs.h
+       - extern declarations for strtoll, strtoimax, strtoumax
+
+lib/malloc/alloca.c
+       - include <bashtypes.h> for size_t
+
+builtins/printf.def
+       - new functions: getllong, getullong, getintmax, getuintmax; return
+         long long, unsigned long long, intmax_t, uintmax_t respectively
+       - builtin printf now handles `ll' and `j' length modifiers directly
+
+lib/sh/Makefile.in
+       - use LIBOBJS to decide whether or not the strto* functions are
+         needed
+
+                                  10/17
+                                  -----
+configure.in
+       - call AC_REPLACE_FUNCS(rename)
+       - move getcwd, strpbrk, strcasecmp, strerror, strtod
+         from AC_CHECK_FUNCS to AC_REPLACE_FUNCS
+       - only call BASH_FUNC_GETCWD if $ac_func_getcwd == "yes"
+       - call BASH_CHECK_SYS_SIGLIST
+       - if we don't have vprintf but have _doprnt, call AC_LIBOBJ(vprint)
+
+lib/sh/Makefile.in
+       - remove rename, getcwd, inet_aton, strpbrk, strcasecmp, strerror,
+         strtod, vprint from OBJECTS; picked up from LIBOBJS
+
+aclocal.m4
+       - change BASH_FUNC_GETCWD to call AC_LIBOBJ(getcwd) if the libc
+         getcwd(3) calls popen(3)
+       - change BASH_FUNC_INET_ATON to call AC_LIBOBJ(inet_aton) if it's
+         not found in libc or as a #define even with the special includes
+       - BASH_KERNEL_RLIMIT_CHECK -> BASH_CHECK_KERNEL_RLIMIT
+       - BASH_DEFAULT_MAILDIR -> BASH_SYS_DEFAULT_MAILDIR
+       - BASH_JOB_CONTROL_MISSING -> BASH_SYS_JOB_CONTROL_MISSING
+       - BASH_REINSTALL_SIGHANDLERS -> BASH_SYS_REINSTALL_SIGHANDLERS
+       - BASH_SIGNAL_CHECK -> BASH_SYS_SIGNAL_VINTAGE
+       - BASH_DUP2_CLOEXEC_CHECK -> BASH_FUNC_DUP2_CLOEXEC_CHECK
+       - BASH_PGRP_SYNC -> BASH_SYS_PGRP_SYNC
+       - BASH_RLIMIT_TYPE -> BASH_TYPE_RLIMIT
+       - BASH_FUNC_PRINTF -> BASH_DECL_PRINTF
+       - BASH_FUNC_SBRK_DECLARED -> BASH_DECL_SBRK
+       - BASH_MISC_SPEED_T -> BASH_CHECK_SPEED_T
+       - BASH_CHECK_SOCKLIB -> BASH_CHECK_LIB_SOCKET
+       - new macro, BASH_CHECK_SYS_SIGLIST, encapsulates all the checks for
+         sys_siglist, _sys_siglist, and strsignal(), sets SIGLIST_O to
+         siglist.o if appropriate
+
+Makefile.in
+       - use SIGLIST_O variable to decide whether or not we need siglist.o
+
+{execute_cmd,subst}.c
+       - change a couple of instances of ISDIGIT to DIGIT, where we really,
+         really only want ascii digits
+
+ansi_stdlib.h
+       - don't need a declaration for atol()
+
+                                  10/18
+                                  -----
+
+aclocal.m4
+       - new macro, BASH_FUNC_PRINTF_A_FORMAT, checks for printf support
+         for %a, %A conversion specifiers, defines HAVE_PRINTF_A_FORMAT
+         if successful
+
+configure.in
+       - call AC_CHECK_FUNCS for isascii
+       - call BASH_FUNC_PRINTF_A_FORMAT
+
+config.h.in
+       - add a define for HAVE_ISASCII
+       - add a define for HAVE_PRINTF_A_FORMAT
+
+lib/sh/snprintf.c
+       - for long double output, fall back to sprintf using ldfallback()
+         function for floating point formats
+       - support %a, %A using dfallback() or ldfallback() if
+         HAVE_PRINTF_A_FORMAT is defined
+       - fix bug in vasprintf that returned wrong value in its first
+         argument if the buffer holding the result string got reallocated
+       - fixed PUT_CHAR macro to increment the counter even if we've
+         exceeded the buffer size, for the return value from
+         vsnprintf/snprintf
+       - fix vsnprintf_internal to not use counter < length as a loop
+         condition, but always process the entire format string (for
+         the return value from vsnprintf/snprintf)
+
+builtins/printf.def
+       - support %a, %A if HAVE_PRINTF_A_FORMAT is defined
+
+include/typemax.h
+       - new file, with the TYPE_MAXIMUM stuff that's duplicated in several
+         files in lib/sh
+
+lib/sh/{fmtulong,strtol,snprintf}.c
+       - include <typemax.h> instead of having the definitions in each file
+
+lib/sh/Makefile.in
+       - updated dependencies for typemax.h
+
+                                  10/22
+                                  -----
+configure.in
+       - call AC_CHECK_FUNCS on ctype.h functions/macros that bash redefines
+         in chartypes.h
+
+config.h.in
+       - defines for HAVE_IS{ASCII,BLANK,GRAPH,PRINT,SPACE,XDIGIT}
+
+include/chartypes.h, lib/glob/strmatch.c, lib/readline/chardefs.h
+       - don't redefine some is* ctype macros/functions if HAVE_ISXXX is
+         defined (meaning that an appropriate function, but not a macro,
+         exists)
+
+lib/sh/strtrans.c
+       - new function, ansic_shouldquote, returns 1 if argument string
+         contains non-printing chars that should be quoted with $'...'
+
+externs.h
+       - new declaration for ansic_shouldquote()
+
+variables.c
+       - change print_var_value to ansi C quote the string if we're not in
+         posix mode and the variable's value contains non-printing chars,
+         to use the regular shell single quoting if the value contains
+         shell meta-characters, and to just output the string otherwise
+
+lib/sh/shquote.c
+       - add `break' to `case '~':' to avoid fallthrough and extra test
+
+doc/bashref.texi
+       - note that in POSIX mode, `set' displays variable values that
+         include nonprinting characters without quoting, unless they
+         contain shell metacharacters
+
+builtins/printf.def, lib/sh/snprintf.c
+       - handle `F' conversion specifier as equivalent to 'f'
+
+parse.y, {nojobs,variables}.c
+       - a couple of cleanups for when building a minimal configuration
+
+nojobs.c
+       - new function: stop_making_children(), just sets
+         already_making_children to 0 (like stop_pipeline)
+
+subst.c
+       - call stop_making_children from subst.c:command_substitute if
+         JOB_CONTROL is not defined.  This fixes the bug where the wrong
+         process is waited for (and its status returned) when using
+         command substitution in a null command in a shell function
+
+builtins/printf.def
+       - new variable `tw' used to keep track of the total number of
+         characters written by a single call to `printf' -- to be
+         used for the `%n' conversion, which will be added later.  It
+         gets reset each time we reuse the format string, which is what
+         ksh93 seems to do
+
+                                  10/23
+                                  -----
+variables.c
+       - new function, bind_var_to_int (char *var, long val)
+
+variables.h
+       - extern declaration for bind_var_to_int
+
+lib/sh/netopen.c
+       - use gai_strerror() for error messages when getaddrinfo() fails
+       - use PF_INET if DEBUG is defined, since IPv6 doesn't work for me
+
+Makefile.in
+       - pass DEBUG=${DEBUG} down to makes in some subdirectories
+
+{builtins,lib/{glob,sh}}/Makefile.in
+       - append ${DEBUG} to LOCAL_CFLAGS value, passed by top-level Makefile
+
+builtins/printf.def
+       - added support for %n format conversion char (number of chars printed
+         so far from current format string)
+
+                                  10/24
+                                  -----
+variables.c
+       - if posixly_correct is set, the default value of $MAILCHECK is 600
+       - use legal_number instead of atoi in adjust_shell_level
+       - treat non-numeric assignments to SECONDS as 0 in assign_seconds
+       - new function, init_funcname_var; sets FUNCNAME as a dynamic variable
+         if it's not set in the initial environment
+       - new function, init_groups_var; sets GROUPS as a dynamic array
+         variable if it's not set in the initial environment
+       - new function, init_dirstack_var; sets DIRSTACK as a dynamic array
+         variable if it's not set in the initial environment
+       - new function, init_seconds_var; sets SECONDS as a dynamic
+         variable using any valid integer value in the initial environment
+         as the initial value, as if an assignment had been performed
+       - call init_funcname_var, init_groups_var, init_dirstack_var,
+         init_seconds_var from initialize_dynamic_variables
+       - non-numeric values assigned to LINENO are treated as 0
+       - change initialize_shell_variables to not auto-export PATH or TERM
+       - change set_home_var to not auto-export HOME
+       - change set_shell_var to not auto-export SHELL
+       - broke the code that sets HOSTNAME, HOSTTYPE, MACHTYPE, OSTYPE
+         out into a separate function, set_machine_vars; none of those
+         variables are auto-exported
+       - bash no longer un-exports SSH_CLIENT or SSH2_CLIENT
+
+shell.c
+       - changed isnetconn() to check SSH_CLIENT and SSH2_CLIENT only if
+         SSH_SOURCE_BASHRC is defined in config-top.h
+
+config-top.h
+       - added a commented-out definition for SSH_SOURCE_BASHRC
+
+                                  10/25
+                                  -----
+
+Makefile.in
+       - changed RELSTATUS to `rc1' (release candidate 1)
+
+                                  10/29
+                                  -----
+locale.c
+       - fixed an `=' vs. `==' typo in set_locale_var when parsing
+         LC_NUMERIC
+
+doc/{bash.1,bashref.texi}
+       - document what bash does with $POSIXLY_CORRECT
+
+doc/builtins.1
+       - some updates
+
+builtins/psize.sh
+       - some mktemp(1) changes
+
+lib/readline/readline.c
+       - change rl_backward to check for rl_point < 0 and reset to 0 if so
+
+lib/readline/util.c
+       - don't compile in _rl_strpbrk if HAVE_STRPBRK is defined
+
+lib/readline/rlprivate.h
+       - remove extern declaration of _rl_strpbrk
+
+lib/readline/rldefs.h
+       - #define _rl_strpbrk as strpbrk if HAVE_STRPBRK is define, otherwise
+         add extern declaration of _rl_strpbrk from rlprivate.h
+
+{mailcheck,shell,variables}.c
+       - make sure to include posixtime.h to get any prototype for time(3)
+         in scope
+
+{array,eval,execute_cmd,mksyntax,subst}.c, parse.y
+builtins/common.c
+lib/sh/pathcanon.c
+       - a few changes as the result of `gcc -Wall' patches from solar
+         designer
+
+builtins/read.def, parse.y
+       - change some calls to free() to xfree()
+
+builtins/set.def
+       - make sure unset_builtin() resets unset_array to 0 each time through
+         the loop, because it's set (and used) depending on the current
+         argument
+
+shell.h
+       - new define, USE_VAR, to force the compiler to not put a particular
+         variable in a register -- helpful if registers are not restored
+         by setjmp/longjmp
+
+builtins/{evalfile.c,{read,wait}.def}, {eval,execute_cmd,shell,test}.c
+       - use USE_VAR for some variables
+
+subst.c
+       - fixed a case in expand_word_internal where a NULL pointer could
+         have been passed to free() (though free() should ignore it)
+       - fixed a case at the end of expand_word_internal where LIST could
+         have been used uninitialized (it makes gcc happy, though it
+         doesn't happen in practice)
+
+test.c
+       - give test_syntax_error(), beyond(), and integer_expected_error()
+         the `__noreturn__' attribute for gcc
+
+unwind_prot.c
+       - in clear_unwind_protect_list(), convert `flags' to `long' (via
+         assignment to a `long' variable) before casting to `char *', in
+         case pointers and longs are 64 bits and ints are 32 (makes no
+         difference on 32-bit machines)
+
+                                  10/30
+                                  -----
+print_cmd.c
+       - fixed cprintf to avoid gcc warning about assigning const pointer
+         to non-const (discarding type qualifier)
+
+{make_cmd,pcomplete,test}.c,parse.y
+       - some minor changes to shut up gcc warnings
+
+lib/sh/tmpfile.c
+       - fixed sh_mktmpfp to avoid file descriptor leaks in the case that
+         sh_mktmpfd succeeds but fdopen fails for some reason
+       - change sh_mktmpfd to use the same scheme for computing `filenum'
+         as sh_mktmpname
+       - change get_sys_tmpdir to prefer P_tmpdir if P_tmpdir is defined
+       - changed sh_mktmpname and sh_mktmpfd to avoid trying to assign to
+         `nameroot' if `nameroot == 0' (duh)
+       - add code to sh_mktmpfd to use mkstemp(3) if USE_MKSTEMP is defined
+       - add code to sh_mktmpname to use mktemp(3) if USE_MKTEMP is defined
+
+support/{fixlinks,mkclone}
+       - use mktemp if it's available for the symlink test
+       - use $TMPDIR instead of hardcoding /tmp; default to /tmp
+       - use a better filename for the symlink test instead of `z'
+
+support/bashbug.sh
+       - more changes inspired by a patch from solar designer
+
+lib/malloc/Makefile.in
+       - new target `alloca', which builds libmalloc.a with alloca.o only
+         (for systems without alloca that are configured --without-bash-malloc)
+
+configure.in
+       - if we don't have a working alloca and are not configured to build
+         the bash malloc library, make a malloc library containing only
+         alloca.o
+
+aclocal.m4
+       - slight change to RL_LIB_READLINE_VERSION to deal with minor version
+         numbers with a letter appended (like 4.2a)
+
+                                  10/31
+                                  -----
+doc/{bash.1,bashref.texi}
+       - slight change to note that only interactive shells resend a SIGHUP
+         to all jobs before exiting
+
+externs.h
+       - declare strto[ui]max only if NEED_STRTOIMAX_DECL is defined.  This
+         keeps picky compilers from choking because intmax_t is not defined
+         (MacOS X 10.1)
+
+builtins/printf.def
+       - #define NEED_STRTOIMAX_DECL before including shell.h
+
+                                  11/1
+                                  ----
+general.c
+       - check in bash_tilde_expand() for an unquoted tilde-prefix; don't
+         bother passing the string to tilde_expand unless the prefix is
+         unquoted
+
+shell.c
+       - fix a problem with $LINENO when executing commands supplied with
+         the -c invocation option when ONESHOT is defined
+
+[bash-2.05a-rc1 frozen]
+
+builtins/printf.def
+       - fix the %n conversion to require that the variable name supplied
+         be a valid shell identifier
+
+variables.c
+       - improve random number generator slightly by using the upper 16
+         bits of the running random number instead of the lower 16, which
+         are incrementally more random
+
+                                  11/2
+                                  ----
+configure.in
+       - if RL_INCLUDEDIR ends up being /usr/include, don't put
+         -I$(RL_INCLUDEDIR) into CFLAGS
+
+                                  11/5
+                                  ----
+doc/{bash.1,bashref.texi}
+       - correct description of POSIXLY_CORRECT to note that the shell enters
+         posix mode *before* the startup files are read if POSIXLY_CORRECT
+         is in the initial environment
+
+variables.c
+       - fix function prologues for init_dirstack_var and init_groups_var
+         to agree with caller (no arguments)
+
+jobs.c
+       - fix forward function declarations for pipe_read and pipe_close
+
+subst.c
+       - removed `inline' attribute from skip_double_quoted because it can
+         potentially be called recursively
+
+bashline.c
+       - quick fix to bashline.c:attempt_shell_completion programmable
+         completion code to just punt if the end of the command word found
+         by find_cmd_end is <= the start found by find_cmd_start (the bug
+         is probably in find_cmd_start -- fix later)
+
+pcomplete.c
+       - fix gen_matches_from_itemlist to return if the stringlist is null
+         after any cleaning or initialization, before trying to use it
+       - fix GEN_COMPS to only bother to try to append the STRINGLIST
+         returned by gen_matches_from_itemlist to `glist' if it's non-NULL
+
+lib/sh/stringlist.c
+       - make copy_stringlist return NULL if the STRINGLIST * passed as an
+         argument is NULL
+       - make append_stringlist call copy_stringlist only if M2 is non-NULL;
+         otherwise just return NULL if m1 is NULL
+       - make word_list_to_stringlist return 0 immediately if the passed
+         LIST argument is NULL
+       - make realloc_stringlist call alloc_stringlist if the passed
+         STRINGLIST argument (`sl') is 0, just like realloc calls malloc
+
+subst.c
+       - in skip_to_delim(), if we have an unclosed ${, and it's at the end
+         of the string (string[i] == '{', string[i+1] == '{' and
+         string[i+2] == 0, return si (i +2) immediately without bothering
+         to call extract_dollar_brace_string or extract_delimited_string
+       - in skip_to_delim(), if string[i] is 0 after a call to
+         extract_dollar_brace_string or extract_delimited_string (meaning we
+         have an unclosed ${ or other expansion, return i immediately without
+         doing a `continue' (which will increment i past the end of string)
+       - in split_at_delims, don't increment te by 1 if it's pointing to a
+         delimiter.  this has the effect of skipping the first delimiter
+         char in a possibly multi-character delimiter, and ignoring
+         single-char delimiters like `>'
+
+configure.in
+       - use AC_CHECK_MEMBERS([struct stat.st_blocks]) instead of a call to
+         AC_STRUCT_ST_BLOCKS to avoid configure changing LIBOBJS if the test
+         fails
+
+general.c
+       - introduce two new variables: bash_tilde_{prefixes,suffixes}, set
+         to the additional prefixes and suffixes bash wants to pass to the
+         tilde expansion code (reserved for post-bash-2.05a fix)
+
+aclocal.m4
+       - add missing `test' in BASH_CHECK_SYS_SIGLIST
+
+                                  11/7
+                                  ----
+lib/readline/vi_mode.c
+       - fix rl_vi_goto_mark to explicitly check that the desired mark is
+         between 'a' and 'z', since some locales have lowercase letters
+         outside that range, which could cause a negative subscript
+
+include/chartypes.h
+       - remove superfluous `#undef ISASCII'
+
+lib/sh/strto[iu]max.c
+       - changes from Paul Eggert to work around buggy compilers and catch
+         configuration errors at compile time
+
+aclocal.m4
+       - new macro, BASH_C_LONG_DOUBLE, identical to AC_C_LONG_DOUBLE but
+         with a fix for Irix 5.3 (not called, since I'm not sure it's the
+         right thing to do -- the C standard allows double and long double
+         to be the same size)
+
+lib/sh/snprintf.c
+       - only try to write the trailing NUL in vsnprintf_internal if
+         data->length is >= 0, since if it's not, we probably don't have
+         a buffer
+
+Makefile.in
+       - changed RELSTATUS to `release'
+
+                                  11/8
+                                  ----
+lib/sh/strtol.c
+       - make sure chars passed to toupper are cast to unsigned
+
+unwind_prot.c
+       - change clear_unwind_protect_list to not require a cast from `int'
+         to `char *'
+
+lib/readline/chardefs.h
+       - make _rl_digit_p succeed only for ascii digits, since that's what
+         most callers assume
+
+                                  11/13
+                                  -----
+doc/bashref.texi
+       - added `ERR' trap and [-+]O invocation option to section listing
+         differences from the Bourne shell
+
+                                  11/15
+                                  -----
+[bash-2.05a released]
+
+                                  11/19
+                                  -----
+include/stdc.h
+       - new define, INLINE, defined as `inline' for gcc and empty otherwise
+
+subst.c
+       - make skip_double_quoted, sub_append_string have INLINE attribute
+
+trap.c
+       - use BASH_NSIG as upper limit for signal names in signal_name()
+
+lib/readline/bind.c
+       - use RL_COMMENT_BEGIN_DEFAULT in output for rl-comment-begin value
+
+error.c
+       - fix sys_error to save value of errno around calls to fprintf
+
+doc/Makefile.in
+       - added rules to create PDF files from postscript and dvi input
+
+MANIFEST.doc
+       - added {article,bash,bashref,rose94}.pdf
+
+doc/bash.1
+       - rearranged some `.PD 0' and `.TP' directives so man2html will
+         handle them better (shouldn't affect groff output)
+
+support/man2html.c
+       - small fix to handle quoted string arguments to directives like
+         `.BR' without mangling the output
+
+                                  11/20
+                                  -----
+{arrayfunc,variables}.c
+       - changed calling sequence for dynamic array variable `assign'
+         functions to (SHELL_VAR *self, char *value, arrayind_t ind)
+       - changed calling sequence for dynamic variable assign functions
+         to the same as array variable assign_func.  Now this can be
+         prototyped
+
+variables.h
+       - the assign_func member of a `struct variable' is now of type
+         `sh_var_assign_func_t', which is prototyped
+       - the dynamic_value member of a `struct variable' is now of type
+         `sh_var_value_func_t', which is prototyped
+
+variables.c
+       - changed to use `sh_var_assign_func_t' and `sh_var_value_func_t'
+
+builtins/cd.def
+       - when in posix mode, if the new directory name formed by PWD and
+         the argument passed by the user cannot be canonicalized, and the
+         -P option has not been supplied, return failure immediately
+       - if canonicalization failed, but the fallback to the directory
+         name specified by the user succeeds, reset the current working
+         directory
+
+lib/readline/{input.c,rlprivate.h}
+       - renamed rl_unget_char to _rl_unget_char; made library global
+
+lib/readline/{{bind,readline}.c,{keymaps,rlprivate}.h}
+       - support for `key subsequences'; allows a key sequence and a function
+         mapped to a subsequence of that key sequence.  Primarily to allow
+         arrow keys to be bound in readline vi insert mode, while preserving
+         the ESC function to switch to command mode.
+
+lib/readline/{input.c,rlprivate.h}
+       - new function, _rl_input_queued(T), does a check with select or
+         FIONREAD with a timeout of `T' (which is generally 0)
+
+lib/readline/readline.c
+       - change _rl_dispatch_subseq to test for input in the queue if we
+         get ESC while in vi insertion mode if the keymap entry type for
+         ESC is ISKMAP.  If _rl_input_queued returns non-zero, we assume
+         that an arrow key sequence has been pressed and go ahead with the
+         subsequence.  If it returns zero, we assume that the user pressed
+         ESC to switch into command mode, and dispatch to that right away.
+         This avoids forcing the user to press another key before switching
+         into command mode
+
+                                  11/21
+                                  -----
+lib/readline/readline.c
+       - bind common arrow key sequences in vi insertion keymap
+
+lib/readline/terminal.c
+       - bind termcap definition's arrow keys in vi insertion keymap
+
+lib/readline/bind.c
+       - check for rl_vi_movement_mode in _rl_bind_if_unbound, so
+         binding the arrow keys can work
+
+lib/readline/readline.c
+       - since _rl_bind_if_unbound does the check of what's currently
+         bound to the key sequence, the check in bind_arrow_keys_internal
+         was redundant
+       - bind_arrow_keys_internal now takes a Keymap argument and handles
+         saving and restoring _rl_keymap; changed bind_arrow_keys
+         accordingly
+
+builtins/fc.def
+       - fix from Paul Eggert to substitute the nearest history number in
+         range if an out-of-range value is supplied.  POSIX requires this
+
+lib/sh/pathcanon.c
+       - fix from Corrina Vinschen for the special `cygdrive' prefix on
+         Cygwin
+
+bashhist.c
+       - split the history adding code into more pieces:
+           check_history_control (char *line) checks LINE against the value
+           of HISTCONTROL, returning 1 if LINE should be saved and 0 if not
+
+           check_add_history (char *line) calls check_history_control and
+           history_should_ignore (line) and saves the line with
+           bash_add_history if the checks indicate that it should be saved
+
+           maybe_add_history just calls check_add_history to set the value
+           of first_line_saved
+
+bashhist.h
+       - extern declaration for check_add_history()
+
+shell.c
+       - don't call load_history() from the interactive shell startup
+         code if history_lines_this_session is > 0, indicating that we've
+         already saved some lines in the history and that we probably
+         don't want to overwrite them
+
+builtins/history.def
+       - call check_add_history from push_history, so `history -s xx'
+         works even when in a compound command whose first line has not
+         been saved.  (Caveat:  in a compound command when the first
+         line has been saved, the line supplied to history -s will become
+         part of the compound command's history entry.  Of course, the
+         delete_history call could remove the compound command from the
+         history entirely)
+
+bashline.c
+       - use sh_makepath instead of xmalloc/sprintf in
+         command_word_completion_function
+
+lib/readline/complete.c
+       - get_y_or_n now takes an int FOR_PAGER argument; caller changed
+         If FOR_PAGER is non-zero, get_y_or_n returns appropriate values
+         for a more-like pager:  `newline' or `return' return 2; `q' or
+         `Q' return 0
+       - there is now a mini internal more-like pager for displaying a
+         list of completions that exceeds the screen height (new function
+         _rl_internal_pager, called from rl_display_match_list)
+
+                                  11/24
+                                  -----
+command.h
+       - new flag, W_TILDEEXP, says to do tilde expansion on an
+         assignment word
+
+execute_cmd.c
+       - fix_assignment_words now sets W_TILDEEXP for assignment word
+         arguments to `assignment builtins'
+
+general.c
+       - bash_tilde_expand now takes a second argument indicating whether
+         or not it's being invoked in an `assignment context'
+
+general.h
+       - change extern declaration for bash_tilde_expand
+
+{bashline,execute_cmd,findcmd,general,variables}.c
+builtins/evalfile.c
+lib/sh/makepath.c
+       - fix callers of bash_tilde_expand appropriately
+
+subst.c
+       - fix callers of bash_tilde_expansion appropriately
+       - add (currently commented-out) code that would tilde expand assignment
+         statement arguments to assignment builtins (W_TILDEEXP flag set)
+         even when the shell is in posix mode
+
+bashline.c
+       - fix attempt_shell_completion to turn off
+         rl_filename_completion_desired when doing command name completion,
+         so no slash gets appended to the name if there happens to be a
+         directory with the same name in the current directory
+
+                                  11/26
+                                  -----
+lib/readline/rltech.texinfo
+       - a couple of additions to the rl_stuff_char description
+
+parse.y
+       - turn off echo_input_at_read in parse_string_to_word_list, so `set -v'
+         doesn't give extra lines of output when doing compound array
+         assignment
+
+subst.c
+       - fix split_at_delims to handle skipping over a `\n' if it's a
+         delimiter (use spctabnl(c) instead of whitespace(c))
+
+                                  11/27
+                                  -----
+support/config.{guess,sub}
+       - updated (with bash changes) to latest version from gnu.org
+
+sig.h
+       - add prototype for set_signal_handler declaration
+
+builtins/setattr.def
+       - add prototype to extern declaration of declare_builtin
+
+builtins/times.def
+       - add no_options call, since times takes no options
+
+lib/sh/spell.c
+       - add prototypes to forward declarations for midist and spdist
+
+lib/sh/strtrans.c
+       - add explicit int return type to ansic_shouldquote declaration
+
+lib/readline/rldefs.h, lib/readline/{macro,readline,util,undo}.c
+       - move define for SWAP to rldefs.h, removed from various C files
+
+lib/readline/vi_mode.c
+       - removed define for exchange(), changed to use SWAP instead
+
+lib/readline/bind.c
+       - added some static forward function declarations
+       - find_boolean_var, find_string_var now take a `const char *' argument
+
+lib/readline/signals.c
+       - added static forward declaration for rl_maybe_set_sighandler
+
+lib/readline/readline.c
+       - add some common key bindings for the HOME and END keys in
+         bind_arrow_keys_internal
+
+lib/readline/terminal.c
+       - fetch the `@7' termcap string; it's sent by the END key
+       - attempt to bind the terminal's END key to rl_end_of_line in
+         bind_termcap_arrow_keys; I don't know why I was using `kH'
+         instead of `@7'
+
+doc/builtins.1
+       - remove `case', `for', `if', `until', `while' from NAME section;
+         those are not shell builtins
+
+                                  11/28
+                                  -----
+stringlib.c
+       - new function, find_token_in_alist, takes a token value and an
+         ALIST argument, and returns the string correspoinding to the
+         token if found in the alist
+
+externs.h
+       - new extern declaration for find_token_in_alist()
+
+subst.c
+       - string_list_internal is no longer static
+
+subst.h
+       - new extern declaration for string_list_internal()
+
+parse.y
+       - new alist array of other tokens returned by read_token which are
+         not reserved words in word_token_alist[]
+       - reworked error reporting:  new functions print_offending_line,
+         which prints the line containing the syntax error,
+         error_token_from_token, which takes the current token and tries to
+         figure out its textual representation, and error_token_from_text,
+         which does the old job of finding the bad token by analyzing the
+         text of shell_input_line at the current index
+       - report_syntax_error now tries to figure out the token that caused
+         the syntax error by first looking at current_token and falling
+         back to the old method of textual analysis if that fails
+       - report_syntax_error doesn't say the token resulting from the textual
+         analysis of the input line is an `unexpected token'; it just
+         says there is a `syntax error near xxx'
+       - changed conditional command error reporting to use the value
+         returned by error_token_from_token if it's not null instead of
+         just using the token value in the message, since current_token
+         ends up being set to -1, and the text of the message from
+         report_syntax_error might not be exactly right
+       - change parse_string_to_word_list to set current_token to the
+         offending token returned by read_token before calling yyerror()
+         to make the error reporting do the right thing
+
+aclocal.m4
+       - fixed typo in BASH_CHECK_LIB_TERMCAP
+
+configure.in
+       - add check for isinf(3); define HAVE_ISINF_IN_LIBC if found
+
+config.h.in
+       - add define for HAVE_ISINF_IN_LIBC
+
+lib/sh/snprintf.c
+       - check for Inf and NaN, using isinf and isnan if they're found in
+         libc
+       - use the current locale for thousands separator and decimal point
+       - recognize "'" flag; not implemented yet
+       - fix for snprintf/vsnprintf with length of 0 and string argument of
+         0 with non-zero length
+
+builtins/read.def
+       - TMOUT is now the default timeout for `read' (and select) if set,
+         like ksh93 when reading from the terminal
+       - edit_line (called by read -e) now just does readline's filename
+         completion by setting rl_attempted_completion_function to NULL,
+         since e.g., doing command completion for the first word on the
+         line wasn't really useful
+
+execute_cmd.c
+       - changed select_command to return failure status if select_query
+         returns NULL, indicating that read_builtin returned
+         EXECUTION_FAILURE
+
+doc/{bash.1,bashref.texi}
+       - documented new TMOUT behavior 
+       - slight change to the description of the test `-ef' option
+
+doc/bashref.texi
+       - added item to posix mode section describing failure behavior of
+         cd when invoked in logical mode and the pathname formed by
+         combining $PWD and the directory argument does not refer to an
+         existing directory
+
+                                  11/29
+                                  -----
+execute_cmd.c
+       - fix execute_function to call dispose_function_env after
+         merge_function_env if the shell is in posix mode (fixes debian
+         bash bug #117673)
+
+lib/readline/readline.c
+       - rl_forward -> rl_forward_char; rl_forward function for compatibility
+       - rl_backward -> rl_backward_char; rl_forward function for
+         compatibility
+       - new functions, rl_forward_byte, rl_backward_byte, for future use
+
+lib/readline/readline.h
+       - extern declarations for rl_forward_char, rl_backward_char,
+         rl_forward_byte, rl_backward_byte
+
+lib/readline/{emacs_keymap,funmap,vi_keymap,vi_mode
+       - rl_forward -> rl_forward_char
+       - rl_backward -> rl_backward_char
+
+lib/readline/funmap.c
+       - new bindable names, `backward-byte' and `forward-byte'
+
+aclocal.m4
+       - new function, BASH_CHECK_MULTIBYTE, encapsulates checks for
+         multibyte code
+
+config.h.in
+       - add necessary defines for multibyte include files and functions
+
+configure.in
+       - add call to BASH_CHECK_MULTIBYTE
+
+config-bot.h
+       - add code to define HANDLE_MULTIBYTE if prerequisites are met
+
+lib/sh/xstrchr.c
+       - new file, xstrchr() is strchr(3) that handles multibyte characters
+
+bashhist.c
+       - first_line_saved -> current_command_first_line_saved; variable is
+         now global
+
+bashhist.h
+       - extern declaration for current_command_first_line_saved
+
+                                  11/30
+                                  -----
+bashhist.c
+       - break the code that actually calls add_history out of
+         bash_add_history into a new function, really_add_history;
+         bash_add_history now calls really_add_history
+       - check_add_history takes a second `force' argument telling it
+         whether to call bash_add_history (force == 0) or really_add_history
+         (force != 0)
+
+builtins/history.def
+       - in push_history, call delete_last_history if the current command
+         has more than one line, the first line was saved, and
+         command-oriented history is active.  This takes care of deleting
+         the right history element if `history -s' is used within a
+         compound or multiline command
+       - in push_history, call check_add_history with second argument of 1
+         to skip check of current_command_line_count and add the arguments
+         to history -s as a single separate history entry
+
+                                  12/3
+                                  ----
+lib/readline/complete.c
+       - append a slash to completed names which are symlinks to directories
+         if the new variable _rl_complete_mark_symlink_dirs is non-zero
+
+lib/readline/rlprivate.h
+       - extern declaration for _rl_complete_mark_symlink_dirs
+
+lib/readline/bind.c
+       - new bindable variable, `mark-symlinked-directories', mirrors the
+         value of _rl_complete_mark_symlink_dirs
+
+doc/bash.1, lib/readline/doc/{readline.3,rluser.texinfo}
+       - documented new `mark-symlinked-directories' variable
+
+                                  12/4
+                                  ----
+variables.[ch]
+       - set_pipestatus_array now takes a second argument with the number
+         of processes in the array
+       - changed set_pipestatus_array to just modify the value in place if
+         the existing array has one element and the new array has one
+         element, and to modify existing values in place if new array has
+         more elements than existing array
+
+variables.c, jobs.c
+       - changed set_pipestatus_array callers
+
+jobs.c
+       - moved call to setjstatus() from set_job_status_and_cleanup to
+         wait_for, since set_job_status_and_cleanup is part of the SIGCHLD
+         signal handler call path, and race conditions accessing the
+         PIPESTATUS array will result for things like
+
+               while true; do date; done | cat > /dev/null
+
+                                  12/5
+                                  ----
+xmalloc.h
+       - don't redefine xmalloc, xrealloc, and xfree if DISABLE_MALLOC_WRAPPERS
+         is #defined
+
+config.h.in
+       - #undef for DISABLE_MALLOC_WRAPPERS
+
+configure.in
+       - define DISABLE_MALLOC_WRAPPERS if the --with-purify option is
+         supplied
+
+lib/malloc/trace.c
+       - new function, malloc_trace_bin(N), traces allocations and frees
+         to bucket N (uses the same type of bitmap as `busy')
+
+lib/malloc/table.c
+       - fix wraparound search problem in find_entry when searching for a
+         free entry when the table is full
+
+                                  12/6
+                                  ----
+lib/malloc/table.c
+       - keep an `overflow bucket' around to use when the table is full,
+         so find_entry always returns a valid pointer when FIND_ALLOC
+         is set
+       - new static variable to keep a count of the number of MT_ALLOC
+         entries in the mem_table
+
+lib/sh/{oslib,clktck}.c
+       - if HAVE_LIMITS_H is defined, include <limits.h>
+
+lib/sh/oslib.c
+       - new function, getmaxgroups() returns max number of simultaneous
+         groups
+       - new function, getmaxchild(), returns max number of simultaneous
+         user processes
+
+general.c
+       - removed forest of #defines for getmaxgroups()
+
+externs.h
+       - new extern declaration for getmaxgroups()
+       - new extern declaration for getmaxchild()
+       - new extern declaration for isnetconn()
+
+lib/sh/netconn.c,shell.c
+       - new file, isnetconn() from shell.c moved here
+
+Makefile.in, lib/sh/Makefile.in
+       - necessary changes for netconn.c
+
+builtins/ulimit.def
+       - changed getmaxuprc() to just call getmaxchild() and massage the
+         return value appropriately
+
+{jobs,nojobs}.c
+       - use the value returned by getmaxchild() in
+         mark_dead_jobs_as_notified instead of static CHILD_MAX
+
+jobs.c
+       - new function, compact_jobs_list, removes some number of jobs from
+         the jobs table and reallocates the table, copying the jobs that
+         are left from the old table to the new.  Compaction happens from
+         the beginning of the list and removes dead jobs, and we make sure
+         to keep the last CHILD_MAX jobs as POSIX.2 requires
+       - call compact_jobs_list from stop_pipeline if we're in a subshell,
+         there are no free jobs in the jobs table, and the jobs table is
+         at or above some maximum limit
+
+execute_cmd.c
+       - change eval_arith_for_expr to set this_command_name to `((' before
+         calling evalexp, since it might be changed by evaluating the
+         loop body between evalexp calls
+
+trap.c
+       - change reset_signal to turn off the SIG_TRAPPED flag for the
+         given signal, so shell builtins and functions running in command
+         substitutions don't run the signal handlers (traps are not supposed
+         to be inherited by command substitutions)
+
+parse.y
+       - changed parse_string_to_word_list to turn off alias expansion
+         while parsing the array assignment
+
+                                  12/9
+                                  ----
+alias.c
+       - fix add_alias so that redefining an alias's value also resets the
+         EXPANDNEXT flag
+
+                                  12/10
+                                  -----
+parse.y
+       - new function, token_is_assignment, called to check whether the text
+         before `=' makes up a valid assignment token before trying to parse
+         a compound assignment statement
+       - new function, parse_compound_assignment, to parse a compound
+         assignment statement instead of using parse_matched_pair; handles
+         comments and error reporting in the parser instead of waiting until
+         expansion time
+       - changed parse_compound_assignment and parse_string_to_word_list to
+         allow reserved words in compound array assignments
+
+lib/readline/doc/rltech.texinfo
+       - changed the documentation for rl_callback_read_char and
+         rl_callback_handler_remove to say what happens to the terminal
+         settings and what needs to be done to reset them
+
+                                  12/11
+                                  -----
+bashline.c
+       - add emacs_edit_and_execute_command, bound to C-xC-e, like vi-mode
+         `v' command
+       - add bindable command name `edit-and-execute-command', bound to
+         run emacs_edit_and_execute_command()
+
+lib/glob/strmatch.c
+       - add support for ksh93-like [:word:] character class (isalnum + `_')
+
+doc/{bash.1,bashref.texi}
+       - add note to section describing lists to clarify that a sequence of
+         one or more newlines may be used to delimit a command, equivalent
+         to a semicolon
+       - document new [:word:] pattern matching character class
+
+doc/bash.1, lib/readline/doc/rluser.texinfo
+       - document `edit-and-execute-command' and its default emacs-mode
+         binding
+
+include/chartypes.h
+       - add defines for TOCTRL and UNCTRL if they're not already defined
+
+lib/readline/chardefs.h
+       - #undef UNCTRL if it's defined to avoid cpp redefinition warnings
+
+lib/sh/strtrans.c
+       - add \cX (Control-X) escape for $'...' to ansicstr()
+       - change ansic_quote() to allocate at least four chars for each char
+         in the string argument, to account for \0xx octal values
+       - change ansic_quote() to no longer call sprintf for non-printable
+         characters; just translate the string to octal directly
+
+print_cmd.c
+       - change xtrace_print_word_list to call ansic_quote() if
+         ansic_shouldquote() indicates that there are nonprinting characters
+         in a word
+
+builtins/type.def
+       - changed deprecated long option parsing to just replace the word
+         in the list with the equivalent short option (-type -> -t) instead
+         of removing words from the list
+       - changed describe_command to take a single flags argument instead
+         of two int args; changed caller
+       - type now has two new options:  -f suppresses function lookup (like
+         command), and -P forces a PATH search for the name(s)
+
+builtins/common.h
+       - flags for describe_command are here
+       - changed extern declaration of describe_command
+
+builtins/command.def
+       - changed call to describe_command to use flags from common.h, and
+         the right number of arguments
+
+doc/{bash.1,bashref.texi}
+       - documented new -f and -P options to `type'
+
+                                  12/12
+                                  -----
+lib/readline/rldefs.h
+       - fixed prototype for _rl_strnicmp
+
+execute_cmd.c
+       - select_query now takes a new argument, an int flag saying whether
+         or not to print the menu the first time through the loop.  An
+         empty line in response to the prompt will always cause the menu
+         to be reprinted
+       - changed execute_select_command to cause select_query to reprint
+         the menu only if REPLY is set to NULL, if KSH_COMPATIBLE_SELECT
+         is defined
+
+config-top.h
+       - define KSH_COMPATIBLE_SELECT, with a comment about its meaning
+
+lib/readline/readline.c
+       - change rl_insert_comment to toggle if given an explicit numeric
+         argument:  if the first characters on the line don't specify a
+         comment, insert one; if they do, delete the comment text
+
+doc/bash.1, lib/readline/doc/{readline.3,rluser.texinfo}
+       - documented new behavior of insert-comment with a numeric argument
+
+                                  12/13
+                                  -----
+lib/malloc/watch.c
+       - new file, implements watchpoint functions
+
+lib/malloc/watch.h
+       - new file, define some `events' for watchpoints and extern function
+         and variable declarations for watchpoint code
+
+lib/malloc/imalloc.h
+       - #define MALLOC_WATCH if MALLOC_DEBUG is defined
+       - add __P define as in include/stdc.h if not already defined
+
+lib/malloc/malloc.c
+       - remove __P define, now in imalloc.h
+       - include watch.h if MALLOC_WATCH is defined
+       - added calls to _malloc_ckwatch in internal_malloc, internal_free,
+         and internal_realloc
+
+include/stdc.h
+       - augment __P define to allow prototypes if PROTOTYPES is defined
+
+lib/readline/rlstdc.h
+       - augment PARAMS define to allow prototypes if PROTOTYPES is defined
+
+lib/malloc/Makefile.in, Makefile.in
+       necessary changes to include watch.c in libmalloc
+
+lib/readline/readline.c
+       - fix rl_delete_text to make sure that the starting position is >= 0
+       - _rl_init_line_state (called by readline via readline_initialize)
+         now sets rl_mark to 0
+       - rl_get_{next,previous}_history set rl_mark to 0 if rl_point is at
+         the end of the line and rl_end otherwise in emacs mode
+
+lib/readline/kill.c
+       - rl_yank_nth_arg_internal and rl_paste_clipboard now set the mark
+         at point before calling rl_insert_text, like rl_yank
+       - rl_kill_full_line now resets rl_mark to 0
+       - rl_kill_line and rl_backward_kill_line now set rl_mark to the
+         point after the kill in emacs mode
+       - rl_kill_word and rl_backward_kill_word now set rl_mark to the
+         point after the kill in emacs mode
+       - rl_unix_word_rubout and rl_unix_line_discard now set rl_mark to
+         the point after the kill in emacs mode
+
+lib/readline/search.c
+       - noninc_search saves and restores the mark, since it can be changed
+         while reading the search string
+       - noninc_dosearch sets the mark at the end of the line, making the
+         region bound the `inserted' text since rl_point is set to 0
+       - rl_history_search_internal sets the mark at the end of the line,
+         for the same reason
+
+lib/readline/isearch.c
+       - rl_search_history now saves and restores the mark
+       - if no matching lines are found at all when doing an isearch, leave
+         point where it was instead of moving it to the end of the line
+
+                                  12/17
+                                  -----
+lib/readline/rlmbutil.h
+       - new file, place for multi-byte character defines and extern
+         declarations
+
+lib/readline/{bind.c,readline.c,rlprivate.h}
+       - new bindable variable, `byte-oriented', tracks value of
+         rl_byte_oriented variable
+
+lib/readline/mbutil.c
+       - new file, with multibyte char utility functions
+
+lib/readline/{complete,display,readline,util,vi_mode}.c
+       - new code for multibyte characters, derived from IBM patch
+
+                                  12/18
+                                  -----
+lib/sh/tmpfile.c
+       - include posixtime.h for time() extern declaration
+
+support/bashversion.c
+       - include <unistd.h> if it's available
+
+lib/readline/{histexpand,input,isearch,search}.c
+       - new code for multibyte characters, derived from IBM patch
+
+lib/readline/readline.h
+       - include rltypedefs.h
+
+                                  12/19
+                                  -----
+lib/readline/complete.c
+       - slight change to mark-directories code to avoid adding a slash if
+         point is at the end of the line (rl_line_buffer[rl_point] == '\0')
+         and the previous character was a slash
+       - change printable_part to not return empty pathnames, which could
+         happen when completing filenames and a filename with a trailing
+         slash was passed as the argument.  If the portion following the
+         trailing slash is NULL, ignore it and look for a previous slash.
+         If there's no previous slash, just return the filename argument
+       - new variable, rl_completion_mark_symlink_dirs, mirrors the value
+         of (user-settable with a variable) _rl_complete_mark_symlink_dirs
+         but may be modified by application-specific completion functions
+         when appropriate (set in rl_complete_internal and rl_menu_complete)
+
+lib/readline/readline.h
+       - extern declaration for rl_completion_mark_symlink_dirs
+
+pcomplete.c
+       - if one of the actions is CA_DIRECTORY, set
+         rl_completion_mark_symlink_dirs to indicate that we want the
+         trailing slash (might have to relax this)
+
+lib/readline/doc/rltech.texinfo
+       - documented rl_completion_mark_symlink_dirs variable
+
+lib/readline/doc/rluser.texinfo, doc/bash.1
+       - documented the fact that `complete -d' and `complete -o dirnames'
+         force readline to append a slash to symlinks to directories
+
+builtins/enable.def
+       - changed enable_shell_builtin to disallow enabling disabled
+         builtins in a restricted shell
+
+doc/{bash.1,bashref.texi}
+       - documented new enable behavior in restricted shells
+
+doc/Makefile.in
+       - new rule to make an `RBASH' file documenting the restrictions
+         imposed by a restricted shell
+
+expr.c
+       - broke the code that evaluates variables and returns results out
+         of readtok() into a new function: expr_streval()
+       - expr_streval() now performs the standard unset variable error
+         behavior if `set -u' has been executed and it's asked to look
+         up an unset variable
+       - broke the code that frees up the expression context stack into
+         a new function: expr_unwind()
+
+variables.c
+       - fixed bind_int_variable so it handles array element assignment,
+         so expressions like `b[7]++' and `b[0] = 42' work right
+       - new function, get_variable_value, returns the string value of
+         the SHELL_VAR * passed as an argument
+       - get_string_value now calls get_variable_value with a non-null
+         result from find_variable
+
+                                  12/20
+                                  -----
+lib/readline/rlmbutil.h, mbutil.c
+       - combined _rl_find_next_mbchar and _rl_find_next_nonzero_mbchar into
+         a single function
+       - combined _rl_find_prev_mbchar and _rl_find_prev_nonzero_mbchar into
+         a single function
+
+lib/readline/{display,readline,vi_mode}.c
+       - changed callers of _rl_find_next_mbchar and
+         _rl_find_next_nonzero_mbchar
+
+lib/readline/{complete,display,histexpand,readline,vi_mode}.c
+       - changed callers of _rl_find_prev_mbchar and
+         _rl_find_prev_nonzero_mbchar
+
+                                  12/20
+                                  -----
+lib/sh/mktime.c
+       - new file, from glibc/gawk, compiled in if system doesn't have a
+         working mktime(3)
+
+lib/sh/strftime.c
+       - new file, from gawk, compiled in if system doesn't have a
+         working strftime(3)
+
+lib/sh/Makefile.in, Makefile.in
+       - changes for mktime.c, strftime.c
+
+configure.in
+       - call AC_FUNC_MKTIME, AC_STRUCT_TM, AC_STRUCT_TIMEZONE
+       - call AC_REPLACE_FUNC(strftime)
+
+config.h.in
+       - add defines for TM_IN_SYS_TIME, HAVE_TZSET, HAVE_TM_ZONE,
+         HAVE_STRUCT_TM_TM_ZONE, HAVE_STRFTIME
+
+externs.h
+       - provide an extern declaration for strftime if HAVE_STRFTIME is
+         not defined and NEED_STRFTIME_DECL is
+
+lib/tilde/tilde.h
+       - header files should not include <config.h>
+
+parse.y
+       - replace code in decode_prompt_string that chops up value returned
+         by ctime(3) with calls to strftime -- as a result, the expansion
+         of \@ has changed slightly (since it depends on the locale)
+       - added new \D{format} prompt string escape; `format' is passed to
+         strftime(3).  Empty format is the same as `%X' (locale-specific
+         representation of the current time)
+       - combined cases for '\\', '\a', '\e', and '\r' in same case branch
+         in decode_prompt_string
+
+doc/{bash.1,bashref.texi}
+       - documented new \D{format} prompt string expansion
+
+builtins/printf.def
+       - use ISO C PRIdMAX instead of INTMAX_CONV
+       - pass length of format modifiers to mklong instead of computing it
+         with strlen()
+
+lib/sh/{fmtulong,fmtullong}.c
+       - changes from Paul Eggert to make more general
+
+arrayfunc.c
+       - when converting a variable to an array, make sure to unset the
+         dynamic_value and assign_func members of the struct variable,
+         since they're not valid anymore
+
+                                  12/27
+                                  -----
+configure.in
+       - use AC_HELP_STRING in AC_ARG_WITH and AC_ARG_ENABLE 
+       - remove AC_ARG_ENABLE for largefile, since AC_SYS_LARGEFILE adds
+         one
+
+                                1/2/2002
+                                --------
+{alias,bashline,execute_cmd,general,shell,subst,variables,arrayfunc}.c,general.h
+       - changed some calls to strchr to calls to xstrchr for multibyte
+         characters
+
+include/shmbutil.h
+       - add extern declaration for xstrchr to avoid including externs.h
+         where it's not appropriate
+
+{braces,make_cmd,pathexp,subst,arrayfunc}.c, lib/sh/xstrchr.c
+       - include shmbutil.h
+
+{stringlib,subst}.c, {externs,subst}.h
+       - moved substring() from subst.c to stringlib.c, moved declaration
+         from subst.h to externs.h
+
+lib/sh/xmbsrtowcs.c
+       - new file, replacement function for mbsrtowcs
+
+lib/sh/Makefile.in
+       - add entries for xmbsrtowcs.c
+
+Makefile.in
+       - add dependencies on shmbutil.h to appropriate object files
+
+lib/glob/strmatch.c
+       - break character-class testing out into separate function:
+         is_cclass, in prep for multibyte changes
+
+{braces,make_cmd}.c
+       - changes for multibyte characters
+
+builtins/printf.def
+       - changes from Paul Eggert to just use intmax_t everywhere an
+         int/long/quad is needed and print with "%ld" if the number
+         fits in a long and %PRIdMAX otherwise
+       - remove getlong, getulong, getllong, getullong, since they're
+         no longer needed
+       - use a new type `floatmax_t' to print floating point numbers, the
+         widest-available floating point type (like `intmax_t'); new
+         function `getfloatmax' that calls strtold or strtod as appropriate
+       - remove getdouble, getldouble, since they're no longer needed
+
+lib/sh/fmtumax.c
+       - new file, string-to-[u]intmax_t conversion, just includes
+         fmtulong.c with the right defines
+
+Makefile.in, lib/sh/Makefile.in
+       - additions for fmtumax.c
+
+bashtypes.h
+       - include <inttypes.h> if it's available
+
+expr.c
+       - arithmetic is now in intmax_t instead of long
+
+externs.h
+       - extern declaration for fmtumax
+       - change extern declarations for evalexp, itos, inttostr,
+         uitos, uinttostr since they now return or use intmax_t instead
+         of long
+
+{execute_cmd,general,mailcheck,subst,variables}.c, parse.y
+{array,general,subst,test,variables}.h
+lib/sh/{itos,netopen}.c
+builtins/{bashgetopt,common}.c, builtins/common.h
+builtins/{break,fc,history,jobs,let,printf,pushd,read,shift,wait}.def
+       - changes for intmax_t shell arithmetic conversion
+
+doc/{bashref.texi,bash.1}
+       - documented long->intmax_t shell arithmetic conversion
+
+sig.c
+       - in initialize_terminating_signals, if we've already trapped a
+         terminating signal, don't reset the signal handler for it
+
+                                   1/3
+                                   ---
+{arrayfunc,pathexp}.c, parse.y
+       - changes for multibyte chars
+
+parse.y, lib/sh/strtrans.c
+       - moved ansiexpand from parse.y to lib/sh/strtrans.c
+
+parse.y, locale.c
+       - moved mk_msgstr and localeexpand from parse.y to locale.c
+
+parse.y
+       - new function, yy_input_name, returns name of input file from
+         bash_input.name
+       - broke the code that parses ((...)) constructs out of read_token
+         into a new function, parse_dparen()
+
+externs.h
+       - new extern declaration for ansiexpand(), mk_msgstr(), and
+         localeexpand()
+
+input.h
+       - new extern declaration for yy_input_name()
+
+{error,locale}.c
+       - use yy_input_name for error and other messages
+
+execute_cmd.c
+       - change shell_execve to make sure that the file is executable
+         before looking at the interpreter to find out why the execve()
+         failed (avoids misleading error message)
+
+lib/glob/glob.c
+       - move code that matches leading `.' and skips those filenames into
+         a separate function: skipname(), so there can be unibyte and
+         multibyte versions of that function
+
+                                   1/7
+                                   ---
+subst.c
+       - more changes for multibyte characters
+
+print_cmd.c
+       - change semicolon() so it doesn't output a `;' immediately after a
+         newline, since that results in a null command, which is a syntax
+         error
+
+variables.c
+       - fix indirection_level_string to turn off set -x while evaluating
+         PS4
+
+                                   1/8
+                                   ---
+builtins/set.def
+       - make -o options into one struct, instead of separate structs for
+         option names corresponding to flags and non-flag option names.
+         This has the side effect of sorting the option names in output
+
+lib/glob/glob.c
+       - new function, mbskipname(), multibyte char version of skipname()
+       - removed all #ifndef SHELL code, this will never be used outside
+         the shell
+
+include/posixdir.h
+       - move REAL_DIR_ENTRY define here from lib/glob/glob.c
+
+lib/glob/glob_loop.c
+       - new file, included in glob.c for unibyte and multibyte versions of
+         glob_pattern_p
+       - added some forward static function declarations with prototypes
+       - more changes for multibyte character handling
+
+lib/glob/Makefile.in
+       - make glob.c depend on glob_loop.c
+       - changes for xmbsrtowcs.[co]
+
+lib/glob/xmbsrtowcs.c
+       - moved here from lib/sh, since the matching functions use it, and
+         libglob.a is linked after libsh.a
+
+                                   1/9
+                                   ---
+lib/glob/smatch.c
+       - new file, with strmatch (now xstrmatch) and associated functions,
+         with changes for multibyte chars
+
+lib/glob/sm_loop.c
+       - new file, included by smatch.c, with `generic' versions of matching
+         functions that are compiled twice:  once each for single-byte and
+         multibyte characters
+
+lib/glob/strmatch.c
+       - strip out everything except strmatch(), which either calls fnmatch
+         (if HAVE_LIBC_FNM_EXTMATCH is defined) or xstrmatch
+
+lib/glob/collsyms.c
+       - changes for multibyte chars
+
+lib/glob/Makefile.in, Makefile.in
+       - changes for new source files
+
+                                  1/10
+                                  ----
+lib/readline/complete.c
+       - new function, rl_completion_mode (rl_command_func_t *func), returns
+         the appropriate value to pass to rl_complete_internal depending on
+         FUNC and the value of `show-all-if-ambiguous'.  This allows
+         application completion functions to present the same interface as
+         rl_complete
+
+lib/readline/readline.h
+       - new extern declaration for rl_completion_mode()
+
+lib/readline/doc/rltech.texinfo
+       - documented rl_completion_mode
+
+lib/readline/readline.[ch]
+       - bumped the version number to 4.3, changing the relevant cpp defines
+
+configure.in
+       - require that an installed readline version be at least readline-4.3
+
+bashline.c
+       - converted bash-specific completion functions to use
+         rl_completion_mode instead of passing TAB unconditionally
+
+builtins/bashgetopt.c
+       - the `#' option specifier now means a required numeric argument,
+         not an optional one
+
+builtins/type.def
+       - when converting [-]-{path,type,all} to -[pta], don't bother
+         freeing and reallocating the option string; just change opt[1]
+         and null opt[2]
+
+lib/sh/snprintf.c
+       - support %ls/%S and %lc/%C for wide strings and characters,
+         respectively, if HANDLE_MULTIBYTE is defined
+
+mailcheck.c
+       - don't print a message about new mail if the file has not grown,
+         even if the access time is less than the modification time
+
+                                  1/14
+                                  ----
+lib/readline/readline.c
+       - new function, rl_replace_line, to replace the readline line buffer
+         with the text supplied as an argument
+       - new function, rl_replace_from_history, replaces readline line
+         buffer with text from history entry passed as argument (undocumented,
+         not in readline.h because it requires a definition of
+         HIST_ENTRY for the prototype)
+
+lib/readline/readlne.h
+       - new extern declaration for rl_replace_line
+
+lib/readline/doc/rltech.texinfo
+       - documented rl_replace_line
+
+lib/readline/{isearch,readline,search}.c
+       - use rl_replace_line and rl_replace_from_history where appropriate
+
+lib/readline/readline.c
+       - broke the code that sets point after moving through the history
+         (_rl_history_preserve_point and _rl_history_saved_point) out
+         into a separate function, _rl_history_set_point()
+
+lib/readline/{complete.c,rlprivate.h}
+       - find_completion_word -> _rl_find_completion_word
+       - free_match_list -> _rl_free_match_list
+
+lib/readline/complete.c
+       - postprocess_matches and _rl_free_match_list now return immediately
+         if passed a null match list
+
+variables.c
+       - new function, find_local_variable, finds a local variable by name
+         at the current variable context
+       - in find_variable_internal, call find_local_variable before searching
+         any of the temporary environments if variable_context > 0 (meaning
+         we're in a shell function).  This lets a local variable
+         override a variable whose value was passed in the `function
+         environment'
+
+                                  1/15
+                                  ----
+variables.h, execute_cmd.c
+       - declare variables describing the temporary environments in
+         variables.h instead of in C files
+
+findcmd.c, builtins/setattr.def
+       - instead of calling find_tempenv_variable, use find_variable_internal
+         and check whether the returned SHELL_VAR * has the tempvar
+         attribute
+
+variables.c
+       - tentative change to lookup order in find_variable_internal so that
+         function local variables are found before variables in
+         function_env when executing a shell function
+       - change make_local_variable to handle making a local variable when
+         a variable with the same name already appears in one of the
+         temporary environments
+       - broke the body of make_var_array out into a new function:
+               static char **make_env_array_from_var_list (SHELL_VAR **vars)
+       - new function, make_var_array_internal, takes a hash table to look
+         in and a pointer to a mapping function and returns a char **
+         environment-style list
+       - make_var_array now just calls make_var_array_internal
+       - new mapping function, local_and_exported, returns all local variables
+         in the current variable context with the export attribute set
+       - new function, make_local_export_array, returns an environment-style
+         char ** array of exported local variables in current context
+       - change environment creation order in maybe_make_export_env to
+         add variables to the environment in opposite order that
+         find_variable_internal uses.  This means that local variables in
+         shell functions override variables with the same name in the
+         function_env
+       - change make_local_variable to set the initial value of the
+         variable it creates to NULL to make the `is set' and `is null'
+         tests that the expansion code does work right
+       - change make_local_variable to inherit the value of a variable with
+         the same name from the temporary enviroment
+
+                                  1/16
+                                  ----
+Makefile.in
+       - link bashversion with buildversion.o instead of version.o, for
+         cross-compiling.  version.o is for the target system;
+         buildversion.o is for the build system
+
+error.c
+       - add line numbers to internal_error() messages if the shell is
+         not interactive and running a shell script or a -c command
+       - report_error now prints non-zero line numbers for non-interactive
+         shells
+
+test.c
+       - test_syntax_error now calls builtin_error() instead of printing
+         its own messages
+
+builtins/common.c
+       - builtin_error now prints line numbers if a non-interactive shell
+         is running a shell script or a -c command
+
+print_cmd.c
+       - in cprintf, remove free_argp, since it's not used
+
+builtins/history.def
+       - make `history -n' increment the number of history lines in this
+         session by the number of lines read from the history file
+
+arrayfunc.c
+       - fix array_value_internal to expand the subscript even if the
+         variable is unset, so side effects produced by the arithmetic
+         evaluation will take place
+
+lib/readline/doc/{rluser,rltech}.texinfo
+       - some fixes for printing in @smallbook format from Brian
+         Youmans
+
+                                  1/17
+                                  ----
+jobs.h
+       - new PRUNNING, PSTOPPED, PDEADPROC defines for PROCESSes, analogous
+         to RUNNING, STOPPED, and DEADJOB defines for jobs
+
+jobs.c
+       - use PS_RUNNING, PS_DONE, PS_STOPPED values for `running' field
+         of a PROCESS
+       - find_pipeline and find_job now take an additional flags argument
+         that, if non-zero, means to find only running processes; changed
+         all callers
+       - changed calls to find_pipeline and find_job made from waitchld
+         to find only running processes
+       - find_pipeline takes a third argument: an int *.  If it looks in
+         the jobs list to find the pid, and the arg is non-null, it passes
+         the job index back to the caller.  Used to avoid calls to
+         find_pipeline immediately followed by find_job with the same PID
+
+nojobs.c
+       - a couple of changes to make sure that set_pid_status is never
+         called with a pid argument of 0 or -1
+
+trap.c
+       - change trap_handler to longjmp to wait_intr_buf (set by wait_builtin)
+         if a signal is received for which a trap has been set during
+         execution of the wait builtin (need to include builtins.h and
+         builtins/builtext.h and declare some extern variables for the
+         right things to check)
+       - new variable to keep track of which signal caused the longjmp to
+         wait_intr_buf, set by trap_handler (wait_signal_received)
+
+builtins/wait.def
+       - set the return value of wait when a longjmp(wait_intr_buf, 1) is
+         done to 128 + wait_signal_received
+
+{jobs,nojobs}.c
+       - set wait_signal_received to SIGINT in wait_sigint_handler before
+         the longjmp(wait_intr_buf, 1)
+
+                                  1/18
+                                  ----
+bashline.c
+       - turn off rl_filename_completion_desired when completing a command
+         name with a single match only if the first char of that match is
+         not a `/'
+       - if there are multiple identical matches for a command name in
+         attempt_shell_completion, turn off rl_filename_completion_desired
+         if the first char is not a `/' to avoid readline appending a
+         slash if there's a directory with the same name in the current
+         directory
+
+                                  1/22
+                                  ----
+lib/readline/complete.c
+       - new variable, _rl_page_completions, to control whether we want to
+         run the internal pager when listing completions (defaults to 1)
+
+lib/readline/rlprivate.h
+       - extern declaration for _rl_page_completions
+
+lib/readline/bind.c
+       - new bindable variable, `page-completions', controls value of
+         _rl_page_completions
+
+lib/readline/doc/{rluser.texinfo,readline.3}, doc/bash.1
+       - documented `page-completions' variable
+
+Makefile.in
+       - use $(INSTALL_SCRIPT) instead of $(INSTALL_PROGRAM) to install
+         `bashbug'
+
+aclocal.m4
+       - fix small quoting problem in RL_LIB_READLINE_VERSION macro
+
+lib/readline/terminal.c
+       - fetch and save terminal's `vs' and `ve' cursor control attributes
+       - fetch and save terminal's `kI' attribute (string sent by Insert)
+       - new function, _rl_set_cursor, sets cursor to normal (insert mode)
+         or very visible (overwrite mode)
+
+lib/readline/readline.c
+       - new global variable, rl_insert_mode
+       - new function to toggle overwrite mode, rl_overwrite_mode
+       - each new line starts in insert mode
+       - switching to vi mode or emacs mode resets to insert mode
+       - reset cursor to normal before returning line
+       - _rl_replace_text now returns the number of characters inserted,
+         the return value from rl_insert_text
+       - new function, _rl_insert_or_replace_text (const char *string, int insert),
+         either inserts STRING or replaces the number of chars in STRING
+         with STRING starting at rl_point, depending on value of INSERT
+       - renamed rl_insert to _rl_insert_char, rl_insert just calls
+         _rl_insert_char with the same arguments when in insert mode
+       - new function, _rl_overwrite_char, handles self-insert in overwrite
+         mode.  Does multibyte chars by reading an entire multibyte character
+         before entering overwrite loop
+       - new function, _rl_overwrite_rubout, handles RUBOUT when in
+         overwrite mode, called from rl_rubout
+       - new function, _rl_rubout_char, old body of rl_rubout; rl_rubout
+         calls this when not in overwrite mode 
+
+lib/readline/readline.h
+       - extern declarations for rl_insert_mode and rl_overwrite_mode()
+
+lib/readline/rldefs.h
+       - define constants for values of rl_insert_mode
+
+lib/readline/rlprivate.h
+       - extern declarations for _rl_set_cursor and _rl_set_insert_mode
+       - change type of _rl_replace_text to return int
+       - extern declarations for _rl_insert_char, _rl_rubout_char
+
+lib/readline/funmap.c
+       - new bindable name `overwrite-mode', bound to rl_overwrite_mode
+
+lib/readline/rlconf.h
+       - define CURSOR_MODE if you want the cursor to show insert or
+         overwrite mode (only available if both `vs' and `ve' capabilities
+         are present)
+
+lib/readline/{complete,parens,readline,search,vi_mode}.c
+       - change calls to rl_insert to _rl_insert_char
+
+lib/readline/{readline,search}.c
+       - change calls to rl_rubout to _rl_rubout_char to avoid overwrite
+         mode problems
+
+lib/readline/vi_mode.c
+       - fix rl_vi_overstrike to just call _rl_overwrite_char, which
+         handles multibyte chars
+
+lib/readline/doc/{rluser.texinfo,readline.3}, doc/bash.1
+       - document new `overwrite-mode' command
+
+                                  1/23
+                                  ----
+lib/readline/readline.c
+       - return 0 immediately from rl_insert_text if the string to insert
+         is NULL or ""
+
+bashline.c
+       - if a numeric argument is given to one of the bash-specific glob
+         pattern completion functions (including TAB), append a `*' to
+         the word before generating matches
+       - in attempt_shell_completion, when doing glob completion, only
+         set the match list to NULL if rl_completion_type == TAB and
+         there is more than one completion.  This permits listing completions
+         with double tabs and displaying ambiguous completions
+       - new function, bash_glob_complete_word, appends a `*' to the word
+         to be completed and then globs it.  It uses a new filename
+         quoting function (bash_glob_quote_filename) to avoid quoting
+         globbing characters in the filename if there are no matches or
+         multiple matches
+
+lib/readline/complete.c
+       - set completion_changed_buffer to 0 in rl_complete_internal if
+         no matches were produced by the completion generator function
+       - new variable, rl_completion_suppress_append, suppresses appending
+         of rl_completion_append_character.  Settable by application
+         completion functions, always 0 when application completion
+         functions are called (set to 0 by rl_complete_internal and
+         rl_menu_complete)
+       - broke the code that assigns default values to readline completion
+         variables out of rl_complete_internal and rl_menu_complete into
+         a new function, set_completion_defaults (int what_to_do)
+
+lib/readline/readline.h
+       - extern declaration for rl_completion_suppress_append
+
+lib/readline/doc/rluser.texinfo, doc/bash.1
+       - documented behavior of glob-expand-word and glob-list-expansions
+         when supplied a numeric argument
+       - documented glob-complete-word
+
+lib/readline/doc/rltech.texinfo
+       - documented rl_completion_suppress_append
+
+                                  1/24
+                                  ----
+lib/readline/text.c
+       - new file, text and character handling functions from readline.c
+
+lib/readline/misc.c
+       - new file, miscellanous bindable functions and their supporting
+         code from readline.c
+
+Makefile.in, lib/readline/Makefile.in
+       - changes for text.c, misc.c
+
+lib/readline/bind.c
+       - change ISKMAP case of rl_invoking_keyseqs_in_map to output
+         ESC as "\M-" instead of "\e" -- it's closer to the documentation
+       - change _rl_get_keyname to output ESC as \e instead of \C-[
+         (it's easier to understand)
+
+pcomplete.h
+       - new flag, COPT_NOSPACE
+
+builtins/complete.def
+       - new `-o nospace' option for complete and compgen (though it doesn't
+         really do anything for compgen, since that doesn't hand anything
+         off to readline)
+
+bashline.c
+       - if a programmable completion specifies COPT_NOSPACE, set
+         rl_completion_suppress_append = 1
+
+lib/readline/doc/rluser.texinfo
+       - documented new `-o nospace' option to complete and compgen
+
+doc/{bash.1,bashref.texi}
+       - documented $'\cX' escape sequence (forgot to before)
+
+                                  1/28
+                                  ----
+variables.c
+       - make_new_variable now takes the HASH_TABLE * as its second
+         argument; changed callers
+       - new function, bind_variable_in_table, takes the HASH_TABLE * as
+         its third paramter; bind_variable calls bind_variable_in_table
+         with shell_variables as third argument
+
+variables.h
+       - new struct var_context, variable context (per-scope -- global,
+         function local, etc.)
+
+variables.[ch],builtins/common.[ch]
+       - moved functions that push and pop a variable context from
+         builtins/common.c to variables.c; move extern function
+         declarations to variables.h
+       - new function, all_local_variables
+       - variable_in_context is now static, used only by all_local_variables
+
+variables.[ch],execute_cmd.c
+       - push_context now takes the function name as an argument for
+         future use
+       - push_context takes an indication of whether or not the function is
+         executing in a subshell and saves the positional parameters only
+         if not in a subshell
+       - new functions for managing a stack of variable contexts and
+         scopes:  new_var_context, dispose_var_context, push_var_context,
+         pop_var_context, push_scope, pop_scope
+
+builtins/declare.def
+       - call all_local_variables instead of map_over (...) in declare_internal
+       - don't call make_local_variable if we're looking at functions
+         ((flags_on & att_function) != 0), since it's wasted
+       - make sure VAR is set to NULL if check for variable_context fails
+         and we didn't just create or fetch a local variable in
+         declare_internal
+       - in non-function branch of declare_internal, only call find_variable
+         if VAR is NULL -- if it's not null, we just created or fetched a
+         local variable and don't need to do it again
+
+                                  1/29
+                                  ----
+variables.[ch]
+       - the temporary environments (temporary_env, builtin_env, function_env)
+         are now HASH_TABLEs instead of argv-style arrays of strings (this
+         is an intermediate step on the way to the new lcc-inspired symbol
+         table scope structure)
+       - new internal attribute for variables: att_propagate.  This means
+         to propagate the value out of the temporary environment up the
+         (for now implicit) chain of variable scopes when the containing
+         temporary environment is deleted
+
+variables.c
+       - assign_in_env now adds to the HASH_TABLE temporary_env instead
+         of making environment-style strings in an array of strings
+       - changed the way the temporary environments are merged into the
+         shell variable table to account for the new HASH_TABLE temp
+         environments
+       - changed the way the export environment is created due to the new
+         structure of the temporary environments
+       - new function, bind_variable_internal (name, value, table), binds
+         NAME to have VALUE in TABLE without searching the temporary
+         environments
+       - removed: shell_var_from_env_string, bind_name_in_env_array
+       - variable_in_context now checks the att_local attribute and makes
+         sure the variable is not invisible
+       - local_and_exported now makes sure the variable is not invisible
+
+execute_cmd.c
+       - we no longer need to copy the temporary environment to function_env
+         or builtin_env, we can simply use variable assignments
+
+{findcmd,subst,variables}.c, builtins/{declare,setattr}.def
+       - since variables from the temporary environments are no longer turned
+         into SHELL_VARs on the fly, don't dispose the SHELL_VAR returned
+         by find_variable or find_variable_internal
+       - need to savestring() the value returned by find_variable if it has
+         the tempvar attribute before calling bind_variable on it, because
+         bind_variable will search and bind into the temporary environments
+         and will free the old value before binding the new.  For temporary
+         environments, these two pointers will be the same, and
+         bind_tempenv_variable will end up using freed memory
+
+builtins/{declare,setattr}.def
+       - set the att_propagate attribute when exporting or making readonly
+         variables from the temp environment (i.e., `var=value declare -x var'
+         or `var=value export var' sets the propagate attribute on the entry
+         for `var' in the temporary environment HASH_TABLE)
+
+lib/readline/isearch.c
+       - ^W when reading isearch string yanks the current word out of the
+         current line into the search string, skipping the portion already
+         matched
+       - ^Y when reading isearch string yanks the rest of the current line
+         into the search string, skipping the portion already matched
+
+                                  1/30
+                                  ----
+{print_cmd,variables}.c
+       - moved indirection_level_string() from variables.c to print_cmd.c
+
+{externs,variables}.h
+       - moved extern declaration of indirection_level_string to externs.h
+
+{general,variables}.c
+       - moved assignment() from variables.c to general.c
+
+{general,variables}.h
+       - moved extern declaration of assignment() to general.h
+
+{externs,input}.h
+       - moved extern declaration of decode_prompt_string to externs.h
+
+print_cmd.c
+       - include flags.h, don't include stdc.h
+
+variables.c
+       - moved some functions around to group functions better
+       - changed new_shell_variable to explicitly initialize each member
+         of the created struct variable instead of calling bzero()
+       - make_new_variable now just calls new_shell_variable instead
+         of duplicating what it does
+       - removed some code in bind_function that duplicated what
+         new_variable does on the newly-created SHELL_VAR
+       - since there are no local function variables (functions are always
+         made at the global scope), kill_all_local_variables() doesn't
+         need to consider functions
+
+                                  1/31
+                                  ----
+variables.c
+       - sort the array of special variables
+       - short-circuit the search in stupidly_hack_special_variables if
+         the passed name can't be found in the rest of the array
+         (that is, if name[0] < special_vars[i].name[0])
+
+lib/readline/history.c
+       - unstifle_history() was returning values exactly opposite of
+         the documentation
+
+lib/readline/doc/{hsuser.texinfo,history.3}
+       - clarified the unstifle_history() documentation a little
+
+                                   2/4
+                                   ---
+variables.c
+       - in bind_variable, don't call bind_tempenv_variable after a
+         find_tempenv_variable succeeds -- just change the value inline.
+         There's no reason to look it up twice
+       - change makunbound to only call stupidly_hack_special_variables
+         if we're not unsetting a function
+
+variables.[ch]
+       - new function, unbind_function, like makunbound but doesn't mess
+         with previous contexts or calling stupidly_hack_special_variables
+
+builtins/set.def
+       - change unset_builtin to call either unbind_func or unbind_variable
+
+builtins/getopts.def
+       - call unbind_variable(name) instead of makunbound(name, shell_variables)
+
+                                   2/5
+                                   ---
+lib/glob/sm_loop.c
+       - use malloc instead of xmalloc in BRACKMATCH and handle failures
+
+error.c
+       - add extern declaration of executing_line_number with prototype,
+         since execute_cmd.h can't be included without including other
+         files
+
+lib/readline/parens.c
+       - include <unistd.h>
+
+lib/malloc/stats.c
+       - include <unistd.h>
+       - add extern declaration of malloc_free_blocks() with prototype
+
+pathexp.c
+       - added some forward declarations with prototypes for static functions
+
+lib/readline/rlprivate.h
+       - removed declarations of rl_untranslate_keyseq, rl_discard_argument,
+         rl_stop_output, rl_alphabetic since they appear in readline.h
+
+                                   2/6
+                                   ---
+{arrayfunc,execute_cmd,pcomplete,shell}.c
+       - change calls to makunbound(name, shell_variables) to
+         unbind_variable (name)
+
+                                   2/7
+                                   ---
+builtins/getopt.c
+       - don't defer incrementing of OPTIND when an invalid option is
+         encountered until the next call to sh_getopt() -- what if OPTIND
+         is reset before that next call?  This means that OPTIND is always
+         incremented to the next option to be handled when an option is
+         returned, whether it's valid or not.  This is what POSIX-2002
+         says to do.
+
+syntax.h
+       - new #define, CSUBSTOP
+
+mksyntax.c
+       - add "-=?+" with value CSUBSTOP to the syntax table.  These are the
+         valid expansion operators OP in ${param[:]OPword}
+
+subst.c
+       - use table lookup for CSUBSTOP in VALID_PARAM_EXPAND_CHAR
+       - new flags for the string extraction functions:  EX_NOALLOC.  This
+         indicates that the functions are being used only to skip over
+         strings and the result won't be used, so the substring shouldn't
+         be allocated, copied, and freed
+       - new flag for string_extract:  EX_VARNAME.  This serves the same
+         purpose as the old `varname' parameter.  parameter_brace_expand()
+         changed appropriately
+       - extract_delimited_string and extract_dollar_brace_string now take
+         an additional `flags' argument, which may include EX_NOALLOC
+       - changed callers of extract_delimited_string and
+         extract_dollar_brace_string appropriately
+       - string_extract now understands EX_NOALLOC; callers changed
+       - some smaller code cleanups
+       - converted char_is_quoted(), unclosed_pair(), and skip_to_delim()
+         to understand multibyte characters
+
+                                  2/11
+                                  ----
+variables.[ch]
+       - moved to a symbol organization inspired by lcc.  The basic structure
+         is no longer a HASH_TABLE, but a VAR_CONTEXT, which includes a hash
+         table as one of its members.  VAR_CONTEXTs are linked together to do
+         variable scoping.  One nice thing about this is that the entire
+         symbol table doesn't need to be searched at function scope exit to
+         remove local variables.  Fixes problems with only one instance of
+         builtin_env and function_env, even though it really is a stack
+       - shell_variables is now a VAR_CONTEXT *, with a global_variables
+         variable that points to the bottom of the stack for fast access
+       - function-scope local variables (assignments specified on the command
+         line before a function call) and function-local variables (declared
+         with the `local' builtin) have been unified in the same variable
+         context, replacing function_env
+       - assignment statements preceding the `.' and `eval' builtins are now
+         a separate variable scope VAR_CONTEXT, replacing builtin_env
+       - temporary_env (a HASH_TABLE) is now the only separate environment
+       - changes to export environment creation, variable binding, variable
+         lookup, local variable propagation all changed to work with the
+         new symbol table/scope structure
+       - a SHELL_VAR no longer has a `prev_context' member; it's not needed
+
+execute_cmd.c
+       - changes to push_context calls to include any temporary variables in
+         temporary_env; pop_context takes care of propagating any temporary
+         variables if necessary
+       - calls to push_scope if `eval' or `.' is called with a list of
+         preceding variable assignments, and pop_scope called at end of
+         builtin's execution.  pop_scope takes care of merging temporary
+         variables into the shell environment when appropriate
+
+builtins/{setattr,declare}.def
+       - changes to account for variable assignments preceding `local',
+         `export', `readonly', `declare', etc. to work with the new
+         variable scoping implementation
+
+shell.c
+       - since shell_variables is now a VAR_CONTEXT, call
+         delete_all_contexts() when the shell is reinitializing instead of
+         delete_all_variables()
+
+builtins/common.c
+       - new function, get_job_by_name(), used by execute_simple_command()
+         for the `auto_resume' stuff and get_job_spec()
+
+builtins/common.h
+       - new set of #defined constants for flags argument to
+         get_job_by_name()
+
+                                  2/12
+                                  ----
+command.h
+       - new redirection operator: r_reading_string for `here strings'
+
+parse.y
+       - new token, LESS_LESS_LESS, for new redirection `here string'
+         operator: [N]<<< word
+       - recognize LESS_LESS_LESS and create the appropriate redirection
+
+{dispose_cmd,copy_cmd,make_cmd,print_cmd}.c
+       - recognize r_reading_string and do the right thing (dispose_redirects,
+         copy_redirect, print_redirection, and make_redirection, respectively)
+
+redir.c
+       - here_document_to_fd now takes the redirection operator as its
+         second argument
+       - new function, write_here_string, expands a here string and writes it
+         to the here document file descriptor
+       - here_document_to_fd calls write_here_string for r_reading_string
+         operator
+       - handle r_reading_string in do_redirection_internal() and
+         stdin_redirection()
+
+                                  2/18
+                                  ----
+doc/{bash.1,bashref.texi}
+       - documented here strings
+
+{configure,Makefile}.in
+       - bumped version number up to bash-2.05b and the release status
+         to alpha1
+
+expr.c
+       - make expr_streval understand that variables with the `invisible'
+         attribute are really unset, and accessing such a variable when
+         `set -u' is set should be an error
+
+variables.h
+       - new accessor macros: var_isset(var) and var_isnull(var), test
+         whether var->value is NULL
+
+{eval,subst,variables}.c, builtins/{declare,setattr}.def
+       - be more consistent about using value_cell(var) instead of
+         directly referencing var->value
+       - use var_isset and var_isnull where appropriate
+
+builtins/help.def
+       - augmented a couple of help strings with pointers to `info' and
+         `man -k'
+
+                                  2/14
+                                  ----
+variables.h
+       - new macros to use when setting variable values directly instead of
+         through bind_variable and its siblings
+
+{arrayfunc,variables}.c
+       - use var_setarray and other lvalue macros instead of assigning to
+         var->value directly
+
+builtins/setattr.def
+       - change show_var_attributes to show function definitions separately
+         from function attributes.  This allows the output of `declare -f'
+         (with other flags), `export -f', and `readonly -f' to be reused as
+         shell input, instead of the old
+
+               declare -f[flags] func()
+               {
+                       foo
+               }
+
+         which has syntax errors.  When in posix mode, `export -fp' and
+         `readonly -fp' still don't print function definitions
+
+                                  2/16
+                                  ----
+parse.y
+       - comment out calls to discard_parser_constructs; no need to call
+         empty functions
+
+                                  2/18
+                                  ----
+lib/sh/memset.c
+       - replacement function for memset(3)
+
+lib/sh/Makefile.in, Makefile.in
+       - additions for memset.c
+
+configure.in,config.h.in
+       - check for memset, define HAVE_MEMSET if found, add memset.o to
+         LIBOBJS if not
+
+lib/malloc/malloc.c
+       - removed zmemset(), replaced with calls to memset(3)
+
+{subst,execute_cmd,lib/sh/netopen}.c
+       - replaced calls to bzero with calls to memset
+
+subst.c
+       - word_split() now takes a second argument: the value of $IFS, so
+         it doesn't have to look up IFS every time
+       - word_list_split() now calls getifs() and passes the result to
+         each call to word_split() as its second arg
+       - do a quick scan for CTLNUL in remove_quoted_nulls before allocating
+         new string, copying old string to it, copying over original string
+         and freeing new string
+
+eval.c
+       - don't bother calling dispose_used_env_vars if temporary_env is NULL
+
+execute_cmd.c
+       - fix fix_assignment_words to only look up the builtin corresponding
+         to the first word if one of the words in the list is marked as
+         W_ASSIGNMENT
+
+hashlib.c
+       - renamed hash_string to hash_bucket, which better reflects what it
+         does
+       - extracted the portion of hash_bucket that computes the hash out
+         into a new hash_string()
+       - made new body of hash_bucket into a macro HASH_BUCKET; function
+         just calls the macro
+       - calls to hash_bucket in this file now call HASH_BUCKET macro
+       - in add_hash_item, just add a new item at the front of the appropriate
+         bucket list instead of at the end
+
+hashcmd.h
+       - reduced FILENAME_HASH_BUCKETS to 53 from 107
+
+                                  2/19
+                                  ----
+hashlib.[ch]
+       - find_hash_item, remove_hash_item, add_hash_item all take a new
+         third `flags' argument
+       - add_hash_item doesn't call find_hash_item if HASH_NOSRCH passed in
+         flags arg
+       - find_hash_item will create a new hash table entry if HASH_CREATE is
+         passed in flags arg
+       - new function, hash_walk, takes a pointer to a function and a table
+         and calls the function for each item in the table.  If the function
+         returns < 0, the walk is terminated
+       - fixed flush_hash_table to set table->nentries to 0 after freeing
+         all entries
+       - BUCKET_CONTENTS now has a new `khash' member, what key hashes to;
+         set by HASH_BUCKET macro (which calls hash_string), assigned in
+         find_hash_item (HASH_CREATE) and add_hash_item
+       - find_hash_item and remove_hash_item check `khash' against the
+         hash of the string argument before calling strcmp
+
+{alias,hashlib,hashcmd,pcomplib,variables}.c
+       - changed all calls to {find,remove,add}_hash_item
+
+builtins/hash.def
+       - return immediately from print_hashed_commands if there are no
+         entries in the hash table (this eliminates need for `any_printed'
+         variable)
+       - change print_hashed_commands to use hash_walk
+
+alias.c
+       - short-circuit all_aliases and map_over_aliases if
+         HASH_ENTRIES(aliases) == 0
+       - simplify map_over_aliases by just allocating enough room in the
+         returned list for all entries in the aliases hash table, instead
+         of doing the check and xrealloc
+       - add_alias now calls add_hash_item with HASH_NOSRCH argument
+
+pcomplete.h
+       - sh_csprint_func_t is no more; use hash_wfunc instead
+
+pcomplib.c
+       - short-circuit print_all_compspecs if HASH_ENTRIES(prog_completes)
+         is 0
+       - print_all_compspecs now takes a `hash_wfunc *' argument
+       - print_all_compspecs now just calls hash_walk
+
+builtins/complete.def
+       - new function, print_compitem, takes a BUCKET_CONTENTS *, extracts
+         the right info, and calls print_one_completion
+
+variables.c
+       - short-circuit map_over_funcs if HASH_ENTRIES(shell_functions) == 0
+       - short-circuit flatten if the passed table has no entries
+       - bind_variable_internal takes a new fourth argument: `hflags',
+         to pass to hash table functions
+       - make_new_variable now passes HASH_NOSRCH flag to add_hash_item
+       - set_if_not now calls bind_variable_internal and passes
+         HASH_NOSRCH as flags argument
+       - bind_function now calls add_hash_item with HASH_NOSRCH argument
+       - fixed make_local_variable:  old_var == 0 && was_tmpvar can never
+         be true
+       - if we didn't find an old variable in make_local_variable, call
+         bind_variable_internal with HASH_NOSRCH argument
+       - fix push_temp_var to reset variable context to 0 if binding into
+         global_variables->table
+
+parse.y
+       - fix to parse_compound_assignment to avoid core dumps on empty
+         compound array assignments
+
+subst.c
+       - getifs() is now global so read_builtin can call it
+
+subst.h
+       - extern declaration for getifs()
+
+                                  2/20
+                                  ----
+hashlib.c
+       - changed hash_string to use a better hash function
+       - changed HASH_BUCKET to use masking rather than modulus to hash a
+         string to a bucket -- HASH TABLES MUST NOW BE SIZED BY POWERS
+         OF TWO
+
+hashlib.h
+       - DEFAULT_HASH_BUCKETS is now 64
+
+hashcmd.h
+       - FILENAME_HASH_BUCKETS is now 64
+
+pcomplib.c
+       - COMPLETE_HASH_BUCKETS is now 32
+
+variables.c
+       - TEMPENV_HASH_BUCKETS is now 4
+
+alias.c
+       - new define, ALIAS_HASH_BUCKETS, set to 16, used to size alias table
+
+hashlib.c
+       - removed initialize_hash_table; folded code into make_hash_table
+       - fixed copy_bucket_array to copy the `khash' member of an item
+       - renamed functions to be more systematic and easier for me:
+               make_hash_table -> hash_create
+               hash_table_nentries -> hash_size
+               copy_hash_table -> hash_copy
+               find_hash_item -> hash_search
+               remove_hash_item -> hash_remove
+               add_hash_item -> hash_insert
+               flush_hash_table -> hash_flush
+               dispose_hash_table -> hash_dispose
+               print_table_stats -> hash_pstats
+               get_hash_bucket -> hash_items
+       - changed hash_search to short-circuit if table->nentries == 0 and
+         HASH_CREATE has not been passed in the flags argument
+
+{alias,variables,hashcmd,pcomplib}.c
+       - renamed calls to all renamed functions from hashlib.c
+
+builtins/kill.def
+       - don't drop a leading `-' in a pid argument
+       - call kill_pid with an explicit third argument of 1 if the pid
+         argument to kill is < -1, rather than rely on the behavior of
+         kill(2)
+
+                                  2/21
+                                  ----
+subst.c
+       - quoted_strchr is no longer declared `inline'
+       - skip_double_quoted is no longer declared `inline'
+       - string_extract_double_quoted is no longer declared `inline'
+
+lib/readline/input.c
+       - rl_gather_tyi is now an `int' valued function; returns the number
+         of characters read (0 or 1) or -1 on error
+       - if rl_gather_tyi() returns -1 to rl_read_key(), set rl_done to 1
+         and return a newline; something is wrong with the input fd
+
+                                  2/25
+                                  ----
+variables.[ch]
+       - IFS is now a special variable
+       - new special var function, sv_ifs(), called when IFS is set or unset
+       - call setifs() when IFS is first set in initialize_shell_variables
+       - call setifs() from make_local_variable and assign_in_env if
+         appropriate
+       - if assign_in_env() is called with a var assignment like `VAR=',
+         make the value in the new SHELL_VAR created be "" like
+         do_assignment_internal does, since certain parts of the shell use
+         a NULL value as evidence that the variable is unset (though
+         attributes may have been assigned)
+       - if push_temp_var pushes something up to the global_variables table,
+         make sure that the context is set to 0
+       - new function dispose_temporary_env, called by both
+         dispose_used_env_vars and merge_temporary_env with different `free
+         func' function pointers; calls sv_ifs after disposing the temporary
+         environment
+       - push_exported_var now calls bind_variable_internal instead of
+         bind_variable
+       - pop_scope and pop_context now call sv_ifs
+
+subst.[ch]
+       - new global variables used to keep track of IFS state, to avoid
+         having to call find_variable("IFS") all the time:
+
+               ifs_var         the SHELL_VAR for IFS
+               ifs_value       ifs_var ? value_cell (ifs_var) : " \t\n"
+               ifs_cmap        bitmap of characters in ifs_value
+               ifs_firstc      first character in ifs_value
+
+       - new function setifs(), sets the aforementioned ifs variables each
+         time IFS is set or unset, and at nested scope exit
+       - instead of calling getifs() from inside subst.c, use ifs_value
+       - getifs() now just returns ifs_value
+       - use ifs_firstc in string_list_dollar_star()
+       - only call member() in issep() if separators is more than one char
+       - don't cache a bitmap every time expand_word_internal() is called;
+         use ifs_cmap instead
+       - new macro, isifs(c), checks whether C is in ifs_cmap
+
+builtins/read.def
+       - use issep() and isifs() macros instead of looking at $IFS directly
+
+syntax.h
+       - make sure macros that access sh_syntaxtab cast the argument to
+         `unsigned char' before array access
+       - new macros: issyntype(c, type) and notsyntype(c, type), check
+         sh_syntaxtab[c] for a particular flag value `type'
+
+                                  2/26
+                                  ----
+hashlib.h
+       - the `data' member of a `BUCKET_CONTENTS' is now a PTR_T
+
+{hashlib,alias,variables,hashcmd,pcomplib}.c
+       - removed some casts when assigning to and using `data' member of a
+         `BUCKET_CONTENTS'
+
+subst.c
+       - in split_at_delims, call make_word_list instead of allocating and
+         initializing a WORD_LIST * directly
+
+make_cmd.[ch]
+       - add_string_to_list is now just a macro that calls make_word_list
+       - make_simple_command now calls make_word_list instead of allocating
+         a WORD_LIST * directly
+
+                                  2/27
+                                  ----
+copy_cmd.c
+       - copy_word now calls make_bare_word to allocate the copy
+       - copy_word_list now calls make_word_list to allocate the copy
+
+shell.h
+       - include `ocache.h' for simple object caching
+       - call cmd_init() to initialize the WORD_DESC and WORD_LIST object
+         caches
+
+{make,dispose}_cmd.c
+       - allocate WORD_DESC * and WORD_LIST * vars from their respective
+         ocaches, and return them to the cache when disposing
+
+jobs.c
+       - renamed old `waiting_for_job' variable to `queue_sigchld', which
+         better reflects its intent:  sigchld_handler does not call waitchld
+         if `queue_sigchld' is non-zero, it simply increments the count of
+         waiting children
+       - cleanup_dead_jobs now just sets and clears queue_sigchld instead of
+         blocking and unblocking SIGCHLD; it calls waitchld at the end if
+         `sigchld' is non-zero, but that's not really necessary
+       - in setjstatus, only call xrealloc if `statsize' is less than the
+         number of processes passed -- no reason to do it if they're the
+         same
+
+                                  2/28
+                                  ----
+sig.[ch]
+       - reinitialize_signals is no more; initialize_signals takes an
+         argument saying whether or not we are reinitializing
+
+builtins/exec.def
+       - reinitialize_signals() -> initialize_signals(1)
+
+test.c
+       - fix filecomp() to work right when one file has a non-positive
+         timestamp and the other file does not exist
+
+doc/{bash.1,bashref.texi}
+       - document what happens for test's -nt and -ot operators when one
+         file operand exists and the other does not
+
+jobs.c
+       - if we haven't messed with SIGTTOU, just manipulate queue_sigchld
+         in notify_of_job_status instead of calling sigprocmask()
+       - list_one_job now calls pretty_print_job directly instead of going
+         through print_job
+       - pretty_print_job now must be called with SIGCHLD blocked or held
+         instead of blocking SIGCHLD itself
+       - changed start_job so that it doesn't call UNBLOCK_CHILD and then
+         immediately call BLOCK_CHILD again (explicitly or via last_pid()),
+         call find_last_pid instead of last_pid and then UNBLOCK_CHILD
+       - changed wait_for_job the same way
+       - find_last_pid now takes a second argument: block; uses BLOCK_CHILD
+         if `block' is 1, not otherwise.  Changed existing calls:
+               find_last_pid(j) -> find_last_pid(j, 0)
+               last_pid(j) -> find_last_pid(j, 1)
+         `last_pid()' is now gone
+       - rewrote wait_for_background_pids(); it was a little strange
+       
+copy_cmd.c
+       - copy_if_command: don't copy null false_case commands
+       - copy_simple_command: don't copy a null redirection list
+
+subst.c
+       - in get_word_from_string and list_string, just check for " \t\n"
+         directly rather than calling strcmp
+       - in get_word_from_string and strip_trailing_ifs_whitespace, use
+         isifs() instead of issep(), since they're never called with
+         separators != $IFS
+       - change issep() to call isifs if separators is longer than one
+         character, since it's never called with anything but "", " ",
+         or $IFS
+
+                                   3/1
+                                   ---
+sig.h
+       - enclose the BLOCK_SIGNAL macro in a do {...} while (0) loop, at it
+         should have been all along
+
+lib/readline/doc/rltech.texinfo
+       - document that readline defaults to stdin/stdout if rl_instream/
+         rl_outstream are NULL
+
+lib/readline/terminal.c
+       - if an application is using a custom redisplay function,
+         rl_resize_terminal just calls rl_forced_update_display to tell
+         (*rl_redisplay_func) to update the display, otherwise call
+         _rl_redisplay_after_sigwinch
+
+lib/readline/readline.c
+       - change readline_internal_setup() so the change to vi insertion mode
+         happens even if readline_echoing_p is 0
+       - don't print the prompt to rl_outstream in readline_internal_setup
+         if we're not echoing and the caller has defined a custom redisplay
+         function -- let the redisplay function deal with it
+
+configure.in
+       - new option: --enable-mem-scramble, controls memory scrambling on
+         free() (on by default; only affects use of bash malloc)
+
+config.h.in
+       - new option MEMSCRAMBLE, controlled by --enable-mem-scramble
+
+                                   3/5
+                                   ---
+parse.y
+       - added ksh-like behavior of [...] to read_token_word:  if a `[' is
+         seen in an assignment context and the previous characters in the
+         token form a valid identifier, parse the [...] with
+         parse_matched_pair to allow spaces (and newlines) in the subscript
+
+bashline.c
+       - new function bash_servicename_completion_function, for completing
+         service names from /etc/services
+
+bashline.h
+       - new extern declaration for bash_servicename_completion_function
+
+builtins/complete.def
+       - allow new `-s/-A service' option to complete and compgen builtins
+
+pcomplete.h
+       - new CA_SERVICE define, new ITEMLIST variable it_services
+
+pcomplete.c
+       - add callback to bash_servicename_completion_function to generate
+         list of matching service names for completion
+
+doc/bash.1,lib/readline/doc/rluser.texinfo
+       - documented new `-s/-A service' option to complete and compgen
+
+                                   3/6
+                                   ---
+builtins/read.def
+       - change hard-coded `0' to new variable `fd' (initially 0) in
+         preparation for adding `-u fd' option
+
+bashline.c
+       - bash_directory_completion_hook calls expand_prompt_string instead
+         of expand_string (it does the right thing).  This keeps expansion
+         errors from causing a longjmp, which shouldn't happen because of
+         completion
+       - command_subst_completion_function was augmented very slightly to
+         do filename completion on a non-command-word in a command
+         substitution
+       - command_subst_completion_function now skips over the lcd that
+         rl_completion_matches puts in matches[0] if there is more than
+         one possible completion
+
+                                   3/7
+                                   ---
+builtins/read.def
+       - only add the unwind_protect to free `rlbuf' if `edit' is non-zero,
+         since we won't be using readline otherwise
+
+lib/sh/zread.c
+       - renamed zread1 -> zreadintr
+
+redir.c
+       - small change to redirection_error() to make a slightly better
+         guess about the invalid file descriptor if the redirection op is
+         r_duplicating_input or r_duplicating_output
+
+include/stdc.h
+       - new macro, SH_VA_START, to encapsulate the difference between
+         stdarg va_start and varargs va_start
+
+{error,pcomplete,print_cmd}.c,builtins/common.c,lib/sh/snprintf.c
+       - use SH_VA_START
+
+                                   3/8
+                                   ---
+builtins/read.def
+       - support for the ksh-like `-u fd' option
+
+general.c
+       - new function sh_validfd(fd), returns 1 if fd is a valid open file
+         descriptor
+
+general.h
+       - extern decl for sh_validfd
+
+bashline.c
+       - don't call posix_readline_initialize() from initialize_readline();
+         sv_strict_posix() should already have taken care of it
+
+                                  3/11
+                                  ----
+{error,pcomplete,print_cmd}.c, builtins/common.c
+       - removed non-varargs versions of functions
+
+builtins/printf.def
+       - if the string argument to %q has non-printing characters, call
+         ansic_quote to quote it rather than sh_backslash_quote
+
+variables.h
+       - new attribute: att_trace (and corresponding trace_p() macro).
+         Functions with this attribute will inherit the DEBUG trap.
+         Currently ignored for variables
+
+builtins/declare.def
+       - new `-t' option to declare/typeset toggle the `att_trace' attribute
+
+builtins/setattr.def
+       - check for att_trace and output `-t' flag in show_var_attributes
+
+execute_cmd.c
+       - if a function is being traced (it has the `-t' attribute set),
+         don't turn off the DEBUG trap when it executes
+
+doc/{bash.1,bashref.texi}
+       - document the new `-t' option to declare/typeset
+
+                                  3/12
+                                  ----
+execute_cmd.c
+       - don't execute the debug trap in the `cm_simple:' case of
+         execute_command_internal; run it in execute_simple_command so we
+         get the line number information right when executing in a shell
+         function
+       - run a DEBUG trap before executing ((...)) arithmetic commands,
+         like ksh93
+       - run a DEBUG trap before executing [[...]] conditional commands,
+         like ksh93
+
+eval.c
+       - add a static forward declaration for alrm_catcher()
+
+general.c
+       - add static forward declarations for bash_special_tilde_expansions,
+         unquoted_tilde_word, initialize_group_array
+
+variables.h
+       - add extern declarations for sh_get_env_value, map_over_funcs,
+         local_exported_variables
+
+variables.c
+       - add static forward declarations for dispose_temporary_env,
+         make_func_export_array
+
+bashhist.c
+       - add static forward declaration for check_history_control
+
+configure.in
+       - add a call to AC_CHECK_DECLS for strcpy
+
+config.h.in
+       - add placeholder for HAVE_DECL_STRCPY define, set by configure
+
+general.h
+       - don't declare strcpy if HAVE_DECL_STRCPY is defined with a non-zero
+         value
+
+sig.h
+       - add prototype to typedef of SigHandler
+
+lib/readline/histlib.h
+       - removed extern declaration of strcpy()
+       - include string.h/strings.h directly in histlib.h instead of source
+         files
+
+lib/readline/{histexpand,histfile,history,histsearch}.c
+       - don't include string.h/strings.h now that histlib.h includes it
+
+lib/tilde/tilde.c
+       - removed extern declaration of strcpy(), rely on string.h/strings.h
+
+command.h
+       - four new redirection types: r_move_input, r_move_output,
+         r_move_input_word, r_move_output_word, for
+         [N]<&word- and [N]>&word- from ksh93
+
+print_cmd.c
+       - changes to print r_move_input[_word] and r_move_output[_word]
+
+copy_cmd.c
+       - changes to copy r_move_input[_word] and r_move_output[_word]
+
+dispose_cmd.c
+       - changes to dispose r_move_input_word and r_move_output_word
+
+make_cmd.c
+       - changes to make r_move_input[_word] and r_move_output[_word] from
+         r_duplicating_{input,output}_word, which is how the new redirs
+         are passed by the parser
+
+redir.c
+       - changes to make r_move_input[_word] and r_move_output[_word] do
+         the right thing when executed
+
+builtins/read.def
+       - print an error message and return failure immediately if zread/zreadc
+         return < 0
+
+doc/{bash.1,bashref.texi}
+       - documented new [n]<&word- and [n]>&word- redirections
+
+                                  3/13
+                                  ----
+lib/readline/isearch.c 
+       - enabled code to allow chars bound to rl_rubout to delete characters
+         from the incremental search string
+
+shell.c
+       - add `-l' invocation option to parse_shell_options; equivalent to
+         `--login'
+       - fixed set_login_shell to check first char of base pathname of argv0
+         for `-', like other shells
+       - move the check for make_login_shell after the call to
+         parse_shell_options because the `-l' option might set it
+
+doc/{bash.1,bashref.texi}
+       - documented new `-l' invocation option
+
+array.c
+       - new function, array_shift, shifts an array left by a specified
+         number of elements
+       - array_walk is now compiled in by default
+       - array_to_assignment_string now takes a second argument: int quoted.
+         If non-zero, the result is single-quoted before being returned
+       - quoted_array_assignment_string has been removed
+
+array.[ch]
+       - renamed most of the array functions so that all have an array_
+         prefix and are more systematically named
+       - array_slice now preserves the indicies from the original array
+       - change array_to_assign to use a static buffer for expanding the
+         array indices, instead of malloc/free
+
+{arrayfunc,subst,variables}.c, builtins/read.def
+       - changed calls to various array functions to use new names
+
+lib/sh/stringvec.c, externs.h
+       - renamed all of the functions to have a strvec_ prefix and to have
+         a more sensible name scheme
+       - strvec_search's arguments are now supplied in reverse order, so
+         the char **array is first, like the other functions
+       - new function, strvec_resize, xrealloc for strvecs
+
+{alias,array,bracecomp,braces,bashline,execute_cmd,findcmd,general,pathexp,
+pcomplete,variables}.c
+lib/sh/stringlist.c
+builtins/{bind,complete,exec,getopts,pushd,set}.def
+       - change calls to all functions from lib/sh/stringvec.c
+       - use strvec_resize where appropriate
+
+externs.h
+       - only declare dup2() if HAVE_DUP2 is undefined or DUP2_BROKEN is
+         defined
+
+lib/readline/{macro,readline,util}.c, lib/readline/rlprivate.h
+       - _rl_defining_kbd_macro is gone, use RL_ISSTATE(RL_STATE_MACRODEF)
+
+lib/readline/readline.h
+       - new struct readline_state, encapsulates most of readline's internal
+         state in case you need reentrancy or nested calls to readline()
+       - extern declarations for rl_save_state, rl_restore_state
+
+lib/readline/readline.c
+       - add (undocumented) int rl_save_state (struct readline_state *),
+         int rl_restore_state (struct readline_state *)
+
+                                  3/14
+                                  ----
+array.[ch]
+       - new function, array_rshift, shifts an array right by a specified
+         number of elements, optionally inserting a new element 0
+
+examples/bashdb/bashdb
+       - new single-file version of bash debugger, originally modified from
+         version in bash-2.04 by Gary Vaughan (the old debugger still
+         appears in examples/obashdb).  This version has a more gdb-like
+         command set
+
+examples/bashdb/bashdb.el
+       - new emacs bashdb debugger mode from Masatake YAMATO <jet@gyve.org>
+
+execute_cmd.c
+       - don't make $LINENO relative to function start unless the shell is
+         currently interactive -- this is what ksh93 does and what I
+         believe to be the intent of POSIX.2 (this required changing some
+         of the test checks because the output has changed)
+       - run the debug trap for each command in an arithmetic for expression,
+         like ksh93 does
+
+lib/readline/vi_mode.c
+       - redid rl_vi_subst (binding func for `s' and `S') in terms of
+         rl_vi_change_to:  `S' == `cc' and `s' == `c '.  This makes undo
+         work right
+
+                                  3/18
+                                  ----
+hashlib.c
+       - fixed hash_walk to return if the item function returns < 0, instead
+         of breaking out of the current hash chain
+
+array.c
+       - fixed array_walk to return if the item function returns < 0, like
+         hash_walk
+
+lib/sh/stringlist.c, externs.h
+       - new function: strlist_walk, takes a stringlist and a pointer to an
+         item func.  Like other _walk funcs, if item func returns < 0 the
+         walk is cancelled
+       - new function: strlist_flush, frees items in the contained list
+         with strvec_flush
+       - renamed functions to have a strlist_ prefix and be more systematic
+
+pcomplib.c,pcomplete.h
+       - removed redundant `progcomp_initialized' variable
+       - renamed functions to have `progcomp_' or `compspec_' prefixes
+         like the hash library
+
+{bashline,pcomplete}.c,builtins/complete.def
+       - fixed calls to stringlist functions to use new names
+       - fixed calls to functions in pcomplib.c to use new names
+
+pcomplete.c
+       - made the debugging code #ifdef DEBUG -- it should be mature enough
+
+builtins/hash.def,parse.y
+       - use REVERSE_LIST(x, t) instead of (t)reverse_list(x)
+
+list.c,{externs,general}.h
+       - renamed the list functions to have a list_ prefix, changed callers
+
+externs.h,{execute_cmd,stringlib,subst}.c,builtins/common.c,lib/sh/stringvec.c
+       - word_list_to_argv -> strvec_from_word_list
+       - argv_to_word_list -> strvec_to_word_list
+       - moved functions to lib/sh/stringvec.c
+
+lib/sh/stringvec.c
+       - changed name of second argument to strvec_from_word_list from `copy'
+         to `alloc' so the use of `copy' between strvec_from_word_list and
+         strvec_to_word_list isn't as confusing
+       - changed name and sense of second argument to
+         strvec_to_word_list from `copy' to `alloc' for the same reason --
+         now both functions agree on semantics of second argument
+
+lib/sh/stringlist.c
+       - ditto for strlist_from_word_list and strlist_to_word_list
+
+subst.c
+       - changed callers of strvec_to_word_list
+
+                                  3/19
+                                  ----
+builtins/hash.def
+       - added `-l' option to list table or individual targets in reusable
+         format
+       - added `-d' option to remove one or more names from the table of
+         hashed commands (provides `unhash' or `unalias -t' functionality)
+
+doc/{bash.1,bashref.texi}
+       - documented new `-l' and `-d' options to `hash'
+
+hashcmd.[ch]
+       - renamed functions to have a `phash_' prefix and follow new naming
+         convention
+       - phash_remove now returns an int:  1 if command not in hash table,
+         0 if filename removed OK
+
+{findcmd,variables}.c, builtins/{hash,type}.def
+       - changed callers to use new names from hashcmd.c
+
+builtins/common.[ch]
+       - new function, sh_notfound(s), prints standard `not found' message
+       - new function, sh_invalidid(s), prints standard `invalid identifier'
+         message
+       - new function, sh_restricted(s), prints standard `restricted' message
+         for restricted shells
+       - new function, sh_invalidnum(s), prints standard `invalid number'
+         message
+       - renamed bad_option to sh_invalidopt, changed to print
+         `invalid option' instead of `unknown option'
+       - new function, sh_invalidoptname, prints standard `invalid option
+         name' for long options
+       - new function, sh_badjob (s), prints standard `no such job' message
+       - new function, sh_invalidsig (s), prints standard `invalid signal
+         specification' message
+       - new function, sh_nojobs (s), prints standard `no job control' message
+       - new function, sh_needarg (s), prints standard `option requires an
+         argument' message
+       - new function, sh_neednumarg (s), prints standard `numeric
+         argument required' message
+       - new function, sh_badpid(s), prints standard `not a pid...' message
+       - new function, sh_erange (s, desc) prints standard `out of range'
+         message, optionally using `desc' to say what the argument is
+
+builtins/{alias,command,declare,exec,hash,type}.def
+       - call sh_notfound() instead of calling builtin_error directly
+
+builtins/{declare,getopts,read,set,setattr}.def
+       - call sh_invalidid() instead of calling builtin_error directly
+
+builtins/{cd,command,enable,exec,hash,source}.def
+       - call sh_restricted() instead of calling builtin_error directly
+
+builtins/{printf,read,ulimit}.def, builtins/common.c
+       - call sh_invalidnum instead of calling builtin_error directly
+
+builtins/{complete,declare,pushd,set}.def, builtins/bashgetopt.c
+       - call sh_invalidopt instead of bad_option or builtin_error directly
+
+builtins/{complete,set,shopt}.def
+       - call sh_invalidoptname instead of builtin_error directly
+
+builtins/{fg_bg,jobs,kill,wait}.def
+       - call sh_badjob instead of calling builtin_error directly
+
+builtins/common.c, builtins/{kill,signal}.def
+       - call sh_invalidsig instead of calling builtin_error directly
+
+builtins/{fg_bg,suspend,wait}.def
+       - call sh_nojobs instead of calling builtin_error directly
+
+builtins/{common,bashgetopt}.c, builtins/{hash,kill}.def
+       - call sh_neednumarg and sh_needarg where required
+
+builtins/{kill,wait}.def
+       - call sh_badpid where required
+
+builtins/{break,fc,history,pushd,shift,ulimit,umask}.def
+       - call sh_erange where appropriate
+
+builtins/printf.def
+       - new static function, printf_erange, prints standard out-of-range
+         warning message
+
+builtins/set.def
+       - changed so that calls to sh_invalidopt always include the leading
+         `+' or `-'
+
+builtins/shopt.def
+       - changed SHOPT_ERROR macro to shopt_error function
+
+builtins/bind.def
+       - regularized error messages to `bind: object: error string' like
+         other error messages
+
+builtins.h
+       - the `short_doc' member of a `struct builtin' is now of type
+         `const char *'
+       - the strings in `long_doc' array of a struct builtin are now const
+
+builtins/mkbuiltins.c
+       - changes for new `const' members of struct builtin
+
+                                  3/20
+                                  ----
+lib/readline/histfile.c
+       - use pointers instead of indexing into buffer when reading the
+         contents of the history file in read_history_range and
+         history_truncate_file
+
+                                  3/21
+                                  ----
+lib/readline/histfile.c
+       - new file, with code to mmap the history file for reading and
+         writing (depends on HAVE_MMAP, currently nothing checks for that)
+
+                                  3/25
+                                  ----
+error.[ch]
+       - new function, err_badarraysub(s), calls report_error with standard
+         `bad array subscript' message
+       - new function, err_unboundvar(s), calls report_error with standard
+         `unbound variable' message
+       - new function, err_readonly(s), calls report_error with standard
+         `readonly variable' message
+
+{arrayfunc,subst}.c
+       - call err_badarraysub where appropriate
+
+{expr,subst}.c
+       - call err_unboundvar where appropriate
+
+{arrayfunc,variables}.c
+       - call err_readonly where appropriate
+
+shell.c
+       - changed text of bad option error messages to be the same as that
+         printed for builtin errors
+
+builtins/common.c
+       - changed sh_invalidopt to print the invalid option before the rest
+         of the error message (required some tests to be modified)
+       - new function, sh_readonly, calls builtin_error with standard
+         `readonly variable' message
+
+variables.c,builtins/declare.def
+       - call sh_readonly where appropriate
+
+lib/sh/stringvec.c
+       - added strvec_remove (sv, s), removes S from SV and shuffles rest of
+         elements down 1
+
+lib/sh/stringlist.c
+       - added strlist_remove(sl, s), just calls strvec_remove on the
+         component list
+
+externs.h
+       - new extern declarations for strvec_remove and strlist_remove
+       - fixed extern declaration for strvec_search; the arguments were
+         reversed (unimportant, it's not compiled into the shell)
+
+subst.c
+       - change param_expand to call quote_escapes on values retrieved when
+         expanding the positional parameters
+       - change parameter_brace_expand_word to quote escapes on values
+         retrieved when expanding the positional parameters
+       - fix parameter_brace_substring to quote escape characters on unquoted
+         substrings extracted from variable values (needed to separate case
+         VT_VARIABLE from VT_ARRAYMEMBER for this, since, because
+         get_var_and_type calls array_value for VT_ARRAYMEMBER, we need to
+         skip over quoted characters in an already-appropriately-quoted
+         string to find the substring we want)
+       - fix parameter_brace_substring to quote escape characters in the
+         value returned by pos_params when expanding subsets of the
+         positional parameters and not within double quotes (in which case
+         pos_params() quotes the string for us)
+       - fix parameter_brace_substring to quote escape characters in the
+         value returned by array_subrange when expanding subsets of an
+         array and not within double quotes (in which case
+         array_subrange() quotes the string for us)
+       - new function, quoted_strlen(s), does strlen(s) while skipping over
+         characters quoted with CTLESC (#ifdef INCLUDE_UNUSED, since it's
+         not used yet)
+       - changed pos_params() so it always returns a list whose members are
+         quoted strings if (quoted&(Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) != 0
+
+arrayfunc.c
+       - fix array_value to consistently call quote_escapes, even when a
+         non-array variable is being subscripted with element 0, in which
+         case we return the variable value
+
+lib/sh/strtrans.c
+       - make the for_echo parameter to ansicstr a `flags' parameter that
+         has its old `for echo' meaning if flags&1 is non-zero (which is
+         consistent with the old code)
+       - Added code to the `flags' parameter to ansicstr so that if flags&2
+         is non-zero, CTLESC and CTLNUL are escaped with CTLESC in the
+         expanded string
+       - change ansiexpand() to call ansicstr with a `flags' parameter of 2
+
+                                  3/26
+                                  ----
+lib/readline/histfile.c
+       - when reading and writing the history file, use malloc instead of
+         xmalloc and handle failures gracefully, so the application doesn't
+         abort if the history file or history list is too big
+
+                                  3/27
+                                  ----
+arrayfunc.c
+       - changed array_value_internal to take an additional `int *'
+         parameter, in which is returned the type of array indexing
+         performed (array[@] vs. array or array[index])
+       - changed array_value and get_array_value to take a corresponding
+         extra parameter and pass it to array_value_internal
+       - changed array_value_internal to no longer return newly-allocated
+         memory or quote CTLESC and CTLNUL in the returned string if
+         `simple' array indexing (subscript not `@' or `*') is being
+         performed.  This makes it more like a variable lookup
+
+arrayfunc.h
+       - changed prototypes for array_value and get_array_value
+
+expr.c
+       - added new parameter to call to get_array_value in expr_streval
+       - don't need to free memory returned by get_array_value any more
+
+subst.c
+       - quote_escapes now works with multibyte characters
+       - dequote_string now works with multibyte characters
+       - dequote_escapes is now needed, so it's compiled in, and it
+         now works with multibyte characters
+       - remove_quoted_escapes now just calls dequote_escapes and copies the
+         result over the argument string
+       - remove_quoted_nulls now returns its char * argument, parallels
+         remove_quoted_escapes
+       - parameter_brace_expand_word now passes the new argument to
+         array_value and quotes CTLESC and CTLNUL in the result if it's a
+         `simple' array expansion by calling quote_escapes
+       - get_var_and_type now returns VT_ARRAYMEMBER for references like
+         ${array} where `array' is an array variable (just like ${array[0]}).
+         Documented (in comment) that a VT_VARIABLE return value means that
+         quote_escapes has been called at some point
+       - changed callers of get_var_and_type to no longer free value if
+         VT_ARRAYMEMBER is returned as type
+       - changed parameter_brace_substring and parameter_brace_patsub to
+         call dequote_escapes on the value from get_var_and_type if the
+         type is VT_VARIABLE, since the substring and pattern substitution
+         code doesn't understand CTLESC quoting
+       - parameter_brace_substring no longer needs to call quoted_substring
+         for the VT_ARRAYMEMBER case
+       - changed parameter_brace_patsub to call quote_escapes on the result
+         of pat_subst for the VT_VARIABLE and VT_ARRAYMEMBER cases, and to
+         quote the returned string in the VT_ARRAYVAR and VT_POSPARAMS cases
+         if the `MATCH_QUOTED' flag isn't set (if it is, the pattern
+         substitution functions perform any necessary quoting)
+       - quoted_substring is no longer used; it's now #ifdef INCLUDE_UNUSED
+
+lib/malloc/mstats.h
+       - new member in _malstats: u_bits32_t bytesreq, the total number of
+         bytes requested by the caller via calls to malloc() and realloc()
+
+lib/malloc/stats.c
+       - print bytesreq member in _print_malloc_stats
+       - don't print statistics for buckets for which nmal == 0 (no mallocs)
+
+lib/malloc/malloc.c
+       - modified internal_malloc, internal_realloc to keep running total of
+         number of bytes requested by calling application
+
+shell.c
+       - sh_exit is now compiled in; exit_shell calls sh_exit
+
+error.c
+       - changed fatal_error, report_error, parser_error to call sh_exit
+
+                                  3/28
+                                  ----
+subst.[ch]
+       - changed Q_NOQUOTE to Q_PATQUOTE; it makes the intent more clear
+
+subst.c
+       - moved code from parameter_brace_expand into a new function that
+         dispatches for pattern substitution: parameter_brace_remove_pattern
+       - changed structure of parameter_brace_remove_pattern to be like
+         parameter_brace_patsub and its ilk:  call get_var_and_type to
+         isolate the variable name, move the pattern isolation code out of
+         the various *_remove_pattern functions into
+         parameter_brace_remove_pattern and pass the results to the various
+         functions, use a switch on the return value from get_var_and_type
+         to decide which function to call, regularized the arguments to the
+         separate pattern removal functions
+       - parameter_brace_remove_pattern now properly quotes escape chars in
+         the returned value
+       - changed get_var_and_type to call dequote_escapes on the `value'
+         parameter for case VT_VARIABLE and return the result in *valp,
+         so the calling functions don't have to do it themselves; changed
+         callers appropriately
+       - fixed getpattern() where it broke posix compliance:  if you enclose
+         a pattern removal spec in double quotes, the outer double quotes
+         have no effect on the pattern (POSIX.1-200x 2.6.2).  This uncovered
+         a bug in the test suite (!)
+
+pathexp.c
+       - fixed a problem with quote_string_for_globbing where it would change
+         consecutive CTLESC chars all to \ instead of changing every other
+         quoted char
+
+                                  3/31
+                                  ----
+lib/malloc/{malloc,stats}.c
+       - moved declaration of _mstats to malloc.c so stats.o doesn't get
+         linked into the shell if the stats functions aren't called
+
+                                   4/2
+                                   ---
+lib/glob/smatch.c
+       - introduce `XCHAR' define, which is the type of arguments passed to
+         strcoll/strcmp/strlen and their wide-character equivalents, added
+         appropriate casts
+       - static arrays in single-byte version of rangecmp() are `char', not
+         `unsigned char', so compilers don't complain about calls to strcoll
+
+lib/glob/sm_loop.c
+       - casts for `XCHAR' and `XCHAR *' arguments to libc functions
+       - use prototype declaration for BRACKMATCH if `PROTOTYPES' is defined
+         to avoid problems with type promotion (unsigned char -> int)
+
+lib/glob/collsyms.h
+       - `name' member of struct _COLLSYM is now of type `XCHAR *', since
+         some compilers don't like `unsigned char *' initializers from
+         constant strings
+
+[bash-2.05b-alpha1 released]
+
+                                   4/3
+                                   ---
+builtins/{evalstring.c,common.h}
+       - new flag for parse_and_execute, SEVAL_NOFREE, means to not free
+         the argument string when finished
+
+lib/readline/text.c
+       - fixed a trivial typo in _rl_insert_char when reading multibyte
+         char sequences
+       - replace calls to ding() with rl_ding()
+
+include/chartypes.h
+       - remove SIGN_EXTEND_CHAR and TOASCII macros; they're unused
+
+make_cmd.c
+       - include dispose_cmd.h for extern function declarations
+
+lib/glob/glob.c
+       - include `shmbutil.h' and `xmalloc.h' for extern function declarations
+
+lib/glob/smatch.c
+       - include `xmalloc.h' for extern function declarations
+
+shell.c
+       - fix maybe_make_restricted to use its argument instead of global
+         `shell_name'
+
+version.c
+       - update copyright message to include this year
+
+lib/readline/display.c
+       - fixes from Jiro SEKIBA <jir@yamato.ibm.com> to fix autowrapping
+         when using multibyte characters
+
+lib/glob/sm_loop.c
+       - fixed a problem in BRACKMATCH where not enough memory was allocated
+         to hold a multibyte character when parsing POSIX.2 char class names
+
+support/config.{guess,sub}
+       - updated via patch from Paul Eggert with latest GNU additions
+
+variables.c
+       - var_lookup should use its `vcontext' argument instead of
+         unconditionally using `shell_variables'
+
+                                   4/4
+                                   ---
+builtins/bind.def,doc/{bash.1,bashref.texi}
+       - changed the usage summary and help text to make it clear that any
+         readline command that may appear in ~/.inputrc may be supplied as
+         one of the non-option arguments to `bind'
+
+builtins/mkbuiltins.c
+       - added support for `-H' option, which means to write long documentation
+         for each builtin to a separate file in the `helpfiles' directory
+
+builtins/Makefile.in
+       - new target `helpdoc', just creates long doc files in helpfiles
+         directory
+
+lib/sh/zcatfd.c
+       - new file, with zcatfd(int fd, int ofd, char *fn); dumps data from
+         FD to OFD
+
+Makefile.in,lib/sh/Makefile.in
+       - added zcatfd.c, zcatfd.o member of libsh.a
+
+builtins/evalstring.c
+       - changed cat_file to call zcatfd(fd, 1, fn)
+
+builtins/{shopt,colon}.def
+       - removed the $DOCNAME directive for `shopt', `true', and `false';
+         just use the names
+       - changed $DOCNAME for `:' to just be `colon' instead of
+         `colon_builtin'
+
+builtins/reserved.def
+       - added help entries for ((, [[, `for (('
+
+builtins/let.def
+       - add id++, id--, ++id, --id, ** to help text
+
+                                   4/8
+                                   ---
+builtins/bashgetopt.[ch]
+       - changed to allow options beginning with `+', enabled by a leading
+         `+' in the option string
+       - new variable, list_opttype, set to `-' or `+'
+
+builtins/{common.c,{builtin,eval,exit,fg_bg,let,printf,pushd,return,source,wait}.def
+       - changes to allow a `--' option for every builtin that accepts
+         operands but not options, as per posix.1-2001
+
+builtins/{declare,setattr}.def
+       - use internal_getopt for parsing options, now that it supports `+'
+
+builtins/set.def
+       - use internal_getopt for initial option parse, now that it supports
+         a leading `+'
+
+
+{configure,Makefile}.in, builtins/{Makefile.in,help.def,mkbuiltins.c}
+       - support for a new configure option, ``--enable-separate-helpfiles'',
+         moves the `long' help text to separate help files, installed by
+         default into ${datadir}/bash, one file per builtin.  Off by
+         default -- it saves 47K, but it's only 47K, and it's in the text
+         segment
+
+flags.c
+       - build internal_getopt() option string argument from flags array at
+         runtime in shell.c
+
+shell.c
+       - new variable to control writing malloc stats at exit:
+         malloc_trace_at_exit, 0 by default
+
+lib/malloc/malloc.c
+       - heavily updated:
+               o partial page allocated on first call to malloc to make
+                 subsequent sbrks page-aligned no longer wasted
+               o begin and end range guards are now the same value: the chunk
+                 requested
+               o coalescing code was changed to attempt to coalesce first two
+                 adjacent blocks on the free list; enabled by default
+               o blocks of size 32 are now candidates for larger block
+                 splitting, since 32 is the most popular size
+               o blocks of size 32 are now candidates for smaller block
+                 coalescing
+               o the IN_BUCKET check was changed to just make sure that the
+                 size isn't too big for the bucket, since the `busy block'
+                 checking code may increase the bucket by one or more,
+                 meaning that the old check would fail and cause a panic when
+                 a chunk allocated in such a way was freed
+               o bin sizes are now precomputed and looked up in an array
+                 rather than being computed at runtime
+               o moved the _mstats declaration here to avoid the stats code
+                 being linked in even when no stats functions were called
+                 (only matters if MALLOC_DEBUG is defined)
+               o malloc now keeps track of the address of the top of the heap
+                 and will return large chunks to the system with calls to
+                 sbrk with a negative argument when freeing the top chunk.
+                 Two thresholds:  LESSCORE_FRC means to unconditionally return
+                 memory to the system; LESSCORE_MIN means to return memory if
+                 there's at least one block already on the free list
+
+lib/malloc/mstats.h
+       - stats struct now keeps track of number of block coalesces by bin,
+         and the number of times memory was returned to the system by bin
+
+lib/malloc/stats.c
+       - trace_malloc_stats now takes a second argument: the name of the file
+         to write to.  The first `%p' in the template file name is replaced
+         by the pid
+
+                                   4/9
+                                   ---
+lib/malloc/imalloc.h
+       - added some macros derived from dlmalloc and glibc malloc to inline
+         memcpy and memset if the requested size is <= 32 bytes
+
+lib/malloc/malloc.c
+       - use MALLOC_MEMSET instead of memset in internal_{malloc,free}
+
+include/ocache.h
+       - use OC_MEMSET (variant of MALLOC_MEMSET) in ocache_free
+
+configure.in, config.h.in
+       - check for getservent(), define HAVE_GETSERVENT if found
+
+bashline.c
+       - punt immediately from bash_servicename_completion_function if
+         HAVE_GETSERVENT is not defined (cygwin seems to not define it)
+       - include "input.h" for extern save_token_state() and
+         restore_token_state() declarations
+       - change bash_execute_unix_command to call parse_and_execute with
+         SEVAL_NOHIST flag so the command doesn't get saved on the history
+         list
+       - change bash_execute_unix_command to save and restore the current
+         command line count and the token state (last_read_token, etc.).
+         Everything else is saved by either parse_and_execute directly or
+         the call it makes to push_stream().  The shell_input_line stuff
+         doesn't need to be saved and restored; it's not computed until
+         readline() returns
+
+                                  4/10
+                                  ----
+lib/glob/glob.[ch]
+       - glob_filename and glob_vector now take an additional `flags' arg
+       - define GX_MARKDIRS as possible flag value for glob_filename and
+         glob_vector
+
+lib/sh/snprintf.c
+       - fixed some bugs with handling of `g' and `G' formats
+       - make sure numtoa returns the fractional part correctly when passed 0
+       - implemented thousands grouping for `'' flag character
+
+lib/sh/rename.c
+       - a few changes to make it more bulletproof
+
+                                  4/11
+                                  ----
+lib/glob/glob.c
+       - added the couple of dozen lines of code to glob_dir_to_array to
+         finish implementing GX_MARKDIRS
+
+builtins/set.def
+       - changed unset builtin so that it no longer considers unsetting an
+         unset variable or function to be an error
+
+lib/readline/display.c
+       - fix to rl_redisplay for a problem which caused display to be messed
+         up when the last line of a multi-line prompt (possibly containing
+         invisible characters) was longer than the screen width
+
+                                  4/15
+                                  ----
+aclocal.m4
+       - use AC_DEFINE_UNQUOTED in BASH_SYS_DEFAULT_MAIL_DIR instead of
+         enumerating all of the possible values and using AC_DEFINE
+
+                                  4/16
+                                  ----
+Makefile.in, {builtins,support}/Makefile.in
+       - new variables, CFLAGS_FOR_BUILD and CPPFLAGS_FOR_BUILD, substituted
+         by `configure'
+       - changed CCFLAGS_FOR_BUILD to BASE_CCFLAGS, removing $(CPPFLAGS);
+         CCFLAGS and CCFLAGS_FOR_BUILD now include $(BASE_CCFLAGS) with
+         (possibly) different values for CPPFLAGS and CFLAGS 
+       - GCC_LINT_CFLAGS now includes $(BASE_CCFLAGS) and $(CPPFLAGS)
+         instead of CCFLAGS_FOR_BUILD
+       - new variable, LDFLAGS_FOR_BUILD, right now equivalent to LDFLAGS
+       - remove $(CPPFLAGS) from recipes for buildversion, mksignames, and
+         mksyntax
+
+configure.in
+       - compute and substitute CFLAGS_FOR_BUILD, CPPFLAGS_FOR_BUILD, and
+         LDFLAGS_FOR_BUILD
+       - changed qnx to use LOCAL_LDFLAGS and LOCAL_LIBS instead of putting
+         everything in LOCAL_LDFLAGS
+
+builtins/Makefile.in
+       - remove $(PROFILE_FLAGS) from recipe for building `mkbuiltins'
+       - use LDFLAGS_FOR_BUILD instead of LDFLAGS in recipe for building
+         `mkbuiltins'
+
+Makefile.in
+       - use $(CC_FOR_BUILD) and $(CCFLAGS_FOR_BUILD) to build auxiliary
+         test programs (printenv, recho, zecho)
+
+support/Makefile.in
+       - use CC_FOR_BUILD and CCFLAGS_FOR_BUILD in recipe for building
+         `man2html'
+
+lib/tilde/Makefile.in
+       - substitute PROFILE_FLAGS, use PROFILE_FLAGS in $(CCFLAGS)
+
+                                  4/25
+                                  ----
+Makefile.in, configure.in
+       - moved RELSTATUS to configure.in; configure substitutes it into
+         the generated Makefile
+
+lib/sh/snprintf.c
+       - fix wchars() to deal with systems where MB_CUR_MAX is not a
+         constant expression
+
+                                   5/2
+                                   ---
+lib/sh/shquote.c
+       - add `,' to list of chars that are backslash-quoted.  It doesn't
+         hurt normal usage and prevents filenames with commas from being
+         inappropriately split by brace expansion after using
+         complete-into-braces
+
+                                   5/6
+                                   ---
+lib/sh/xstrchr.c
+       - we only need the check of MB_CUR_MAX and the slow code for a
+         few encodings, and even then only for a subset of the charset
+
+arrayfunc.c
+       - some speedups for skipsubscript and multibyte chars from Bruno Haible
+
+locale.c
+       - changed set_lang to call setlocale(LC_ALL, ...) if LC_ALL doesn't
+         already have a value, but doesn't change any shell variables
+
+include/shmbutil.h
+       - major speedups from Bruno Haible, mostly concerned with reducing
+         the number of strlen(3) calls
+
+subst.c
+       - change callers of macros in shmbutil.h to add extra argument as
+         necessary
+       - skip_single_quoted and skip_double_quoted take another argument:
+         the length of the string; mostly useful when using multibyte chars
+       - many speedups from precomputing string lengths at function start
+       - fixed a small bug in de_backslash in the midst of rewriting for
+         better efficiency
+
+{braces,make_cmd,pathexp}.c
+       - change callers of macros in shmbutil.h to add extra argument as
+         necessary
+
+pathexp.c
+       - fix a one-too-far problem with multibyte chars in
+         unquoted_glob_pattern_p
+
+braces.c
+       - brace_gobbler takes a new argument, the length of the passed string
+       - expand_amble takes a new argument, the length of the passed string
+
+                                   5/7
+                                   ---
+subst.c
+       - modified remove_quoted_nulls to eliminate the memory allocation and
+         do the copy in place using the same strategy as de_backslash
+
+lib/readline/{rldefs.h,complete.c}
+       - new define RL_QF_OTHER_QUOTE, so _rl_find_completion_word can note
+         that it found a quoting character other than \'" that appears in
+         rl_completer_quote_characters
+
+                                   5/9
+                                   ---
+jobs.c
+       - save and restore old value of jobs_list_frozen when calling trap
+         handlers from set_job_status_and_cleanup to avoid seg faults when
+         running recursive trap handlers
+
+                                  5/10
+                                  ----
+builtins/common.h
+       - new #defines to use for value of changed_dollar_vars (provides
+         information about the caller who wants to blow away the old dollar
+         variables)
+
+builtins/common.c
+       - changed set_dollar_vars_changed to set changed_dollar_vars to one
+         of the ARGS_* values depending on the caller and environment
+
+builtins/source.def
+       - source restores the positional parameters unless the `set' builtin
+         was called to specify a new set while not executing a shell function
+
+                                  5/13
+                                  ----
+POSIX
+       - new file, was in CWRU/POSIX.NOTES
+
+doc/{Makefile.in,Makefile}
+       - changed `posix' rule to modify ../POSIX
+
+doc/mkposix
+       - write to `POSIX' by default
+
+lib/sh/strtrans.c
+       - when ansicstr is parsing a format string for `echo -e' (or the
+         equivalent xpg_echo option is enabled), obey the POSIX-2001/SUSv3
+         standard and accept 0-3 octal digits after a leading `0'
+
+doc/{bash.1,bashref.texi}
+       - updated `echo' description to note that up to three octal digits
+         are now accepted following `\0'
+
+                                  5/16
+                                  ----
+doc/Makefile.in
+       - remove the generated documentation on `make distclean' if the
+         build directory and source directory are not the same
+
+Makefile.in
+       - descend into `support' subdirectory on a `make clean' and
+         `make distclean'
+       - remove parser-built, y.tab[ch] on a `make distclean' if the build
+         directory and source directory are not the same
+
+support/Makefile.in
+       - support various `clean' targets and remove man2html.o and man2html
+
+{configure,Makefile}.in
+       - move values for DEBUG and MALLOC_DEBUG into configure.in; on by
+         default for development versions; off by default for releases
+         (off for profiling, too)
+
+                                  5/21
+                                  ----
+parse.y
+       - modified the grammar to allow a simple_list followed by yacc_EOF
+         to terminate a command.  This fixes problems with things like
+         a backslash-newline at the end of an `eval'd string
+       - change handle_eof_input_unit() to reset the token state before
+         calling prompt_again(), in case the prompt to be evaluated contains
+         a command substitution
+
+                                  5/23
+                                  ----
+lib/readline/vi_mode.c
+       - fix `r' command (rl_vi_change_char) when HANDLE_MULTIBYTE is defined
+         but MB_CUR_MAX == 1
+
+                                  5/24
+                                  ----
+lib/malloc/watch.c
+       - don't try to print `file' argument to _watch_warn if it's null
+
+lib/malloc/malloc.c
+       - changed guard checking code in internal_{malloc,free,realloc} to
+         access memory as (char *) and copy into a union instead of
+         casting and dereferencing a pointer to u_bits32_t, since that
+         results in unaligned accesses which will cause Sparcs to upchuck
+
+                                  5/30
+                                  ----
+[bash-2.05b-beta1 released]
+
+lib/readline/text.c
+       - fixed a problem with rl_transpose_chars on systems supporting
+         multibyte characters with a locale that doesn't have any multibyte
+         chars
+
+                                   6/4
+                                   ---
+expr.c
+       - fix a/=0 and a%=0 to throw evaluation errors rather than core dumps
+
+lib/readline/display.c
+       - fix core dump when line wrapping a multibyte character (line
+         accidentally dropped from the original patch)
+
+lib/readline/mbutil.c
+       - fix reversed return value from _rl_is_mbchar_matched; fixes problem
+         with backward-char-search
+
+                                  6/10
+                                  ----
+lib/sh/getenv.c
+       - fix getenv to not free value returned by find_tempenv_variable
+       - add setenv, putenv, unsetenv for completeness
+
+                                  6/12
+                                  ----
+shell.c
+       - change init_noninteractive to init expand_aliases to the value of
+         posixly_correct
+       - don't initialize expand_aliases to posixly_correct anywhere else.
+         This allows the -O expand_aliases invocation option to work correctly
+
+general.c
+       - fix move_to_high_fd to not try the dup2 unless the fd loop results
+         in an fd > 3; just return the passed file descriptor otherwise
+       - use HIGH_FD_MAX, defined in general.h, instead of hard-coded 256
+         as highest file descriptor to try
+
+subst.c
+       - in process_substitute, call move_to_high_fd with `maxfd' parameter
+         of -1 instead of 64, so move_to_high_fd will use its maximum
+
+                                  6/21
+                                  ----
+lib/malloc/malloc.c
+       - don't bother calling MALLOC_MEMSET if the requested size is 0
+
+builtins/setattr.def
+       - note in short doc that export and readonly can take assignment
+         statements as arguments
+
+error.c
+       - new function, error_prolog(), to capture common error message
+         prefix code (except for parser errors)
+
+                                  6/25
+                                  ----
+aclocal.m4
+       - add tests for standard-conforming declarations for putenv and
+         unsetenv in system header files
+
+{configure,config.h}.in
+       - call BASH_FUNC_STD_PUTENV and BASH_FUNC_STD_UNSETENV, define
+         HAVE_STD_GETENV and HAVE_STD_UNSETENV, respectively, if they
+         succeed
+
+lib/sh/getenv.c
+       - change putenv and unsetenv to take differing prototypes in
+         stdlib.h into account
+
+                                  6/27
+                                  ----
+[bash-2.05b-beta2 released]
+
+                                  6/28
+                                  ----
+builtins/common.c
+       - fix get_job_spec so that %N works when N is the size of the jobs
+         list (%8 means job 8, but the 7th member of the jobs array, so
+         it's OK if N == job_slots because the function returns N-1)
+
+                                   7/1
+                                   ---
+shell.c
+       - turn off line editing if $EMACS is set to `t'
+
+                                  7/10
+                                  ----
+builtins/set.def
+       - remove mention of `-i' from long help doc, since it has no effect
+
+                                  7/17
+                                  ----
+[bash-2.05b released]
+
+                                  7/18
+                                  ----
+
+lib/malloc/malloc.c
+       - make sure that the `free_return' label has a non-empty statement
+         to branch to
+
+                                  7/19
+                                  ----
+locale.c
+       -  only call setlocale() from set_lang() if HAVE_SETLOCALE is defined;
+          otherwise just return 0
+
+lib/readline/mbutil.c
+       - only try to memset `ps' in _rl_get_char_len if it's non-NULL.  Ditto
+         for _rl_adjust_point
+
+                                  7/23
+                                  ----
+execute_cmd.c
+       - fix for executing_line_number() when compiling without conditional
+         commands, dparen arithmetic or the arithmetic for command
+
+                                  
+                                  7/24
+                                  ----
+support/Makefile.in
+       - fix maintainer-clean, distclean, mostlyclean targets
+
+builtins/common.c
+       - fix bug in sh_nojobs where it doesn't pass the right number of args
+         to builtin_error
+
+bashline.c
+       - when using command completion and trying to avoid appending a slash
+         if there's a directory with the same name in the current directory,
+         use absolute_pathname() instead of just checking whether the first
+         char of the match is a slash to catch things like ./ and ../
+
+examples/complete/bashcc-1.0.1.tar.gz
+       - a package of completions for Clear Case, from Richard S. Smith
+         (http://www.rssnet.org/bashcc.html)
+
+input.c
+       - fix check_bash_input to call sync_buffered_stream if the passed fd
+         is 0 and the shell is currently reading input from fd 0 -- all it
+         should cost is maybe an additional read system call, and it fixes
+         the bug where an input redirection to a builtin inside a script
+         which is being read from stdin causes the already-read-and-buffered
+         part of the script to be thrown away, e.g.:
+
+               bash < x1
+
+         where x1 is
+
+               hostname
+               read Input < t.in
+               echo $Input
+               echo xxx
+
+execute_cmd.c
+       - in initialize_subshell(), call unset_bash_input (0) to not mess with
+         fd 0 if that's where bash thinks it's reading input from.  Fixes
+         bug reported by jg@cs.tu-berlin.de on 17 July 2002.  Should be a way
+         to check whether or not the current fd 0 at the time of the call has
+         not been redirected, like in the bug report.  Also might eventually
+         want to throw in a sync_buffered_stream if bash is reading input
+         from fd 0 in a non-interactive shell into a buffered stream, so the
+         stream is sync'd -- might be necessary for some uses
+
+                                  7/25
+                                  ----
+lib/readline/signals.c
+       - make sure rl_catch_sigwinch is declared even if SIGWINCH is not
+         defined, so the readline state saving and restoring functions in
+         readline.c are always the same size even if SIGWINCH is not defined,
+         and undefined references don't occur when SIGWINCH is not defined
+
+                                  7/30
+                                  ----
+bashline.c
+       - augment patch from 7/24 to not disable rl_filename_completion_desired
+         if the first char of the match is `~'
+
+lib/readline/bind.c
+       - when creating `shadow' keymaps `bound' to ANYOTHERKEY, don't bind
+         a key whose type is ISFUNC but whose function is the `fake'
+         rl_do_lowercase_version (fixes debian bash bug #154123)
+
+lib/readline/readline.c
+       - don't call _rl_vi_set_last from _rl_dispatch_subseq if
+         key == ANYOTHERKEY (when truncated to `sizeof(char)', it will be 0,
+         which strchr will find in `vi_textmod')
+
+                                  7/31
+                                  ----
+lib/readline/input.c
+       - fix rl_gather_tyi to only slurp up one line of available input, even
+         if more than one line is available (fixes debian bash bug #144585)
+
+                                   8/3
+                                   ---
+bashline.c
+       - better fix for command completion problem -- test for directory
+         explicitly with test_for_directory before turning off
+         rl_filename_completion_desired, since that's the case we're trying
+         to protect against
+
+                                   8/5
+                                   ---
+include/shmbutil.h
+       - fix ADVANCE_CHAR macro to advance the string pointer if mbrlen
+         returns 0, indicating that the null wide character (wide string
+         terminator) was found (debian bash bug #155436)
+
+lib/readline/mbutil.c
+       - fix _rl_adjust_point to increment the string pointer if mbrlen
+         returns 0
+
+support/shobj-conf
+       - fix for the `-install_name' value in SHLIB_XLDFLAGS assignment for
+         Darwin from the fink folks
+
+                                   8/6
+                                   ---
+builtins/exit.def
+       - broke code that runs ~/.bash_logout out into a separate function:
+         bash_logout()
+
+builtins/common.h
+       - extern declaration for bash_logout()
+
+eval.c
+       - call bash_logout() from alrm_catcher(), so timed-out login shells
+         run ~/.bash_logout before processing the exit trap
+
+lib/sh/strtrans.c
+       - implemented $'\x{hexdigits}' expansion from ksh93
+
+configure.in
+       - define RECYCLES_PIDS in LOCAL_CFLAGS for cygwin; don't bother to
+         link with -luser32
+
+examples/loadables/strftime.c
+       - new loadable builtin, interface to strftime(3)
+
+                                   8/7
+                                   ---
+parse.y
+       - parse_arith_cmd now takes a second argument, a flag saying whether
+         or not to add double quotes to a parsed arithmetic command; changed
+         callers
+       - changed parse_dparen so it tells parse_arith_cmd to not add the
+         double quotes and therefore doesn't need to remove them
+       - change parse_dparen to add W_NOGLOB|W_NOSPLIT|W_QUOTED flags to word
+         created when parsing (( ... )) arithmetic command, since the double
+         quotes are no longer added
+
+make_cmd.c
+       - in make_arith_for_expr, set the flags on the created word to
+         W_NOGLOB|W_NOSPLIT|W_QUOTED
+
+execute_cmd.c
+       - change execute_arith_command to expand the expression with
+         expand_words_no_vars, like the arithmetic for command code does
+       - fix execute_arith_command to handle the case where the expanded
+         expression results in a NULL word without crashing
+
+tests/{arith-for,cprint}.tests
+       - change expected output to account for no longer adding quotes to
+         ((...)) commands
+
+                                   8/8
+                                   ---
+print_cmd.c
+       - take out the space after printing the `((' and before printing the
+         `))' in print_arith_command, print_arith_for_command, and
+         xtrace_print_arith_cmd
+
+tests/{arith-for,cprint}.tests
+       - change expected output to account for no longer adding leading and
+         trailing spaces when printing ((...)) and arithmetic for commands
+
+                                  8/17
+                                  ----
+subst.c
+       - fix issep() define to handle case where separators[0] == '\0', in
+         which case it always returns false
+
+lib/readline/histexpand.c
+       - fix off-by-one error in history_expand_internal when using the `g'
+         modifier that causes it to skip every other match when matching a
+         single character (reported by gjyun90@resl.auto.inha.ac.kr)
+
+doc/{bash.1,bashref.texi}
+       - make sure that the name=word form of argument to declare/typeset,
+         export, and readonly is documented in the description
+
+                                  8/30
+                                  ----
+lib/readline/histexpand.c
+       - make history_expand_internal understand double quotes, because
+         single quotes are not special inside double quotes, according to
+         our shell-like quoting conventions.  We don't want unmatched
+         single quotes inside double-quoted strings inhibiting history
+         expansion
+       - make `a' modifier equivalent to `g' modifier for compatibility with
+         the BSD csh
+       - add a `G' modifier that performs a given substitution once per word
+         (tokenized as the shell would do it) like the BSD csh `g' modifier
+
+                                  8/31
+                                  ----
+braces.c
+       - when compiling for the shell, treat ${...} like \{...} instead of
+         trying to peek backward when we see a `{'.  This makes it easier
+         to handle things like \${, which should be brace expanded because
+         the $ is quoted
+
+                                   9/7
+                                   ---
+aclocal.m4
+       - redirect stdin from /dev/null in BASH_CHECK_DEV_FD before testing
+         the readability of /dev/fd/0, so we're dealing with a known quantity
+
+                                  9/11
+                                  ----
+[prayers for the victims of 9/11/01]
+
+shell.c
+       - fix maybe_make_restricted to handle a restricted login shell with a
+         base pathname of `-rbash' and skip over any leading `-'
+
+                                  9/13
+                                  ----
+builtins/evalstring.c
+       - in parse_and_execute, make sure we don't try to run unwind-protects
+         back to `pe_dispose' after a longjmp back to top_level if the
+         pe_dispose frame hasn't been initialized
+
+lib/readline/display.c
+       - fix problem with prompt overwriting previous output when the output
+         doesn't contain a newline in a multi-byte locale.  This also should
+         fix the problem of bash slowing down drastically on long lines when
+         using a multi-byte locale, because it no longer tries to rewrite the
+         entire line each time.  Patch from Jiro SEKIBA <jir@yamato.ibm.com>
+
+parse.y
+       - move the typedef for alias_t that is compiled in if ALIAS is not
+         defined up before the prototype for push_string, since that takes
+         an alias_t * parameter
+
+lib/readline/terminal.c
+       - bind the termcap description's left and right arrow keys to
+         rl_backward_char and rl_forward_char, respectively,  instead of
+         rl_forward and rl_backward (which are just there for backwards
+         compatibility)
+
+aclocal.m4
+       - when testing readability of /dev/stdin, redirect stdin from /dev/null
+         to make sure it's a readable file
+
+                                  9/17
+                                  ----
+config-bot.h
+       - don't test __STDC__ when deciding whether or not to use stdarg.h;
+         just use it if it's present
+
+tests/read2.sub
+       - redirect from /dev/tty when using `read -t' 
+
+                                  9/20
+                                  ----
+builtins/history.def
+       - when reading `new' entries from the history file with `history -n',
+         fix increment of history_lines_this_session by taking any change
+         in history_base into account
+
+lib/sh/pathphys.c
+       - changes to sh_physpath to deal with pathnames that end up being
+         longer than PATH_MAX without dumping core
+
+lib/readline/doc/{history.3,hsuser.texinfo},doc/ bash.1
+       - documented new `a' and `G' history modifiers
+
+                                  9/25
+                                  ----
+lib/readline/misc.c
+       - when traversing the history list with arrow keys in vi insertion
+         mode, put the cursor at the end of the line (like in emacs mode)
+
+mksyntax.c
+       - don't try to use \a and \v unless __STDC__ is defined; use the
+         ascii integer equivalents otherwise
+       - include "config.h" in the generated syntax.c file for a possible
+         definition of `const'
+
+doc/{bash.1,bashref.texi}
+       - document the meaning of a null directory in $PATH
+
+                                  9/26
+                                  ----
+parse.y
+       - fix set_line_mbstate to handle case where mbrlen() returns 0,
+         indicating the null wide character
+       - fix set_line_mbstate so we don't directly compare a char variable
+         to EOF, since char can (and is) unsigned on some machines
+
+bashline.c
+       - change bash_execute_unix_command to save a little bit more state:
+         last_shell_builtin, this_shell_builtin, last_command_exit_value
+
+                                  9/27
+                                  ----
+execute_cmd.c
+       - tentative change to execute_simple_command to avoid freeing freed
+         memory in the case where bash forks early but still ends up calling
+         execute_disk_command, without passing newly-allocated memory to
+         make_child.  This may fix the core dumps with the linux-from-scratch
+         folks
+
+                                  9/28
+                                  ----
+Makefile.in,{builtins,lib/sh}/Makefile.in      
+       - fix up dependencies, mostly on ${BUILD_DIR}/version.h, so that
+         parallel makes work with GNU and BSD makes
+
+shell.h
+       - new struct to save partial parsing state when doing things like
+         bash_execute_unix_command and other operations that execute
+         commands while a line is being entered and parsed
+
+parse.y
+       - new functions, save_parser_state() and restore_parser_state(), to
+         save and restore partial parsing state 
+
+bashline.c
+       - change bash_execute_unix_command to call {save,restore}_parser_state
+
+builtins/jobs.def
+       - change execute_list_with_replacements to eliminate a run_unwind_frame
+         in favor of calling the cleanup explicitly and discarding the frame
+
+execute_cmd.c
+       - change execute_for_command to avoid a run_unwind_frame in the case
+         where the loop variable is readonly or otherwise not assignable
+       - change execute_select_command and execute_simple_command to use
+         discard_unwind_frame by running the cleanup code explicitly, instead
+         of using run_unwind_frame
+       - make sure execute_select_command decreases loop_level even on error
+
+                                  9/30
+                                  ----
+doc/{bash.1,bashref.texi}
+       - fixed description of `unset' now that unsetting a previously-unset
+         variable is no longer an error
+
+                                  10/3
+                                  ----
+{configure,config.h}.in
+       - augment check for strtold with additional check to detect the
+         horribly broken hp/ux 11.x implementation that returns `long_double';
+         defines STRTOLD_BROKEN if so
+
+builtins/printf.def
+       - define floatmax_t as `double' if STRTOLD_BROKEN is defined
+
+                                  10/5
+                                  ----
+lib/readline/keymaps.c
+       - don't automatically bind uppercase keys to rl_do_lowercase_version
+         in rl_make_bare_keymap
+
+lib/readline/readline.c
+       - explicitly check for ANYOTHERKEY binding to rl_do_lowercase_version
+         and dispatch to lowercase of key when a prefix is not matched
+
+                                  10/12
+                                  -----
+bashline.c
+       - set COMP_WORDBREAKS in enable_hostname_completion to the value
+         of rl_completer_word_break_characters
+
+variables.c
+       - new special variable COMP_WORDBREAKS, controls the value of
+         rl_completer_word_break_characters
+
+variables.h
+       - new extern declaration for sv_comp_wordbreaks()
+
+subst.c
+       - change split_at_delims to behave more like shell word splitting if
+         the passed value for the delimiters is NULL, indicating that the
+         function is to use $IFS to split
+
+{execute_cmd,jobs,test,findcmd,input,make_cmd,redir,shell}.c
+builtins/mkbuiltins.c,builtins/{fc,history,source,umask}.def
+lib/sh/netconn.c
+lib/termcap/termcap.c
+lib/readline/histfile.c
+       - make sure all inclusions of <sys/file.h> are protected by
+         HAVE_SYS_FILE_H
+
+bashline.c
+       - don't turn off rl_filename_completion_desired in
+         attempt_shell_completion if the partial pathname contains a slash.
+         This still doesn't solve the problem of partial pathname completion
+         starting with a directory in the current directory without a
+         leading `./'.  There's no way to tell the difference between that
+         and a file found in $PATH (which may contain `.') at the point that
+         attempt_shell_completion acts
+
+                                  10/18
+                                  -----
+locale.c
+       - don't set lc_all to the default locale when LC_ALL is being unset
+       - new function, reset_locale_vars(), called to recompute the correct
+         locale variable values when LC_ALL is unset
+       - changed set_lang to not set LC_ALL, which it never should have been
+         doing in the first place, and to maintain a local variable `lang'
+         corresponding to $LANG
+       - change get_locale_var to use the precedence posix.2 specifies:
+         LC_ALL overrides individual variables; LANG, if set, is the default
+       - change set_locale_var to call get_locale_var to get the appropriate
+         value for the variable being set or unset
+       - call get_locale_var instead of using passed value in set_locale_var
+         to get the defaulting and precedence right
+
+lib/readline/nls.c
+       - new function, _rl_get_locale_var(), which does the same thing as
+         locale.c:get_locale_var(), with the right precedence and defaulting,
+         using sh_get_env_value to get the right bash variable values
+       - if HAVE_SETLOCALE is defined, _rl_init_eightbit first calls
+         _rl_get_locale_var to get the right value for LC_CTYPE, and uses
+         that in the call to setlocale.  If _rl_get_locale_var returns NULL,
+         call setlocale() to get the current international environment, and,
+         finally, if that returns null, call setlocale with a second argument
+         of "" to force the implementation's `native' environment
+
+pcomplete.c
+       - change gen_wordlist_completions to dequote the text before comparing
+         it against the expanded word list 
+       - changed gen_matches_from_itemlist to do the same thing
+
+bashline.c
+       - new global function, bash_dequote_word, calls bash_dequote_filename
+         on the text passed.  Used by the programmable completion code
+
+lib/readline/histfile.c
+       - make sure that whenever read_history_range returns a non-zero value
+         that it sets errno to some useful value
+
+                                  10/19
+                                  -----
+variables.c
+       - COMP_WORDBREAKS is now a dynamic variable, mirroring value of
+         rl_completer_word_break_characters.  Makes sure that the variable
+         always points to dynamic memory if it's not null or the readline
+         default
+
+bashline.c
+       - change enable_hostname_completion to manage a dynamic value of
+         rl_completer_word_break_characters, since assignments to
+         COMP_WORDBREAKS can change its value unpredictably
+
+lib/readline/{complete.c,readline.h}
+       - rl_completer_word_break_characters no longer has `const' attribute
+
+bashline.c
+       - clean up necessary places due to rl_completer_word_break_characters
+         no longer being `const'
+
+doc/{bash.1,bashref.texi}
+       - document new COMP_WORDBREAKS variable
+
+                                  10/21
+                                  -----
+print_cmd.c
+       - fix indirection_level_string to handle the case where the decoded
+         $PS4 is null without seg faulting
+
+                                  10/22
+                                  -----
+builtins/shift.def
+       - make sure that there is actually an argument when reporting a shift
+         count that exceeds the number of positional paramters and
+         shift_verbose is enabled
+
+lib/readline/rltty.c
+       - change SET_SPECIAL to call a new function, set_special_char, since
+         it contains a block.  It's called infrequently, so the performance
+         impact of making it a function should be negligible, and it helps
+         debugging
+
+                                  10/29
+                                  -----
+bashline.c
+       - make sure the editor in VI_EDIT_COMMAND and EMACS_EDIT_COMMAND is
+         quoted; it might contain spaces (e.g., `emacs -nw')
+
+aclocal.m4
+       - cache ac_cv_rl_version in RL_LIB_READLINE_VERSION macro
+
+configure.in
+       - change logic that sets RL_INCLUDEDIR so that it doesn't try to set
+         a bogus include path if the argument to --with-installed-readline
+         is `yes' -- helps with cross-compiling
+
+lib/readline/histexpand.c
+       - fix history_tokenize_word so that it handles <( and >( better
+
+                                  10/30
+                                  -----
+redir.c
+       - fix write_here_string so it handles the case where `herestr' expands
+         to NULL without seg faulting
+
+                                  10/31
+                                  -----
+mailcheck.c
+       - reverse logic flip from bash-2.05 that handled systems that don't
+         change the atime when the mailbox is accessed; make sure the file
+         is bigger before we report new mail.  This is the case in the vast
+         majority of cases.  Reported by jim@jtan.com
+
+                                  11/5
+                                  ----
+parse.y
+       - change action for `for x; { list; }' and corresponding `select'
+         production to use \"$@\" instead of just $@, as it is with all the
+         other actions
+
+                                  11/9
+                                  ----
+parse.y
+       - new flag for parse_matched_pair: P_DQUOTE, indicating that the
+         pair of characters being matched is between double quotes
+       - parse_matched_pair now passes P_DQUOTE down to recursive calls:
+         if the open char to be matched is a `"' or the passed-in flags
+         include P_DQUOTE, set the local `rflags' variable to P_DQUOTE and
+         pass `rflags' down to recursive calls
+       - if `rflags' includes P_DQUOTE, don't try to ansiexpand $'...' or
+         locale expand $"..."; consistent with other quoting constructs
+
+                                  11/11
+                                  -----
+doc/{bash.1,bashref.texi}
+       - explicitly note that variables referenced in arithmetic expressions
+         without using `$' evaluate to 0 if they are null or unset
+       - note that a null variable value evaluates to 0 when used in an
+         arithmetic context, like when a variable with the `-i' attribute is
+         assigned a null value
+       - document the ${!prefix@} expansion as equivalent to ${!prefix*}
+
+                                  11/12
+                                  -----
+doc/{bash.1,bashref.texi}
+       - note that the value of an arithmetic expression is as in C
+       - change the wording to note that `arithmetic evaluation' (not
+         arithmetic expansion, which has a different meaning) is performed
+         on the value assigned to a variable whose integer attribute is set
+
+                                  11/13
+                                  -----
+execute_cmd.c
+       - fix execute_disk_command so it calls exit() after printing the error
+         message in a restricted shell context if the shell has already forked
+         (nofork != 0 && there are no pipes)
+
+                                  11/19
+                                  -----
+builtins/type.def
+       - don't report on aliases unless expand_aliases is set and the parser
+         is performing alias expansion; changed tests/type.tests and
+         tests/type.right accordingly
+
+                                  11/25
+                                  -----
+general.c
+       - fix for full pathnames including drive letters on cygwin from
+         Corinna (convert to posix-style paths, which the rest of the
+         code handles much better)
+
+lib/readline/text.c
+       - fixes to overwrite mode from jimmy@is-vn.bg:
+               o in _rl_overwrite_char, do the overwrite mode self-insert
+                 as one group, even when overwriting more than 1 char
+               o in _rl_overwrite_char, do the insert before the delete so
+                 that an undo positions the cursor on the character restored,
+                 not to the right of it
+               o in _rl_overwrite_rubout, don't do rl_insert_char(' ') unless
+                 rl_point < rl_end.  Since overwrite-mode self-insert acts as
+                 in insert-mode when at eol, make rubout behave like
+                 insert-mode rubout
+
+                                  11/30
+                                  -----
+lib/readline/misc.c
+       - call rl_replace_line with `1' as second parameter if we're going to
+         immediately overwrite the undo list
+
+lib/readline/search.c
+       - in make_history_line_current, use _rl_replace_text to make the line
+         replacement an undoable operation.  Affects all non-incremental
+         search functions.
+
+parse.y
+       - make behavior introduced on 11/9 dependent on extended_quote
+         variable, controllable by extquote shopt option.  Default setting is
+         on for backwards compatibility
+
+builtins/shopt.def
+       - new `extquote' option to control extended_quote variable
+
+                                  12/3
+                                  ----
+jobs.c
+       - change message printed when attempting to put a background job in
+         the background with `bg' to include the job id and make the
+         statement declarative
+
+                                  12/10
+                                  -----
+bashhist.h
+       - define explicit flag values for history_control
+
+variables.c
+       - change sv_history_control to use new flag values
+       - change sv_history_control to parse $HISTCONTROL as a colon-separated
+         list of values for the history_control variable
+
+bashhist.c
+       - change check_history_control to use new flag values and restructure
+         to remove case statement
+       - new function hc_erasedups(line); removes all entries matching LINE
+         from the history list
+       - call hc_erasedups() from check_add_history after we've determined
+         that we're saving the line
+
+doc/{bash.1,bashref.texi}
+       - documented new options available for $HISTCONTROL and that it can
+         be a colon-separated list of history control options
+
+                                  12/11
+                                  -----
+subst.c
+       - fix pat_subst() to not increment `e' (pointer to the end of the
+         matched portion of the string) until after we're sure we're going
+         around the loop again; fixes problem with empty replacements for
+         a pattern that doesn't match (bug reported by Don Coleman
+         <don@coleman.org>)
+
+                                  12/17
+                                  -----
+lib/readline/display.c
+       - fixes to multibyte redisplay from jir@yamato.ibm.com (Jiro SEKIBA):
+               o speed up calculation of first difference between old and new
+                 lines in the common case
+               o don't try to see if we're in the middle of a multbyte char
+                 in update_line (we'll see how this one works out)
+
+                                  12/18
+                                  -----
+doc/bashref.texi
+       - make it clear that the `command-list' function definition may be
+         terminated by an ampersand before the closing brace
+
+                                  12/28
+                                  -----
+redir.c
+       - set `expanding_redir' flag when expanding words in a redirection
+
+subst.c
+       - new function, exp_jump_to_top_level(), to do any word expansion
+         cleanup before a call to jump_to_top_level from within that file;
+         sets expanding_redir back to 0 before jump_to_top_level
+
+variables.c
+       - in find_variable(), don't call find_variable_internal with a second
+         parameter of 1 if expanding_redir is non-zero
+       - in find_variable_internal(), don't search the temporary env if
+         subshell_environment includes SUBSHELL_FORK (indicating a simple
+         command) and expanding_redir is non-zero
+
+parse.y
+       - increment line_number when we read a \<newline> pair
+
+array.c
+       - added array_unshift_element and array_shift_element (which just call
+         array_shift and array_rshift, respectively), for bash debugger
+         support
+
+                                1/4/2003
+                                --------
+doc/{bash.1,bashref.texi}
+       - note in the section describing the execution environment passed to
+         children that subshells inherit shell functions marked for export
+       - note in the section describing shell functions the possibility
+         that exported functions may result in two entries in the environment
+         with the same name
+
+parse.y
+       - when pushing an alias expansion onto the pushed_string list, append
+         a space to the expanded definition to make the parser's lookahead
+         work without using the `mustpop' hack in shell_getc
+
+                                   1/8
+                                   ---
+shell.c
+       - change calls to exit() with EX_USAGE as a parameter to use
+         EX_BADUSAGE instead, since EX_USAGE is defined as 258 and is
+         technically out of range
+
+                                  1/14
+                                  ----
+aclocal.m4
+       - check for the termcap functions in libc first:  if we don't have
+         to link in another library, let's not do it
+       - change the test for mbstate_t to use AC_TRY_COMPILE instead of
+         AC_TRY_RUN
+
+doc/{bash.1,bashref.texi}
+       - document that bash turns line editing off if environment variable
+         EMACS is set to `t' when it starts up
+
+doc/bash.1
+       - minor change to give the ftp url for the latest version of bash in
+         the bug reports section
+
+lib/readline/histexpand.c
+       - in get_history_event, cast a couple of `const char *' variables to
+         `char *' in function call parameter lists to avoid compiler warnings
+
+                                  1/21
+                                  ----
+builtins/cd.def
+       - change `cd -' so it prints the current working directory after a
+          successful chdir even when the shell is not interactive
+
+                                  1/31
+                                  ----
+lib/readline/doc/rltech.texinfo
+       - clarified exactly what is meant by the term `application-specific
+         completion function', made its use consistent, and documented
+         what variables are changed before such a function is called
+
+lib/readline/input.c
+       - new function, _rl_pushed_input_available(), returns non-zero if
+         there are characters in the input queue managed by rl_get_char
+         and _rl_unget_char
+
+lib/readline/rlprivate.h
+       - new extern declaration for _rl_pushed_input_available
+
+lib/readline/callback.c
+       - change rl_callback_read_char to check _rl_pushed_input_available
+         and loop if there's something there, so characters don't languish
+         until more keyboard input is read
+
+execute_cmd.c
+       - new variable, last_command_exit_signal, non-zero if
+         last_command_exit_value result from wait_for was result of a signal
+
+nojobs.c
+       - keep track of whether or not a given pid was killed by a signal with
+         a new flag in the pid_list array
+       - new function int find_termsig_by_pid(pid_t pid) to get the
+         terminating signal, if any, for a particular pid
+       - new function int get_termsig(WAIT status) returns the terminating
+         signal corresponding to status
+       - set last_command_exit_signal in wait_for and the various wait_for_xx
+         functions
+
+jobs.c
+       - new functions, process_exit_signal and job_exit_signal, return the
+         signal that killed a given process or job, if a signal caused its
+         death
+       - set last_command_exit_signal in wait_for by calling job_exit_signal
+         or process_exit_signal appropriately
+
+subst.c
+       - don't resend SIGINT to ourselves unless last_command_exit_signal is
+         SIGINT and last_command_exit_value == 128 + SIGINT.  This fixes the
+         $(exit 130) bug reported by Paul Jarc
+
+expr.c
+       - new function, expr_bind_variable, calls bind_int_variable and
+         then stupidly_hack_special_variables.  This fixes the
+         `let OPTIND=1' bug
+
+bashline.c
+       - change history_and_alias_expand_line and shell_expand_line to call
+         history_expand_line_internal so calls to pre_process_line are
+         localized
+       - change history_expand_line_internal and cleanup_expansion_error to
+         temporarily turn off hist_verify before calling pre_process_line
+         to avoid the effects described by teirllm@dms.auburn.edu
+
+parse.y
+       - don't unconditionally turn off PST_ALEXPNEXT in push_string.  This
+         fixes the multiple alias expansion bug reported by Paul Jarc.
+
+lib/readline/vi_mode.c
+       - change rl_vi_subst to push `l' instead of ` ' -- it should be
+         equivalent, but this has been reported to fix a problem in multibyte
+         locales
+
+lib/readline/readline.h
+       - new state flag value RL_STATE_TTYCSAVED, indicates that save_tty_chars
+         has been called.  Since it's only used and visible internally, it's
+         undocumented
+
+lib/readline/rltty.h
+       - changed all of the members of _rl_tty_chars struct to `unsigned char'
+
+lib/readline/rltty.c
+       - set the RL_STATE_TTYCSAVED after save_tty_chars is called
+       - new function, rl_tty_unset_default_bindings(), resets bindings for
+         everything rl_tty_set_default_bindings() messes with back to
+         rl_insert, so rl_tty_set_default_bindings can be called again with
+         possible changes
+       - new function that does the bulk of the work for
+         rltty_set_default_bindings:  _rl_bind_tty_special_chars()
+       - change prepare_terminal_settings so that it can track changes to the
+         terminal special chars made by stty(1):  unset the bindings with
+         rl_tty_unset_default_bindings before calling save_tty_chars, and
+         _rl_tty_set_default_bindings after, with the new values from
+         get_tty_settings().  This implements a long-standing request, most
+         recently made by Tim Waugh of Red Hat.
+
+lib/readline/readline.h
+       - extern declaration for rl_tty_unset_default_bindings()
+
+lib/readline/readline.c
+       - new function, reset_default_bindings, calls
+         rl_tty_unset_default_bindings() to reset the terminal special chars
+         back to rl_insert and then read the new ones
+
+lib/readline/doc/rltech.texinfo
+       - documented rl_tty_unset_default_bindings()
+
+                                   2/1
+                                   ---
+[prayers and condolences to the families of the space shuttle crew members]
+
+aclocal.m4
+       - add checks for mbrtowc and mbrlen in BASH_CHECK_MULTIBYTE
+       - new check, BASH_FUNC_CTYPE_NONASCII, checks whether or not the ctype
+         functions handle non-ascii characters correctly
+
+config.h.in
+       - add HAVE_MBRTOWC and HAVE_MBRLEN
+       - add NO_MULTIBYTE_SUPPORT for new configure argument
+       - add CTYPE_NON_ASCII
+
+config-bot.h, lib/readline/rlmbutil.h
+       - make sure that mbrtowc, mbrlen, and wcwidth are all present before
+         turning on HANDLE_MULTIBYTE
+       - turn off multibyte chars if NO_MULTIBYTE_SUPPORT is defined
+
+configure.in
+       - new argument --enable-multibyte (enabled by default), allows
+         multibyte support to be turned off even on systems that support it
+
+lib/readline/chardefs.h
+       - define NON_NEGATIVE as 1 if CTYPE_NON_ASCII is defined
+
+                                   2/3
+                                   ---
+config.h.in
+       - add HAVE_WCTOMB
+
+aclocal.m4
+       - check for wctomb in BASH_CHECK_MULTIBYTE
+
+                                   2/4
+                                   ---
+lib/readline/vi_mode.c
+       - in _rl_vi_change_mbchar_case, make sure the result from wctomb()
+         is NULL-terminated before trying to insert it with rl_insert_text()
+
+                                   2/5
+                                   ---
+lib/readline/display.c
+       - fix to update_line to avoid problems on systems with multibyte
+         characters when moving between history lines when the new line
+         has more glyphs but fewer bytes (twaugh@redhat.com)
+
+lib/readline/vi_mode.c
+       - use wcrtomb() instead of wctomb() in _rl_vi_change_mbchar_case
+
+pcomplete.c
+       - fix init_itemlist_from_varlist to handle the case where the
+         `varlist' is NULL
+
+doc/{bash.1,bashref.texi}
+       - clarified when a simple command may fail without the shell exiting
+         when -e is set
+
+                                  2/13
+                                  ----
+parse.y
+       - when bash is started with --nolineediting, ignore \[ and \] when
+         decoding the prompt string
+
+subst.c
+       - fix remove_quoted_nulls so that a string with a CTLESC appearing
+         after a CTLNUL (which was removed) does not leave characters in
+         the string inappropriately
+
+                                  2/14
+                                  ----
+builtins/common.h
+       - new flag value for parse_and_execute(): SEVAL_RESETLINE, which
+         allows the caller to specify whether or not the internal idea
+         of the line number should be reset to 1
+
+builtins/evalstring.c
+       - parse_and_execute() now tells push_string to reset the line
+         number only if the SEVAL_RESETLINE flag is set by the caller
+
+                                  2/15
+                                  ----
+builtins/evalfile.c
+       - pass SEVAL_RESETLINE from _evalfile() to parse_and_execute()
+
+subst.c
+       - if the shell is currently interactive, pass SEVAL_RESETLINE to
+         parse_and_execute() when doing command substitution
+
+jobs.c
+       - add SEVAL_RESETLINE to parse_and_execute while running SIGCHLD trap
+
+command.h
+       - add `line' members to case_com, for_com, select_com
+       - rearranged order of members in some of the command structs, so
+         `flags' and `line' are first
+       - added a `source_file' member to the function_def struct; keeps
+         track of where the function was defined
+
+doc/Makefile.in
+       - add some new suffix rules:  .dvi.ps
+
+doc/{bash.1,bashref.texi}
+       - added text to the description of the `trap' builtin tightening up
+         the language describing when the ERR trap will be run
+
+error.c
+       - if $BASH_SOURCE (internally-maintained) exists, use BASH_SOURCE[0]
+         in get_name_for_error if the shell is not interactive
+
+array.h
+       - new convenience defines: array_push and array_pop
+
+variables.c
+       - change get_funcname to return this_shell_function->name only if
+         arrays have not been compiled into the shell
+       - change init_funcname_var to initialize FUNCNAME as an array variable
+         if we have arrays
+       - new function: get_self(SHELL_VAR *self), a degenerate `dynamic_value'
+         function for dynamic variables
+       - new function: init_dynamic_array_var(), a generic dynamic array
+         variable initializer to handle the common case
+       - use init_dynamic_array_var() instead of explicit init_dirstack_var()
+       - use init_dynamic_array_var() instead of explicit init_groups_var()
+       - new dynamic array variables:  BASH_ARGC, BASH_ARGV, BASH_SOURCE,
+         BASH_LINENO, initialized with init_dynamic_array_var
+
+shell.c
+       - initialize BASH_LINENO, BASH_SOURCE, FUNCNAME in open_shell_script
+
+{execute_cmd,trap}.c
+       - take out trap_line_number, since parse_and_execute doesn't reset the
+         line number any more when running the trap commands
+
+make_cmd.c
+       - augment make_function_def to get source file name and call
+         bind_function_def to save the entire FUNCTION_DEF
+
+variables.c
+       - new hash table: shell_function_defs, keeps table of shell function
+         definitions including source file and line number info corresponding
+         to shell_functions table
+       - new functions: find_function_def and bind_function_def to manage
+         the shell_function_defs hash table
+       - new function: unbind_function_def to remove a function definition
+         from the shell_function_defs table (right now uncalled)
+
+variables.h
+       - extern declaration for bind_function_def, find_function_def
+       - new extern declaration for unbind_function_def
+
+execute_cmd.c
+       - in function prologue and epilogue, push and pop FUNCNAME,
+         BASH_SOURCE, and BASH_LINENO information
+
+dispose_cmd.c
+       - broke the code that disposes a FUNCTION_DEF out into two new
+         functions: dispose_function_def and dispose_function_def_contents
+
+dispose_cmd.h
+       - new extern declarations for dispose_function_def_contents and 
+         dispose_function_def
+
+copy_cmd.c
+       - move body of copy_function_def (other than allocating a new
+         FUNCTION_DEF) to copy_function_def_contents
+       - make sure to copy the new source_file member of a function_def in
+         copy_function_def_contents
+       - copy_function_def is no longer static, copy_function_def_contents
+         is not either
+
+command.h
+       - new extern declaration for copy_function_def_contents and
+         copy_function_def
+
+parse.y
+       - keep a stack of line numbers where case, select, and for commands
+         start, with a maximum nesting level of 128; increment when reading
+         word after `for', `select' or `case' in read_token_word; decrement
+         in grammar actions after parsing a complete for, arith_for, select,
+         or case command
+       - create for, case, arith_for, and select commands with an extra
+         line number (word_lineno[word_top]) argument
+
+make_cmd.c
+       - make_for_or_select, make_for_command, make_case_command, and
+         make_select_command all take an extra `line_number' argument
+
+make_cmd.h
+       - corresponding changes to extern declarations for those functions
+
+                                  2/16
+                                  ----
+{execute_cmd,shell,variables}.c
+       - follow each call to remember_args with a call to push_args or
+         pop_args to manage the BASH_ARGV and BASH_ARGC arrays.  Only set
+         when the shell is started to run shell script or runs a shell
+         function.  Doesn't handle `set' or `shift' yet, nor `source'.
+
+execute_cmd.c
+       - keep track of the level of subshells with a new variable, manipulated
+         in execute_in_subshell
+       - set currently_executing_command in execute_command_internal(),
+         even if we're running a trap
+       - better line number management when executing simple commands,
+         conditional commands, for commands in execute_command_internal()
+         and the various functions that implement the commands
+         (execute_cond_command, execute_for_command, execute_etc.)
+
+variables.c
+       - new dynamic variable BASH_SUBSHELL, with new get_subshell and
+         assign_subshell functions to manipulate it
+       - new functions push_args (WORD_LIST *list) and pop_args (void) to
+         manage the BASH_ARGC and BASH_ARGV dynamic array variables
+
+variables.h
+       - new extern declarations for push_args and pop_args
+
+builtins/evalfile.c
+       - in _evalfile, do appropriate things to the FUNCNAME, BASH_ARGV,
+         BASH_ARGC, BASH_SOURCE, and BASH_LINENO variables
+
+support/mksignames.c
+       - add another fake signal for `trap'; make NSIG+2 == `RETURN'
+
+trap.c
+       - _run_trap_internal now returns an int:  the exit value of the command
+         run as the result of the trap
+       - run_debug_trap now returns an int: the exit value of the command
+         run as the result of the trap
+       - RETURN is a new special trap
+       - new function: set_return_trap(char *command) interface for the rest
+         of the shell, like set_{debug,error}_trap
+       - new function: run_return_trap()
+       - command substitution and other child processes don't inherit the
+         return trap
+
+trap.h
+       - new extern declaration for set_return_trap() and run_return_trap
+       - new defines for RETURN_TRAP; increment BASH_NSIG
+       - change extern declaration for run_debug_trap() since it now returns
+         an int
+
+shell.c
+       - new invocation long option:  --debugger, turns on debugging and
+         sets internal `debugging_mode' variable
+
+execute_cmd.c
+       - new code to save return trap when executing a shell function, so
+         shell functions don't inherit it
+       - run debug trap before binding the variable and running the action
+         list in a `for' command
+       - run debug trap before binding the selection variable and running
+         the query in a `select' command
+       - run debug trap before running matcher in a `case' command
+
+builtins/set.def
+       - new `set -o functrace' (set -T), causes DEBUG trap to be inherited
+         by shell functions
+       - new `set -o errtrace' (set -E), causes ERR trap to be inherited
+         by shell functions
+
+flags.c
+       - new flags -E and -T, control error_trace_mode and
+         function_trace_mode respectively
+
+flags.h
+       - new extern declarations for error_trace_mode and function_trace_mode
+
+                                  2/17
+                                  ----
+doc/bashref.texi
+       - changed the `dircategory' as per Karl Berry's suggestion
+
+doc/texinfo.tex
+       - update to version of 2003/02/04 from texinfo.org
+
+support/texi2dvi
+       - update to version 1.14 from texinfo-4.5 distribution
+
+                                  2/20
+                                  ----
+support/config.{guess,sub}
+       - update to versions of 2002/11/30
+
+lib/readline/doc/manvers.texinfo
+       - renamed to version.texi to match other GNU software
+       - UPDATE-MONTH variable is now `UPDATED-MONTH'
+
+lib/readline/doc/{hist,rlman,rluserman}.texinfo
+       - include version.texi
+
+doc/version.texi
+       - new file, with standard stuff matching other GNU distributions
+
+{doc,lib/readline/doc}/Makefile.in
+       - include right stuff for `version.texi'
+
+lib/readline/doc/{rluserman,rlman,hist}.texinfo
+       - use @copying and @insertcopying and @ifnottex instead of @ifinfo
+       - add FDL as an appendix entitled `Copying This Manual'
+
+lib/readline/doc/{rltech,rluser,hstech,hsuser}.texi
+       - changed the suffix from `texinfo' to `texi'
+
+lib/readline/doc/{rlman,rluserman}.texinfo, doc/bashref.texi
+       - include rltech.texi,rluser.texi,hstech.texi, and hsuser.texi
+
+lib/readline/doc/Makefile.in,doc/Makefile.in
+       - made appropriate changes for {{rl,hs}tech,{rl,hs}user}.texi
+
+lib/readline/doc/{rlman,rluserman}.texinfo
+       - changed the suffix from `texinfo' to `texi'
+
+lib/readline/doc/hist.texinfo
+       - renamed to history.texi
+
+                                  2/25
+                                  ----
+pathnames.h.in
+       - moved pathnames.h here so value of DEBUGGER_START_FILE can be
+         substituted by configure
+
+aclocal.m4
+       - added AM_PATH_LISPDIR for debugger
+
+configure.in
+       - added some variables: `bashvers', `relstatus' to use info in more
+         than one place
+       - call AM_PATH_LISPDIR
+       - new option:  --enable-debugger, sets DEBUGGER cpp option
+       - new option with AC_ARG_VAR:  DEBUGGER_START_FILE
+       - make `pathnames.h' a file generated by configure
+
+Makefile.in
+       - add rule to create pathnames.h
+
+builtins/declare.def
+       - added extra line number and source file name to `declare -F' output
+         if `--debugger' is used at startup
+
+builtins/evalfile.c
+       - call run_return_trap from source_file before returning the result
+         from _evalfile()
+
+execute_cmd.c
+       - call run_return_trap in execute_function before restoring the old
+         context
+
+builtins/source.def
+       - arrange to save and restore DEBUG traps when sourcing files if
+         function_trace_mode (set -o functrace) is not set
+
+print_cmd.c
+       - broke print_for_command, print_select_command, print_case_command
+         into two functions each:  one to print the `header' and one for
+         the body
+       - print_cond_command is no longer static
+       - print_arith_command now takes a WORD_LIST *, since it doesn't
+         actually do anything with the ARITH_COM it's passed except print
+         the enclosed WORD_LIST
+       - print_arith_command is no longer static
+
+externs.h
+       - extern declarations for print_{for,select,case}_command_head,
+         print_cond_command, print_arith_command
+
+{.,builtins,lib/sh}/Makefile.in
+       - corrected dependencies on pathnames.h, since it's now created in
+         the build directory
+
+                                   3/5
+                                   ---
+lib/glob/glob.c
+       - handle alloca() failing (it's supposed to return NULL)
+       - use malloc() (with its attendent bookkeeping) instead of alloca()
+         in glob_filename()
+
+subst.c
+       - check whether shell_glob_filename returns NULL in
+         glob_expand_word_list
+       - change parameter_brace_expand_rhs to handle cases like
+         ${a[2]:=value} by properly creating the array element instead of a
+         variable named `a[2]' (reported by <opengeometry@yahoo.ca>)
+
+variables.c
+       - change bind_int_variable to use valid_array_reference instead
+         of looking for `[' 
+
+lib/readline/vi_mode.c
+       - check for `a' in _rl_vi_done_inserting so the text inserted by an
+         `a' command can be reinserted with a `.'
+
+lib/readline/readline.c
+       - when entering vi insertion mode in readline_internal_setup(), make
+         sure that _rl_vi_last_key_before_insert is set to `i' so that undo
+         groups and redo work better (reported by <opengeometry@yahoo.ca>)
+
+lib/glob/sm_loop.c
+       - handle ?(...) in a pattern immediately following a `*', instead of
+         ignoring the `(' and treating the `?' as a single-char match, as
+         long as FNM_EXTFLAG is set (reported by <anderson110@poptop.llnl.gov>)
+
+aclocal.m4
+       - new test for presence of struct timezone, BASH_STRUCT_TIMEZONE
+
+config.h.in
+       - add HAVE_STRUCT_TIMEZONE
+
+configure.in
+       - call BASH_STRUCT_TIMEZONE
+
+execute_cmd.c
+       - don't try to use `struct timezone' in calls to gettimeofday unless
+         HAVE_STRUCT_TIMEZONE is defined; use (void *)NULL otherwise
+
+                                  3/20
+                                  ----
+execute_cmd.c
+       - new variable, the_printed_command_except_trap, saves the command
+         being executed before a trap is executed, for the debugger
+
+trap.c
+       - if in debugging mode, let command substitutions and other child
+         processes inherit the DEBUG and ERR traps if the `functrace'
+         (which is really a bad name, given this semantic) or `errtrace'
+         options, respectively, have been set
+
+shell.c
+       - local_pending_command renamed to command_execution_string; no longer
+         static
+
+variables.c
+       - new dynamic variable, BASH_COMMAND, set to the command string
+         currently executing, or the one that caused a trap to execute
+         (mapped to the_printed_command_except_trap)
+       - new variable, BASH_EXECUTION_STRING, set to the argument to the
+         -c invocation option, if the shell was started that way
+
+                                  3/22
+                                  ----
+execute_cmd.c
+       - changed execute_for_command, eval_arith_for_expr,
+         execute_select_command, execute_arith_command, execute_cond_command,
+         execute_simple_command to implement new DEBUG trap semantics
+         for the debugger: if the DEBUG trap commands return a non-zero
+         status and debugging_mode is non-zero, we skip the command to be
+         executed
+
+trap.c
+       - change run_debug_trap for the debugger: if we're in the debugger
+         and the DEBUG trap returns 2 while we're in a function or sourced
+         script, we force a `return'
+
+shell.c
+       - new function, start_debugger(), that sources the debugger start file
+         and turns the debugger on
+
+builtins/shopt.def
+       - new settable option, `extdebug', turns on debugging_mode, as if
+         --debugger had been supplied at invocation (but does not source
+         debugger startup file)
+
+trap.c
+       - make sure that run_exit_trap arranges for `returns' to come back
+         there, too, so a `return' executed by an `exit' invoked within a
+         shell function behaves correctly
+
+support/shobj-conf
+       - change darwin/MacOS X stanza based on advice from mac os x developers
+
+lib/sh/mailstat.c
+       - set the atime member of the synthesized stat struct to 0 if `cur/'
+         is empty, rather than leaving it undefined
+
+                                  3/24
+                                  ----
+builtins/caller.def
+       - new builtin to provide a call stack for the debugger
+
+builtins/evalfile.c
+       - added a second `flags' argument to source_file()
+       - new flag value for flags argument to _evalfile():  FEVAL_NOPUSHARGS.
+         If included in flags arg, it means to not manipulate the BASH_ARGV
+         and BASH_ARGC arrays
+
+builtins/common.h
+       - change prototype for source_file()
+
+builtins/source.def
+       - add flag value to call to source_file():  set to 1 if we replaced
+         the positional parameters
+       - add call to push_args if additional arguments supplied to the
+         source builtin
+       - add call to pop_args in maybe_pop_dollar_vars
+
+execute_cmd.c
+       - run the debug trap in execute_function so the debugger can stop
+         before the first command in a function body is executed
+       - modify subshell_level before executing a builtin or function in a
+         subshell
+       - print `for', `select', `case' command heads when set -x is enabled
+
+print_cmd.c
+       - `xtrace_print_word_list' now takes an additional flags argument,
+         which, if non-zero, says to print indirection_level_string()
+       - new functions to print for, select, and case command heads when
+         set -x is enabled
+       - add spaces after `((' and before `))' in xtrace_print_arith_command
+
+externs.h
+       - changed extern declaration for xtrace_print_word_list
+       - new declarations for xtrace_print_{for,case,select}_command_head()
+
+subst.c
+       - modify subshell_level when executing a command substitution
+
+                                  3/25
+                                  ----
+execute_cmd.c
+       - use `line_number' in executing_line_number instead of looking into
+         the current command if it's a simple command; rearrange code to
+         make this simpler to compile in and out
+       - need to save and restore value of currently_executing_command around
+         calls to debug trap and return trap in execute_function
+
+make_cmd.c
+       - make sure make_arith_for_command() disposes the WORD_LIST * it is
+         passed, since nothing else does and it's not used directly
+
+                                  3/28
+                                  ----
+Makefile.in    
+       - fixed dependencies for `error.o' on shell.h and version.h -- makes
+         parallel makes (gmake -j 4) work correctly
+
+doc/{bash.1,bashref.texi}
+       - documented all new features added to support the debugger
+
+                                   4/1
+                                   ---
+lib/sh/shquote.c
+       - make sure CTLESC and CTLNUL characters are escaped with CTLESC
+         by sh_double_quote, sh_backslash_quote and
+         sh_backslash_quote_for_double_quotes
+         Fixes vulnerability reported by svdb@stack.nl
+
+shell.h
+       - new `pipestatus' member of sh_parser_state_t, to save and restore
+         $PIPESTATUS
+
+parse.y
+       - changes to save_parser_state and restore_parser_state to save and
+         restore $PIPESTATUS
+
+builtins/read.def
+       - add a call to word_list_remove_quoted_nulls before assigning the
+         word list read from standard input to an array variable.  Fixes
+         bug reported by holzhey@ppprs1.phy.tu-dresden.de
+
+                                   4/3
+                                   ---
+execute_cmd.c
+       - in execute_null_command, if redirections are supplied, make sure
+         things like 3</etc/passwd are undone immediately, since they're
+         being done in the current shell
+       - functions now inherit the RETURN trap only if function tracing is
+         on for that function or globally
+
+lib/readline/misc.c
+       - in rl_replace_from_history, don't force rl_replace_line to clear
+         the undo_list, since it might point directly at an undo list
+         from a history entry (to which we have no handle)
+
+                                   4/4
+                                   ---
+trap.c
+       - initialize RETURN_TRAP stuff appropriately in initialize_traps()
+       - let command substitutions inherit the RETURN trap if we're in
+         the debugger and function tracing has been enabled
+
+redir.c
+       - do_redirections and do_redirection_internal now take a single
+         flags argument instead of multiple boolean flags
+
+redir.h
+       - new #defines for flags argument to do_redirection{s,_internal}
+
+execute_cmd.c
+       - change all calls to do_redirection to use new flag values
+
+parse.y
+       - new function, free_pushed_string_input(), an external interface to
+         clear the pushed_string list (alias expansion)
+       - new define SHOULD_PROMPT to say when it's OK to call prompt_again
+         (if the shell is currently interactive and is reading input from
+         the terminal or readline)
+       - make sure shell_getc and read_secondary_line only call prompt_again
+         if SHOULD_PROMPT evaluates to true
+       - prompt_again shouldn't do anything if the shell is currently in the
+         middle of expanding a multiline alias, since we won't be printing a
+         prompt anyway
+
+externs.h
+       - new extern declaration for free_pushed_string_input()
+
+execute_cmd.c
+       - command_substitute and process_substitute now call
+         free_pushed_string_input because they shouldn't deal with any
+         partial alias expansion the parent shell may have started
+
+                                   4/5
+                                   ---
+braces.c
+       - added {x..y} brace expansion, shorthand for {x,x+1,x+2,...,y}:
+         x, y can be integers or single characters; the sequence can be
+         ascending or descending; increment is always 1.  Beware that
+         something like {a..A} can lead to non-letters.
+
+                                   4/7
+                                   ---
+subst.c
+       - change extract_delimited_string and extract_dollar_brace_string to
+         return NULL on an expansion error when no_longjmp_on_fatal_error
+         is set, so the calling functions don't assume that the expansion
+         was successful (primarily the prompt expansion and completion code)
+
+doc/{bash.1,bashref.texi}
+       - documented new sequence generation feature of brace expansion
+
+                                   4/8
+                                   ---
+lib/sh/strstr.c
+       - replacement for strstr(3), in case the C library doesn't provide it
+
+configure.in
+       - check for strstr, add to LIBOBJS if not found
+
+array.c
+       - array_walk now takes a third void * argument; it's passed to `func'
+         as its second argument
+
+array.h
+       - change sh_ae_map_func_t prototype to add void * second argument
+
+                                  4/10
+                                  ----
+array.c
+       - new function: array_keys_to_word_list, returns a list of indices for
+         a given array
+
+array.h
+       - new extern declaration for array_keys_to_word_list
+
+arrayfunc.c
+3      - new function: char *array_keys(), returns a string with keys for a
+         given array, with the appropriate quoting
+
+arrayfunc.h
+       - new extern declaration for array_keys
+
+subst.c
+       - code to implement ksh93-like ${!array[@]} expansion (array[*] works,
+         too), which expands to all the keys (indices) of ARRAY
+
+doc/{bash.1,bashref.texi}
+       - documented new ${!array[@]} expansion
+
+                                  4/19
+                                  ----
+builtins/setattr.def
+       - remove any mention of the `-n' option from readonly builtin's short
+         and long documentation
+
+pcomplib.c
+       - fix progcomp_insert to increase the refcount on the `cs' argument
+         before calling hash_insert -- fixes the problem of multiple calls
+         to progcomp_insert with the same compspec
+
+doc/bash.1
+       - add mention of characters that inhibit history expansion when found
+         immediately following an unquoted `!'
+
+bashline.c
+       - convert the code conditionally compiled in by the NO_FORCE_FIGNORE
+         #define to something runtime-tunable with the `force_fignore'
+         variable (opposite sense).  force_fignore is 1 by default
+
+builtins/shopt.def
+       - new tunable `shopt' option: `force_fignore', controls force_fignore
+         variable and behavior of FIGNORE handling
+
+lib/readline/complete.c
+       - new variable, _rl_complete_show_unmodified, causes completer to list
+         possible completions if more than one completion and partial
+         completion cannot be done
+       - new value for what_to_do argument to rl_complete_internal, `@',
+         indicating that we want the show-unmodified behavior
+       - change rl_completion_type to return `@' when appropriate
+
+lib/readline/bind.c
+       - new bindable variable, show-all-if-unmodified, which controls value
+         of _rl_complete_show_unmodified
+
+lib/readline/rlprivate.h
+       - extern declaration for _rl_complete_show_unmodified
+
+lib/readline/doc/rluser.texi
+       - documented show-all-if-unmodified
+
+lib/readline/doc/rltech.texi
+       - documented new `@' value for second argument to rl_complete_internal
+       - documented new return value from rl_completion_type
+
+                                  4/22
+                                  ----
+lib/readline/signals.c
+       - in rl_set_signal, set sa_flags to SA_RESTART for SIGWINCH, if we
+         have POSIX signals -- this is what most applications expect, and
+         they're not prepared to deal with EINTR
+
+                                  4/25
+                                  ----
+bashline.c
+       - take out attempts to suppress readline filename completion when
+         attempting command completion and there is a directory with the
+         same name in the current directory.  #if 0'd for now; maybe make
+         it conditional later
+
+error.c
+       - new variable, gnu_error_format, causes non-interactive errors with
+         line numbers to be printed in the `gnu style' (filename:lineno:)
+         instead of the `shell style' (filename: line lineno:) by
+         error_prolog and parser_error
+
+version.h,support/mkversion
+       - don't put extern function declarations into created version.h any
+         more
+
+version.c,externs.h
+       - add extern declarations for show_shell_version() and
+         shell_version_string(), since they're no longer in version.h
+         (this backs out change from 9/10/2001)
+
+shell.h
+       - don't include version.h
+
+Makefile.in
+       - remove unneeded dependencies on version.h -- only version.o
+         needs it now
+
+builtins/shopt.def
+       - new option `gnu_errfmt', changes error messages with line numbers
+         to use the `standard' gnu format
+
+pcomplete.h
+       - new COPT_BASHDEFAULT and COPT_PLUSDIRS defines
+
+bashline.c
+       - if the COPT_BASHDEFAULT flag is set, try the rest of the bash
+         default completions if a compspec with that flag set generates
+         no matches
+       - broke bash completions out into separate function:
+         bash_default_completion(const char *text, int start, int end,
+         int qc, int in_command_position); change attempt_shell_completion
+         to call it
+
+bashline.h
+       - new extern declaration for bash_default_completion
+
+builtins/complete.def
+       - added code to compgen to call bash_default_completion if a compspec
+         with the COPT_BASHDEFAULT flag set generates no matches from the
+         programmable completion options
+
+doc/{bash.1,bashref.texi}
+       - document new `gnu_errfmt' shopt option
+
+doc/bash.1,lib/readline/doc/rluser.texi
+       - document new `-o bashdefault' option to complete and compgen
+
+                                  4/26
+                                  ----
+pcomplete.c
+       - if a compspec has the COPT_PLUSDIRS flag set, generate any
+         directory name completions and add them to the set of possible
+         completions as the last thing in gen_compspec_completions
+
+builtins/complete.def
+       - add new `-o plusdirs' option to complete and compgen; means to
+         do directory name completion in addition to other actions
+         specified by the compspec, and do it last
+
+                                  5/12
+                                  ----
+copy_cmd.c
+       - fix copy_{for,select,case}_command to copy the line member
+
+                                  5/13
+                                  ----
+lib/readline/rlmbutil.h,include/shmbutil.h
+       - new #define MB_INVALIDCH, evaluates to true if return value from
+         mbrtowc or mbrlen denotes an invalid multibyte character
+       - new #define MB_NULLWCH, evaluates to true if return value from
+         mbrtowc or mbrlen denotes a null multibyte character
+
+lib/readline/complete.c
+       - new function, fnwidth(), returns printed length of a filename,
+         correctly handling multibyte characters
+       - new function, fnprint(), displays a filename on rl_outstream
+         correctly handling multibyte characters
+       - use fnwidth() instead of simple strlen() for length calculations
+         in display_matches
+
+lib/readline/{display,mbutil}.c
+       - use MB_INVALIDCH and MB_NULLWCH as appropriate
+
+                                  5/21
+                                  ----
+configure.in
+       - turn off the builtin malloc on GNU/FreeBSD systems (GNU userland,
+         FreeBSD kernel)
+
+                                  5/24
+                                  ----
+pcomplete.c
+       - in programmable_completions, copy the compspec before using it to
+         generate the possible completions, allowing the completion for the
+         command to be changed by the compspec itself without causing a
+         core dump because the shell frees freed memory (reported by
+         marcus.harnish@gmx.net)
+
+parse.y
+       - in shell_getc, don't call notify_and_cleanup in an interactive shell
+         unless the shell is currently reading from the terminal and would
+         print a prompt.  This fixes programmable completions printing job
+         notifications (reported by r.d.green@lancaster.ac.uk)
+       - use SHOULD_PROMPT macro consistently
+
+shell.c
+       - use get_string_value instead of getenv when looking up $TERM and
+         $EMACS to see whether the shell is running under emacs
+       - check for `TERM=dumb' as well as `EMACS=t' to decide whether or
+         not to turn off command-line editing in emacs shell windows
+         (reported by jik@kamens.brookline.ma.us)
+
+                                  5/28
+                                  ----
+expr.c
+       - save and restore the `evalbuf' context, since evalexp can be
+         called recursively (e.g. a[b[c]])
+
+                                  5/29
+                                  ----
+builtins/common.c
+       - in display_signal_list, when displaying for `kill -l' in posix mode,
+         display signal names without the `SIG' prefix
+
+doc/bashref.texi
+       - documented changed to posix mode behavior of kill -l
+
+builtins/kill.def
+       - changed the error message printed when argument is not a pid or
+         job id
+
+doc/{bash.1,bashref.texi}
+       - fixed a slight inconsistency in the different sections describing
+         the `promptvars' option
+
+doc/Makefile.in
+       - new rule to create `bash.info' from `bashref.info' by running sed
+         to change the internal references.  This makes the installed
+         bash.info work right
+       - make the install target install bash.info after it's been modified
+         from bashref.info
+
+                                  5/30
+                                  ----
+builtins/cd.def
+       - after testing with ferror(), call clearerr if ferror() evaluates
+         to true
+
+execute_cmd.c
+       - call clearerr(stdout) after executing a builtin or function,
+         before restoring any redirections
+
+bashhist.c
+       - initialize history_comment_char in bash_history_initialize
+
+builtins/alias.def
+       - if posix mode is enabled, and the `-p' option is not supplied, don't
+         list aliases with a preceding `alias ', as POSIX.2 specifies
+
+doc/bashref.texi
+       - note new posix mode behavior of displaying alias names and values
+       - note new posix mode behavior of trap builtin not checking first
+         argument to be a possible signal specification
+       - note new posix mode behavior of `kill' not accepting signal names
+         with a leading `SIG'
+
+include/stdc.h,lib/readline/rlstdc.h
+       - don't check the __STRICT_ANSI__ define when deciding whether or not
+         to #undef __attribute__
+
+trap.[ch]
+       - decode_signal and signal_object_p take an additional flags arg to
+         satisfy POSIX.2 signal name translation requirements
+       - change decode_signal to honor the new DSIG_NOCASE (match case-
+         insensitively) and DSIG_SIGPREFIX (allow signal specifications
+         with the `SIG' prefix) flags
+
+builtins/{common.c,{trap,kill}.def}
+       - change calls to decode_signal to add the new argument with
+         appropriate values.  For kill, POSIX.2 says case-insensitive without
+         the `SIG' prefix.  For trap, POSIX.2 says applications may decode
+         case-insensitively and with the SIG prefix
+
+builtins/trap.def
+       - when in posix mode, don't check for the first argument being a
+         possible signal spec and revert the signal handling to the
+         original disposition
+
+                                   6/1
+                                   ---
+shell.h
+       - new MATCH_STARSUB define, to tell the callers of the various pattern
+         substitution and removal functions that call string_list to treat
+         "$*" and "${array[*]}" appropriately
+subst.c
+       - if get_var_and_type encounters an array variable subscripted by `*'
+         or `$*', return VT_STARSUB or'd into the variable type value
+       - callers of get_var_and_type check for VT_STARSUB in the returned type
+         this will fix problems with ${*...} not being separated with the
+         first character of $IFS when double-quoted
+       - in parameter_brace_patsub, set MATCH_STARSUB if VT_STARSUB is set by
+         get_var_and_type
+       - change pos_params_pat_subst to call string_list_dollar_star if
+         the match flags include MATCH_STARSUB
+       - change parameter_brace_substring to call array_subrange with an
+         additional argument indicating the character indexing the array
+
+array.c
+       - change array_patsub to join the modified array elements with the
+         first character of $IFS if MATCH_STARSUB is set in the match flags
+         passed in
+       - array_subrange now takes an additional argument indicating the
+         index type -- used to separate with first char of $IFS if the
+         index char is `*' and the expression is double-quoted
+
+array.h
+       - change prototype declaration of array_subrange to add additional arg
+
+                                   6/2
+                                   ---
+doc/FAQ
+       - merged in some updates about POSIX from Andrew Josey
+
+                                   6/3
+                                   ---
+bashjmp.h
+       - new value for jump_to_top_level:  ERREXIT
+
+{eval,shell,subst,trap}.c,builtins/evalstring.c
+       - ERREXIT is (for now) equivalent to EXITPROG as a value from
+         jump_to_top_level
+
+                                   6/9
+                                   ---
+doc/bash.1,lib/readline/doc/readline.3
+       - documented new `show-all-if-unmodified' readline variable
+
+                                  6/14
+                                  ----
+lib/readline/history.c
+       - new function, histdata_t free_history_entry (HIST_ENTRY *h),
+         frees H and its line, returning the application-specific data
+       - use free_history_entry where appropriate
+
+lib/readline/history.h
+       - extern declaration for free_history_entry()
+
+lib/readline/doc/{history.3,hstech.texi}
+       - document free_history_entry
+
+                                  6/16
+                                  ----
+lib/readline/readline.[ch]
+       - changed varions defines for readline/history library versions to 5.0
+
+subst.c
+       - pass information about whether or not an indirectly-expanded variable
+         contains ${array[@]}, ${array[*]}, $@, or $*
+       - new function, chk_atstar, performs checks for ${array[@]},
+         ${array[*]}, $@, or $* for the parameter_brace_expand* functions and
+         sets flags (passed as args) to the results
+       - call chk_atstar from parameter_brace_expand_indir and
+         parameter_brace_expand
+
+                                  6/28
+                                  ----
+doc/{bash.1,bashref.texi}
+       - clarified that (...) commands are executed in a subshell environment
+
+                                  6/30
+                                  ----
+lib/readline/bind.c
+       - fix a problem with parsing nested if statements in inputrc files
+         (fix from dimakar@yahoo.com)
+
+                                   7/3
+                                   ---
+{jobs,nojobs}.c
+       - fix places that use the return value from strsignal() to check
+         for NULL return values using a new function, j_strsignal()
+
+builtins/kill.def
+       - removed JOB_CONTROL restriction; kill is now available as a builtin
+         when the shell is built without job control
+
+                                  7/10
+                                  ----
+jobs.c
+       - some systems have WIFCONTINUED but not WCONTINUED; separate the
+         #defines
+
+                                  7/14
+                                  ----
+lib/readline/history.h
+       - new `timestamp' member of a HIST_ENTRY
+       - extern declarations for add_history_time and history_get_time
+
+lib/readline/doc/{history.3,hstech.texi}
+       - document add_history_time and history_get_time
+
+lib/readline/history.c
+       - implementations of history_get_time and add_history_time
+       - change add_history to initialize the timestamp information
+       - change free_history_entry to free the timestamp
+       - change replace_history_entry to duplicate the timestamp
+       - change history_total_bytes to add the memory taken by the time
+         stamps
+
+bashhist.c,builtins/history.def
+       - use free_history_entry where appropriate
+
+lib/readline/histfile.c
+       - changes to read_history_range to deal with new history file format
+         including timestamps
+       - changes to history_do_write to write out the timestamp information
+       - changes to history_truncate_file to understand the timestamp
+         information
+
+                                  7/22
+                                  ----
+doc/{bash.1,bashref.texi}
+       - fixed function declaration documentation to specify that any compound
+         command may be used as the function body, not just a group command
+
+                                  7/23
+                                  ----
+lib/readline/histfile.c
+       - don't allocate space for null timestamps in history_do_write, and
+         don't write out null timestamp entries
+
+parse.y
+       - fix CHECK_FOR_RESERVED_WORD to call time_command_acceptable() and
+         return TIME if the token is "time" and `time' is legal in that
+         context
+
+                                  7/29
+                                  ----
+lib/sh/fmtulong.c
+       - include <inttypes.h> for possible definitions of intmax_t, uintmax_t
+         (reported by ro@techfak.uni-bielefeld.de)
+
+                                  7/30
+                                  ----
+parse.y
+       - remove checking for `time' reserved word from special_case_tokens();
+         use regular mechanism in CHECK_FOR_RESERVED_WORD.  This allows `time'
+         to be aliased.  (Reported by Glenn Morris
+         <gmorris+gmane@ast.cam.ac.uk>)
+
+                                  7/31
+                                  ----
+lib/readline/history.h
+       - extern declaration for history_write_timestamps
+
+lib/readline/histfile.c
+       - don't write timestamps to the history file in history_do_write
+         unless history_write_timestamps is set to non-zero by the application
+         (set to 0 by default)
+
+lib/readline/doc/{hstech.texi,history.3}
+       - document history_write_timestamps
+
+variables.[ch]
+       - new special variable function, HISTTIMEFORMAT; special function
+         sets history_write_timestamps to 1 if HISTTIMEFORMAT is set
+
+                                   8/4
+                                   ---
+builtins/history.def
+       - added support for printing time stamps based on the value of the
+         HISTTIMEFORMAT variable when displaying history entries
+
+doc/{bash.1,bashref.texi}
+       - added description of new HISTTIMEFORMAT variable
+
+                                   8/5
+                                   ---
+config-top.h
+       - remove /usr/ucb from any default paths
+
+mailcheck.c
+       - make_default_mailpath now returns NULL if DEFAULT_MAIL_DIRECTORY
+         is not defined
+       - remember_mail_dates now returns if make_default_mailpath returns
+         NULL
+
+config-bot.h
+       - reorganized the sections; provide an explicit placeholder for
+         builders to #undef any feature defines they don't want that
+         configure creates for them, like the default mail path
+
+                                   8/9
+                                   ---
+config.h.in
+       - add HAVE_REGEX_H, HAVE_REGCOMP, HAVE_REGEXEC for detection of POSIX.2
+         regular expression functions
+       - add COND_REGEXP define to enable and disable the =~ operator for
+         matching extended regular expressions in [[...]] commands
+
+configure.in
+       - new option, --enable-cond-regexp, enables =~ and code to perform
+         regular expression matching in [[...]]
+
+config-bot.h
+       - undef COND_REGEXP if the OS doesn't provide posix regexp support
+
+doc/bashref.texi
+       - documnent new --enable-cond-regexp option to configure
+
+                                  8/18
+                                  ----
+support/shobj-conf
+       - support for shared objects on FreeBSD-gnu (from Robert Millan)
+
+                                  8/25
+                                  ----
+lib/sh/shmatch.c
+       - new file, shell interface to posix extended regular expression
+         matching
+
+externs.h
+       - new extern declarations for functions in shmatch.c
+
+execute_cmd.c
+       - incorporate code into execute_cond_node that does extended regular
+         expression matching for the =~ operator
+
+parse.y
+       - add `=~' to the list of binary operators accepted by the conditional
+         command parser
+
+doc/{bash.1,bashref.texi}
+       - documented =~ conditional binary operator and the BASH_REMATCH
+         variable
+
+                                  8/27
+                                  ----
+lib/readline/display.c
+       - take multibyte characters into account when looking for quoted
+         substrings on which to do completion (fix from jir@yamato.ibm.com)
+
+lib/readline/util.c
+       - fix typo in _rl_strpbrk
+
+lib/readline/rldefs.h
+       - use function version of _rl_strpbrk in multibyte locales, because
+         it understands to skip over special characters in multibyte
+         character sequences
+
+                                  8/28
+                                  ----
+jobs.c
+       - in wait_for, check for window size changes if a job that exits due
+         to a signal or is stopped was in the foreground, not just if it's
+         the current job
+
+                                  9/10
+                                  ----
+support/config.{guess,sub}
+       - add support to recognize FreeBSD running on the amd64
+
+subst.c
+       - if the new `fail_glob_expansion' variable is non-zero, globbing that
+         fails to match anything causes an expansion error
+
+builtins/shopt.def
+       - new `failglob' expansion:  if enabled, failed globs cause an error
+
+test/shopt.right
+       - take `failglob' into account
+
+doc/{bash.1,bashref.texi}
+       - documented new `failglob' option and its effects
+
+                                  9/12
+                                  ----
+findcmd.c
+       - fix file_status to treat the mode bits and uid right -- in particular,
+         don't assume the `other' bits always apply.  Bug reported by
+         <moseley@hank.org>; fix inspired by <carlo@alinoe.com>
+
+command.h
+       - new word flag: W_NOCOMSUB, meaning to not perform command
+         substitution on a word
+
+subst.c
+       - new flag for param_expand: PF_NOCOMSUB.  If non-zero, $(...)
+         command substitutions are not expanded, but returned unchanged
+       - change expand_word_internal to pass through `` command substitutions
+         unchanged if (word->flags & W_NOCOMSUB) != 0
+       - change expand_word_internal to pass PF_NOCOMSUB to param_expand
+         if (word->flags & W_NOCOMSUB) != 0
+
+builtins/shopt.def
+       - rename set_interactive_comments to set_shellopts_after_change, which
+         more accurately reflects its purpose
+
+syntax.h
+       - add a define for isblank() in case the system doesn't provide one
+
+jobs.c
+       - change raw_job_exit_status to understand `pipefail', using the new
+         `pipefail_opt' variable
+
+flags.[ch]
+       - declare pipefail_opt
+       - reset pipefail_opt to 0 in reset_shell_flags
+
+builtins/set.def
+       - add `set -o pipefail' and document it in help output
+
+doc/{bash.1,bashref.texi}
+       - document `set -o pipefail' and the effect of the pipefail option
+
+mksyntax.c,syntax.h
+       - sh_syntaxtab is no longer `const'
+       - new generated variable, sh_syntabsiz, set to number of entries in
+         sh_syntaxtab, written to generated syntax.c
+
+locale.c
+       - new function, locale_setblanks(), sets each member of the current
+         locale's <blank> class to have the CSHBRK flag in sh_syntaxtab
+
+                                  9/17
+                                  ----
+arrayfunc.c
+       - change convert_var_to_array to not set array[0] to a NULL value
+         (if the scalar variable had no value; e.g., after being created
+         with `local arrayvar')
+
+lib/readline/display.c
+       - save and restore the value of prompt_invis_chars_first_line in
+         rl_{save,restore}_prompt, and reinitialize it to 0 before printing
+         something in the message area
+
+lib/readline/bind.c
+       - new functions: rl_bind_keyseq_if_unbound_in_map(ks, func, kmap);
+         binds key sequence KS to function FUNC in keymap KMAP, and
+         rl_bind_keyseq_if_unbound (ks, func); binds key sequence KS to
+         function FUNC in the current keymap
+
+lib/readline/readline.h
+       - extern function declarations for rl_bind_keyseq_if_unbound_in_map and
+         rl_bind_keyseq_if_unbound
+
+lib/readline/{readline,terminal}.c
+       - _rl_bind_if_unbound -> rl_bind_keyseq_if_unbound
+
+lib/readline/{bind.c,rlprivate.h}
+       - remove _rl_bind_if_unbound
+
+                                  9/18
+                                  ----
+lib/readline/doc/rltech.texi
+       - document rl_bind_keyseq_if_unbound and
+         rl_bind_keyseq_if_unbound_in_map
+
+                                  9/19
+                                  ----
+lib/readline/bind.c
+       - new functions rl_bind_key_if_unbound_in_map and
+         rl_bind_key_if_unbound; analogous to (and implemented in terms of)
+         keyseq functions
+       - rl_bind_keyseq_in_map: a new function, equivalent to rl_set_key
+         (which remains for backwards compatibility); changed callers to
+         use it
+       - new function, rl_bind_keyseq, equivalent to rl_bind_keyseq_in_map
+         with a third argument of _rl_keymap
+
+lib/readline/readline.h
+       - extern declarations for rl_bind_key_if_unbound_in_map and
+         rl_bind_key_if_unbound
+       - extern declarations for rl_bind_keyseq_in_map and rl_bind_keyseq
+
+lib/readline/doc/rltech.texi
+       - document rl_bind_keyseq and rl_bind_keyseq_in_map
+
+configure.in
+       - require at least readline-5.0
+
+config-bot.h
+       - define SYS_SIGLIST_DECLARED if it's not defined, but
+         HAVE_DECL_SYS_SIGLIST is, to deal with differences between
+         autoconf versions
+
+bashline.c
+       - use rl_bind_key_if_unbound_in_map when binding bash keybindings in
+         initialize_readline(), so inputrc files can override them
+
+                                  9/22
+                                  ----
+lib/readline/histsearch.c
+       - do better bounds checking for history_offset and history_length in
+         history_search_internal
+
+builtins/history.def
+       - in delete_last_history(), make sure we don't leave the history
+         offset longer than the history length after calling delete_histent
+
+                                  9/23
+                                  ----
+jobs.c
+       - small change to notify_of_job_status so job status messages get
+         printed even if the shell was started to run `-c command'.  The
+         old behavior was intentional, but I cannot remember why, so we'll
+         try it the other way for a while (debian bash bug #211693)
+
+                                  9/24
+                                  ----
+jobs.c
+       - slightly modify change from 9/23 so that jobs started to run
+         command substitutions don't print job status messages
+
+                                  9/25
+                                  ----
+lib/readline/search.c
+       - when reading a non-incremental search string from the terminal,
+         use a separate undo list rather than chaining it to the undo list
+         from the rest of the line, since the whole undo list will get
+         freed when the search string is complete
+
+lib/readline/readline.h
+       - changed the defines guarding the stdarg prototype for rl_message to
+         match what's actually used in display.c, where it's defined
+
+                                  9/26
+                                  ----
+[bash-3.0-alpha released]
+
+                                  9/29
+                                  ----
+lib/sh/shmatch.c
+       - fix to build correctly when arrays are not compiled into the shell
+
+subst.c
+       - fix command substitution to run any exit trap defined in the
+         command substitution before returning; the exit trap is not inherited
+         from the calling shell
+
+lib/readline/shell.c
+       - change sh_set_lines_and_columns to free the memory allocated and
+         passed to setenv(), since setenv is specified by POSIX to allocate
+         new memory and copy its arguments
+
+jobs.c
+       - change logic in make_child so that every child process attempts to
+         set the terminal's process group to the pipeline's process group
+         when PGRP_PIPE is defined, just like when it's undefined.  This is
+         reported to fix some tricky synchronization problems on Red Hat
+         Enterprise Linux 3.  Fix from Ernie Petrides <petrides@redhat.com>.
+
+                                  9/30
+                                  ----
+builtins/printf.def
+       - tescape no longer needs a `trans_squote' argument, since it's the
+         same as the `sawc' argument.  The `sawc' argument now means to do
+         the %b argument processing if non-null
+       - fix processing of octal constants for %b arguments (\0 followed by
+         up to three octal digits) and other escape sequences (\ followed by
+         up to three octal digits)
+       - hex constants `\xHHH' are now allowed to contain any positive
+         number of digits; previously they were restricted to two [removed]
+       - allow two new escape sequences: \" and \?, for compatibility with
+         ksh93 and ANSI C
+
+doc/{bash.1,bashref.texi}
+       - documented processing that printf performs for arguments to %b
+         escape sequences
+
+lib/sh/strtrans.c
+       - add \" and \? to escape sequences recognized by `echo -e'
+
+                                  10/1
+                                  ----
+version.c
+       - use snprintf instead of sprintf if configure tells us we have it
+
+                                  10/3
+                                  ----
+subst.c
+       - in list_remove_pattern, take into account the fact that one of the
+         list elements may be NULL, and don't free the result of
+         remove_pattern() without checking
+       - in remove_pattern, return savestring(param) if *param == '\0',
+         since callers expect to free() non-null return values
+
+                                  10/4
+                                  ----
+subst.c
+       - change verify_substring_values to make it clearer that the first
+         offset deals with array indices and the second deals with numbers
+         of elements, when doing array subranges with ${a[@]:e1:e2}
+
+array.c
+       - change array_subrange to make it explicit that the second offset
+         argument is a count of the desired number of elements, not an
+         ending index.  This deals with sparse arrays correctly.
+
+                                  10/6
+                                  ----
+variables.c
+       - fix memory leak in assign_in_env
+
+                                  10/8
+                                  ----
+subst.c
+       - in parameter_brace_expand, check that the last characters are `]}'
+         before checking for ${!array[@]}
+
+execute_cmd.c,builtins/source.def
+       - push and pop the args (BASH_ARGV and BASH_ARGC) when executing a
+         shell function or sourcing a script only when in debugging mode
+
+                                  10/11
+                                  -----
+arrayfunc.c
+       - make sure array_variable_name returns values for the SUBP and LENP
+         arguments if they're non-null, since callers expect to use them
+         even if the array subscript is bad
+
+error.c
+       - call exit_shell instead of sh_exit from parser_error and
+         report_error so the right things happen (running exit trap, doing
+         the right interactive cleanup, etc.)
+
+lib/readline/complete.c
+       - new variable, rl_completion_quote_character, set to any quote char
+         readline thinks it finds before any application completion
+         function is called
+       - new variable, rl_completion_suppress_quote, settable by an
+         application-specific completion function.  If set to non-zero, the
+         completion code does not append a closing quote in append_to_match
+
+lib/readline/readline.h
+       - extern declarations for rl_completion_quote_character and
+         rl_completion_suppress_quote
+
+bashline.c
+       - set rl_completion_suppress_quote in command_subst_completion_function
+         because that would be inserted before any closing "`" or ")", which
+         is somewhat disconcerting
+
+lib/readline/doc/rltech.texi
+       - documented rl_completion_suppress_quote and
+         rl_completion_quote_character
+
+                                  10/13
+                                  -----
+bashhist.c
+       - use sv_histchars instead of setting history_comment_char directly in
+         bash_initialize_history so assignments to $histchars made in
+         ~/.bashrc are honored
+
+                                  10/21
+                                  -----
+trap.c
+       - make sure run_exit_trap sets `running_trap' appropriately
+       - new variable, trap_saved_exit_value, set to last_command_exit_value
+         before running any trap commands; available to the rest of the
+         shell; use trap_saved_exit_value to replace some function-local
+         variables
+
+builtins/exit.def
+       - if the shell is running the exit trap, and no argument is given
+         to `exit', use trap_saved_exit_value as the exit status instead
+         of the last command exit value (which could be the previous command
+         run in the exit trap), as required by POSIX.2
+
+                                  10/25
+                                  -----
+doc/{bash.1,bashref.texi}
+       - add `alias' to the list of documented `assignment statement' builtins
+
+                                  11/1
+                                  ----
+doc/bash.1
+       - remove the `.' from the sample $PATH value
+
+parse.y
+       - make sure parse_compound_assignment prompts with $PS2 if it reads
+         a newline while parsing the compound assignment statement. Bug
+         reported by Stephane Chazelas
+       - parse_string_to_word_list now takes a new second argument: `int flags'
+       - new parser state flag: PST_COMPASSIGN; indicates that the shell is
+         parsing a compound assignment statement
+       - parse_string_to_word_list turns on PST_COMPASSIGN if `flags' arg
+         has low bit set
+       - turn PST_COMPASSIGN on and off in parse_compound_assignment
+
+externs.h
+       - change prototype declaration for parse_string_to_word_list
+
+arrayfunc.c
+       - change call to parse_string_to_word_list to add new flags arg
+
+general.c
+       - assignment() takes a new `flags' second argument
+       - if `flags' is non-zero, accept `[' as a legal assignment statement
+         starter character (for parsing compound array assignments)
+
+general.h
+       - add new argument to prototype declaration for assignment()
+
+parse.y,{subst,variables}.c, builtins/{setattr,declare}.def
+       - change calls to assignment() (parse.y calls with flags == 1 when
+         parser_state inlcudes PST_COMPASSIGN)
+
+arrayfunc.c
+       - in assign_array_var_from_string(), don't treat an expanded word
+         of the form [ind]=value specially unless the W_ASSIGNMENT flag is
+         set.  This means that words that are the result of expansions but
+         happen to have the same format as compound assignment statement
+         words will not be treated as such.  For instance
+
+               v='[12]=foobar'
+               a=( $v )
+
+         will result in a[0]='[12]=foobar' instead of a[12]=foobar.  This
+         is closer to how `regular' assignment statements are treated and
+         compatible with ksh93.  Bug reported by Stephane Chazelas
+
+shell.c
+       - new --protected argument, disables command substitution when used
+         with --wordexp (like --wordexp, it remains undocumented)
+       - change run_wordexp to turn on the W_NOCOMSUB flag in each word
+         to be expanded if protected_mode is set
+
+                                  11/7
+                                  ----
+doc/{bash.1,bashref.texi}
+       - clarified the language concerning inherited signal dispositions and
+         when traps are run
+
+support/shobj-conf
+       - slight changes to the darwin (Mac OS X) stanza for MacOS X 10.3
+         (for the readline shared library builds, which shares this script)
+
+lib/readline/histexpand.c
+       - change to make `^' behave as equivalent to word one, as csh does,
+         and as the documentation states
+
+lib/readline/display.c
+       - in update_line, make sure to use col_lendiff in all calculations
+         where the cursor position is concerned (like when calculating
+         the value of _rl_last_c_pos).  Fixes bug reported by Andreas
+         Schwab
+
+                                  11/12
+                                  -----
+trap.c
+       - make _run_trap_internal catch `return' builtin longjmps and clean
+         up before longjmping on to where the return was intended to go
+         (fixes bug with not turning off SIG_INPROGRESS flag when `return'
+         executed in trap command)
+
+                                  11/18
+                                  -----
+builtins/cd.def
+       - in posix mode, set errno to ENOTDIR if canonicalization fails,
+         unless the canonicalization functions leave it set to ENOENT
+
+                                  11/25
+                                  -----
+make_cmd.c
+       - in make_simple_command, don't blindly dereference element.redirect
+
+parse.y
+       - the list_terminator production now has an `int' value so it can be
+         used in other grammar productions
+       - add a rule that makes `time' on a line by itself time a null
+         command (this is iffy)
+
+                                  11/28
+                                  -----
+subst.c
+       - change the pattern substitution code (${var//pat/rep}) to use the
+         same pattern expansion function (getpattern()) as the pattern
+         removal expansions.  This has the effect of no longer performing
+         quote removal on the pattern before trying to match it.  This
+         fixes an incompatibility with ksh93 reported on comp.unix.shell
+
+nojobs.c
+       - add replacement function for siginterrupt on the off chance that a
+         system has posix signals but lacks siginterrrupt
+
+lib/readline/display.c
+       - fix from Tim Waugh at Red Hat to speed up inserting characters into
+         long lines in a UTF-8 environment by optimizing the calculation of
+         the first difference between old and new lines by checking to see
+         whether the old line is a subset of the new
+
+                                  11/29
+                                  -----
+lib/malloc/stats.c
+       - break code that opens file (and interprets %p) into separate function
+         _imalloc_fopen(char *s, char *fn, char *def, char *defbuf, size_t defsiz)
+         for use by rest of library
+       - default stats file is now `stats.PID'
+
+lib/malloc/trace.c
+       - new function, malloc_set_tracefn (char *s, char *fn), sets tracing
+         to the file named by FN (with %p interpolated as the pid), using
+         some default if FN is NULL
+
+lib/malloc/shmalloc.h
+       - new extern declaration for malloc_set_tracefn
+
+                                  12/4
+                                  ----
+execute_cmd.c
+       - combined several common strings from do_piping() into one
+         dup_error() function
+
+builtins/common.[ch]
+       - new function, `sh_notbuiltin(s)' prints error message about s not
+         being a shell builtin
+
+builtins/{builtin,enable}.def
+       - call sh_notbuiltin instead of using literal string
+
+{arrayfunc,expr,error}.c
+       - use one string variable for `bad array subscript' error message; use
+         in calls to various error reporting functions
+
+Makefile.in
+       - add variables for localedir and the PACKAGE_* variables, auto-set
+         by configure
+
+configure.in
+       - un-cache values for gettext, textdomain, and bindtextdomain if they're
+         not in libc but in libintl so the right variables get set
+
+bashintl.h
+       - add necessary defines for marking strings to be translated using
+         gettext
+
+locale.c
+       - set textdomain and directory in set_default_locale
+       - don't call textdomain with the value of $TEXTDOMAIN, since we don't
+         want to override the default domain ("bash")
+       - don't call bindtextdomain unless default_domain already has a value
+       - when translating $"..." strings, use dgettext with the script's
+         default domain (value of $TEXTDOMAIN)
+
+                                  12/9
+                                  ----
+builtins/mkbuiltins.c
+       - include "bashintl.h" in the generated "builtins.c"
+
+support/{config.rpath,mkinstalldirs}
+       - new files to support gettext i18n
+
+ABOUT-NLS
+       - new readme file for gettext internationalization
+
+po/{Makefile.in.in,Rules-quot,boldquot.sed,en@boldquot.header,en@quot.header,insert-header.sin,quot.sed,remove-potcdate.sin}
+po/{POTFILES.in,bash.pot}
+       - new files for gettext
+
+lib/intl
+       - new directory, with libintl stuff from gettext
+
+aclocal.m4
+       - add m4 files from gettext distribution needed by libintl
+
+configure.in
+       - create po/Makefile.in and lib/intl/Makefile in AC_OUTPUT
+       - add call to AM_GNU_GETTEXT to initialize gettext stuff
+
+Makefile.in
+       - use mkinstalldirs instead of mkdirs in the `installdirs' target
+       - changes for intl/ and po/ subdirectories in build and install
+       - changes to have libintl linked in, as determined by configure
+       - changes to have libintl built, just in case it's used (though I'd
+         rather not)
+
+                                  12/10
+                                  -----
+config.h.in
+       - additional #defines required by the libintl library
+       - add ENABLE_NLS define for AM_GNU_GETTEXT
+       - take out defines for HAVE_{BINDTEXTDOMAIN,GETTEXT,TEXTDOMAIN}
+
+configure.in
+       - removed old tests for libintl and gettext/textdomain/bindtextdomain
+
+locale.c
+       - remove HAVE_GETTEXT code; we have gettext unconditionally now
+
+bashintl.h
+       - change to include "gettext.h" and remove the conditional code based
+         on whether or not gettext is present
+
+                                  12/16
+                                  -----
+lib/readline/vi_mode.c
+       - fix problem with rl_vi_eWord that caused it to skip over the last
+         character of a word if invoked while point was on the next-to-last
+         character
+
+                                  12/18
+                                  -----
+{arrayfunc,bashhist,bashline,error,eval,execute_cmd,expr,general,input,jobs}.c
+{mailcheck,make_cmd,nojobs,pcomplete,pcomplib,print_cmd,redir,shell,sig}.c
+{subst,test,trap,variables,version,xmalloc}.c
+parse.y
+builtins/{common,evalfile,getopt}.c
+builtins/{bind,break,caller,cd,complete,declare,enable,exec,exit,fc,fg_bg}.def
+builtins/{hash,help,history,jobs,kill,printf,pushd,read,return,set,setattr}.def
+builtins/{shift,shopt,source,suspend,type,ulimit,umask}.def
+lib/sh/{fmtulong,netopen}.c
+       - include "bashintl.h" for gettext defines
+
+Makefile.in
+       - add `-DBUILDTOOL' to CFLAGS for buildversion.o
+
+bashintl.h
+       - if `BUILDTOOL' is defined, define ENABLE_NLS to 0 so we don't have
+         to compile and link in the gettext stuff
+
+Makefile.in,lib/sh/Makefile.in,builtins/Makefile.in
+       - update dependencies on bashintl.h and include/gettext.h
+
+                                  12/19
+                                  -----
+{arrayfunc,bashhist,bashline,error,eval,execute_cmd,expr,general,input,jobs}.c
+{mailcheck,make_cmd,nojobs,pcomplete,pcomplib,print_cmd,redir,shell,sig}.c
+{subst,test,trap,variables,version,xmalloc}.c
+builtins/{common,evalfile,getopt}.c
+builtins/{bind,break,caller,cd,complete,declare,enable,exec,exit,fc,fg_bg}.def
+builtins/{hash,help,history,jobs,kill,let,printf,pushd,read,return,set}.def
+builtins/{setattr,shift,shopt,source,suspend,type,ulimit,umask}.def
+lib/sh/{fmtulong,netopen}.c
+lib/malloc/{malloc,stats,table,watch}.c
+       - mark up strings in source files for gettext processing
+
+lib/malloc/imalloc.h
+       - include "bashintl.h" if SHELL is defined, otherwise make _(x) an
+         identity define
+
+lib/malloc/Makefile.in
+       - add dependencies on ${topdir}/bashintl.h and ${BASHINCDIR}/gettext.h
+
+                                  12/21
+                                  -----
+bashline.c
+       - make sure we index into rl_line_buffer with indexes > 0 in
+         attempt_shell_completion
+
+                                  12/31
+                                  -----
+Makefile.in
+       - descend into `po' and run make recursively for the various clean
+         targets
+
+                                   1/4
+                                   ---
+include/shmbutil.h
+       - two new macros:  BACKUP_CHAR(str, strsize, i), which backs up one
+         multibyte character in STR starting at index I, and
+         BACKUP_CHAR_P(str, strsize, p), which backs up one multibyte
+         character in STR starting at P, which is a char *
+
+                                   1/6
+                                   ---
+pcomplete.c
+       - in pcomp_filename_completion_function, use the quote character
+         readline found (and assigned to rl_complete_quote_character) when
+         dequoting the filename by a completion call from readline (when
+         rl_dispatching != 0)
+
+bashline.c
+       - ditto for bash_directory_completion_matches
+
+                                   1/7
+                                   ---
+lib/readline/complete.c
+       - new variable,  rl_completion_found_quote, set to non-zero value if
+         readline finds what it thinks is quoting in the word to be completed
+
+lib/readline/readline.h
+       - extern declaration for rl_completion_found_quote
+
+                                   1/8
+                                   ---
+lib/readline/doc/rltech.texi
+       - documented rl_completion_found_quote
+
+lib/readline/complete.c
+       - in compute_lcd_of_matches, if it looks like what the user typed was
+         dequoted before generating filename matches, dequote the user's
+         text again before figuring out the case-insensitive lcd
+
+                                   1/9
+                                   ---
+lib/readline/display.c
+       - fix from Edward Catmur <ed@catmur.co.uk> to logic that handles
+         invisible characters in prompt string.  Original code was wrong
+         about local_prompt_prefix; it gave incorrect results when prompt
+         contained invisible characters after a line break
+
+                                  1/10
+                                  ----
+subst.c
+       - new function, mb_substring(), does character (possibly multibyte)
+         oriented rather than strictly byte-oriented substring extraction.
+         The passed indices, rather than strictly indexing into the string,
+         indicate character positions that need to be calculated.  From
+         Tim Waugh <twaugh@redhat.com>
+       - change parameter_brace_substring to use mb_substring if necessary
+
+included/shmbutil.h
+       - new define SADD_MBQCHAR_BODY, common code for adding a quoted
+         (preceded by CTLESC) multibyte character to an accumulating string
+         in the subst.c expansion code
+
+subst.c
+       - use SADD_MBQCHAR_BODY in expand_word_internal
+       - new static function, mb_getcharlens, allocates and returns an array
+         of character lengths for (possibly multibyte) characters in the
+         argument string
+       - change pattern matching operations to use while loops instead of
+         for loops to handle multibyte characters better (no more simple
+         increment or decrement)
+       - change pattern matching operations to use multibyte character
+         operations instead of simple increments and decrements.  Don't
+         use BACKUP_CHAR_P -- use the mblen array instead, because that
+         avoids the N**2 behavior of having to count from the beginning
+         of the string each time you want to back up one character.  Changes
+         to remove_pattern and match_pattern
+
+                                  1/12
+                                  ----
+lib/readline/display.c
+       - make expand_prompt count multbyte characters in the prompt string
+         by using _rl_find_next_mbchar (and copying possibly more than one
+         byte) instead of a simple increment and single byte copy
+
+                                  1/13
+                                  ----
+lib/readline/display.c
+       - expand_prompt takes a new reference argument -- it returns
+         the actual count of (possibly multibyte) characters displayed
+         on the screen
+       - don't short-circuit in expand_prompt unless we're not going to
+         be using any multibyte characters
+       - change calls  to expand_prompt to pass an argument for the
+         number of physical characters the prompt occupies
+         (prompt_physical_chars)
+       - initialize `lpos' (the physical cursor position) from
+         prompt_physical_chars in rl_redisplay
+
+lib/readline/mbutil.c
+       - in _rl_find_prev_mbchar_internal, if mbrtowc returns -1 or -2, and
+         we assume that the character is a single-byte char, make sure we
+         update `prev' so it doesn't get lost.  Fixes problems encountered
+         when a non-ascii char is the last char on the line and we're moving
+         back past it with ^B, and other display problems caused by the same
+         situation
+
+                                  1/15
+                                  ----
+lib/readline/doc/rltech.texi
+       - document RL_PROMPT_START_IGNORE and RL_PROMPT_END_IGNORE in the
+         description of rl_expand_prompt()
+
+                                  1/20
+                                  ----
+bashline.c
+       - in initialize_readline, make sure M-C-j and M-C-m are still bound to
+         vi-editing-mode before unbinding them -- they may have been rebound
+         in an inputrc
+
+variables.c
+       - in unbind_variable, unset attributes other than `local' and exported
+         (if the variable came from a temporary environment) when unsetting a
+         local variable inside a function
+
+                                  1/21
+                                  ----
+configure.in
+       - add libintl build directory to the list of include directories if
+         it's being built (using INTL_BUILDDIR)
+
+Makefile.in,{builtins,lib/{sh,malloc}}/Makefile.in
+       - substitute LIBBUILD as ${BUILD_DIR}/${LIBSUBDIR}
+       - define INTL_BUILDDIR as ${LIBBUILD}/intl
+
+{builtins,lib/sh}/Makefile.in
+       - make sure INTL_INC is added to  the list of include directories
+       - make sure INTL_LIBSRC is defined with the correct value
+
+{configure,Makefile,{builtins,lib/sh}/Makefile}.in
+       - substitute LIBINTL_H as ${INTL_BUILDDIR}/libintl.h
+
+Makefile.in,builtins/Makefile.iin
+       - all files depending on bashintl.h also depend on ${LIBINTL_H}
+         (which may be empty)
+
+Makefile.in
+       - make a rule telling how to build lib/intl/libintl.h if necessary
+
+                                  1/24
+                                  ----
+builtins/read.def
+       - make sure that the array name supplied as an argument to -a is a
+         valid identifier
+
+parse.y
+       - make the \W expansion abbreviate $HOME with a ~ (seems to be more
+         useful)
+
+doc/{bash.1,bashref.texi}
+       - document new behavior of \W
+
+subst.c
+       - make sure parameter_brace_expand_rhs uses the first character of
+         $IFS when making the string to return from the expanded word
+         (which, in the case of "$@" or $@, contains multiple words that
+         need to be separated)
+
+                                  1/25
+                                  ----
+builtins/common.c
+       - change get_job_spec to make `%' by itself or an empty argument
+         return NO_JOB
+
+jobs.h
+       - new possible value for a job spec return value:  BAD_JOBSPEC
+         (for syntactically invalid specs, like the empty string)
+
+shell.c
+       - in open_shell_script, check to see whether or not we can find and
+         open the filename argument before setting dollar_vars[0] or
+         manipulating BASH_SOURCE, so the error messages come out better
+
+subst.c
+       - in string_list_internal, short-circuit right away to savestring()
+         if the list only has a single element
+
+                                  1/28
+                                  ----
+lib/readline/rltypedefs.h
+       - new set of typedefs for functions returning char * with various
+         arguments (standard set)
+
+lib/readline/complete.c
+       - new function pointer, rl_completion_word_break_hook, called by
+         _rl_find_completion_word, used to set word break characters at
+         completion time, allowing them to be position-based
+
+lib/readline/doc/rltech.texi
+       - documented rl_completion_word_break_hook
+
+lib/readline/kill.c
+       - added new rl_unix_filename_rubout, which deletes one filename
+         component in a Unix pathname backward (delimiters are whitespace
+         and `/')
+
+lib/readline/readline.h
+       - extern declaration for rl_unix_filename_rubout
+
+lib/readline/funmap.c
+       - new bindable readline command `unix-filename-rubout'
+
+lib/readline/doc/{readline.3,rluser.texi},doc/bash.1
+       - documented `unix-filename-rubout'
+
+                                  1/29
+                                  ----
+lib/readline/histexpand.c
+       - change history_tokenize_internal to handle non-whitespace delimiter
+         characters by creating separate fields (like the shell does when
+         splitting on $IFS)
+
+                                  1/30
+                                  ----
+lib/glob/xmbsrtowcs.c
+       - new function, xdupmbstowcs, for convenience:  calls xmbsrtowcs
+         while allocating memory for the new wide character string
+       - some small efficiency improvments to xmbsrtowcs
+
+include/shmbutil.h
+       - extern declaration for xdupmbstowcs
+
+lib/glob/strmatch.h
+       - include config.h for definition of HANDLE_MULTIBYTE
+       - remove the HAVE_LIBC_FNM_EXTMATCH tests
+       - new extern declaration for wcsmatch(whchar_t *, wchar_t *, int)
+
+configure.in
+       - remove call to BASH_FUNC_FNMATCH_EXTMATCH; it's no longer used
+
+lib/glob/smatch.c
+       - simplify xstrmatch() by using xdupmbstowcs() instead of inline code
+
+lib/glob/glob.c
+       - modify mbskipname() to avoid the use of alloca
+       - simplify mbskipname() by using xdupmbstowcs() instead of inline code
+       - simplify glob_pattern_p() by using xdupmbstowcs() instead of
+         inline code
+       - fix memory leak in wdequote_pathname
+       - simplify wdequote_pathname() by using xdupmbstowcs() instead of
+         inline code
+
+lib/glob/strmatch.c
+       - new function, wcsmatch(), `exported' wide-character equivalent of
+         strmatch()
+
+subst.c
+       - old match_pattern is now match_upattern
+       - match_pattern now either calls match_upattern or converts
+         mbstrings to wide chars and calls match_wpattern
+       - match_upattern reverted to old non-multibyte code
+       - new function: match_pattern_wchar, wide character version of
+         match_pattern_char
+
+                                   2/1
+                                   ---
+subst.c
+       - old remove_pattern is now remove_upattern
+       - remove_upattern reverted to old non-multibyte code (pre-Waugh patch)
+       - new multibyte version of remove_pattern: remove_wpattern
+       - remove_pattern now calls either remove_upattern or converts a
+         multibyte string to a wide character string and calls
+         remove_wpattern
+       - new function, wcsdup, wide-character version of strdup(3)
+
+                                   2/4
+                                   ---
+print_cmd.c
+       - temporarily translate a >&filename redirection from
+         r_duplicating_output_word to r_err_and_out (as the expansion code
+         in redir.c does) so it prints without a leading `1' (file
+         descriptor)
+
+                                   2/5
+                                   ---
+aclocal.m4
+       - add a check for wcsdup to BASH_CHECK_MULTIBYTE
+
+config.h.in
+       - add HAVE_WCSDUP define
+
+                                   2/9
+                                   ---
+builtins/shift.def
+       - fix a call to sh_erange that possibly dereferences a NULL pointer
+
+                                  2/12
+                                  ----
+general.c
+       - start at a general set of file property checking functions:
+         file_isdir(), file_iswdir() (is writable directory)
+
+general.h
+       - extern declarations for new functions
+
+lib/sh/tmpfile.c
+       - use file_iswdir() to make sure the temporary directory used for
+         here documents and other temp files is writable in get_sys_tmpdir()
+
+                                  2/17
+                                  ----
+bashline.c
+       - fix conditional binding of emacs-mode M-~ -- there is a default
+         binding for it (rl_tilde_expand), so a straight call to
+         rl_bind_key_if_unbound_in_map doesn't do the right thing
+
+                                  2/27
+                                  ----
+[bash-3.0-beta1 released]
+
+                                  2/29
+                                  ----
+subst.c
+       - fixed expansion so referencing $a, when a is an array variable
+         without an element assigned to index 0, exits the shell when
+         `-u' is enabled
+
+expr.c
+       - make the exponentiation operator (**) associative, so things like
+         2**3**4  work right (change `if' to `while')
+
+                                   3/3
+                                   ---
+lib/sh/strftime.c
+       - SCO Unix 3.2, like Solaris, requires that the system's `timezone'
+         variable be declared as long
+
+lib/readline/{bind,histfile,input,parens}.c
+       - changes for Tandem (including `floss.h' (?))
+
+                                   3/4
+                                   ---
+subst.c
+       - change param_expand to quote the entire expanded string instead
+         of just the escape characters if the expansion appears between
+         double quotes or in a here-document (for simple variable expansions
+         or expansions of positional parameters)
+
+                                   3/8
+                                   ---
+subst.c
+       - analogous changes to parameter_brace_expand_word to fix the same
+         quoting problem as on 3/4; fix callers to understand that  the
+         value returned might be quoted now and should be dequoted if
+         necessary
+       - add a `quoted' argument to get_var_and_type, change callers
+       - change today's fix and fix from 3/4 to not call quote_string if the
+         value is "" (because quote_string turns that into CTLNUL\0)
+
+                                   3/9
+                                   ---
+builtins/cd.def
+       - resetpwd() now takes a `caller' argument so it can be used by pwd
+         as well as cd
+       - change pwd_builtin to call resetpwd() if sh_physpath() fails to
+         return a valid pathname
+
+                                  3/14
+                                  ----
+expr.c
+       - reworked exp0 and readtok() to make post-increment and post-decrement
+         into real tokens, which may be separated from their accompanying
+         variables by whitesapce
+       - made analogous changes to readtok() to make pre-increment and
+         pre-decrement work when separated from their accompanying identifier
+         by whitespace
+
+                                  3/18
+                                  ----
+lib/readline/misc.c
+       - in rl_maybe_unsave_line, don't force rl_replace_line to clear
+         the undo_list, since it might point directly at an undo list
+         from a history entry (to which we have no handle)
+
+                                  3/19
+                                  ----
+lib/readline/display.c
+       - rl_save_prompt and rl_restore_prompt now save and restore the value
+         of prompt_physical_chars
+       - set prompt_physical_chars in rl_redisplay when expand_prompt has
+         not been called (e.g., when rl_display_prompt is set and is not
+         equal to rl_prompt, like when searching)
+
+lib/readline/histexpand.c
+       - don't call add_history in history_expand if the `:p' modifier is
+         supplied; leave that to the calling application.  This means that
+         `history -p', for example, will not add anything to the history
+         list (as documented), nor will history expansions invoked by
+         emacs-mode M-C-e line editing
+
+config-bot.h
+       - check whether HAVE_DECL_SYS_SIGLIST is defined to 1 rather than just
+         defined, to work around newer versions of autoconf defining it to 0
+
+config.h.in
+       - change default status of HAVE_MALLOC to #undef instead of #define
+
+bashhist.c
+       - extern declarations for rl_done and rl_dispatching
+       - don't call re_edit from pre_process_line unless rl_dispatcing is zero,
+         so we don't call it from something like shell-expand-line
+       - change pre_process_line to add an expanded history specification
+         that returned `print only' to the history list, since history_expand
+         no longer does it (and, when using readline, do it only when
+         rl_dispatching is zero)
+
+                                  3/22
+                                  ----
+config.h.in,aclocal.m4
+       - change bash-specific functions that look in struct dirent to define
+         HAVE_STRUCT_DIRENT_xxx, like AC_CHECK_MEMBERS does (though the
+         functions are otherwise the same)
+       - new function, BASH_STRUCT_DIRENT_D_NAMLEN, define
+         HAVE_STRUCT_DIRENT_D_NAMLEN if struct dirent has a `d_namlen' member
+
+configure.in
+       - call BASH_STRUCT_DIRENT_D_NAMLEN
+
+include/posixdir.h
+       - use new and renamed HAVE_STRUCT_DIRENT_D_xxx defines
+
+                                   4/7
+                                   ---
+builtins/cd.def
+       - ensure that we print out a non-null pathname after getting a
+         directory from CDPATH and canonicalizing it (e.g., if the result
+         exceeds PATH_MAX in length and the_current_working_directory is
+         set to NULL)
+
+                                  4/12
+                                  ----
+print_cmd.c
+       - new function to print out assignment statements when `set -x' has
+         been enabled: xtrace_print_assignment
+
+externs.h
+       - extern declaration for xtrace_print_assignment
+
+                                  4/13
+                                  ----
+{subst,variables}.c
+       - call xtrace_print_assignment instead of using inline code
+
+jobs.c
+       - if turning on job control when it was previously off, set
+         pipeline_pgrp to 0 in set_job_control so make_child puts
+         subsequent children in their own process group
+
+                                  4/14
+                                  ----
+general.c
+       - new function, legal_alias_name, called to decide whether an
+         argument to add_alias is a valid alias name -- essentially any
+         character except one which must be quoted to the shell parser
+         and `/'
+
+general.h
+       - new extern declaration for legal_alias_name
+
+builtins/alias.def
+       - `unalias' now returns failure status if no NAME arguments are
+         supplied and -a is not given
+       - call legal_alias_name to make sure alias name is valid before
+         calling add_alias from alias_builtin
+
+                                  4/19
+                                  ----
+include/shmbutil.h
+       - include <config.h> for definition of HANDLE_MULTIBYTE rather than
+         duplicating logic
+
+                                  4/20
+                                  ----
+
+doc/{bash.1,bashref.texi}
+       - make sure $0 is consistently referred to as a `special parameter'
+       - document which characters are now not allowed in alias names
+
+                                  4/23
+                                  ----
+
+builtins/{jobs,kill,wait}.def
+       - removed requirement that job control be enabled to use job control
+         notation, as SUSv3 implies
+
+subst.c
+       - based on a message from David Korn, change param_expand to not call
+         string_list_dollar_star if the only quoting is Q_HERE_DOCUMENT --
+         quoted here documents are like double quoting, but not exactly
+       - analogous changes to list_remove_pattern and pos_params
+
+                                  4/24
+                                  ----
+lib/readline/doc/rluser.texi
+       - fix error in description of emacs-mode C-xC-e command (uses $VISUAL
+         instead of $FCEDIT)
+
+                                  4/28
+                                  ----
+support/bashbug.sh
+       - integrate a patch from Stefan Nordhausen
+         <nordhaus@informatik.hu-berlin.de> that reduces race conditions
+         by using a temporary directory inside $TMPDIR and creating the
+         temp files in that
+
+                                  4/30
+                                  ----
+builtins/common.c
+       - use pathconf(".", _PC_PATH_MAX) where available to size the argument
+         to getcwd() rather than using a straight PATH_MAX
+
+builtins/cd.def
+       - if get_working_directory fails and returns null (causing resetpwd
+         to return NULL), use set_working_directory to set $PWD to the
+         absolute pathname for which chdir just succeeded
+
+                                   5/1
+                                   ---
+lib/readline/vi_mode.c
+       - in rl_vi_change_to, call _rl_vi_set_last with rl_numeric_arg instead
+         of `last', since they're equal at call time and rl_vi_domove can
+         change rl_numeric_arg (which vi apparently updates).  Fixes redo bug
+         of `c2....' reported by Marion Berryman <mwberryman@copper.net>
+
+                                   5/4
+                                   ---
+parse.y
+       - fix decode_prompt_string to properly deal with strftime() returning 0
+
+                                   5/6
+                                   ---
+variables.c
+       - in make_local_array_variable, return an already-existing local array
+         variable immediately rather than creating a new array (causing a
+         memory leak)
+
+                                   5/8
+                                   ---
+lib/readline/vi_mode.c
+       - change rl_vi_domove to set rl_explicit_arg before calling
+         rl_digit_loop1 so that multi-digit numeric arguments work right
+       - _rl_vi_last_command is no longer static
+
+lib/readline/rlprivate.h
+       - new extern declaration for _rl_vi_last_command
+
+lib/readline/text.c
+       - change rl_newline to only call _rl_vi_reset_last if the last command
+         (_rl_vi_last_command) is not a text modification command.  This lets
+         the last-command and last-argument work across command lines
+
+                                  5/13
+                                  ----
+builtins/common.c
+       - use getcwd(0,0) rather than providing a fixed pathname with a fixed
+         length (PATH_MAX) so getcwd() will allocate sufficient memory
+
+aclocal.m4
+       - change BASH_FUNC_GETCWD to check whether or not getcwd(0,0) will
+         allocate memory for the returned value -- nobody implements that
+         and getcwd-via-popen, so it should capture the old test as well
+
+                                  5/27
+                                  ----
+
+builtins/trap.def
+       - the historical behavior of assuming that a signal's handling should
+         be set to its original disposition is only in effect if a single
+         argument is given, otherwise the first argument is assumed to be a
+         command to execute
+       - when in posix mode, if trap gets a single argument, display an
+         error message and return EX_USAGE
+       - change the help message and usage string to better explain trap's
+         behavior
+
+doc/{bash.1,bashref.texi}
+       - describe the new default behavior when the first argument is a
+         signal spec
+       - note that signal names are case insensitive and the SIG prefix is
+         optional in arguments to `trap'
+       - note that signal name arguments to `kill' are case-insensitive
+
+lib/readline/display.c
+       - make sure rl_on_new_line_with_prompt sets rl_display_prompt to
+         rl_prompt (just to make sure it's set)
+       - have rl_on_new_line_with_prompt use local_prompt if it's set
+
+                                   6/2
+                                   ---
+subst.c
+       - in string_extract_double_quoted, cope with extract_delimited_string
+         returning NULL, as it can when attempting completion on an unclosed
+         command substitution
+
+                                  6/24
+                                  ----
+lib/readline/complete.c
+       - change print_filename to add a `/' to listed directory names if
+         `mark-directories' has been enabled
+
+builtins/umask.def
+       - make sure that the mask passed to parse_symbolic_mode has all but
+         the operative low eight bits masked off, to avoid complementing
+         all 0s to -1, which is the error return code.  This makes things
+         like `a=rwx' and `ugo=rwx' work and turn off the umask
+
+                                  6/26
+                                  ----
+builtins/getopts.def
+       - when `getopts' reaches the end of options, unset OPTARG before
+         returning EOF.  In response to a bug report from Apple
+
+configure.in
+       - when cross-compiling, don't set CPPFLAGS_FOR_BUILD and
+         LDFLAGS_FOR_BUILD from CPPFLAGS and LDFLAGS, respectively, since
+         those are for the target instead of the build platform (report
+         from robert@schwebel.de)
+
+shell.c
+       - a shell whose standard error (but not standard output) is directed
+         to a terminal should be interactive, according to POSIX/SUS.  This
+         means that sh > sh.out will start an interactive shell.  Bug report
+         from llattanzi@apple.com
+
+doc/{bash.1,bashref.texi}
+       - change mention of standard output to standard error in definition
+         of interactive shell
+
+lib/readline/vi_mode.c
+       - new convenience function, rl_vi_start_inserting, calls
+         _rl_vi_set_last to save the last textmod command state and then calls
+         rl_vi_insertion_mode to enter insert mode
+       - change functions to use rl_vi_start_inserting
+
+lib/readline/readline.h
+       - extern declaration for rl_vi_start_inserting
+
+bashline.c
+       - new function for vi-mode completion, bash_vi_complete.  Does
+         filename expansion as POSIX specifies, unlike the default readline
+         bindings (which don't know about globbing).  Bound to `\', `*',
+         and `=' in vi command keymap.  Internals very similar to
+         rl_vi_complete; just calls bash glob expansion functions
+
+lib/readline/vi_mode.c
+       - change rl_vi_change_char so that an entire change is a single
+         undoable event, rather than each individual change in a [count]r
+         command
+       - fix rl_vi_change_char so that replacing characters up to EOL works
+         rather than generating rl_ding
+       - fix rl_vi_change_case so that replacing characters up to EOL works
+         rather than generating rl_ding
+
+                                  6/28
+                                  ----
+builtins/echo.def
+       - call clearerr(stdout) before writing anything and testing its
+         failure or success
+
+                                  6/29
+                                  ----
+bashline.c
+       - only set rl_explicit_arg in bash_glob_complete_word if readline is
+         in emacs mode; let bash_vi_complete take care of setting it in vi
+         mode
+       - fix bash_vi_complete to only set rl_explicit_arg unless the posix
+         conditions are met: no globbing characters in the vi `bigword'
+         being completed
+
+                                  6/30
+                                  ----
+[bash-3.0-rc1 released]
+
+                                   7/1
+                                   ---
+lib/readline/complete.c
+       - make sure `extension_char' is initialized before deciding whether
+         or not the append a `/' to a possible completion when visible-stats
+         is not enabled
+
+                                   7/2
+                                   ---
+subst.c
+       - fix a boundary overrun in string_extract_double_quoted that could
+         occur when the word completion code attempts to expand an incomplete
+         construct (like a quoted unclosed command substitution)
+
+                                   7/4
+                                   ---
+subst.c
+       - set tempenv_assign_error to non-zero if an assignment to the
+         temporary environment fails for some reason (e.g., attempted
+         assignment to a readonly variable)
+
+execute_cmd.c
+       - fix execute_simple_command to force a non-interactive shell in
+         POSIX mode to exit if an assignment to the temporary environment
+         preceding a special builtin fails (bug report from
+         llattanzi@apple.com)
+
+                                   7/5
+                                   ---
+bashline.c
+       - in bash_directory_completion_hook, don't perform word expansions
+         if the filename appears to have been completed from the file
+         system rather than typed in by the user.  Bug reported by Tim
+         Waugh <twaugh@redhat.com>
+
+                                   7/7
+                                   ---
+lib/readline/misc.c
+       - if _rl_maybe_save_line is being asked to save a line other than
+         what's already saved, free up the current saved line and save the
+         current contents of rl_line_buffer.  Bug reported by
+         llattanzi@apple.com
+
+                                  7/12
+                                  ----
+lib/readline/input.c
+       - do better EOF detection in rl_gather_tyi -- if a read returns 0 when
+         the fd is in non-blocking mode, stuff an EOF into the input stream
+         (reported by mattias@virtutech.se)
+
+                                  7/13
+                                  ----
+lib/readline/vi_mode.c
+       - make sure rl_vi_put honors `count' arguments and yanks things
+         multiple times if requested
+
+                                  7/16
+                                  ----
+builtins/umask.def
+       - make sure that the `who' part of the umask symbolic mode argument
+         defaults to `a' if it's missing
+
+flags.c
+       - make sure that maybe_make_restricted only gets called after the
+         shell is initialized, so `bash -r' doesn't result in inappropriate
+         error messages
+
+[bash-3.0 frozen]
+
+                                  7/27
+                                  ----
+doc/Makefile.in
+       - small fixes
+
+[bash-3.0-released]
+
+                                  7/28
+                                  ----
+array.c
+       - in array_insert(), make sure the value to be added is non-NULL before
+         calling savestring() on it
+
+builtins/reserved.def
+       - fix description of `CDPATH'
+
+lib/readline/display.c
+       - when expanding a prompt that spans multiple lines with embedded
+         newlines, set prompt_physical_chars from the portion after the
+         final newline, not the preceding portion.  Bug reported by
+         "Ralf S. Engelschall" <rse@engelschall.com>
+
+make_cmd.c
+       - explicitly declare `lineno' in function prologue for make_case_command
+
+builtins/evalfile.c
+       - include `trap.h' for declaration for run_return_trap
+
+bashline.c
+       - fix a `return' without a value in enable_hostname_completion
+
+general.c
+       - include test.h for extern declaration for test_eaccess
+
+externs.h
+       - add declaration for zcatfd
+
+tests/{history,histexp}.tests
+       - unset HISTFILESIZE to avoid problems if a value of 0 is inherited
+         from the environment
+
+                                  7/30
+                                  ----
+bashline.c
+       - small changes to glob_expand_word to perform tilde expansion before
+         attempting globbing
+
+builtins/Makefile.in
+       - fix the install-help target to not cd into the `helpfiles'
+         subdirectory, so a value of $INSTALL_DATA containing a relative
+         pathname (e.g., .././support/install.sh) remains valid
+
+                                  7/31
+                                  ----
+subst.c
+       - new function, mbstrlen(s), returns length of a multibyte character
+         string
+
+include/shmbutil.h
+       - new macro, MB_STRLEN(s), calls mbstrlen or STRLEN as appropriate
+
+builtins/trap.def
+       - small change so that a first argument that's a valid signal number
+         (digits only -- no symbolic names) will be treated as a signal and
+         reverted back to the original handling disposition.  Fixes debian
+         complaints
+
+subst.c
+       - call MB_STRLEN instead of STRLEN where appropriate in
+         parameter_brace_expand_length to handle multibyte characters properly
+       - call MB_STRLEN instead of strlen in verify_substring_values so that
+         negative substrings of strings with multibyte chars work properly
+
+                                   8/1
+                                   ---
+jobs.c
+       - describe_pid needs to write to stderr, not stdout (POSIX)
+       - start_job, since it's only used by builtins (fg/bg), needs to write
+         its output to stdout, not stderr (POSIX)
+
+sig.c
+       - add an `orig_flags' member to struct terminating_signal so the
+         original signal handling flags (SA_RESTART, etc.) can be preserved
+         on POSIX systems
+       - make sure to preserve the signal flags state in
+         initialize_terminating_signals and reset them for child processes
+         in reset_terminating_signals
+
+builtins/fc.def
+       - fixed an off-by-one error that caused `fc -l' to list one too many
+         history entries
+       - in posix mode, `fc' should not list any indication as to whether or
+         not history lines have been modified (POSIX)
+       - when in posix mode, the default editor for `fc' should be `ed' (POSIX)
+
+doc/bashref.texi
+       - updated the description of `trap' behavior when given a first
+         argument that is a valid signal number
+       - noted that `fc -l' won't indicate whether a history entry has been
+         modified if the shell is in posix mode
+
+builtins/command.def
+       - fixed bug: `command -v' is supposed to be silent if a command is not
+         found
+
+builtins/hash.def
+       - `hash' should print its `hash table empty' message to stderr
+
+lib/readline/misc.c
+       - back out 7/7 change to _rl_maybe_save_line; it breaks emacs-mode ^P
+
+general.c
+       - changed base_pathname so that it will return reasonable results for
+         non-absolute pathnames -- this is what is intended by all of its
+         callers
+
+arrayfunc.c
+       - fix array_variable_part to return NULL if it finds an invisible
+         variable in the hash table.  Fixes seg fault caused by referring to
+         unset local variable using array notation
+
+{locale,variables}.c
+       - support LC_TIME as a special locale variable so HISTTIMEFORMAT tracks
+         the current locale
index b5b377d63857bd1689c6e850ae9995100109bc00..652eb754afa8e1c9f37ee8db72a6cfa9058d8d25 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -758,7 +758,7 @@ tests/histexp.tests f
 tests/histexp.right    f
 tests/history.tests    f
 tests/history.right    f
-tests/history.list     f
+tests/history.list     f       444
 tests/ifs.tests                f
 tests/ifs.right                f
 tests/input-line.sh    f
index a00f17f86aa3c190a36085d72f4ff44a04533dcb..0d644b121f6cecaadd1d603c21a2cf07acca1e0f 100644 (file)
@@ -611,7 +611,7 @@ array_variable_part (s, subp, lenp)
   var = find_variable (t);
 
   free (t);
-  return var;
+  return (var == 0 || invisible_p (var)) ? (SHELL_VAR *)0 : var;
 }
 
 /* Return a string containing the elements in the array and subscript
diff --git a/arrayfunc.c~ b/arrayfunc.c~
new file mode 100644 (file)
index 0000000..a00f17f
--- /dev/null
@@ -0,0 +1,763 @@
+/* arrayfunc.c -- High-level array functions used by other parts of the shell. */
+
+/* Copyright (C) 2001-2003 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 2, 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, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+
+#include "config.h"
+
+#if defined (ARRAY_VARS)
+
+#if defined (HAVE_UNISTD_H)
+#  include <unistd.h>
+#endif
+#include <stdio.h>
+
+#include "bashintl.h"
+
+#include "shell.h"
+
+#include "shmbutil.h"
+
+#include "builtins/common.h"
+
+extern char *this_command_name;
+extern int last_command_exit_value;
+
+static void quote_array_assignment_chars __P((WORD_LIST *));
+static char *array_value_internal __P((char *, int, int, int *));
+
+/* Standard error message to use when encountering an invalid array subscript */
+char *bash_badsub_errmsg = N_("bad array subscript");
+
+/* **************************************************************** */
+/*                                                                 */
+/*  Functions to manipulate array variables and perform assignments */
+/*                                                                 */
+/* **************************************************************** */
+
+/* Convert a shell variable to an array variable.  The original value is
+   saved as array[0]. */
+SHELL_VAR *
+convert_var_to_array (var)
+     SHELL_VAR *var;
+{
+  char *oldval;
+  ARRAY *array;
+
+  oldval = value_cell (var);
+  array = array_create ();
+  if (oldval)
+    array_insert (array, 0, oldval);
+
+  FREE (value_cell (var));
+  var_setarray (var, array);
+
+  /* these aren't valid anymore */
+  var->dynamic_value = (sh_var_value_func_t *)NULL;
+  var->assign_func = (sh_var_assign_func_t *)NULL;
+
+  INVALIDATE_EXPORTSTR (var);
+
+  VSETATTR (var, att_array);
+  VUNSETATTR (var, att_invisible);
+
+  return var;
+}
+
+/* Perform an array assignment name[ind]=value.  If NAME already exists and
+   is not an array, and IND is 0, perform name=value instead.  If NAME exists
+   and is not an array, and IND is not 0, convert it into an array with the
+   existing value as name[0].
+
+   If NAME does not exist, just create an array variable, no matter what
+   IND's value may be. */
+SHELL_VAR *
+bind_array_variable (name, ind, value)
+     char *name;
+     arrayind_t ind;
+     char *value;
+{
+  SHELL_VAR *entry;
+  char *newval;
+
+  entry = var_lookup (name, shell_variables);
+
+  if (entry == (SHELL_VAR *) 0)
+    entry = make_new_array_variable (name);
+  else if (readonly_p (entry) || noassign_p (entry))
+    {
+      if (readonly_p (entry))
+       err_readonly (name);
+      return (entry);
+    }
+  else if (array_p (entry) == 0)
+    entry = convert_var_to_array (entry);
+
+  /* ENTRY is an array variable, and ARRAY points to the value. */
+  newval = make_variable_value (entry, value);
+  if (entry->assign_func)
+    (*entry->assign_func) (entry, newval, ind);
+  else
+    array_insert (array_cell (entry), ind, newval);
+  FREE (newval);
+
+  return (entry);
+}
+
+/* Parse NAME, a lhs of an assignment statement of the form v[s], and
+   assign VALUE to that array element by calling bind_array_variable(). */
+SHELL_VAR *
+assign_array_element (name, value)
+     char *name, *value;
+{
+  char *sub, *vname;
+  arrayind_t ind;
+  int sublen;
+  SHELL_VAR *entry;
+
+  vname = array_variable_name (name, &sub, &sublen);
+
+  if (vname == 0)
+    return ((SHELL_VAR *)NULL);
+
+  if ((ALL_ELEMENT_SUB (sub[0]) && sub[1] == ']') || (sublen <= 1))
+    {
+      free (vname);
+      err_badarraysub (name);
+      return ((SHELL_VAR *)NULL);
+    }
+
+  ind = array_expand_index (sub, sublen);
+  if (ind < 0)
+    {
+      free (vname);
+      err_badarraysub (name);
+      return ((SHELL_VAR *)NULL);
+    }
+
+  entry = bind_array_variable (vname, ind, value);
+
+  free (vname);
+  return (entry);
+}
+
+/* Find the array variable corresponding to NAME.  If there is no variable,
+   create a new array variable.  If the variable exists but is not an array,
+   convert it to an indexed array.  If CHECK_FLAGS is non-zero, an existing
+   variable is checked for the readonly or noassign attribute in preparation
+   for assignment (e.g., by the `read' builtin). */
+SHELL_VAR *
+find_or_make_array_variable (name, check_flags)
+     char *name;
+     int check_flags;
+{
+  SHELL_VAR *var;
+
+  var = find_variable (name);
+
+  if (var == 0)
+    var = make_new_array_variable (name);
+  else if (check_flags && (readonly_p (var) || noassign_p (var)))
+    {
+      if (readonly_p (var))
+       err_readonly (name);
+      return ((SHELL_VAR *)NULL);
+    }
+  else if (array_p (var) == 0)
+    var = convert_var_to_array (var);
+
+  return (var);
+}
+  
+/* Perform a compound assignment statement for array NAME, where VALUE is
+   the text between the parens:  NAME=( VALUE ) */
+SHELL_VAR *
+assign_array_from_string (name, value)
+     char *name, *value;
+{
+  SHELL_VAR *var;
+
+  var = find_or_make_array_variable (name, 1);
+  if (var == 0)
+    return ((SHELL_VAR *)NULL);
+
+  return (assign_array_var_from_string (var, value));
+}
+
+/* Sequentially assign the indices of indexed array variable VAR from the
+   words in LIST. */
+SHELL_VAR *
+assign_array_var_from_word_list (var, list)
+     SHELL_VAR *var;
+     WORD_LIST *list;
+{
+  register arrayind_t i;
+  register WORD_LIST *l;
+  ARRAY *a;
+
+  for (a = array_cell (var), l = list, i = 0; l; l = l->next, i++)
+    if (var->assign_func)
+      (*var->assign_func) (var, l->word->word, i);
+    else
+      array_insert (a, i, l->word->word);
+  return var;
+}
+
+/* Perform a compound array assignment:  VAR->name=( VALUE ).  The
+   VALUE has already had the parentheses stripped. */
+SHELL_VAR *
+assign_array_var_from_string (var, value)
+     SHELL_VAR *var;
+     char *value;
+{
+  ARRAY *a;
+  WORD_LIST *list, *nlist;
+  char *w, *val, *nval;
+  int ni, len;
+  arrayind_t ind, last_ind;
+
+  if (value == 0)
+    return var;
+
+  /* If this is called from declare_builtin, value[0] == '(' and
+     xstrchr(value, ')') != 0.  In this case, we need to extract
+     the value from between the parens before going on. */
+  if (*value == '(')   /*)*/
+    {
+      ni = 1;
+      val = extract_array_assignment_list (value, &ni);
+      if (val == 0)
+       return var;
+    }
+  else
+    val = value;
+
+  /* Expand the value string into a list of words, performing all the
+     shell expansions including pathname generation and word splitting. */
+  /* First we split the string on whitespace, using the shell parser
+     (ksh93 seems to do this). */
+  list = parse_string_to_word_list (val, 1, "array assign");
+
+  /* If we're using [subscript]=value, we need to quote each [ and ] to
+     prevent unwanted filename expansion. */
+  if (list)
+    quote_array_assignment_chars (list);
+
+  /* Now that we've split it, perform the shell expansions on each
+     word in the list. */
+  nlist = list ? expand_words_no_vars (list) : (WORD_LIST *)NULL;
+
+  dispose_words (list);
+
+  if (val != value)
+    free (val);
+
+  a = array_cell (var);
+
+  /* Now that we are ready to assign values to the array, kill the existing
+     value. */
+  if (a)
+    array_flush (a);
+
+  for (last_ind = 0, list = nlist; list; list = list->next)
+    {
+      w = list->word->word;
+
+      /* We have a word of the form [ind]=value */
+      if ((list->word->flags & W_ASSIGNMENT) && w[0] == '[')
+       {
+         len = skipsubscript (w, 0);
+
+         if (w[len] != ']' || w[len+1] != '=')
+           {
+             nval = make_variable_value (var, w);
+             if (var->assign_func)
+               (*var->assign_func) (var, nval, last_ind);
+             else
+               array_insert (a, last_ind, nval);
+             FREE (nval);
+             last_ind++;
+             continue;
+           }
+
+         if (len == 1)
+           {
+             err_badarraysub (w);
+             continue;
+           }
+
+         if (ALL_ELEMENT_SUB (w[1]) && len == 2)
+           {
+             report_error (_("%s: cannot assign to non-numeric index"), w);
+             continue;
+           }
+
+         ind = array_expand_index (w + 1, len);
+         if (ind < 0)
+           {
+             err_badarraysub (w);
+             continue;
+           }
+         last_ind = ind;
+         val = w + len + 2;
+       }
+      else             /* No [ind]=value, just a stray `=' */
+       {
+         ind = last_ind;
+         val = w;
+       }
+
+      if (integer_p (var))
+       this_command_name = (char *)NULL;       /* no command name for errors */
+      nval = make_variable_value (var, val);
+      if (var->assign_func)
+       (*var->assign_func) (var, nval, ind);
+      else
+       array_insert (a, ind, nval);
+      FREE (nval);
+      last_ind++;
+    }
+
+  dispose_words (nlist);
+  return (var);
+}
+
+/* For each word in a compound array assignment, if the word looks like
+   [ind]=value, quote the `[' and `]' before the `=' to protect them from
+   unwanted filename expansion. */
+static void
+quote_array_assignment_chars (list)
+     WORD_LIST *list;
+{
+  char *s, *t, *nword;
+  int saw_eq;
+  WORD_LIST *l;
+
+  for (l = list; l; l = l->next)
+    {
+      if (l->word == 0 || l->word->word == 0 || l->word->word[0] == '\0')
+       continue;       /* should not happen, but just in case... */
+      /* Don't bother if it doesn't look like [ind]=value */
+      if (l->word->word[0] != '[' || xstrchr (l->word->word, '=') == 0) /* ] */
+       continue;
+      s = nword = (char *)xmalloc (strlen (l->word->word) * 2 + 1);
+      saw_eq = 0;
+      for (t = l->word->word; *t; )
+       {
+         if (*t == '=')
+           saw_eq = 1;
+         if (saw_eq == 0 && (*t == '[' || *t == ']'))
+           *s++ = '\\';
+         *s++ = *t++;
+       }
+      *s = '\0';
+      free (l->word->word);
+      l->word->word = nword;
+    }
+}
+
+/* This function assumes s[i] == '['; returns with s[ret] == ']' if
+   an array subscript is correctly parsed. */
+int
+skipsubscript (s, i)
+     const char *s;
+     int i;
+{
+  int count, c;
+#if defined (HANDLE_MULTIBYTE)
+  mbstate_t state, state_bak;
+  size_t slength, mblength;
+  size_t mb_cur_max;
+#endif
+
+#if defined (HANDLE_MULTIBYTE)
+  memset (&state, '\0', sizeof (mbstate_t));
+  slength = strlen (s + i);
+  mb_cur_max = MB_CUR_MAX;
+#endif
+  
+  count = 1;
+  while (count)
+    {
+      /* Advance one (possibly multibyte) character in S starting at I. */
+#if defined (HANDLE_MULTIBYTE)
+      if (mb_cur_max > 1)
+       {
+         state_bak = state;
+         mblength = mbrlen (s + i, slength, &state);
+
+         if (MB_INVALIDCH (mblength))
+           {
+             state = state_bak;
+             i++;
+             slength--;
+           }
+         else if (MB_NULLWCH (mblength))
+           return i;
+         else
+           {
+             i += mblength;
+             slength -= mblength;
+           }
+       }
+      else
+#endif
+      ++i;
+
+      c = s[i];
+
+      if (c == 0)
+        break;
+      else if (c == '[')
+       count++;
+      else if (c == ']')
+       count--;
+    }
+
+  return i;
+}
+
+/* This function is called with SUB pointing to just after the beginning
+   `[' of an array subscript and removes the array element to which SUB
+   expands from array VAR.  A subscript of `*' or `@' unsets the array. */
+int
+unbind_array_element (var, sub)
+     SHELL_VAR *var;
+     char *sub;
+{
+  int len;
+  arrayind_t ind;
+  ARRAY_ELEMENT *ae;
+
+  len = skipsubscript (sub, 0);
+  if (sub[len] != ']' || len == 0)
+    {
+      builtin_error ("%s[%s: %s", var->name, sub, _(bash_badsub_errmsg));
+      return -1;
+    }
+  sub[len] = '\0';
+
+  if (ALL_ELEMENT_SUB (sub[0]) && sub[1] == 0)
+    {
+      unbind_variable (var->name);
+      return (0);
+    }
+  ind = array_expand_index (sub, len+1);
+  if (ind < 0)
+    {
+      builtin_error ("[%s]: %s", sub, _(bash_badsub_errmsg));
+      return -1;
+    }
+  ae = array_remove (array_cell (var), ind);
+  if (ae)
+    array_dispose_element (ae);
+  return 0;
+}
+
+/* Format and output an array assignment in compound form VAR=(VALUES),
+   suitable for re-use as input. */
+void
+print_array_assignment (var, quoted)
+     SHELL_VAR *var;
+     int quoted;
+{
+  char *vstr;
+
+  vstr = array_to_assign (array_cell (var), quoted);
+
+  if (vstr == 0)
+    printf ("%s=%s\n", var->name, quoted ? "'()'" : "()");
+  else
+    {
+      printf ("%s=%s\n", var->name, vstr);
+      free (vstr);
+    }
+}
+
+/***********************************************************************/
+/*                                                                    */
+/* Utility functions to manage arrays and their contents for expansion */
+/*                                                                    */
+/***********************************************************************/
+
+/* Return 1 if NAME is a properly-formed array reference v[sub]. */
+int
+valid_array_reference (name)
+     char *name;
+{
+  char *t;
+  int r, len;
+
+  t = xstrchr (name, '[');     /* ] */
+  if (t)
+    {
+      *t = '\0';
+      r = legal_identifier (name);
+      *t = '[';
+      if (r == 0)
+       return 0;
+      /* Check for a properly-terminated non-blank subscript. */
+      len = skipsubscript (t, 0);
+      if (t[len] != ']' || len == 1)
+       return 0;
+      for (r = 1; r < len; r++)
+       if (whitespace (t[r]) == 0)
+         return 1;
+      return 0;
+    }
+  return 0;
+}
+
+/* Expand the array index beginning at S and extending LEN characters. */
+arrayind_t
+array_expand_index (s, len)
+     char *s;
+     int len;
+{
+  char *exp, *t;
+  int expok;
+  arrayind_t val;
+
+  exp = (char *)xmalloc (len);
+  strncpy (exp, s, len - 1);
+  exp[len - 1] = '\0';
+  t = expand_string_to_string (exp, 0);
+  this_command_name = (char *)NULL;
+  val = evalexp (t, &expok);
+  free (t);
+  free (exp);
+  if (expok == 0)
+    {
+      last_command_exit_value = EXECUTION_FAILURE;
+      jump_to_top_level (DISCARD);
+    }
+  return val;
+}
+
+/* Return the name of the variable specified by S without any subscript.
+   If SUBP is non-null, return a pointer to the start of the subscript
+   in *SUBP. If LENP is non-null, the length of the subscript is returned
+   in *LENP.  This returns newly-allocated memory. */
+char *
+array_variable_name (s, subp, lenp)
+     char *s, **subp;
+     int *lenp;
+{
+  char *t, *ret;
+  int ind, ni;
+
+  t = xstrchr (s, '[');
+  if (t == 0)
+    {
+      if (subp)
+       *subp = t;
+      if (lenp)
+       *lenp = 0;
+      return ((char *)NULL);
+    }
+  ind = t - s;
+  ni = skipsubscript (s, ind);
+  if (ni <= ind + 1 || s[ni] != ']')
+    {
+      err_badarraysub (s);
+      if (subp)
+       *subp = t;
+      if (lenp)
+       *lenp = 0;
+      return ((char *)NULL);
+    }
+
+  *t = '\0';
+  ret = savestring (s);
+  *t++ = '[';          /* ] */
+
+  if (subp)
+    *subp = t;
+  if (lenp)
+    *lenp = ni - ind;
+
+  return ret;
+}
+
+/* Return the variable specified by S without any subscript.  If SUBP is
+   non-null, return a pointer to the start of the subscript in *SUBP.
+   If LENP is non-null, the length of the subscript is returned in *LENP. */
+SHELL_VAR *
+array_variable_part (s, subp, lenp)
+     char *s, **subp;
+     int *lenp;
+{
+  char *t;
+  SHELL_VAR *var;
+
+  t = array_variable_name (s, subp, lenp);
+  if (t == 0)
+    return ((SHELL_VAR *)NULL);
+  var = find_variable (t);
+
+  free (t);
+  return var;
+}
+
+/* Return a string containing the elements in the array and subscript
+   described by S.  If the subscript is * or @, obeys quoting rules akin
+   to the expansion of $* and $@ including double quoting.  If RTYPE
+   is non-null it gets 1 if the array reference is name[@] or name[*]
+   and 0 otherwise. */
+static char *
+array_value_internal (s, quoted, allow_all, rtype)
+     char *s;
+     int quoted, allow_all, *rtype;
+{
+  int len;
+  arrayind_t ind;
+  char *retval, *t, *temp;
+  WORD_LIST *l;
+  SHELL_VAR *var;
+
+  var = array_variable_part (s, &t, &len);
+
+  /* Expand the index, even if the variable doesn't exist, in case side
+     effects are needed, like ${w[i++]} where w is unset. */
+#if 0
+  if (var == 0)
+    return (char *)NULL;
+#endif
+
+  if (len == 0)
+    return ((char *)NULL);     /* error message already printed */
+
+  /* [ */
+  if (ALL_ELEMENT_SUB (t[0]) && t[1] == ']')
+    {
+      if (rtype)
+       *rtype = 1;
+      if (allow_all == 0)
+       {
+         err_badarraysub (s);
+         return ((char *)NULL);
+       }
+      else if (var == 0)
+       return ((char *)NULL);
+      else if (array_p (var) == 0)
+       l = add_string_to_list (value_cell (var), (WORD_LIST *)NULL);
+      else
+       {
+         l = array_to_word_list (array_cell (var));
+         if (l == (WORD_LIST *)NULL)
+           return ((char *) NULL);
+       }
+
+      if (t[0] == '*' && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
+       {
+         temp = string_list_dollar_star (l);
+         retval = quote_string (temp);
+         free (temp);
+       }
+      else     /* ${name[@]} or unquoted ${name[*]} */
+       retval = string_list_dollar_at (l, quoted);
+
+      dispose_words (l);
+    }
+  else
+    {
+      if (rtype)
+       *rtype = 0;
+      ind = array_expand_index (t, len);
+      if (ind < 0)
+       {
+         if (var)
+           err_badarraysub (var->name);
+         else
+           {
+             t[-1] = '\0';
+             err_badarraysub (s);
+             t[-1] = '[';      /* ] */
+           }
+         return ((char *)NULL);
+       }
+      if (var == 0)
+       return ((char *)NULL);
+      if (array_p (var) == 0)
+       return (ind == 0 ? value_cell (var) : (char *)NULL);
+      retval = array_reference (array_cell (var), ind);
+    }
+
+  return retval;
+}
+
+/* Return a string containing the elements described by the array and
+   subscript contained in S, obeying quoting for subscripts * and @. */
+char *
+array_value (s, quoted, rtype)
+     char *s;
+     int quoted, *rtype;
+{
+  return (array_value_internal (s, quoted, 1, rtype));
+}
+
+/* Return the value of the array indexing expression S as a single string.
+   If ALLOW_ALL is 0, do not allow `@' and `*' subscripts.  This is used
+   by other parts of the shell such as the arithmetic expression evaluator
+   in expr.c. */
+char *
+get_array_value (s, allow_all, rtype)
+     char *s;
+     int allow_all, *rtype;
+{
+  return (array_value_internal (s, 0, allow_all, rtype));
+}
+
+char *
+array_keys (s, quoted)
+     char *s;
+     int quoted;
+{
+  int len;
+  char *retval, *t, *temp;
+  WORD_LIST *l;
+  SHELL_VAR *var;
+
+  var = array_variable_part (s, &t, &len);
+
+  /* [ */
+  if (var == 0 || ALL_ELEMENT_SUB (t[0]) == 0 || t[1] != ']')
+    return (char *)NULL;
+
+  if (array_p (var) == 0)
+    l = add_string_to_list ("0", (WORD_LIST *)NULL);
+  else
+    {
+      l = array_keys_to_word_list (array_cell (var));
+      if (l == (WORD_LIST *)NULL)
+        return ((char *) NULL);
+    }
+
+  if (t[0] == '*' && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
+    {
+      temp = string_list_dollar_star (l);
+      retval = quote_string (temp);
+      free (temp);
+    }
+  else /* ${!name[@]} or unquoted ${!name[*]} */
+    retval = string_list_dollar_at (l, quoted);
+
+  dispose_words (l);
+  return retval;
+}
+#endif /* ARRAY_VARS */
index 6e8c81410270aaf566006317af8720b00f9cf4c0..667a97ebace612e58a4efc00f4965b45685aa12f 100644 (file)
@@ -2513,7 +2513,7 @@ glob_complete_word (text, state)
   static char **matches = (char **)NULL;
   static int ind;
   int glen;
-  char *ret;
+  char *ret, *ttext;
 
   if (state == 0)
     {
@@ -2523,17 +2523,22 @@ glob_complete_word (text, state)
        FREE (globorig);
       FREE (globtext);
 
+      ttext = bash_tilde_expand (text, 0);
+
       if (rl_explicit_arg)
        {
-         globorig = savestring (text);
-         glen = strlen (text);
+         globorig = savestring (ttext);
+         glen = strlen (ttext);
          globtext = (char *)xmalloc (glen + 2);
-         strcpy (globtext, text);
+         strcpy (globtext, ttext);
          globtext[glen] = '*';
          globtext[glen+1] = '\0';
        }
       else
-        globtext = globorig = savestring (text);
+        globtext = globorig = savestring (ttext);
+
+      if (ttext != text)
+       free (ttext);
 
       matches = shell_glob_filename (globtext);
       if (GLOB_FAILED (matches))
diff --git a/bashline.c~ b/bashline.c~
new file mode 100644 (file)
index 0000000..6e8c814
--- /dev/null
@@ -0,0 +1,3053 @@
+/* bashline.c -- Bash's interface to the readline library. */
+
+/* Copyright (C) 1987-2004 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 2, 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, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+
+#include "config.h"
+
+#if defined (READLINE)
+
+#include "bashtypes.h"
+#include "posixstat.h"
+
+#if defined (HAVE_UNISTD_H)
+#  include <unistd.h>
+#endif
+
+#if defined (HAVE_GRP_H)
+#  include <grp.h>
+#endif
+
+#if defined (HAVE_NETDB_H)
+#  include <netdb.h>
+#endif
+
+#include <stdio.h>
+#include "chartypes.h"
+#include "bashansi.h"
+#include "bashintl.h"
+
+#include "shell.h"
+#include "input.h"
+#include "builtins.h"
+#include "bashhist.h"
+#include "bashline.h"
+#include "execute_cmd.h"
+#include "findcmd.h"
+#include "pathexp.h"
+#include "builtins/common.h"
+#include <readline/rlconf.h>
+#include <readline/readline.h>
+#include <readline/history.h>
+
+#include <glob/glob.h>
+
+#if defined (ALIAS)
+#  include "alias.h"
+#endif
+
+#if defined (PROGRAMMABLE_COMPLETION)
+#  include "pcomplete.h"
+#endif
+
+/* These should agree with the defines for emacs_mode and vi_mode in
+   rldefs.h, even though that's not a public readline header file. */
+#ifndef EMACS_EDITING_MODE
+#  define NO_EDITING_MODE      -1
+#  define EMACS_EDITING_MODE    1
+#  define VI_EDITING_MODE       0
+#endif
+
+#if defined (BRACE_COMPLETION)
+extern int bash_brace_completion __P((int, int));
+#endif /* BRACE_COMPLETION */
+
+/* Forward declarations */
+
+/* Functions bound to keys in Readline for Bash users. */
+static int shell_expand_line __P((int, int));
+static int display_shell_version __P((int, int));
+static int operate_and_get_next __P((int, int));
+
+static int bash_ignore_filenames __P((char **));
+static int bash_ignore_everything __P((char **));
+
+#if defined (BANG_HISTORY)
+static char *history_expand_line_internal __P((char *));
+static int history_expand_line __P((int, int));
+static int tcsh_magic_space __P((int, int));
+#endif /* BANG_HISTORY */
+#ifdef ALIAS
+static int alias_expand_line __P((int, int));
+#endif
+#if defined (BANG_HISTORY) && defined (ALIAS)
+static int history_and_alias_expand_line __P((int, int));
+#endif
+
+/* Helper functions for Readline. */
+static int bash_directory_completion_hook __P((char **));
+static int filename_completion_ignore __P((char **));
+static int bash_push_line __P((void));
+
+static void cleanup_expansion_error __P((void));
+static void maybe_make_readline_line __P((char *));
+static void set_up_new_line __P((char *));
+
+static int check_redir __P((int));
+static char **attempt_shell_completion __P((const char *, int, int));
+static char *variable_completion_function __P((const char *, int));
+static char *hostname_completion_function __P((const char *, int));
+static char *command_subst_completion_function __P((const char *, int));
+
+static void build_history_completion_array __P((void));
+static char *history_completion_generator __P((const char *, int));
+static int dynamic_complete_history __P((int, int));
+
+static void initialize_hostname_list __P((void));
+static void add_host_name __P((char *));
+static void snarf_hosts_from_file __P((char *));
+static char **hostnames_matching __P((char *));
+
+static void _ignore_completion_names __P((char **, sh_ignore_func_t *));
+static int name_is_acceptable __P((const char *));
+static int test_for_directory __P((const char *));
+static int return_zero __P((const char *));
+
+static char *bash_dequote_filename __P((char *, int));
+static char *quote_word_break_chars __P((char *));
+static char *bash_quote_filename __P((char *, int, char *));
+
+static int bash_execute_unix_command __P((int, int));
+static void init_unix_command_map __P((void));
+static int isolate_sequence __P((char *, int, int, int *));
+
+static int set_saved_history __P((void));
+
+#if defined (ALIAS)
+static int posix_edit_macros __P((int, int));
+#endif
+
+#if defined (PROGRAMMABLE_COMPLETION)
+static int find_cmd_start __P((int));
+static int find_cmd_end __P((int));
+static char *find_cmd_name __P((int));
+static char *prog_complete_return __P((const char *, int));
+
+static char **prog_complete_matches;
+#endif
+
+/* Variables used here but defined in other files. */
+#if defined (BANG_HISTORY)
+extern int hist_verify;
+#endif
+
+extern int current_command_line_count, last_command_exit_value;
+extern int posixly_correct, no_symbolic_links;
+extern char *current_prompt_string, *ps1_prompt;
+extern STRING_INT_ALIST word_token_alist[];
+extern sh_builtin_func_t *last_shell_builtin, *this_shell_builtin;
+
+/* SPECIFIC_COMPLETION_FUNCTIONS specifies that we have individual
+   completion functions which indicate what type of completion should be
+   done (at or before point) that can be bound to key sequences with
+   the readline library. */
+#define SPECIFIC_COMPLETION_FUNCTIONS
+
+#if defined (SPECIFIC_COMPLETION_FUNCTIONS)
+static int bash_specific_completion __P((int, rl_compentry_func_t *));
+
+static int bash_complete_filename_internal __P((int));
+static int bash_complete_username_internal __P((int));
+static int bash_complete_hostname_internal __P((int));
+static int bash_complete_variable_internal __P((int));
+static int bash_complete_command_internal __P((int));
+
+static int bash_complete_filename __P((int, int));
+static int bash_possible_filename_completions __P((int, int));
+static int bash_complete_username __P((int, int));
+static int bash_possible_username_completions __P((int, int));
+static int bash_complete_hostname __P((int, int));
+static int bash_possible_hostname_completions __P((int, int));
+static int bash_complete_variable __P((int, int));
+static int bash_possible_variable_completions __P((int, int));
+static int bash_complete_command __P((int, int));
+static int bash_possible_command_completions __P((int, int));
+
+static char *glob_complete_word __P((const char *, int));
+static int bash_glob_completion_internal __P((int));
+static int bash_glob_complete_word __P((int, int));
+static int bash_glob_expand_word __P((int, int));
+static int bash_glob_list_expansions __P((int, int));
+
+#endif /* SPECIFIC_COMPLETION_FUNCTIONS */
+
+static int edit_and_execute_command __P((int, int, int, char *));
+#if defined (VI_MODE)
+static int vi_edit_and_execute_command __P((int, int));
+static int bash_vi_complete __P((int, int));
+#endif
+static int emacs_edit_and_execute_command __P((int, int));
+
+/* Non-zero once initalize_readline () has been called. */
+int bash_readline_initialized = 0;
+
+/* If non-zero, we do hostname completion, breaking words at `@' and
+   trying to complete the stuff after the `@' from our own internal
+   host list. */
+int perform_hostname_completion = 1;
+
+/* If non-zero, we don't do command completion on an empty line. */
+int no_empty_command_completion;
+
+/* Set FORCE_FIGNORE if you want to honor FIGNORE even if it ignores the
+   only possible matches.  Set to 0 if you want to match filenames if they
+   are the only possible matches, even if FIGNORE says to. */
+int force_fignore = 1;
+
+static char *bash_completer_word_break_characters = " \t\n\"'@><=;|&(:";
+static char *bash_nohostname_word_break_characters = " \t\n\"'><=;|&(:";
+/* )) */
+
+static rl_hook_func_t *old_rl_startup_hook = (rl_hook_func_t *)NULL;
+
+/* What kind of quoting is performed by bash_quote_filename:
+       COMPLETE_DQUOTE = double-quoting the filename
+       COMPLETE_SQUOTE = single_quoting the filename
+       COMPLETE_BSQUOTE = backslash-quoting special chars in the filename
+*/
+#define COMPLETE_DQUOTE  1
+#define COMPLETE_SQUOTE  2
+#define COMPLETE_BSQUOTE 3
+static int completion_quoting_style = COMPLETE_BSQUOTE;
+
+/* Change the readline VI-mode keymaps into or out of Posix.2 compliance.
+   Called when the shell is put into or out of `posix' mode. */
+void
+posix_readline_initialize (on_or_off)
+     int on_or_off;
+{
+  if (on_or_off)
+    rl_variable_bind ("comment-begin", "#");
+#if defined (VI_MODE)
+  rl_bind_key_in_map (CTRL ('I'), on_or_off ? rl_insert : rl_complete, vi_insertion_keymap);
+#endif
+}
+
+/* When this function returns, rl_completer_word_break_characters points to
+   dynamically allocated memory. */
+int
+enable_hostname_completion (on_or_off)
+     int on_or_off;
+{
+  int old_value;
+  char *at, *nv, *nval;
+
+  old_value = perform_hostname_completion;
+
+  if (on_or_off)
+    {
+      perform_hostname_completion = 1;
+      rl_special_prefixes = "$@";
+    }
+  else
+    {
+      perform_hostname_completion = 0;
+      rl_special_prefixes = "$";
+    }
+
+  /* Now we need to figure out how to appropriately modify and assign
+     rl_completer_word_break_characters depending on whether we want
+     hostname completion on or off. */
+
+  /* If this is the first time this has been called
+     (bash_readline_initialized == 0), use the sames values as before, but
+     allocate new memory for rl_completer_word_break_characters. */
+
+  if (bash_readline_initialized == 0 &&
+      (rl_completer_word_break_characters == 0 || 
+       rl_completer_word_break_characters == rl_basic_word_break_characters))
+    {
+      if (on_or_off)
+       rl_completer_word_break_characters = savestring (bash_completer_word_break_characters);
+      else
+       rl_completer_word_break_characters = savestring (bash_nohostname_word_break_characters);
+    }
+  else
+    {
+      /* See if we have anything to do. */
+      at = strchr (rl_completer_word_break_characters, '@');
+      if ((at == 0 && on_or_off == 0) || (at != 0 && on_or_off != 0))
+        return old_value;
+
+      /* We have something to do.  Do it. */
+      nval = (char *)xmalloc (strlen (rl_completer_word_break_characters) + 1 + on_or_off);
+
+      if (on_or_off == 0)
+       {
+         /* Turn it off -- just remove `@' from word break chars.  We want
+            to remove all occurrences of `@' from the char list, so we loop
+            rather than just copy the rest of the list over AT. */
+         for (nv = nval, at = rl_completer_word_break_characters; *at; )
+           if (*at != '@')
+             *nv++ = *at++;
+           else
+             at++;
+         *nv = '\0';
+       }
+      else
+       {
+         nval[0] = '@';
+         strcpy (nval + 1, rl_completer_word_break_characters);
+        }
+
+      free (rl_completer_word_break_characters);
+      rl_completer_word_break_characters = nval;
+    }
+
+  return (old_value);
+}
+
+/* Called once from parse.y if we are going to use readline. */
+void
+initialize_readline ()
+{
+  rl_command_func_t *func;
+  char kseq[2];
+
+  if (bash_readline_initialized)
+    return;
+
+  rl_terminal_name = get_string_value ("TERM");
+  rl_instream = stdin;
+  rl_outstream = stderr;
+
+  /* Allow conditional parsing of the ~/.inputrc file. */
+  rl_readline_name = "Bash";
+
+  /* Add bindable names before calling rl_initialize so they may be
+     referenced in the various inputrc files. */
+  rl_add_defun ("shell-expand-line", shell_expand_line, -1);
+#ifdef BANG_HISTORY
+  rl_add_defun ("history-expand-line", history_expand_line, -1);
+  rl_add_defun ("magic-space", tcsh_magic_space, -1);
+#endif
+
+#ifdef ALIAS
+  rl_add_defun ("alias-expand-line", alias_expand_line, -1);
+#  ifdef BANG_HISTORY
+  rl_add_defun ("history-and-alias-expand-line", history_and_alias_expand_line, -1);
+#  endif
+#endif
+
+  /* Backwards compatibility. */
+  rl_add_defun ("insert-last-argument", rl_yank_last_arg, -1);
+
+  rl_add_defun ("operate-and-get-next", operate_and_get_next, -1);
+  rl_add_defun ("display-shell-version", display_shell_version, -1);
+  rl_add_defun ("edit-and-execute-command", emacs_edit_and_execute_command, -1);
+
+#if defined (BRACE_COMPLETION)
+  rl_add_defun ("complete-into-braces", bash_brace_completion, -1);
+#endif
+
+#if defined (SPECIFIC_COMPLETION_FUNCTIONS)
+  rl_add_defun ("complete-filename", bash_complete_filename, -1);
+  rl_add_defun ("possible-filename-completions", bash_possible_filename_completions, -1);
+  rl_add_defun ("complete-username", bash_complete_username, -1);
+  rl_add_defun ("possible-username-completions", bash_possible_username_completions, -1);
+  rl_add_defun ("complete-hostname", bash_complete_hostname, -1);
+  rl_add_defun ("possible-hostname-completions", bash_possible_hostname_completions, -1);
+  rl_add_defun ("complete-variable", bash_complete_variable, -1);
+  rl_add_defun ("possible-variable-completions", bash_possible_variable_completions, -1);
+  rl_add_defun ("complete-command", bash_complete_command, -1);
+  rl_add_defun ("possible-command-completions", bash_possible_command_completions, -1);
+  rl_add_defun ("glob-complete-word", bash_glob_complete_word, -1);
+  rl_add_defun ("glob-expand-word", bash_glob_expand_word, -1);
+  rl_add_defun ("glob-list-expansions", bash_glob_list_expansions, -1);
+#endif
+
+  rl_add_defun ("dynamic-complete-history", dynamic_complete_history, -1);
+
+  /* Bind defaults before binding our custom shell keybindings. */
+  if (RL_ISSTATE(RL_STATE_INITIALIZED) == 0)
+    rl_initialize ();
+
+  /* Bind up our special shell functions. */
+  rl_bind_key_if_unbound_in_map (CTRL('E'), shell_expand_line, emacs_meta_keymap);
+
+#ifdef BANG_HISTORY
+  rl_bind_key_if_unbound_in_map ('^', history_expand_line, emacs_meta_keymap);
+#endif
+
+  rl_bind_key_if_unbound_in_map (CTRL ('O'), operate_and_get_next, emacs_standard_keymap);
+  rl_bind_key_if_unbound_in_map (CTRL ('V'), display_shell_version, emacs_ctlx_keymap);
+
+  /* In Bash, the user can switch editing modes with "set -o [vi emacs]",
+     so it is not necessary to allow C-M-j for context switching.  Turn
+     off this occasionally confusing behaviour. */
+  kseq[0] = CTRL('J');
+  kseq[1] = '\0';
+  func = rl_function_of_keyseq (kseq, emacs_meta_keymap, (int *)NULL);
+  if (func == rl_vi_editing_mode)
+    rl_unbind_key_in_map (CTRL('J'), emacs_meta_keymap);
+  kseq[0] = CTRL('M');
+  func = rl_function_of_keyseq (kseq, emacs_meta_keymap, (int *)NULL);
+  if (func == rl_vi_editing_mode)
+    rl_unbind_key_in_map (CTRL('M'), emacs_meta_keymap);
+#if defined (VI_MODE)
+  rl_unbind_key_in_map (CTRL('E'), vi_movement_keymap);
+#endif
+
+#if defined (BRACE_COMPLETION)
+  rl_bind_key_if_unbound_in_map ('{', bash_brace_completion, emacs_meta_keymap); /*}*/
+#endif /* BRACE_COMPLETION */
+
+#if defined (SPECIFIC_COMPLETION_FUNCTIONS)
+  rl_bind_key_if_unbound_in_map ('/', bash_complete_filename, emacs_meta_keymap);
+  rl_bind_key_if_unbound_in_map ('/', bash_possible_filename_completions, emacs_ctlx_keymap);
+
+  /* Have to jump through hoops here because there is a default binding for
+     M-~ (rl_tilde_expand) */
+  kseq[0] = '~';
+  kseq[1] = '\0';
+  func = rl_function_of_keyseq (kseq, emacs_meta_keymap, (int *)NULL);
+  if (func == 0 || func == rl_tilde_expand)
+    rl_bind_keyseq_in_map (kseq, bash_complete_username, emacs_meta_keymap);
+
+  rl_bind_key_if_unbound_in_map ('~', bash_possible_username_completions, emacs_ctlx_keymap);
+
+  rl_bind_key_if_unbound_in_map ('@', bash_complete_hostname, emacs_meta_keymap);
+  rl_bind_key_if_unbound_in_map ('@', bash_possible_hostname_completions, emacs_ctlx_keymap);
+
+  rl_bind_key_if_unbound_in_map ('$', bash_complete_variable, emacs_meta_keymap);
+  rl_bind_key_if_unbound_in_map ('$', bash_possible_variable_completions, emacs_ctlx_keymap);
+
+  rl_bind_key_if_unbound_in_map ('!', bash_complete_command, emacs_meta_keymap);
+  rl_bind_key_if_unbound_in_map ('!', bash_possible_command_completions, emacs_ctlx_keymap);
+
+  rl_bind_key_if_unbound_in_map ('g', bash_glob_complete_word, emacs_meta_keymap);
+  rl_bind_key_if_unbound_in_map ('*', bash_glob_expand_word, emacs_ctlx_keymap);
+  rl_bind_key_if_unbound_in_map ('g', bash_glob_list_expansions, emacs_ctlx_keymap);
+
+#endif /* SPECIFIC_COMPLETION_FUNCTIONS */
+
+  rl_bind_key_if_unbound_in_map (TAB, dynamic_complete_history, emacs_meta_keymap);
+
+  /* Tell the completer that we want a crack first. */
+  rl_attempted_completion_function = attempt_shell_completion;
+
+  /* Tell the completer that we might want to follow symbolic links or
+     do other expansion on directory names. */
+  rl_directory_completion_hook = bash_directory_completion_hook;
+
+  /* Tell the filename completer we want a chance to ignore some names. */
+  rl_ignore_some_completions_function = filename_completion_ignore;
+
+  /* Bind C-xC-e to invoke emacs and run result as commands. */
+  rl_bind_key_if_unbound_in_map (CTRL ('E'), emacs_edit_and_execute_command, emacs_ctlx_keymap);
+#if defined (VI_MODE)
+  rl_bind_key_if_unbound_in_map ('v', vi_edit_and_execute_command, vi_movement_keymap);
+#  if defined (ALIAS)
+  rl_bind_key_if_unbound_in_map ('@', posix_edit_macros, vi_movement_keymap);
+#  endif
+
+  rl_bind_key_in_map ('\\', bash_vi_complete, vi_movement_keymap);
+  rl_bind_key_in_map ('*', bash_vi_complete, vi_movement_keymap);
+  rl_bind_key_in_map ('=', bash_vi_complete, vi_movement_keymap);
+#endif
+
+  rl_completer_quote_characters = "'\"";
+
+  /* This sets rl_completer_word_break_characters and rl_special_prefixes
+     to the appropriate values, depending on whether or not hostname
+     completion is enabled. */
+  enable_hostname_completion (perform_hostname_completion);
+
+  /* characters that need to be quoted when appearing in filenames. */
+  rl_filename_quote_characters = " \t\n\\\"'@<>=;|&()#$`?*[!:{";       /*}*/
+  rl_filename_quoting_function = bash_quote_filename;
+  rl_filename_dequoting_function = bash_dequote_filename;
+  rl_char_is_quoted_p = char_is_quoted;
+
+#if 0
+  /* This is superfluous and makes it impossible to use tab completion in
+     vi mode even when explicitly binding it in ~/.inputrc.  sv_strict_posix()
+     should already have called posix_readline_initialize() when
+     posixly_correct was set. */
+  if (posixly_correct)
+    posix_readline_initialize (1);
+#endif
+
+  bash_readline_initialized = 1;
+}
+
+/* On Sun systems at least, rl_attempted_completion_function can end up
+   getting set to NULL, and rl_completion_entry_function set to do command
+   word completion if Bash is interrupted while trying to complete a command
+   word.  This just resets all the completion functions to the right thing.
+   It's called from throw_to_top_level(). */
+void
+bashline_reinitialize ()
+{
+  tilde_initialize ();
+  rl_attempted_completion_function = attempt_shell_completion;
+  rl_completion_entry_function = NULL;
+  rl_directory_completion_hook = bash_directory_completion_hook;
+  rl_ignore_some_completions_function = filename_completion_ignore;
+}
+
+/* Contains the line to push into readline. */
+static char *push_to_readline = (char *)NULL;
+
+/* Push the contents of push_to_readline into the
+   readline buffer. */
+static int
+bash_push_line ()
+{
+  if (push_to_readline)
+    {
+      rl_insert_text (push_to_readline);
+      free (push_to_readline);
+      push_to_readline = (char *)NULL;
+      rl_startup_hook = old_rl_startup_hook;
+    }
+  return 0;
+}
+
+/* Call this to set the initial text for the next line to read
+   from readline. */
+int
+bash_re_edit (line)
+     char *line;
+{
+  FREE (push_to_readline);
+
+  push_to_readline = savestring (line);
+  old_rl_startup_hook = rl_startup_hook;
+  rl_startup_hook = bash_push_line;
+
+  return (0);
+}
+
+static int
+display_shell_version (count, c)
+     int count, c;
+{
+  rl_crlf ();
+  show_shell_version (0);
+  putc ('\r', rl_outstream);
+  fflush (rl_outstream);
+  rl_on_new_line ();
+  rl_redisplay ();
+  return 0;
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*                          Readline Stuff                         */
+/*                                                                 */
+/* **************************************************************** */
+
+/* If the user requests hostname completion, then simply build a list
+   of hosts, and complete from that forever more, or at least until
+   HOSTFILE is unset. */
+
+/* THIS SHOULD BE A STRINGLIST. */
+/* The kept list of hostnames. */
+static char **hostname_list = (char **)NULL;
+
+/* The physical size of the above list. */
+static int hostname_list_size;
+
+/* The number of hostnames in the above list. */
+static int hostname_list_length;
+
+/* Whether or not HOSTNAME_LIST has been initialized. */
+int hostname_list_initialized = 0;
+
+/* Initialize the hostname completion table. */
+static void
+initialize_hostname_list ()
+{
+  char *temp;
+
+  temp = get_string_value ("HOSTFILE");
+  if (temp == 0)
+    temp = get_string_value ("hostname_completion_file");
+  if (temp == 0)
+    temp = DEFAULT_HOSTS_FILE;
+
+  snarf_hosts_from_file (temp);
+
+  if (hostname_list)
+    hostname_list_initialized++;
+}
+
+/* Add NAME to the list of hosts. */
+static void
+add_host_name (name)
+     char *name;
+{
+  if (hostname_list_length + 2 > hostname_list_size)
+    {
+      hostname_list_size = (hostname_list_size + 32) - (hostname_list_size % 32);
+      hostname_list = strvec_resize (hostname_list, hostname_list_size);
+    }
+
+  hostname_list[hostname_list_length++] = savestring (name);
+  hostname_list[hostname_list_length] = (char *)NULL;
+}
+
+#define cr_whitespace(c) ((c) == '\r' || (c) == '\n' || whitespace(c))
+
+static void
+snarf_hosts_from_file (filename)
+     char *filename;
+{
+  FILE *file;
+  char *temp, buffer[256], name[256];
+  register int i, start;
+
+  file = fopen (filename, "r");
+  if (file == 0)
+    return;
+
+  while (temp = fgets (buffer, 255, file))
+    {
+      /* Skip to first character. */
+      for (i = 0; buffer[i] && cr_whitespace (buffer[i]); i++)
+       ;
+
+      /* If comment or blank line, ignore. */
+      if (buffer[i] == '\0' || buffer[i] == '#')
+       continue;
+
+      /* If `preprocessor' directive, do the include. */
+      if (strncmp (buffer + i, "$include ", 9) == 0)
+       {
+         char *incfile, *t;
+
+         /* Find start of filename. */
+         for (incfile = buffer + i + 9; *incfile && whitespace (*incfile); incfile++)
+           ;
+
+         /* Find end of filename. */
+         for (t = incfile; *t && cr_whitespace (*t) == 0; t++)
+           ;
+
+         *t = '\0';
+
+         snarf_hosts_from_file (incfile);
+         continue;
+       }
+
+      /* Skip internet address if present. */
+      if (DIGIT (buffer[i]))
+       for (; buffer[i] && cr_whitespace (buffer[i]) == 0; i++);
+
+      /* Gobble up names.  Each name is separated with whitespace. */
+      while (buffer[i])
+       {
+         for (; cr_whitespace (buffer[i]); i++)
+           ;
+         if (buffer[i] == '\0' || buffer[i] ==  '#')
+           break;
+
+         /* Isolate the current word. */
+         for (start = i; buffer[i] && cr_whitespace (buffer[i]) == 0; i++)
+           ;
+         if (i == start)
+           continue;
+         strncpy (name, buffer + start, i - start);
+         name[i - start] = '\0';
+         add_host_name (name);
+       }
+    }
+  fclose (file);
+}
+
+/* Return the hostname list. */
+char **
+get_hostname_list ()
+{
+  if (hostname_list_initialized == 0)
+    initialize_hostname_list ();
+  return (hostname_list);
+}
+
+void
+clear_hostname_list ()
+{
+  register int i;
+
+  if (hostname_list_initialized == 0)
+    return;
+  for (i = 0; i < hostname_list_length; i++)
+    free (hostname_list[i]);
+  hostname_list_length = 0;
+}
+
+/* Return a NULL terminated list of hostnames which begin with TEXT.
+   Initialize the hostname list the first time if neccessary.
+   The array is malloc ()'ed, but not the individual strings. */
+static char **
+hostnames_matching (text)
+     char *text;
+{
+  register int i, len, nmatch, rsize;
+  char **result;
+
+  if (hostname_list_initialized == 0)
+    initialize_hostname_list ();
+
+  if (hostname_list_initialized == 0)
+    return ((char **)NULL);
+
+  /* Special case.  If TEXT consists of nothing, then the whole list is
+     what is desired. */
+  if (*text == '\0')
+    {
+      result = strvec_create (1 + hostname_list_length);
+      for (i = 0; i < hostname_list_length; i++)
+       result[i] = hostname_list[i];
+      result[i] = (char *)NULL;
+      return (result);
+    }
+
+  /* Scan until found, or failure. */
+  len = strlen (text);
+  result = (char **)NULL;
+  for (i = nmatch = rsize = 0; i < hostname_list_length; i++)
+    {
+      if (STREQN (text, hostname_list[i], len) == 0)
+       continue;
+
+      /* OK, it matches.  Add it to the list. */
+      if (nmatch >= (rsize - 1))
+       {
+         rsize = (rsize + 16) - (rsize % 16);
+         result = strvec_resize (result, rsize);
+       }
+
+      result[nmatch++] = hostname_list[i];
+    }
+  if (nmatch)
+    result[nmatch] = (char *)NULL;
+  return (result);
+}
+
+/* The equivalent of the Korn shell C-o operate-and-get-next-history-line
+   editing command. */
+static int saved_history_line_to_use = -1;
+
+static int
+set_saved_history ()
+{
+  if (saved_history_line_to_use >= 0)
+    rl_get_previous_history (history_length - saved_history_line_to_use, 0);
+  saved_history_line_to_use = -1;
+  rl_startup_hook = old_rl_startup_hook;
+  return (0);
+}
+
+static int
+operate_and_get_next (count, c)
+     int count, c;
+{
+  int where;
+
+  /* Accept the current line. */
+  rl_newline (1, c);
+
+  /* Find the current line, and find the next line to use. */
+  where = where_history ();
+
+  if ((history_is_stifled () && (history_length >= history_max_entries)) ||
+      (where >= history_length - 1))
+    saved_history_line_to_use = where;
+  else
+    saved_history_line_to_use = where + 1;
+
+  old_rl_startup_hook = rl_startup_hook;
+  rl_startup_hook = set_saved_history;
+
+  return 0;
+}
+
+/* This vi mode command causes VI_EDIT_COMMAND to be run on the current
+   command being entered (if no explicit argument is given), otherwise on
+   a command from the history file. */
+
+#define VI_EDIT_COMMAND                "fc -e \"${VISUAL:-${EDITOR:-vi}}\""
+#define EMACS_EDIT_COMMAND     "fc -e \"${VISUAL:-${EDITOR:-emacs}}\""
+
+static int
+edit_and_execute_command (count, c, editing_mode, edit_command)
+     int count, c, editing_mode;
+     char *edit_command;
+{
+  char *command;
+  int r, cclc, rrs;
+
+  rrs = rl_readline_state;
+  cclc = current_command_line_count;
+
+  /* Accept the current line. */
+  rl_newline (1, c);
+
+  if (rl_explicit_arg)
+    {
+      command = (char *)xmalloc (strlen (edit_command) + 8);
+      sprintf (command, "%s %d", edit_command, count);
+    }
+  else
+    {
+      /* Take the command we were just editing, add it to the history file,
+        then call fc to operate on it.  We have to add a dummy command to
+        the end of the history because fc ignores the last command (assumes
+        it's supposed to deal with the command before the `fc'). */
+      using_history ();
+      bash_add_history (rl_line_buffer);
+      bash_add_history ("");
+      history_lines_this_session++;
+      using_history ();
+      command = savestring (edit_command);
+    }
+
+  /* Now, POSIX.1-2001 and SUSv3 say that the commands executed from the
+     temporary file should be placed into the history.  We don't do that
+     yet. */
+  r = parse_and_execute (command, (editing_mode == VI_EDITING_MODE) ? "v" : "C-xC-e", SEVAL_NOHIST);
+
+  current_command_line_count = cclc;
+
+  /* Now erase the contents of the current line and undo the effects of the
+     rl_accept_line() above.  We don't even want to make the text we just
+     executed available for undoing. */
+  rl_line_buffer[0] = '\0';    /* XXX */
+  rl_point = rl_end = 0;
+  rl_done = 0;
+  rl_readline_state = rrs;
+
+  rl_forced_update_display ();
+
+  return r;
+}
+
+#if defined (VI_MODE)
+static int
+vi_edit_and_execute_command (count, c)
+     int count, c;
+{
+  return (edit_and_execute_command (count, c, VI_EDITING_MODE, VI_EDIT_COMMAND));
+}
+#endif /* VI_MODE */
+
+static int
+emacs_edit_and_execute_command (count, c)
+     int count, c;
+{
+  return (edit_and_execute_command (count, c, EMACS_EDITING_MODE, EMACS_EDIT_COMMAND));
+}
+
+#if defined (ALIAS)
+static int
+posix_edit_macros (count, key)
+     int count, key;
+{
+  int c;
+  char alias_name[3], *alias_value, *macro;
+
+  c = rl_read_key ();
+  alias_name[0] = '_';
+  alias_name[1] = c;
+  alias_name[2] = '\0';
+
+  alias_value = get_alias_value (alias_name);
+  if (alias_value && *alias_value)
+    {
+      macro = savestring (alias_value);
+      rl_push_macro_input (macro);
+    }
+  return 0;
+}
+#endif
+
+/* **************************************************************** */
+/*                                                                 */
+/*                     How To Do Shell Completion                  */
+/*                                                                 */
+/* **************************************************************** */
+
+#define COMMAND_SEPARATORS ";|&{(`"
+/* )} */ 
+
+static int
+check_redir (ti)
+     int ti;
+{
+  register int this_char, prev_char;
+
+  /* Handle the two character tokens `>&', `<&', and `>|'.
+     We are not in a command position after one of these. */
+  this_char = rl_line_buffer[ti];
+  prev_char = rl_line_buffer[ti - 1];
+
+  if ((this_char == '&' && (prev_char == '<' || prev_char == '>')) ||
+      (this_char == '|' && prev_char == '>'))
+    return (1);
+  else if ((this_char == '{' && prev_char == '$') || /* } */
+          (char_is_quoted (rl_line_buffer, ti)))
+    return (1);
+  return (0);
+}
+
+#if defined (PROGRAMMABLE_COMPLETION)
+/*
+ * XXX - because of the <= start test, and setting os = s+1, this can
+ * potentially return os > start.  This is probably not what we want to
+ * happen, but fix later after 2.05a-release.
+ */
+static int
+find_cmd_start (start)
+     int start;
+{
+  register int s, os;
+
+  os = 0;
+  while (((s = skip_to_delim (rl_line_buffer, os, COMMAND_SEPARATORS)) <= start) &&
+        rl_line_buffer[s])
+    os = s+1;
+  return os;
+}
+
+static int
+find_cmd_end (end)
+     int end;
+{
+  register int e;
+
+  e = skip_to_delim (rl_line_buffer, end, COMMAND_SEPARATORS);
+  return e;
+}
+
+static char *
+find_cmd_name (start)
+     int start;
+{
+  char *name;
+  register int s, e;
+
+  for (s = start; whitespace (rl_line_buffer[s]); s++)
+    ;
+
+  /* skip until a shell break character */
+  e = skip_to_delim (rl_line_buffer, s, "()<>;&| \t\n");
+
+  name = substring (rl_line_buffer, s, e);
+
+  return (name);
+}
+
+static char *
+prog_complete_return (text, matchnum)
+     const char *text;
+     int matchnum;
+{
+  static int ind;
+
+  if (matchnum == 0)
+    ind = 0;
+
+  if (prog_complete_matches == 0 || prog_complete_matches[ind] == 0)
+    return (char *)NULL;
+  return (prog_complete_matches[ind++]);
+}
+
+#endif /* PROGRAMMABLE_COMPLETION */
+
+/* Do some completion on TEXT.  The indices of TEXT in RL_LINE_BUFFER are
+   at START and END.  Return an array of matches, or NULL if none. */
+static char **
+attempt_shell_completion (text, start, end)
+     const char *text;
+     int start, end;
+{
+  int in_command_position, ti, saveti, qc;
+  char **matches, *command_separator_chars;
+
+  command_separator_chars = COMMAND_SEPARATORS;
+  matches = (char **)NULL;
+  rl_ignore_some_completions_function = filename_completion_ignore;
+
+  /* Determine if this could be a command word.  It is if it appears at
+     the start of the line (ignoring preceding whitespace), or if it
+     appears after a character that separates commands.  It cannot be a
+     command word if we aren't at the top-level prompt. */
+  ti = start - 1;
+  saveti = qc = -1;
+
+  while ((ti > -1) && (whitespace (rl_line_buffer[ti])))
+    ti--;
+
+#if 1
+  /* If this is an open quote, maybe we're trying to complete a quoted
+     command name. */
+  if (ti >= 0 && (rl_line_buffer[ti] == '"' || rl_line_buffer[ti] == '\''))
+    {
+      qc = rl_line_buffer[ti];
+      saveti = ti--;
+      while (ti > -1 && (whitespace (rl_line_buffer[ti])))
+       ti--;
+    }
+#endif
+      
+  in_command_position = 0;
+  if (ti < 0)
+    {
+      /* Only do command completion at the start of a line when we
+        are prompting at the top level. */
+      if (current_prompt_string == ps1_prompt)
+       in_command_position++;
+    }
+  else if (member (rl_line_buffer[ti], command_separator_chars))
+    {
+      in_command_position++;
+
+      if (check_redir (ti) == 1)
+       in_command_position = 0;
+    }
+  else
+    {
+      /* This still could be in command position.  It is possible
+        that all of the previous words on the line are variable
+        assignments. */
+    }
+
+  /* Check that we haven't incorrectly flagged a closed command substitution
+     as indicating we're in a command position. */
+  if (in_command_position && ti >= 0 && rl_line_buffer[ti] == '`' &&
+       *text != '`' && unclosed_pair (rl_line_buffer, end, "`") == 0)
+    in_command_position = 0;
+
+  /* Special handling for command substitution.  If *TEXT is a backquote,
+     it can be the start or end of an old-style command substitution, or
+     unmatched.  If it's unmatched, both calls to unclosed_pair will
+     succeed.  */
+  if (*text == '`' && 
+       (in_command_position || (unclosed_pair (rl_line_buffer, start, "`") &&
+                                unclosed_pair (rl_line_buffer, end, "`"))))
+    matches = rl_completion_matches (text, command_subst_completion_function);
+
+#if defined (PROGRAMMABLE_COMPLETION)
+  /* Attempt programmable completion. */
+  if (!matches && in_command_position == 0 && prog_completion_enabled &&
+      (progcomp_size () > 0) && current_prompt_string == ps1_prompt)
+    {
+      int s, e, foundcs;
+      char *n;
+
+      /* XXX - don't free the members */
+      if (prog_complete_matches)
+       free (prog_complete_matches);
+      prog_complete_matches = (char **)NULL;
+
+      s = find_cmd_start (start);
+      e = find_cmd_end (end);
+      n = find_cmd_name (s);
+      if (e > s)
+       prog_complete_matches = programmable_completions (n, text, s, e, &foundcs);
+      else
+       foundcs = 0;
+      FREE (n);
+      /* XXX - if we found a COMPSPEC for the command, just return whatever
+        the programmable completion code returns, and disable the default
+        filename completion that readline will do unless the COPT_DEFAULT
+        option has been set with the `-o default' option to complete. */
+      if (foundcs)
+       {
+         /* If the user specified that the compspec returns filenames, make
+            sure that readline knows it. */
+         if (foundcs & COPT_FILENAMES)
+           rl_filename_completion_desired = 1;
+         /* If the user doesn't want a space appended, tell readline. */
+         if (foundcs & COPT_NOSPACE)
+           rl_completion_suppress_append = 1;
+         /* Turn what the programmable completion code returns into what
+            readline wants.  I should have made compute_lcd_of_matches
+            external... */
+         matches = rl_completion_matches (text, prog_complete_return);
+         if ((foundcs & COPT_DEFAULT) == 0)
+           rl_attempted_completion_over = 1;   /* no default */
+         if (matches || ((foundcs & COPT_BASHDEFAULT) == 0))
+           return (matches);
+       }
+    }
+#endif
+
+  if (matches == 0)
+    matches = bash_default_completion (text, start, end, qc, in_command_position);
+
+  return matches;
+}
+
+char **
+bash_default_completion (text, start, end, qc, in_command_position)
+     const char *text;
+     int start, end, qc, in_command_position;
+{
+  char **matches;
+
+  matches = (char **)NULL;
+
+  /* New posix-style command substitution or variable name? */
+  if (!matches && *text == '$')
+    {
+      if (qc != '\'' && text[1] == '(') /* ) */
+       matches = rl_completion_matches (text, command_subst_completion_function);
+      else
+       matches = rl_completion_matches (text, variable_completion_function);
+    }
+
+  /* If the word starts in `~', and there is no slash in the word, then
+     try completing this word as a username. */
+  if (!matches && *text == '~' && !xstrchr (text, '/'))
+    matches = rl_completion_matches (text, rl_username_completion_function);
+
+  /* Another one.  Why not?  If the word starts in '@', then look through
+     the world of known hostnames for completion first. */
+  if (!matches && perform_hostname_completion && *text == '@')
+    matches = rl_completion_matches (text, hostname_completion_function);
+
+  /* And last, (but not least) if this word is in a command position, then
+     complete over possible command names, including aliases, functions,
+     and command names. */
+  if (!matches && in_command_position)
+    {
+      if (start == 0 && end == 0 && text[0] == '\0' && no_empty_command_completion)
+       {
+         matches = (char **)NULL;
+         rl_ignore_some_completions_function = bash_ignore_everything;
+       }
+      else
+       {
+#define CMD_IS_DIR(x)  (absolute_pathname(x) == 0 && absolute_program(x) == 0 && *(x) != '~' && test_for_directory (x))
+
+         matches = rl_completion_matches (text, command_word_completion_function);
+
+         /* If we are attempting command completion and nothing matches, we
+            do not want readline to perform filename completion for us.  We
+            still want to be able to complete partial pathnames, so set the
+            completion ignore function to something which will remove
+            filenames and leave directories in the match list. */
+         if (matches == (char **)NULL)
+           rl_ignore_some_completions_function = bash_ignore_filenames;
+#if 0
+         else if (matches[1] == 0 && CMD_IS_DIR(matches[0]))
+           /* Turn off rl_filename_completion_desired so readline doesn't
+              append a slash if there is a directory with the same name
+              in the current directory, or other filename-specific things.
+              If the name begins with a slash, we're either completing a
+              full pathname or a directory pathname, and readline won't be
+              looking in the current directory anyway, so there's no
+              conflict. */
+           rl_filename_completion_desired = 0;
+         else if (matches[0] && matches[1] && STREQ (matches[0], matches[1]) && CMD_IS_DIR (matches[0]))
+           /* There are multiple instances of the same match (duplicate
+              completions haven't yet been removed).  In this case, all of
+              the matches will be the same, and the duplicate removal code
+              will distill them all down to one.  We turn off
+              rl_filename_completion_desired for the same reason as above.
+              Remember: we only care if there's eventually a single unique
+              completion.  If there are multiple completions this won't
+              make a difference and the problem won't occur. */
+           rl_filename_completion_desired = 0;
+#endif
+       }
+    }
+
+  /* This could be a globbing pattern, so try to expand it using pathname
+     expansion. */
+  if (!matches && glob_pattern_p (text))
+    {
+      matches = rl_completion_matches (text, glob_complete_word);
+      /* A glob expression that matches more than one filename is problematic.
+        If we match more than one filename, punt. */
+      if (matches && matches[1] && rl_completion_type == TAB)
+       {
+         strvec_dispose (matches);
+         matches = (char **)0;
+       }
+    }
+
+  return (matches);
+}
+
+/* This is the function to call when the word to complete is in a position
+   where a command word can be found.  It grovels $PATH, looking for commands
+   that match.  It also scans aliases, function names, and the shell_builtin
+   table. */
+char *
+command_word_completion_function (hint_text, state)
+     const char *hint_text;
+     int state;
+{
+  static char *hint = (char *)NULL;
+  static char *path = (char *)NULL;
+  static char *val = (char *)NULL;
+  static char *filename_hint = (char *)NULL;
+  static int path_index, hint_len, istate;
+  static int mapping_over, local_index;
+  static SHELL_VAR **varlist = (SHELL_VAR **)NULL;
+#if defined (ALIAS)
+  static alias_t **alias_list = (alias_t **)NULL;
+#endif /* ALIAS */
+
+  /* We have to map over the possibilities for command words.  If we have
+     no state, then make one just for that purpose. */
+  if (!state)
+    {
+      if (hint)
+       free (hint);
+
+      mapping_over = 0;
+      val = (char *)NULL;
+
+      /* If this is an absolute program name, do not check it against
+        aliases, reserved words, functions or builtins.  We must check
+        whether or not it is unique, and, if so, whether that filename
+        is executable. */
+      if (absolute_program (hint_text))
+       {
+         /* Perform tilde expansion on what's passed, so we don't end up
+            passing filenames with tildes directly to stat(). */
+         if (*hint_text == '~')
+           hint = bash_tilde_expand (hint_text, 0);
+         else
+           hint = savestring (hint_text);
+         hint_len = strlen (hint);
+
+         if (filename_hint)
+           free (filename_hint);
+         filename_hint = savestring (hint);
+
+         mapping_over = 4;
+         istate = 0;
+         goto inner;
+       }
+
+      hint = savestring (hint_text);
+      hint_len = strlen (hint);
+
+      path = get_string_value ("PATH");
+      path_index = 0;
+
+      /* Initialize the variables for each type of command word. */
+      local_index = 0;
+
+      if (varlist)
+       free (varlist);
+
+      varlist = all_visible_functions ();
+
+#if defined (ALIAS)
+      if (alias_list)
+       free (alias_list);
+
+      alias_list = all_aliases ();
+#endif /* ALIAS */
+    }
+
+  /* mapping_over says what we are currently hacking.  Note that every case
+     in this list must fall through when there are no more possibilities. */
+
+  switch (mapping_over)
+    {
+    case 0:                    /* Aliases come first. */
+#if defined (ALIAS)
+      while (alias_list && alias_list[local_index])
+       {
+         register char *alias;
+
+         alias = alias_list[local_index++]->name;
+
+         if (STREQN (alias, hint, hint_len))
+           return (savestring (alias));
+       }
+#endif /* ALIAS */
+      local_index = 0;
+      mapping_over++;
+
+    case 1:                    /* Then shell reserved words. */
+      {
+       while (word_token_alist[local_index].word)
+         {
+           register char *reserved_word;
+
+           reserved_word = word_token_alist[local_index++].word;
+
+           if (STREQN (reserved_word, hint, hint_len))
+             return (savestring (reserved_word));
+         }
+       local_index = 0;
+       mapping_over++;
+      }
+
+    case 2:                    /* Then function names. */
+      while (varlist && varlist[local_index])
+       {
+         register char *varname;
+
+         varname = varlist[local_index++]->name;
+
+         if (STREQN (varname, hint, hint_len))
+           return (savestring (varname));
+       }
+      local_index = 0;
+      mapping_over++;
+
+    case 3:                    /* Then shell builtins. */
+      for (; local_index < num_shell_builtins; local_index++)
+       {
+         /* Ignore it if it doesn't have a function pointer or if it
+            is not currently enabled. */
+         if (!shell_builtins[local_index].function ||
+             (shell_builtins[local_index].flags & BUILTIN_ENABLED) == 0)
+           continue;
+
+         if (STREQN (shell_builtins[local_index].name, hint, hint_len))
+           {
+             int i = local_index++;
+
+             return (savestring (shell_builtins[i].name));
+           }
+       }
+      local_index = 0;
+      mapping_over++;
+    }
+
+  /* Repeatedly call filename_completion_function while we have
+     members of PATH left.  Question:  should we stat each file?
+     Answer: we call executable_file () on each file. */
+ outer:
+
+  istate = (val != (char *)NULL);
+
+  if (!istate)
+    {
+      char *current_path;
+
+      /* Get the next directory from the path.  If there is none, then we
+        are all done. */
+      if (!path || !path[path_index] ||
+         (current_path = extract_colon_unit (path, &path_index)) == 0)
+       return ((char *)NULL);
+
+      if (*current_path == 0)
+       {
+         free (current_path);
+         current_path = savestring (".");
+       }
+
+      if (*current_path == '~')
+       {
+         char *t;
+
+         t = bash_tilde_expand (current_path, 0);
+         free (current_path);
+         current_path = t;
+       }
+
+      if (filename_hint)
+       free (filename_hint);
+
+      filename_hint = sh_makepath (current_path, hint, 0);
+
+      free (current_path);
+    }
+
+ inner:
+  val = rl_filename_completion_function (filename_hint, istate);
+  istate = 1;
+
+  if (val == 0)
+    {
+      /* If the hint text is an absolute program, then don't bother
+        searching through PATH. */
+      if (absolute_program (hint))
+       return ((char *)NULL);
+
+      goto outer;
+    }
+  else
+    {
+      int match, freetemp;
+      char *temp;
+
+      if (absolute_program (hint))
+       {
+         match = strncmp (val, hint, hint_len) == 0;
+         /* If we performed tilde expansion, restore the original
+            filename. */
+         if (*hint_text == '~')
+           {
+             int l, tl, vl;
+             vl = strlen (val);
+             tl = strlen (hint_text);
+             l = vl - hint_len;        /* # of chars added */
+             temp = (char *)xmalloc (l + 2 + tl);
+             strcpy (temp, hint_text);
+             strcpy (temp + tl, val + vl - l);
+           }
+         else
+           temp = savestring (val);
+         freetemp = 1;
+       }
+      else
+       {
+         temp = strrchr (val, '/');
+
+         if (temp)
+           {
+             temp++;
+             freetemp = match = strncmp (temp, hint, hint_len) == 0;
+             if (match)
+               temp = savestring (temp);
+           }
+         else
+           freetemp = match = 0;
+       }
+
+      /* If we have found a match, and it is an executable file or a
+        directory name, return it. */
+      if (match && executable_or_directory (val))
+       {
+         free (val);
+         val = "";             /* So it won't be NULL. */
+         return (temp);
+       }
+      else
+       {
+         if (freetemp)
+           free (temp);
+         free (val);
+         goto inner;
+       }
+    }
+}
+
+/* Completion inside an unterminated command substitution. */
+static char *
+command_subst_completion_function (text, state)
+     const char *text;
+     int state;
+{
+  static char **matches = (char **)NULL;
+  static const char *orig_start;
+  static char *filename_text = (char *)NULL;
+  static int cmd_index, start_len;
+  char *value;
+
+  if (state == 0)
+    {
+      if (filename_text)
+       free (filename_text);
+      orig_start = text;
+      if (*text == '`')
+       text++;
+      else if (*text == '$' && text[1] == '(') /* ) */
+       text += 2;
+      /* If the text was quoted, suppress any quote character that the
+        readline completion code would insert. */
+      rl_completion_suppress_quote = 1;
+      start_len = text - orig_start;
+      filename_text = savestring (text);
+      if (matches)
+       free (matches);
+
+      /*
+       * At this point we can entertain the idea of re-parsing
+       * `filename_text' into a (possibly incomplete) command name and
+       * arguments, and doing completion based on that.  This is
+       * currently very rudimentary, but it is a small improvement.
+       */
+      for (value = filename_text + strlen (filename_text) - 1; value > filename_text; value--)
+        if (whitespace (*value) || member (*value, COMMAND_SEPARATORS))
+          break;
+      if (value <= filename_text)
+       matches = rl_completion_matches (filename_text, command_word_completion_function);
+      else
+       {
+         value++;
+         start_len += value - filename_text;
+         if (whitespace (value[-1]))
+           matches = rl_completion_matches (value, rl_filename_completion_function);
+         else
+           matches = rl_completion_matches (value, command_word_completion_function);
+       }
+
+      /* If there is more than one match, rl_completion_matches has already
+        put the lcd in matches[0].  Skip over it. */
+      cmd_index = matches && matches[0] && matches[1];
+    }
+
+  if (!matches || !matches[cmd_index])
+    {
+      rl_filename_quoting_desired = 0; /* disable quoting */
+      return ((char *)NULL);
+    }
+  else
+    {
+      value = (char *)xmalloc (1 + start_len + strlen (matches[cmd_index]));
+
+      if (start_len == 1)
+       value[0] = *orig_start;
+      else
+       strncpy (value, orig_start, start_len);
+
+      strcpy (value + start_len, matches[cmd_index]);
+
+      cmd_index++;
+      return (value);
+    }
+}
+
+/* Okay, now we write the entry_function for variable completion. */
+static char *
+variable_completion_function (text, state)
+     const char *text;
+     int state;
+{
+  static char **varlist = (char **)NULL;
+  static int varlist_index;
+  static char *varname = (char *)NULL;
+  static int namelen;
+  static int first_char, first_char_loc;
+
+  if (!state)
+    {
+      if (varname)
+       free (varname);
+
+      first_char_loc = 0;
+      first_char = text[0];
+
+      if (first_char == '$')
+       first_char_loc++;
+
+      if (text[first_char_loc] == '{')
+       first_char_loc++;
+
+      varname = savestring (text + first_char_loc);
+
+      namelen = strlen (varname);
+      if (varlist)
+       strvec_dispose (varlist);
+
+      varlist = all_variables_matching_prefix (varname);
+      varlist_index = 0;
+    }
+
+  if (!varlist || !varlist[varlist_index])
+    {
+      return ((char *)NULL);
+    }
+  else
+    {
+      char *value;
+
+      value = (char *)xmalloc (4 + strlen (varlist[varlist_index]));
+
+      if (first_char_loc)
+       {
+         value[0] = first_char;
+         if (first_char_loc == 2)
+           value[1] = '{';
+       }
+
+      strcpy (value + first_char_loc, varlist[varlist_index]);
+      if (first_char_loc == 2)
+       strcat (value, "}");
+
+      varlist_index++;
+      return (value);
+    }
+}
+
+/* How about a completion function for hostnames? */
+static char *
+hostname_completion_function (text, state)
+     const char *text;
+     int state;
+{
+  static char **list = (char **)NULL;
+  static int list_index = 0;
+  static int first_char, first_char_loc;
+
+  /* If we don't have any state, make some. */
+  if (state == 0)
+    {
+      FREE (list);
+
+      list = (char **)NULL;
+
+      first_char_loc = 0;
+      first_char = *text;
+
+      if (first_char == '@')
+       first_char_loc++;
+
+      list = hostnames_matching ((char *)text+first_char_loc);
+      list_index = 0;
+    }
+
+  if (list && list[list_index])
+    {
+      char *t;
+
+      t = (char *)xmalloc (2 + strlen (list[list_index]));
+      *t = first_char;
+      strcpy (t + first_char_loc, list[list_index]);
+      list_index++;
+      return (t);
+    }
+
+  return ((char *)NULL);
+}
+
+/*
+ * A completion function for service names from /etc/services (or wherever).
+ */
+char *
+bash_servicename_completion_function (text, state)
+     const char *text;
+     int state;
+{
+#if defined (__WIN32__) || defined (__OPENNT) || !defined (HAVE_GETSERVENT)
+  return ((char *)NULL);
+#else
+  static char *sname = (char *)NULL;
+  static struct servent *srvent;
+  static int snamelen, firstc;
+  char *value;
+  char **alist, *aentry;
+  int afound;
+
+  if (state == 0)
+    {
+      FREE (sname);
+      firstc = *text;
+
+      sname = savestring (text);
+      snamelen = strlen (sname);
+      setservent (0);
+    }
+
+  while (srvent = getservent ())
+    {
+      afound = 0;
+      if (snamelen == 0 || (STREQN (sname, srvent->s_name, snamelen)))
+       break;
+      /* Not primary, check aliases */
+      for (alist = srvent->s_aliases; aentry = *alist; alist++)
+       {
+         if (STREQN (sname, aentry, snamelen))
+           {
+             afound = 1;
+             break;
+           }
+       }
+
+      if (afound)
+       break;
+    }
+
+  if (srvent == 0)
+    {
+      endservent ();
+      return ((char *)NULL);
+    }
+
+  value = afound ? savestring (aentry) : savestring (srvent->s_name);
+  return value;
+#endif
+}
+
+/*
+ * A completion function for group names from /etc/group (or wherever).
+ */
+char *
+bash_groupname_completion_function (text, state)
+     const char *text;
+     int state;
+{
+#if defined (__WIN32__) || defined (__OPENNT) || !defined (HAVE_GRP_H)
+  return ((char *)NULL);
+#else
+  static char *gname = (char *)NULL;
+  static struct group *grent;
+  static int gnamelen;
+  char *value;
+
+  if (state == 0)
+    {
+      FREE (gname);
+      gname = savestring (text);
+      gnamelen = strlen (gname);
+
+      setgrent ();
+    }
+
+  while (grent = getgrent ())
+    {
+      if (gnamelen == 0 || (STREQN (gname, grent->gr_name, gnamelen)))
+        break;
+    }
+
+  if (grent == 0)
+    {
+      endgrent ();
+      return ((char *)NULL);
+    }
+
+  value = savestring (grent->gr_name);
+  return (value);
+#endif
+}
+
+/* Functions to perform history and alias expansions on the current line. */
+
+#if defined (BANG_HISTORY)
+/* Perform history expansion on the current line.  If no history expansion
+   is done, pre_process_line() returns what it was passed, so we need to
+   allocate a new line here. */
+static char *
+history_expand_line_internal (line)
+     char *line;
+{
+  char *new_line;
+  int old_verify;
+
+  old_verify = hist_verify;
+  hist_verify = 0;
+  new_line = pre_process_line (line, 0, 0);
+  hist_verify = old_verify;
+
+  return (new_line == line) ? savestring (line) : new_line;
+}
+#endif
+
+/* There was an error in expansion.  Let the preprocessor print
+   the error here. */
+static void
+cleanup_expansion_error ()
+{
+  char *to_free;
+#if defined (BANG_HISTORY)
+  int old_verify;
+
+  old_verify = hist_verify;
+  hist_verify = 0;
+#endif
+
+  fprintf (rl_outstream, "\r\n");
+  to_free = pre_process_line (rl_line_buffer, 1, 0);
+#if defined (BANG_HISTORY)
+  hist_verify = old_verify;
+#endif
+  if (to_free != rl_line_buffer)
+    FREE (to_free);
+  putc ('\r', rl_outstream);
+  rl_forced_update_display ();
+}
+
+/* If NEW_LINE differs from what is in the readline line buffer, add an
+   undo record to get from the readline line buffer contents to the new
+   line and make NEW_LINE the current readline line. */
+static void
+maybe_make_readline_line (new_line)
+     char *new_line;
+{
+  if (strcmp (new_line, rl_line_buffer) != 0)
+    {
+      rl_point = rl_end;
+
+      rl_add_undo (UNDO_BEGIN, 0, 0, 0);
+      rl_delete_text (0, rl_point);
+      rl_point = rl_end = rl_mark = 0;
+      rl_insert_text (new_line);
+      rl_add_undo (UNDO_END, 0, 0, 0);
+    }
+}
+
+/* Make NEW_LINE be the current readline line.  This frees NEW_LINE. */
+static void
+set_up_new_line (new_line)
+     char *new_line;
+{
+  int old_point, at_end;
+
+  old_point = rl_point;
+  at_end = rl_point == rl_end;
+
+  /* If the line was history and alias expanded, then make that
+     be one thing to undo. */
+  maybe_make_readline_line (new_line);
+  free (new_line);
+
+  /* Place rl_point where we think it should go. */
+  if (at_end)
+    rl_point = rl_end;
+  else if (old_point < rl_end)
+    {
+      rl_point = old_point;
+      if (!whitespace (rl_line_buffer[rl_point]))
+       rl_forward_word (1, 0);
+    }
+}
+
+#if defined (ALIAS)
+/* Expand aliases in the current readline line. */
+static int
+alias_expand_line (count, ignore)
+     int count, ignore;
+{
+  char *new_line;
+
+  new_line = alias_expand (rl_line_buffer);
+
+  if (new_line)
+    {
+      set_up_new_line (new_line);
+      return (0);
+    }
+  else
+    {
+      cleanup_expansion_error ();
+      return (1);
+    }
+}
+#endif
+
+#if defined (BANG_HISTORY)
+/* History expand the line. */
+static int
+history_expand_line (count, ignore)
+     int count, ignore;
+{
+  char *new_line;
+
+  new_line = history_expand_line_internal (rl_line_buffer);
+
+  if (new_line)
+    {
+      set_up_new_line (new_line);
+      return (0);
+    }
+  else
+    {
+      cleanup_expansion_error ();
+      return (1);
+    }
+}
+
+/* Expand history substitutions in the current line and then insert a
+   space (hopefully close to where we were before). */
+static int
+tcsh_magic_space (count, ignore)
+     int count, ignore;
+{
+  int dist_from_end, old_point;
+
+  old_point = rl_point;
+  dist_from_end = rl_end - rl_point;
+  if (history_expand_line (count, ignore) == 0)
+    {
+      /* Try a simple heuristic from Stephen Gildea <gildea@intouchsys.com>.
+        This works if all expansions were before rl_point or if no expansions
+        were performed. */
+      rl_point = (old_point == 0) ? old_point : rl_end - dist_from_end;
+      rl_insert (1, ' ');
+      return (0);
+    }
+  else
+    return (1);
+}
+#endif
+
+/* History and alias expand the line. */
+static int
+history_and_alias_expand_line (count, ignore)
+     int count, ignore;
+{
+  char *new_line;
+
+  new_line = history_expand_line_internal (rl_line_buffer);
+
+#if defined (ALIAS)
+  if (new_line)
+    {
+      char *alias_line;
+
+      alias_line = alias_expand (new_line);
+      free (new_line);
+      new_line = alias_line;
+    }
+#endif /* ALIAS */
+
+  if (new_line)
+    {
+      set_up_new_line (new_line);
+      return (0);
+    }
+  else
+    {
+      cleanup_expansion_error ();
+      return (1);
+    }
+}
+
+/* History and alias expand the line, then perform the shell word
+   expansions by calling expand_string.  This can't use set_up_new_line()
+   because we want the variable expansions as a separate undo'able
+   set of operations. */
+static int
+shell_expand_line (count, ignore)
+     int count, ignore;
+{
+  char *new_line;
+  WORD_LIST *expanded_string;
+
+  new_line = history_expand_line_internal (rl_line_buffer);
+
+#if defined (ALIAS)
+  if (new_line)
+    {
+      char *alias_line;
+
+      alias_line = alias_expand (new_line);
+      free (new_line);
+      new_line = alias_line;
+    }
+#endif /* ALIAS */
+
+  if (new_line)
+    {
+      int old_point = rl_point;
+      int at_end = rl_point == rl_end;
+
+      /* If the line was history and alias expanded, then make that
+        be one thing to undo. */
+      maybe_make_readline_line (new_line);
+      free (new_line);
+
+      /* If there is variable expansion to perform, do that as a separate
+        operation to be undone. */
+      new_line = savestring (rl_line_buffer);
+      expanded_string = expand_string (new_line, 0);
+      FREE (new_line);
+      if (expanded_string == 0)
+       {
+         new_line = (char *)xmalloc (1);
+         new_line[0] = '\0';
+       }
+      else
+       {
+         new_line = string_list (expanded_string);
+         dispose_words (expanded_string);
+       }
+
+      maybe_make_readline_line (new_line);
+      free (new_line);
+
+      /* Place rl_point where we think it should go. */
+      if (at_end)
+       rl_point = rl_end;
+      else if (old_point < rl_end)
+       {
+         rl_point = old_point;
+         if (!whitespace (rl_line_buffer[rl_point]))
+           rl_forward_word (1, 0);
+       }
+      return 0;
+    }
+  else
+    {
+      cleanup_expansion_error ();
+      return 1;
+    }
+}
+
+/* If FIGNORE is set, then don't match files with the given suffixes when
+   completing filenames.  If only one of the possibilities has an acceptable
+   suffix, delete the others, else just return and let the completer
+   signal an error.  It is called by the completer when real
+   completions are done on filenames by the completer's internal
+   function, not for completion lists (M-?) and not on "other"
+   completion types, such as hostnames or commands. */
+
+static struct ignorevar fignore =
+{
+  "FIGNORE",
+  (struct ign *)0,
+  0,
+  (char *)0,
+  (sh_iv_item_func_t *) 0,
+};
+
+static void
+_ignore_completion_names (names, name_func)
+     char **names;
+     sh_ignore_func_t *name_func;
+{
+  char **newnames;
+  int idx, nidx;
+  char **oldnames;
+  int oidx;
+
+  /* If there is only one completion, see if it is acceptable.  If it is
+     not, free it up.  In any case, short-circuit and return.  This is a
+     special case because names[0] is not the prefix of the list of names
+     if there is only one completion; it is the completion itself. */
+  if (names[1] == (char *)0)
+    {
+      if (force_fignore)
+       if ((*name_func) (names[0]) == 0)
+         {
+           free (names[0]);
+           names[0] = (char *)NULL;
+         }
+
+      return;
+    }
+
+  /* Allocate space for array to hold list of pointers to matching
+     filenames.  The pointers are copied back to NAMES when done. */
+  for (nidx = 1; names[nidx]; nidx++)
+    ;
+  newnames = strvec_create (nidx + 1);
+
+  if (force_fignore == 0)
+    {
+      oldnames = strvec_create (nidx - 1);
+      oidx = 0;
+    }
+
+  newnames[0] = names[0];
+  for (idx = nidx = 1; names[idx]; idx++)
+    {
+      if ((*name_func) (names[idx]))
+       newnames[nidx++] = names[idx];
+      else if (force_fignore == 0)
+       oldnames[oidx++] = names[idx];
+      else
+       free (names[idx]);
+    }
+
+  newnames[nidx] = (char *)NULL;
+
+  /* If none are acceptable then let the completer handle it. */
+  if (nidx == 1)
+    {
+      if (force_fignore)
+       {
+         free (names[0]);
+         names[0] = (char *)NULL;
+       }
+      else
+       free (oldnames);
+
+      free (newnames);
+      return;
+    }
+
+  if (force_fignore == 0)
+    {
+      while (oidx)
+       free (oldnames[--oidx]);
+      free (oldnames);
+    }
+
+  /* If only one is acceptable, copy it to names[0] and return. */
+  if (nidx == 2)
+    {
+      free (names[0]);
+      names[0] = newnames[1];
+      names[1] = (char *)NULL;
+      free (newnames);
+      return;
+    }
+
+  /* Copy the acceptable names back to NAMES, set the new array end,
+     and return. */
+  for (nidx = 1; newnames[nidx]; nidx++)
+    names[nidx] = newnames[nidx];
+  names[nidx] = (char *)NULL;
+  free (newnames);
+}
+
+static int
+name_is_acceptable (name)
+     const char *name;
+{
+  struct ign *p;
+  int nlen;
+
+  for (nlen = strlen (name), p = fignore.ignores; p->val; p++)
+    {
+      if (nlen > p->len && p->len > 0 && STREQ (p->val, &name[nlen - p->len]))
+       return (0);
+    }
+
+  return (1);
+}
+
+#if 0
+static int
+ignore_dot_names (name)
+     char *name;
+{
+  return (name[0] != '.');
+}
+#endif
+
+static int
+filename_completion_ignore (names)
+     char **names;
+{
+#if 0
+  if (glob_dot_filenames == 0)
+    _ignore_completion_names (names, ignore_dot_names);
+#endif
+
+  setup_ignore_patterns (&fignore);
+
+  if (fignore.num_ignores == 0)
+    return 0;
+
+  _ignore_completion_names (names, name_is_acceptable);
+
+  return 0;
+}
+
+/* Return 1 if NAME is a directory. */
+static int
+test_for_directory (name)
+     const char *name;
+{
+  struct stat finfo;
+  char *fn;
+
+  fn = bash_tilde_expand (name, 0);
+  if (stat (fn, &finfo) != 0)
+    {
+      free (fn);
+      return 0;
+    }
+  free (fn);
+  return (S_ISDIR (finfo.st_mode));
+}
+
+/* Remove files from NAMES, leaving directories. */
+static int
+bash_ignore_filenames (names)
+     char **names;
+{
+  _ignore_completion_names (names, test_for_directory);
+  return 0;
+}
+
+static int
+return_zero (name)
+     const char *name;
+{
+  return 0;
+}
+
+static int
+bash_ignore_everything (names)
+     char **names;
+{
+  _ignore_completion_names (names, return_zero);
+  return 0;
+}
+
+/* Handle symbolic link references and other directory name
+   expansions while hacking completion. */
+static int
+bash_directory_completion_hook (dirname)
+     char **dirname;
+{
+  char *local_dirname, *new_dirname, *t;
+  int return_value, should_expand_dirname;
+  WORD_LIST *wl;
+  struct stat sb;
+
+  return_value = should_expand_dirname = 0;
+  local_dirname = *dirname;
+
+#if 0
+  should_expand_dirname = xstrchr (local_dirname, '$') || xstrchr (local_dirname, '`');
+#else
+  if (xstrchr (local_dirname, '$'))
+    should_expand_dirname = 1;
+  else
+    {
+      t = xstrchr (local_dirname, '`');
+      if (t && unclosed_pair (local_dirname, strlen (local_dirname), "`") == 0)
+       should_expand_dirname = 1;
+    }
+#endif
+
+#if defined (HAVE_LSTAT)
+  if (should_expand_dirname && lstat (local_dirname, &sb) == 0)
+#else
+  if (should_expand_dirname && stat (local_dirname, &sb) == 0)
+#endif
+    should_expand_dirname = 0;
+
+  if (should_expand_dirname)  
+    {
+      new_dirname = savestring (local_dirname);
+      wl = expand_prompt_string (new_dirname, 0);      /* does the right thing */
+      if (wl)
+       {
+         *dirname = string_list (wl);
+         /* Tell the completer to replace the directory name only if we
+            actually expanded something. */
+         return_value = STREQ (local_dirname, *dirname) == 0;
+         free (local_dirname);
+         free (new_dirname);
+         dispose_words (wl);
+         local_dirname = *dirname;
+       }
+      else
+       {
+         free (new_dirname);
+         free (local_dirname);
+         *dirname = (char *)xmalloc (1);
+         **dirname = '\0';
+         return 1;
+       }
+    }
+
+  if (!no_symbolic_links && (local_dirname[0] != '.' || local_dirname[1]))
+    {
+      char *temp1, *temp2;
+      int len1, len2;
+
+      t = get_working_directory ("symlink-hook");
+      temp1 = make_absolute (local_dirname, t);
+      free (t);
+      temp2 = sh_canonpath (temp1, PATH_CHECKDOTDOT|PATH_CHECKEXISTS);
+      /* If we can't canonicalize, bail. */
+      if (temp2 == 0)
+       {
+         free (temp1);
+         return 1;
+       }
+      len1 = strlen (temp1);
+      if (temp1[len1 - 1] == '/')
+       {
+         len2 = strlen (temp2);
+         temp2 = (char *)xrealloc (temp2, len2 + 2);
+         temp2[len2] = '/';
+         temp2[len2 + 1] = '\0';
+       }
+      free (local_dirname);
+      *dirname = temp2;
+      free (temp1);
+    }
+  return (return_value);
+}
+
+static char **history_completion_array = (char **)NULL;
+static int harry_size;
+static int harry_len;
+
+static void
+build_history_completion_array ()
+{
+  register int i, j;
+  HIST_ENTRY **hlist;
+  char **tokens;
+
+  /* First, clear out the current dynamic history completion list. */
+  if (harry_size)
+    {
+      strvec_dispose (history_completion_array);
+      history_completion_array = (char **)NULL;
+      harry_size = 0;
+      harry_len = 0;
+    }
+
+  /* Next, grovel each line of history, making each shell-sized token
+     a separate entry in the history_completion_array. */
+  hlist = history_list ();
+
+  if (hlist)
+    {
+      for (i = 0; hlist[i]; i++)
+       {
+         /* Separate each token, and place into an array. */
+         tokens = history_tokenize (hlist[i]->line);
+
+         for (j = 0; tokens && tokens[j]; j++)
+           {
+             if (harry_len + 2 > harry_size)
+               history_completion_array = strvec_resize (history_completion_array, harry_size += 10);
+
+             history_completion_array[harry_len++] = tokens[j];
+             history_completion_array[harry_len] = (char *)NULL;
+           }
+         free (tokens);
+       }
+
+      /* Sort the complete list of tokens. */
+      qsort (history_completion_array, harry_len, sizeof (char *), (QSFUNC *)strvec_strcmp);
+    }
+}
+
+static char *
+history_completion_generator (hint_text, state)
+     const char *hint_text;
+     int state;
+{
+  static int local_index, len;
+  static const char *text;
+
+  /* If this is the first call to the generator, then initialize the
+     list of strings to complete over. */
+  if (state == 0)
+    {
+      local_index = 0;
+      build_history_completion_array ();
+      text = hint_text;
+      len = strlen (text);
+    }
+
+  while (history_completion_array && history_completion_array[local_index])
+    {
+      if (strncmp (text, history_completion_array[local_index++], len) == 0)
+       return (savestring (history_completion_array[local_index - 1]));
+    }
+  return ((char *)NULL);
+}
+
+static int
+dynamic_complete_history (count, key)
+     int count, key;
+{
+  int r;
+
+  rl_compentry_func_t *orig_func;
+  rl_completion_func_t *orig_attempt_func;
+
+  orig_func = rl_completion_entry_function;
+  orig_attempt_func = rl_attempted_completion_function;
+  rl_completion_entry_function = history_completion_generator;
+  rl_attempted_completion_function = (rl_completion_func_t *)NULL;
+
+  /* XXX - use rl_completion_mode here? */
+  if (rl_last_func == dynamic_complete_history)
+    r = rl_complete_internal ('?');
+  else
+    r = rl_complete_internal (TAB);
+
+  rl_completion_entry_function = orig_func;
+  rl_attempted_completion_function = orig_attempt_func;
+  return r;
+}
+
+#if defined (SPECIFIC_COMPLETION_FUNCTIONS)
+static int
+bash_complete_username (ignore, ignore2)
+     int ignore, ignore2;
+{
+  return bash_complete_username_internal (rl_completion_mode (bash_complete_username));
+}
+
+static int
+bash_possible_username_completions (ignore, ignore2)
+     int ignore, ignore2;
+{
+  return bash_complete_username_internal ('?');
+}
+
+static int
+bash_complete_username_internal (what_to_do)
+     int what_to_do;
+{
+  return bash_specific_completion (what_to_do, rl_username_completion_function);
+}
+
+static int
+bash_complete_filename (ignore, ignore2)
+     int ignore, ignore2;
+{
+  return bash_complete_filename_internal (rl_completion_mode (bash_complete_filename));
+}
+
+static int
+bash_possible_filename_completions (ignore, ignore2)
+     int ignore, ignore2;
+{
+  return bash_complete_filename_internal ('?');
+}
+
+static int
+bash_complete_filename_internal (what_to_do)
+     int what_to_do;
+{
+  rl_compentry_func_t *orig_func;
+  rl_completion_func_t *orig_attempt_func;
+  rl_icppfunc_t *orig_dir_func;
+  /*const*/ char *orig_rl_completer_word_break_characters;
+  int r;
+
+  orig_func = rl_completion_entry_function;
+  orig_attempt_func = rl_attempted_completion_function;
+  orig_dir_func = rl_directory_completion_hook;
+  orig_rl_completer_word_break_characters = rl_completer_word_break_characters;
+  rl_completion_entry_function = rl_filename_completion_function;
+  rl_attempted_completion_function = (rl_completion_func_t *)NULL;
+  rl_directory_completion_hook = (rl_icppfunc_t *)NULL;
+  rl_completer_word_break_characters = " \t\n\"\'";
+
+  r = rl_complete_internal (what_to_do);
+
+  rl_completion_entry_function = orig_func;
+  rl_attempted_completion_function = orig_attempt_func;
+  rl_directory_completion_hook = orig_dir_func;
+  rl_completer_word_break_characters = orig_rl_completer_word_break_characters;
+
+  return r;
+}
+
+static int
+bash_complete_hostname (ignore, ignore2)
+     int ignore, ignore2;
+{
+  return bash_complete_hostname_internal (rl_completion_mode (bash_complete_hostname));
+}
+
+static int
+bash_possible_hostname_completions (ignore, ignore2)
+     int ignore, ignore2;
+{
+  return bash_complete_hostname_internal ('?');
+}
+
+static int
+bash_complete_variable (ignore, ignore2)
+     int ignore, ignore2;
+{
+  return bash_complete_variable_internal (rl_completion_mode (bash_complete_variable));
+}
+
+static int
+bash_possible_variable_completions (ignore, ignore2)
+     int ignore, ignore2;
+{
+  return bash_complete_variable_internal ('?');
+}
+
+static int
+bash_complete_command (ignore, ignore2)
+     int ignore, ignore2;
+{
+  return bash_complete_command_internal (rl_completion_mode (bash_complete_command));
+}
+
+static int
+bash_possible_command_completions (ignore, ignore2)
+     int ignore, ignore2;
+{
+  return bash_complete_command_internal ('?');
+}
+
+static int
+bash_complete_hostname_internal (what_to_do)
+     int what_to_do;
+{
+  return bash_specific_completion (what_to_do, hostname_completion_function);
+}
+
+static int
+bash_complete_variable_internal (what_to_do)
+     int what_to_do;
+{
+  return bash_specific_completion (what_to_do, variable_completion_function);
+}
+
+static int
+bash_complete_command_internal (what_to_do)
+     int what_to_do;
+{
+  return bash_specific_completion (what_to_do, command_word_completion_function);
+}
+
+static char *globtext;
+static char *globorig;
+
+static char *
+glob_complete_word (text, state)
+     const char *text;
+     int state;
+{
+  static char **matches = (char **)NULL;
+  static int ind;
+  int glen;
+  char *ret;
+
+  if (state == 0)
+    {
+      rl_filename_completion_desired = 1;
+      FREE (matches);
+      if (globorig != globtext)
+       FREE (globorig);
+      FREE (globtext);
+
+      if (rl_explicit_arg)
+       {
+         globorig = savestring (text);
+         glen = strlen (text);
+         globtext = (char *)xmalloc (glen + 2);
+         strcpy (globtext, text);
+         globtext[glen] = '*';
+         globtext[glen+1] = '\0';
+       }
+      else
+        globtext = globorig = savestring (text);
+
+      matches = shell_glob_filename (globtext);
+      if (GLOB_FAILED (matches))
+       matches = (char **)NULL;
+      ind = 0;
+    }
+
+  ret = matches ? matches[ind] : (char *)NULL;
+  ind++;
+  return ret;
+}
+
+static int
+bash_glob_completion_internal (what_to_do)
+     int what_to_do;
+{
+  return bash_specific_completion (what_to_do, glob_complete_word);
+}
+
+/* A special quoting function so we don't end up quoting globbing characters
+   in the word if there are no matches or multiple matches. */
+static char *
+bash_glob_quote_filename (s, rtype, qcp)
+     char *s;
+     int rtype;
+     char *qcp;
+{
+  if (globorig && qcp && *qcp == '\0' && STREQ (s, globorig))
+    return (savestring (s));
+  else
+    return (bash_quote_filename (s, rtype, qcp));
+}
+
+static int
+bash_glob_complete_word (count, key)
+     int count, key;
+{
+  int r;
+  rl_quote_func_t *orig_quoting_function;
+
+  if (rl_editing_mode == EMACS_EDITING_MODE)
+    rl_explicit_arg = 1;       /* force `*' append */
+  orig_quoting_function = rl_filename_quoting_function;
+  rl_filename_quoting_function = bash_glob_quote_filename;
+  
+  r = bash_glob_completion_internal (rl_completion_mode (bash_glob_complete_word));
+
+  rl_filename_quoting_function = orig_quoting_function;
+  return r;
+}
+
+static int
+bash_glob_expand_word (count, key)
+     int count, key;
+{
+  return bash_glob_completion_internal ('*');
+}
+
+static int
+bash_glob_list_expansions (count, key)
+     int count, key;
+{
+  return bash_glob_completion_internal ('?');
+}
+
+static int
+bash_specific_completion (what_to_do, generator)
+     int what_to_do;
+     rl_compentry_func_t *generator;
+{
+  rl_compentry_func_t *orig_func;
+  rl_completion_func_t *orig_attempt_func;
+  int r;
+
+  orig_func = rl_completion_entry_function;
+  orig_attempt_func = rl_attempted_completion_function;
+  rl_completion_entry_function = generator;
+  rl_attempted_completion_function = NULL;
+
+  r = rl_complete_internal (what_to_do);
+
+  rl_completion_entry_function = orig_func;
+  rl_attempted_completion_function = orig_attempt_func;
+
+  return r;
+}
+
+#endif /* SPECIFIC_COMPLETION_FUNCTIONS */
+
+#if defined (VI_MODE)
+/* Completion, from vi mode's point of view.  This is a modified version of
+   rl_vi_complete which uses the bash globbing code to implement what POSIX
+   specifies, which is to append a `*' and attempt filename generation (which
+   has the side effect of expanding any globbing characters in the word). */
+static int
+bash_vi_complete (count, key)
+     int count, key;
+{
+#if defined (SPECIFIC_COMPLETION_FUNCTIONS)
+  int p, r;
+  char *t;
+
+  if ((rl_point < rl_end) && (!whitespace (rl_line_buffer[rl_point])))
+    {
+      if (!whitespace (rl_line_buffer[rl_point + 1]))
+       rl_vi_end_word (1, 'E');
+      rl_point++;
+    }
+
+  /* Find boundaries of current word, according to vi definition of a
+     `bigword'. */
+  t = 0;
+  if (rl_point > 0)
+    {
+      p = rl_point;
+      rl_vi_bWord (1, 'B');
+      r = rl_point;
+      rl_point = p;
+      p = r;
+
+      t = substring (rl_line_buffer, p, rl_point);
+    }      
+
+  if (t && glob_pattern_p (t) == 0)
+    rl_explicit_arg = 1;       /* XXX - force glob_complete_word to append `*' */
+  FREE (t);
+
+  if (key == '*')      /* Expansion and replacement. */
+    r = bash_glob_expand_word (count, key);
+  else if (key == '=') /* List possible completions. */
+    r = bash_glob_list_expansions (count, key);
+  else if (key == '\\')        /* Standard completion */
+    r = bash_glob_complete_word (count, key);
+  else
+    r = rl_complete (0, key);
+
+  if (key == '*' || key == '\\')
+    rl_vi_start_inserting (key, 1, 1);
+
+  return (r);
+#else
+  return rl_vi_complete (count, key);
+#endif /* !SPECIFIC_COMPLETION_FUNCTIONS */
+}
+#endif /* VI_MODE */
+
+/* Filename quoting for completion. */
+/* A function to strip unquoted quote characters (single quotes, double
+   quotes, and backslashes).  It allows single quotes to appear
+   within double quotes, and vice versa.  It should be smarter. */
+static char *
+bash_dequote_filename (text, quote_char)
+     char *text;
+     int quote_char;
+{
+  char *ret, *p, *r;
+  int l, quoted;
+
+  l = strlen (text);
+  ret = (char *)xmalloc (l + 1);
+  for (quoted = quote_char, p = text, r = ret; p && *p; p++)
+    {
+      /* Allow backslash-quoted characters to pass through unscathed. */
+      if (*p == '\\')
+       {
+         *r++ = *++p;
+         if (*p == '\0')
+           break;
+         continue;
+       }
+      /* Close quote. */
+      if (quoted && *p == quoted)
+       {
+         quoted = 0;
+         continue;
+       }
+      /* Open quote. */
+      if (quoted == 0 && (*p == '\'' || *p == '"'))
+       {
+         quoted = *p;
+         continue;
+       }
+      *r++ = *p;
+    }
+  *r = '\0';
+  return ret;
+}
+
+/* Quote characters that the readline completion code would treat as
+   word break characters with backslashes.  Pass backslash-quoted
+   characters through without examination. */
+static char *
+quote_word_break_chars (text)
+     char *text;
+{
+  char *ret, *r, *s;
+  int l;
+
+  l = strlen (text);
+  ret = (char *)xmalloc ((2 * l) + 1);
+  for (s = text, r = ret; *s; s++)
+    {
+      /* Pass backslash-quoted characters through, including the backslash. */
+      if (*s == '\\')
+       {
+         *r++ = '\\';
+         *r++ = *++s;
+         if (*s == '\0')
+           break;
+         continue;
+       }
+      /* OK, we have an unquoted character.  Check its presence in
+        rl_completer_word_break_characters. */
+      if (xstrchr (rl_completer_word_break_characters, *s))
+       *r++ = '\\';
+      *r++ = *s;
+    }
+  *r = '\0';
+  return ret;
+}
+
+/* Quote a filename using double quotes, single quotes, or backslashes
+   depending on the value of completion_quoting_style.  If we're
+   completing using backslashes, we need to quote some additional
+   characters (those that readline treats as word breaks), so we call
+   quote_word_break_chars on the result.  This returns newly-allocated
+   memory. */
+static char *
+bash_quote_filename (s, rtype, qcp)
+     char *s;
+     int rtype;
+     char *qcp;
+{
+  char *rtext, *mtext, *ret;
+  int rlen, cs;
+
+  rtext = (char *)NULL;
+
+  /* If RTYPE == MULT_MATCH, it means that there is
+     more than one match.  In this case, we do not add
+     the closing quote or attempt to perform tilde
+     expansion.  If RTYPE == SINGLE_MATCH, we try
+     to perform tilde expansion, because single and double
+     quotes inhibit tilde expansion by the shell. */
+
+  mtext = s;
+  if (mtext[0] == '~' && rtype == SINGLE_MATCH)
+    mtext = bash_tilde_expand (s, 0);
+
+  cs = completion_quoting_style;
+  /* Might need to modify the default completion style based on *qcp,
+     since it's set to any user-provided opening quote.  We also change
+     to single-quoting if there is no user-provided opening quote and
+     the word being completed contains newlines, since those are not
+     quoted correctly using backslashes (a backslash-newline pair is
+     special to the shell parser). */
+  if (*qcp == '\0' && cs == COMPLETE_BSQUOTE && xstrchr (mtext, '\n'))
+    cs = COMPLETE_SQUOTE;
+  else if (*qcp == '"')
+    cs = COMPLETE_DQUOTE;
+  else if (*qcp == '\'')
+    cs = COMPLETE_SQUOTE;
+#if defined (BANG_HISTORY)
+  else if (*qcp == '\0' && history_expansion && cs == COMPLETE_DQUOTE &&
+          history_expansion_inhibited == 0 && xstrchr (mtext, '!'))
+    cs = COMPLETE_BSQUOTE;
+
+  if (*qcp == '"' && history_expansion && cs == COMPLETE_DQUOTE &&
+       history_expansion_inhibited == 0 && xstrchr (mtext, '!'))
+    {
+      cs = COMPLETE_BSQUOTE;
+      *qcp = '\0';
+    }
+#endif
+
+  switch (cs)
+    {
+    case COMPLETE_DQUOTE:
+      rtext = sh_double_quote (mtext);
+      break;
+    case COMPLETE_SQUOTE:
+      rtext = sh_single_quote (mtext);
+      break;
+    case COMPLETE_BSQUOTE:
+      rtext = sh_backslash_quote (mtext);
+      break;
+    }
+
+  if (mtext != s)
+    free (mtext);
+
+  /* We may need to quote additional characters: those that readline treats
+     as word breaks that are not quoted by backslash_quote. */
+  if (rtext && cs == COMPLETE_BSQUOTE)
+    {
+      mtext = quote_word_break_chars (rtext);
+      free (rtext);
+      rtext = mtext;
+    }
+
+  /* Leave the opening quote intact.  The readline completion code takes
+     care of avoiding doubled opening quotes. */
+  rlen = strlen (rtext);
+  ret = (char *)xmalloc (rlen + 1);
+  strcpy (ret, rtext);
+
+  /* If there are multiple matches, cut off the closing quote. */
+  if (rtype == MULT_MATCH && cs != COMPLETE_BSQUOTE)
+    ret[rlen - 1] = '\0';
+  free (rtext);
+  return ret;
+}
+
+/* Support for binding readline key sequences to Unix commands. */
+static Keymap cmd_xmap;
+
+static int
+bash_execute_unix_command (count, key)
+     int count;        /* ignored */
+     int key;
+{
+  Keymap ckmap;                /* current keymap */
+  Keymap xkmap;                /* unix command executing keymap */
+  register int i;
+  char *cmd;
+  sh_parser_state_t ps;
+
+  /* First, we need to find the right command to execute.  This is tricky,
+     because we might have already indirected into another keymap. */
+  ckmap = rl_get_keymap ();
+  if (ckmap != rl_executing_keymap)
+    {
+      /* bogus.  we have to search.  only handle one level of indirection. */
+      for (i = 0; i < KEYMAP_SIZE; i++)
+       {
+         if (ckmap[i].type == ISKMAP && (Keymap)ckmap[i].function == rl_executing_keymap)
+           break;
+       }
+      if (i < KEYMAP_SIZE)
+       xkmap = (Keymap)cmd_xmap[i].function;
+      else
+       {
+         rl_crlf ();
+         internal_error (_("bash_execute_unix_command: cannot find keymap for command"));
+         rl_forced_update_display ();
+         return 1;
+       }
+    }
+  else
+    xkmap = cmd_xmap;
+
+  cmd = (char *)xkmap[key].function;
+
+  if (cmd == 0)
+    {
+      rl_ding ();
+      return 1;
+    }
+
+  rl_crlf ();  /* move to a new line */
+
+  save_parser_state (&ps);
+
+  cmd = savestring (cmd);
+  parse_and_execute (cmd, "bash_execute_unix_command", SEVAL_NOHIST);
+
+  restore_parser_state (&ps);
+
+  /* and restore the readline buffer and display after command execution. */
+  rl_forced_update_display ();
+  return 0;
+}
+
+static void
+init_unix_command_map ()
+{
+  cmd_xmap = rl_make_bare_keymap ();
+}
+
+static int
+isolate_sequence (string, ind, need_dquote, startp)
+     char *string;
+     int ind, need_dquote, *startp;
+{
+  register int i;
+  int c, passc, delim;
+
+  for (i = ind; string[i] && whitespace (string[i]); i++)
+    ;
+  /* NEED_DQUOTE means that the first non-white character *must* be `"'. */
+  if (need_dquote && string[i] != '"')
+    {
+      builtin_error (_("%s: first non-whitespace character is not `\"'"), string);
+      return -1;
+    }
+
+  /* We can have delimited strings even if NEED_DQUOTE == 0, like the command
+     string to bind the key sequence to. */
+  delim = (string[i] == '"' || string[i] == '\'') ? string[i] : 0;
+    
+  if (startp)
+    *startp = delim ? ++i : i;
+
+  for (passc = 0; c = string[i]; i++)
+    {
+      if (passc)
+       {
+         passc = 0;
+         continue;
+       }
+      if (c == '\\')
+       {
+         passc++;
+         continue;
+       }
+      if (c == delim)
+       break;
+    }
+
+  if (delim && string[i] != delim)
+    {
+      builtin_error (_("no closing `%c' in %s"), delim, string);
+      return -1;
+    }
+
+  return i;
+}
+
+int
+bind_keyseq_to_unix_command (line)
+     char *line;
+{
+  Keymap kmap;
+  char *kseq, *value;
+  int i, kstart;
+
+  if (cmd_xmap == 0)
+    init_unix_command_map ();
+
+  kmap = rl_get_keymap ();
+
+  /* We duplicate some of the work done by rl_parse_and_bind here, but
+     this code only has to handle `"keyseq": ["]command["]' and can
+     generate an error for anything else. */
+  i = isolate_sequence (line, 0, 1, &kstart);
+  if (i < 0)
+    return -1;
+
+  /* Create the key sequence string to pass to rl_generic_bind */
+  kseq = substring (line, kstart, i);
+
+  for ( ; line[i] && line[i] != ':'; i++)
+    ;
+  if (line[i] != ':')
+    {
+      builtin_error (_("%s: missing colon separator"), line);
+      return -1;
+    }
+
+  i = isolate_sequence (line, i + 1, 0, &kstart);
+  if (i < 0)
+    return -1;
+
+  /* Create the value string containing the command to execute. */
+  value = substring (line, kstart, i);
+
+  /* Save the command to execute and the key sequence in the CMD_XMAP */
+  rl_generic_bind (ISMACR, kseq, value, cmd_xmap);
+
+  /* and bind the key sequence in the current keymap to a function that
+     understands how to execute from CMD_XMAP */
+  rl_bind_keyseq_in_map (kseq, bash_execute_unix_command, kmap);
+  
+  return 0;
+}
+
+/* Used by the programmable completion code.  Complete TEXT as a filename,
+   but return only directories as matches.  Dequotes the filename before
+   attempting to find matches. */
+char **
+bash_directory_completion_matches (text)
+     const char *text;
+{
+  char **m1;
+  char *dfn;
+  int qc;
+
+#if 0
+  qc = (text[0] == '"' || text[0] == '\'') ? text[0] : 0;
+#else
+  qc = rl_dispatching ? rl_completion_quote_character : 0;  
+#endif
+  dfn = bash_dequote_filename ((char *)text, qc);
+  m1 = rl_completion_matches (dfn, rl_filename_completion_function);
+  free (dfn);
+
+  if (m1 == 0 || m1[0] == 0)
+    return m1;
+  /* We don't bother recomputing the lcd of the matches, because it will just
+     get thrown away by the programmable completion code and recomputed
+     later. */
+  (void)bash_ignore_filenames (m1);
+  return m1;
+}
+
+char *
+bash_dequote_text (text)
+     const char *text;
+{
+  char *dtxt;
+  int qc;
+
+  qc = (text[0] == '"' || text[0] == '\'') ? text[0] : 0;
+  dtxt = bash_dequote_filename ((char *)text, qc);
+  return (dtxt);
+}
+#endif /* READLINE */
index 4bb4ea5eabd70fa9b1f6cf0175dc39886174b0db..df648c5e224b7b069f325af0541347acc1110520 100644 (file)
@@ -178,8 +178,7 @@ helpdoc:    $(MKBUILTINS) $(DEFSRC)
 install-help:
        @-if test -n "${HELPDIR}" && test -d helpfiles ; then \
                test -d ${HELPDIR} || ${SHELL} ${MKDIRS} $(DESTDIR)$(HELPDIR) ;\
-               ( cd helpfiles ; \
-                 for f in *; do \
+               ( for f in helpfiles/*; do \
                        echo installing $$f; \
                        ${INSTALL_DATA} $$f $(DESTDIR)$(HELPDIR); \
                  done; ) ; \
diff --git a/builtins/Makefile.in~ b/builtins/Makefile.in~
new file mode 100644 (file)
index 0000000..4bb4ea5
--- /dev/null
@@ -0,0 +1,621 @@
+# This Makefile for building libbuiltins.a is in -*- text -*- for Emacs.
+#
+# Copyright (C) 1996-2003 Free Software Foundation, Inc.     
+
+# This program 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 2, or (at your option)
+# any later version.
+
+# This program 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 this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
+
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+
+#
+SHELL = @MAKE_SHELL@
+RANLIB = @RANLIB@
+CC = @CC@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+AR = @AR@
+ARFLAGS = @ARFLAGS@
+RM = rm -f
+CP = cp
+
+EXEEXT = @EXEEXT@
+
+prefix = @prefix@
+
+srcdir = @srcdir@
+VPATH = .:@srcdir@
+topdir = @top_srcdir@
+includedir = @includedir@
+datadir = @datadir@
+localedir = $(datadir)/locale
+
+# Support an alternate destination root directory for package building
+DESTDIR =
+
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+BUILD_DIR = @BUILD_DIR@
+
+LIBBUILD = ${BUILD_DIR}/lib
+
+PROFILE_FLAGS = @PROFILE_FLAGS@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+LOCAL_CFLAGS = @LOCAL_CFLAGS@ ${DEBUG}
+DEFS = @DEFS@
+LOCAL_DEFS = @LOCAL_DEFS@
+
+LIBS = @LIBS@
+LDFLAGS = @LDFLAGS@ $(LOCAL_LDFLAGS) $(CFLAGS)
+LDFLAGS_FOR_BUILD = $(LDFLAGS)
+LOCAL_LDFLAGS = @LOCAL_LDFLAGS@
+#LIBS_FOR_BUILD = @LIBS_FOR_BUILD@
+LIBS_FOR_BUILD = $(LIBS)
+
+BASHINCDIR = ${topdir}/include
+
+RL_INCLUDEDIR = @RL_INCLUDEDIR@
+
+INTL_LIBSRC = ${topdir}/lib/intl
+INTL_BUILDDIR = ${LIBBUILD}/intl
+INTL_INC = @INTL_INC@
+LIBINTL_H = @LIBINTL_H@
+
+HELPDIR = @HELPDIR@
+MKDIRS = ${topdir}/support/mkdirs
+
+INCLUDES = -I. -I.. @RL_INCLUDE@ -I$(topdir) -I$(BASHINCDIR) -I$(topdir)/lib -I$(srcdir) ${INTL_INC}
+
+BASE_CCFLAGS = ${PROFILE_FLAGS} $(DEFS) $(LOCAL_DEFS) $(SYSTEM_FLAGS) \
+        ${INCLUDES} $(LOCAL_CFLAGS)
+
+CCFLAGS = $(BASE_CCFLAGS) $(CPPFLAGS) $(CFLAGS)
+
+CCFLAGS_FOR_BUILD = $(BASE_CCFLAGS) $(CPPFLAGS_FOR_BUILD) $(CFLAGS_FOR_BUILD)
+
+GCC_LINT_FLAGS = -Wall -Wshadow -Wpointer-arith -Wcast-qual \
+                -Wcast-align -Wstrict-prototypes -Wconversion \
+                -Wmissing-prototypes -Wtraditional -Wredundant-decls -pedantic
+
+MKBUILTINS = mkbuiltins$(EXEEXT)
+DIRECTDEFINE = -D $(srcdir)
+HELPDIRDEFINE = @HELPDIRDEFINE@
+
+# xxx this is bad style
+RL_LIBSRC = $(topdir)/lib/readline
+
+.SUFFIXES:
+.SUFFIXES: .def .c .o
+# How to make a .o file from a .def file.
+.def.o:
+       $(RM) $@
+       ./$(MKBUILTINS) $(DIRECTDEFINE) $<
+       $(CC) -c $(CCFLAGS) $*.c || ( $(RM) $*.c ; exit 1 )
+       $(RM) $*.c
+
+# How to make a .c file from a .def file.
+.def.c:
+       $(RM) $@
+       ./$(MKBUILTINS) $(DIRECTDEFINE) $<
+
+# default rule for making a .o file from a .c file
+.c.o:
+       $(RM) $@
+       $(CC) -c $(CCFLAGS) $<
+
+DEFSRC =  $(srcdir)/alias.def $(srcdir)/bind.def $(srcdir)/break.def \
+         $(srcdir)/builtin.def $(srcdir)/caller.def \
+         $(srcdir)/cd.def $(srcdir)/colon.def \
+         $(srcdir)/command.def $(srcdir)/declare.def $(srcdir)/echo.def \
+         $(srcdir)/enable.def $(srcdir)/eval.def $(srcdir)/getopts.def \
+         $(srcdir)/exec.def $(srcdir)/exit.def $(srcdir)/fc.def \
+         $(srcdir)/fg_bg.def $(srcdir)/hash.def $(srcdir)/help.def \
+         $(srcdir)/history.def $(srcdir)/jobs.def $(srcdir)/kill.def \
+         $(srcdir)/let.def $(srcdir)/read.def $(srcdir)/return.def \
+         $(srcdir)/set.def $(srcdir)/setattr.def $(srcdir)/shift.def \
+         $(srcdir)/source.def $(srcdir)/suspend.def $(srcdir)/test.def \
+         $(srcdir)/times.def $(srcdir)/trap.def $(srcdir)/type.def \
+         $(srcdir)/ulimit.def $(srcdir)/umask.def $(srcdir)/wait.def \
+         $(srcdir)/reserved.def $(srcdir)/pushd.def $(srcdir)/shopt.def \
+         $(srcdir)/printf.def $(srcdir)/complete.def
+
+STATIC_SOURCE = common.c evalstring.c evalfile.c getopt.c bashgetopt.c \
+               getopt.h 
+
+OFILES = builtins.o \
+       alias.o bind.o break.o builtin.o caller.o cd.o colon.o command.o \
+       common.o declare.o echo.o enable.o eval.o evalfile.o \
+       evalstring.o exec.o \
+       exit.o fc.o fg_bg.o hash.o help.o history.o jobs.o kill.o let.o \
+       pushd.o read.o return.o set.o setattr.o shift.o source.o \
+       suspend.o test.o times.o trap.o type.o ulimit.o umask.o \
+       wait.o getopts.o shopt.o printf.o getopt.o bashgetopt.o complete.o
+
+CREATED_FILES = builtext.h builtins.c psize.aux pipesize.h
+
+all: $(MKBUILTINS) libbuiltins.a
+
+libbuiltins.a: $(MKBUILTINS) $(OFILES) builtins.o
+       $(RM) $@
+       $(AR) $(ARFLAGS) $@ $(OFILES)
+       -$(RANLIB) $@
+
+builtext.h builtins.c: $(MKBUILTINS) $(DEFSRC)
+       @-if test -f builtins.c; then mv -f builtins.c old-builtins.c; fi
+       @-if test -f builtext.h; then mv -f builtext.h old-builtext.h; fi
+       ./$(MKBUILTINS) -externfile builtext.h -structfile builtins.c \
+           -noproduction $(DIRECTDEFINE) $(HELPDIRDEFINE) $(DEFSRC)
+       @-if cmp -s old-builtext.h builtext.h 2>/dev/null; then \
+               mv old-builtext.h builtext.h; \
+        else \
+               $(RM) old-builtext.h; \
+        fi
+       @-if cmp -s old-builtins.c builtins.c 2>/dev/null; then \
+               mv old-builtins.c builtins.c; \
+        else \
+               $(RM) old-builtins.c; \
+        fi
+
+helpdoc:       $(MKBUILTINS) $(DEFSRC)
+       ./$(MKBUILTINS) ${HELPDIRDEFINE} -noproduction $(DIRECTDEFINE) $(DEFSRC)
+
+install-help:
+       @-if test -n "${HELPDIR}" && test -d helpfiles ; then \
+               test -d ${HELPDIR} || ${SHELL} ${MKDIRS} $(DESTDIR)$(HELPDIR) ;\
+               ( cd helpfiles ; \
+                 for f in *; do \
+                       echo installing $$f; \
+                       ${INSTALL_DATA} $$f $(DESTDIR)$(HELPDIR); \
+                 done; ) ; \
+       fi
+
+install:       @HELPINSTALL@
+
+mkbuiltins.o: ../config.h
+mkbuiltins.o: mkbuiltins.c
+       $(RM) $@
+       $(CC_FOR_BUILD) -c $(CCFLAGS_FOR_BUILD) $<
+
+mkbuiltins$(EXEEXT): mkbuiltins.o
+       $(CC_FOR_BUILD) $(LDFLAGS_FOR_BUILD) -o $(MKBUILTINS) mkbuiltins.o $(LIBS_FOR_BUILD)
+
+# rules for deficient makes, like SunOS
+mkbuiltins.o: mkbuiltins.c
+builtins.o: builtins.c
+common.o: common.c
+bashgetopt.o: bashgetopt.c
+getopt.o: getopt.c
+evalstring.o: evalstring.c          
+evalfile.o: evalfile.c  
+
+ulimit.o: pipesize.h
+
+pipesize.h:    psize.aux
+       $(SHELL) $(srcdir)/psize.sh > $@
+
+psize.aux:     psize.c
+       $(CC_FOR_BUILD) $(CCFLAGS_FOR_BUILD) -o $@ $(srcdir)/psize.c
+
+documentation: builtins.texi
+
+builtins.texi: $(MKBUILTINS)
+       ./$(MKBUILTINS) -documentonly $(DEFSRC)
+
+clean:
+       $(RM) $(OFILES) $(CREATED_FILES) $(MKBUILTINS) mkbuiltins.o libbuiltins.a
+       -test -d helpfiles && $(RM) -r helpfiles
+
+mostlyclean: 
+       $(RM) $(OFILES) libbuiltins.a
+
+distclean maintainer-clean: clean
+       $(RM) Makefile
+
+$(OFILES):     $(MKBUILTINS) ../config.h
+
+../version.h: ../config.h ../Makefile Makefile
+       -( cd ${BUILD_DIR} && ${MAKE} ${MFLAGS} version.h )
+
+# maintainer special - for now
+po:    builtins.c
+       xgettext -L C -o $(topdir)/po/builtins.pot --keyword='N_' builtins.c 2>/dev/null
+
+# dependencies
+
+alias.o: alias.def
+bind.o: bind.def
+break.o: break.def
+builtin.o: builtin.def
+caller.o: caller.def
+cd.o: cd.def
+colon.o: colon.def
+command.o: command.def
+declare.o: declare.def
+echo.o: echo.def
+enable.o: enable.def
+eval.o: eval.def
+exec.o: exec.def
+exit.o: exit.def
+fc.o: fc.def
+fg_bg.o: fg_bg.def
+hash.o: hash.def
+help.o: help.def
+history.o: history.def
+jobs.o: jobs.def
+kill.o: kill.def
+let.o: let.def
+printf.o: printf.def
+pushd.o: pushd.def
+read.o: read.def
+return.o: return.def
+set.o: set.def
+setattr.o: setattr.def
+shift.o: shift.def
+shopt.o: shopt.def
+source.o: source.def
+suspend.o: suspend.def
+test.o: test.def
+times.o: times.def
+trap.o: trap.def
+type.o: type.def
+ulimit.o: ulimit.def
+umask.o: umask.def
+wait.o: wait.def
+getopts.o: getopts.def
+reserved.o: reserved.def
+complete.o: complete.def
+
+# C files
+bashgetopt.o: ../config.h $(topdir)/bashansi.h $(BASHINCDIR)/ansi_stdlib.h
+bashgetopt.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/bashjmp.h
+bashgetopt.o: $(topdir)/command.h $(topdir)/general.h $(topdir)/xmalloc.h $(topdir)/error.h
+bashgetopt.o: $(topdir)/variables.h $(topdir)/conftypes.h $(topdir)/quit.h $(BASHINCDIR)/maxpath.h
+bashgetopt.o: $(topdir)/unwind_prot.h $(topdir)/dispose_cmd.h
+bashgetopt.o: $(topdir)/make_cmd.h $(topdir)/subst.h $(topdir)/sig.h
+bashgetopt.o: ../pathnames.h $(topdir)/externs.h $(srcdir)/common.h
+bashgetopt.o: $(BASHINCDIR)/chartypes.h
+common.o: $(topdir)/bashtypes.h $(BASHINCDIR)/posixstat.h $(topdir)/bashansi.h $(BASHINCDIR)/ansi_stdlib.h
+common.o: $(topdir)/shell.h $(topdir)/syntax.h ../config.h $(topdir)/bashjmp.h $(BASHINCDIR)/posixjmp.h
+common.o: $(topdir)/sig.h $(topdir)/command.h
+common.o: $(topdir)/general.h $(topdir)/xmalloc.h $(BASHINCDIR)/stdc.h $(BASHINCDIR)/memalloc.h
+common.o: $(topdir)/variables.h $(topdir)/conftypes.h $(topdir)/input.h
+common.o: $(topdir)/siglist.h $(topdir)/bashhist.h $(topdir)/quit.h
+common.o: $(topdir)/unwind_prot.h $(BASHINCDIR)/maxpath.h $(topdir)/jobs.h
+common.o: $(topdir)/builtins.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
+common.o: $(topdir)/subst.h $(topdir)/execute_cmd.h $(topdir)/error.h
+common.o: $(topdir)/externs.h ../pathnames.h ./builtext.h
+common.o: $(BASHINCDIR)/chartypes.h
+evalfile.o: $(topdir)/bashtypes.h $(BASHINCDIR)/posixstat.h ${BASHINCDIR}/filecntl.h
+evalfile.o: $(topdir)/bashansi.h $(BASHINCDIR)/ansi_stdlib.h
+evalfile.o: $(topdir)/shell.h $(topdir)/syntax.h ../config.h $(topdir)/bashjmp.h
+evalfile.o: $(topdir)/command.h $(topdir)/general.h $(topdir)/xmalloc.h $(topdir)/error.h
+evalfile.o: $(topdir)/variables.h $(topdir)/conftypes.h $(topdir)/quit.h $(BASHINCDIR)/maxpath.h
+evalfile.o: $(topdir)/unwind_prot.h $(topdir)/dispose_cmd.h
+evalfile.o: $(topdir)/make_cmd.h $(topdir)/subst.h $(topdir)/sig.h
+evalfile.o: ../pathnames.h $(topdir)/externs.h
+evalfile.o: $(topdir)/jobs.h $(topdir)/builtins.h $(topdir)/flags.h
+evalfile.o: $(topdir)/input.h $(topdir)/execute_cmd.h
+evalfile.o: $(topdir)/bashhist.h $(srcdir)/common.h
+evalstring.o: ../config.h $(topdir)/bashansi.h $(BASHINCDIR)/ansi_stdlib.h
+evalstring.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/bashjmp.h $(BASHINCDIR)/posixjmp.h
+evalstring.o: $(topdir)/sig.h $(topdir)/command.h $(topdir)/siglist.h
+evalstring.o: $(BASHINCDIR)/memalloc.h $(topdir)/variables.h $(topdir)/conftypes.h $(topdir)/input.h
+evalstring.o: $(topdir)/quit.h $(topdir)/unwind_prot.h
+evalstring.o: $(BASHINCDIR)/maxpath.h $(topdir)/jobs.h $(topdir)/builtins.h
+evalstring.o: $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h $(topdir)/subst.h
+evalstring.o: $(topdir)/externs.h $(topdir)/jobs.h $(topdir)/builtins.h
+evalstring.o: $(topdir)/flags.h $(topdir)/input.h $(topdir)/execute_cmd.h
+evalstring.o: $(topdir)/bashhist.h $(srcdir)/common.h
+evalstring.o: $(topdir)/trap.h $(topdir)/redir.h
+getopt.o: ../config.h $(BASHINCDIR)/memalloc.h
+getopt.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/bashjmp.h $(topdir)/command.h
+getopt.o: $(topdir)/general.h $(topdir)/xmalloc.h $(topdir)/error.h $(topdir)/variables.h $(topdir)/conftypes.h
+getopt.o: $(topdir)/quit.h $(BASHINCDIR)/maxpath.h $(topdir)/unwind_prot.h
+getopt.o: $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h $(topdir)/subst.h
+getopt.o: $(topdir)/sig.h ../pathnames.h $(topdir)/externs.h
+getopt.o: $(srcdir)/getopt.h 
+mkbuiltins.o: ../config.h $(topdir)/bashtypes.h $(BASHINCDIR)/posixstat.h
+mkbuiltins.o: ${BASHINCDIR}/filecntl.h
+mkbuiltins.o: $(topdir)/bashansi.h $(BASHINCDIR)/ansi_stdlib.h
+
+# def files
+alias.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h
+alias.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h $(BASHINCDIR)/maxpath.h
+alias.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
+alias.o: $(topdir)/subst.h $(topdir)/externs.h $(srcdir)/common.h
+alias.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h
+bind.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h $(topdir)/error.h
+bind.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
+bind.o: $(topdir)/subst.h $(topdir)/externs.h $(srcdir)/bashgetopt.h
+bind.o: $(topdir)/general.h $(topdir)/xmalloc.h $(BASHINCDIR)/maxpath.h $(topdir)/bashline.h
+bind.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h 
+break.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h 
+break.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h
+break.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
+break.o: $(topdir)/subst.h $(topdir)/externs.h $(BASHINCDIR)/maxpath.h
+break.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h
+builtin.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h
+builtin.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h $(topdir)/externs.h
+builtin.o: $(topdir)/quit.h $(srcdir)/common.h $(BASHINCDIR)/maxpath.h
+builtin.o: $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h $(topdir)/subst.h
+builtin.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h 
+builtin.o: $(srcdir)/bashgetopt.h
+caller.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h $(topdir)/error.h
+caller.o: $(topdir)/general.h $(topdir)/xmalloc.h $(topdir)/quit.h $(topdir)/dispose_cmd.h
+caller.o: $(topdir)/make_cmd.h $(topdir)/subst.h $(topdir)/externs.h
+caller.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h
+caller.o: $(srcdir)/common.h $(BASHINCDIR)/maxpath.h ./builtext.h
+caller.o: ${BASHINCDIR}/chartypes.h $(topdir)/bashtypes.h
+cd.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h $(topdir)/error.h
+cd.o: $(topdir)/general.h $(topdir)/xmalloc.h $(topdir)/quit.h $(topdir)/dispose_cmd.h
+cd.o: $(topdir)/make_cmd.h $(topdir)/subst.h $(topdir)/externs.h
+cd.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h
+cd.o: $(srcdir)/common.h $(BASHINCDIR)/maxpath.h
+command.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h
+command.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h $(topdir)/externs.h
+command.o: $(topdir)/quit.h $(srcdir)/bashgetopt.h $(BASHINCDIR)/maxpath.h
+command.o: $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h $(topdir)/subst.h
+command.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h 
+declare.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h
+declare.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h
+declare.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
+declare.o: $(topdir)/subst.h $(topdir)/externs.h $(BASHINCDIR)/maxpath.h
+declare.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h
+declare.o: $(topdir)/arrayfunc.h $(srcdir)/bashgetopt.h
+declare.o: ./builtext.h
+echo.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h $(topdir)/error.h
+echo.o: $(topdir)/general.h $(topdir)/xmalloc.h $(topdir)/subst.h $(topdir)/externs.h
+echo.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
+echo.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h
+echo.o: $(BASHINCDIR)/maxpath.h
+enable.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h
+enable.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h
+enable.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
+enable.o: $(topdir)/subst.h $(topdir)/externs.h
+enable.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h
+enable.o: $(BASHINCDIR)/maxpath.h
+enable.o: $(topdir)/pcomplete.h
+eval.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h
+eval.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h
+eval.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
+eval.o: $(topdir)/subst.h $(topdir)/externs.h 
+eval.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h
+eval.o: $(BASHINCDIR)/maxpath.h
+exec.o: $(topdir)/bashtypes.h
+exec.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h
+exec.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h
+exec.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
+exec.o: $(topdir)/subst.h $(topdir)/externs.h $(topdir)/flags.h
+exec.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h
+exec.o: $(srcdir)/common.h $(topdir)/execute_cmd.h $(BASHINCDIR)/maxpath.h
+exec.o: $(topdir)/findcmd.h
+exit.o: $(topdir)/bashtypes.h
+exit.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h
+exit.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h
+exit.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
+exit.o: $(topdir)/subst.h $(topdir)/externs.h 
+exit.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h
+exit.o: $(BASHINCDIR)/maxpath.h ./builtext.h
+fc.o: $(topdir)/bashtypes.h $(BASHINCDIR)/posixstat.h
+fc.o: $(topdir)/builtins.h $(topdir)/command.h $(srcdir)/bashgetopt.h
+fc.o: $(topdir)/bashhist.h
+fc.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h $(topdir)/error.h
+fc.o: $(topdir)/general.h $(topdir)/xmalloc.h $(BASHINCDIR)/maxpath.h
+fc.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
+fc.o: $(topdir)/subst.h $(topdir)/externs.h $(topdir)/shell.h $(topdir)/syntax.h
+fc.o: $(topdir)/flags.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h
+fc.o: $(topdir)/bashansi.h $(BASHINCDIR)/ansi_stdlib.h $(BASHINCDIR)/chartypes.h
+fg_bg.o: $(topdir)/bashtypes.h $(srcdir)/bashgetopt.h
+fg_bg.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h
+fg_bg.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h
+fg_bg.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
+fg_bg.o: $(topdir)/subst.h $(topdir)/externs.h $(BASHINCDIR)/maxpath.h
+fg_bg.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h
+getopts.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h
+getopts.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h
+getopts.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
+getopts.o: $(topdir)/subst.h $(topdir)/externs.h $(BASHINCDIR)/maxpath.h
+getopts.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h
+hash.o: $(topdir)/builtins.h $(topdir)/command.h $(topdir)/quit.h
+hash.o: $(topdir)/findcmd.h $(topdir)/hashlib.h
+hash.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h
+hash.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h
+hash.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h
+hash.o: $(srcdir)/common.h $(BASHINCDIR)/maxpath.h
+help.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h
+help.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h
+help.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
+help.o: $(topdir)/subst.h $(topdir)/externs.h $(BASHINCDIR)/maxpath.h
+help.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h
+help.o: ${srcdir}/common.h
+history.o: $(topdir)/bashtypes.h
+history.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h
+history.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h
+history.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
+history.o: $(topdir)/subst.h $(topdir)/externs.h 
+history.o: ${BASHINCDIR}/filecntl.h $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h
+history.o: $(topdir)/variables.h $(topdir)/conftypes.h $(topdir)/bashhist.h $(BASHINCDIR)/maxpath.h
+inlib.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h
+inlib.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h
+inlib.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h
+inlib.o: $(BASHINCDIR)/maxpath.h $(topdir)/subst.h $(topdir)/externs.h
+inlib.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
+jobs.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h $(topdir)/error.h
+jobs.o: $(topdir)/general.h $(topdir)/xmalloc.h $(topdir)/quit.h $(srcdir)/bashgetopt.h
+jobs.o: $(BASHINCDIR)/maxpath.h $(topdir)/externs.h 
+jobs.o: $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h $(topdir)/subst.h
+jobs.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h 
+kill.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h $(topdir)/error.h
+kill.o: $(topdir)/general.h $(topdir)/xmalloc.h $(topdir)/subst.h $(topdir)/externs.h
+kill.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
+kill.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/trap.h $(topdir)/unwind_prot.h
+kill.o: $(topdir)/variables.h $(topdir)/conftypes.h $(BASHINCDIR)/maxpath.h
+let.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h
+let.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h
+let.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
+let.o: $(topdir)/subst.h $(topdir)/externs.h $(BASHINCDIR)/maxpath.h
+let.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h
+printf.o: ../config.h $(BASHINCDIR)/memalloc.h $(topdir)/bashjmp.h
+printf.o: $(topdir)/command.h $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h
+printf.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
+printf.o: $(topdir)/subst.h $(topdir)/externs.h $(topdir)/sig.h
+printf.o: ../pathnames.h $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h
+printf.o: $(topdir)/variables.h $(topdir)/conftypes.h $(BASHINCDIR)/stdc.h $(srcdir)/bashgetopt.h
+printf.o: $(topdir)/bashtypes.h ${srcdir}/common.h $(BASHINCDIR)/chartypes.h
+pushd.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h
+pushd.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h
+pushd.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
+pushd.o: $(topdir)/subst.h $(topdir)/externs.h 
+pushd.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h
+pushd.o: $(BASHINCDIR)/maxpath.h $(srcdir)/common.h ./builtext.h
+read.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h
+read.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h
+read.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
+read.o: $(topdir)/subst.h $(topdir)/externs.h $(BASHINCDIR)/maxpath.h
+read.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h
+read.o: $(BASHINCDIR)/shtty.h
+read.o: $(topdir)/arrayfunc.h
+return.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h
+return.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h
+return.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
+return.o: $(topdir)/subst.h $(topdir)/externs.h $(BASHINCDIR)/maxpath.h
+return.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h
+set.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h
+set.o: $(topdir)/general.h $(topdir)/xmalloc.h $(topdir)/subst.h $(topdir)/externs.h
+set.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
+set.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h
+set.o: $(BASHINCDIR)/maxpath.h $(topdir)/error.h
+set.o: $(topdir)/arrayfunc.h
+setattr.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h
+setattr.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h $(BASHINCDIR)/maxpath.h
+setattr.o: $(topdir)/quit.h $(srcdir)/common.h $(srcdir)/bashgetopt.h
+setattr.o: $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h $(topdir)/subst.h
+setattr.o: $(topdir)/externs.h 
+setattr.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h 
+setattr.o: $(topdir)/arrayfunc.h
+shift.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h
+shift.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h
+shift.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
+shift.o: $(topdir)/subst.h $(topdir)/externs.h $(BASHINCDIR)/maxpath.h
+shift.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h
+source.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h
+source.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h $(topdir)/findcmd.h
+source.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
+source.o: $(topdir)/subst.h $(topdir)/externs.h $(BASHINCDIR)/maxpath.h
+source.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h
+source.o: $(srcdir)/bashgetopt.h $(topdir)/flags.h $(topdir)/trap.h
+suspend.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h
+suspend.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h
+suspend.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
+suspend.o: $(topdir)/subst.h $(topdir)/externs.h $(BASHINCDIR)/maxpath.h
+suspend.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h
+test.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h
+test.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h
+test.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
+test.o: $(topdir)/subst.h $(topdir)/externs.h $(BASHINCDIR)/maxpath.h
+test.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h
+test.o: $(topdir)/test.h
+times.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h
+times.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h
+times.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
+times.o: $(topdir)/subst.h $(topdir)/externs.h $(BASHINCDIR)/maxpath.h
+times.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h
+times.o: $(BASHINCDIR)/posixtime.h
+trap.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h
+trap.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h $(topdir)/externs.h
+trap.o: $(topdir)/quit.h $(srcdir)/common.h $(BASHINCDIR)/maxpath.h
+trap.o: $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h $(topdir)/subst.h
+trap.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h
+trap.o: $(topdir)/findcmd.h
+type.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h
+type.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h
+type.o: $(topdir)/quit.h $(srcdir)/common.h $(BASHINCDIR)/maxpath.h
+type.o: $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h $(topdir)/subst.h
+type.o: $(topdir)/externs.h $(topdir)/hashcmd.h
+type.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h 
+ulimit.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h
+ulimit.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h
+ulimit.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
+ulimit.o: $(topdir)/subst.h $(topdir)/externs.h $(BASHINCDIR)/maxpath.h
+ulimit.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h
+umask.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h
+umask.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h
+umask.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
+umask.o: $(topdir)/subst.h $(topdir)/externs.h $(BASHINCDIR)/maxpath.h
+umask.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h
+umask.o: $(BASHINCDIR)/chartypes.h
+wait.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h
+wait.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h
+wait.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
+wait.o: $(topdir)/subst.h $(topdir)/externs.h $(BASHINCDIR)/maxpath.h
+wait.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h
+wait.o: $(BASHINCDIR)/chartypes.h
+shopt.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h
+shopt.o: $(topdir)/error.h $(topdir)/general.h $(topdir)/xmalloc.h 
+shopt.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h
+shopt.o: $(topdir)/subst.h $(topdir)/externs.h $(BASHINCDIR)/maxpath.h
+shopt.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h
+shopt.o: $(srcdir)/common.h $(srcdir)/bashgetopt.h
+
+complete.o: ../config.h 
+complete.o: ${topdir}/shell.h $(topdir)/syntax.h ${topdir}/bashjmp.h ${BASHINCDIR}/posixjmp.h ${topdir}/sig.h  
+complete.o: ${topdir}/unwind_prot.h ${topdir}/variables.h
+complete.o: ${topdir}/bashtypes.h ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h
+complete.o: ${topdir}/builtins.h
+complete.o: ${topdir}/pcomplete.h
+complete.o: ${srcdir}/common.h ${srcdir}/bashgetopt.h
+
+#bind.o: $(RL_LIBSRC)chardefs.h $(RL_LIBSRC)readline.h $(RL_LIBSRC)keymaps.h
+
+# libintl dependencies
+bind.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h
+break.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h
+caller.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h
+cd.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h
+common.c: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h
+complete.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h
+declare.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h
+enable.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h
+evalfile.c: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h
+exec.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h
+exit.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h
+fc.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h
+fg_bg.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h
+getopt.c: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h
+hash.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h
+help.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h
+history.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h
+inlib.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h
+jobs.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h
+kill.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h
+let.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h
+mkbuiltins.c: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h
+printf.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h
+pushd.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h
+read.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h
+return.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h
+set.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h
+setattr.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h
+shift.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h
+shopt.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h
+source.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h
+suspend.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h
+type.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h
+ulimit.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h
+umask.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h
index dcbbec148149c2784de00757372dae7a9683934f..73d395277876e7eed4d37062e82b5f5493930d57 100644 (file)
@@ -101,7 +101,7 @@ command_builtin (list)
        {
          found = describe_command (list->word->word, verbose);
 
-         if (found == 0)
+         if (found == 0 && verbose != CDESC_REUSABLE)
            sh_notfound (list->word->word);
 
          any_found += found;
diff --git a/builtins/command.def~ b/builtins/command.def~
new file mode 100644 (file)
index 0000000..47b871a
--- /dev/null
@@ -0,0 +1,207 @@
+This file is command.def, from which is created command.c.
+It implements the builtin "command" in Bash.
+
+Copyright (C) 1987-2002 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 2, 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, 59 Temple Place, Suite 330, Boston, MA 02111 USA.
+
+$PRODUCES command.c
+
+$BUILTIN command
+$FUNCTION command_builtin
+$SHORT_DOC command [-pVv] command [arg ...]
+Runs COMMAND with ARGS ignoring shell functions.  If you have a shell
+function called `ls', and you wish to call the command `ls', you can
+say "command ls".  If the -p option is given, a default value is used
+for PATH that is guaranteed to find all of the standard utilities.  If
+the -V or -v option is given, a string is printed describing COMMAND.
+The -V option produces a more verbose description.
+$END
+
+#include <config.h>
+
+#if defined (HAVE_UNISTD_H)
+#  ifdef _MINIX
+#    include <sys/types.h>
+#  endif
+#  include <unistd.h>
+#endif
+
+#include "../bashansi.h"
+
+#include "../shell.h"
+#include "../execute_cmd.h"
+#include "../flags.h"
+#include "bashgetopt.h"
+#include "common.h"
+
+#if defined (_CS_PATH) && defined (HAVE_CONFSTR) && !HAVE_DECL_CONFSTR
+extern size_t confstr __P((int, char *, size_t));
+#endif
+
+extern int subshell_environment, posixly_correct;
+
+static void restore_path __P((char *));
+static char *get_standard_path __P((void));
+
+/* Run the commands mentioned in LIST without paying attention to shell
+   functions. */
+int
+command_builtin (list)
+     WORD_LIST *list;
+{
+  int result, verbose, use_standard_path, opt;
+  char *old_path, *standard_path;
+  COMMAND *command;
+
+  verbose = use_standard_path = 0;
+  reset_internal_getopt ();
+  while ((opt = internal_getopt (list, "pvV")) != -1)
+    {
+      switch (opt)
+       {
+       case 'p':
+         use_standard_path = 1;
+         break;
+       case 'V':
+         verbose = CDESC_SHORTDESC;    /* look in common.h for constants */
+         break;
+       case 'v':
+         verbose = CDESC_REUSABLE;     /* ditto */
+         break;
+       default:
+         builtin_usage ();
+         return (EX_USAGE);
+       }
+    }
+  list = loptend;
+
+  if (list == 0)
+    return (EXECUTION_SUCCESS);
+
+  if (verbose)
+    {
+      int found, any_found;
+
+      for (any_found = 0; list; list = list->next)
+       {
+         found = describe_command (list->word->word, verbose);
+
+         if (found == 0 && (posixly_correct == 0 || verbose != CDESC_REUSABLE))
+           sh_notfound (list->word->word);
+
+         any_found += found;
+       }
+      return (any_found ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
+    }
+
+#if defined (RESTRICTED_SHELL)
+  if (use_standard_path && restricted)
+    {
+      sh_restricted ("-p");
+      return (EXECUTION_FAILURE);
+    }
+#endif
+
+  begin_unwind_frame ("command_builtin");
+
+  /* We don't want this to be reparsed (consider command echo 'foo &'), so
+     just make a simple_command structure and call execute_command with it. */
+  if (use_standard_path)
+    {      
+      old_path = get_string_value ("PATH");
+      /* If old_path is NULL, $PATH is unset.  If so, we want to make sure
+        it's unset after this command completes. */
+      if (old_path)
+       old_path = savestring (old_path);
+      add_unwind_protect ((Function *)restore_path, old_path);
+
+      standard_path = get_standard_path ();
+      bind_variable ("PATH", standard_path ? standard_path : "");
+      FREE (standard_path);
+    }
+
+#define COMMAND_BUILTIN_FLAGS (CMD_NO_FUNCTIONS | CMD_INHIBIT_EXPANSION | CMD_COMMAND_BUILTIN)
+
+  command = make_bare_simple_command ();
+  command->value.Simple->words = (WORD_LIST *)copy_word_list (list);
+  command->value.Simple->redirects = (REDIRECT *)NULL;
+  command->flags |= COMMAND_BUILTIN_FLAGS;
+  command->value.Simple->flags |= COMMAND_BUILTIN_FLAGS;
+#if 0
+  /* This breaks for things like ( cd /tmp ; command z ababa ; echo next )
+     or $(command echo a ; command echo b;) or even
+     { command echo a; command echo b; } & */
+  /* If we're in a subshell, see if we can get away without forking
+     again, since we've already forked to run this builtin. */
+  if (subshell_environment)
+    {
+      command->flags |= CMD_NO_FORK;
+      command->value.Simple->flags |= CMD_NO_FORK;
+    }
+#endif
+  add_unwind_protect ((char *)dispose_command, command);
+  result = execute_command (command);
+
+  run_unwind_frame ("command_builtin");
+
+  return (result);
+}
+
+/* Restore the value of the $PATH variable after replacing it when
+   executing `command -p'. */
+static void
+restore_path (var)
+     char *var;
+{
+  if (var)
+    {
+      bind_variable ("PATH", var);
+      free (var);
+    }
+  else
+    unbind_variable ("PATH");
+}
+
+/* Return a value for PATH that is guaranteed to find all of the standard
+   utilities.  This uses Posix.2 configuration variables, if present.  It
+   uses a value defined in config.h as a last resort. */
+static char *
+get_standard_path ()
+{
+#if defined (_CS_PATH) && defined (HAVE_CONFSTR)
+  char *p;
+  size_t len;
+
+  len = (size_t)confstr (_CS_PATH, (char *)NULL, (size_t)0);
+  if (len > 0)
+    {
+      p = (char *)xmalloc (len + 2);
+      *p = '\0';
+      confstr (_CS_PATH, p, len);
+      return (p);
+    }
+  else
+    return (savestring (STANDARD_UTILS_PATH));
+#else /* !_CS_PATH || !HAVE_CONFSTR  */
+#  if defined (CS_PATH)
+  return (savestring (CS_PATH));
+#  else
+  return (savestring (STANDARD_UTILS_PATH));
+#  endif /* !CS_PATH */
+#endif /* !_CS_PATH || !HAVE_CONFSTR */
+}
index 93c7ae3df4f37b6f94dde2c3bab584fdfd7eb41a..a2bf881ee3de2adc12e0e92f928be7df21394e47 100644 (file)
@@ -84,6 +84,7 @@ extern int errno;
 extern int echo_input_at_read;
 extern int current_command_line_count;
 extern int literal_history;
+extern int posixly_correct;
 
 extern int unlink __P((const char *));
 
@@ -155,6 +156,7 @@ static void fc_addhist __P((char *));
 
 /* String to execute on a file that we want to edit. */
 #define FC_EDIT_COMMAND "${FCEDIT:-${EDITOR:-vi}}"
+#define POSIX_FC_EDIT_COMMAND "${FCEDIT:-${EDITOR:-ed}}"
 
 int
 fc_builtin (list)
@@ -166,7 +168,7 @@ fc_builtin (list)
   int histbeg, histend, last_hist, retval, opt;
   FILE *stream;
   REPL *rlist, *rl;
-  char *ename, *command, *newcom;
+  char *ename, *command, *newcom, *fcedit;
   HIST_ENTRY **hlist;
   char *fn;
 
@@ -302,7 +304,7 @@ fc_builtin (list)
       if (listing)
        {
          histend = last_hist;
-         histbeg = histend - 16;
+         histbeg = histend - 16 + 1;   /* +1 because loop below uses >= */
          if (histbeg < 0)
            histbeg = 0;
        }
@@ -347,7 +349,12 @@ fc_builtin (list)
       if (numbering)
        fprintf (stream, "%d", i + history_base);
       if (listing)
-       fprintf (stream, "\t%c", histdata (i) ? '*' : ' ');
+       {
+         if (posixly_correct)
+           fputs ("\t", stream);
+         else
+           fprintf (stream, "\t%c", histdata (i) ? '*' : ' ');
+       }
       fprintf (stream, "%s\n", histline (i));
     }
 
@@ -364,7 +371,8 @@ fc_builtin (list)
     }
   else
     {
-      command = (char *)xmalloc (3 + strlen (FC_EDIT_COMMAND) + strlen (fn));
+      fcedit = posixly_correct ? POSIX_FC_EDIT_COMMAND : FC_EDIT_COMMAND;
+      command = (char *)xmalloc (3 + strlen (fcedit) + strlen (fn));
       sprintf (command, "%s %s", FC_EDIT_COMMAND, fn);
     }
   retval = parse_and_execute (command, "fc", SEVAL_NOHIST);
diff --git a/builtins/fc.def~ b/builtins/fc.def~
new file mode 100644 (file)
index 0000000..93c7ae3
--- /dev/null
@@ -0,0 +1,633 @@
+This file is fc.def, from which is created fc.c.
+It implements the builtin "fc" in Bash.
+
+Copyright (C) 1987-2003 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 2, 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, 59 Temple Place, Suite 330, Boston, MA 02111 USA.
+
+$PRODUCES fc.c
+
+$BUILTIN fc
+$FUNCTION fc_builtin
+$DEPENDS_ON HISTORY
+$SHORT_DOC fc [-e ename] [-nlr] [first] [last] or fc -s [pat=rep] [cmd]
+fc is used to list or edit and re-execute commands from the history list.
+FIRST and LAST can be numbers specifying the range, or FIRST can be a
+string, which means the most recent command beginning with that
+string.
+
+   -e ENAME selects which editor to use.  Default is FCEDIT, then EDITOR,
+      then vi.
+
+   -l means list lines instead of editing.
+   -n means no line numbers listed.
+   -r means reverse the order of the lines (making it newest listed first).
+
+With the `fc -s [pat=rep ...] [command]' format, the command is
+re-executed after the substitution OLD=NEW is performed.
+
+A useful alias to use with this is r='fc -s', so that typing `r cc'
+runs the last command beginning with `cc' and typing `r' re-executes
+the last command.
+$END
+
+#include <config.h>
+
+#if defined (HISTORY)
+#ifndef _MINIX
+#  include <sys/param.h>
+#endif
+#include "../bashtypes.h"
+#include "posixstat.h"
+#if ! defined(_MINIX) && defined (HAVE_SYS_FILE_H)
+#  include <sys/file.h>
+#endif
+
+#if defined (HAVE_UNISTD_H)
+#  include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include <chartypes.h>
+
+#include "../bashansi.h"
+#include "../bashintl.h"
+#include <errno.h>
+
+#include "../shell.h"
+#include "../builtins.h"
+#include "../flags.h"
+#include "../bashhist.h"
+#include "maxpath.h"
+#include <readline/history.h>
+#include "bashgetopt.h"
+#include "common.h"
+
+#if !defined (errno)
+extern int errno;
+#endif /* !errno */
+
+extern int echo_input_at_read;
+extern int current_command_line_count;
+extern int literal_history;
+
+extern int unlink __P((const char *));
+
+extern FILE *sh_mktmpfp __P((char *, int, char **));
+
+/* **************************************************************** */
+/*                                                                 */
+/*     The K*rn shell style fc command (Fix Command)               */
+/*                                                                 */
+/* **************************************************************** */
+
+/* fc builtin command (fix command) for Bash for those who
+   like K*rn-style history better than csh-style.
+
+     fc [-e ename] [-nlr] [first] [last]
+
+   FIRST and LAST can be numbers specifying the range, or FIRST can be
+   a string, which means the most recent command beginning with that
+   string.
+
+   -e ENAME selects which editor to use.  Default is FCEDIT, then EDITOR,
+      then the editor which corresponds to the current readline editing
+      mode, then vi.
+
+   -l means list lines instead of editing.
+   -n means no line numbers listed.
+   -r means reverse the order of the lines (making it newest listed first).
+
+     fc -e - [pat=rep ...] [command]
+     fc -s [pat=rep ...] [command]
+
+   Equivalent to !command:sg/pat/rep execpt there can be multiple PAT=REP's.
+*/
+
+/* Data structure describing a list of global replacements to perform. */
+typedef struct repl {
+  struct repl *next;
+  char *pat;
+  char *rep;
+} REPL;
+
+/* Accessors for HIST_ENTRY lists that are called HLIST. */
+#define histline(i) (hlist[(i)]->line)
+#define histdata(i) (hlist[(i)]->data)
+
+#define FREE_RLIST() \
+       do { \
+               for (rl = rlist; rl; ) { \
+                       REPL *r;        \
+                       r = rl->next; \
+                       if (rl->pat) \
+                               free (rl->pat); \
+                       if (rl->rep) \
+                               free (rl->rep); \
+                       free (rl); \
+                       rl = r; \
+               } \
+       } while (0)
+
+static char *fc_dosubs __P((char *, REPL *));
+static char *fc_gethist __P((char *, HIST_ENTRY **));
+static int fc_gethnum __P((char *, HIST_ENTRY **));
+static int fc_number __P((WORD_LIST *));
+static void fc_replhist __P((char *));
+#ifdef INCLUDE_UNUSED
+static char *fc_readline __P((FILE *));
+static void fc_addhist __P((char *));
+#endif
+
+/* String to execute on a file that we want to edit. */
+#define FC_EDIT_COMMAND "${FCEDIT:-${EDITOR:-vi}}"
+
+int
+fc_builtin (list)
+     WORD_LIST *list;
+{
+  register int i;
+  register char *sep;
+  int numbering, reverse, listing, execute;
+  int histbeg, histend, last_hist, retval, opt;
+  FILE *stream;
+  REPL *rlist, *rl;
+  char *ename, *command, *newcom;
+  HIST_ENTRY **hlist;
+  char *fn;
+
+  numbering = 1;
+  reverse = listing = execute = 0;
+  ename = (char *)NULL;
+
+  /* Parse out the options and set which of the two forms we're in. */
+  reset_internal_getopt ();
+  lcurrent = list;             /* XXX */
+  while (fc_number (loptend = lcurrent) == 0 &&
+        (opt = internal_getopt (list, ":e:lnrs")) != -1)
+    {
+      switch (opt)
+       {
+       case 'n':
+         numbering = 0;
+         break;
+
+       case 'l':
+         listing = 1;
+         break;
+
+       case 'r':
+         reverse = 1;
+         break;
+
+       case 's':
+         execute = 1;
+         break;
+
+       case 'e':
+         ename = list_optarg;
+         break;
+
+       default:
+         builtin_usage ();
+         return (EX_USAGE);
+       }
+    }
+
+  list = loptend;
+
+  if (ename && (*ename == '-') && (ename[1] == '\0'))
+    execute = 1;
+
+  /* The "execute" form of the command (re-run, with possible string
+     substitutions). */
+  if (execute)
+    {
+      rlist = (REPL *)NULL;
+      while (list && ((sep = (char *)strchr (list->word->word, '=')) != NULL))
+       {
+         *sep++ = '\0';
+         rl = (REPL *)xmalloc (sizeof (REPL));
+         rl->next = (REPL *)NULL;
+         rl->pat = savestring (list->word->word);
+         rl->rep = savestring (sep);
+
+         if (rlist == NULL)
+           rlist = rl;
+         else
+           {
+             rl->next = rlist;
+             rlist = rl;
+           }
+         list = list->next;
+       }
+
+      /* If we have a list of substitutions to do, then reverse it
+        to get the replacements in the proper order. */
+
+      rlist = REVERSE_LIST (rlist, REPL *);
+
+      hlist = history_list ();
+
+      /* If we still have something in list, it is a command spec.
+        Otherwise, we use the most recent command in time. */
+      command = fc_gethist (list ? list->word->word : (char *)NULL, hlist);
+
+      if (command == NULL)
+       {
+         builtin_error (_("no command found"));
+         if (rlist)
+           FREE_RLIST ();
+
+         return (EXECUTION_FAILURE);
+       }
+
+      if (rlist)
+       {
+         newcom = fc_dosubs (command, rlist);
+         free (command);
+         FREE_RLIST ();
+         command = newcom;
+       }
+
+      fprintf (stderr, "%s\n", command);
+      fc_replhist (command);   /* replace `fc -s' with command */
+      return (parse_and_execute (command, "fc", SEVAL_NOHIST));
+    }
+
+  /* This is the second form of the command (the list-or-edit-and-rerun
+     form). */
+  hlist = history_list ();
+  if (hlist == 0)
+    return (EXECUTION_SUCCESS);
+  for (i = 0; hlist[i]; i++);
+
+  /* With the Bash implementation of history, the current command line
+     ("fc blah..." and so on) is already part of the history list by
+     the time we get to this point.  This just skips over that command
+     and makes the last command that this deals with be the last command
+     the user entered before the fc.  We need to check whether the
+     line was actually added (HISTIGNORE may have caused it to not be),
+     so we check hist_last_line_added. */
+
+  last_hist = i - 1 - hist_last_line_added;
+
+  if (list)
+    {
+      histbeg = fc_gethnum (list->word->word, hlist);
+      list = list->next;
+
+      if (list)
+       histend = fc_gethnum (list->word->word, hlist);
+      else
+       histend = listing ? last_hist : histbeg;
+    }
+  else
+    {
+      /* The default for listing is the last 16 history items. */
+      if (listing)
+       {
+         histend = last_hist;
+         histbeg = histend - 16;
+         if (histbeg < 0)
+           histbeg = 0;
+       }
+      else
+       /* For editing, it is the last history command. */
+       histbeg = histend = last_hist;
+    }
+
+  /* We print error messages for line specifications out of range. */
+  if ((histbeg < 0) || (histend < 0))
+    {
+      sh_erange ((char *)NULL, _("history specification"));
+      return (EXECUTION_FAILURE);
+    }
+
+  if (histend < histbeg)
+    {
+      i = histend;
+      histend = histbeg;
+      histbeg = i;
+
+      reverse = 1;
+    }
+
+  if (listing)
+    stream = stdout;
+  else
+    {
+      numbering = 0;
+      stream = sh_mktmpfp ("bash-fc", MT_USERANDOM|MT_USETMPDIR, &fn);
+      if (stream == 0)
+       {
+         builtin_error (_("%s: cannot open temp file: %s"), fn ? fn : "", strerror (errno));
+         FREE (fn);
+         return (EXECUTION_FAILURE);
+       }
+    }
+
+  for (i = reverse ? histend : histbeg; reverse ? i >= histbeg : i <= histend; reverse ? i-- : i++)
+    {
+      QUIT;
+      if (numbering)
+       fprintf (stream, "%d", i + history_base);
+      if (listing)
+       fprintf (stream, "\t%c", histdata (i) ? '*' : ' ');
+      fprintf (stream, "%s\n", histline (i));
+    }
+
+  if (listing)
+    return (EXECUTION_SUCCESS);
+
+  fclose (stream);
+
+  /* Now edit the file of commands. */
+  if (ename)
+    {
+      command = (char *)xmalloc (strlen (ename) + strlen (fn) + 2);
+      sprintf (command, "%s %s", ename, fn);
+    }
+  else
+    {
+      command = (char *)xmalloc (3 + strlen (FC_EDIT_COMMAND) + strlen (fn));
+      sprintf (command, "%s %s", FC_EDIT_COMMAND, fn);
+    }
+  retval = parse_and_execute (command, "fc", SEVAL_NOHIST);
+  if (retval != EXECUTION_SUCCESS)
+    {
+      unlink (fn);
+      free (fn);
+      return (EXECUTION_FAILURE);
+    }
+
+  /* Make sure parse_and_execute doesn't turn this off, even though a
+     call to parse_and_execute farther up the function call stack (e.g.,
+     if this is called by vi_edit_and_execute_command) may have already
+     called bash_history_disable. */
+  remember_on_history = 1;
+
+  /* Turn on the `v' flag while fc_execute_file runs so the commands
+     will be echoed as they are read by the parser. */
+  begin_unwind_frame ("fc builtin");
+  add_unwind_protect ((Function *)xfree, fn);
+  add_unwind_protect (unlink, fn);
+  unwind_protect_int (echo_input_at_read);
+  echo_input_at_read = 1;
+    
+  retval = fc_execute_file (fn);
+
+  run_unwind_frame ("fc builtin");
+
+  return (retval);
+}
+
+/* Return 1 if LIST->word->word is a legal number for fc's use. */
+static int
+fc_number (list)
+     WORD_LIST *list;
+{
+  char *s;
+
+  if (list == 0)
+    return 0;
+  s = list->word->word;
+  if (*s == '-')
+    s++;
+  return (legal_number (s, (intmax_t *)NULL));
+}
+
+/* Return an absolute index into HLIST which corresponds to COMMAND.  If
+   COMMAND is a number, then it was specified in relative terms.  If it
+   is a string, then it is the start of a command line present in HLIST. */
+static int
+fc_gethnum (command, hlist)
+     char *command;
+     HIST_ENTRY **hlist;
+{
+  int sign = 1, n, clen;
+  register int i, j;
+  register char *s;
+
+  /* Count history elements. */
+  for (i = 0; hlist[i]; i++);
+
+  /* With the Bash implementation of history, the current command line
+     ("fc blah..." and so on) is already part of the history list by
+     the time we get to this point.  This just skips over that command
+     and makes the last command that this deals with be the last command
+     the user entered before the fc.  We need to check whether the
+     line was actually added (HISTIGNORE may have caused it to not be),
+     so we check hist_last_line_added. */
+  i -= 1 + hist_last_line_added;
+
+  /* No specification defaults to most recent command. */
+  if (command == NULL)
+    return (i);
+
+  /* Otherwise, there is a specification.  It can be a number relative to
+     the current position, or an absolute history number. */
+  s = command;
+
+  /* Handle possible leading minus sign. */
+  if (s && (*s == '-'))
+    {
+      sign = -1;
+      s++;
+    }
+
+  if (s && DIGIT(*s))
+    {
+      n = atoi (s);
+      n *= sign;
+
+      /* If the value is negative or zero, then it is an offset from
+        the current history item. */
+      if (n < 0)
+       {
+         n += i + 1;
+         return (n < 0 ? 0 : n);
+       }
+      else if (n == 0)
+       return (i);
+      else
+       {
+         n -= history_base;
+         return (i < n ? i : n);
+       }
+    }
+
+  clen = strlen (command);
+  for (j = i; j >= 0; j--)
+    {
+      if (STREQN (command, histline (j), clen))
+       return (j);
+    }
+  return (-1);
+}
+
+/* Locate the most recent history line which begins with
+   COMMAND in HLIST, and return a malloc()'ed copy of it. */
+static char *
+fc_gethist (command, hlist)
+     char *command;
+     HIST_ENTRY **hlist;
+{
+  int i;
+
+  if (!hlist)
+    return ((char *)NULL);
+
+  i = fc_gethnum (command, hlist);
+
+  if (i >= 0)
+    return (savestring (histline (i)));
+  else
+    return ((char *)NULL);
+}
+
+#ifdef INCLUDE_UNUSED
+/* Read the edited history lines from STREAM and return them
+   one at a time.  This can read unlimited length lines.  The
+   caller should free the storage. */
+static char *
+fc_readline (stream)
+     FILE *stream;
+{
+  register int c;
+  int line_len = 0, lindex = 0;
+  char *line = (char *)NULL;
+
+  while ((c = getc (stream)) != EOF)
+    {
+      if ((lindex + 2) >= line_len)
+       line = (char *)xrealloc (line, (line_len += 128));
+
+      if (c == '\n')
+       {
+         line[lindex++] = '\n';
+         line[lindex++] = '\0';
+         return (line);
+       }
+      else
+       line[lindex++] = c;
+    }
+
+  if (!lindex)
+    {
+      if (line)
+       free (line);
+
+      return ((char *)NULL);
+    }
+
+  if (lindex + 2 >= line_len)
+    line = (char *)xrealloc (line, lindex + 3);
+
+  line[lindex++] = '\n';           /* Finish with newline if none in file */
+  line[lindex++] = '\0';
+  return (line);
+}
+#endif
+
+/* Perform the SUBS on COMMAND.
+   SUBS is a list of substitutions, and COMMAND is a simple string.
+   Return a pointer to a malloc'ed string which contains the substituted
+   command. */
+static char *
+fc_dosubs (command, subs)
+     char *command;
+     REPL *subs;
+{
+  register char *new, *t;
+  register REPL *r;
+
+  for (new = savestring (command), r = subs; r; r = r->next)
+    {
+      t = strsub (new, r->pat, r->rep, 1);
+      free (new);
+      new = t;
+    }
+  return (new);
+}
+
+/* Use `command' to replace the last entry in the history list, which,
+   by this time, is `fc blah...'.  The intent is that the new command
+   become the history entry, and that `fc' should never appear in the
+   history list.  This way you can do `r' to your heart's content. */
+static void
+fc_replhist (command)
+     char *command;
+{
+  register int i;
+  HIST_ENTRY **hlist, *histent, *discard;
+  int n;
+
+  if (command == 0 || *command == '\0')
+    return;
+
+  hlist = history_list ();
+
+  if (hlist == NULL)
+    return;
+
+  for (i = 0; hlist[i]; i++);
+  i--;
+
+  /* History_get () takes a parameter that should be
+     offset by history_base. */
+
+  histent = history_get (history_base + i);    /* Don't free this */
+  if (histent == NULL)
+    return;
+
+  n = strlen (command);
+
+  if (command[n - 1] == '\n')
+    command[n - 1] = '\0';
+
+  if (command && *command)
+    {
+      discard = remove_history (i);
+      if (discard)
+       {
+         FREE (discard->line);
+         free ((char *) discard);
+       }
+      maybe_add_history (command);     /* Obeys HISTCONTROL setting. */
+    }
+}
+
+#ifdef INCLUDE_UNUSED
+/* Add LINE to the history, after removing a single trailing newline. */
+static void
+fc_addhist (line)
+     char *line;
+{
+  register int n;
+
+  n = strlen (line);
+
+  if (line[n - 1] == '\n')
+    line[n - 1] = '\0';
+
+  if (line && *line)
+    maybe_add_history (line);
+}
+#endif
+
+#endif /* HISTORY */
index d311ac90bf11f9fe2304dcf026683a8365ad2094..9425c03704bb45ce93b8cf57b19b5f59b3f67280 100644 (file)
@@ -125,7 +125,7 @@ hash_builtin (list)
   if (list == 0 && expunge_hash_table == 0)
     {
       if (print_hashed_commands (list_portably) == 0)
-       printf (_("%s: hash table empty\n"), this_command_name);
+       fprintf (stderr, _("%s: hash table empty\n"), this_command_name);
 
       return (EXECUTION_SUCCESS);
     }
diff --git a/builtins/hash.def~ b/builtins/hash.def~
new file mode 100644 (file)
index 0000000..d311ac9
--- /dev/null
@@ -0,0 +1,268 @@
+This file is hash.def, from which is created hash.c.
+It implements the builtin "hash" in Bash.
+
+Copyright (C) 1987-2003 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 2, 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, 59 Temple Place, Suite 330, Boston, MA 02111 USA.
+
+$PRODUCES hash.c
+
+$BUILTIN hash
+$FUNCTION hash_builtin
+$SHORT_DOC hash [-lr] [-p pathname] [-dt] [name ...]
+For each NAME, the full pathname of the command is determined and
+remembered.  If the -p option is supplied, PATHNAME is used as the
+full pathname of NAME, and no path search is performed.  The -r
+option causes the shell to forget all remembered locations.  The -d
+option causes the shell to forget the remembered location of each NAME.
+If the -t option is supplied the full pathname to which each NAME
+corresponds is printed.  If multiple NAME arguments are supplied with
+-t, the NAME is printed before the hashed full pathname.  The -l option
+causes output to be displayed in a format that may be reused as input.
+If no arguments are given, information about remembered commands is displayed.
+$END
+
+#include <config.h>
+
+#include <stdio.h>
+
+#include "../bashtypes.h"
+
+#if defined (HAVE_UNISTD_H)
+#  include <unistd.h>
+#endif
+
+#include <errno.h>
+
+#include "../bashansi.h"
+#include "../bashintl.h"
+
+#include "../shell.h"
+#include "../builtins.h"
+#include "../flags.h"
+#include "../findcmd.h"
+#include "../hashcmd.h"
+#include "common.h"
+#include "bashgetopt.h"
+
+extern int dot_found_in_search;
+extern char *this_command_name;
+
+static int add_hashed_command __P((char *, int));
+static int print_hash_info __P((BUCKET_CONTENTS *));
+static int print_portable_hash_info __P((BUCKET_CONTENTS *));
+static int print_hashed_commands __P((int));
+static int list_hashed_filename_targets __P((WORD_LIST *, int));
+
+/* Print statistics on the current state of hashed commands.  If LIST is
+   not empty, then rehash (or hash in the first place) the specified
+   commands. */
+int
+hash_builtin (list)
+     WORD_LIST *list;
+{
+  int expunge_hash_table, list_targets, list_portably, delete, opt;
+  char *w, *pathname;
+
+  if (hashing_enabled == 0)
+    {
+      builtin_error (_("hashing disabled"));
+      return (EXECUTION_FAILURE);
+    }
+
+  expunge_hash_table = list_targets = list_portably = delete = 0;
+  pathname = (char *)NULL;
+  reset_internal_getopt ();
+  while ((opt = internal_getopt (list, "dlp:rt")) != -1)
+    {
+      switch (opt)
+       {
+       case 'd':
+         delete = 1;
+         break;
+       case 'l':
+         list_portably = 1;
+         break;
+       case 'p':
+         pathname = list_optarg;
+         break;
+       case 'r':
+         expunge_hash_table = 1;
+         break;
+       case 't':
+         list_targets = 1;
+         break;
+       default:
+         builtin_usage ();
+         return (EX_USAGE);
+       }
+    }
+  list = loptend;
+
+  /* hash -t requires at least one argument. */
+  if (list == 0 && list_targets)
+    {
+      sh_needarg ("-t");
+      return (EXECUTION_FAILURE);
+    }
+
+  /* We want hash -r to be silent, but hash -- to print hashing info, so
+     we test expunge_hash_table. */
+  if (list == 0 && expunge_hash_table == 0)
+    {
+      if (print_hashed_commands (list_portably) == 0)
+       printf (_("%s: hash table empty\n"), this_command_name);
+
+      return (EXECUTION_SUCCESS);
+    }
+
+  if (expunge_hash_table)
+    phash_flush ();
+
+  /* If someone runs `hash -r -t xyz' he will be disappointed. */
+  if (list_targets)
+    return (list_hashed_filename_targets (list, list_portably));
+      
+#if defined (RESTRICTED_SHELL)
+  if (restricted && pathname && strchr (pathname, '/'))
+    {
+      sh_restricted (pathname);
+      return (EXECUTION_FAILURE);
+    }
+#endif
+
+  for (opt = EXECUTION_SUCCESS; list; list = list->next)
+    {
+      /* Add, remove or rehash the specified commands. */
+      w = list->word->word;
+      if (pathname)
+       {
+         if (is_directory (pathname))
+           {
+#ifdef EISDIR
+             builtin_error ("%s: %s", pathname, strerror (EISDIR));
+#else
+             builtin_error ("%s: is a directory", pathname);
+#endif
+             opt = EXECUTION_FAILURE;
+           }
+         else
+           phash_insert (w, pathname, 0, 0);
+       }
+      else if (absolute_program (w))
+       continue;
+      else if (delete && phash_remove (w))
+       {
+         sh_notfound (w);
+         opt = EXECUTION_FAILURE;
+       }
+      else if (add_hashed_command (w, 0))
+       opt = EXECUTION_FAILURE;
+    }
+
+  fflush (stdout);
+  return (opt);
+}
+
+static int
+add_hashed_command (w, quiet)
+     char *w;
+     int quiet;
+{
+  int rv;
+  char *full_path;
+
+  rv = 0;
+  if (find_function (w) == 0 && find_shell_builtin (w) == 0)
+    {
+      full_path = find_user_command (w);
+      if (full_path && executable_file (full_path))
+       phash_insert (w, full_path, dot_found_in_search, 0);
+      else
+       {
+         if (quiet == 0)
+           sh_notfound (w);
+         rv++;
+       }
+      FREE (full_path);
+    }
+  return (rv);
+}
+
+/* Print information about current hashed info. */
+static int
+print_hash_info (item)
+     BUCKET_CONTENTS *item;
+{
+  printf ("%4d\t%s\n", item->times_found, pathdata(item)->path);
+  return 0;
+}
+
+static int
+print_portable_hash_info (item)
+     BUCKET_CONTENTS *item;
+{
+  printf ("builtin hash -p %s %s\n", pathdata(item)->path, item->key);
+  return 0;
+}
+
+static int
+print_hashed_commands (fmt)
+     int fmt;
+{
+  if (hashed_filenames == 0 || HASH_ENTRIES (hashed_filenames) == 0)
+    return (0);
+
+  if (fmt == 0)
+    printf ("hits\tcommand\n");
+  hash_walk (hashed_filenames, fmt ? print_portable_hash_info : print_hash_info);
+  return (1);
+}
+
+static int
+list_hashed_filename_targets (list, fmt)
+     WORD_LIST *list;
+     int fmt;
+{
+  int all_found, multiple;
+  char *target;
+  WORD_LIST *l;
+
+  all_found = 1;
+  multiple = list->next != 0;
+
+  for (l = list; l; l = l->next)
+    {
+      target = phash_search (l->word->word);
+      if (target == 0)
+       {
+         all_found = 0;
+         sh_notfound (l->word->word);
+         continue;
+       }
+      if (fmt)
+       printf ("builtin hash -p %s %s\n", target, l->word->word);
+      else
+       {
+         if (multiple)
+           printf ("%s\t", l->word->word);
+         printf ("%s\n", target);
+       }
+    }
+
+  return (all_found ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
+}
index 50a942f877063867b67fd513d287c6050cab2533..47846bb31d7b713f2fa98ef01042b23b2cd3e4d4 100644 (file)
@@ -109,11 +109,10 @@ $END
 
 $BUILTIN %
 $DOCNAME fg_percent
-$SHORT_DOC %[DIGITS | WORD] [&]
+$SHORT_DOC %JOBSPEC [&]
 This is similar to the `fg' command.  Resume a stopped or background
-job.  If you specifiy DIGITS, then that job is used.  If you specify
-WORD, then the job whose name begins with WORD is used.  Following the
-job specification with a `&' places the job in the background.
+job specified by %JOBSPEC.  Following the job specification with a `&'
+places the job in the background.
 $END
 
 $BUILTIN (( ... ))
diff --git a/builtins/reserved.def~ b/builtins/reserved.def~
new file mode 100644 (file)
index 0000000..50a942f
--- /dev/null
@@ -0,0 +1,201 @@
+This file is reserved.def, in which the shell reserved words are defined.
+It has no direct C file production, but defines builtins for the Bash
+builtin help command.
+
+Copyright (C) 1987-2002 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 2, 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, 59 Temple Place, Suite 330, Boston, MA 02111 USA.
+
+$BUILTIN for
+$SHORT_DOC for NAME [in WORDS ... ;] do COMMANDS; done
+The `for' loop executes a sequence of commands for each member in a
+list of items.  If `in WORDS ...;' is not present, then `in "$@"' is
+assumed.  For each element in WORDS, NAME is set to that element, and
+the COMMANDS are executed.
+$END
+
+$BUILTIN for ((
+$DOCNAME arith_for
+$SHORT_DOC for (( exp1; exp2; exp3 )); do COMMANDS; done
+Equivalent to
+       (( EXP1 ))
+       while (( EXP2 )); do
+               COMMANDS
+               (( EXP3 ))
+       done
+EXP1, EXP2, and EXP3 are arithmetic expressions.  If any expression is
+omitted, it behaves as if it evaluates to 1.
+$END
+
+$BUILTIN select
+$SHORT_DOC select NAME [in WORDS ... ;] do COMMANDS; done
+The WORDS are expanded, generating a list of words.  The
+set of expanded words is printed on the standard error, each
+preceded by a number.  If `in WORDS' is not present, `in "$@"'
+is assumed.  The PS3 prompt is then displayed and a line read
+from the standard input.  If the line consists of the number
+corresponding to one of the displayed words, then NAME is set
+to that word.  If the line is empty, WORDS and the prompt are
+redisplayed.  If EOF is read, the command completes.  Any other
+value read causes NAME to be set to null.  The line read is saved
+in the variable REPLY.  COMMANDS are executed after each selection
+until a break command is executed.
+$END
+
+$BUILTIN time
+$SHORT_DOC time [-p] PIPELINE
+Execute PIPELINE and print a summary of the real time, user CPU time,
+and system CPU time spent executing PIPELINE when it terminates.
+The return status is the return status of PIPELINE.  The `-p' option
+prints the timing summary in a slightly different format.  This uses
+the value of the TIMEFORMAT variable as the output format.
+$END
+
+$BUILTIN case
+$SHORT_DOC case WORD in [PATTERN [| PATTERN]...) COMMANDS ;;]... esac
+Selectively execute COMMANDS based upon WORD matching PATTERN.  The
+`|' is used to separate multiple patterns.
+$END
+
+$BUILTIN if
+$SHORT_DOC if COMMANDS; then COMMANDS; [ elif COMMANDS; then COMMANDS; ]... [ else COMMANDS; ] fi
+The if COMMANDS are executed.  If the exit status is zero, then the then
+COMMANDS are executed.  Otherwise, each of the elif COMMANDS are executed
+in turn, and if the exit status is zero, the corresponding then COMMANDS
+are executed and the if command completes.  Otherwise, the else COMMANDS
+are executed, if present.  The exit status is the exit status of the last
+command executed, or zero if no condition tested true.
+$END
+
+$BUILTIN while
+$SHORT_DOC while COMMANDS; do COMMANDS; done
+Expand and execute COMMANDS as long as the final command in the
+`while' COMMANDS has an exit status of zero.
+$END
+
+$BUILTIN until
+$SHORT_DOC until COMMANDS; do COMMANDS; done
+Expand and execute COMMANDS as long as the final command in the
+`until' COMMANDS has an exit status which is not zero.
+$END
+
+$BUILTIN function
+$SHORT_DOC function NAME { COMMANDS ; } or NAME () { COMMANDS ; }
+Create a simple command invoked by NAME which runs COMMANDS.
+Arguments on the command line along with NAME are passed to the
+function as $0 .. $n.
+$END
+
+$BUILTIN { ... }
+$DOCNAME grouping_braces
+$SHORT_DOC { COMMANDS ; }
+Run a set of commands in a group.  This is one way to redirect an
+entire set of commands.
+$END
+
+$BUILTIN %
+$DOCNAME fg_percent
+$SHORT_DOC %[DIGITS | WORD] [&]
+This is similar to the `fg' command.  Resume a stopped or background
+job.  If you specifiy DIGITS, then that job is used.  If you specify
+WORD, then the job whose name begins with WORD is used.  Following the
+job specification with a `&' places the job in the background.
+$END
+
+$BUILTIN (( ... ))
+$DOCNAME arith
+$SHORT_DOC (( expression ))
+The EXPRESSION is evaluated according to the rules for arithmetic
+evaluation.  Equivalent to "let EXPRESSION".
+$END
+
+$BUILTIN [[ ... ]]
+$DOCNAME conditional
+$SHORT_DOC [[ expression ]]
+Returns a status of 0 or 1 depending on the evaluation of the conditional
+expression EXPRESSION.  Expressions are composed of the same primaries used
+by the `test' builtin, and may be combined using the following operators
+
+       ( EXPRESSION )  Returns the value of EXPRESSION
+       ! EXPRESSION    True if EXPRESSION is false; else false
+       EXPR1 && EXPR2  True if both EXPR1 and EXPR2 are true; else false
+       EXPR1 || EXPR2  True if either EXPR1 or EXPR2 is true; else false
+
+When the `==' and `!=' operators are used, the string to the right of the
+operator is used as a pattern and pattern matching is performed.  The
+&& and || operators do not evaluate EXPR2 if EXPR1 is sufficient to
+determine the expression's value.
+$END
+
+$BUILTIN variables
+$DOCNAME variable_help
+$SHORT_DOC variables - Some variable names and meanings
+BASH_VERSION    Version information for this Bash.
+CDPATH          A colon-separated list of directories to search
+               for directries given as arguments to `cd'.
+GLOBIGNORE     A colon-separated list of patterns describing filenames to
+               be ignored by pathname expansion.
+#if defined (HISTORY)
+HISTFILE        The name of the file where your command history is stored.
+HISTFILESIZE    The maximum number of lines this file can contain.
+HISTSIZE        The maximum number of history lines that a running
+               shell can access.
+#endif /* HISTORY */
+HOME            The complete pathname to your login directory.
+HOSTNAME       The name of the current host.
+HOSTTYPE        The type of CPU this version of Bash is running under.
+IGNOREEOF       Controls the action of the shell on receipt of an EOF
+               character as the sole input.  If set, then the value
+               of it is the number of EOF characters that can be seen
+               in a row on an empty line before the shell will exit
+               (default 10).  When unset, EOF signifies the end of input.
+MACHTYPE       A string describing the current system Bash is running on.
+MAILCHECK      How often, in seconds, Bash checks for new mail.
+MAILPATH       A colon-separated list of filenames which Bash checks
+               for new mail.
+OSTYPE         The version of Unix this version of Bash is running on.
+PATH            A colon-separated list of directories to search when
+               looking for commands.
+PROMPT_COMMAND  A command to be executed before the printing of each
+               primary prompt.
+PS1             The primary prompt string.
+PS2             The secondary prompt string.
+PWD            The full pathname of the current directory.
+SHELLOPTS      A colon-separated list of enabled shell options.
+TERM            The name of the current terminal type.
+TIMEFORMAT     The output format for timing statistics displayed by the
+               `time' reserved word.
+auto_resume     Non-null means a command word appearing on a line by
+               itself is first looked for in the list of currently
+               stopped jobs.  If found there, that job is foregrounded.
+               A value of `exact' means that the command word must
+               exactly match a command in the list of stopped jobs.  A
+               value of `substring' means that the command word must
+               match a substring of the job.  Any other value means that
+               the command must be a prefix of a stopped job.
+#if defined (HISTORY)
+#  if defined (BANG_HISTORY)
+histchars       Characters controlling history expansion and quick
+               substitution.  The first character is the history
+               substitution character, usually `!'.  The second is
+               the `quick substitution' character, usually `^'.  The
+               third is the `history comment' character, usually `#'.
+#  endif /* BANG_HISTORY */
+HISTIGNORE     A colon-separated list of patterns used to decide which
+               commands should be saved on the history list.
+#endif /* HISTORY */
+$END
index 9dd746fbaf81e8d0069100517ec59244bc7ad12a..1531d208df13f9861c1d55230d33dae7720bdf9d 100644 (file)
@@ -87,7 +87,7 @@ int
 trap_builtin (list)
      WORD_LIST *list;
 {
-  int list_signal_names, display, result, opt;
+  int list_signal_names, display, result, opt, first_signal;
 
   list_signal_names = display = 0;
   result = EXECUTION_SUCCESS;
@@ -118,14 +118,19 @@ trap_builtin (list)
   else
     {
       char *first_arg;
-      int operation, sig;
+      int operation, sig, first_signal;
 
       operation = SET;
       first_arg = list->word->word;
+      first_signal = first_arg && *first_arg && all_digits (first_arg) && signal_object_p (first_arg, opt);
+
+      /* Backwards compatibility */
+      if (first_signal)
+       operation = REVERT;
       /* When in posix mode, the historical behavior of looking for a
         missing first argument is disabled.  To revert to the original
         signal handling disposition, use `-' as the first argument. */
-      if (posixly_correct == 0 && first_arg && *first_arg &&
+      else if (posixly_correct == 0 && first_arg && *first_arg &&
                (*first_arg != '-' || first_arg[1]) &&
                signal_object_p (first_arg, opt) && list->next == 0)
        operation = REVERT;
diff --git a/builtins/trap.def~ b/builtins/trap.def~
new file mode 100644 (file)
index 0000000..c43bf7a
--- /dev/null
@@ -0,0 +1,263 @@
+This file is trap.def, from which is created trap.c.
+It implements the builtin "trap" in Bash.
+
+Copyright (C) 1987-2004 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 2, 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, 59 Temple Place, Suite 330, Boston, MA 02111 USA.
+
+$PRODUCES trap.c
+
+$BUILTIN trap
+$FUNCTION trap_builtin
+$SHORT_DOC trap [-lp] [[arg] signal_spec ...]
+The command ARG is to be read and executed when the shell receives
+signal(s) SIGNAL_SPEC.  If ARG is absent (and a single SIGNAL_SPEC
+is supplied) or `-', each specified signal is reset to its original
+value.  If ARG is the null string each SIGNAL_SPEC is ignored by the
+shell and by the commands it invokes.  If a SIGNAL_SPEC is EXIT (0)
+the command ARG is executed on exit from the shell.  If a SIGNAL_SPEC
+is DEBUG, ARG is executed after every simple command.  If the`-p' option
+is supplied then the trap commands associated with each SIGNAL_SPEC are
+displayed.  If no arguments are supplied or if only `-p' is given, trap
+prints the list of commands associated with each signal.  Each SIGNAL_SPEC
+is either a signal name in <signal.h> or a signal number.  Signal names
+are case insensitive and the SIG prefix is optional.  `trap -l' prints
+a list of signal names and their corresponding numbers.  Note that a
+signal can be sent to the shell with "kill -signal $$".
+$END
+
+#include <config.h>
+
+#if defined (HAVE_UNISTD_H)
+#  ifdef _MINIX
+#    include <sys/types.h>
+#  endif
+#  include <unistd.h>
+#endif
+
+#include "../bashtypes.h"
+#include <signal.h>
+#include <stdio.h>
+#include "../bashansi.h"
+
+#include "../shell.h"
+#include "../trap.h"
+#include "common.h"
+#include "bashgetopt.h"
+
+static void showtrap __P((int));
+static int display_traps __P((WORD_LIST *));
+
+/* The trap command:
+
+   trap <arg> <signal ...>
+   trap <signal ...>
+   trap -l
+   trap -p [sigspec ...]
+   trap [--]
+
+   Set things up so that ARG is executed when SIGNAL(s) N is recieved.
+   If ARG is the empty string, then ignore the SIGNAL(s).  If there is
+   no ARG, then set the trap for SIGNAL(s) to its original value.  Just
+   plain "trap" means to print out the list of commands associated with
+   each signal number.  Single arg of "-l" means list the signal names. */
+
+/* Possible operations to perform on the list of signals.*/
+#define SET 0                  /* Set this signal to first_arg. */
+#define REVERT 1               /* Revert to this signals original value. */
+#define IGNORE 2               /* Ignore this signal. */
+
+extern int posixly_correct;
+
+int
+trap_builtin (list)
+     WORD_LIST *list;
+{
+  int list_signal_names, display, result, opt, first_signal;
+
+  list_signal_names = display = 0;
+  result = EXECUTION_SUCCESS;
+  reset_internal_getopt ();
+  while ((opt = internal_getopt (list, "lp")) != -1)
+    {
+      switch (opt)
+       {
+       case 'l':
+         list_signal_names++;
+         break;
+       case 'p':
+         display++;
+         break;
+       default:
+         builtin_usage ();
+         return (EX_USAGE);
+       }
+    }
+  list = loptend;
+
+  opt = DSIG_NOCASE|DSIG_SIGPREFIX;    /* flags for decode_signal */
+
+  if (list_signal_names)
+    return (display_signal_list ((WORD_LIST *)NULL, 1));
+  else if (display || list == 0)
+    return (display_traps (list));
+  else
+    {
+      char *first_arg;
+      int operation, sig, first_signal;
+
+      operation = SET;
+      first_arg = list->word->word;
+      first_signal = first_arg && *first_arg && all_digits (first_arg) && signal_object_p (first_arg, opt);
+
+      /* When in posix mode, the historical behavior of looking for a
+        missing first argument is disabled.  To revert to the original
+        signal handling disposition, use `-' as the first argument. */
+      if (posixly_correct == 0 && first_arg && *first_arg &&
+               (*first_arg != '-' || first_arg[1]) &&
+               signal_object_p (first_arg, opt) && list->next == 0)
+       operation = REVERT;
+      else
+       {
+         list = list->next;
+         if (list == 0)
+           {
+             builtin_usage ();
+             return (EX_USAGE);
+           }
+         else if (*first_arg == '\0')
+           operation = IGNORE;
+         else if (first_arg[0] == '-' && !first_arg[1])
+           operation = REVERT;
+       }
+
+      while (list)
+       {
+         sig = decode_signal (list->word->word, opt);
+
+         if (sig == NO_SIG)
+           {
+             sh_invalidsig (list->word->word);
+             result = EXECUTION_FAILURE;
+           }
+         else
+           {
+             switch (operation)
+               {
+                 case SET:
+                   set_signal (sig, first_arg);
+                   break;
+
+                 case REVERT:
+                   restore_default_signal (sig);
+
+                   /* Signals that the shell treats specially need special
+                      handling. */
+                   switch (sig)
+                     {
+                     case SIGINT:
+                       if (interactive)
+                         set_signal_handler (SIGINT, sigint_sighandler);
+                       else
+                         set_signal_handler (SIGINT, termination_unwind_protect);
+                       break;
+
+                     case SIGQUIT:
+                       /* Always ignore SIGQUIT. */
+                       set_signal_handler (SIGQUIT, SIG_IGN);
+                       break;
+                     case SIGTERM:
+#if defined (JOB_CONTROL)
+                     case SIGTTIN:
+                     case SIGTTOU:
+                     case SIGTSTP:
+#endif /* JOB_CONTROL */
+                       if (interactive)
+                         set_signal_handler (sig, SIG_IGN);
+                       break;
+                     }
+                   break;
+
+                 case IGNORE:
+                   ignore_signal (sig);
+                   break;
+               }
+           }
+         list = list->next;
+       }
+    }
+
+  return (result);
+}
+
+static void
+showtrap (i)
+     int i;
+{
+  char *t, *p, *sn;
+
+  p = trap_list[i];
+
+  if (p == (char *)DEFAULT_SIG)
+    return;
+
+  t = (p == (char *)IGNORE_SIG) ? (char *)NULL : sh_single_quote (p);
+  sn = signal_name (i);
+  /* Make sure that signals whose names are unknown (for whatever reason)
+     are printed as signal numbers. */
+  if (STREQN (sn, "SIGJUNK", 7) || STREQN (sn, "unknown", 7))
+    printf ("trap -- %s %d\n", t ? t : "''", i);
+  else if (posixly_correct)
+    {
+      if (STREQN (sn, "SIG", 3))
+       printf ("trap -- %s %s\n", t ? t : "''", sn+3);
+      else
+       printf ("trap -- %s %s\n", t ? t : "''", sn);
+    }
+  else
+    printf ("trap -- %s %s\n", t ? t : "''", sn);
+
+  FREE (t);
+}
+
+static int
+display_traps (list)
+     WORD_LIST *list;
+{
+  int result, i;
+
+  if (list == 0)
+    {
+      for (i = 0; i < BASH_NSIG; i++)
+       showtrap (i);
+      return (EXECUTION_SUCCESS);
+    }
+
+  for (result = EXECUTION_SUCCESS; list; list = list->next)
+    {
+      i = decode_signal (list->word->word, DSIG_NOCASE|DSIG_SIGPREFIX);
+      if (i == NO_SIG)
+       {
+         sh_invalidsig (list->word->word);
+         result = EXECUTION_FAILURE;
+       }
+      else
+       showtrap (i);
+    }
+
+  return (result);
+}
index 3b33021b36d9f67ea372a16057bb0c0d20097b5f..6edef623af3ab5d60d3fb43ccf917c48d669a587 100644 (file)
@@ -5953,7 +5953,8 @@ The @code{trap} builtin displays signal names without the leading
 @item
 The @code{trap} builtin doesn't check the first argument for a possible
 signal specification and revert the signal handling to the original
-disposition if it is.  If users want to reset the handler for a given
+disposition if it is, unless that argument consists solely of digits and
+is a valid signal number.  If users want to reset the handler for a given
 signal to the original disposition, they should use @samp{-} as the
 first argument.
 
@@ -5988,6 +5989,14 @@ When the @code{cd} builtin is invoked in @var{logical} mode, and the pathname
 constructed from @code{$PWD} and the directory name supplied as an argument
 does not refer to an existing directory, @code{cd} will fail instead of
 falling back to @var{physical} mode.
+
+@item
+When listing the history, the @code{fc} builtin does not include an
+indication of whether or not a history entry has been modified.
+
+@item
+The default editor used by @code{fc} is @code{ed}.
+
 @end enumerate
 
 There is other @sc{posix} 1003.2 behavior that Bash does not implement.
diff --git a/doc/bashref.texi~ b/doc/bashref.texi~
new file mode 100644 (file)
index 0000000..f0bd046
--- /dev/null
@@ -0,0 +1,7273 @@
+\input texinfo.tex @c -*- texinfo -*-
+@c %**start of header
+@setfilename bashref.info
+@settitle Bash Reference Manual
+@c %**end of header
+
+@setchapternewpage odd
+
+@include version.texi
+
+@copying
+This text is a brief description of the features that are present in
+the Bash shell (version @value{VERSION}, @value{UPDATED}).
+
+This is Edition @value{EDITION}, last updated @value{UPDATED},
+of @cite{The GNU Bash Reference Manual},
+for @code{Bash}, Version @value{VERSION}.
+
+Copyright @copyright{} 1988-2004 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+@quotation
+Permission is granted to copy, distribute and/or modify this document
+under the terms of the GNU Free Documentation License, Version 1.1 or
+any later version published by the Free Software Foundation; with no
+Invariant Sections, with the Front-Cover texts being ``A GNU Manual,''
+and with the Back-Cover Texts as in (a) below.  A copy of the license is
+included in the section entitled ``GNU Free Documentation License.''
+
+(a) The FSF's Back-Cover Text is: ``You have freedom to copy and modify
+this GNU Manual, like GNU software.  Copies published by the Free
+Software Foundation raise funds for GNU development.''
+@end quotation
+@end copying
+
+@defcodeindex bt
+@defcodeindex rw
+@set BashFeatures
+
+@dircategory Basics
+@direntry
+* Bash: (bash).                     The GNU Bourne-Again SHell.
+@end direntry
+
+@finalout
+
+@titlepage
+@title Bash Reference Manual
+@subtitle Reference Documentation for Bash
+@subtitle Edition @value{EDITION}, for @code{Bash} Version @value{VERSION}.
+@subtitle @value{UPDATED-MONTH}
+@author Chet Ramey, Case Western Reserve University
+@author Brian Fox, Free Software Foundation
+
+@page
+@vskip 0pt plus 1filll
+@insertcopying
+
+@sp 1
+Published by the Free Software Foundation @*
+59 Temple Place, Suite 330, @*
+Boston, MA 02111-1307 @*
+USA @*
+
+@end titlepage
+
+@contents
+
+@ifnottex
+@node Top, Introduction, (dir), (dir)
+@top Bash Features
+
+This text is a brief description of the features that are present in
+the Bash shell (version @value{VERSION}, @value{UPDATED})..
+
+This is Edition @value{EDITION}, last updated @value{UPDATED},
+of @cite{The GNU Bash Reference Manual},
+for @code{Bash}, Version @value{VERSION}.
+
+Bash contains features that appear in other popular shells, and some
+features that only appear in Bash.  Some of the shells that Bash has
+borrowed concepts from are the Bourne Shell (@file{sh}), the Korn Shell
+(@file{ksh}), and the C-shell (@file{csh} and its successor,
+@file{tcsh}). The following menu breaks the features up into
+categories based upon which one of these other shells inspired the
+feature.
+
+This manual is meant as a brief introduction to features found in
+Bash.  The Bash manual page should be used as the definitive
+reference on shell behavior.
+
+@menu
+* Introduction::               An introduction to the shell.
+
+* Definitions::                        Some definitions used in the rest of this
+                               manual.
+
+* Basic Shell Features::       The shell "building blocks".
+
+* Shell Builtin Commands::     Commands that are a part of the shell.
+
+* Shell Variables::            Variables used or set by Bash.
+
+* Bash Features::              Features found only in Bash.
+
+* Job Control::                        A chapter describing what job control is
+                               and how Bash allows you to use it.
+
+* Using History Interactively::        Chapter dealing with history expansion
+                               rules.
+
+* Command Line Editing::       Chapter describing the command line
+                               editing features.
+
+* Installing Bash::            How to build and install Bash on your system.
+
+* Reporting Bugs::             How to report bugs in Bash.
+
+* Major Differences From The Bourne Shell::    A terse list of the differences
+                                               between Bash and historical
+                                               versions of /bin/sh.
+
+* Copying This Manual::                Copying this manual.
+
+* Builtin Index::              Index of Bash builtin commands.
+
+* Reserved Word Index::                Index of Bash reserved words.
+
+* Variable Index::             Quick reference helps you find the
+                               variable you want.
+
+* Function Index::             Index of bindable Readline functions.
+
+* Concept Index::              General index for concepts described in
+                               this manual.
+@end menu
+@end ifnottex
+
+@node Introduction
+@chapter Introduction
+@menu
+* What is Bash?::              A short description of Bash.
+
+* What is a shell?::           A brief introduction to shells.
+@end menu
+
+@node What is Bash?
+@section What is Bash?
+
+Bash is the shell, or command language interpreter,
+for the @sc{gnu} operating system.
+The name is an acronym for the @samp{Bourne-Again SHell},
+a pun on Stephen Bourne, the author of the direct ancestor of
+the current Unix shell @code{sh}, 
+which appeared in the Seventh Edition Bell Labs Research version
+of Unix.
+
+Bash is largely compatible with @code{sh} and incorporates useful
+features from the Korn shell @code{ksh} and the C shell @code{csh}.
+It is intended to be a conformant implementation of the @sc{ieee}
+@sc{posix} Shell and Tools specification (@sc{ieee} Working Group 1003.2).
+It offers functional improvements over @code{sh} for both interactive and
+programming use.
+
+While the @sc{gnu} operating system provides other shells, including
+a version of @code{csh}, Bash is the default shell.
+Like other @sc{gnu} software, Bash is quite portable.  It currently runs
+on nearly every version of Unix and a few other operating systems @minus{}
+independently-supported ports exist for @sc{ms-dos}, @sc{os/2},
+and Windows platforms.
+
+@node What is a shell?
+@section What is a shell?
+
+At its base, a shell is simply a macro processor that executes
+commands.  The term macro processor means functionality where text
+and symbols are expanded to create larger expressions.
+
+A Unix shell is both a command interpreter and a programming
+language.  As a command interpreter, the shell provides the user
+interface to the rich set of @sc{gnu} utilities.  The programming
+language features allow these utilitites to be combined.
+Files containing commands can be created, and become
+commands themselves.  These new commands have the same status as
+system commands in directories such as @file{/bin}, allowing users
+or groups to establish custom environments to automate their common
+tasks.
+
+Shells may be used interactively or non-interactively.  In
+interactive mode, they accept input typed from the keyboard.
+When executing non-interactively, shells execute commands read
+from a file.
+
+A shell allows execution of @sc{gnu} commands, both synchronously and
+asynchronously.
+The shell waits for synchronous commands to complete before accepting
+more input; asynchronous commands continue to execute in parallel
+with the shell while it reads and executes additional commands.
+The @dfn{redirection} constructs permit
+fine-grained control of the input and output of those commands.
+Moreover, the shell allows control over the contents of commands'
+environments.
+
+Shells also provide a small set of built-in
+commands (@dfn{builtins}) implementing functionality impossible
+or inconvenient to obtain via separate utilities.
+For example, @code{cd}, @code{break}, @code{continue}, and
+@code{exec}) cannot be implemented outside of the shell because
+they directly manipulate the shell itself.
+The @code{history}, @code{getopts}, @code{kill}, or @code{pwd}
+builtins, among others, could be implemented in separate utilities,
+but they are more convenient to use as builtin commands.
+All of the shell builtins are described in
+subsequent sections.
+
+While executing commands is essential, most of the power (and
+complexity) of shells is due to their embedded programming
+languages.  Like any high-level language, the shell provides
+variables, flow control constructs, quoting, and functions. 
+
+Shells offer features geared specifically for
+interactive use rather than to augment the programming language. 
+These interactive features include job control, command line
+editing, command history and aliases.  Each of these features is
+described in this manual.
+
+@node Definitions
+@chapter Definitions
+These definitions are used throughout the remainder of this manual.
+
+@table @code
+
+@item POSIX
+@cindex POSIX
+A family of open system standards based on Unix.  Bash
+is concerned with @sc{posix} 1003.2, the Shell and Tools Standard.
+
+@item blank
+A space or tab character.
+
+@item builtin
+@cindex builtin
+A command that is implemented internally by the shell itself, rather
+than by an executable program somewhere in the file system.
+
+@item control operator
+@cindex control operator
+A @code{word} that performs a control function.  It is a @code{newline}
+or one of the following:
+@samp{||}, @samp{&&}, @samp{&}, @samp{;}, @samp{;;},
+@samp{|}, @samp{(}, or @samp{)}.
+
+@item exit status
+@cindex exit status
+The value returned by a command to its caller.  The value is restricted
+to eight bits, so the maximum value is 255.
+
+@item field
+@cindex field
+A unit of text that is the result of one of the shell expansions.  After
+expansion, when executing a command, the resulting fields are used as
+the command name and arguments.
+
+@item filename
+@cindex filename
+A string of characters used to identify a file.
+
+@item job
+@cindex job
+A set of processes comprising a pipeline, and any processes descended
+from it, that are all in the same process group.
+
+@item job control
+@cindex job control
+A mechanism by which users can selectively stop (suspend) and restart
+(resume) execution of processes.
+
+@item metacharacter
+@cindex metacharacter
+A character that, when unquoted, separates words.  A metacharacter is
+a @code{blank} or one of the following characters:
+@samp{|}, @samp{&}, @samp{;}, @samp{(}, @samp{)}, @samp{<}, or
+@samp{>}.
+
+@item name
+@cindex name
+@cindex identifier
+A @code{word} consisting solely of letters, numbers, and underscores,
+and beginning with a letter or underscore.  @code{Name}s are used as
+shell variable and function names.
+Also referred to as an @code{identifier}.
+
+@item operator
+@cindex operator, shell
+A @code{control operator} or a @code{redirection operator}.
+@xref{Redirections}, for a list of redirection operators.
+
+@item process group
+@cindex process group
+A collection of related processes each having the same process
+group @sc{id}.
+
+@item process group ID
+@cindex process group ID
+A unique identifer that represents a @code{process group}
+during its lifetime.
+
+@item reserved word
+@cindex reserved word
+A @code{word} that has a special meaning to the shell.  Most reserved
+words introduce shell flow control constructs, such as @code{for} and
+@code{while}.
+
+@item return status
+@cindex return status
+A synonym for @code{exit status}.
+
+@item signal
+@cindex signal
+A mechanism by which a process may be notified by the kernel
+of an event occurring in the system.
+
+@item special builtin
+@cindex special builtin
+A shell builtin command that has been classified as special by the
+@sc{posix} 1003.2 standard.
+
+@item token
+@cindex token
+A sequence of characters considered a single unit by the shell.  It is
+either a @code{word} or an @code{operator}.
+
+@item word
+@cindex word
+A @code{token} that is not an @code{operator}.
+@end table
+
+@node Basic Shell Features
+@chapter Basic Shell Features
+@cindex Bourne shell
+
+Bash is an acronym for @samp{Bourne-Again SHell}.
+The Bourne shell is
+the traditional Unix shell originally written by Stephen Bourne.
+All of the Bourne shell builtin commands are available in Bash,
+The rules for evaluation and quoting are taken from the @sc{posix}
+specification for the `standard' Unix shell.
+
+This chapter briefly summarizes the shell's `building blocks':
+commands, control structures, shell functions, shell @i{parameters},
+shell expansions,
+@i{redirections}, which are a way to direct input and output from
+and to named files, and how the shell executes commands.
+
+@menu
+* Shell Syntax::               What your input means to the shell.
+* Shell Commands::             The types of commands you can use.
+* Shell Functions::            Grouping commands by name.
+* Shell Parameters::           How the shell stores values.
+* Shell Expansions::           How Bash expands parameters and the various
+                               expansions available.
+* Redirections::               A way to control where input and output go.
+* Executing Commands::         What happens when you run a command.
+* Shell Scripts::              Executing files of shell commands.
+@end menu
+
+@node Shell Syntax
+@section Shell Syntax
+@menu
+* Shell Operation::    The basic operation of the shell.
+
+* Quoting::            How to remove the special meaning from characters.
+
+* Comments::           How to specify comments.
+@end menu
+
+When the shell reads input, it proceeds through a
+sequence of operations.  If the input indicates the beginning of a
+comment, the shell ignores the comment symbol (@samp{#}), and the rest
+of that line.
+                                
+Otherwise, roughly speaking,  the shell reads its input and
+divides the input into words and operators, employing the quoting rules
+to select which meanings to assign various words and characters.
+
+The shell then parses these tokens into commands and other constructs,
+removes the special meaning of certain words or characters, expands
+others, redirects input and output as needed, executes the specified
+command, waits for the command's exit status, and makes that exit status
+available for further inspection or processing.
+
+@node Shell Operation
+@subsection Shell Operation
+
+The following is a brief description of the shell's operation when it
+reads and executes a command.  Basically, the shell does the
+following:
+
+@enumerate
+@item
+Reads its input from a file (@pxref{Shell Scripts}), from a string
+supplied as an argument to the @option{-c} invocation option
+(@pxref{Invoking Bash}), or from the user's terminal.
+
+@item
+Breaks the input into words and operators, obeying the quoting rules
+described in @ref{Quoting}.  These tokens are separated by
+@code{metacharacters}.  Alias expansion is performed by this step
+(@pxref{Aliases}).
+
+@item
+Parses the tokens into simple and compound commands
+(@pxref{Shell Commands}).
+
+@item
+Performs the various shell expansions (@pxref{Shell Expansions}), breaking
+the expanded tokens into lists of filenames (@pxref{Filename Expansion})
+and commands and arguments.
+
+@item
+Performs any necessary redirections (@pxref{Redirections}) and removes
+the redirection operators and their operands from the argument list.
+
+@item
+Executes the command (@pxref{Executing Commands}).
+
+@item
+Optionally waits for the command to complete and collects its exit
+status (@pxref{Exit Status}).
+
+@end enumerate
+
+@node Quoting
+@subsection Quoting
+@cindex quoting
+@menu
+* Escape Character::   How to remove the special meaning from a single
+                       character.
+* Single Quotes::      How to inhibit all interpretation of a sequence
+                       of characters.
+* Double Quotes::      How to suppress most of the interpretation of a
+                       sequence of characters.
+* ANSI-C Quoting::     How to expand ANSI-C sequences in quoted strings.
+
+* Locale Translation:: How to translate strings into different languages.
+@end menu
+
+Quoting is used to remove the special meaning of certain
+characters or words to the shell.  Quoting can be used to
+disable special treatment for special characters, to prevent
+reserved words from being recognized as such, and to prevent
+parameter expansion.
+
+Each of the shell metacharacters (@pxref{Definitions})
+has special meaning to the shell and must be quoted if it is to
+represent itself.
+When the command history expansion facilities are being used, the
+@var{history expansion} character, usually @samp{!}, must be quoted
+to prevent history expansion.  @xref{Bash History Facilities}, for
+more details concerning history expansion.
+
+There are three quoting mechanisms: the
+@var{escape character}, single quotes, and double quotes.
+
+@node Escape Character
+@subsubsection Escape Character
+A non-quoted backslash @samp{\} is the Bash escape character.
+It preserves the literal value of the next character that follows,
+with the exception of @code{newline}.  If a @code{\newline} pair
+appears, and the backslash itself is not quoted, the @code{\newline}
+is treated as a line continuation (that is, it is removed from
+the input stream and effectively ignored).
+
+@node Single Quotes
+@subsubsection Single Quotes
+
+Enclosing characters in single quotes (@samp{'}) preserves the literal value
+of each character within the quotes.  A single quote may not occur
+between single quotes, even when preceded by a backslash.
+
+@node Double Quotes
+@subsubsection Double Quotes
+
+Enclosing characters in double quotes (@samp{"}) preserves the literal value
+of all characters within the quotes, with the exception of
+@samp{$}, @samp{`}, and @samp{\}.
+The characters @samp{$} and @samp{`}
+retain their special meaning within double quotes (@pxref{Shell Expansions}).
+The backslash retains its special meaning only when followed by one of
+the following characters:
+@samp{$}, @samp{`}, @samp{"}, @samp{\}, or @code{newline}.
+Within double quotes, backslashes that are followed by one of these
+characters are removed.  Backslashes preceding characters without a
+special meaning are left unmodified.
+A double quote may be quoted within double quotes by preceding it with
+a backslash.
+When command history is being used, the double quote may not be used to
+quote the history expansion character.
+
+The special parameters @samp{*} and @samp{@@} have special meaning
+when in double quotes (@pxref{Shell Parameter Expansion}).
+
+@node ANSI-C Quoting
+@subsubsection ANSI-C Quoting
+@cindex quoting, ANSI
+
+Words of the form @code{$'@var{string}'} are treated specially.  The
+word expands to @var{string}, with backslash-escaped characters replaced
+as specified by the ANSI C standard.  Backslash escape sequences, if
+present, are decoded as follows:
+
+@table @code
+@item \a
+alert (bell)
+@item \b
+backspace
+@item \e
+an escape character (not ANSI C)
+@item \f
+form feed
+@item \n
+newline
+@item \r
+carriage return
+@item \t
+horizontal tab
+@item \v
+vertical tab
+@item \\
+backslash
+@item \'
+single quote
+@item \@var{nnn}
+the eight-bit character whose value is the octal value @var{nnn}
+(one to three digits)
+@item \x@var{HH}
+the eight-bit character whose value is the hexadecimal value @var{HH}
+(one or two hex digits)
+@item \c@var{x}
+a control-@var{x} character
+@end table
+
+@noindent
+The expanded result is single-quoted, as if the dollar sign had not
+been present.
+
+@node Locale Translation
+@subsubsection Locale-Specific Translation
+@cindex localization
+@cindex internationalization
+@cindex native languages
+@cindex translation, native languages
+
+A double-quoted string preceded by a dollar sign (@samp{$}) will cause
+the string to be translated according to the current locale.
+If the current locale is @code{C} or @code{POSIX}, the dollar sign
+is ignored.
+If the string is translated and replaced, the replacement is
+double-quoted.
+
+@vindex LC_MESSAGES
+@vindex TEXTDOMAIN
+@vindex TEXTDOMAINDIR
+Some systems use the message catalog selected by the @env{LC_MESSAGES}
+shell variable.  Others create the name of the message catalog from the
+value of the @env{TEXTDOMAIN} shell variable, possibly adding a
+suffix of @samp{.mo}.  If you use the @env{TEXTDOMAIN} variable, you
+may need to set the @env{TEXTDOMAINDIR} variable to the location of
+the message catalog files.  Still others use both variables in this
+fashion:
+@env{TEXTDOMAINDIR}/@env{LC_MESSAGES}/LC_MESSAGES/@env{TEXTDOMAIN}.mo.
+
+@node Comments
+@subsection Comments
+@cindex comments, shell
+
+In a non-interactive shell, or an interactive shell in which the
+@code{interactive_comments} option to the @code{shopt}
+builtin is enabled (@pxref{Bash Builtins}),
+a word beginning with @samp{#}
+causes that word and all remaining characters on that line to
+be ignored.  An interactive shell without the @code{interactive_comments}
+option enabled does not allow comments.  The @code{interactive_comments}
+option is on by default in interactive shells.
+@xref{Interactive Shells}, for a description of what makes
+a shell interactive.
+
+@node Shell Commands
+@section Shell Commands
+@cindex commands, shell
+
+A simple shell command such as @code{echo a b c} consists of the command
+itself followed by arguments, separated by spaces.
+
+More complex shell commands are composed of simple commands arranged together
+in a variety of ways: in a pipeline in which the output of one command
+becomes the input of a second, in a loop or conditional construct, or in
+some other grouping.
+
+@menu
+* Simple Commands::            The most common type of command.
+* Pipelines::                  Connecting the input and output of several
+                               commands.
+* Lists::                      How to execute commands sequentially.
+* Compound Commands::          Shell commands for control flow.
+@end menu
+
+@node Simple Commands
+@subsection Simple Commands
+@cindex commands, simple
+
+A simple command is the kind of command encountered most often.
+It's just a sequence of words separated by @code{blank}s, terminated
+by one of the shell's control operators (@pxref{Definitions}).  The
+first word generally specifies a command to be executed, with the
+rest of the words being that command's arguments.
+
+The return status (@pxref{Exit Status}) of a simple command is
+its exit status as provided
+by the @sc{posix} 1003.1 @code{waitpid} function, or 128+@var{n} if
+the command was terminated by signal @var{n}.
+
+@node Pipelines
+@subsection Pipelines
+@cindex pipeline
+@cindex commands, pipelines
+
+A @code{pipeline} is a sequence of simple commands separated by
+@samp{|}.
+
+@rwindex time
+@rwindex !
+@cindex command timing
+The format for a pipeline is
+@example
+[@code{time} [@code{-p}]] [@code{!}] @var{command1} [@code{|} @var{command2} @dots{}]
+@end example
+
+@noindent
+The output of each command in the pipeline is connected via a pipe
+to the input of the next command.
+That is, each command reads the previous command's output.
+
+The reserved word @code{time} causes timing statistics
+to be printed for the pipeline once it finishes.
+The statistics currently consist of elapsed (wall-clock) time and
+user and system time consumed by the command's execution.
+The @option{-p} option changes the output format to that specified
+by @sc{posix}.
+The @env{TIMEFORMAT} variable may be set to a format string that
+specifies how the timing information should be displayed.
+@xref{Bash Variables}, for a description of the available formats.
+The use of @code{time} as a reserved word permits the timing of
+shell builtins, shell functions, and pipelines.  An external
+@code{time} command cannot time these easily.
+
+If the pipeline is not executed asynchronously (@pxref{Lists}), the
+shell waits for all commands in the pipeline to complete.
+
+Each command in a pipeline is executed in its own subshell
+(@pxref{Command Execution Environment}).  The exit
+status of a pipeline is the exit status of the last command in the
+pipeline, unless the @code{pipefail} option is enabled
+(@pxref{The Set Builtin}).
+If @code{pipefail} is enabled, the pipeline's return status is the
+value of the last (rightmost) command to exit with a non-zero status,
+or zero if all commands exit successfully.
+If the reserved word @samp{!} precedes the pipeline, the
+exit status is the logical negation of the exit status as described
+above.
+The shell waits for all commands in the pipeline to terminate before
+returning a value.
+
+@node Lists
+@subsection Lists of Commands
+@cindex commands, lists
+
+A @code{list} is a sequence of one or more pipelines separated by one
+of the operators @samp{;}, @samp{&}, @samp{&&}, or @samp{||},
+and optionally terminated by one of @samp{;}, @samp{&}, or a
+@code{newline}.
+
+Of these list operators, @samp{&&} and @samp{||}
+have equal precedence, followed by @samp{;} and @samp{&},
+which have equal precedence.
+
+A sequence of one or more newlines may appear in a @code{list}
+to delimit commands, equivalent to a semicolon.
+
+If a command is terminated by the control operator @samp{&},
+the shell executes the command asynchronously in a subshell.
+This is known as executing the command in the @var{background}.
+The shell does not wait for the command to finish, and the return
+status is 0 (true).
+When job control is not active (@pxref{Job Control}),
+the standard input for asynchronous commands, in the absence of any
+explicit redirections, is redirected from @code{/dev/null}.
+
+Commands separated by a @samp{;} are executed sequentially; the shell
+waits for each command to terminate in turn.  The return status is the
+exit status of the last command executed.
+
+The control operators @samp{&&} and @samp{||}
+denote @sc{and} lists and @sc{or} lists, respectively.
+An @sc{and} list has the form
+@example
+@var{command1} && @var{command2}
+@end example
+
+@noindent
+@var{command2} is executed if, and only if, @var{command1}
+returns an exit status of zero.
+
+An @sc{or} list has the form
+@example
+@var{command1} || @var{command2}
+@end example
+
+@noindent
+@var{command2} is executed if, and only if, @var{command1}
+returns a non-zero exit status.
+
+The return status of
+@sc{and} and @sc{or} lists is the exit status of the last command
+executed in the list.
+
+@node Compound Commands
+@subsection Compound Commands
+@cindex commands, compound
+
+@menu
+* Looping Constructs::         Shell commands for iterative action.
+* Conditional Constructs::     Shell commands for conditional execution.
+* Command Grouping::           Ways to group commands.
+@end menu
+
+Compound commands are the shell programming constructs.
+Each construct begins with a reserved word or control operator and is
+terminated by a corresponding reserved word or operator.
+Any redirections (@pxref{Redirections}) associated with a compound command
+apply to all commands within that compound command unless explicitly overridden.
+
+Bash provides looping constructs, conditional commands, and mechanisms
+to group commands and execute them as a unit.
+
+@node Looping Constructs
+@subsubsection Looping Constructs
+@cindex commands, looping
+
+Bash supports the following looping constructs.
+
+Note that wherever a @samp{;} appears in the description of a
+command's syntax, it may be replaced with one or more newlines.
+
+@table @code
+@item until
+@rwindex until
+@rwindex do
+@rwindex done
+The syntax of the @code{until} command is:
+@example
+until @var{test-commands}; do @var{consequent-commands}; done
+@end example
+Execute @var{consequent-commands} as long as
+@var{test-commands} has an exit status which is not zero.
+The return status is the exit status of the last command executed
+in @var{consequent-commands}, or zero if none was executed.
+
+@item while
+@rwindex while
+The syntax of the @code{while} command is:
+@example
+while @var{test-commands}; do @var{consequent-commands}; done
+@end example
+
+Execute @var{consequent-commands} as long as
+@var{test-commands} has an exit status of zero.
+The return status is the exit status of the last command executed
+in @var{consequent-commands}, or zero if none was executed.
+
+@item for
+@rwindex for
+The syntax of the @code{for} command is:
+
+@example
+for @var{name} [in @var{words} @dots{}]; do @var{commands}; done
+@end example
+Expand @var{words}, and execute @var{commands} once for each member
+in the resultant list, with @var{name} bound to the current member.
+If @samp{in @var{words}} is not present, the @code{for} command
+executes the @var{commands} once for each positional parameter that is
+set, as if @samp{in "$@@"} had been specified
+(@pxref{Special Parameters}).
+The return status is the exit status of the last command that executes.
+If there are no items in the expansion of @var{words}, no commands are
+executed, and the return status is zero.
+
+An alternate form of the @code{for} command is also supported:
+
+@example
+for (( @var{expr1} ; @var{expr2} ; @var{expr3} )) ; do @var{commands} ; done
+@end example
+First, the arithmetic expression @var{expr1} is evaluated according
+to the rules described below (@pxref{Shell Arithmetic}).
+The arithmetic expression @var{expr2} is then evaluated repeatedly
+until it evaluates to zero.   
+Each time @var{expr2} evaluates to a non-zero value, @var{commands} are
+executed and the arithmetic expression @var{expr3} is evaluated.       
+If any expression is omitted, it behaves as if it evaluates to 1.
+The return value is the exit status of the last command in @var{list}
+that is executed, or false if any of the expressions is invalid.
+
+@end table
+
+The @code{break} and @code{continue} builtins (@pxref{Bourne Shell Builtins})
+may be used to control loop execution.
+
+@node Conditional Constructs
+@subsubsection Conditional Constructs
+@cindex commands, conditional
+
+@table @code
+@item if
+@rwindex if
+@rwindex then
+@rwindex else
+@rwindex elif
+@rwindex fi
+The syntax of the @code{if} command is:
+
+@example
+if @var{test-commands}; then
+  @var{consequent-commands};
+[elif @var{more-test-commands}; then
+  @var{more-consequents};]
+[else @var{alternate-consequents};]
+fi
+@end example
+
+The @var{test-commands} list is executed, and if its return status is zero,
+the @var{consequent-commands} list is executed.
+If @var{test-commands} returns a non-zero status, each @code{elif} list
+is executed in turn, and if its exit status is zero,
+the corresponding @var{more-consequents} is executed and the   
+command completes.
+If @samp{else @var{alternate-consequents}} is present, and
+the final command in the final @code{if} or @code{elif} clause
+has a non-zero exit status, then @var{alternate-consequents} is executed.
+The return status is the exit status of the last command executed, or
+zero if no condition tested true.
+
+@item case
+@rwindex case
+@rwindex in
+@rwindex esac
+The syntax of the @code{case} command is:
+
+@example
+@code{case @var{word} in [ [(] @var{pattern} [| @var{pattern}]@dots{}) @var{command-list} ;;]@dots{} esac}
+@end example
+
+@code{case} will selectively execute the @var{command-list} corresponding to
+the first @var{pattern} that matches @var{word}.
+The @samp{|} is used to separate multiple patterns, and the @samp{)}
+operator terminates a pattern list.
+A list of patterns and an associated command-list is known
+as a @var{clause}.  Each clause must be terminated with @samp{;;}.
+The @var{word} undergoes tilde expansion, parameter expansion, command
+substitution, arithmetic expansion, and quote removal before matching is
+attempted.  Each @var{pattern} undergoes tilde expansion, parameter
+expansion, command substitution, and arithmetic expansion.
+
+There may be an arbitrary number of @code{case} clauses, each terminated
+by a @samp{;;}.  The first pattern that matches determines the
+command-list that is executed.
+
+Here is an example using @code{case} in a script that could be used to
+describe one interesting feature of an animal:
+
+@example
+echo -n "Enter the name of an animal: "
+read ANIMAL
+echo -n "The $ANIMAL has "
+case $ANIMAL in
+  horse | dog | cat) echo -n "four";;
+  man | kangaroo ) echo -n "two";;
+  *) echo -n "an unknown number of";;
+esac
+echo " legs."
+@end example
+
+@noindent
+The return status is zero if no @var{pattern} is matched.  Otherwise, the
+return status is the exit status of the @var{command-list} executed.
+
+@item select
+@rwindex select
+
+The @code{select} construct allows the easy generation of menus.
+It has almost the same syntax as the @code{for} command:
+
+@example
+select @var{name} [in @var{words} @dots{}]; do @var{commands}; done
+@end example
+
+The list of words following @code{in} is expanded, generating a list
+of items.  The set of expanded words is printed on the standard
+error output stream, each preceded by a number.  If the
+@samp{in @var{words}} is omitted, the positional parameters are printed,
+as if @samp{in "$@@"} had been specifed.
+The @env{PS3} prompt is then displayed and a line is read from the
+standard input.
+If the line consists of a number corresponding to one of the displayed
+words, then the value of @var{name} is set to that word.
+If the line is empty, the words and prompt are displayed again.
+If @code{EOF} is read, the @code{select} command completes.
+Any other value read causes @var{name} to be set to null.
+The line read is saved in the variable @env{REPLY}.
+
+The @var{commands} are executed after each selection until a
+@code{break} command is executed, at which
+point the @code{select} command completes.
+
+Here is an example that allows the user to pick a filename from the
+current directory, and displays the name and index of the file
+selected.
+
+@example
+select fname in *;
+do
+       echo you picked $fname \($REPLY\)
+       break;
+done
+@end example
+
+@item ((@dots{}))
+@example
+(( @var{expression} ))
+@end example
+
+The arithmetic @var{expression} is evaluated according to the rules
+described below (@pxref{Shell Arithmetic}).
+If the value of the expression is non-zero, the return status is 0;
+otherwise the return status is 1.  This is exactly equivalent to
+@example
+let "@var{expression}"
+@end example
+@noindent
+@xref{Bash Builtins}, for a full description of the @code{let} builtin.
+
+@item [[@dots{}]]
+@rwindex [[
+@rwindex ]]
+@example
+[[ @var{expression} ]]
+@end example
+
+Return a status of 0 or 1 depending on the evaluation of
+the conditional expression @var{expression}.
+Expressions are composed of the primaries described below in
+@ref{Bash Conditional Expressions}.
+Word splitting and filename expansion are not performed on the words
+between the @samp{[[} and @samp{]]}; tilde expansion, parameter and
+variable expansion, arithmetic expansion, command substitution, process
+substitution, and quote removal are performed.
+Conditional operators such as @samp{-f} must be unquoted to be recognized
+as primaries.
+
+When the @samp{==} and @samp{!=} operators are used, the string to the
+right of the operator is considered a pattern and matched according
+to the rules described below in @ref{Pattern Matching}.
+The return value is 0 if the string matches or does not match
+the pattern, respectively, and 1 otherwise.
+Any part of the pattern may be quoted to force it to be matched as a
+string.
+
+An additional binary operator, @samp{=~}, is available, with the same
+precedence as @samp{==} and @samp{!=}.
+When it is used, the string to the right of the operator is considered
+an extended regular expression and matched accordingly (as in @i{regex}3)).  
+The return value is 0 if the string matches
+the pattern, and 1 otherwise.
+If the regular expression is syntactically incorrect, the conditional
+expression's return value is 2.
+If the shell option @code{nocaseglob}
+(see the description of @code{shopt} in @ref{Bash Builtins})
+is enabled, the match is performed without regard to the case
+of alphabetic characters.
+Substrings matched by parenthesized subexpressions within the regular
+expression are saved in the array variable @code{BASH_REMATCH}.
+The element of @code{BASH_REMATCH} with index 0 is the portion of the string
+matching the entire regular expression.
+The element of @code{BASH_REMATCH} with index @var{n} is the portion of the
+string matching the @var{n}th parenthesized subexpression.
+
+Expressions may be combined using the following operators, listed
+in decreasing order of precedence:
+
+@table @code
+@item ( @var{expression} )
+Returns the value of @var{expression}.
+This may be used to override the normal precedence of operators.
+
+@item ! @var{expression}
+True if @var{expression} is false.
+
+@item @var{expression1} && @var{expression2}
+True if both @var{expression1} and @var{expression2} are true.
+
+@item @var{expression1} || @var{expression2}
+True if either @var{expression1} or @var{expression2} is true.
+@end table
+@noindent
+The @code{&&} and @code{||} operators do not evaluate @var{expression2} if the
+value of @var{expression1} is sufficient to determine the return
+value of the entire conditional expression.
+
+@end table
+
+@node Command Grouping
+@subsubsection Grouping Commands
+@cindex commands, grouping
+
+Bash provides two ways to group a list of commands to be executed
+as a unit.  When commands are grouped, redirections may be applied
+to the entire command list.  For example, the output of all the
+commands in the list may be redirected to a single stream.
+
+@table @code
+@item ()
+@example
+( @var{list} )
+@end example
+
+Placing a list of commands between parentheses causes a subshell
+environment to be created (@pxref{Command Execution Environment}), and each
+of the commands in @var{list} to be executed in that subshell.  Since the
+@var{list} is executed in a subshell, variable assignments do not remain in
+effect after the subshell completes. 
+
+@item @{@}
+@rwindex @{
+@rwindex @}
+@example
+@{ @var{list}; @}
+@end example
+
+Placing a list of commands between curly braces causes the list to
+be executed in the current shell context.  No subshell is created.
+The semicolon (or newline) following @var{list} is required.
+@end table
+
+In addition to the creation of a subshell, there is a subtle difference
+between these two constructs due to historical reasons.  The braces
+are @code{reserved words}, so they must be separated from the @var{list}
+by @code{blank}s.  The parentheses are @code{operators}, and are
+recognized as separate tokens by the shell even if they are not separated
+from the @var{list} by whitespace.
+
+The exit status of both of these constructs is the exit status of
+@var{list}.
+
+@node Shell Functions
+@section Shell Functions
+@cindex shell function
+@cindex functions, shell
+
+Shell functions are a way to group commands for later execution
+using a single name for the group.  They are executed just like
+a "regular" command.
+When the name of a shell function is used as a simple command name,
+the list of commands associated with that function name is executed.
+Shell functions are executed in the current
+shell context; no new process is created to interpret them.
+
+Functions are declared using this syntax:
+@rwindex function
+@example
+[ @code{function} ] @var{name} () @var{compound-command} [ @var{redirections} ]
+@end example
+
+This defines a shell function named @var{name}.  The reserved
+word @code{function} is optional.
+If the @code{function} reserved
+word is supplied, the parentheses are optional.
+The @var{body} of the function is the compound command
+@var{compound-command} (@pxref{Compound Commands}).
+That command is usually a @var{list} enclosed between @{ and @}, but
+may be any compound command listed above.
+@var{compound-command} is executed whenever @var{name} is specified as the
+name of a command.
+Any redirections (@pxref{Redirections}) associated with the shell function
+are performed when the function is executed.
+
+The exit status of a function definition is zero unless a syntax error
+occurs or a readonly function with the same name already exists.
+When executed, the exit status of a function is the exit status of the
+last command executed in the body.
+
+Note that for historical reasons, in the most common usage the curly braces
+that surround the body of the function must be separated from the body by
+@code{blank}s or newlines.
+This is because the braces are reserved words and are only recognized
+as such when they are separated by whitespace.
+Also, when using the braces, the @var{list} must be terminated by a semicolon,
+a @samp{&}, or a newline.
+
+When a function is executed, the arguments to the
+function become the positional parameters
+during its execution (@pxref{Positional Parameters}).
+The special parameter @samp{#} that expands to the number of
+positional parameters is updated to reflect the change.
+Special parameter @code{0} is unchanged.
+The first element of the @env{FUNCNAME} variable is set to the
+name of the function while the function is executing.
+All other aspects of the shell execution
+environment are identical between a function and its caller
+with the exception that the @env{DEBUG} trap
+below) is not inherited unless the function has been given the
+@code{trace} attribute using the @code{declare} builtin or
+the @code{-o functrace} option has been enabled with
+the @code{set} builtin,
+(in which case all functions inherit the @code{DEBUG} trap).
+@xref{Bourne Shell Builtins}, for the description of the
+@code{trap} builtin.
+
+If the builtin command @code{return}
+is executed in a function, the function completes and
+execution resumes with the next command after the function
+call.
+Any command associated with the @code{RETURN} trap is executed
+before execution resumes.
+When a function completes, the values of the
+positional parameters and the special parameter @samp{#}
+are restored to the values they had prior to the function's
+execution.  If a numeric argument is given to @code{return},
+that is the function's return status; otherwise the function's
+return status is the exit status of the last command executed
+before the @code{return}.
+
+Variables local to the function may be declared with the
+@code{local} builtin.  These variables are visible only to
+the function and the commands it invokes.
+
+Function names and definitions may be listed with the
+@option{-f} option to the @code{declare} or @code{typeset}
+builtin commands (@pxref{Bash Builtins}).
+The @option{-F} option to @code{declare} or @code{typeset}
+will list the function names only
+(and optionally the source file and line number, if the @code{extdebug}
+shell option is enabled).
+Functions may be exported so that subshells
+automatically have them defined with the
+@option{-f} option to the @code{export} builtin
+(@pxref{Bourne Shell Builtins}).
+Note that shell functions and variables with the same name may result
+in multiple identically-named entries in the environment passed to the
+shell's children.
+Care should be taken in cases where this may cause a problem.
+
+Functions may be recursive.  No limit is placed on the number of
+recursive  calls.
+
+@node Shell Parameters
+@section Shell Parameters
+@cindex parameters
+@cindex variable, shell
+@cindex shell variable
+
+@menu
+* Positional Parameters::      The shell's command-line arguments.
+* Special Parameters::         Parameters denoted by special characters.
+@end menu
+
+A @var{parameter} is an entity that stores values.
+It can be a @code{name}, a number, or one of the special characters
+listed below.
+A @var{variable} is a parameter denoted by a @code{name}.
+A variable has a @var{value} and zero or more @var{attributes}.
+Attributes are assigned using the @code{declare} builtin command
+(see the description of the @code{declare} builtin in @ref{Bash Builtins}).
+
+A parameter is set if it has been assigned a value.  The null string is
+a valid value.  Once a variable is set, it may be unset only by using
+the @code{unset} builtin command.
+
+A variable may be assigned to by a statement of the form
+@example
+@var{name}=[@var{value}]
+@end example
+@noindent
+If @var{value}
+is not given, the variable is assigned the null string.  All
+@var{value}s undergo tilde expansion, parameter and variable expansion,
+command substitution, arithmetic expansion, and quote
+removal (detailed below).  If the variable has its @code{integer}
+attribute set, then @var{value} 
+is evaluated as an arithmetic expression even if the @code{$((@dots{}))}
+expansion is not used (@pxref{Arithmetic Expansion}).
+Word splitting is not performed, with the exception
+of @code{"$@@"} as explained below.
+Filename expansion is not performed.
+Assignment statements may also appear as arguments to the
+@code{alias}, 
+@code{declare}, @code{typeset}, @code{export}, @code{readonly},
+and @code{local} builtin commands.
+
+@node Positional Parameters
+@subsection Positional Parameters
+@cindex parameters, positional
+
+A @var{positional parameter} is a parameter denoted by one or more
+digits, other than the single digit @code{0}.  Positional parameters are
+assigned from the shell's arguments when it is invoked,
+and may be reassigned using the @code{set} builtin command.
+Positional parameter @code{N} may be referenced as @code{$@{N@}}, or
+as @code{$N} when @code{N} consists of a single digit.
+Positional parameters may not be assigned to with assignment statements.
+The @code{set} and @code{shift} builtins are used to set and
+unset them (@pxref{Shell Builtin Commands}).
+The positional parameters are
+temporarily replaced when a shell function is executed
+(@pxref{Shell Functions}).
+
+When a positional parameter consisting of more than a single
+digit is expanded, it must be enclosed in braces.
+
+@node Special Parameters
+@subsection Special Parameters
+@cindex parameters, special
+
+The shell treats several parameters specially.  These parameters may
+only be referenced; assignment to them is not allowed.
+
+@vtable @code
+
+@item *
+Expands to the positional parameters, starting from one.  When the
+expansion occurs within double quotes, it expands to a single word
+with the value of each parameter separated by the first character
+of the @env{IFS}
+special variable.  That is, @code{"$*"} is equivalent
+to @code{"$1@var{c}$2@var{c}@dots{}"}, where @var{c}
+is the first character of the value of the @code{IFS}
+variable.
+If @env{IFS} is unset, the parameters are separated by spaces.
+If @env{IFS} is null, the parameters are joined without intervening
+separators.
+
+
+@item @@
+Expands to the positional parameters, starting from one.  When the
+expansion occurs within double quotes, each parameter expands to a
+separate word.  That is, @code{"$@@"} is equivalent to
+@code{"$1" "$2" @dots{}}.
+When there are no positional parameters, @code{"$@@"} and
+@code{$@@}
+expand to nothing (i.e., they are removed).
+
+@item #
+Expands to the number of positional parameters in decimal.
+
+@item ?
+Expands to the exit status of the most recently executed foreground
+pipeline.
+
+@item -
+(A hyphen.)  Expands to the current option flags as specified upon
+invocation, by the @code{set}
+builtin command, or those set by the shell itself
+(such as the @option{-i} option).
+
+@item $
+Expands to the process @sc{id} of the shell.  In a @code{()} subshell, it
+expands to the process @sc{id} of the invoking shell, not the subshell.
+
+@item !
+Expands to the process @sc{id} of the most recently executed background
+(asynchronous) command.
+
+@item 0
+Expands to the name of the shell or shell script.  This is set at
+shell initialization.  If Bash is invoked with a file of commands
+(@pxref{Shell Scripts}), @code{$0} is set to the name of that file.
+If Bash is started with the @option{-c} option (@pxref{Invoking Bash}),
+then @code{$0} is set to the first argument after the string to be
+executed, if one is present.  Otherwise, it is set
+to the filename used to invoke Bash, as given by argument zero.
+
+@item _
+(An underscore.)
+At shell startup, set to the absolute filename of the shell or shell
+script being executed as passed in the argument list.
+Subsequently, expands to the last argument to the previous command,
+after expansion.   
+Also set to the full pathname of each command executed and placed in
+the environment exported to that command.
+When checking mail, this parameter holds the name of the mail file.
+@end vtable
+
+@node Shell Expansions
+@section Shell Expansions
+@cindex expansion
+
+Expansion is performed on the command line after it has been split into
+@code{token}s.  There are seven kinds of expansion performed:
+@itemize @bullet
+@item brace expansion
+@item tilde expansion
+@item parameter and variable expansion
+@item command substitution
+@item arithmetic expansion
+@item word splitting
+@item filename expansion
+@end itemize
+
+@menu
+* Brace Expansion::            Expansion of expressions within braces.
+* Tilde Expansion::            Expansion of the ~ character.
+* Shell Parameter Expansion::  How Bash expands variables to their values.
+* Command Substitution::       Using the output of a command as an argument.
+* Arithmetic Expansion::       How to use arithmetic in shell expansions.
+* Process Substitution::       A way to write and read to and from a
+                               command.
+* Word Splitting::     How the results of expansion are split into separate
+                       arguments.
+* Filename Expansion:: A shorthand for specifying filenames matching patterns.
+* Quote Removal::      How and when quote characters are removed from
+                       words.
+@end menu
+
+The order of expansions is: brace expansion, tilde expansion,
+parameter, variable, and arithmetic expansion and
+command substitution
+(done in a left-to-right fashion), word splitting, and filename
+expansion.
+
+On systems that can support it, there is an additional expansion
+available: @var{process substitution}.  This is performed at the
+same time as parameter, variable, and arithmetic expansion and
+command substitution.
+
+Only brace expansion, word splitting, and filename expansion
+can change the number of words of the expansion; other expansions
+expand a single word to a single word.
+The only exceptions to this are the expansions of
+@code{"$@@"} (@pxref{Special Parameters}) and @code{"$@{@var{name}[@@]@}"}
+(@pxref{Arrays}).
+
+After all expansions, @code{quote removal} (@pxref{Quote Removal})
+is performed.
+
+@node Brace Expansion
+@subsection Brace Expansion
+@cindex brace expansion
+@cindex expansion, brace
+
+Brace expansion is a mechanism by which arbitrary strings may be generated.
+This mechanism is similar to
+@var{filename expansion} (@pxref{Filename Expansion}),
+but the file names generated need not exist.
+Patterns to be brace expanded take the form of an optional @var{preamble},
+followed by either a series of comma-separated strings or a sequnce expression
+between a pair of braces,
+followed by an optional @var{postscript}.
+The preamble is prefixed to each string contained within the braces, and
+the postscript is then appended to each resulting string, expanding left
+to right.
+
+Brace expansions may be nested.
+The results of each expanded string are not sorted; left to right order
+is preserved.
+For example,
+@example
+bash$ echo a@{d,c,b@}e
+ade ace abe
+@end example
+
+A sequence expression takes the form @code{@{@var{x}..@var{y}@}},
+where @var{x} and @var{y} are either integers or single characters.
+When integers are supplied, the expression expands to each number between
+@var{x} and @var{y}, inclusive.
+When characters are supplied, the expression expands to each character
+lexicographically between @var{x} and @var{y}, inclusive.  Note that
+both @var{x} and @var{y} must be of the same type.
+
+Brace expansion is performed before any other expansions,
+and any characters special to other expansions are preserved
+in the result.  It is strictly textual.  Bash
+does not apply any syntactic interpretation to the context of the
+expansion or the text between the braces.
+To avoid conflicts with parameter expansion, the string @samp{$@{}
+is not considered eligible for brace expansion.
+
+A correctly-formed brace expansion must contain unquoted opening
+and closing braces, and at least one unquoted comma or a valid
+sequence expression.
+Any incorrectly formed brace expansion is left unchanged.
+
+A @{ or @samp{,} may be quoted with a backslash to prevent its
+being considered part of a brace expression.
+To avoid conflicts with parameter expansion, the string @samp{$@{}
+is not considered eligible for brace expansion.
+
+This construct is typically used as shorthand when the common
+prefix of the strings to be generated is longer than in the
+above example:
+@example
+mkdir /usr/local/src/bash/@{old,new,dist,bugs@}
+@end example
+or
+@example
+chown root /usr/@{ucb/@{ex,edit@},lib/@{ex?.?*,how_ex@}@}
+@end example
+
+@node Tilde Expansion
+@subsection Tilde Expansion
+@cindex tilde expansion
+@cindex expansion, tilde
+
+If a word begins with an unquoted tilde character (@samp{~}), all of the
+characters up to the first unquoted slash (or all characters,
+if there is no unquoted slash) are considered a @var{tilde-prefix}.
+If none of the characters in the tilde-prefix are quoted, the
+characters in the tilde-prefix following the tilde are treated as a
+possible @var{login name}.
+If this login name is the null string, the tilde is replaced with the
+value of the @env{HOME} shell variable.
+If @env{HOME} is unset, the home directory of the user executing the
+shell is substituted instead.
+Otherwise, the tilde-prefix is replaced with the home directory
+associated with the specified login name.
+
+If the tilde-prefix is @samp{~+}, the value of
+the shell variable @env{PWD} replaces the tilde-prefix.
+If the tilde-prefix is @samp{~-}, the value of the shell variable
+@env{OLDPWD}, if it is set, is substituted.
+
+If the characters following the tilde in the tilde-prefix consist of a
+number @var{N}, optionally prefixed by a @samp{+} or a @samp{-},
+the tilde-prefix is replaced with the
+corresponding element from the directory stack, as it would be displayed
+by the @code{dirs} builtin invoked with the characters following tilde
+in the tilde-prefix as an argument (@pxref{The Directory Stack}).
+If the tilde-prefix, sans the tilde, consists of a number without a
+leading @samp{+} or @samp{-}, @samp{+} is assumed.
+
+If the login name is invalid, or the tilde expansion fails, the word is
+left unchanged.
+
+Each variable assignment is checked for unquoted tilde-prefixes immediately
+following a @samp{:} or @samp{=}.
+In these cases, tilde expansion is also performed.
+Consequently, one may use file names with tildes in assignments to
+@env{PATH}, @env{MAILPATH}, and @env{CDPATH},
+and the shell assigns the expanded value.
+
+The following table shows how Bash treats unquoted tilde-prefixes:
+
+@table @code
+@item ~
+The value of @code{$HOME}
+@item ~/foo
+@file{$HOME/foo}
+
+@item ~fred/foo
+The subdirectory @code{foo} of the home directory of the user
+@code{fred}
+
+@item ~+/foo
+@file{$PWD/foo}
+
+@item ~-/foo
+@file{$@{OLDPWD-'~-'@}/foo}
+
+@item ~@var{N}
+The string that would be displayed by @samp{dirs +@var{N}}
+
+@item ~+@var{N}
+The string that would be displayed by @samp{dirs +@var{N}}
+
+@item ~-@var{N}
+The string that would be displayed by @samp{dirs -@var{N}}
+
+@end table
+
+@node Shell Parameter Expansion
+@subsection Shell Parameter Expansion
+@cindex parameter expansion
+@cindex expansion, parameter
+
+The @samp{$} character introduces parameter expansion,
+command substitution, or arithmetic expansion.  The parameter name
+or symbol to be expanded may be enclosed in braces, which
+are optional but serve to protect the variable to be expanded from
+characters immediately following it which could be
+interpreted as part of the name.
+
+When braces are used, the matching ending brace is the first @samp{@}}
+not escaped by a backslash or within a quoted string, and not within an
+embedded arithmetic expansion, command substitution, or parameter
+expansion.
+
+The basic form of parameter expansion is $@{@var{parameter}@}.
+The value of @var{parameter} is substituted.  The braces are required
+when @var{parameter}
+is a positional parameter with more than one digit,
+or when @var{parameter}
+is followed by a character that is not to be
+interpreted as part of its name.
+
+If the first character of @var{parameter} is an exclamation point,
+a level of variable indirection is introduced.
+Bash uses the value of the variable formed from the rest of
+@var{parameter} as the name of the variable; this variable is then
+expanded and that value is used in the rest of the substitution, rather
+than the value of @var{parameter} itself.
+This is known as @code{indirect expansion}.
+The exceptions to this are the expansions of $@{!@var{prefix*}@}
+and $@{!@var{name}[@@]@}
+described below.
+The exclamation point must immediately follow the left brace in order to
+introduce indirection.
+
+In each of the cases below, @var{word} is subject to tilde expansion,
+parameter expansion, command substitution, and arithmetic expansion.
+
+When not performing substring expansion, Bash tests for a parameter
+that is unset or null; omitting the colon results in a test only for a
+parameter that is unset.  Put another way, if the colon is included,
+the operator tests for both existence and that the value is not null;
+if the colon is omitted, the operator tests only for existence.
+
+@table @code
+
+@item $@{@var{parameter}:@minus{}@var{word}@}
+If @var{parameter} is unset or null, the expansion of
+@var{word} is substituted.  Otherwise, the value of
+@var{parameter} is substituted.
+
+@item $@{@var{parameter}:=@var{word}@}
+If @var{parameter}
+is unset or null, the expansion of @var{word}
+is assigned to @var{parameter}.
+The value of @var{parameter} is then substituted. 
+Positional parameters and special parameters may not be assigned to
+in this way.
+
+@item $@{@var{parameter}:?@var{word}@}
+If @var{parameter}
+is null or unset, the expansion of @var{word} (or a message
+to that effect if @var{word}
+is not present) is written to the standard error and the shell, if it
+is not interactive, exits.  Otherwise, the value of @var{parameter} is
+substituted.
+
+@item $@{@var{parameter}:+@var{word}@}
+If @var{parameter}
+is null or unset, nothing is substituted, otherwise the expansion of
+@var{word} is substituted.
+
+@item $@{@var{parameter}:@var{offset}@}
+@itemx $@{@var{parameter}:@var{offset}:@var{length}@}
+Expands to up to @var{length} characters of @var{parameter}
+starting at the character specified by @var{offset}.
+If @var{length} is omitted, expands to the substring of
+@var{parameter} starting at the character specified by @var{offset}.
+@var{length} and @var{offset} are arithmetic expressions
+(@pxref{Shell Arithmetic}).
+This is referred to as Substring Expansion.
+
+@var{length} must evaluate to a number greater than or equal to zero.
+If @var{offset} evaluates to a number less than zero, the value
+is used as an offset from the end of the value of @var{parameter}.
+If @var{parameter} is @samp{@@}, the result is @var{length} positional
+parameters beginning at @var{offset}.
+If @var{parameter} is an array name indexed by @samp{@@} or @samp{*},
+the result is the @var{length}
+members of the array beginning with @code{$@{@var{parameter}[@var{offset}]@}}.
+Substring indexing is zero-based unless the positional parameters
+are used, in which case the indexing starts at 1.
+
+@item $@{!@var{prefix}*@}
+@itemx $@{!@var{prefix}@@@}
+Expands to the names of variables whose names begin with @var{prefix},
+separated by the first character of the @env{IFS} special variable.
+
+@item $@{!@var{name}[@@]@}
+@itemx $@{!@var{name}[*]@}
+If @var{name} is an array variable, expands to the list of array indices
+(keys) assigned in @var{name}.
+If @var{name} is not an array, expands to 0 if @var{name} is set and null
+otherwise.
+When @samp{@@} is used and the expansion appears within double quotes, each
+key expands to a separate word.
+
+@item $@{#@var{parameter}@}
+The length in characters of the expanded value of @var{parameter} is
+substituted.
+If @var{parameter} is @samp{*} or @samp{@@}, the value substituted
+is the number of positional parameters.
+If @var{parameter} is an array name subscripted by @samp{*} or @samp{@@}, 
+the value substituted is the number of elements in the array.
+
+@item $@{@var{parameter}#@var{word}@}
+@itemx $@{@var{parameter}##@var{word}@}
+The @var{word}
+is expanded to produce a pattern just as in filename
+expansion (@pxref{Filename Expansion}).  If the pattern matches
+the beginning of the expanded value of @var{parameter},
+then the result of the expansion is the expanded value of @var{parameter}
+with the shortest matching pattern (the @samp{#} case) or the
+longest matching pattern (the @samp{##} case) deleted.
+If @var{parameter} is @samp{@@} or @samp{*},
+the pattern removal operation is applied to each positional
+parameter in turn, and the expansion is the resultant list.
+If @var{parameter} is an array variable subscripted with
+@samp{@@} or @samp{*},
+the pattern removal operation is applied to each member of the
+array in turn, and the expansion is the resultant list.
+
+@item $@{@var{parameter}%@var{word}@}
+@itemx $@{@var{parameter}%%@var{word}@}
+The @var{word} is expanded to produce a pattern just as in
+filename expansion.
+If the pattern matches a trailing portion of the expanded value of
+@var{parameter}, then the result of the expansion is the value of
+@var{parameter} with the shortest matching pattern (the @samp{%} case)
+or the longest matching pattern (the @samp{%%} case) deleted.
+If @var{parameter} is @samp{@@} or @samp{*},
+the pattern removal operation is applied to each positional
+parameter in turn, and the expansion is the resultant list.
+If @var{parameter}
+is an array variable subscripted with @samp{@@} or @samp{*},
+the pattern removal operation is applied to each member of the
+array in turn, and the expansion is the resultant list.
+
+@item $@{@var{parameter}/@var{pattern}/@var{string}@} 
+@itemx $@{@var{parameter}//@var{pattern}/@var{string}@}
+
+The @var{pattern} is expanded to produce a pattern just as in
+filename expansion.
+@var{Parameter} is expanded and the longest match of @var{pattern}
+against its value is replaced with @var{string}.
+In the first form, only the first match is replaced.
+The second form causes all matches of @var{pattern} to be
+replaced with @var{string}.
+If @var{pattern} begins with @samp{#}, it must match at the beginning
+of the expanded value of @var{parameter}.
+If @var{pattern} begins with @samp{%}, it must match at the end
+of the expanded value of @var{parameter}.
+If @var{string} is null, matches of @var{pattern} are deleted
+and the @code{/} following @var{pattern} may be omitted.
+If @var{parameter} is @samp{@@} or @samp{*},
+the substitution operation is applied to each positional
+parameter in turn, and the expansion is the resultant list.
+If @var{parameter}
+is an array variable subscripted with @samp{@@} or @samp{*},
+the substitution operation is applied to each member of the
+array in turn, and the expansion is the resultant list.
+
+@end table
+
+@node Command Substitution
+@subsection Command Substitution
+@cindex command substitution
+
+Command substitution allows the output of a command to replace
+the command itself.
+Command substitution occurs when a command is enclosed as follows:
+@example
+$(@var{command})
+@end example
+@noindent
+or
+@example
+`@var{command}`
+@end example
+
+@noindent
+Bash performs the expansion by executing @var{command} and
+replacing the command substitution with the standard output of the
+command, with any trailing newlines deleted.
+Embedded newlines are not deleted, but they may be removed during
+word splitting.
+The command substitution @code{$(cat @var{file})} can be
+replaced by the equivalent but faster @code{$(< @var{file})}.
+
+When the old-style backquote form of substitution is used,
+backslash retains its literal meaning except when followed by
+@samp{$}, @samp{`}, or @samp{\}. 
+The first backquote not preceded by a backslash terminates the
+command substitution.
+When using the @code{$(@var{command})} form, all characters between
+the parentheses make up the command; none are treated specially.
+
+Command substitutions may be nested.  To nest when using the backquoted
+form, escape the inner backquotes with backslashes.
+
+If the substitution appears within double quotes, word splitting and
+filename expansion are not performed on the results.
+
+@node Arithmetic Expansion
+@subsection Arithmetic Expansion
+@cindex expansion, arithmetic
+@cindex arithmetic expansion
+
+Arithmetic expansion allows the evaluation of an arithmetic expression
+and the substitution of the result.  The format for arithmetic expansion is:
+
+@example
+$(( @var{expression} ))
+@end example
+
+The expression is treated as if it were within double quotes, but
+a double quote inside the parentheses is not treated specially.
+All tokens in the expression undergo parameter expansion, command
+substitution, and quote removal.
+Arithmetic expansions may be nested. 
+
+The evaluation is performed according to the rules listed below
+(@pxref{Shell Arithmetic}).
+If the expression is invalid, Bash prints a message indicating
+failure to the standard error and no substitution occurs.
+
+@node Process Substitution
+@subsection Process Substitution
+@cindex process substitution
+
+Process substitution is supported on systems that support named
+pipes (@sc{fifo}s) or the @file{/dev/fd} method of naming open files.
+It takes the form of 
+@example
+<(@var{list})
+@end example
+@noindent
+or
+@example
+>(@var{list})
+@end example
+@noindent
+The process @var{list} is run with its input or output connected to a
+@sc{fifo} or some file in @file{/dev/fd}.  The name of this file is
+passed as an argument to the current command as the result of the
+expansion.  If the @code{>(@var{list})} form is used, writing to
+the file will provide input for @var{list}.  If the
+@code{<(@var{list})} form is used, the file passed as an
+argument should be read to obtain the output of @var{list}.
+Note that no space may appear between the @code{<} or @code{>}
+and the left parenthesis, otherwise the construct would be interpreted
+as a redirection.
+
+When available, process substitution is performed simultaneously with
+parameter and variable expansion, command substitution, and arithmetic
+expansion.
+
+@node Word Splitting
+@subsection Word Splitting
+@cindex word splitting
+
+The shell scans the results of parameter expansion, command substitution,
+and arithmetic expansion that did not occur within double quotes for
+word splitting.
+
+The shell treats each character of @env{$IFS}
+as a delimiter, and splits the results of the other
+expansions into words on these characters.  If
+@env{IFS} is unset, or its value is exactly @code{<space><tab><newline>},
+the default, then any sequence of @env{IFS}
+characters serves to delimit words.  If @env{IFS}
+has a value other than the default, then sequences of
+the whitespace characters @code{space} and @code{tab}
+are ignored at the beginning and end of the
+word, as long as the whitespace character is in the
+value of @env{IFS} (an @env{IFS} whitespace character).
+Any character in @env{IFS} that is not @env{IFS}
+whitespace, along with any adjacent @env{IFS}
+whitespace characters, delimits a field.  A sequence of @env{IFS}
+whitespace characters is also treated as a delimiter.
+If the value of @env{IFS} is null, no word splitting occurs.
+
+Explicit null arguments (@code{""} or @code{''}) are retained.
+Unquoted implicit null arguments, resulting from the expansion of
+parameters that have no values, are removed.
+If a parameter with no value is expanded within double quotes, a
+null argument results and is retained.
+
+Note that if no expansion occurs, no splitting
+is performed.
+
+@node Filename Expansion
+@subsection Filename Expansion
+@menu
+* Pattern Matching::   How the shell matches patterns.
+@end menu
+@cindex expansion, filename
+@cindex expansion, pathname
+@cindex filename expansion
+@cindex pathname expansion
+
+After word splitting, unless the @option{-f} option has been set
+(@pxref{The Set Builtin}), Bash scans each word for the characters
+@samp{*}, @samp{?}, and @samp{[}.
+If one of these characters appears, then the word is
+regarded as a @var{pattern},
+and replaced with an alphabetically sorted list of
+file names matching the pattern. If no matching file names are found,
+and the shell option @code{nullglob} is disabled, the word is left
+unchanged.
+If the @code{nullglob} option is set, and no matches are found, the word
+is removed.
+If the @code{failglob} shell option is set, and no matches are found,
+an error message is printed and the command is not executed.
+If the shell option @code{nocaseglob} is enabled, the match is performed
+without regard to the case of alphabetic characters.
+
+When a pattern is used for filename generation, the character @samp{.}
+at the start of a filename or immediately following a slash
+must be matched explicitly, unless the shell option @code{dotglob} is set.
+When matching a file name, the slash character must always be
+matched explicitly.
+In other cases, the @samp{.} character is not treated specially.
+
+See the description of @code{shopt} in @ref{Bash Builtins},
+for a description of the @code{nocaseglob}, @code{nullglob},
+@code{failglob}, and @code{dotglob} options.
+
+The @env{GLOBIGNORE}
+shell variable may be used to restrict the set of filenames matching a
+pattern.  If @env{GLOBIGNORE}
+is set, each matching filename that also matches one of the patterns in
+@env{GLOBIGNORE} is removed from the list of matches.  The filenames
+@file{.} and @file{..}
+are always ignored when @env{GLOBIGNORE}
+is set and not null.
+However, setting @env{GLOBIGNORE} to a non-null value has the effect of
+enabling the @code{dotglob}
+shell option, so all other filenames beginning with a
+@samp{.} will match.
+To get the old behavior of ignoring filenames beginning with a
+@samp{.}, make @samp{.*} one of the patterns in @env{GLOBIGNORE}.
+The @code{dotglob} option is disabled when @env{GLOBIGNORE}
+is unset.
+
+@node Pattern Matching
+@subsubsection Pattern Matching
+@cindex pattern matching
+@cindex matching, pattern
+
+Any character that appears in a pattern, other than the special pattern
+characters described below, matches itself.
+The @sc{nul} character may not occur in a pattern.
+A backslash escapes the following character; the
+escaping backslash is discarded when matching.
+The special pattern characters must be quoted if they are to be matched
+literally.
+
+The special pattern characters have the following meanings:
+@table @code
+@item *
+Matches any string, including the null string.
+@item ?
+Matches any single character.
+@item [@dots{}]
+Matches any one of the enclosed characters.  A pair of characters
+separated by a hyphen denotes a @var{range expression};
+any character that sorts between those two characters, inclusive,
+using the current locale's collating sequence and character set,
+is matched.  If the first character following the
+@samp{[} is a @samp{!}  or a @samp{^}
+then any character not enclosed is matched.  A @samp{@minus{}}
+may be matched by including it as the first or last character
+in the set.  A @samp{]} may be matched by including it as the first
+character in the set.
+The sorting order of characters in range expressions is determined by
+the current locale and the value of the @env{LC_COLLATE} shell variable,
+if set.
+
+For example, in the default C locale, @samp{[a-dx-z]} is equivalent to
+@samp{[abcdxyz]}.  Many locales sort characters in dictionary order, and in
+these locales @samp{[a-dx-z]} is typically not equivalent to @samp{[abcdxyz]};
+it might be equivalent to @samp{[aBbCcDdxXyYz]}, for example.  To obtain
+the traditional interpretation of ranges in bracket expressions, you can
+force the use of the C locale by setting the @env{LC_COLLATE} or
+@env{LC_ALL} environment variable to the value @samp{C}.
+
+Within @samp{[} and @samp{]}, @var{character classes} can be specified
+using the syntax
+@code{[:}@var{class}@code{:]}, where @var{class} is one of the
+following classes defined in the @sc{posix} 1003.2 standard:
+@example
+alnum   alpha   ascii   blank   cntrl   digit   graph   lower
+print   punct   space   upper   word    xdigit
+@end example
+@noindent
+A character class matches any character belonging to that class.
+The @code{word} character class matches letters, digits, and the character
+@samp{_}.
+
+Within @samp{[} and @samp{]}, an @var{equivalence class} can be
+specified using the syntax @code{[=}@var{c}@code{=]}, which
+matches all characters with the same collation weight (as defined
+by the current locale) as the character @var{c}.
+
+Within @samp{[} and @samp{]}, the syntax @code{[.}@var{symbol}@code{.]}
+matches the collating symbol @var{symbol}.
+@end table
+
+If the @code{extglob} shell option is enabled using the @code{shopt}
+builtin, several extended pattern matching operators are recognized.
+In the following description, a @var{pattern-list} is a list of one
+or more patterns separated by a @samp{|}.
+Composite patterns may be formed using one or more of the following
+sub-patterns:
+
+@table @code
+@item ?(@var{pattern-list})
+Matches zero or one occurrence of the given patterns.
+
+@item *(@var{pattern-list})
+Matches zero or more occurrences of the given patterns.
+
+@item +(@var{pattern-list})
+Matches one or more occurrences of the given patterns.
+
+@item @@(@var{pattern-list})
+Matches exactly one of the given patterns.
+
+@item !(@var{pattern-list})
+Matches anything except one of the given patterns.
+@end table
+
+@node Quote Removal
+@subsection Quote Removal
+
+After the preceding expansions, all unquoted occurrences of the
+characters @samp{\}, @samp{'}, and @samp{"} that did not
+result from one of the above expansions are removed.
+
+@node Redirections
+@section Redirections
+@cindex redirection
+
+Before a command is executed, its input and output
+may be @var{redirected}
+using a special notation interpreted by the shell.
+Redirection may also be used to open and close files for the
+current shell execution environment.  The following redirection
+operators may precede or appear anywhere within a
+simple command or may follow a command.
+Redirections are processed in the order they appear, from
+left to right.
+
+In the following descriptions, if the file descriptor number is
+omitted, and the first character of the redirection operator is
+@samp{<}, the redirection refers to the standard input (file
+descriptor 0).  If the first character of the redirection operator
+is @samp{>}, the redirection refers to the standard output (file
+descriptor 1).
+
+The word following the redirection operator in the following
+descriptions, unless otherwise noted, is subjected to brace expansion,
+tilde expansion, parameter expansion, command substitution, arithmetic
+expansion, quote removal, filename expansion, and word splitting.
+If it expands to more than one word, Bash reports an error.
+
+Note that the order of redirections is significant.  For example,
+the command
+@example
+ls > @var{dirlist} 2>&1
+@end example
+@noindent
+directs both standard output (file descriptor 1) and standard error
+(file descriptor 2) to the file @var{dirlist}, while the command
+@example
+ls 2>&1 > @var{dirlist}
+@end example
+@noindent
+directs only the standard output to file @var{dirlist},
+because the standard error was duplicated as standard output
+before the standard output was redirected to @var{dirlist}.
+
+Bash handles several filenames specially when they are used in
+redirections, as described in the following table:
+
+@table @code
+@item /dev/fd/@var{fd}
+If @var{fd} is a valid integer, file descriptor @var{fd} is duplicated.
+
+@item /dev/stdin
+File descriptor 0 is duplicated.
+
+@item /dev/stdout
+File descriptor 1 is duplicated.
+
+@item /dev/stderr
+File descriptor 2 is duplicated.
+
+@item /dev/tcp/@var{host}/@var{port}
+If @var{host} is a valid hostname or Internet address, and @var{port}
+is an integer port number or service name, Bash attempts to open a TCP
+connection to the corresponding socket.
+
+@item /dev/udp/@var{host}/@var{port}
+If @var{host} is a valid hostname or Internet address, and @var{port}
+is an integer port number or service name, Bash attempts to open a UDP
+connection to the corresponding socket.
+
+@end table
+
+A failure to open or create a file causes the redirection to fail.
+
+@subsection Redirecting Input
+Redirection of input causes the file whose name results from
+the expansion of @var{word}
+to be opened for reading on file descriptor @code{n},
+or the standard input (file descriptor 0) if @code{n}
+is not specified.
+
+The general format for redirecting input is:
+@example
+[@var{n}]<@var{word}
+@end example
+
+@subsection Redirecting Output
+Redirection of output causes the file whose name results from
+the expansion of @var{word}
+to be opened for writing on file descriptor @var{n},
+or the standard output (file descriptor 1) if @var{n}
+is not specified.  If the file does not exist it is created;
+if it does exist it is truncated to zero size.
+
+The general format for redirecting output is:
+@example
+[@var{n}]>[|]@var{word}
+@end example
+
+If the redirection operator is @samp{>}, and the @code{noclobber}
+option to the @code{set} builtin has been enabled, the redirection
+will fail if the file whose name results from the expansion of
+@var{word} exists and is a regular file.
+If the redirection operator is @samp{>|}, or the redirection operator is
+@samp{>} and the @code{noclobber} option is not enabled, the redirection
+is attempted even if the file named by @var{word} exists.
+
+@subsection Appending Redirected Output
+Redirection of output in this fashion
+causes the file whose name results from
+the expansion of @var{word}
+to be opened for appending on file descriptor @var{n},
+or the standard output (file descriptor 1) if @var{n}
+is not specified.  If the file does not exist it is created.
+
+The general format for appending output is:
+@example
+[@var{n}]>>@var{word}
+@end example
+
+@subsection Redirecting Standard Output and Standard Error
+Bash allows both the
+standard output (file descriptor 1) and
+the standard error output (file descriptor 2)
+to be redirected to the file whose name is the
+expansion of @var{word} with this construct.
+
+There are two formats for redirecting standard output and
+standard error:
+@example
+&>@var{word}
+@end example
+@noindent
+and
+@example
+>&@var{word}
+@end example
+@noindent
+Of the two forms, the first is preferred.
+This is semantically equivalent to
+@example
+>@var{word} 2>&1
+@end example
+
+@subsection Here Documents
+This type of redirection instructs the shell to read input from the
+current source until a line containing only @var{word}
+(with no trailing blanks) is seen.  All of
+the lines read up to that point are then used as the standard
+input for a command.
+
+The format of here-documents is:
+@example
+<<[@minus{}]@var{word}
+        @var{here-document}
+@var{delimiter}
+@end example
+
+No parameter expansion, command substitution, arithmetic expansion,
+or filename expansion is performed on
+@var{word}.  If any characters in @var{word} are quoted, the
+@var{delimiter} is the result of quote removal on @var{word},
+and the lines in the here-document are not expanded.
+If @var{word} is unquoted,
+all lines of the here-document are subjected to parameter expansion,
+command substitution, and arithmetic expansion.  In the latter
+case, the character sequence @code{\newline} is ignored, and @samp{\}
+must be used to quote the characters
+@samp{\}, @samp{$}, and @samp{`}.
+
+If the redirection operator is @samp{<<-},
+then all leading tab characters are stripped from input lines and the
+line containing @var{delimiter}.
+This allows here-documents within shell scripts to be indented in a
+natural fashion.
+
+@subsection Here Strings
+A variant of here documents, the format is:
+@example
+<<< @var{word}
+@end example
+
+The @var{word} is expanded and supplied to the command on its standard
+input.
+
+@subsection Duplicating File Descriptors
+The redirection operator
+@example
+[@var{n}]<&@var{word}
+@end example
+@noindent
+is used to duplicate input file descriptors.
+If @var{word}
+expands to one or more digits, the file descriptor denoted by @var{n}
+is made to be a copy of that file descriptor.
+If the digits in @var{word} do not specify a file descriptor open for
+input, a redirection error occurs.
+If @var{word}
+evaluates to @samp{-}, file descriptor @var{n} is closed.  If
+@var{n} is not specified, the standard input (file descriptor 0) is used.
+
+The operator
+@example
+[@var{n}]>&@var{word}
+@end example
+@noindent
+is used similarly to duplicate output file descriptors.  If
+@var{n} is not specified, the standard output (file descriptor 1) is used.
+If the digits in @var{word} do not specify a file descriptor open for
+output, a redirection error occurs.
+As a special case, if @var{n} is omitted, and @var{word} does not
+expand to one or more digits, the standard output and standard
+error are redirected as described previously.
+
+@subsection Moving File Descriptors
+The redirection operator
+@example
+[@var{n}]<&@var{digit}-
+@end example
+@noindent
+moves the file descriptor @var{digit} to file descriptor @var{n},
+or the standard input (file descriptor 0) if @var{n} is not specified.
+@var{digit} is closed after being duplicated to @var{n}.
+
+Similarly, the redirection operator
+@example
+[@var{n}]>&@var{digit}-
+@end example
+@noindent
+moves the file descriptor @var{digit} to file descriptor @var{n},
+or the standard output (file descriptor 1) if @var{n} is not specified.
+
+@subsection Opening File Descriptors for Reading and Writing
+The redirection operator
+@example
+[@var{n}]<>@var{word}
+@end example
+@noindent
+causes the file whose name is the expansion of @var{word}
+to be opened for both reading and writing on file descriptor
+@var{n}, or on file descriptor 0 if @var{n}
+is not specified.  If the file does not exist, it is created.
+
+@node Executing Commands
+@section Executing Commands
+
+@menu
+* Simple Command Expansion::   How Bash expands simple commands before
+                               executing them.
+
+* Command Search and Execution::       How Bash finds commands and runs them.
+
+* Command Execution Environment::      The environment in which Bash
+                                       executes commands that are not
+                                       shell builtins.
+
+* Environment::                The environment given to a command.
+
+* Exit Status::                The status returned by commands and how Bash
+                       interprets it.
+
+* Signals::            What happens when Bash or a command it runs
+                       receives a signal.
+
+@end menu
+
+@node Simple Command Expansion
+@subsection Simple Command Expansion
+@cindex command expansion
+
+When a simple command is executed, the shell performs the following
+expansions, assignments, and redirections, from left to right.
+
+@enumerate
+@item
+The words that the parser has marked as variable assignments (those
+preceding the command name) and redirections are saved for later
+processing.
+
+@item
+The words that are not variable assignments or redirections are
+expanded (@pxref{Shell Expansions}).
+If any words remain after expansion, the first word
+is taken to be the name of the command and the remaining words are
+the arguments.
+
+@item
+Redirections are performed as described above (@pxref{Redirections}).
+
+@item
+The text after the @samp{=} in each variable assignment undergoes tilde
+expansion, parameter expansion, command substitution, arithmetic expansion,
+and quote removal before being assigned to the variable.
+@end enumerate
+
+If no command name results, the variable assignments affect the current
+shell environment.  Otherwise, the variables are added to the environment
+of the executed command and do not affect the current shell environment.
+If any of the assignments attempts to assign a value to a readonly variable,
+an error occurs, and the command exits with a non-zero status.
+
+If no command name results, redirections are performed, but do not
+affect the current shell environment.  A redirection error causes the
+command to exit with a non-zero status.
+
+If there is a command name left after expansion, execution proceeds as
+described below.  Otherwise, the command exits.  If one of the expansions
+contained a command substitution, the exit status of the command is
+the exit status of the last command substitution performed.  If there
+were no command substitutions, the command exits with a status of zero.
+
+@node Command Search and Execution
+@subsection Command Search and Execution
+@cindex command execution
+@cindex command search
+
+After a command has been split into words, if it results in a
+simple command and an optional list of arguments, the following
+actions are taken.
+
+@enumerate
+@item
+If the command name contains no slashes, the shell attempts to
+locate it.  If there exists a shell function by that name, that
+function is invoked as described in @ref{Shell Functions}.
+
+@item
+If the name does not match a function, the shell searches for
+it in the list of shell builtins.  If a match is found, that
+builtin is invoked.
+
+@item
+If the name is neither a shell function nor a builtin,
+and contains no slashes, Bash searches each element of
+@env{$PATH} for a directory containing an executable file
+by that name.  Bash uses a hash table to remember the full
+pathnames of executable files to avoid multiple @env{PATH} searches
+(see the description of @code{hash} in @ref{Bourne Shell Builtins}).
+A full search of the directories in @env{$PATH}
+is performed only if the command is not found in the hash table.
+If the search is unsuccessful, the shell prints an error
+message and returns an exit status of 127.
+
+@item
+If the search is successful, or if the command name contains
+one or more slashes, the shell executes the named program in
+a separate execution environment.
+Argument 0 is set to the name given, and the remaining arguments
+to the command are set to the arguments supplied, if any.
+
+@item
+If this execution fails because the file is not in executable
+format, and the file is not a directory, it is assumed to be a
+@var{shell script} and the shell executes it as described in
+@ref{Shell Scripts}.
+
+@item
+If the command was not begun asynchronously, the shell waits for
+the command to complete and collects its exit status.
+
+@end enumerate
+
+@node Command Execution Environment
+@subsection Command Execution Environment
+@cindex execution environment
+
+The shell has an @var{execution environment}, which consists of the
+following:
+
+@itemize @bullet
+@item
+open files inherited by the shell at invocation, as modified by
+redirections supplied to the @code{exec} builtin
+
+@item
+the current working directory as set by @code{cd}, @code{pushd}, or
+@code{popd}, or inherited by the shell at invocation
+
+@item
+the file creation mode mask as set by @code{umask} or inherited from
+the shell's parent
+
+@item
+current traps set by @code{trap}
+
+@item
+shell parameters that are set by variable assignment or with @code{set}
+or inherited from the shell's parent in the environment
+
+@item
+shell functions defined during execution or inherited from the shell's
+parent in the environment
+
+@item
+options enabled at invocation (either by default or with command-line
+arguments) or by @code{set}
+
+@item
+options enabled by @code{shopt}
+
+@item
+shell aliases defined with @code{alias} (@pxref{Aliases})
+
+@item
+various process @sc{id}s, including those of background jobs
+(@pxref{Lists}), the value of @code{$$}, and the value of
+@env{$PPID}
+
+@end itemize
+
+When a simple command other than a builtin or shell function
+is to be executed, it
+is invoked in a separate execution environment that consists of
+the following.  Unless otherwise noted, the values are inherited
+from the shell.
+
+@itemize @bullet
+@item
+the shell's open files, plus any modifications and additions specified
+by redirections to the command
+
+@item
+the current working directory
+
+@item
+the file creation mode mask
+
+@item
+shell variables and functions marked for export, along with variables
+exported for the command, passed in the environment (@pxref{Environment})
+
+@item
+traps caught by the shell are reset to the values inherited from the
+shell's parent, and traps ignored by the shell are ignored
+
+@end itemize
+
+A command invoked in this separate environment cannot affect the
+shell's execution environment. 
+
+Command substitution, commands grouped with parentheses,
+and asynchronous commands are invoked in a
+subshell environment that is a duplicate of the shell environment,
+except that traps caught by the shell are reset to the values
+that the shell inherited from its parent at invocation.  Builtin
+commands that are invoked as part of a pipeline are also executed
+in a subshell environment.  Changes made to the subshell environment
+cannot affect the shell's execution environment.
+
+If a command is followed by a @samp{&} and job control is not active, the
+default standard input for the command is the empty file @file{/dev/null}.
+Otherwise, the invoked command inherits the file descriptors of the calling
+shell as modified by redirections.
+
+@node Environment
+@subsection Environment
+@cindex environment
+
+When a program is invoked it is given an array of strings
+called the @var{environment}.
+This is a list of name-value pairs, of the form @code{name=value}.
+
+Bash provides several ways to manipulate the environment.
+On invocation, the shell scans its own environment and
+creates a parameter for each name found, automatically marking
+it for @var{export}
+to child processes.  Executed commands inherit the environment.
+The @code{export} and @samp{declare -x}
+commands allow parameters and functions to be added to and
+deleted from the environment.  If the value of a parameter
+in the environment is modified, the new value becomes part
+of the environment, replacing the old.  The environment
+inherited by any executed command consists of the shell's
+initial environment, whose values may be modified in the shell,
+less any pairs removed by the @code{unset} and @samp{export -n}
+commands, plus any additions via the @code{export} and
+@samp{declare -x} commands.
+
+The environment for any simple command
+or function may be augmented temporarily by prefixing it with
+parameter assignments, as described in @ref{Shell Parameters}.
+These assignment statements affect only the environment seen
+by that command.
+
+If the @option{-k} option is set (@pxref{The Set Builtin}), then all
+parameter assignments are placed in the environment for a command,
+not just those that precede the command name.
+
+When Bash invokes an external command, the variable @samp{$_}
+is set to the full path name of the command and passed to that
+command in its environment.
+
+@node Exit Status
+@subsection Exit Status
+@cindex exit status
+
+For the shell's purposes, a command which exits with a
+zero exit status has succeeded.
+A non-zero exit status indicates failure.
+This seemingly counter-intuitive scheme is used so there
+is one well-defined way to indicate success and a variety of
+ways to indicate various failure modes.
+When a command terminates on a fatal signal whose number is @var{N},
+Bash uses the value 128+@var{N} as the exit status.
+
+If a command is not found, the child process created to
+execute it returns a status of 127.  If a command is found  
+but is not executable, the return status is 126.
+
+If a command fails because of an error during expansion or redirection,
+the exit status is greater than zero.
+
+The exit status is used by the Bash conditional commands
+(@pxref{Conditional Constructs}) and some of the list
+constructs (@pxref{Lists}).
+
+All of the Bash builtins return an exit status of zero if they succeed
+and a non-zero status on failure, so they may be used by the
+conditional and list constructs.
+All builtins return an exit status of 2 to indicate incorrect usage.
+
+@node Signals
+@subsection Signals
+@cindex signal handling
+
+When Bash is interactive, in the absence of any traps, it ignores
+@code{SIGTERM} (so that @samp{kill 0} does not kill an interactive shell),
+and @code{SIGINT}
+is caught and handled (so that the @code{wait} builtin is interruptible).
+When Bash receives a @code{SIGINT}, it breaks out of any executing loops.
+In all cases, Bash ignores @code{SIGQUIT}.
+If job control is in effect (@pxref{Job Control}), Bash
+ignores @code{SIGTTIN}, @code{SIGTTOU}, and @code{SIGTSTP}.
+
+Non-builtin commands started by Bash have signal handlers set to the
+values inherited by the shell from its parent.
+When job control is not in effect, asynchronous commands
+ignore @code{SIGINT} and @code{SIGQUIT} in addition to these inherited
+handlers.
+Commands run as a result of
+command substitution ignore the keyboard-generated job control signals
+@code{SIGTTIN}, @code{SIGTTOU}, and @code{SIGTSTP}.
+
+The shell exits by default upon receipt of a @code{SIGHUP}.
+Before exiting, an interactive shell resends the @code{SIGHUP} to
+all jobs, running or stopped.
+Stopped jobs are sent @code{SIGCONT} to ensure that they receive
+the @code{SIGHUP}.
+To prevent the shell from sending the @code{SIGHUP} signal to a
+particular job, it should be removed
+from the jobs table with the @code{disown}
+builtin (@pxref{Job Control Builtins}) or marked
+to not receive @code{SIGHUP} using @code{disown -h}.
+
+If the  @code{huponexit} shell option has been set with @code{shopt}
+(@pxref{Bash Builtins}), Bash sends a @code{SIGHUP} to all jobs when
+an interactive login shell exits.
+
+If Bash is waiting for a command to complete and receives a signal
+for which a trap has been set, the trap will not be executed until
+the command completes. 
+When Bash is waiting for an asynchronous
+command via the @code{wait} builtin, the reception of a signal for
+which a trap has been set will cause the @code{wait} builtin to return
+immediately with an exit status greater than 128, immediately after
+which the trap is executed.
+
+@node Shell Scripts
+@section Shell Scripts
+@cindex shell script
+
+A shell script is a text file containing shell commands.  When such
+a file is used as the first non-option argument when invoking Bash,
+and neither the @option{-c} nor @option{-s} option is supplied
+(@pxref{Invoking Bash}), 
+Bash reads and executes commands from the file, then exits.  This
+mode of operation creates a non-interactive shell.  The shell first
+searches for the file in the current directory, and looks in the
+directories in @env{$PATH} if not found there.
+
+When Bash runs
+a shell script, it sets the special parameter @code{0} to the name
+of the file, rather than the name of the shell, and the positional
+parameters are set to the remaining arguments, if any are given.
+If no additional arguments are supplied, the positional parameters
+are unset.
+
+A shell script may be made executable by using the @code{chmod} command
+to turn on the execute bit.  When Bash finds such a file while
+searching the @env{$PATH} for a command, it spawns a subshell to
+execute it.  In other words, executing
+@example
+filename @var{arguments}
+@end example
+@noindent
+is equivalent to executing
+@example
+bash filename @var{arguments}
+@end example
+
+@noindent
+if @code{filename} is an executable shell script.
+This subshell reinitializes itself, so that the effect is as if a
+new shell had been invoked to interpret the script, with the
+exception that the locations of commands remembered by the parent
+(see the description of @code{hash} in @ref{Bourne Shell Builtins})
+are retained by the child.
+
+Most versions of Unix make this a part of the operating system's command
+execution mechanism.  If the first line of a script begins with
+the two characters @samp{#!}, the remainder of the line specifies
+an interpreter for the program.
+Thus, you can specify Bash, @code{awk}, Perl, or some other
+interpreter and write the rest of the script file in that language.
+
+The arguments to the interpreter
+consist of a single optional argument following the interpreter
+name on the first line of the script file, followed by the name of
+the script file, followed by the rest of the arguments.  Bash
+will perform this action on operating systems that do not handle it
+themselves.  Note that some older versions of Unix limit the interpreter
+name and argument to a maximum of 32 characters.
+
+Bash scripts often begin with @code{#! /bin/bash} (assuming that
+Bash has been installed in @file{/bin}), since this ensures that
+Bash will be used to interpret the script, even if it is executed
+under another shell.
+
+@node Shell Builtin Commands
+@chapter Shell Builtin Commands
+
+@menu
+* Bourne Shell Builtins::      Builtin commands inherited from the Bourne
+                               Shell.
+* Bash Builtins::              Table of builtins specific to Bash.
+* The Set Builtin::            This builtin is so overloaded it
+                               deserves its own section.
+* Special Builtins::           Builtin commands classified specially by
+                               POSIX.2.
+@end menu
+
+Builtin commands are contained within the shell itself.
+When the name of a builtin command is used as the first word of
+a simple command (@pxref{Simple Commands}), the shell executes
+the command directly, without invoking another program.
+Builtin commands are necessary to implement functionality impossible
+or inconvenient to obtain with separate utilities.
+
+This section briefly the builtins which Bash inherits from
+the Bourne Shell, as well as the builtin commands which are unique
+to or have been extended in Bash.
+
+Several builtin commands are described in other chapters:  builtin
+commands which provide the Bash interface to the job control
+facilities (@pxref{Job Control Builtins}), the directory stack
+(@pxref{Directory Stack Builtins}), the command history
+(@pxref{Bash History Builtins}), and the programmable completion
+facilities (@pxref{Programmable Completion Builtins}).
+
+Many of the builtins have been extended by @sc{posix} or Bash.
+
+Unless otherwise noted, each builtin command documented as accepting
+options preceded by @samp{-} accepts @samp{--}
+to signify the end of the options.
+
+@node Bourne Shell Builtins
+@section Bourne Shell Builtins
+
+The following shell builtin commands are inherited from the Bourne Shell.
+These commands are implemented as specified by the @sc{posix} 1003.2 standard.
+
+@table @code
+@item :    @r{(a colon)}
+@btindex :
+@example
+: [@var{arguments}]
+@end example
+Do nothing beyond expanding @var{arguments} and performing redirections.
+The return status is zero.
+
+@item .    @r{(a period)}
+@btindex .
+@example
+. @var{filename} [@var{arguments}]
+@end example
+Read and execute commands from the @var{filename} argument in the
+current shell context.  If @var{filename} does not contain a slash,
+the @env{PATH} variable is used to find @var{filename}.
+When Bash is not in @sc{posix} mode, the current directory is searched
+if @var{filename} is not found in @env{$PATH}.
+If any @var{arguments} are supplied, they become the positional
+parameters when @var{filename} is executed.  Otherwise the positional
+parameters are unchanged.
+The return status is the exit status of the last command executed, or
+zero if no commands are executed.  If @var{filename} is not found, or
+cannot be read, the return status is non-zero.
+This builtin is equivalent to @code{source}.
+
+@item break
+@btindex break
+@example
+break [@var{n}]
+@end example
+Exit from a @code{for}, @code{while}, @code{until}, or @code{select} loop.
+If @var{n} is supplied, the @var{n}th enclosing loop is exited.
+@var{n} must be greater than or equal to 1.
+The return status is zero unless @var{n} is not greater than or equal to 1.
+
+@item cd
+@btindex cd
+@example
+cd [-L|-P] [@var{directory}]
+@end example
+Change the current working directory to @var{directory}.
+If @var{directory} is not given, the value of the @env{HOME} shell
+variable is used.
+If the shell variable @env{CDPATH} exists, it is used as a search path.
+If @var{directory} begins with a slash, @env{CDPATH} is not used.
+
+The @option{-P} option means to not follow symbolic links; symbolic
+links are followed by default or with the @option{-L} option.
+If @var{directory} is @samp{-}, it is equivalent to @env{$OLDPWD}.
+
+If a non-empty directory name from @env{CDPATH} is used, or if
+@samp{-} is the first argument, and the directory change is
+successful, the absolute pathname of the new working directory is
+written to the standard output.
+
+The return status is zero if the directory is successfully changed,
+non-zero otherwise.
+
+@item continue
+@btindex continue
+@example
+continue [@var{n}]
+@end example
+Resume the next iteration of an enclosing @code{for}, @code{while},
+@code{until}, or @code{select} loop.
+If @var{n} is supplied, the execution of the @var{n}th enclosing loop
+is resumed.
+@var{n} must be greater than or equal to 1.
+The return status is zero unless @var{n} is not greater than or equal to 1.
+
+@item eval
+@btindex eval
+@example
+eval [@var{arguments}]
+@end example
+The arguments are concatenated together into a single command, which is
+then read and executed, and its exit status returned as the exit status
+of @code{eval}.
+If there are no arguments or only empty arguments, the return status is
+zero.
+
+@item exec
+@btindex exec
+@example
+exec [-cl] [-a @var{name}] [@var{command} [@var{arguments}]]
+@end example
+If @var{command}
+is supplied, it replaces the shell without creating a new process.
+If the @option{-l} option is supplied, the shell places a dash at the
+beginning of the zeroth arg passed to @var{command}.
+This is what the @code{login} program does.
+The @option{-c} option causes @var{command} to be executed with an empty
+environment.
+If @option{-a} is supplied, the shell passes @var{name} as the zeroth
+argument to @var{command}.
+If no @var{command} is specified, redirections may be used to affect
+the current shell environment.  If there are no redirection errors, the
+return status is zero; otherwise the return status is non-zero.
+
+@item exit
+@btindex exit
+@example
+exit [@var{n}]
+@end example
+Exit the shell, returning a status of @var{n} to the shell's parent.
+If @var{n} is omitted, the exit status is that of the last command executed.
+Any trap on @code{EXIT} is executed before the shell terminates.
+
+@item export
+@btindex export
+@example
+export [-fn] [-p] [@var{name}[=@var{value}]]
+@end example
+Mark each @var{name} to be passed to child processes
+in the environment.  If the @option{-f} option is supplied, the @var{name}s
+refer to shell functions; otherwise the names refer to shell variables.
+The @option{-n} option means to no longer mark each @var{name} for export.
+If no @var{names} are supplied, or if the @option{-p} option is given, a
+list of exported names is displayed.
+The @option{-p} option displays output in a form that may be reused as input.
+If a variable name is followed by =@var{value}, the value of
+the variable is set to @var{value}.
+
+The return status is zero unless an invalid option is supplied, one of
+the names is not a valid shell variable name, or @option{-f} is supplied
+with a name that is not a shell function.
+
+@item getopts
+@btindex getopts
+@example
+getopts @var{optstring} @var{name} [@var{args}]
+@end example
+@code{getopts} is used by shell scripts to parse positional parameters.
+@var{optstring} contains the option characters to be recognized; if a
+character is followed by a colon, the option is expected to have an
+argument, which should be separated from it by white space.
+The colon (@samp{:}) and question mark (@samp{?}) may not be
+used as option characters.
+Each time it is invoked, @code{getopts}
+places the next option in the shell variable @var{name}, initializing
+@var{name} if it does not exist,
+and the index of the next argument to be processed into the
+variable @env{OPTIND}.
+@env{OPTIND} is initialized to 1 each time the shell or a shell script
+is invoked.
+When an option requires an argument,
+@code{getopts} places that argument into the variable @env{OPTARG}.
+The shell does not reset @env{OPTIND} automatically; it must be manually
+reset between multiple calls to @code{getopts} within the same shell
+invocation if a new set of parameters is to be used.
+
+When the end of options is encountered, @code{getopts} exits with a
+return value greater than zero.
+@env{OPTIND} is set to the index of the first non-option argument,
+and @code{name} is set to @samp{?}.
+
+@code{getopts}
+normally parses the positional parameters, but if more arguments are
+given in @var{args}, @code{getopts} parses those instead.
+
+@code{getopts} can report errors in two ways.  If the first character of
+@var{optstring} is a colon, @var{silent}
+error reporting is used.  In normal operation diagnostic messages
+are printed when invalid options or missing option arguments are
+encountered.
+If the variable @env{OPTERR}
+is set to 0, no error messages will be displayed, even if the first
+character of @code{optstring} is not a colon.
+
+If an invalid option is seen,
+@code{getopts} places @samp{?} into @var{name} and, if not silent,
+prints an error message and unsets @env{OPTARG}.
+If @code{getopts} is silent, the option character found is placed in
+@env{OPTARG} and no diagnostic message is printed.
+
+If a required argument is not found, and @code{getopts}
+is not silent, a question mark (@samp{?}) is placed in @var{name},
+@code{OPTARG} is unset, and a diagnostic message is printed.
+If @code{getopts} is silent, then a colon (@samp{:}) is placed in
+@var{name} and @env{OPTARG} is set to the option character found.
+
+@item hash
+@btindex hash
+@example
+hash [-'r] [-p @var{filename}] [-dt] [@var{name}]
+@end example
+Remember the full pathnames of commands specified as @var{name} arguments,
+so they need not be searched for on subsequent invocations.
+The commands are found by searching through the directories listed in
+@env{$PATH}.
+The @option{-p} option inhibits the path search, and @var{filename} is
+used as the location of @var{name}.
+The @option{-r} option causes the shell to forget all remembered locations.
+The @option{-d} option causes the shell to forget the remembered location
+of each @var{name}.
+If the @option{-t} option is supplied, the full pathname to which each
+@var{name} corresponds is printed.  If multiple @var{name} arguments are
+supplied with @option{-t} the @var{name} is printed before the hashed
+full pathname.
+The @option{-l} option causes output to be displayed in a format
+that may be reused as input.
+If no arguments are given, or if only @option{-l} is supplied,
+information about remembered commands is printed.
+The return status is zero unless a @var{name} is not found or an invalid
+option is supplied.
+
+@item pwd
+@btindex pwd
+@example
+pwd [-LP]
+@end example
+Print the absolute pathname of the current working directory.
+If the @option{-P} option is supplied, the pathname printed will not
+contain symbolic links.
+If the @option{-L} option is supplied, the pathname printed may contain
+symbolic links.
+The return status is zero unless an error is encountered while
+determining the name of the current directory or an invalid option
+is supplied.
+
+@item readonly
+@btindex readonly
+@example
+readonly [-apf] [@var{name}[=@var{value}]] @dots{}
+@end example
+Mark each @var{name} as readonly.
+The values of these names may not be changed by subsequent assignment.
+If the @option{-f} option is supplied, each @var{name} refers to a shell
+function.
+The @option{-a} option means each @var{name} refers to an array variable.
+If no @var{name} arguments are given, or if the @option{-p}
+option is supplied, a list of all readonly names is printed.
+The @option{-p} option causes output to be displayed in a format that
+may be reused as input.
+If a variable name is followed by =@var{value}, the value of
+the variable is set to @var{value}.
+The return status is zero unless an invalid option is supplied, one of
+the @var{name} arguments is not a valid shell variable or function name,
+or the @option{-f} option is supplied with a name that is not a shell function.
+
+@item return
+@btindex return
+@example
+return [@var{n}]
+@end example
+Cause a shell function to exit with the return value @var{n}.
+If @var{n} is not supplied, the return value is the exit status of the
+last command executed in the function.
+This may also be used to terminate execution of a script being executed
+with the @code{.} (or @code{source}) builtin, returning either @var{n} or
+the exit status of the last command executed within the script as the exit
+status of the script.
+Any command associated with the @code{RETURN} trap is executed
+before execution resumes after the function or script.
+The return status is non-zero if @code{return} is used outside a function
+and not during the execution of a script by @code{.} or @code{source}.
+
+@item shift
+@btindex shift
+@example
+shift [@var{n}]
+@end example
+Shift the positional parameters to the left by @var{n}.
+The positional parameters from @var{n}+1 @dots{} @code{$#} are
+renamed to @code{$1} @dots{} @code{$#}-@var{n}+1.
+Parameters represented by the numbers @code{$#} to @var{n}+1 are unset.
+@var{n} must be a non-negative number less than or equal to @code{$#}.
+If @var{n} is zero or greater than @code{$#}, the positional parameters
+are not changed.
+If @var{n} is not supplied, it is assumed to be 1.
+The return status is zero unless @var{n} is greater than @code{$#} or
+less than zero, non-zero otherwise.
+
+@item test
+@itemx [
+@btindex test
+@btindex [
+Evaluate a conditional expression @var{expr}.
+Each operator and operand must be a separate argument.
+Expressions are composed of the primaries described below in
+@ref{Bash Conditional Expressions}.
+
+When the @code{[} form is used, the last argument to the command must
+be a @code{]}.
+
+Expressions may be combined using the following operators, listed in
+decreasing order of precedence.
+
+@table @code
+@item ! @var{expr}
+True if @var{expr} is false.
+
+@item ( @var{expr} )
+Returns the value of @var{expr}.
+This may be used to override the normal precedence of operators.
+
+@item @var{expr1} -a @var{expr2}
+True if both @var{expr1} and @var{expr2} are true.
+
+@item @var{expr1} -o @var{expr2}
+True if either @var{expr1} or @var{expr2} is true.
+@end table
+
+The @code{test} and @code{[} builtins evaluate conditional
+expressions using a set of rules based on the number of arguments.
+
+@table @asis
+@item 0 arguments
+The expression is false.
+
+@item 1 argument
+The expression is true if and only if the argument is not null.
+
+@item 2 arguments
+If the first argument is @samp{!}, the expression is true if and
+only if the second argument is null.
+If the first argument is one of the unary conditional operators
+(@pxref{Bash Conditional Expressions}), the expression
+is true if the unary test is true.
+If the first argument is not a valid unary operator, the expression is
+false.
+
+@item 3 arguments
+If the second argument is one of the binary conditional
+operators (@pxref{Bash Conditional Expressions}), the
+result of the expression is the result of the binary test using the
+first and third arguments as operands.
+If the first argument is @samp{!}, the value is the negation of
+the two-argument test using the second and third arguments.
+If the first argument is exactly @samp{(} and the third argument is
+exactly @samp{)}, the result is the one-argument test of the second
+argument.
+Otherwise, the expression is false.
+The @samp{-a} and @samp{-o} operators are considered binary operators
+in this case.  
+
+@item 4 arguments
+If the first argument is @samp{!}, the result is the negation of
+the three-argument expression composed of the remaining arguments.
+Otherwise, the expression is parsed and evaluated according to 
+precedence using the rules listed above.
+
+@item 5 or more arguments
+The expression is parsed and evaluated according to precedence
+using the rules listed above.
+@end table
+
+@item times
+@btindex times
+@example
+times
+@end example
+Print out the user and system times used by the shell and its children.
+The return status is zero.
+
+@item trap
+@btindex trap
+@example
+trap [-lp] [@var{arg}] [@var{sigspec} @dots{}]
+@end example
+The commands in @var{arg} are to be read and executed when the
+shell receives signal @var{sigspec}.  If @var{arg} is absent (and
+there is a single @var{sigspec}) or
+equal to @samp{-}, each specified signal's disposition is reset
+to the value it had when the shell was started.
+If @var{arg} is the null string, then the signal specified by
+each @var{sigspec} is ignored by the shell and commands it invokes.
+If @var{arg} is not present and @option{-p} has been supplied,
+the shell displays the trap commands associated with each @var{sigspec}.
+If no arguments are supplied, or
+only @option{-p} is given, @code{trap} prints the list of commands
+associated with each signal number in a form that may be reused as
+shell input.
+The @option{-l} option causes the shell to print a list of signal names
+and their corresponding numbers.
+Each @var{sigspec} is either a signal name or a signal number.
+Signal names are case insensitive and the @code{SIG} prefix is optional.
+If a @var{sigspec}
+is @code{0} or @code{EXIT}, @var{arg} is executed when the shell exits.
+If a @var{sigspec} is @code{DEBUG}, the command @var{arg} is executed
+before every simple command, @code{for} command, @code{case} command,
+@code{select} command, every arithmetic @code{for} command, and before
+the first command executes in a shell function.
+Refer to the description of the @code{extglob} option to the
+@code{shopt} builtin (@pxref{Bash Builtins}) for details of its
+effect on the @code{DEBUG} trap.
+If a @var{sigspec} is @code{ERR}, the command @var{arg} 
+is executed whenever a simple command has a non-zero exit status,
+subject to the following conditions.
+The @code{ERR} trap is not executed if the failed command is part of the
+command list immediately following an @code{until} or @code{while} keyword,
+part of the test in an @code{if} statement,
+part of a @code{&&} or @code{||} list, or if the command's return
+status is being inverted using @code{!}.
+These are the same conditions obeyed by the @code{errexit} option.
+If a @var{sigspec} is @code{RETURN}, the command @var{arg} is executed
+each time a shell function or a script executed with the @code{.} or
+@code{source} builtins finishes executing.
+
+Signals ignored upon entry to the shell cannot be trapped or reset.
+Trapped signals are reset to their original values in a child  
+process when it is created.
+
+The return status is zero unless a @var{sigspec} does not specify a
+valid signal.
+
+@item umask
+@btindex umask
+@example
+umask [-p] [-S] [@var{mode}]
+@end example
+Set the shell process's file creation mask to @var{mode}.  If
+@var{mode} begins with a digit, it is interpreted as an octal number;
+if not, it is interpreted as a symbolic mode mask similar
+to that accepted by the @code{chmod} command.  If @var{mode} is
+omitted, the current value of the mask is printed.  If the @option{-S}
+option is supplied without a @var{mode} argument, the mask is printed
+in a symbolic format.
+If the  @option{-p} option is supplied, and @var{mode}
+is omitted, the output is in a form that may be reused as input.
+The return status is zero if the mode is successfully changed or if
+no @var{mode} argument is supplied, and non-zero otherwise.
+
+Note that when the mode is interpreted as an octal number, each number
+of the umask is subtracted from @code{7}.  Thus, a umask of @code{022}
+results in permissions of @code{755}.
+
+@item unset
+@btindex unset
+@example
+unset [-fv] [@var{name}]
+@end example
+Each variable or function @var{name} is removed.
+If no options are supplied, or the @option{-v} option is given, each
+@var{name} refers to a shell variable. 
+If the @option{-f} option is given, the @var{name}s refer to shell
+functions, and the function definition is removed.
+Readonly variables and functions may not be unset.
+The return status is zero unless a @var{name} is readonly.
+@end table
+
+@node Bash Builtins
+@section Bash Builtin Commands
+
+This section describes builtin commands which are unique to
+or have been extended in Bash.
+Some of these commands are specified in the @sc{posix} 1003.2 standard.
+
+@table @code
+
+@item alias
+@btindex alias
+@example
+alias [@code{-p}] [@var{name}[=@var{value}] @dots{}]
+@end example
+
+Without arguments or with the @option{-p} option, @code{alias} prints
+the list of aliases on the standard output in a form that allows
+them to be reused as input.
+If arguments are supplied, an alias is defined for each @var{name}
+whose @var{value} is given.  If no @var{value} is given, the name
+and value of the alias is printed.
+Aliases are described in @ref{Aliases}.
+
+@item bind
+@btindex bind
+@example
+bind [-m @var{keymap}] [-lpsvPSV]
+bind [-m @var{keymap}] [-q @var{function}] [-u @var{function}] [-r @var{keyseq}]
+bind [-m @var{keymap}] -f @var{filename}
+bind [-m @var{keymap}] -x @var{keyseq:shell-command}
+bind [-m @var{keymap}] @var{keyseq:function-name}
+bind @var{readline-command}
+@end example
+
+Display current Readline (@pxref{Command Line Editing})
+key and function bindings,
+bind a key sequence to a Readline function or macro,
+or set a Readline variable.
+Each non-option argument is a command as it would appear in a
+a Readline initialization file (@pxref{Readline Init File}),
+but each binding or command must be passed as a separate argument;  e.g.,
+@samp{"\C-x\C-r":re-read-init-file}.
+Options, if supplied, have the following meanings:
+
+@table @code
+@item -m @var{keymap}
+Use @var{keymap} as the keymap to be affected by
+the subsequent bindings.  Acceptable @var{keymap}
+names are
+@code{emacs},
+@code{emacs-standard},
+@code{emacs-meta},
+@code{emacs-ctlx},
+@code{vi},
+@code{vi-move},
+@code{vi-command}, and
+@code{vi-insert}.
+@code{vi} is equivalent to @code{vi-command};
+@code{emacs} is equivalent to @code{emacs-standard}.
+
+@item -l
+List the names of all Readline functions.
+
+@item -p
+Display Readline function names and bindings in such a way that they
+can be used as input or in a Readline initialization file.
+
+@item -P
+List current Readline function names and bindings.
+
+@item -v
+Display Readline variable names and values in such a way that they
+can be used as input or in a Readline initialization file.
+
+@item -V
+List current Readline variable names and values.
+
+@item -s
+Display Readline key sequences bound to macros and the strings they output
+in such a way that they can be used as input or in a Readline
+initialization file.
+
+@item -S
+Display Readline key sequences bound to macros and the strings they output.
+
+@item -f @var{filename}
+Read key bindings from @var{filename}.
+
+@item -q @var{function}
+Query about which keys invoke the named @var{function}.
+
+@item -u @var{function}
+Unbind all keys bound to the named @var{function}.
+
+@item -r @var{keyseq}
+Remove any current binding for @var{keyseq}.
+
+@item -x @var{keyseq:shell-command}
+Cause @var{shell-command} to be executed whenever @var{keyseq} is
+entered.
+
+@end table
+
+@noindent
+The return status is zero unless an invalid option is supplied or an
+error occurs.
+
+@item builtin
+@btindex builtin
+@example
+builtin [@var{shell-builtin} [@var{args}]]
+@end example
+Run a shell builtin, passing it @var{args}, and return its exit status.
+This is useful when defining a shell function with the same
+name as a shell builtin, retaining the functionality of the builtin within
+the function.
+The return status is non-zero if @var{shell-builtin} is not a shell
+builtin command.
+
+@item caller
+@btindex caller
+@example
+caller [@var{expr}]
+@end example
+Returns the context of any active subroutine call (a shell function or
+a script executed with the @code{.} or @code{source} builtins).
+
+Without @var{expr}, @code{caller} displays the line number and source
+filename of the current subroutine call.
+If a non-negative integer is supplied as @var{expr}, @code{caller} 
+displays the line number, subroutine name, and source file corresponding
+to that position in the current execution call stack.  This extra
+information may be used, for example, to print a stack trace.  The
+current frame is frame 0.
+
+The return value is 0 unless the shell is not executing a subroutine
+call or @var{expr} does not correspond to a valid position in the
+call stack.
+
+@item command
+@btindex command
+@example
+command [-pVv] @var{command} [@var{arguments} @dots{}]
+@end example
+Runs @var{command} with @var{arguments} ignoring any shell function
+named @var{command}.
+Only shell builtin commands or commands found by searching the
+@env{PATH} are executed.
+If there is a shell function named @code{ls}, running @samp{command ls}
+within the function will execute the external command @code{ls}
+instead of calling the function recursively.
+The @option{-p} option means to use a default value for @env{PATH}
+that is guaranteed to find all of the standard utilities.
+The return status in this case is 127 if @var{command} cannot be
+found or an error occurred, and the exit status of @var{command}
+otherwise.
+
+If either the @option{-V} or @option{-v} option is supplied, a
+description of @var{command} is printed.  The @option{-v} option
+causes a single word indicating the command or file name used to
+invoke @var{command} to be displayed; the @option{-V} option produces
+a more verbose description.  In this case, the return status is
+zero if @var{command} is found, and non-zero if not.
+
+@item declare
+@btindex declare
+@example
+declare [-afFirtx] [-p] [@var{name}[=@var{value}] @dots{}]
+@end example
+
+Declare variables and give them attributes.  If no @var{name}s
+are given, then display the values of variables instead. 
+
+The @option{-p} option will display the attributes and values of each
+@var{name}.
+When @option{-p} is used, additional options are ignored.
+The @option{-F} option inhibits the display of function definitions;
+only the function name and attributes are printed.
+If the @code{extdebug} shell option is enabled using @code{shopt}
+(@pxref{Bash Builtins}), the source file name and line number where
+the function is defined are displayed as well.
+@option{-F} implies @option{-f}.
+The following options can be used to restrict output to variables with
+the specified attributes or to give variables attributes:
+
+@table @code
+@item -a
+Each @var{name} is an array variable (@pxref{Arrays}).
+
+@item -f
+Use function names only.
+
+@item -i
+The variable is to be treated as
+an integer; arithmetic evaluation (@pxref{Shell Arithmetic}) is
+performed when the variable is assigned a value.
+
+@item -r
+Make @var{name}s readonly.  These names cannot then be assigned values
+by subsequent assignment statements or unset.
+
+@item -t
+Give each @var{name} the @code{trace} attribute.
+Traced functions inherit the @code{DEBUG} trap from the calling shell.
+The trace attribute has no special meaning for variables.
+
+@item -x
+Mark each @var{name} for export to subsequent commands via
+the environment.
+@end table
+
+Using @samp{+} instead of @samp{-} turns off the attribute instead.
+When used in a function, @code{declare} makes each @var{name} local,
+as with the @code{local} command.  If a variable name is followed by
+=@var{value}, the value of the variable is set to @var{value}.
+
+The return status is zero unless an invalid option is encountered,
+an attempt is made to define a function using @samp{-f foo=bar},
+an attempt is made to assign a value to a readonly variable,
+an attempt is made to assign a value to an array variable without
+using the compound assignment syntax (@pxref{Arrays}),
+one of the @var{names} is not a valid shell variable name,
+an attempt is made to turn off readonly status for a readonly variable,
+an attempt is made to turn off array status for an array variable,
+or an attempt is made to display a non-existent function with @option{-f}.
+
+@item echo
+@btindex echo
+@example
+echo [-neE] [@var{arg} @dots{}]
+@end example
+Output the @var{arg}s, separated by spaces, terminated with a
+newline.
+The return status is always 0.
+If @option{-n} is specified, the trailing newline is suppressed.
+If the @option{-e} option is given, interpretation of the following
+backslash-escaped characters is enabled.
+The @option{-E} option disables the interpretation of these escape characters,
+even on systems where they are interpreted by default.
+The @code{xpg_echo} shell option may be used to
+dynamically determine whether or not @code{echo} expands these
+escape characters by default.
+@code{echo} interprets the following escape sequences:
+@table @code
+@item \a
+alert (bell)
+@item \b
+backspace
+@item \c
+suppress trailing newline
+@item \e
+escape
+@item \f
+form feed
+@item \n
+new line
+@item \r
+carriage return
+@item \t 
+horizontal tab
+@item \v
+vertical tab
+@item \\
+backslash
+@item \0@var{nnn}
+the eight-bit character whose value is the octal value @var{nnn}
+(zero to three octal digits)
+@item \@var{nnn}
+the eight-bit character whose value is the octal value @var{nnn}
+(one to three octal digits)
+@item \x@var{HH}
+the eight-bit character whose value is the hexadecimal value @var{HH}
+(one or two hex digits)
+@end table
+
+@item enable
+@btindex enable
+@example
+enable [-n] [-p] [-f @var{filename}] [-ads] [@var{name} @dots{}]
+@end example
+Enable and disable builtin shell commands.
+Disabling a builtin allows a disk command which has the same name
+as a shell builtin to be executed without specifying a full pathname,
+even though the shell normally searches for builtins before disk commands.
+If @option{-n} is used, the @var{name}s become disabled.  Otherwise
+@var{name}s are enabled.  For example, to use the @code{test} binary
+found via @env{$PATH} instead of the shell builtin version, type
+@samp{enable -n test}.
+
+If the @option{-p} option is supplied, or no @var{name} arguments appear,
+a list of shell builtins is printed.  With no other arguments, the list
+consists of all enabled shell builtins.
+The @option{-a} option means to list
+each builtin with an indication of whether or not it is enabled. 
+
+The @option{-f} option means to load the new builtin command @var{name}
+from shared object @var{filename}, on systems that support dynamic loading.
+The @option{-d} option will delete a builtin loaded with @option{-f}.
+
+If there are no options, a list of the shell builtins is displayed.
+The @option{-s} option restricts @code{enable} to the @sc{posix} special
+builtins.  If @option{-s} is used with @option{-f}, the new builtin becomes
+a special builtin (@pxref{Special Builtins}).
+
+The return status is zero unless a @var{name} is not a shell builtin
+or there is an error loading a new builtin from a shared object.
+
+@item help
+@btindex help
+@example
+help [-s] [@var{pattern}]
+@end example
+Display helpful information about builtin commands.
+If @var{pattern} is specified, @code{help} gives detailed help
+on all commands matching @var{pattern}, otherwise a list of
+the builtins is printed.
+The @option{-s} option restricts the information displayed to a short
+usage synopsis.
+The return status is zero unless no command matches @var{pattern}.
+
+@item let
+@btindex let
+@example
+let @var{expression} [@var{expression}]
+@end example
+The @code{let} builtin allows arithmetic to be performed on shell
+variables.  Each @var{expression} is evaluated according to the
+rules given below in @ref{Shell Arithmetic}.  If the
+last @var{expression} evaluates to 0, @code{let} returns 1;
+otherwise 0 is returned.
+
+@item local
+@btindex local
+@example
+local [@var{option}] @var{name}[=@var{value}] @dots{}
+@end example
+For each argument, a local variable named @var{name} is created,
+and assigned @var{value}.
+The @var{option} can be any of the options accepted by @code{declare}.
+@code{local} can only be used within a function; it makes the variable
+@var{name} have a visible scope restricted to that function and its
+children.  The return status is zero unless @code{local} is used outside
+a function, an invalid @var{name} is supplied, or @var{name} is a
+readonly variable.
+
+@item logout
+@btindex logout
+@example
+logout [@var{n}]
+@end example
+Exit a login shell, returning a status of @var{n} to the shell's
+parent.
+
+@item printf
+@btindex printf
+@example
+@code{printf} @var{format} [@var{arguments}]
+@end example
+Write the formatted @var{arguments} to the standard output under the
+control of the @var{format}.
+The @var{format} is a character string which contains three types of objects:
+plain characters, which are simply copied to standard output, character
+escape sequences, which are converted and copied to the standard output, and
+format specifications, each of which causes printing of the next successive
+@var{argument}.
+In addition to the standard @code{printf(1)} formats, @samp{%b} causes
+@code{printf} to expand backslash escape sequences in the corresponding
+@var{argument},
+(except that @samp{\c} terminates output, backslashes in
+@samp{\'}, @samp{\"}, and @samp{\?} are not removed, and octal escapes
+beginning with @samp{\0} may contain up to four digits),
+and @samp{%q} causes @code{printf} to output the
+corresponding @var{argument} in a format that can be reused as shell input.
+
+The @var{format} is reused as necessary to consume all of the @var{arguments}.
+If the @var{format} requires more @var{arguments} than are supplied, the
+extra format specifications behave as if a zero value or null string, as
+appropriate, had been supplied.  The return value is zero on success,
+non-zero on failure.
+
+@item read
+@btindex read
+@example
+read [-ers] [-a @var{aname}] [-d @var{delim}] [-n @var{nchars}] [-p @var{prompt}] [-t @var{timeout}] [-u @var{fd}] [@var{name} @dots{}]
+@end example
+One line is read from the standard input, or from the file descriptor
+@var{fd} supplied as an argument to the @option{-u} option, and the first word
+is assigned to the first @var{name}, the second word to the second @var{name},
+and so on, with leftover words and their intervening separators assigned
+to the last @var{name}.
+If there are fewer words read from the input stream than names,
+the remaining names are assigned empty values.
+The characters in the value of the @env{IFS} variable
+are used to split the line into words.
+The backslash character @samp{\} may be used to remove any special
+meaning for the next character read and for line continuation.
+If no names are supplied, the line read is assigned to the
+variable @env{REPLY}.
+The return code is zero, unless end-of-file is encountered, @code{read}
+times out, or an invalid file descriptor is supplied as the argument to
+@option{-u}.
+Options, if supplied, have the following meanings:
+
+@table @code
+@item -a @var{aname}
+The words are assigned to sequential indices of the array variable
+@var{aname}, starting at 0.
+All elements are removed from @var{aname} before the assignment.
+Other @var{name} arguments are ignored.
+
+@item -d @var{delim}
+The first character of @var{delim} is used to terminate the input line,
+rather than newline.
+
+@item -e
+Readline (@pxref{Command Line Editing}) is used to obtain the line.
+
+@item -n @var{nchars}
+@code{read} returns after reading @var{nchars} characters rather than
+waiting for a complete line of input.
+
+@item -p @var{prompt}
+Display @var{prompt}, without a trailing newline, before attempting
+to read any input.
+The prompt is displayed only if input is coming from a terminal.
+
+@item -r
+If this option is given, backslash does not act as an escape character.
+The backslash is considered to be part of the line.
+In particular, a backslash-newline pair may not be used as a line
+continuation.
+
+@item -s
+Silent mode.  If input is coming from a terminal, characters are
+not echoed.
+
+@item -t @var{timeout}
+Cause @code{read} to time out and return failure if a complete line of
+input is not read within @var{timeout} seconds.
+This option has no effect if @code{read} is not reading input from the
+terminal or a pipe.
+
+@item -u @var{fd}
+Read input from file descriptor @var{fd}.
+
+@end table
+
+@item shopt
+@btindex shopt
+@example
+shopt [-pqsu] [-o] [@var{optname} @dots{}]
+@end example
+Toggle the values of variables controlling optional shell behavior.
+With no options, or with the @option{-p} option, a list of all settable
+options is displayed, with an indication of whether or not each is set.
+The @option{-p} option causes output to be displayed in a form that
+may be reused as input.
+Other options have the following meanings:
+
+@table @code
+@item -s
+Enable (set) each @var{optname}.
+
+@item -u
+Disable (unset) each @var{optname}.
+
+@item -q
+Suppresses normal output; the return status
+indicates whether the @var{optname} is set or unset.
+If multiple @var{optname} arguments are given with @option{-q},
+the return status is zero if all @var{optnames} are enabled;
+non-zero otherwise.
+
+@item -o
+Restricts the values of
+@var{optname} to be those defined for the @option{-o} option to the
+@code{set} builtin (@pxref{The Set Builtin}).
+@end table
+
+If either @option{-s} or @option{-u}
+is used with no @var{optname} arguments, the display is limited to
+those options which are set or unset, respectively.
+
+Unless otherwise noted, the @code{shopt} options are disabled (off)
+by default.
+
+The return status when listing options is zero if all @var{optnames}
+are enabled, non-zero otherwise.  When setting or unsetting options,
+the return status is zero unless an @var{optname} is not a valid shell
+option.
+
+The list of @code{shopt} options is:
+@table @code
+@item cdable_vars
+If this is set, an argument to the @code{cd}
+builtin command that
+is not a directory is assumed to be the name of a variable whose
+value is the directory to change to.
+
+@item cdspell
+If set, minor errors in the spelling of a directory component in a
+@code{cd} command will be corrected.
+The errors checked for are transposed characters,
+a missing character, and a character too many.
+If a correction is found, the corrected path is printed,
+and the command proceeds.
+This option is only used by interactive shells.
+
+@item checkhash
+If this is set, Bash checks that a command found in the hash
+table exists before trying to execute it.  If a hashed command no
+longer exists, a normal path search is performed.
+
+@item checkwinsize
+If set, Bash checks the window size after each command
+and, if necessary, updates the values of    
+@env{LINES} and @env{COLUMNS}.
+
+@item cmdhist
+If set, Bash
+attempts to save all lines of a multiple-line
+command in the same history entry.  This allows
+easy re-editing of multi-line commands.
+
+@item dotglob
+If set, Bash includes filenames beginning with a `.' in
+the results of filename expansion.
+
+@item execfail
+If this is set, a non-interactive shell will not exit if
+it cannot execute the file specified as an argument to the @code{exec}
+builtin command.  An interactive shell does not exit if @code{exec}
+fails.
+
+@item expand_aliases
+If set, aliases are expanded as described below under Aliases,
+@ref{Aliases}.
+This option is enabled by default for interactive shells.
+
+@item extdebug
+If set, behavior intended for use by debuggers is enabled:
+
+@enumerate
+@item
+The @option{-F} option to the @code{declare} builtin (@pxref{Bash Builtins})
+displays the source file name and line number corresponding to each function
+name supplied as an argument.
+
+@item
+If the command run by the @code{DEBUG} trap returns a non-zero value, the
+next command is skipped and not executed.
+
+@item
+If the command run by the @code{DEBUG} trap returns a value of 2, and the
+shell is executing in a subroutine (a shell function or a shell script
+executed by the @code{.} or @code{source} builtins), a call to
+@code{return} is simulated.
+@end enumerate
+
+@item extglob
+If set, the extended pattern matching features described above
+(@pxref{Pattern Matching}) are enabled.
+
+@item extquote
+If set, @code{$'@var{string}'} and @code{$"@var{string}"} quoting is  
+performed within @code{$@{@var{parameter}@}} expansions                     
+enclosed in double quotes.  This option is enabled by default. 
+
+@item failglob
+If set, patterns which fail to match filenames during pathname expansion
+result in an expansion error.
+
+@item force_fignore
+If set, the suffixes specified by the @env{FIGNORE} shell variable
+cause words to be ignored when performing word completion even if
+the ignored words are the only possible completions.
+@xref{Bash Variables}, for a description of @env{FIGNORE}.
+This option is enabled by default.
+
+@item gnu_errfmt
+If set, shell error messages are written in the standard @sc{gnu} error
+message format.
+
+@item histappend
+If set, the history list is appended to the file named by the value
+of the @env{HISTFILE}
+variable when the shell exits, rather than overwriting the file.
+
+@item histreedit
+If set, and Readline
+is being used, a user is given the opportunity to re-edit a
+failed history substitution.
+
+@item histverify
+If set, and Readline
+is being used, the results of history substitution are not immediately
+passed to the shell parser.  Instead, the resulting line is loaded into
+the Readline editing buffer, allowing further modification.
+
+@item hostcomplete
+If set, and Readline is being used, Bash will attempt to perform
+hostname completion when a word containing a @samp{@@} is being
+completed (@pxref{Commands For Completion}).  This option is enabled
+by default.
+
+@item huponexit
+If set, Bash will send @code{SIGHUP} to all jobs when an interactive
+login shell exits (@pxref{Signals}).
+
+@item interactive_comments
+Allow a word beginning with @samp{#}
+to cause that word and all remaining characters on that
+line to be ignored in an interactive shell.
+This option is enabled by default.
+
+@item lithist
+If enabled, and the @code{cmdhist}
+option is enabled, multi-line commands are saved to the history with
+embedded newlines rather than using semicolon separators where possible.
+
+@item login_shell
+The shell sets this option if it is started as a login shell
+(@pxref{Invoking Bash}).
+The value may not be changed.
+
+@item mailwarn
+If set, and a file that Bash is checking for mail has been  
+accessed since the last time it was checked, the message
+@code{"The mail in @var{mailfile} has been read"} is displayed.
+
+@item no_empty_cmd_completion
+If set, and Readline is being used, Bash will not attempt to search
+the @env{PATH} for possible completions when completion is attempted
+on an empty line.
+
+@item nocaseglob
+If set, Bash matches filenames in a case-insensitive fashion when
+performing filename expansion.
+
+@item nullglob
+If set, Bash allows filename patterns which match no
+files to expand to a null string, rather than themselves.
+
+@item progcomp
+If set, the programmable completion facilities
+(@pxref{Programmable Completion}) are enabled.
+This option is enabled by default.
+
+@item promptvars
+If set, prompt strings undergo
+parameter expansion, command substitution, arithmetic
+expansion, and quote removal after being expanded
+as described below (@pxref{Printing a Prompt}).
+This option is enabled by default.
+
+@item restricted_shell
+The shell sets this option if it is started in restricted mode
+(@pxref{The Restricted Shell}).
+The value may not be changed.
+This is not reset when the startup files are executed, allowing
+the startup files to discover whether or not a shell is restricted.
+
+@item shift_verbose
+If this is set, the @code{shift}
+builtin prints an error message when the shift count exceeds the
+number of positional parameters.
+
+@item sourcepath
+If set, the @code{source} builtin uses the value of @env{PATH}
+to find the directory containing the file supplied as an argument.
+This option is enabled by default.
+
+@item xpg_echo
+If set, the @code{echo} builtin expands backslash-escape sequences
+by default.
+
+@end table
+
+@noindent
+The return status when listing options is zero if all @var{optnames}
+are enabled, non-zero otherwise.
+When setting or unsetting options, the return status is zero unless an
+@var{optname} is not a valid shell option.
+
+@item source
+@btindex source
+@example
+source @var{filename}
+@end example
+A synonym for @code{.} (@pxref{Bourne Shell Builtins}).
+
+@item type
+@btindex type
+@example
+type [-afptP] [@var{name} @dots{}]
+@end example
+For each @var{name}, indicate how it would be interpreted if used as a
+command name.
+
+If the @option{-t} option is used, @code{type} prints a single word
+which is one of @samp{alias}, @samp{function}, @samp{builtin},
+@samp{file} or @samp{keyword},
+if @var{name} is an alias, shell function, shell builtin,
+disk file, or shell reserved word, respectively.
+If the @var{name} is not found, then nothing is printed, and
+@code{type} returns a failure status.
+
+If the @option{-p} option is used, @code{type} either returns the name
+of the disk file that would be executed, or nothing if @option{-t}
+would not return @samp{file}.
+
+The @option{-P} option forces a path search for each @var{name}, even if
+@option{-t} would not return @samp{file}.
+
+If a command is hashed, @option{-p} and @option{-P} print the hashed value,
+not necessarily the file that appears first in @code{$PATH}.
+
+If the @option{-a} option is used, @code{type} returns all of the places
+that contain an executable named @var{file}.
+This includes aliases and functions, if and only if the @option{-p} option
+is not also used.
+
+If the @option{-f} option is used, @code{type} does not attempt to find
+shell functions, as with the @code{command} builtin.
+
+The return status is zero if any of the @var{names} are found, non-zero
+if none are found.
+
+@item typeset
+@btindex typeset
+@example
+typeset [-afFrxi] [-p] [@var{name}[=@var{value}] @dots{}]
+@end example
+The @code{typeset} command is supplied for compatibility with the Korn
+shell; however, it has been deprecated in favor of the @code{declare}
+builtin command.
+
+@item ulimit
+@btindex ulimit
+@example
+ulimit [-acdflmnpstuvSH] [@var{limit}]
+@end example
+@code{ulimit} provides control over the resources available to processes
+started by the shell, on systems that allow such control.  If an
+option is given, it is interpreted as follows:
+@table @code
+@item -S
+Change and report the soft limit associated with a resource.
+
+@item -H
+Change and report the hard limit associated with a resource.
+
+@item -a
+All current limits are reported.
+
+@item -c
+The maximum size of core files created.
+
+@item -d
+The maximum size of a process's data segment.
+
+@item -f
+The maximum size of files created by the shell.
+
+@item -l
+The maximum size that may be locked into memory.
+
+@item -m
+The maximum resident set size.
+
+@item -n
+The maximum number of open file descriptors.
+
+@item -p
+The pipe buffer size.
+
+@item -s
+The maximum stack size.
+
+@item -t
+The maximum amount of cpu time in seconds.
+
+@item -u
+The maximum number of processes available to a single user.
+
+@item -v
+The maximum amount of virtual memory available to the process.
+
+@end table
+
+If @var{limit} is given, it is the new value of the specified resource;
+the special @var{limit} values @code{hard}, @code{soft}, and
+@code{unlimited} stand for the current hard limit, the current soft limit,
+and no limit, respectively.
+Otherwise, the current value of the soft limit for the specified resource
+is printed, unless the @option{-H} option is supplied.
+When setting new limits, if neither @option{-H} nor @option{-S} is supplied,
+both the hard and soft limits are set.
+If no option is given, then @option{-f} is assumed.  Values are in 1024-byte
+increments, except for @option{-t}, which is in seconds, @option{-p},
+which is in units of 512-byte blocks, and @option{-n} and @option{-u}, which
+are unscaled values.
+
+The return status is zero unless an invalid option or argument is supplied,
+or an error occurs while setting a new limit.
+
+@item unalias
+@btindex unalias
+@example
+unalias [-a] [@var{name} @dots{} ]
+@end example
+
+Remove each @var{name} from the list of aliases.  If @option{-a} is
+supplied, all aliases are removed.
+Aliases are described in @ref{Aliases}.
+
+@end table
+
+@node The Set Builtin
+@section The Set Builtin
+
+This builtin is so complicated that it deserves its own section.
+
+@table @code
+@item set
+@btindex set
+@example
+set [--abefhkmnptuvxBCHP] [-o @var{option}] [@var{argument} @dots{}]
+@end example
+
+If no options or arguments are supplied, @code{set} displays the names
+and values of all shell variables and functions, sorted according to the
+current locale, in a format that may be reused as input.
+
+When options are supplied, they set or unset shell attributes.
+Options, if specified, have the following meanings:
+
+@table @code
+@item -a
+Mark variables and function which are modified or created for export
+to the environment of subsequent commands.
+
+@item -b
+Cause the status of terminated background jobs to be reported
+immediately, rather than before printing the next primary prompt.
+
+@item -e
+Exit immediately if a simple command (@pxref{Simple Commands}) exits
+with a non-zero status, unless the command that fails is part of the
+command list immediately following a @code{while} or @code{until}
+keyword, part of the test in an @code{if} statement,
+part of a @code{&&} or @code{||} list, or if the command's return
+status is being inverted using @code{!}.
+A trap on @code{ERR}, if set, is executed before the shell exits.
+
+@item -f
+Disable file name generation (globbing).
+
+@item -h
+Locate and remember (hash) commands as they are looked up for execution.
+This option is enabled by default.
+
+@item -k
+All arguments in the form of assignment statements are placed
+in the environment for a command, not just those that precede
+the command name.
+
+@item -m
+Job control is enabled (@pxref{Job Control}).
+
+@item -n
+Read commands but do not execute them; this may be used to check a
+script for syntax errors.
+This option is ignored by interactive shells.
+
+@item -o @var{option-name}
+
+Set the option corresponding to @var{option-name}:
+
+@table @code
+@item allexport
+Same as @code{-a}.
+
+@item braceexpand
+Same as @code{-B}.
+
+@item emacs
+Use an @code{emacs}-style line editing interface (@pxref{Command Line Editing}).
+
+@item errexit
+Same as @code{-e}.
+
+@item errtrace
+Same as @code{-E}.
+
+@item functrace
+Same as @code{-T}.
+
+@item hashall
+Same as @code{-h}.
+
+@item histexpand
+Same as @code{-H}.
+
+@item history
+Enable command history, as described in @ref{Bash History Facilities}.
+This option is on by default in interactive shells.
+
+@item ignoreeof
+An interactive shell will not exit upon reading EOF.
+
+@item keyword
+Same as @code{-k}.
+
+@item monitor
+Same as @code{-m}.
+
+@item noclobber
+Same as @code{-C}.
+
+@item noexec
+Same as @code{-n}.
+
+@item noglob
+Same as @code{-f}.
+
+@item nolog
+Currently ignored.
+
+@item notify
+Same as @code{-b}.
+
+@item nounset
+Same as @code{-u}.
+
+@item onecmd
+Same as @code{-t}.
+
+@item physical
+Same as @code{-P}.
+
+@item pipefail
+If set, the return value of a pipeline is the value of the last
+(rightmost) command to exit with a non-zero status, or zero if all
+commands in the pipeline exit successfully.
+This option is disabled by default.
+
+@item posix
+Change the behavior of Bash where the default operation differs
+from the @sc{posix} 1003.2 standard to match the standard
+(@pxref{Bash POSIX Mode}).
+This is intended to make Bash behave as a strict superset of that
+standard.
+
+@item privileged
+Same as @code{-p}.
+
+@item verbose
+Same as @code{-v}.
+
+@item vi
+Use a @code{vi}-style line editing interface.
+
+@item xtrace
+Same as @code{-x}.
+@end table
+
+@item -p
+Turn on privileged mode.
+In this mode, the @env{$BASH_ENV} and @env{$ENV} files are not
+processed, shell functions are not inherited from the environment,
+and the @env{SHELLOPTS} variable, if it appears in the environment,
+is ignored.
+If the shell is started with the effective user (group) id not equal to the
+real user (group) id, and the @code{-p} option is not supplied, these actions
+are taken and the effective user id is set to the real user id.
+If the @code{-p} option is supplied at startup, the effective user id is
+not reset.
+Turning this option off causes the effective user
+and group ids to be set to the real user and group ids.
+
+@item -t
+Exit after reading and executing one command.
+
+@item -u
+Treat unset variables as an error when performing parameter expansion.
+An error message will be written to the standard error, and a non-interactive
+shell will exit.
+
+@item -v
+Print shell input lines as they are read.
+
+@item -x
+Print a trace of simple commands, \fBfor\fP commands, \fBcase\fP
+commands, \fBselect\fP commands, and arithmetic \fBfor\fP commands
+and their arguments or associated word lists after they are
+expanded and before they are executed.  The value of the @env{PS4}
+variable is expanded and the resultant value is printed before
+the command and its expanded arguments.
+
+@item -B
+The shell will perform brace expansion (@pxref{Brace Expansion}).
+This option is on by default.
+
+@item -C
+Prevent output redirection using @samp{>}, @samp{>&}, and @samp{<>}
+from overwriting existing files.
+
+@item -E
+If set, any trap on @code{ERR} is inherited by shell functions, command
+substitutions, and commands executed in a subshell environment.
+The @code{ERR} trap is normally not inherited in such cases.
+
+@item -H
+Enable @samp{!} style history substitution (@pxref{History Interaction}).
+This option is on by default for interactive shells.
+
+@item -P
+If set, do not follow symbolic links when performing commands such as
+@code{cd} which change the current directory.  The physical directory
+is used instead.  By default, Bash follows
+the logical chain of directories when performing commands
+which change the current directory.
+
+For example, if @file{/usr/sys} is a symbolic link to @file{/usr/local/sys}
+then:
+@example
+$ cd /usr/sys; echo $PWD
+/usr/sys
+$ cd ..; pwd
+/usr
+@end example
+
+@noindent
+If @code{set -P} is on, then:
+@example
+$ cd /usr/sys; echo $PWD
+/usr/local/sys
+$ cd ..; pwd
+/usr/local
+@end example
+
+@item -T
+If set, any trap on @code{DEBUG} is inherited by shell functions, command
+substitutions, and commands executed in a subshell environment.
+The @code{DEBUG} trap is normally not inherited in such cases.
+
+@item --
+If no arguments follow this option, then the positional parameters are
+unset.  Otherwise, the positional parameters are set to the
+@var{arguments}, even if some of them begin with a @samp{-}.
+
+@item -
+Signal the end of options, cause all remaining @var{arguments}
+to be assigned to the positional parameters.  The @option{-x}
+and @option{-v}  options are turned off.
+If there are no arguments, the positional parameters remain unchanged.
+@end table
+
+Using @samp{+} rather than @samp{-} causes these options to be
+turned off.  The options can also be used upon invocation of the
+shell.  The current set of options may be found in @code{$-}.
+
+The remaining N @var{arguments} are positional parameters and are
+assigned, in order, to @code{$1}, @code{$2}, @dots{}  @code{$N}.
+The special parameter @code{#} is set to N.
+
+The return status is always zero unless an invalid option is supplied.
+@end table
+
+@node Special Builtins
+@section Special Builtins
+@cindex special builtin
+
+For historical reasons, the @sc{posix} 1003.2 standard has classified
+several builtin commands as @emph{special}.
+When Bash is executing in @sc{posix} mode, the special builtins
+differ from other builtin commands in three respects:
+
+@enumerate
+@item
+Special builtins are found before shell functions during command lookup.
+
+@item
+If a special builtin returns an error status, a non-interactive shell exits.
+
+@item
+Assignment statements preceding the command stay in effect in the shell
+environment after the command completes.
+@end enumerate
+
+When Bash is not executing in @sc{posix} mode, these builtins behave no
+differently than the rest of the Bash builtin commands.
+The Bash @sc{posix} mode is described in @ref{Bash POSIX Mode}. 
+
+These are the @sc{posix} special builtins:
+@example
+@w{break : . continue eval exec exit export readonly return set}
+@w{shift trap unset}
+@end example
+
+@node Shell Variables
+@chapter Shell Variables
+
+@menu
+* Bourne Shell Variables::     Variables which Bash uses in the same way
+                               as the Bourne Shell.
+* Bash Variables::             List of variables that exist in Bash.
+@end menu
+
+This chapter describes the shell variables that Bash uses.
+Bash automatically assigns default values to a number of variables.
+
+@node Bourne Shell Variables
+@section Bourne Shell Variables
+
+Bash uses certain shell variables in the same way as the Bourne shell.
+In some cases, Bash assigns a default value to the variable.
+
+@vtable @code
+
+@item CDPATH
+A colon-separated list of directories used as a search path for
+the @code{cd} builtin command.
+
+@item HOME
+The current user's home directory; the default for the @code{cd} builtin
+command.
+The value of this variable is also used by tilde expansion
+(@pxref{Tilde Expansion}).
+
+@item IFS
+A list of characters that separate fields; used when the shell splits
+words as part of expansion.
+
+@item MAIL
+If this parameter is set to a filename and the @env{MAILPATH} variable
+is not set, Bash informs the user of the arrival of mail in
+the specified file.
+
+@item MAILPATH
+A colon-separated list of filenames which the shell periodically checks
+for new mail.
+Each list entry can specify the message that is printed when new mail
+arrives in the mail file by separating the file name from the message with
+a @samp{?}.
+When used in the text of the message, @code{$_} expands to the name of
+the current mail file.
+
+@item OPTARG
+The value of the last option argument processed by the @code{getopts} builtin.
+
+@item OPTIND
+The index of the last option argument processed by the @code{getopts} builtin.
+
+@item PATH
+A colon-separated list of directories in which the shell looks for
+commands.
+A zero-length (null) directory name in the value of @code{PATH} indicates the
+current directory.
+A null directory name may appear as two adjacent colons, or as an initial
+or trailing colon.
+
+
+@item PS1
+The primary prompt string.  The default value is @samp{\s-\v\$ }.
+@xref{Printing a Prompt}, for the complete list of escape
+sequences that are expanded before @env{PS1} is displayed.
+
+@item PS2
+The secondary prompt string.  The default value is @samp{> }.
+
+@end vtable
+
+@node Bash Variables
+@section Bash Variables
+
+These variables are set or used by Bash, but other shells
+do not normally treat them specially.
+
+A few variables used by Bash are described in different chapters:
+variables for controlling the job control facilities
+(@pxref{Job Control Variables}).
+
+@vtable @code
+
+@item BASH
+The full pathname used to execute the current instance of Bash.
+
+@item BASH_ARGC
+An array variable whose values are the number of parameters in each
+frame of the current bash execution call stack.  The number of
+parameters to the current subroutine (shell function or script executed
+with @code{.} or @code{source}) is at the top of the stack.  When a
+subroutine is executed, the number of parameters passed is pushed onto
+@code{BASH_ARGC}.
+
+@item BASH_ARGV
+An array variable containing all of the parameters in the current bash
+execution call stack.  The final parameter of the last subroutine call
+is at the top of the stack; the first parameter of the initial call is
+at the bottom.  When a subroutine is executed, the parameters supplied
+are pushed onto @code{BASH_ARGV}.
+
+@item BASH_COMMAND
+The command currently being executed or about to be executed, unless the
+shell is executing a command as the result of a trap,
+in which case it is the command executing at the time of the trap.
+
+@item BASH_ENV
+If this variable is set when Bash is invoked to execute a shell
+script, its value is expanded and used as the name of a startup file
+to read before executing the script.  @xref{Bash Startup Files}.
+
+@item BASH_EXECUTION_STRING
+The command argument to the @option{-c} invocation option.
+
+@item BASH_LINENO
+An array variable whose members are the line numbers in source files
+corresponding to each member of @var{FUNCNAME}.
+@code{$@{BASH_LINENO[$i]@}} is the line number in the source file where
+@code{$@{FUNCNAME[$i + 1]@}} was called.
+The corresponding source file name is @code{$@{BASH_SOURCE[$i + 1]@}}.
+Use @code{LINENO} to obtain the current line number.
+
+@item BASH_REMATCH
+An array variable whose members are assigned by the @samp{=~} binary
+operator to the @code{[[} conditional command
+(@pxref{Conditional Constructs}).
+The element with index 0 is the portion of the string
+matching the entire regular expression.
+The element with index @var{n} is the portion of the
+string matching the @var{n}th parenthesized subexpression.
+This variable is read-only.
+
+@item BASH_SOURCE
+An array variable whose members are the source filenames corresponding
+to the elements in the @code{FUNCNAME} array variable.
+
+@item BASH_SUBSHELL
+Incremented by one each time a subshell or subshell environment is spawned.
+The initial value is 0.
+
+@item BASH_VERSINFO
+A readonly array variable (@pxref{Arrays})
+whose members hold version information for this instance of Bash.
+The values assigned to the array members are as follows:
+
+@table @code
+
+@item BASH_VERSINFO[0]
+The major version number (the @var{release}).
+
+@item BASH_VERSINFO[1]
+The minor version number (the @var{version}).
+
+@item BASH_VERSINFO[2]
+The patch level.
+
+@item BASH_VERSINFO[3]
+The build version.
+
+@item BASH_VERSINFO[4]
+The release status (e.g., @var{beta1}).
+
+@item BASH_VERSINFO[5]
+The value of @env{MACHTYPE}.
+
+@end table
+
+@item BASH_VERSION
+The version number of the current instance of Bash.
+
+@item COLUMNS
+Used by the @code{select} builtin command to determine the terminal width
+when printing selection lists.  Automatically set upon receipt of a
+@code{SIGWINCH}.
+
+@item COMP_CWORD
+An index into @env{$@{COMP_WORDS@}} of the word containing the current
+cursor position.
+This variable is available only in shell functions invoked by the
+programmable completion facilities (@pxref{Programmable Completion}).
+
+@item COMP_LINE
+The current command line.
+This variable is available only in shell functions and external
+commands invoked by the
+programmable completion facilities (@pxref{Programmable Completion}).
+
+@item COMP_POINT
+The index of the current cursor position relative to the beginning of
+the current command.
+If the current cursor position is at the end of the current command,
+the value of this variable is equal to @code{$@{#COMP_LINE@}}.
+This variable is available only in shell functions and external
+commands invoked by the
+programmable completion facilities (@pxref{Programmable Completion}).
+
+@item COMP_WORDBREAKS
+The set of characters that the Readline library treats as word
+separators when performing word completion.
+If @code{COMP_WORDBREAKS} is unset, it loses its special properties,
+even if it is subsequently reset.
+
+@item COMP_WORDS
+An array variable consisting of the individual
+words in the current command line.
+This variable is available only in shell functions invoked by the
+programmable completion facilities (@pxref{Programmable Completion}).
+
+@item COMPREPLY
+An array variable from which Bash reads the possible completions
+generated by a shell function invoked by the programmable completion
+facility (@pxref{Programmable Completion}).
+
+@item DIRSTACK
+An array variable containing the current contents of the directory stack.
+Directories appear in the stack in the order they are displayed by the
+@code{dirs} builtin.
+Assigning to members of this array variable may be used to modify
+directories already in the stack, but the @code{pushd} and @code{popd}
+builtins must be used to add and remove directories.
+Assignment to this variable will not change the current directory.
+If @env{DIRSTACK} is unset, it loses its special properties, even if
+it is subsequently reset.
+
+@item EMACS
+If Bash finds this variable in the environment when the shell
+starts with value @samp{t}, it assumes that the shell is running in an
+emacs shell buffer and disables line editing.
+
+@item EUID
+The numeric effective user id of the current user.  This variable
+is readonly.
+
+@item FCEDIT
+The editor used as a default by the @option{-e} option to the @code{fc}
+builtin command.
+
+@item FIGNORE
+A colon-separated list of suffixes to ignore when performing
+filename completion.
+A file name whose suffix matches one of the entries in 
+@env{FIGNORE}
+is excluded from the list of matched file names.  A sample
+value is @samp{.o:~}
+
+@item FUNCNAME
+An array variable containing the names of all shell functions
+currently in the execution call stack.
+The element with index 0 is the name of any currently-executing
+shell function.
+The bottom-most element is "main".
+This variable exists only when a shell function is executing.
+Assignments to @env{FUNCNAME} have no effect and return an error status.
+If @env{FUNCNAME} is unset, it loses its special properties, even if
+it is subsequently reset.
+
+@item GLOBIGNORE
+A colon-separated list of patterns defining the set of filenames to
+be ignored by filename expansion.
+If a filename matched by a filename expansion pattern also matches one
+of the patterns in @env{GLOBIGNORE}, it is removed from the list
+of matches.
+
+@item GROUPS
+An array variable containing the list of groups of which the current    
+user is a member.
+Assignments to @env{GROUPS} have no effect and return an error status.
+If @env{GROUPS} is unset, it loses its special properties, even if it is
+subsequently reset.
+
+@item histchars
+Up to three characters which control history expansion, quick
+substitution, and tokenization (@pxref{History Interaction}).
+The first character is the
+@var{history expansion} character, that is, the character which signifies the
+start of a history expansion, normally @samp{!}.  The second character is the
+character which signifies `quick substitution' when seen as the first
+character on a line, normally @samp{^}.  The optional third character is the
+character which indicates that the remainder of the line is a comment when
+found as the first character of a word, usually @samp{#}.  The history
+comment character causes history substitution to be skipped for the
+remaining words on the line.  It does not necessarily cause the shell
+parser to treat the rest of the line as a comment.
+
+@item HISTCMD
+The history number, or index in the history list, of the current
+command.  If @env{HISTCMD} is unset, it loses its special properties,
+even if it is subsequently reset.
+
+@item HISTCONTROL
+A colon-separated list of values controlling how commands are saved on
+the history list.
+If the list of values includes @samp{ignorespace}, lines which begin
+with a space character are not saved in the history list.
+A value of @samp{ignoredups} causes lines which match the previous
+history entry to not be saved.
+A value of @samp{ignoreboth} is shorthand for
+@samp{ignorespace} and @samp{ignoredups}.
+A value of @samp{erasedups} causes all previous lines matching the
+current line to be removed from the history list before that line
+is saved.
+Any value not in the above list is ignored.
+If @env{HISTCONTROL} is unset, or does not include a valid value, 
+all lines read by the shell parser are saved on the history list, 
+subject to the value of @env{HISTIGNORE}.
+The second and subsequent lines of a multi-line compound command are
+not tested, and are added to the history regardless of the value of
+@env{HISTCONTROL}.
+
+@item HISTFILE
+The name of the file to which the command history is saved.  The
+default value is @file{~/.bash_history}.
+
+@item HISTFILESIZE
+The maximum number of lines contained in the history file.  When this
+variable is assigned a value, the history file is truncated, if
+necessary, to contain no more than that number of lines.
+The history file is also truncated to this size after
+writing it when an interactive shell exits.
+The default value is 500.
+
+@item HISTIGNORE
+A colon-separated list of patterns used to decide which command
+lines should be saved on the history list.  Each pattern is
+anchored at the beginning of the line and must match the complete
+line (no implicit @samp{*} is appended).  Each pattern is tested
+against the line after the checks specified by @env{HISTCONTROL}
+are applied.  In addition to the normal shell pattern matching
+characters, @samp{&} matches the previous history line.  @samp{&}
+may be escaped using a backslash; the backslash is removed
+before attempting a match. 
+The second and subsequent lines of a multi-line compound command are
+not tested, and are added to the history regardless of the value of
+@env{HISTIGNORE}.
+
+@env{HISTIGNORE} subsumes the function of @env{HISTCONTROL}.  A
+pattern of @samp{&} is identical to @code{ignoredups}, and a
+pattern of @samp{[ ]*} is identical to @code{ignorespace}. 
+Combining these two patterns, separating them with a colon,
+provides the functionality of @code{ignoreboth}. 
+
+@item HISTSIZE
+The maximum number of commands to remember on the history list.
+The default value is 500.
+
+@item HISTTIMEFORMAT
+If this variable is set and not null, its value is used as a format string
+for @var{strftime} to print the time stamp associated with each history
+entry displayed by the @code{history} builtin.
+If this variable is set, time stamps are written to the history file so
+they may be preserved across shell sessions.
+
+@item HOSTFILE
+Contains the name of a file in the same format as @file{/etc/hosts} that
+should be read when the shell needs to complete a hostname.
+The list of possible hostname completions may be changed while the shell
+is running;
+the next time hostname completion is attempted after the
+value is changed, Bash adds the contents of the new file to the
+existing list.
+If @env{HOSTFILE} is set, but has no value, Bash attempts to read 
+@file{/etc/hosts} to obtain the list of possible hostname completions.
+When @env{HOSTFILE} is unset, the hostname list is cleared.
+
+@item HOSTNAME
+The name of the current host.
+
+@item HOSTTYPE
+A string describing the machine Bash is running on.
+
+@item IGNOREEOF
+Controls the action of the shell on receipt of an @code{EOF} character
+as the sole input.  If set, the value denotes the number
+of consecutive @code{EOF} characters that can be read as the
+first character on an input line
+before the shell will exit.  If the variable exists but does not
+have a numeric value (or has no value) then the default is 10.
+If the variable does not exist, then @code{EOF} signifies the end of 
+input to the shell.  This is only in effect for interactive shells.
+
+@item INPUTRC
+The name of the Readline initialization file, overriding the default
+of @file{~/.inputrc}.
+
+@item LANG  
+Used to determine the locale category for any category not specifically
+selected with a variable starting with @code{LC_}.
+
+@item LC_ALL
+This variable overrides the value of @env{LANG} and any other
+@code{LC_} variable specifying a locale category.
+
+@item LC_COLLATE
+This variable determines the collation order used when sorting the
+results of filename expansion, and
+determines the behavior of range expressions, equivalence classes,
+and collating sequences within filename expansion and pattern matching
+(@pxref{Filename Expansion}).
+
+@item LC_CTYPE
+This variable determines the interpretation of characters and the
+behavior of character classes within filename expansion and pattern
+matching (@pxref{Filename Expansion}).
+
+@item LC_MESSAGES
+This variable determines the locale used to translate double-quoted
+strings preceded by a @samp{$} (@pxref{Locale Translation}).
+
+@item LC_NUMERIC
+This variable determines the locale category used for number formatting.
+
+@item LINENO
+The line number in the script or shell function currently executing.
+
+@item LINES
+Used by the @code{select} builtin command to determine the column length
+for printing selection lists.  Automatically set upon receipt of a
+@code{SIGWINCH}.
+
+@item MACHTYPE
+A string that fully describes the system type on which Bash
+is executing, in the standard @sc{gnu} @var{cpu-company-system} format.
+
+@item MAILCHECK
+How often (in seconds) that the shell should check for mail in the
+files specified in the @env{MAILPATH} or @env{MAIL} variables.
+The default is 60 seconds.  When it is time to check
+for mail, the shell does so before displaying the primary prompt.
+If this variable is unset, or set to a value that is not a number
+greater than or equal to zero, the shell disables mail checking.
+
+@item OLDPWD
+The previous working directory as set by the @code{cd} builtin.
+
+@item OPTERR
+If set to the value 1, Bash displays error messages
+generated by the @code{getopts} builtin command.
+
+@item OSTYPE
+A string describing the operating system Bash is running on.
+
+@item PIPESTATUS
+An array variable (@pxref{Arrays})
+containing a list of exit status values from the processes
+in the most-recently-executed foreground pipeline (which may
+contain only a single command).
+
+@item POSIXLY_CORRECT
+If this variable is in the environment when @code{bash} starts, the shell
+enters @sc{posix} mode (@pxref{Bash POSIX Mode}) before reading the
+startup files, as if the @option{--posix} invocation option had been supplied.
+If it is set while the shell is running, @code{bash} enables @sc{posix} mode,
+as if the command
+@example
+@code{set -o posix}
+@end example
+@noindent
+had been executed.  
+
+@item PPID
+The process @sc{id} of the shell's parent process.  This variable
+is readonly.
+
+@item PROMPT_COMMAND
+If set, the value is interpreted as a command to execute
+before the printing of each primary prompt (@env{$PS1}).
+
+@item PS3
+The value of this variable is used as the prompt for the
+@code{select} command.  If this variable is not set, the
+@code{select} command prompts with @samp{#? }
+
+@item PS4
+The value is the prompt printed before the command line is echoed
+when the @option{-x} option is set (@pxref{The Set Builtin}).
+The first character of @env{PS4} is replicated multiple times, as
+necessary, to indicate multiple levels of indirection.
+The default is @samp{+ }.
+
+@item PWD
+The current working directory as set by the @code{cd} builtin.
+
+@item RANDOM
+Each time this parameter is referenced, a random integer
+between 0 and 32767 is generated.  Assigning a value to this
+variable seeds the random number generator.
+
+@item REPLY
+The default variable for the @code{read} builtin.
+
+@item SECONDS
+This variable expands to the number of seconds since the
+shell was started.  Assignment to this variable resets
+the count to the value assigned, and the expanded value
+becomes the value assigned plus the number of seconds
+since the assignment.
+
+@item SHELL
+The full pathname to the shell is kept in this environment variable.
+If it is not set when the shell starts,
+Bash assigns to it the full pathname of the current user's login shell.
+
+@item SHELLOPTS
+A colon-separated list of enabled shell options.  Each word in
+the list is a valid argument for the @option{-o} option to the
+@code{set} builtin command (@pxref{The Set Builtin}).
+The options appearing in @env{SHELLOPTS} are those reported
+as @samp{on} by @samp{set -o}.
+If this variable is in the environment when Bash
+starts up, each shell option in the list will be enabled before
+reading any startup files.  This variable is readonly.
+
+@item SHLVL
+Incremented by one each time a new instance of Bash is started.  This is
+intended to be a count of how deeply your Bash shells are nested.
+
+@item TIMEFORMAT
+The value of this parameter is used as a format string specifying
+how the timing information for pipelines prefixed with the @code{time}
+reserved word should be displayed.
+The @samp{%} character introduces an
+escape sequence that is expanded to a time value or other
+information.
+The escape sequences and their meanings are as
+follows; the braces denote optional portions. 
+
+@table @code
+
+@item %%
+A literal @samp{%}.
+
+@item %[@var{p}][l]R
+The elapsed time in seconds. 
+
+@item %[@var{p}][l]U
+The number of CPU seconds spent in user mode. 
+
+@item %[@var{p}][l]S
+The number of CPU seconds spent in system mode. 
+
+@item %P
+The CPU percentage, computed as (%U + %S) / %R. 
+@end table
+
+The optional @var{p} is a digit specifying the precision, the number of
+fractional digits after a decimal point.
+A value of 0 causes no decimal point or fraction to be output.
+At most three places after the decimal point may be specified; values
+of @var{p} greater than 3 are changed to 3.
+If @var{p} is not specified, the value 3 is used. 
+
+The optional @code{l} specifies a longer format, including minutes, of
+the form @var{MM}m@var{SS}.@var{FF}s.
+The value of @var{p} determines whether or not the fraction is included. 
+
+If this variable is not set, Bash acts as if it had the value
+@example
+@code{$'\nreal\t%3lR\nuser\t%3lU\nsys\t%3lS'}
+@end example
+If the value is null, no timing information is displayed.
+A trailing newline is added when the format string is displayed.
+
+@item TMOUT
+If set to a value greater than zero, @code{TMOUT} is treated as the
+default timeout for the @code{read} builtin (@pxref{Bash Builtins}).
+The @code{select} command (@pxref{Conditional Constructs}) terminates
+if input does not arrive after @code{TMOUT} seconds when input is coming
+from a terminal.
+
+In an interative shell, the value is interpreted as
+the number of seconds to wait for input after issuing the primary
+prompt when the shell is interactive.
+Bash terminates after that number of seconds if input does
+not arrive.
+
+@item UID
+The numeric real user id of the current user.  This variable is readonly.
+
+@end vtable
+
+@node Bash Features
+@chapter Bash Features
+
+This section describes features unique to Bash.
+
+@menu
+* Invoking Bash::              Command line options that you can give
+                               to Bash.
+* Bash Startup Files::         When and how Bash executes scripts.
+* Interactive Shells::         What an interactive shell is.
+* Bash Conditional Expressions::       Primitives used in composing expressions for
+                               the @code{test} builtin.
+* Shell Arithmetic::           Arithmetic on shell variables.
+* Aliases::                    Substituting one command for another.
+* Arrays::                     Array Variables.
+* The Directory Stack::                History of visited directories.
+* Printing a Prompt::          Controlling the PS1 string.
+* The Restricted Shell::       A more controlled mode of shell execution.
+* Bash POSIX Mode::            Making Bash behave more closely to what
+                               the POSIX standard specifies.
+@end menu
+
+@node Invoking Bash
+@section Invoking Bash
+
+@example
+bash [long-opt] [-ir] [-abefhkmnptuvxdBCDHP] [-o @var{option}] [-O @var{shopt_option}] [@var{argument} @dots{}]
+bash [long-opt] [-abefhkmnptuvxdBCDHP] [-o @var{option}] [-O @var{shopt_option}] -c @var{string} [@var{argument} @dots{}]
+bash [long-opt] -s [-abefhkmnptuvxdBCDHP] [-o @var{option}] [-O @var{shopt_option}] [@var{argument} @dots{}]
+@end example
+
+In addition to the single-character shell command-line options
+(@pxref{The Set Builtin}), there are several multi-character
+options that you can use.  These options must appear on the command
+line before the single-character options to be recognized. 
+
+@table @code
+@item --debugger
+Arrange for the debugger profile to be executed before the shell
+starts.  Turns on extended debugging mode (see @ref{Bash Builtins}
+for a description of the @code{extdebug} option to the @code{shopt}
+builtin) and shell function tracing
+(see @ref{The Set Builtin} for a description of the @code{-o functrace}
+option).
+
+@item --dump-po-strings
+A list of all double-quoted strings preceded by @samp{$}
+is printed on the standard ouput
+in the @sc{gnu} @code{gettext} PO (portable object) file format.
+Equivalent to @option{-D} except for the output format.
+
+@item --dump-strings
+Equivalent to @option{-D}.
+
+@item --help
+Display a usage message on standard output and exit sucessfully.
+
+@item --init-file @var{filename}
+@itemx --rcfile @var{filename}
+Execute commands from @var{filename} (instead of @file{~/.bashrc})
+in an interactive shell.
+
+@item --login
+Equivalent to @option{-l}.
+
+@item --noediting
+Do not use the @sc{gnu} Readline library (@pxref{Command Line Editing})
+to read  command lines when the shell is interactive.
+
+@item --noprofile
+Don't load the system-wide startup file @file{/etc/profile}
+or any of the personal initialization files
+@file{~/.bash_profile}, @file{~/.bash_login}, or @file{~/.profile}
+when Bash is invoked as a login shell.
+
+@item --norc
+Don't read the @file{~/.bashrc} initialization file in an
+interactive shell.  This is on by default if the shell is
+invoked as @code{sh}.
+
+@item --posix
+Change the behavior of Bash where the default operation differs
+from the @sc{posix} 1003.2 standard to match the standard.  This
+is intended to make Bash behave as a strict superset of that
+standard.  @xref{Bash POSIX Mode}, for a description of the Bash
+@sc{posix} mode.
+
+@item --restricted
+Make the shell a restricted shell (@pxref{The Restricted Shell}).
+
+@item --verbose
+Equivalent to @option{-v}.  Print shell input lines as they're read.
+
+@item --version
+Show version information for this instance of
+Bash on the standard output and exit successfully.
+
+@end table
+
+There are several single-character options that may be supplied at
+invocation which are not available with the @code{set} builtin.
+
+@table @code
+@item -c @var{string}
+Read and execute commands from @var{string} after processing the
+options, then exit.  Any remaining arguments are assigned to the
+positional parameters, starting with @code{$0}.
+
+@item -i
+Force the shell to run interactively.  Interactive shells are
+described in @ref{Interactive Shells}.
+
+@item -l
+Make this shell act as if it had been directly invoked by login.
+When the shell is interactive, this is equivalent to starting a
+login shell with @samp{exec -l bash}.
+When the shell is not interactive, the login shell startup files will
+be executed.
+@samp{exec bash -l} or @samp{exec bash --login}
+will replace the current shell with a Bash login shell.
+@xref{Bash Startup Files}, for a description of the special behavior
+of a login shell.
+
+@item -r
+Make the shell a restricted shell (@pxref{The Restricted Shell}).
+
+@item -s
+If this option is present, or if no arguments remain after option
+processing, then commands are read from the standard input.
+This option allows the positional parameters to be set
+when invoking an interactive shell.
+
+@item -D
+A list of all double-quoted strings preceded by @samp{$}
+is printed on the standard ouput.
+These are the strings that
+are subject to language translation when the current locale
+is not @code{C} or @code{POSIX} (@pxref{Locale Translation}).
+This implies the @option{-n} option; no commands will be executed.
+
+@item [-+]O [@var{shopt_option}]
+@var{shopt_option} is one of the shell options accepted by the
+@code{shopt} builtin (@pxref{Shell Builtin Commands}).
+If @var{shopt_option} is present, @option{-O} sets the value of that option;
+@option{+O} unsets it.  
+If @var{shopt_option} is not supplied, the names and values of the shell
+options accepted by @code{shopt} are printed on the standard output.
+If the invocation option is @option{+O}, the output is displayed in a format
+that may be reused as input.
+
+@item --
+A @code{--} signals the end of options and disables further option
+processing.
+Any arguments after the @code{--} are treated as filenames and arguments.
+
+@end table
+
+@cindex login shell
+A @emph{login} shell is one whose first character of argument zero is
+@samp{-}, or one invoked with the @option{--login} option.
+
+@cindex interactive shell
+An @emph{interactive} shell is one started without non-option arguments,
+unless @option{-s} is specified,
+without specifying the @option{-c} option, and whose input and output are both
+connected to terminals (as determined by @code{isatty(3)}), or one
+started with the @option{-i} option.  @xref{Interactive Shells}, for more
+information.
+
+If arguments remain after option processing, and neither the
+@option{-c} nor the @option{-s}
+option has been supplied, the first argument is assumed to
+be the name of a file containing shell commands (@pxref{Shell Scripts}).
+When Bash is invoked in this fashion, @code{$0}
+is set to the name of the file, and the positional parameters
+are set to the remaining arguments.
+Bash reads and executes commands from this file, then exits.   
+Bash's exit status is the exit status of the last command executed
+in the script.  If no commands are executed, the exit status is 0.
+
+@node Bash Startup Files
+@section Bash Startup Files
+@cindex startup files
+
+This section describs how Bash executes its startup files.
+If any of the files exist but cannot be read, Bash reports an error.
+Tildes are expanded in file names as described above under
+Tilde Expansion (@pxref{Tilde Expansion}).
+
+Interactive shells are described in @ref{Interactive Shells}.
+
+@subsubheading Invoked as an interactive login shell, or with @option{--login}
+
+When Bash is invoked as an interactive login shell, or as a
+non-interactive shell with the @option{--login} option, it first reads and
+executes commands from the file @file{/etc/profile}, if that file exists.
+After reading that file, it looks for @file{~/.bash_profile},
+@file{~/.bash_login}, and @file{~/.profile}, in that order, and reads
+and executes commands from the first one that exists and is readable.
+The @option{--noprofile} option may be used when the shell is started to
+inhibit this behavior.
+
+When a login shell exits, Bash reads and executes commands from
+the file @file{~/.bash_logout}, if it exists.
+
+@subsubheading Invoked as an interactive non-login shell
+
+When an interactive shell that is not a login shell is started, Bash
+reads and executes commands from @file{~/.bashrc}, if that file exists.
+This may be inhibited by using the @option{--norc} option.
+The @option{--rcfile @var{file}} option will force Bash to read and
+execute commands from @var{file} instead of @file{~/.bashrc}.
+
+So, typically, your @file{~/.bash_profile} contains the line
+@example
+@code{if [ -f ~/.bashrc ]; then . ~/.bashrc; fi}
+@end example
+@noindent
+after (or before) any login-specific initializations.
+
+@subsubheading Invoked non-interactively
+
+When Bash is started non-interactively, to run a shell script,
+for example, it looks for the variable @env{BASH_ENV} in the environment,
+expands its value if it appears there, and uses the expanded value as
+the name of a file to read and execute.  Bash behaves as if the
+following command were executed:
+@example
+@code{if [ -n "$BASH_ENV" ]; then . "$BASH_ENV"; fi}
+@end example
+@noindent
+but the value of the @env{PATH} variable is not used to search for the
+file name.
+
+As noted above, if a non-interactive shell is invoked with the
+@option{--login} option, Bash attempts to read and execute commands from the
+login shell startup files. 
+
+@subsubheading Invoked with name @code{sh}
+
+If Bash is invoked with the name @code{sh}, it tries to mimic the
+startup behavior of historical versions of @code{sh} as closely as
+possible, while conforming to the @sc{posix} standard as well.
+
+When invoked as an interactive login shell, or as a non-interactive
+shell with the @option{--login} option, it first attempts to read
+and execute commands from @file{/etc/profile} and @file{~/.profile}, in
+that order.
+The @option{--noprofile} option may be used to inhibit this behavior.
+When invoked as an interactive shell with the name @code{sh}, Bash
+looks for the variable @env{ENV}, expands its value if it is defined,
+and uses the expanded value as the name of a file to read and execute.
+Since a shell invoked as @code{sh} does not attempt to read and execute
+commands from any other startup files, the @option{--rcfile} option has
+no effect.
+A non-interactive shell invoked with the name @code{sh} does not attempt
+to read any other startup files.
+
+When invoked as @code{sh}, Bash enters @sc{posix} mode after
+the startup files are read.
+
+@subsubheading Invoked in @sc{posix} mode
+
+When Bash is started in @sc{posix} mode, as with the
+@option{--posix} command line option, it follows the @sc{posix} standard
+for startup files.
+In this mode, interactive shells expand the @env{ENV} variable
+and commands are read and executed from the file whose name is the
+expanded value.
+No other startup files are read.
+
+@subsubheading Invoked by remote shell daemon
+
+Bash attempts to determine when it is being run by the remote shell
+daemon, usually @code{rshd}.  If Bash determines it is being run by
+rshd, it reads and executes commands from @file{~/.bashrc}, if that
+file exists and is readable.
+It will not do this if invoked as @code{sh}.
+The @option{--norc} option may be used to inhibit this behavior, and the
+@option{--rcfile} option may be used to force another file to be read, but
+@code{rshd} does not generally invoke the shell with those options or
+allow them to be specified.
+
+@subsubheading Invoked with unequal effective and real @sc{uid/gid}s
+
+If Bash is started with the effective user (group) id not equal to the
+real user (group) id, and the @code{-p} option is not supplied, no startup
+files are read, shell functions are not inherited from the environment,
+the @env{SHELLOPTS} variable, if it appears in the environment, is ignored,
+and the effective user id is set to the real user id.
+If the @code{-p} option is supplied at invocation, the startup behavior is
+the same, but the effective user id is not reset.
+
+@node Interactive Shells
+@section Interactive Shells
+@cindex interactive shell
+@cindex shell, interactive
+
+@menu
+* What is an Interactive Shell?::      What determines whether a shell is Interactive.
+* Is this Shell Interactive?:: How to tell if a shell is interactive.
+* Interactive Shell Behavior:: What changes in a interactive shell?
+@end menu
+
+@node What is an Interactive Shell?
+@subsection What is an Interactive Shell?
+
+An interactive shell
+is one started without non-option arguments, unless @option{-s} is
+specified, without specifiying the @option{-c} option, and
+whose input and error output are both
+connected to terminals (as determined by @code{isatty(3)}),
+or one started with the @option{-i} option.
+
+An interactive shell generally reads from and writes to a user's
+terminal.
+
+The @option{-s} invocation option may be used to set the positional parameters
+when an interactive shell is started.
+
+@node Is this Shell Interactive?
+@subsection Is this Shell Interactive?
+
+To determine within a startup script whether or not Bash is
+running interactively,
+test the value of the @samp{-} special parameter.
+It contains @code{i} when the shell is interactive.  For example:
+
+@example
+case "$-" in
+*i*)   echo This shell is interactive ;;
+*)     echo This shell is not interactive ;;
+esac
+@end example
+
+Alternatively, startup scripts may examine the variable
+@env{PS1}; it is unset in non-interactive shells, and set in
+interactive shells.  Thus:
+
+@example
+if [ -z "$PS1" ]; then
+        echo This shell is not interactive
+else
+        echo This shell is interactive
+fi
+@end example
+
+@node Interactive Shell Behavior
+@subsection Interactive Shell Behavior
+
+When the shell is running interactively, it changes its behavior in
+several ways.
+
+@enumerate
+@item
+Startup files are read and executed as described in @ref{Bash Startup Files}.
+
+@item
+Job Control (@pxref{Job Control}) is enabled by default.  When job
+control is in effect, Bash ignores the keyboard-generated job control
+signals @code{SIGTTIN}, @code{SIGTTOU}, and @code{SIGTSTP}.
+
+@item
+Bash expands and displays @env{PS1} before reading the first line
+of a command, and expands and displays @env{PS2} before reading the
+second and subsequent lines of a multi-line command.
+
+@item
+Bash executes the value of the @env{PROMPT_COMMAND} variable as a command
+before printing the primary prompt, @env{$PS1}
+(@pxref{Bash Variables}).
+
+@item
+Readline (@pxref{Command Line Editing}) is used to read commands from
+the user's terminal.
+
+@item
+Bash inspects the value of the @code{ignoreeof} option to @code{set -o}
+instead of exiting immediately when it receives an @code{EOF} on its
+standard input when reading a command (@pxref{The Set Builtin}).
+
+@item
+Command history (@pxref{Bash History Facilities})
+and history expansion (@pxref{History Interaction})
+are enabled by default.
+Bash will save the command history to the file named by @env{$HISTFILE}
+when an interactive shell exits.
+
+@item
+Alias expansion (@pxref{Aliases}) is performed by default.
+
+@item
+In the absence of any traps, Bash ignores @code{SIGTERM}
+(@pxref{Signals}).
+
+@item
+In the absence of any traps, @code{SIGINT} is caught and handled
+((@pxref{Signals}).
+@code{SIGINT} will interrupt some shell builtins.
+
+@item
+An interactive login shell sends a @code{SIGHUP} to all jobs on exit
+if the @code{hupoxexit} shell option has been enabled (@pxref{Signals}).
+
+@item
+The @option{-n} invocation option is ignored, and @samp{set -n} has
+no effect (@pxref{The Set Builtin}).
+
+@item
+Bash will check for mail periodically, depending on the values of the
+@env{MAIL}, @env{MAILPATH}, and @env{MAILCHECK} shell variables
+(@pxref{Bash Variables}).
+
+@item
+Expansion errors due to references to unbound shell variables after
+@samp{set -u} has been enabled will not cause the shell to exit
+(@pxref{The Set Builtin}).
+
+@item
+The shell will not exit on expansion errors caused by @var{var} being unset
+or null in @code{$@{@var{var}:?@var{word}@}} expansions
+(@pxref{Shell Parameter Expansion}).
+
+@item
+Redirection errors encountered by shell builtins will not cause the
+shell to exit.
+
+@item
+When running in @sc{posix} mode, a special builtin returning an error
+status will not cause the shell to exit (@pxref{Bash POSIX Mode}).
+@item
+A failed @code{exec} will not cause the shell to exit
+(@pxref{Bourne Shell Builtins}).
+
+@item
+Parser syntax errors will not cause the shell to exit.
+
+@item
+Simple spelling correction for directory arguments to the @code{cd}
+builtin is enabled by default (see the description of the @code{cdspell}
+option to the @code{shopt} builtin in @ref{Bash Builtins}).
+
+@item
+The shell will check the value of the @env{TMOUT} variable and exit
+if a command is not read within the specified number of seconds after
+printing @env{$PS1} (@pxref{Bash Variables}).
+
+@end enumerate
+
+@node Bash Conditional Expressions
+@section Bash Conditional Expressions
+@cindex expressions, conditional
+
+Conditional expressions are used by the @code{[[} compound command
+and the @code{test} and @code{[} builtin commands.
+
+Expressions may be unary or binary.
+Unary expressions are often used to examine the status of a file.
+There are string operators and numeric comparison operators as well.
+If the @var{file} argument to one of the primaries is of the form
+@file{/dev/fd/@var{N}}, then file descriptor @var{N} is checked.
+If the @var{file} argument to one of the primaries is one of
+@file{/dev/stdin}, @file{/dev/stdout}, or @file{/dev/stderr}, file
+descriptor 0, 1, or 2, respectively, is checked.
+
+@table @code
+@item -a @var{file}
+True if @var{file} exists.
+
+@item -b @var{file}
+True if @var{file} exists and is a block special file.
+
+@item -c @var{file}
+True if @var{file} exists and is a character special file.
+
+@item -d @var{file}
+True if @var{file} exists and is a directory.
+
+@item -e @var{file}
+True if @var{file} exists.
+
+@item -f @var{file}
+True if @var{file} exists and is a regular file.
+
+@item -g @var{file}
+True if @var{file} exists and its set-group-id bit is set.
+
+@item -h @var{file}
+True if @var{file} exists and is a symbolic link.
+
+@item -k @var{file}
+True if @var{file} exists and its "sticky" bit is set.
+
+@item -p @var{file}
+True if @var{file} exists and is a named pipe (FIFO).
+
+@item -r @var{file}
+True if @var{file} exists and is readable.
+
+@item -s @var{file}
+True if @var{file} exists and has a size greater than zero.
+
+@item -t @var{fd}
+True if file descriptor @var{fd} is open and refers to a terminal.
+
+@item -u @var{file}
+True if @var{file} exists and its set-user-id bit is set.
+
+@item -w @var{file}
+True if @var{file} exists and is writable.
+
+@item -x @var{file}
+True if @var{file} exists and is executable.
+
+@item -O @var{file}
+True if @var{file} exists and is owned by the effective user id.
+
+@item -G @var{file}
+True if @var{file} exists and is owned by the effective group id.
+
+@item -L @var{file}
+True if @var{file} exists and is a symbolic link.
+
+@item -S @var{file}
+True if @var{file} exists and is a socket.
+
+@item -N @var{file}
+True if @var{file} exists and has been modified since it was last read.
+
+@item @var{file1} -nt @var{file2}
+True if @var{file1} is newer (according to modification date)
+than @var{file2}, or if @var{file1} exists and @var{file2} does not.
+
+@item @var{file1} -ot @var{file2}
+True if @var{file1} is older than @var{file2},
+or if @var{file2} exists and @var{file1} does not.
+
+@item @var{file1} -ef @var{file2}
+True if @var{file1} and @var{file2} refer to the same device and
+inode numbers.
+
+@item -o @var{optname}
+True if shell option @var{optname} is enabled.
+The list of options appears in the description of the @option{-o}
+option to the @code{set} builtin (@pxref{The Set Builtin}).
+
+@item -z @var{string}
+True if the length of @var{string} is zero.
+
+@item -n @var{string}
+@itemx @var{string}
+True if the length of @var{string} is non-zero.
+
+@item @var{string1} == @var{string2}
+True if the strings are equal.
+@samp{=} may be used in place of @samp{==} for strict @sc{posix} compliance.
+
+@item @var{string1} != @var{string2}
+True if the strings are not equal.
+
+@item @var{string1} < @var{string2}
+True if @var{string1} sorts before @var{string2} lexicographically
+in the current locale.
+
+@item @var{string1} > @var{string2}
+True if @var{string1} sorts after @var{string2} lexicographically
+in the current locale.
+
+@item @var{arg1} OP @var{arg2}
+@code{OP} is one of 
+@samp{-eq}, @samp{-ne}, @samp{-lt}, @samp{-le}, @samp{-gt}, or @samp{-ge}.
+These arithmetic binary operators return true if @var{arg1}
+is equal to, not equal to, less than, less than or equal to,
+greater than, or greater than or equal to @var{arg2},
+respectively.  @var{Arg1} and @var{arg2}
+may be positive or negative integers.
+
+@end table
+
+@node Shell Arithmetic
+@section Shell Arithmetic
+@cindex arithmetic, shell
+@cindex shell arithmetic
+@cindex expressions, arithmetic
+@cindex evaluation, arithmetic
+@cindex arithmetic evaluation
+
+The shell allows arithmetic expressions to be evaluated, as one of
+the shell expansions or by the @code{let} and the @option{-i} option
+to the @code{declare} builtins.
+
+Evaluation is done in fixed-width integers with no check for overflow,
+though division by 0 is trapped and flagged as an error.
+The operators and their precedence, associativity, and values
+are the same as in the C language.
+The following list of operators is grouped into levels of
+equal-precedence operators.
+The levels are listed in order of decreasing precedence. 
+
+@table @code
+
+@item @var{id}++ @var{id}--
+variable post-increment and post-decrement 
+
+@item ++@var{id} --@var{id}
+variable pre-increment and pre-decrement
+
+@item - +
+unary minus and plus
+
+@item ! ~
+logical and bitwise negation
+
+@item **
+exponentiation
+
+@item * / %
+multiplication, division, remainder
+
+@item + -
+addition, subtraction
+
+@item << >>
+left and right bitwise shifts
+
+@item <= >= < >
+comparison
+
+@item == !=
+equality and inequality
+
+@item &
+bitwise AND
+
+@item ^
+bitwise exclusive OR
+
+@item |
+bitwise OR
+
+@item &&
+logical AND
+
+@item ||
+logical OR
+
+@item expr ? expr : expr
+conditional operator
+
+@item = *= /= %= += -= <<= >>= &= ^= |=
+assignment
+
+@item expr1 , expr2
+comma
+@end table
+
+Shell variables are allowed as operands; parameter expansion is
+performed before the expression is evaluated. 
+Within an expression, shell variables may also be referenced by name
+without using the parameter expansion syntax.
+A shell variable that is null or unset evaluates to 0 when referenced
+by name without using the parameter expansion syntax.
+The value of a variable is evaluated as an arithmetic expression
+when it is referenced, or when a variable which has been given the  
+@var{integer} attribute using @samp{declare -i} is assigned a value.
+A null value evaluates to 0.
+A shell variable need not have its integer attribute turned on
+to be used in an expression.
+
+Constants with a leading 0 are interpreted as octal numbers.
+A leading @samp{0x} or @samp{0X} denotes hexadecimal.  Otherwise,
+numbers take the form [@var{base}@code{#}]@var{n}, where @var{base}
+is a decimal number between 2 and 64 representing the arithmetic
+base, and @var{n} is a number in that base.  If @var{base}@code{#} is
+omitted, then base 10 is used.
+The digits greater than 9 are represented by the lowercase letters,
+the uppercase letters, @samp{@@}, and @samp{_}, in that order.
+If @var{base} is less than or equal to 36, lowercase and uppercase
+letters may be used interchangably to represent numbers between 10
+and 35.
+
+Operators are evaluated in order of precedence.  Sub-expressions in
+parentheses are evaluated first and may override the precedence
+rules above.
+
+@node Aliases
+@section Aliases
+@cindex alias expansion
+
+@var{Aliases} allow a string to be substituted for a word when it is used
+as the first word of a simple command.
+The shell maintains a list of aliases that may be set and unset with
+the @code{alias} and @code{unalias} builtin commands.
+
+The first word of each simple command, if unquoted, is checked to see
+if it has an alias.
+If so, that word is replaced by the text of the alias.
+The characters @samp{/}, @samp{$}, @samp{`}, @samp{=} and any of the
+shell metacharacters or quoting characters listed above may not appear
+in an alias name.
+The replacement text may contain any valid
+shell input, including shell metacharacters.
+The first word of the replacement text is tested for
+aliases, but a word that is identical to an alias being expanded
+is not expanded a second time.
+This means that one may alias @code{ls} to @code{"ls -F"},
+for instance, and Bash does not try to recursively expand the
+replacement text. If the last character of the alias value is a
+space or tab character, then the next command word following the
+alias is also checked for alias expansion.
+
+Aliases are created and listed with the @code{alias}
+command, and removed with the @code{unalias} command.
+
+There is no mechanism for using arguments in the replacement text,
+as in @code{csh}.
+If arguments are needed, a shell function should be used
+(@pxref{Shell Functions}).
+
+Aliases are not expanded when the shell is not interactive,
+unless the @code{expand_aliases} shell option is set using
+@code{shopt} (@pxref{Bash Builtins}).
+
+The rules concerning the definition and use of aliases are
+somewhat confusing.  Bash
+always reads at least one complete line
+of input before executing any
+of the commands on that line.  Aliases are expanded when a
+command is read, not when it is executed.  Therefore, an
+alias definition appearing on the same line as another
+command does not take effect until the next line of input is read.
+The commands following the alias definition
+on that line are not affected by the new alias.
+This behavior is also an issue when functions are executed.
+Aliases are expanded when a function definition is read,
+not when the function is executed, because a function definition
+is itself a compound command.  As a consequence, aliases
+defined in a function are not available until after that
+function is executed.  To be safe, always put
+alias definitions on a separate line, and do not use @code{alias}
+in compound commands.
+
+For almost every purpose, shell functions are preferred over aliases.
+
+@node Arrays
+@section Arrays
+@cindex arrays
+
+Bash provides one-dimensional array variables.  Any variable may be used as
+an array; the @code{declare} builtin will explicitly declare an array.
+There is no maximum
+limit on the size of an array, nor any requirement that members
+be indexed or assigned contiguously.  Arrays are zero-based.
+
+An array is created automatically if any variable is assigned to using
+the syntax
+@example
+name[@var{subscript}]=@var{value}
+@end example
+
+@noindent
+The @var{subscript}
+is treated as an arithmetic expression that must evaluate to a number
+greater than or equal to zero.  To explicitly declare an array, use
+@example
+declare -a @var{name}
+@end example
+@noindent
+The syntax
+@example
+declare -a @var{name}[@var{subscript}]
+@end example
+@noindent
+is also accepted; the @var{subscript} is ignored.  Attributes may be
+specified for an array variable using the @code{declare} and
+@code{readonly} builtins.  Each attribute applies to all members of
+an array.
+
+Arrays are assigned to using compound assignments of the form
+@example
+name=(value@var{1} @dots{} value@var{n})
+@end example
+@noindent
+where each
+@var{value} is of the form @code{[[@var{subscript}]=]}@var{string}.  If
+the optional subscript is supplied, that index is assigned to;
+otherwise the index of the element assigned is the last index assigned
+to by the statement plus one.  Indexing starts at zero.
+This syntax is also accepted by the @code{declare}
+builtin.  Individual array elements may be assigned to using the
+@code{name[}@var{subscript}@code{]=}@var{value} syntax introduced above.
+
+Any element of an array may be referenced using
+@code{$@{name[}@var{subscript}@code{]@}}.
+The braces are required to avoid
+conflicts with the shell's filename expansion operators.  If the
+@var{subscript} is @samp{@@} or @samp{*}, the word expands to all members
+of the array @var{name}.  These subscripts differ only when the word
+appears within double quotes.  If the word is double-quoted,
+@code{$@{name[*]@}} expands to a single word with
+the value of each array member separated by the first character of the
+@env{IFS} variable, and @code{$@{name[@@]@}} expands each element of
+@var{name} to a separate word.  When there are no array members,
+@code{$@{name[@@]@}} expands to nothing.  This is analogous to the
+expansion of the special parameters @samp{@@} and @samp{*}. 
+@code{$@{#name[}@var{subscript}@code{]@}} expands to the length of
+@code{$@{name[}@var{subscript}@code{]@}}.
+If @var{subscript} is @samp{@@} or
+@samp{*}, the expansion is the number of elements in the array. 
+Referencing an array variable without a subscript is equivalent to
+referencing element zero. 
+
+The @code{unset} builtin is used to destroy arrays.
+@code{unset} @var{name}[@var{subscript}]
+destroys the array element at index @var{subscript}.
+@code{unset} @var{name}, where @var{name} is an array, removes the
+entire array. A subscript of @samp{*} or @samp{@@} also removes the
+entire array.
+
+The @code{declare}, @code{local}, and @code{readonly}
+builtins each accept a @option{-a}
+option to specify an array.  The @code{read}
+builtin accepts a @option{-a}
+option to assign a list of words read from the standard input
+to an array, and can read values from the standard input into
+individual array elements.  The @code{set} and @code{declare}
+builtins display array values in a way that allows them to be
+reused as input.
+
+@node The Directory Stack
+@section The Directory Stack
+@cindex directory stack
+
+@menu
+* Directory Stack Builtins::           Bash builtin commands to manipulate
+                                       the directory stack.
+@end menu
+
+The directory stack is a list of recently-visited directories.  The
+@code{pushd} builtin adds directories to the stack as it changes
+the current directory, and the @code{popd} builtin removes specified
+directories from the stack and changes the current directory to
+the directory removed.  The @code{dirs} builtin displays the contents
+of the directory stack.
+
+The contents of the directory stack are also visible
+as the value of the @env{DIRSTACK} shell variable.
+
+@node Directory Stack Builtins
+@subsection Directory Stack Builtins
+
+@table @code
+
+@item dirs
+@btindex dirs
+@example
+dirs [+@var{N} | -@var{N}] [-clpv]
+@end example
+Display the list of currently remembered directories.  Directories
+are added to the list with the @code{pushd} command; the
+@code{popd} command removes directories from the list.
+@table @code
+@item +@var{N}
+Displays the @var{N}th directory (counting from the left of the
+list printed by @code{dirs} when invoked without options), starting
+with zero.
+@item -@var{N}
+Displays the @var{N}th directory (counting from the right of the
+list printed by @code{dirs} when invoked without options), starting
+with zero.
+@item -c
+Clears the directory stack by deleting all of the elements.
+@item -l
+Produces a longer listing; the default listing format uses a 
+tilde to denote the home directory.
+@item -p
+Causes @code{dirs} to print the directory stack with one entry per
+line.
+@item -v
+Causes @code{dirs} to print the directory stack with one entry per
+line, prefixing each entry with its index in the stack.
+@end table
+
+@item popd
+@btindex popd
+@example
+popd [+@var{N} | -@var{N}] [-n]
+@end example
+
+Remove the top entry from the directory stack, and @code{cd}
+to the new top directory.
+When no arguments are given, @code{popd}
+removes the top directory from the stack and
+performs a @code{cd} to the new top directory.  The
+elements are numbered from 0 starting at the first directory listed with
+@code{dirs}; i.e., @code{popd} is equivalent to @code{popd +0}.
+@table @code
+@item +@var{N}
+Removes the @var{N}th directory (counting from the left of the
+list printed by @code{dirs}), starting with zero.
+@item -@var{N}
+Removes the @var{N}th directory (counting from the right of the
+list printed by @code{dirs}), starting with zero.
+@item -n
+Suppresses the normal change of directory when removing directories
+from the stack, so that only the stack is manipulated.
+@end table
+
+@btindex pushd
+@item pushd
+@example
+pushd [@var{dir} | @var{+N} | @var{-N}] [-n]
+@end example
+
+Save the current directory on the top of the directory stack
+and then @code{cd} to @var{dir}.
+With no arguments, @code{pushd} exchanges the top two directories.
+
+@table @code
+@item +@var{N}
+Brings the @var{N}th directory (counting from the left of the
+list printed by @code{dirs}, starting with zero) to the top of
+the list by rotating the stack.
+@item -@var{N}
+Brings the @var{N}th directory (counting from the right of the
+list printed by @code{dirs}, starting with zero) to the top of
+the list by rotating the stack.
+@item -n
+Suppresses the normal change of directory when adding directories
+to the stack, so that only the stack is manipulated.
+@item @var{dir}
+Makes the current working directory be the top of the stack, and then
+executes the equivalent of `@code{cd} @var{dir}'.
+@code{cd}s to @var{dir}.
+@end table
+
+@end table
+
+@node Printing a Prompt
+@section Controlling the Prompt
+@cindex prompting
+
+The value of the variable @env{PROMPT_COMMAND} is examined just before
+Bash prints each primary prompt.  If @env{PROMPT_COMMAND} is set and
+has a non-null value, then the
+value is executed just as if it had been typed on the command line.
+
+In addition, the following table describes the special characters which
+can appear in the prompt variables:
+
+@table @code
+@item \a
+A bell character.
+@item \d
+The date, in "Weekday Month Date" format (e.g., "Tue May 26").
+@item \D@{@var{format}@}
+The @var{format} is passed to @code{strftime}(3) and the result is inserted
+into the prompt string; an empty @var{format} results in a locale-specific
+time representation.  The braces are required.
+@item \e
+An escape character.
+@item \h
+The hostname, up to the first `.'.
+@item \H
+The hostname.
+@item \j
+The number of jobs currently managed by the shell.
+@item \l
+The basename of the shell's terminal device name.
+@item \n
+A newline.
+@item \r
+A carriage return.
+@item \s
+The name of the shell, the basename of @code{$0} (the portion
+following the final slash).
+@item \t
+The time, in 24-hour HH:MM:SS format.
+@item \T
+The time, in 12-hour HH:MM:SS format.
+@item \@@
+The time, in 12-hour am/pm format.
+@item \A
+The time, in 24-hour HH:MM format.
+@item \u
+The username of the current user.
+@item \v
+The version of Bash (e.g., 2.00)          
+@item \V
+The release of Bash, version + patchlevel (e.g., 2.00.0)
+@item \w
+The current working directory, with @env{$HOME} abbreviated with a tilde.
+@item \W
+The basename of @env{$PWD}, with @env{$HOME} abbreviated with a tilde.
+@item \!
+The history number of this command.
+@item \#
+The command number of this command.
+@item \$
+If the effective uid is 0, @code{#}, otherwise @code{$}.
+@item \@var{nnn}
+The character whose ASCII code is the octal value @var{nnn}.
+@item \\
+A backslash.
+@item \[
+Begin a sequence of non-printing characters.  This could be used to
+embed a terminal control sequence into the prompt.
+@item \]
+End a sequence of non-printing characters.
+@end table
+
+The command number and the history number are usually different:
+the history number of a command is its position in the history
+list, which may include commands restored from the history file
+(@pxref{Bash History Facilities}), while the command number is
+the position in the sequence of commands executed during the current
+shell session.
+
+After the string is decoded, it is expanded via
+parameter expansion, command substitution, arithmetic
+expansion, and quote removal, subject to the value of the
+@code{promptvars} shell option (@pxref{Bash Builtins}).
+
+@node The Restricted Shell
+@section The Restricted Shell
+@cindex restricted shell
+
+If Bash is started with the name @code{rbash}, or the
+@option{--restricted}
+or
+@option{-r}
+option is supplied at invocation, the shell becomes restricted.
+A restricted shell is used to
+set up an environment more controlled than the standard shell.
+A restricted shell behaves identically to @code{bash}
+with the exception that the following are disallowed or not performed:
+
+@itemize @bullet
+@item
+Changing directories with the @code{cd} builtin.
+@item
+Setting or unsetting the values of the @env{SHELL}, @env{PATH},
+@env{ENV}, or @env{BASH_ENV} variables.
+@item
+Specifying command names containing slashes.
+@item
+Specifying a filename containing a slash as an argument to the @code{.}
+builtin command.
+@item
+Specifying a filename containing a slash as an argument to the @option{-p}
+option to the @code{hash} builtin command.
+@item
+Importing function definitions from the shell environment at startup.
+@item
+Parsing the value of @env{SHELLOPTS} from the shell environment at startup.
+@item
+Redirecting output using the @samp{>}, @samp{>|}, @samp{<>}, @samp{>&},
+@samp{&>}, and @samp{>>} redirection operators.
+@item
+Using the @code{exec} builtin to replace the shell with another command.
+@item
+Adding or deleting builtin commands with the
+@option{-f} and @option{-d} options to the @code{enable} builtin.
+@item
+Using the @code{enable} builtin command to enable disabled shell builtins.
+@item
+Specifying the @option{-p} option to the @code{command} builtin.
+@item
+Turning off restricted mode with @samp{set +r} or @samp{set +o restricted}.
+@end itemize
+
+These restrictions are enforced after any startup files are read.
+
+When a command that is found to be a shell script is executed
+(@pxref{Shell Scripts}), @code{rbash} turns off any restrictions in
+the shell spawned to execute the script.
+
+@node Bash POSIX Mode
+@section Bash POSIX Mode
+@cindex POSIX Mode
+
+Starting Bash with the @option{--posix} command-line option or executing
+@samp{set -o posix} while Bash is running will cause Bash to conform more
+closely to the @sc{posix} 1003.2 standard by changing the behavior to
+match that specified by @sc{posix} in areas where the Bash default differs.
+
+When invoked as @code{sh}, Bash enters @sc{posix} mode after reading the
+startup files.
+
+The following list is what's changed when `@sc{posix} mode' is in effect:
+
+@enumerate
+@item
+When a command in the hash table no longer exists, Bash will re-search
+@env{$PATH} to find the new location.  This is also available with
+@samp{shopt -s checkhash}.
+
+@item
+The message printed by the job control code and builtins when a job
+exits with a non-zero status is `Done(status)'.
+
+@item
+The message printed by the job control code and builtins when a job
+is stopped is `Stopped(@var{signame})', where @var{signame} is, for
+example, @code{SIGTSTP}.
+
+@item
+Reserved words may not be aliased.
+
+@item
+The @sc{posix} 1003.2 @env{PS1} and @env{PS2} expansions of @samp{!} to
+the history number and @samp{!!} to @samp{!} are enabled,
+and parameter expansion is performed on the values of @env{PS1} and
+@env{PS2} regardless of the setting of the @code{promptvars} option.
+
+@item
+The @sc{posix} 1003.2 startup files are executed (@env{$ENV}) rather than
+the normal Bash files.
+
+@item
+Tilde expansion is only performed on assignments preceding a command
+name, rather than on all assignment statements on the line.
+
+@item
+The default history file is @file{~/.sh_history} (this is the
+default value of @env{$HISTFILE}).
+
+@item
+The output of @samp{kill -l} prints all the signal names on a single line,
+separated by spaces, without the @samp{SIG} prefix.
+
+@item
+The @code{kill} builtin does not accept signal names with a @samp{SIG}
+prefix.
+
+@item
+Non-interactive shells exit if @var{filename} in @code{.} @var{filename}
+is not found.
+
+@item
+Non-interactive shells exit if a syntax error in an arithmetic expansion
+results in an invalid expression.
+
+@item
+Redirection operators do not perform filename expansion on the word
+in the redirection unless the shell is interactive.
+
+@item
+Redirection operators do not perform word splitting on the word in the
+redirection.
+
+@item
+Function names must be valid shell @code{name}s.  That is, they may not
+contain characters other than letters, digits, and underscores, and
+may not start with a digit.  Declaring a function with an invalid name
+causes a fatal syntax error in non-interactive shells.
+
+@item
+@sc{posix} 1003.2 `special' builtins are found before shell functions
+during command lookup.
+
+@item
+If a @sc{posix} 1003.2 special builtin returns an error status, a
+non-interactive shell exits.  The fatal errors are those listed in
+the POSIX.2 standard, and include things like passing incorrect options,
+redirection errors, variable assignment errors for assignments preceding
+the command name, and so on.
+
+@item
+If the @code{cd} builtin finds a directory to change to
+using @env{$CDPATH}, the
+value it assigns to the @env{PWD} variable does not contain any
+symbolic links, as if @samp{cd -P} had been executed.
+
+@item
+If @env{CDPATH} is set, the @code{cd} builtin will not implicitly
+append the current directory to it.  This means that @code{cd} will
+fail if no valid directory name can be constructed from
+any of the entries in @env{$CDPATH}, even if the a directory with
+the same name as the name given as an argument to @code{cd} exists
+in the current directory.
+
+@item
+A non-interactive shell exits with an error status if a variable
+assignment error occurs when no command name follows the assignment
+statements.
+A variable assignment error occurs, for example, when trying to assign
+a value to a readonly variable.
+
+@item
+A non-interactive shell exits with an error status if the iteration
+variable in a @code{for} statement or the selection variable in a
+@code{select} statement is a readonly variable.
+
+@item
+Process substitution is not available.
+
+@item
+Assignment statements preceding @sc{posix} 1003.2 special builtins
+persist in the shell environment after the builtin completes.
+
+@item
+Assignment statements preceding shell function calls persist in the
+shell environment after the function returns, as if a @sc{posix}
+special builtin command had been executed.
+
+@item
+The @code{export} and @code{readonly} builtin commands display their
+output in the format required by @sc{posix} 1003.2.
+
+@item
+The @code{trap} builtin displays signal names without the leading
+@code{SIG}.
+
+@item
+The @code{trap} builtin doesn't check the first argument for a possible
+signal specification and revert the signal handling to the original
+disposition if it is, unless that argument consists solely of digits and
+is a valid signal number.  If users want to reset the handler for a given
+signal to the original disposition, they should use @samp{-} as the
+first argument.
+
+@item
+The @code{.} and @code{source} builtins do not search the current directory
+for the filename argument if it is not found by searching @env{PATH}.
+
+@item
+Subshells spawned to execute command substitutions inherit the value of
+the @option{-e} option from the parent shell.  When not in @sc{posix} mode,
+Bash clears the @option{-e} option in such subshells.
+
+@item
+Alias expansion is always enabled, even in non-interactive shells.
+
+@item
+When the @code{alias} builtin displays alias definitions, it does not
+display them with a leading @samp{alias } unless the @option{-p} option
+is supplied.
+
+@item
+When the @code{set} builtin is invoked without options, it does not display
+shell function names and definitions.
+
+@item
+When the @code{set} builtin is invoked without options, it displays
+variable values without quotes, unless they contain shell metacharacters,
+even if the result contains nonprinting characters.
+
+@item
+When the @code{cd} builtin is invoked in @var{logical} mode, and the pathname
+constructed from @code{$PWD} and the directory name supplied as an argument
+does not refer to an existing directory, @code{cd} will fail instead of
+falling back to @var{physical} mode.
+
+@item
+When listing the history, the @code{fc} builtin does not include an
+indication of whether or not a history entry has been modified.
+
+@end enumerate
+
+There is other @sc{posix} 1003.2 behavior that Bash does not implement.
+Specifically:
+
+@enumerate
+@item
+Assignment statements affect the execution environment of all
+builtins, not just special ones.
+
+@item
+When a subshell is created to execute a shell script with execute permission,
+but without a leading @samp{#!}, Bash sets @code{$0} to the full pathname of
+the script as found by searching @code{$PATH}, rather than the command as
+typed by the user.
+
+@item
+When using @samp{.} to source a shell script found in @code{$PATH}, bash
+checks execute permission bits rather than read permission bits, just as
+if it were searching for a command.
+
+@end enumerate
+
+@node Job Control
+@chapter Job Control
+
+This chapter discusses what job control is, how it works, and how
+Bash allows you to access its facilities.
+
+@menu
+* Job Control Basics::         How job control works.
+* Job Control Builtins::       Bash builtin commands used to interact
+                               with job control.
+* Job Control Variables::      Variables Bash uses to customize job
+                               control.
+@end menu
+
+@node Job Control Basics
+@section Job Control Basics
+@cindex job control
+@cindex foreground
+@cindex background
+@cindex suspending jobs
+
+Job control
+refers to the ability to selectively stop (suspend)
+the execution of processes and continue (resume)
+their execution at a later point.  A user typically employs
+this facility via an interactive interface supplied jointly
+by the system's terminal driver and Bash.
+
+The shell associates a @var{job} with each pipeline.  It keeps a
+table of currently executing jobs, which may be listed with the
+@code{jobs} command.  When Bash starts a job
+asynchronously, it prints a line that looks
+like:
+@example
+[1] 25647
+@end example
+@noindent
+indicating that this job is job number 1 and that the process @sc{id}
+of the last process in the pipeline associated with this job is
+25647.  All of the processes in a single pipeline are members of
+the same job.  Bash uses the @var{job} abstraction as the
+basis for job control. 
+
+To facilitate the implementation of the user interface to job
+control, the operating system maintains the notion of a current terminal
+process group @sc{id}.  Members of this process group (processes whose
+process group @sc{id} is equal to the current terminal process group
+@sc{id}) receive keyboard-generated signals such as @code{SIGINT}. 
+These processes are said to be in the foreground.  Background
+processes are those whose process group @sc{id} differs from the
+terminal's; such processes are immune to keyboard-generated
+signals.  Only foreground processes are allowed to read from or
+write to the terminal.  Background processes which attempt to
+read from (write to) the terminal are sent a @code{SIGTTIN}
+(@code{SIGTTOU}) signal by the terminal driver, which, unless
+caught, suspends the process. 
+
+If the operating system on which Bash is running supports
+job control, Bash contains facilities to use it.  Typing the
+@var{suspend} character (typically @samp{^Z}, Control-Z) while a
+process is running causes that process to be stopped and returns
+control to Bash.  Typing the @var{delayed suspend} character
+(typically @samp{^Y}, Control-Y) causes the process to be stopped
+when it attempts to read input from the terminal, and control to
+be returned to Bash.  The user then manipulates the state of
+this job, using the @code{bg} command to continue it in the
+background, the @code{fg} command to continue it in the
+foreground, or the @code{kill} command to kill it.  A @samp{^Z}
+takes effect immediately, and has the additional side effect of
+causing pending output and typeahead to be discarded. 
+
+There are a number of ways to refer to a job in the shell.  The
+character @samp{%} introduces a job name.
+
+Job number @code{n} may be referred to as @samp{%n}.
+The symbols @samp{%%} and
+@samp{%+} refer to the shell's notion of the current job, which
+is the last job stopped while it was in the foreground or started
+in the background.  The
+previous job may be referenced using @samp{%-}.  In output
+pertaining to jobs (e.g., the output of the @code{jobs} command),
+the current job is always flagged with a @samp{+}, and the
+previous job with a @samp{-}. 
+
+A job may also be referred to
+using a prefix of the name used to start it, or using a substring
+that appears in its command line.  For example, @samp{%ce} refers
+to a stopped @code{ce} job. Using @samp{%?ce}, on the
+other hand, refers to any job containing the string @samp{ce} in
+its command line.  If the prefix or substring matches more than one job,
+Bash reports an error.
+
+Simply naming a job can be used to bring it into the foreground:
+@samp{%1} is a synonym for @samp{fg %1}, bringing job 1 from the
+background into the foreground.  Similarly, @samp{%1 &} resumes
+job 1 in the background, equivalent to @samp{bg %1}
+
+The shell learns immediately whenever a job changes state. 
+Normally, Bash waits until it is about to print a prompt
+before reporting changes in a job's status so as to not interrupt
+any other output.
+If the @option{-b} option to the @code{set} builtin is enabled,
+Bash reports such changes immediately (@pxref{The Set Builtin}).
+Any trap on @code{SIGCHLD} is executed for each child process
+that exits.
+
+If an attempt to exit Bash is made while jobs are stopped, the
+shell prints a message warning that there are stopped jobs.
+The @code{jobs} command may then be used to inspect their status.
+If a second attempt to exit is made without an intervening command,
+Bash does not print another warning, and the stopped jobs are terminated.
+
+@node Job Control Builtins
+@section Job Control Builtins
+
+@table @code
+
+@item bg
+@btindex bg
+@example
+bg [@var{jobspec}]
+@end example
+Resume the suspended job @var{jobspec} in the background, as if it
+had been started with @samp{&}.
+If @var{jobspec} is not supplied, the current job is used.
+The return status is zero unless it is run when job control is not
+enabled, or, when run with job control enabled, if @var{jobspec} was
+not found or @var{jobspec} specifies a job that was started without
+job control.
+
+@item fg
+@btindex fg
+@example
+fg [@var{jobspec}]
+@end example
+Resume the job @var{jobspec} in the foreground and make it the current job.
+If @var{jobspec} is not supplied, the current job is used.
+The return status is that of the command placed into the foreground,
+or non-zero if run when job control is disabled or, when run with
+job control enabled, @var{jobspec} does not specify a valid job or
+@var{jobspec} specifies a job that was started without job control.
+
+@item jobs
+@btindex jobs
+@example
+jobs [-lnprs] [@var{jobspec}]
+jobs -x @var{command} [@var{arguments}]
+@end example
+
+The first form lists the active jobs.  The options have the
+following meanings:
+
+@table @code
+@item -l
+List process @sc{id}s in addition to the normal information.
+
+@item -n
+Display information only about jobs that have changed status since
+the user was last notified of their status.
+
+@item -p
+List only the process @sc{id} of the job's process group leader.
+
+@item -r
+Restrict output to running jobs.
+
+@item -s
+Restrict output to stopped jobs.
+@end table
+
+If @var{jobspec} is given,
+output is restricted to information about that job. 
+If @var{jobspec} is not supplied, the status of all jobs is
+listed.
+
+If the @option{-x} option is supplied, @code{jobs} replaces any
+@var{jobspec} found in @var{command} or @var{arguments} with the
+corresponding process group @sc{id}, and executes @var{command},
+passing it @var{argument}s, returning its exit status. 
+
+@item kill
+@btindex kill
+@example
+kill [-s @var{sigspec}] [-n @var{signum}] [-@var{sigspec}] @var{jobspec} or @var{pid}
+kill -l [@var{exit_status}]
+@end example
+Send a signal specified by @var{sigspec} or @var{signum} to the process
+named by job specification @var{jobspec} or process @sc{id} @var{pid}.
+@var{sigspec} is either a case-insensitive signal name such as
+@code{SIGINT} (with or without the @code{SIG} prefix)
+or a signal number; @var{signum} is a signal number.
+If @var{sigspec} and @var{signum} are not present, @code{SIGTERM} is used.
+The @option{-l} option lists the signal names.
+If any arguments are supplied when @option{-l} is given, the names of the
+signals corresponding to the arguments are listed, and the return status
+is zero.
+@var{exit_status} is a number specifying a signal number or the exit
+status of a process terminated by a signal.
+The return status is zero if at least one signal was successfully sent,
+or non-zero if an error occurs or an invalid option is encountered.
+
+@item wait
+@btindex wait
+@example
+wait [@var{jobspec} or @var{pid}]
+@end example
+Wait until the child process specified by process @sc{id} @var{pid} or job
+specification @var{jobspec} exits and return the exit status of the 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 neither @var{jobspec} nor @var{pid} specifies an active child process
+of the shell, the return status is 127.
+
+@item disown
+@btindex disown
+@example
+disown [-ar] [-h] [@var{jobspec} @dots{}]
+@end example
+Without options, each @var{jobspec} is removed from the table of
+active jobs.
+If the @option{-h} option is given, the job is not removed from the table,
+but is marked so that @code{SIGHUP} is not sent to the job if the shell
+receives a @code{SIGHUP}.
+If @var{jobspec} is not present, and neither the @option{-a} nor @option{-r}
+option is supplied, the current job is used.
+If no @var{jobspec} is supplied, the @option{-a} option means to remove or
+mark all jobs; the @option{-r} option without a @var{jobspec}
+argument restricts operation to running jobs.
+
+@item suspend
+@btindex suspend
+@example
+suspend [-f]
+@end example
+Suspend the execution of this shell until it receives a
+@code{SIGCONT} signal.  The @option{-f} option means to suspend
+even if the shell is a login shell.
+
+@end table
+
+When job control is not active, the @code{kill} and @code{wait}
+builtins do not accept @var{jobspec} arguments.  They must be
+supplied process @sc{id}s.
+
+@node Job Control Variables
+@section Job Control Variables
+
+@vtable @code
+
+@item auto_resume
+This variable controls how the shell interacts with the user and
+job control.  If this variable exists then single word simple
+commands without redirections are treated as candidates for resumption
+of an existing job.  There is no ambiguity allowed; if there is
+more than one job beginning with the string typed, then
+the most recently accessed job will be selected.
+The name of a stopped job, in this context, is the command line
+used to start it.  If this variable is set to the value @samp{exact},
+the string supplied must match the name of a stopped job exactly;
+if set to @samp{substring},
+the string supplied needs to match a substring of the name of a
+stopped job.  The @samp{substring} value provides functionality
+analogous to the @samp{%?} job @sc{id} (@pxref{Job Control Basics}).
+If set to any other value, the supplied string must
+be a prefix of a stopped job's name; this provides functionality
+analogous to the @samp{%} job @sc{id}.
+
+@end vtable
+
+@set readline-appendix
+@set history-appendix
+@cindex Readline, how to use
+@include rluser.texi
+@cindex History, how to use
+@include hsuser.texi
+@clear readline-appendix
+@clear history-appendix
+
+@node Installing Bash
+@chapter Installing Bash
+
+This chapter provides basic instructions for installing Bash on
+the various supported platforms.  The distribution supports the
+@sc{gnu} operating systems, nearly every version of Unix, and several
+non-Unix systems such as BeOS and Interix.
+Other independent ports exist for
+@sc{ms-dos}, @sc{os/2}, and Windows platforms.
+
+@menu
+* Basic Installation:: Installation instructions.
+
+* Compilers and Options::      How to set special options for various
+                               systems.
+
+* Compiling For Multiple Architectures::       How to compile Bash for more
+                                               than one kind of system from
+                                               the same source tree.
+
+* Installation Names:: How to set the various paths used by the installation.
+
+* Specifying the System Type:: How to configure Bash for a particular system.
+
+* Sharing Defaults::   How to share default configuration values among GNU
+                       programs.
+
+* Operation Controls:: Options recognized by the configuration program.
+
+* Optional Features::  How to enable and disable optional features when
+                       building Bash.
+@end menu
+
+@node Basic Installation
+@section Basic Installation
+@cindex installation
+@cindex configuration
+@cindex Bash installation
+@cindex Bash configuration
+
+These are installation instructions for Bash.
+
+The simplest way to compile Bash is:
+
+@enumerate
+@item
+@code{cd} to the directory containing the source code and type
+@samp{./configure} to configure Bash for your system.  If you're
+using @code{csh} on an old version of System V, you might need to
+type @samp{sh ./configure} instead to prevent @code{csh} from trying
+to execute @code{configure} itself.
+
+Running @code{configure} takes some time.
+While running, it prints messages telling which features it is
+checking for.
+
+@item
+Type @samp{make} to compile Bash and build the @code{bashbug} bug
+reporting script.
+
+@item
+Optionally, type @samp{make tests} to run the Bash test suite.
+
+@item
+Type @samp{make install} to install @code{bash} and @code{bashbug}.
+This will also install the manual pages and Info file.
+
+@end enumerate
+
+The @code{configure} shell script attempts to guess correct
+values for various system-dependent variables used during
+compilation.  It uses those values to create a @file{Makefile} in
+each directory of the package (the top directory, the
+@file{builtins}, @file{doc}, and @file{support} directories,
+each directory under @file{lib}, and several others).  It also creates a
+@file{config.h} file containing system-dependent definitions. 
+Finally, it creates a shell script named @code{config.status} that you
+can run in the future to recreate the current configuration, a
+file @file{config.cache} that saves the results of its tests to
+speed up reconfiguring, and a file @file{config.log} containing
+compiler output (useful mainly for debugging @code{configure}). 
+If at some point
+@file{config.cache} contains results you don't want to keep, you
+may remove or edit it. 
+
+To find out more about the options and arguments that the
+@code{configure} script understands, type 
+
+@example
+bash-2.04$ ./configure --help
+@end example
+
+@noindent
+at the Bash prompt in your Bash source directory.
+
+If you need to do unusual things to compile Bash, please
+try to figure out how @code{configure} could check whether or not
+to do them, and mail diffs or instructions to
+@email{bash-maintainers@@gnu.org} so they can be
+considered for the next release.
+
+The file @file{configure.in} is used to create @code{configure}
+by a program called Autoconf.  You only need
+@file{configure.in} if you want to change it or regenerate
+@code{configure} using a newer version of Autoconf.  If
+you do this, make sure you are using Autoconf version 2.50 or
+newer.
+
+You can remove the program binaries and object files from the
+source code directory by typing @samp{make clean}.  To also remove the
+files that @code{configure} created (so you can compile Bash for
+a different kind of computer), type @samp{make distclean}.
+
+@node Compilers and Options
+@section Compilers and Options
+
+Some systems require unusual options for compilation or linking
+that the @code{configure} script does not know about.  You can
+give @code{configure} initial values for variables by setting
+them in the environment.  Using a Bourne-compatible shell, you
+can do that on the command line like this:
+
+@example
+CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
+@end example
+
+On systems that have the @code{env} program, you can do it like this:
+
+@example
+env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
+@end example
+
+The configuration process uses GCC to build Bash if it
+is available.
+
+@node Compiling For Multiple Architectures
+@section Compiling For Multiple Architectures
+
+You can compile Bash for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory.  To do this, you must use a version of @code{make} that
+supports the @code{VPATH} variable, such as GNU @code{make}.
+@code{cd} to the
+directory where you want the object files and executables to go and run
+the @code{configure} script from the source directory.  You may need to
+supply the @option{--srcdir=PATH} argument to tell @code{configure} where the
+source files are.  @code{configure} automatically checks for the
+source code in the directory that @code{configure} is in and in `..'.
+
+If you have to use a @code{make} that does not supports the @code{VPATH}
+variable, you can compile Bash for one architecture at a
+time in the source code directory.  After you have installed
+Bash for one architecture, use @samp{make distclean} before
+reconfiguring for another architecture.
+
+Alternatively, if your system supports symbolic links, you can use the
+@file{support/mkclone} script to create a build tree which has
+symbolic links back to each file in the source directory.  Here's an
+example that creates a build directory in the current directory from a
+source directory @file{/usr/gnu/src/bash-2.0}:
+
+@example
+bash /usr/gnu/src/bash-2.0/support/mkclone -s /usr/gnu/src/bash-2.0 .
+@end example
+
+@noindent
+The @code{mkclone} script requires Bash, so you must have already built
+Bash for at least one architecture before you can create build
+directories for other architectures.
+
+@node Installation Names
+@section Installation Names
+
+By default, @samp{make install} will install into
+@file{/usr/local/bin}, @file{/usr/local/man}, etc.  You can
+specify an installation prefix other than @file{/usr/local} by
+giving @code{configure} the option @option{--prefix=@var{PATH}},
+or by specifying a value for the @code{DESTDIR} @samp{make}
+variable when running @samp{make install}.
+
+You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. 
+If you give @code{configure} the option
+@option{--exec-prefix=@var{PATH}}, @samp{make install} will use
+@var{PATH} as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix. 
+
+@node Specifying the System Type
+@section Specifying the System Type
+
+There may be some features @code{configure} can not figure out
+automatically, but need to determine by the type of host Bash
+will run on.  Usually @code{configure} can figure that
+out, but if it prints a message saying it can not guess the host
+type, give it the @option{--host=TYPE} option.  @samp{TYPE} can
+either be a short name for the system type, such as @samp{sun4},
+or a canonical name with three fields: @samp{CPU-COMPANY-SYSTEM}
+(e.g., @samp{i386-unknown-freebsd4.2}).
+
+See the file @file{support/config.sub} for the possible
+values of each field. 
+
+@node Sharing Defaults
+@section Sharing Defaults
+
+If you want to set default values for @code{configure} scripts to
+share, you can create a site shell script called
+@code{config.site} that gives default values for variables like
+@code{CC}, @code{cache_file}, and @code{prefix}.  @code{configure}
+looks for @file{PREFIX/share/config.site} if it exists, then
+@file{PREFIX/etc/config.site} if it exists.  Or, you can set the
+@code{CONFIG_SITE} environment variable to the location of the site
+script.  A warning: the Bash @code{configure} looks for a site script,
+but not all @code{configure} scripts do.
+
+@node Operation Controls
+@section Operation Controls
+
+@code{configure} recognizes the following options to control how it
+operates.
+
+@table @code
+
+@item --cache-file=@var{file}
+Use and save the results of the tests in
+@var{file} instead of @file{./config.cache}.  Set @var{file} to
+@file{/dev/null} to disable caching, for debugging
+@code{configure}. 
+
+@item --help
+Print a summary of the options to @code{configure}, and exit.
+
+@item --quiet
+@itemx --silent
+@itemx -q
+Do not print messages saying which checks are being made.
+
+@item --srcdir=@var{dir}
+Look for the Bash source code in directory @var{dir}.  Usually
+@code{configure} can determine that directory automatically.
+
+@item --version
+Print the version of Autoconf used to generate the @code{configure}
+script, and exit.
+@end table
+
+@code{configure} also accepts some other, not widely used, boilerplate
+options.  @samp{configure --help} prints the complete list.
+
+@node Optional Features
+@section Optional Features
+
+The Bash @code{configure} has a number of @option{--enable-@var{feature}}
+options, where @var{feature} indicates an optional part of Bash.
+There are also several @option{--with-@var{package}} options,
+where @var{package} is something like @samp{bash-malloc} or @samp{purify}.
+To turn off the default use of a package, use
+@option{--without-@var{package}}.  To configure Bash without a feature
+that is enabled by default, use @option{--disable-@var{feature}}.
+
+Here is a complete list of the @option{--enable-} and
+@option{--with-} options that the Bash @code{configure} recognizes. 
+
+@table @code
+@item --with-afs
+Define if you are using the Andrew File System from Transarc.
+
+@item --with-bash-malloc
+Use the Bash version of
+@code{malloc} in @file{lib/malloc/malloc.c}.  This is not the same
+@code{malloc} that appears in @sc{gnu} libc, but an older version
+derived from the 4.2 @sc{bsd} @code{malloc}.  This @code{malloc} is
+very fast, but wastes some space on each allocation.
+This option is enabled by default.
+The @file{NOTES} file contains a list of systems for
+which this should be turned off, and @code{configure} disables this
+option automatically for a number of systems.
+
+@item --with-curses
+Use the curses library instead of the termcap library.  This should
+be supplied if your system has an inadequate or incomplete termcap
+database.
+
+@item --with-gnu-malloc
+A synonym for @code{--with-bash-malloc}.
+
+@item --with-installed-readline[=@var{PREFIX}]
+Define this to make Bash link with a locally-installed version of Readline
+rather than the version in @file{lib/readline}.  This works only with
+Readline 4.3 and later versions.  If @var{PREFIX} is @code{yes} or not
+supplied, @code{configure} uses the values of the make variables
+@code{includedir} and @code{libdir}, which are subdirectories of @code{prefix}
+by default, to find the installed version of Readline if it is not in
+the standard system include and library directories.
+If @var{PREFIX} is @code{no}, Bash links with the version in
+@file{lib/readline}.
+If @var{PREFIX} is set to any other value, @code{configure} treats it as
+a directory pathname and looks for
+the installed version of Readline in subdirectories of that directory
+(include files in @var{PREFIX}/@code{include} and the library in
+@var{PREFIX}/@code{lib}).
+
+@item --with-purify
+Define this to use the Purify memory allocation checker from Rational
+Software.
+
+@item --enable-minimal-config
+This produces a shell with minimal features, close to the historical
+Bourne shell.
+@end table
+
+There are several @option{--enable-} options that alter how Bash is
+compiled and linked, rather than changing run-time features.
+
+@table @code
+@item --enable-largefile
+Enable support for @uref{http://www.sas.com/standards/large_file/x_open.20Mar96.html,
+large files} if the operating system requires special compiler options
+to build programs which can access large files.  This is enabled by
+default, if the operating system provides large file support.
+
+@item --enable-profiling
+This builds a Bash binary that produces profiling information to be
+processed by @code{gprof} each time it is executed.
+
+@item --enable-static-link
+This causes Bash to be linked statically, if @code{gcc} is being used.
+This could be used to build a version to use as root's shell.
+@end table
+
+The @samp{minimal-config} option can be used to disable all of
+the following options, but it is processed first, so individual
+options may be enabled using @samp{enable-@var{feature}}. 
+
+All of the following options except for @samp{disabled-builtins} and
+@samp{xpg-echo-default} are
+enabled by default, unless the operating system does not provide the
+necessary support.
+
+@table @code
+@item --enable-alias
+Allow alias expansion and include the @code{alias} and @code{unalias}
+builtins (@pxref{Aliases}).
+
+@item --enable-arith-for-command
+Include support for the alternate form of the @code{for} command
+that behaves like the C language @code{for} statement
+(@pxref{Looping Constructs}).
+
+@item --enable-array-variables
+Include support for one-dimensional array shell variables
+(@pxref{Arrays}).
+
+@item --enable-bang-history
+Include support for @code{csh}-like history substitution
+(@pxref{History Interaction}).
+
+@item --enable-brace-expansion
+Include @code{csh}-like brace expansion
+( @code{b@{a,b@}c} @expansion{} @code{bac bbc} ).
+See @ref{Brace Expansion}, for a complete description.
+
+@item --enable-command-timing
+Include support for recognizing @code{time} as a reserved word and for
+displaying timing statistics for the pipeline following @code{time}
+(@pxref{Pipelines}).
+This allows pipelines as well as shell builtins and functions to be timed.
+
+@item --enable-cond-command
+Include support for the @code{[[} conditional command.
+(@pxref{Conditional Constructs}).
+
+@item --enable-cond-regexp
+Include support for matching POSIX regular expressions using the
+@samp{=~} binary operator in the @code{[[} conditional command.
+(@pxref{Conditional Constructs}).
+
+@item --enable-directory-stack
+Include support for a @code{csh}-like directory stack and the
+@code{pushd}, @code{popd}, and @code{dirs} builtins
+(@pxref{The Directory Stack}).
+
+@item --enable-disabled-builtins
+Allow builtin commands to be invoked via @samp{builtin xxx}
+even after @code{xxx} has been disabled using @samp{enable -n xxx}.
+See @ref{Bash Builtins}, for details of the @code{builtin} and
+@code{enable} builtin commands.
+
+@item --enable-dparen-arithmetic
+Include support for the @code{((@dots{}))} command
+(@pxref{Conditional Constructs}).
+
+@item --enable-extended-glob
+Include support for the extended pattern matching features described
+above under @ref{Pattern Matching}.
+
+@item --enable-help-builtin
+Include the @code{help} builtin, which displays help on shell builtins and
+variables (@pxref{Bash Builtins}).
+
+@item --enable-history
+Include command history and the @code{fc} and @code{history}
+builtin commands (@pxref{Bash History Facilities}).
+
+@item --enable-job-control
+This enables the job control features (@pxref{Job Control}),
+if the operating system supports them.
+
+@item --enable-multibyte
+This enables support for multibyte characters if the operating
+system provides the necessary support.
+
+@item --enable-net-redirections
+This enables the special handling of filenames of the form
+@code{/dev/tcp/@var{host}/@var{port}} and
+@code{/dev/udp/@var{host}/@var{port}}
+when used in redirections (@pxref{Redirections}).
+
+@item --enable-process-substitution
+This enables process substitution (@pxref{Process Substitution}) if
+the operating system provides the necessary support.
+
+@item --enable-prompt-string-decoding
+Turn on the interpretation of a number of backslash-escaped characters
+in the @env{$PS1}, @env{$PS2}, @env{$PS3}, and @env{$PS4} prompt
+strings.  See @ref{Printing a Prompt}, for a complete list of prompt
+string escape sequences.
+
+@item --enable-progcomp
+Enable the programmable completion facilities
+(@pxref{Programmable Completion}).
+If Readline is not enabled, this option has no effect.
+
+@item --enable-readline
+Include support for command-line editing and history with the Bash
+version of the Readline library (@pxref{Command Line Editing}).
+
+@item --enable-restricted
+Include support for a @dfn{restricted shell}.  If this is enabled, Bash,
+when called as @code{rbash}, enters a restricted mode.  See
+@ref{The Restricted Shell}, for a description of restricted mode.
+
+@item --enable-select
+Include the @code{select} builtin, which allows the generation of simple
+menus (@pxref{Conditional Constructs}).
+
+@item --enable-usg-echo-default
+A synonym for @code{--enable-xpg-echo-default}.
+
+@item --enable-xpg-echo-default
+Make the @code{echo} builtin expand backslash-escaped characters by default,
+without requiring the @option{-e} option.
+This sets the default value of the @code{xpg_echo} shell option to @code{on},
+which makes the Bash @code{echo} behave more like the version specified in
+the Single Unix Specification, version 2.
+@xref{Bash Builtins}, for a description of the escape sequences that
+@code{echo} recognizes.
+
+@end table
+
+The file @file{config-top.h} contains C Preprocessor
+@samp{#define} statements for options which are not settable from
+@code{configure}.
+Some of these are not meant to be changed; beware of the consequences if
+you do.
+Read the comments associated with each definition for more
+information about its effect.
+
+@node Reporting Bugs
+@appendix Reporting Bugs
+
+Please report all bugs you find in Bash.
+But first, you should
+make sure that it really is a bug, and that it appears in the latest
+version of Bash.
+The latest version of Bash is always available for FTP from
+@uref{ftp://ftp.gnu.org/pub/bash/}.
+
+Once you have determined that a bug actually exists, use the
+@code{bashbug} command to submit a bug report.
+If you have a fix, you are encouraged to mail that as well!
+Suggestions and `philosophical' bug reports may be mailed
+to @email{bug-bash@@gnu.org} or posted to the Usenet
+newsgroup @code{gnu.bash.bug}.
+
+All bug reports should include:
+@itemize @bullet
+@item
+The version number of Bash.
+@item
+The hardware and operating system.
+@item
+The compiler used to compile Bash.
+@item
+A description of the bug behaviour.
+@item
+A short script or `recipe' which exercises the bug and may be used
+to reproduce it.
+@end itemize
+
+@noindent
+@code{bashbug} inserts the first three items automatically into
+the template it provides for filing a bug report.
+
+Please send all reports concerning this manual to
+@email{chet@@po.CWRU.Edu}.
+
+@node Major Differences From The Bourne Shell
+@appendix Major Differences From The Bourne Shell
+
+Bash implements essentially the same grammar, parameter and
+variable expansion, redirection, and quoting as the Bourne Shell. 
+Bash uses the @sc{posix} 1003.2 standard as the specification of
+how these features are to be implemented.  There are some
+differences between the traditional Bourne shell and Bash; this
+section quickly details the differences of significance.  A
+number of these differences are explained in greater depth in
+previous sections.
+This section uses the version of @code{sh} included in SVR4.2 as
+the baseline reference.
+
+@itemize @bullet
+
+@item
+Bash is @sc{posix}-conformant, even where the @sc{posix} specification
+differs from traditional @code{sh} behavior (@pxref{Bash POSIX Mode}).
+
+@item
+Bash has multi-character invocation options (@pxref{Invoking Bash}).
+
+@item
+Bash has command-line editing (@pxref{Command Line Editing}) and
+the @code{bind} builtin.
+
+@item
+Bash provides a programmable word completion mechanism
+(@pxref{Programmable Completion}), and two builtin commands,
+@code{complete} and @code{compgen}, to manipulate it.
+
+@item
+Bash has command history (@pxref{Bash History Facilities}) and the
+@code{history} and @code{fc} builtins to manipulate it.
+The Bash history list maintains timestamp information and uses the
+value of the @code{HISTTIMEFORMAT} variable to display it.
+
+@item
+Bash implements @code{csh}-like history expansion
+(@pxref{History Interaction}).
+
+@item
+Bash has one-dimensional array variables (@pxref{Arrays}), and the
+appropriate variable expansions and assignment syntax to use them.
+Several of the Bash builtins take options to act on arrays.
+Bash provides a number of built-in array variables.
+
+@item
+The @code{$'@dots{}'} quoting syntax, which expands ANSI-C
+backslash-escaped characters in the text between the single quotes,
+is supported (@pxref{ANSI-C Quoting}).
+
+@item
+Bash supports the @code{$"@dots{}"} quoting syntax to do
+locale-specific translation of the characters between the double
+quotes.  The @option{-D}, @option{--dump-strings}, and @option{--dump-po-strings}
+invocation options list the translatable strings found in a script
+(@pxref{Locale Translation}).
+
+@item
+Bash implements the @code{!} keyword to negate the return value of
+a pipeline (@pxref{Pipelines}).
+Very useful when an @code{if} statement needs to act only if a test fails.
+
+@item
+Bash has the @code{time} reserved word and command timing (@pxref{Pipelines}).
+The display of the timing statistics may be controlled with the
+@env{TIMEFORMAT} variable.
+
+@item
+Bash implements the @code{for (( @var{expr1} ; @var{expr2} ; @var{expr3} ))}
+arithmetic for command, similar to the C language (@pxref{Looping Constructs}).
+
+@item
+Bash includes the @code{select} compound command, which allows the
+generation of simple menus (@pxref{Conditional Constructs}).
+
+@item
+Bash includes the @code{[[} compound command, which makes conditional
+testing part of the shell grammar (@pxref{Conditional Constructs}).
+
+@item
+Bash includes brace expansion (@pxref{Brace Expansion}) and tilde
+expansion (@pxref{Tilde Expansion}).
+
+@item
+Bash implements command aliases and the @code{alias} and @code{unalias}
+builtins (@pxref{Aliases}).
+
+@item
+Bash provides shell arithmetic, the @code{((} compound command
+(@pxref{Conditional Constructs}),
+and arithmetic expansion (@pxref{Shell Arithmetic}).
+
+@item
+Variables present in the shell's initial environment are automatically
+exported to child processes.  The Bourne shell does not normally do
+this unless the variables are explicitly marked using the @code{export}
+command.
+
+@item
+Bash includes the @sc{posix} pattern removal @samp{%}, @samp{#}, @samp{%%}
+and @samp{##} expansions to remove leading or trailing substrings from
+variable values (@pxref{Shell Parameter Expansion}).
+
+@item
+The expansion @code{$@{#xx@}}, which returns the length of @code{$@{xx@}},
+is supported (@pxref{Shell Parameter Expansion}).
+
+@item
+The expansion @code{$@{var:}@var{offset}@code{[:}@var{length}@code{]@}},
+which expands to the substring of @code{var}'s value of length
+@var{length}, beginning at @var{offset}, is present
+(@pxref{Shell Parameter Expansion}).
+
+@item
+The expansion
+@code{$@{var/[/]}@var{pattern}@code{[/}@var{replacement}@code{]@}},
+which matches @var{pattern} and replaces it with @var{replacement} in
+the value of @code{var}, is available (@pxref{Shell Parameter Expansion}).
+
+@item
+The expansion @code{$@{!@var{prefix@}*}} expansion, which expands to
+the names of all shell variables whose names begin with @var{prefix},
+is available (@pxref{Shell Parameter Expansion}).
+
+@item
+Bash has @var{indirect} variable expansion using @code{$@{!word@}}
+(@pxref{Shell Parameter Expansion}).
+
+@item
+Bash can expand positional parameters beyond @code{$9} using
+@code{$@{@var{num}@}}.
+
+@item
+The @sc{posix} @code{$()} form of command substitution
+is implemented (@pxref{Command Substitution}),
+and preferred to the Bourne shell's @code{``} (which
+is also implemented for backwards compatibility).
+
+@item
+Bash has process substitution (@pxref{Process Substitution}).
+
+@item
+Bash automatically assigns variables that provide information about the
+current user (@env{UID}, @env{EUID}, and @env{GROUPS}), the current host
+(@env{HOSTTYPE}, @env{OSTYPE}, @env{MACHTYPE}, and @env{HOSTNAME}),
+and the instance of Bash that is running (@env{BASH},
+@env{BASH_VERSION}, and @env{BASH_VERSINFO}).  @xref{Bash Variables},
+for details.
+
+@item
+The @env{IFS} variable is used to split only the results of expansion,
+not all words (@pxref{Word Splitting}).
+This closes a longstanding shell security hole.
+
+@item
+Bash implements the full set of @sc{posix} 1003.2 filename expansion operators,
+including @var{character classes}, @var{equivalence classes}, and
+@var{collating symbols} (@pxref{Filename Expansion}).
+
+@item
+Bash implements extended pattern matching features when the @code{extglob}
+shell option is enabled (@pxref{Pattern Matching}).
+
+@item
+It is possible to have a variable and a function with the same name;
+@code{sh} does not separate the two name spaces.
+
+@item
+Bash functions are permitted to have local variables using the
+@code{local} builtin, and thus useful recursive functions may be written
+(@pxref{Bash Builtins}).
+
+@item
+Variable assignments preceding commands affect only that command, even
+builtins and functions (@pxref{Environment}).
+In @code{sh}, all variable assignments 
+preceding commands are global unless the command is executed from the
+file system.
+
+@item
+Bash performs filename expansion on filenames specified as operands
+to input and output redirection operators (@pxref{Redirections}).
+
+@item
+Bash contains the @samp{<>} redirection operator, allowing a file to be
+opened for both reading and writing, and the @samp{&>} redirection
+operator, for directing standard output and standard error to the same
+file (@pxref{Redirections}).
+
+@item
+Bash treats a number of filenames specially when they are
+used in redirection operators (@pxref{Redirections}).
+
+@item
+Bash can open network connections to arbitrary machines and services
+with the redirection operators (@pxref{Redirections}).
+
+@item
+The @code{noclobber} option is available to avoid overwriting existing
+files with output redirection (@pxref{The Set Builtin}).
+The @samp{>|} redirection operator may be used to override @code{noclobber}.
+
+@item
+The Bash @code{cd} and @code{pwd} builtins (@pxref{Bourne Shell Builtins})
+each take @option{-L} and @option{-P} options to switch between logical and
+physical modes.
+
+@item
+Bash allows a function to override a builtin with the same name, and provides
+access to that builtin's functionality within the function via the
+@code{builtin} and @code{command} builtins (@pxref{Bash Builtins}).
+
+@item
+The @code{command} builtin allows selective disabling of functions
+when command lookup is performed (@pxref{Bash Builtins}).
+
+@item
+Individual builtins may be enabled or disabled using the @code{enable}
+builtin (@pxref{Bash Builtins}).
+
+@item
+The Bash @code{exec} builtin takes additional options that allow users
+to control the contents of the environment passed to the executed
+command, and what the zeroth argument to the command is to be
+(@pxref{Bourne Shell Builtins}).
+
+@item
+Shell functions may be exported to children via the environment
+using @code{export -f} (@pxref{Shell Functions}).
+
+@item
+The Bash @code{export}, @code{readonly}, and @code{declare} builtins can
+take a @option{-f} option to act on shell functions, a @option{-p} option to
+display variables with various attributes set in a format that can be
+used as shell input, a @option{-n} option to remove various variable
+attributes, and @samp{name=value} arguments to set variable attributes
+and values simultaneously.
+
+@item
+The Bash @code{hash} builtin allows a name to be associated with
+an arbitrary filename, even when that filename cannot be found by
+searching the @env{$PATH}, using @samp{hash -p}
+(@pxref{Bourne Shell Builtins}).
+
+@item
+Bash includes a @code{help} builtin for quick reference to shell
+facilities (@pxref{Bash Builtins}).
+
+@item
+The @code{printf} builtin is available to display formatted output
+(@pxref{Bash Builtins}).
+
+@item
+The Bash @code{read} builtin (@pxref{Bash Builtins})
+will read a line ending in @samp{\} with
+the @option{-r} option, and will use the @env{REPLY} variable as a
+default if no non-option arguments are supplied.
+The Bash @code{read} builtin
+also accepts a prompt string with the @option{-p} option and will use
+Readline to obtain the line when given the @option{-e} option.
+The @code{read} builtin also has additional options to control input:
+the @option{-s} option will turn off echoing of input characters as
+they are read, the @option{-t} option will allow @code{read} to time out
+if input does not arrive within a specified number of seconds, the
+@option{-n} option will allow reading only a specified number of
+characters rather than a full line, and the @option{-d} option will read
+until a particular character rather than newline.
+
+@item
+The @code{return} builtin may be used to abort execution of scripts
+executed with the @code{.} or @code{source} builtins
+(@pxref{Bourne Shell Builtins}).
+
+@item
+Bash includes the @code{shopt} builtin, for finer control of shell
+optional capabilities (@pxref{Bash Builtins}), and allows these options
+to be set and unset at shell invocation (@pxref{Invoking Bash}).
+
+@item
+Bash has much more optional behavior controllable with the @code{set}
+builtin (@pxref{The Set Builtin}).
+
+@item
+The @samp{-x} (@code{xtrace}) option displays commands other than
+simple commands when performing an execution trace
+(@pxref{The Set Builtin}).
+
+@item
+The @code{test} builtin (@pxref{Bourne Shell Builtins})
+is slightly different, as it implements the @sc{posix} algorithm,
+which specifies the behavior based on the number of arguments.
+
+@item
+Bash includes the @code{caller} builtin, which displays the context of
+any active subroutine call (a shell function or a script executed with
+the @code{.} or @code{source} builtins).  This supports the bash
+debugger.
+
+@item
+The @code{trap} builtin (@pxref{Bourne Shell Builtins}) allows a
+@code{DEBUG} pseudo-signal specification, similar to @code{EXIT}.
+Commands specified with a @code{DEBUG} trap are executed before every
+simple command, @code{for} command, @code{case} command,
+@code{select} command, every arithmetic @code{for} command, and before
+the first command executes in a shell function.
+The @code{DEBUG} trap is not inherited by shell functions unless the
+function has been given the @code{trace} attribute or the
+@code{functrace} option has been enabled using the @code{shopt} builtin.
+The @code{extdebug} shell option has additional effects on the
+@code{DEBUG} trap.
+
+The @code{trap} builtin (@pxref{Bourne Shell Builtins}) allows an
+@code{ERR} pseudo-signal specification, similar to @code{EXIT} and @code{DEBUG}.
+Commands specified with an @code{ERR} trap are executed after a simple
+command fails, with a few exceptions.
+The @code{ERR} trap is not inherited by shell functions unless the
+@code{-o errtrace} option to the @code{set} builtin is enabled.
+
+The @code{trap} builtin (@pxref{Bourne Shell Builtins}) allows a
+@code{RETURN} pseudo-signal specification, similar to
+@code{EXIT} and @code{DEBUG}.
+Commands specified with an @code{RETURN} trap are executed before
+execution resumes after a shell function or a shell script executed with
+@code{.} or @code{source} returns.
+The @code{RETURN} trap is not inherited by shell functions.
+
+@item
+The Bash @code{type} builtin is more extensive and gives more information
+about the names it finds (@pxref{Bash Builtins}).
+
+@item
+The Bash @code{umask} builtin permits a @option{-p} option to cause
+the output to be displayed in the form of a @code{umask} command
+that may be reused as input (@pxref{Bourne Shell Builtins}).
+
+@item
+Bash implements a @code{csh}-like directory stack, and provides the
+@code{pushd}, @code{popd}, and @code{dirs} builtins to manipulate it
+(@pxref{The Directory Stack}).
+Bash also makes the directory stack visible as the value of the
+@env{DIRSTACK} shell variable.
+
+@item
+Bash interprets special backslash-escaped characters in the prompt
+strings when interactive (@pxref{Printing a Prompt}).
+
+@item
+The Bash restricted mode is more useful (@pxref{The Restricted Shell});
+the SVR4.2 shell restricted mode is too limited.
+
+@item
+The @code{disown} builtin can remove a job from the internal shell
+job table (@pxref{Job Control Builtins}) or suppress the sending
+of @code{SIGHUP} to a job when the shell exits as the result of a
+@code{SIGHUP}.
+
+@item
+The SVR4.2 shell has two privilege-related builtins
+(@code{mldmode} and @code{priv}) not present in Bash.
+
+@item
+Bash does not have the @code{stop} or @code{newgrp} builtins.
+
+@item
+Bash does not use the @env{SHACCT} variable or perform shell accounting.
+
+@item
+The SVR4.2 @code{sh} uses a @env{TIMEOUT} variable like Bash uses
+@env{TMOUT}.
+
+@end itemize
+
+@noindent
+More features unique to Bash may be found in @ref{Bash Features}.
+
+
+@appendixsec Implementation Differences From The SVR4.2 Shell
+
+Since Bash is a completely new implementation, it does not suffer from
+many of the limitations of the SVR4.2 shell.  For instance:
+
+@itemize @bullet
+
+@item
+Bash does not fork a subshell when redirecting into or out of
+a shell control structure such as  an @code{if} or @code{while}
+statement.
+
+@item
+Bash does not allow unbalanced quotes.  The SVR4.2 shell will silently
+insert a needed closing quote at @code{EOF} under certain circumstances.
+This can be the cause of some hard-to-find errors.
+
+@item
+The SVR4.2 shell uses a baroque memory management scheme based on
+trapping @code{SIGSEGV}.  If the shell is started from a process with
+@code{SIGSEGV} blocked (e.g., by using the @code{system()} C library
+function call), it misbehaves badly.
+
+@item
+In a questionable attempt at security, the SVR4.2 shell,
+when invoked without the @option{-p} option, will alter its real
+and effective @sc{uid} and @sc{gid} if they are less than some
+magic threshold value, commonly 100.
+This can lead to unexpected results.
+
+@item
+The SVR4.2 shell does not allow users to trap @code{SIGSEGV},
+@code{SIGALRM}, or @code{SIGCHLD}.
+
+@item
+The SVR4.2 shell does not allow the @env{IFS}, @env{MAILCHECK},
+@env{PATH}, @env{PS1}, or @env{PS2} variables to be unset.
+
+@item
+The SVR4.2 shell treats @samp{^} as the undocumented equivalent of
+@samp{|}.
+
+@item
+Bash allows multiple option arguments when it is invoked (@code{-x -v});
+the SVR4.2 shell allows only one option argument (@code{-xv}).  In
+fact, some versions of the shell dump core if the second argument begins
+with a @samp{-}.
+
+@item
+The SVR4.2 shell exits a script if any builtin fails; Bash exits
+a script only if one of the @sc{posix} 1003.2 special builtins fails, and
+only for certain failures, as enumerated in the @sc{posix} 1003.2 standard.
+
+@item 
+The SVR4.2 shell behaves differently when invoked as @code{jsh}
+(it turns on job control).
+@end itemize
+
+@node Copying This Manual
+@appendix Copying This Manual
+
+@menu
+* GNU Free Documentation License::      License for copying this manual.
+@end menu
+
+@include fdl.texi
+
+@node Builtin Index
+@unnumbered Index of Shell Builtin Commands
+@printindex bt
+
+@node Reserved Word Index
+@unnumbered Index of Shell Reserved Words
+@printindex rw
+
+@node Variable Index
+@unnumbered Parameter and Variable Index
+@printindex vr
+
+@node Function Index
+@unnumbered Function Index
+@printindex fn
+
+@node Concept Index
+@unnumbered Concept Index
+@printindex cp
+
+@bye
index 0ca20b11ae7d73b9e3aa47af79e48a7085b77061..ae5b6ad8420802f1834cff8e1ae0fea8dbb247d5 100644 (file)
@@ -4,7 +4,7 @@ Copyright (C) 1988-2004 Free Software Foundation, Inc.
 
 @set EDITION 3.0
 @set VERSION 3.0
-@set UPDATED 27 July 2004
-@set UPDATED-MONTH July 2004
+@set UPDATED 1 August 2004
+@set UPDATED-MONTH August 2004
 
-@set LASTCHANGE Tue Jul 27 09:12:07 EDT 2004
+@set LASTCHANGE Sun Aug  1 14:48:46 EDT 2004
diff --git a/doc/version.texi~ b/doc/version.texi~
new file mode 100644 (file)
index 0000000..0ca20b1
--- /dev/null
@@ -0,0 +1,10 @@
+@ignore
+Copyright (C) 1988-2004 Free Software Foundation, Inc.
+@end ignore
+
+@set EDITION 3.0
+@set VERSION 3.0
+@set UPDATED 27 July 2004
+@set UPDATED-MONTH July 2004
+
+@set LASTCHANGE Tue Jul 27 09:12:07 EDT 2004
index f62cccb259192cb89cd10e97eb7a58fe684544eb..1ef6d0f6dd1d0e990acd923fbc1c056cddb697ee 100644 (file)
--- a/general.c
+++ b/general.c
@@ -566,15 +566,20 @@ absolute_program (string)
 }
 
 /* Return the `basename' of the pathname in STRING (the stuff after the
-   last '/').  If STRING is not a full pathname, simply return it. */
+   last '/').  If STRING is `/', just return it. */
 char *
 base_pathname (string)
      char *string;
 {
   char *p;
 
+#if 0
   if (absolute_pathname (string) == 0)
     return (string);
+#endif
+
+  if (string[0] == '/' && string[1] == 0)
+    return (string);
 
   p = (char *)strrchr (string, '/');
   return (p ? ++p : string);
diff --git a/general.c~ b/general.c~
new file mode 100644 (file)
index 0000000..9300bf4
--- /dev/null
@@ -0,0 +1,964 @@
+/* general.c -- Stuff that is used by all files. */
+
+/* Copyright (C) 1987-2004 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 2, 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, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+
+#include "config.h"
+
+#include "bashtypes.h"
+#ifndef _MINIX
+#  include <sys/param.h>
+#endif
+#include "posixstat.h"
+
+#if defined (HAVE_UNISTD_H)
+#  include <unistd.h>
+#endif
+
+#include "filecntl.h"
+#include "bashansi.h"
+#include <stdio.h>
+#include "chartypes.h"
+#include <errno.h>
+
+#include "bashintl.h"
+
+#include "shell.h"
+#include "test.h"
+
+#include <tilde/tilde.h>
+
+#if !defined (errno)
+extern int errno;
+#endif /* !errno */
+
+extern int expand_aliases;
+extern int interrupt_immediately;
+extern int interactive_comments;
+extern int check_hashed_filenames;
+extern int source_uses_path;
+extern int source_searches_cwd;
+
+static char *bash_special_tilde_expansions __P((char *));
+static int unquoted_tilde_word __P((const char *));
+static void initialize_group_array __P((void));
+
+/* A standard error message to use when getcwd() returns NULL. */
+char *bash_getcwd_errstr = N_("getcwd: cannot access parent directories");
+
+/* Do whatever is necessary to initialize `Posix mode'. */
+void
+posix_initialize (on)
+     int on;
+{
+  /* Things that should be turned on when posix mode is enabled. */
+  if (on != 0)
+    {
+      interactive_comments = source_uses_path = expand_aliases = 1;
+    }
+
+  /* Things that should be turned on when posix mode is disabled. */
+  if (on == 0)
+    {
+      source_searches_cwd = 1;
+      expand_aliases = interactive_shell;
+    }
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*  Functions to convert to and from and display non-standard types */
+/*                                                                 */
+/* **************************************************************** */
+
+#if defined (RLIMTYPE)
+RLIMTYPE
+string_to_rlimtype (s)
+     char *s;
+{
+  RLIMTYPE ret;
+  int neg;
+
+  ret = 0;
+  neg = 0;
+  while (s && *s && whitespace (*s))
+    s++;
+  if (*s == '-' || *s == '+')
+    {
+      neg = *s == '-';
+      s++;
+    }
+  for ( ; s && *s && DIGIT (*s); s++)
+    ret = (ret * 10) + TODIGIT (*s);
+  return (neg ? -ret : ret);
+}
+
+void
+print_rlimtype (n, addnl)
+     RLIMTYPE n;
+     int addnl;
+{
+  char s[INT_STRLEN_BOUND (RLIMTYPE) + 1], *p;
+
+  p = s + sizeof(s);
+  *--p = '\0';
+
+  if (n < 0)
+    {
+      do
+       *--p = '0' - n % 10;
+      while ((n /= 10) != 0);
+
+      *--p = '-';
+    }
+  else
+    {
+      do
+       *--p = '0' + n % 10;
+      while ((n /= 10) != 0);
+    }
+
+  printf ("%s%s", p, addnl ? "\n" : "");
+}
+#endif /* RLIMTYPE */
+
+/* **************************************************************** */
+/*                                                                 */
+/*                    Input Validation Functions                   */
+/*                                                                 */
+/* **************************************************************** */
+
+/* Return non-zero if all of the characters in STRING are digits. */
+int
+all_digits (string)
+     char *string;
+{
+  register char *s;
+
+  for (s = string; *s; s++)
+    if (DIGIT (*s) == 0)
+      return (0);
+
+  return (1);
+}
+
+/* Return non-zero if the characters pointed to by STRING constitute a
+   valid number.  Stuff the converted number into RESULT if RESULT is
+   not null. */
+int
+legal_number (string, result)
+     char *string;
+     intmax_t *result;
+{
+  intmax_t value;
+  char *ep;
+
+  if (result)
+    *result = 0;
+
+  errno = 0;
+  value = strtoimax (string, &ep, 10);
+  if (errno)
+    return 0;  /* errno is set on overflow or underflow */
+
+  /* Skip any trailing whitespace, since strtoimax does not. */
+  while (whitespace (*ep))
+    ep++;
+
+  /* If *string is not '\0' but *ep is '\0' on return, the entire string
+     is valid. */
+  if (string && *string && *ep == '\0')
+    {
+      if (result)
+       *result = value;
+      /* The SunOS4 implementation of strtol() will happily ignore
+        overflow conditions, so this cannot do overflow correctly
+        on those systems. */
+      return 1;
+    }
+    
+  return (0);
+}
+
+/* Return 1 if this token is a legal shell `identifier'; that is, it consists
+   solely of letters, digits, and underscores, and does not begin with a
+   digit. */
+int
+legal_identifier (name)
+     char *name;
+{
+  register char *s;
+  unsigned char c;
+
+  if (!name || !(c = *name) || (legal_variable_starter (c) == 0))
+    return (0);
+
+  for (s = name + 1; (c = *s) != 0; s++)
+    {
+      if (legal_variable_char (c) == 0)
+       return (0);
+    }
+  return (1);
+}
+
+/* Make sure that WORD 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.  If CHECK_WORD is non-zero,
+   the word is checked to ensure that it consists of only letters,
+   digits, and underscores. */
+int
+check_identifier (word, check_word)
+     WORD_DESC *word;
+     int check_word;
+{
+  if ((word->flags & (W_HASDOLLAR|W_QUOTED)) || all_digits (word->word))
+    {
+      internal_error (_("`%s': not a valid identifier"), word->word);
+      return (0);
+    }
+  else if (check_word && legal_identifier (word->word) == 0)
+    {
+      internal_error (_("`%s': not a valid identifier"), word->word);
+      return (0);
+    }
+  else
+    return (1);
+}
+
+/* Return 1 if STRING comprises a valid alias name.  The shell accepts
+   essentially all characters except those which must be quoted to the
+   parser (which disqualifies them from alias expansion anyway) and `/'. */
+int
+legal_alias_name (string, flags)
+     char *string;
+     int flags;
+{
+  register char *s;
+
+  for (s = string; *s; s++)
+    if (shellbreak (*s) || shellxquote (*s) || shellexp (*s) || (*s == '/'))
+      return 0;
+  return 1;
+}
+
+/* Returns non-zero if STRING is an assignment statement.  The returned value
+   is the index of the `=' sign. */
+int
+assignment (string, flags)
+     const char *string;
+     int flags;
+{
+  register unsigned char c;
+  register int newi, indx;
+
+  c = string[indx = 0];
+
+#if defined (ARRAY_VARS)
+  if ((legal_variable_starter (c) == 0) && (flags && c != '[')) /* ] */
+#else
+  if (legal_variable_starter (c) == 0)
+#endif
+    return (0);
+
+  while (c = string[indx])
+    {
+      /* The following is safe.  Note that '=' at the start of a word
+        is not an assignment statement. */
+      if (c == '=')
+       return (indx);
+
+#if defined (ARRAY_VARS)
+      if (c == '[')
+       {
+         newi = skipsubscript (string, indx);
+         if (string[newi++] != ']')
+           return (0);
+         return ((string[newi] == '=') ? newi : 0);
+       }
+#endif /* ARRAY_VARS */
+
+      /* Variable names in assignment statements may contain only letters,
+        digits, and `_'. */
+      if (legal_variable_char (c) == 0)
+       return (0);
+
+      indx++;
+    }
+  return (0);
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*          Functions to manage files and file descriptors         */
+/*                                                                 */
+/* **************************************************************** */
+
+/* A function to unset no-delay mode on a file descriptor.  Used in shell.c
+   to unset it on the fd passed as stdin.  Should be called on stdin if
+   readline gets an EAGAIN or EWOULDBLOCK when trying to read input. */
+
+#if !defined (O_NDELAY)
+#  if defined (FNDELAY)
+#    define O_NDELAY FNDELAY
+#  endif
+#endif /* O_NDELAY */
+
+/* Make sure no-delay mode is not set on file descriptor FD. */
+int
+sh_unset_nodelay_mode (fd)
+     int fd;
+{
+  int flags, bflags;
+
+  if ((flags = fcntl (fd, F_GETFL, 0)) < 0)
+    return -1;
+
+  bflags = 0;
+
+  /* This is defined to O_NDELAY in filecntl.h if O_NONBLOCK is not present
+     and O_NDELAY is defined. */
+#ifdef O_NONBLOCK
+  bflags |= O_NONBLOCK;
+#endif
+
+#ifdef O_NDELAY
+  bflags |= O_NDELAY;
+#endif
+
+  if (flags & bflags)
+    {
+      flags &= ~bflags;
+      return (fcntl (fd, F_SETFL, flags));
+    }
+
+  return 0;
+}
+
+/* Return 1 if file descriptor FD is valid; 0 otherwise. */
+int
+sh_validfd (fd)
+     int fd;
+{
+  return (fcntl (fd, F_GETFD, 0) >= 0);
+}
+
+/* There is a bug in the NeXT 2.1 rlogind that causes opens
+   of /dev/tty to fail. */
+
+#if defined (__BEOS__)
+/* On BeOS, opening in non-blocking mode exposes a bug in BeOS, so turn it
+   into a no-op.  This should probably go away in the future. */
+#  undef O_NONBLOCK
+#  define O_NONBLOCK 0
+#endif /* __BEOS__ */
+
+void
+check_dev_tty ()
+{
+  int tty_fd;
+  char *tty;
+
+  tty_fd = open ("/dev/tty", O_RDWR|O_NONBLOCK);
+
+  if (tty_fd < 0)
+    {
+      tty = (char *)ttyname (fileno (stdin));
+      if (tty == 0)
+       return;
+      tty_fd = open (tty, O_RDWR|O_NONBLOCK);
+    }
+  close (tty_fd);
+}
+
+/* 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));
+}
+
+/* Move FD to a number close to the maximum number of file descriptors
+   allowed in the shell process, to avoid the user stepping on it with
+   redirection and causing us extra work.  If CHECK_NEW is non-zero,
+   we check whether or not the file descriptors are in use before
+   duplicating FD onto them.  MAXFD says where to start checking the
+   file descriptors.  If it's less than 20, we get the maximum value
+   available from getdtablesize(2). */
+int
+move_to_high_fd (fd, check_new, maxfd)
+     int fd, check_new, maxfd;
+{
+  int script_fd, nfds, ignore;
+
+  if (maxfd < 20)
+    {
+      nfds = getdtablesize ();
+      if (nfds <= 0)
+       nfds = 20;
+      if (nfds > HIGH_FD_MAX)
+       nfds = HIGH_FD_MAX;             /* reasonable maximum */
+    }
+  else
+    nfds = maxfd;
+
+  for (nfds--; check_new && nfds > 3; nfds--)
+    if (fcntl (nfds, F_GETFD, &ignore) == -1)
+      break;
+
+  if (nfds > 3 && fd != nfds && (script_fd = dup2 (fd, nfds)) != -1)
+    {
+      if (check_new == 0 || fd != fileno (stderr))     /* don't close stderr */
+       close (fd);
+      return (script_fd);
+    }
+
+  /* OK, we didn't find one less than our artificial maximum; return the
+     original file descriptor. */
+  return (fd);
+}
+/* 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. */
+
+int
+check_binary_file (sample, sample_len)
+     char *sample;
+     int sample_len;
+{
+  register int i;
+  unsigned char c;
+
+  for (i = 0; i < sample_len; i++)
+    {
+      c = sample[i];
+      if (c == '\n')
+       return (0);
+
+      if (ISSPACE (c) == 0 && ISPRINT (c) == 0)
+       return (1);
+    }
+
+  return (0);
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*                 Functions to inspect pathnames                  */
+/*                                                                 */
+/* **************************************************************** */
+
+int
+file_isdir (fn)
+     char *fn;
+{
+  struct stat sb;
+
+  return ((stat (fn, &sb) == 0) && S_ISDIR (sb.st_mode));
+}
+
+int
+file_iswdir (fn)
+     char *fn;
+{
+  return (file_isdir (fn) && test_eaccess (fn, W_OK) == 0);
+}
+
+
+/* **************************************************************** */
+/*                                                                 */
+/*                 Functions to manipulate pathnames               */
+/*                                                                 */
+/* **************************************************************** */
+
+/* Turn STRING (a pathname) into an absolute pathname, assuming that
+   DOT_PATH contains the symbolic location of `.'.  This always
+   returns a new string, even if STRING was an absolute pathname to
+   begin with. */
+char *
+make_absolute (string, dot_path)
+     char *string, *dot_path;
+{
+  char *result;
+
+  if (dot_path == 0 || ABSPATH(string))
+#ifdef __CYGWIN__
+    {
+      char pathbuf[PATH_MAX + 1];
+
+      cygwin_conv_to_full_posix_path (string, pathbuf);
+      result = savestring (pathbuf);
+    }
+#else
+    result = savestring (string);
+#endif
+  else
+    result = sh_makepath (dot_path, string, 0);
+
+  return (result);
+}
+
+/* Return 1 if STRING contains an absolute pathname, else 0.  Used by `cd'
+   to decide whether or not to look up a directory name in $CDPATH. */
+int
+absolute_pathname (string)
+     const char *string;
+{
+  if (string == 0 || *string == '\0')
+    return (0);
+
+  if (ABSPATH(string))
+    return (1);
+
+  if (string[0] == '.' && PATHSEP(string[1]))  /* . and ./ */
+    return (1);
+
+  if (string[0] == '.' && string[1] == '.' && PATHSEP(string[2]))      /* .. and ../ */
+    return (1);
+
+  return (0);
+}
+
+/* Return 1 if STRING is an absolute program name; it is absolute if it
+   contains any slashes.  This is used to decide whether or not to look
+   up through $PATH. */
+int
+absolute_program (string)
+     const char *string;
+{
+  return ((char *)xstrchr (string, '/') != (char *)NULL);
+}
+
+/* Return the `basename' of the pathname in STRING (the stuff after the
+   last '/').  If STRING is `/', just return it. */
+char *
+base_pathname (string)
+     char *string;
+{
+  char *p;
+
+#if 0
+  if (absolute_pathname (string) == 0)
+    return (string);
+#endif
+  if (string[0] == '/' && string[1] == 0)
+    return (string);
+
+  p = (char *)strrchr (string, '/');
+  return (p ? ++p : string);
+}
+
+/* Return the full pathname of FILE.  Easy.  Filenames that begin
+   with a '/' are returned as themselves.  Other filenames have
+   the current working directory prepended.  A new string is
+   returned in either case. */
+char *
+full_pathname (file)
+     char *file;
+{
+  char *ret;
+
+  file = (*file == '~') ? bash_tilde_expand (file, 0) : savestring (file);
+
+  if (ABSPATH(file))
+    return (file);
+
+  ret = sh_makepath ((char *)NULL, file, (MP_DOCWD|MP_RMDOT));
+  free (file);
+
+  return (ret);
+}
+
+/* A slightly related function.  Get the prettiest name of this
+   directory possible. */
+static char tdir[PATH_MAX];
+
+/* Return a pretty pathname.  If the first part of the pathname is
+   the same as $HOME, then replace that with `~'.  */
+char *
+polite_directory_format (name)
+     char *name;
+{
+  char *home;
+  int l;
+
+  home = get_string_value ("HOME");
+  l = home ? strlen (home) : 0;
+  if (l > 1 && strncmp (home, name, l) == 0 && (!name[l] || name[l] == '/'))
+    {
+      strncpy (tdir + 1, name + l, sizeof(tdir) - 2);
+      tdir[0] = '~';
+      tdir[sizeof(tdir) - 1] = '\0';
+      return (tdir);
+    }
+  else
+    return (name);
+}
+
+/* Given a string containing units of information separated by colons,
+   return the next one pointed to by (P_INDEX), or NULL if there are no more.
+   Advance (P_INDEX) to the character after the colon. */
+char *
+extract_colon_unit (string, p_index)
+     char *string;
+     int *p_index;
+{
+  int i, start, len;
+  char *value;
+
+  if (string == 0)
+    return (string);
+
+  len = strlen (string);
+  if (*p_index >= len)
+    return ((char *)NULL);
+
+  i = *p_index;
+
+  /* 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++;
+
+  for (start = i; string[i] && string[i] != ':'; i++)
+    ;
+
+  *p_index = i;
+
+  if (i == start)
+    {
+      if (string[i])
+       (*p_index)++;
+      /* Return "" in the case of a trailing `:'. */
+      value = (char *)xmalloc (1);
+      value[0] = '\0';
+    }
+  else
+    value = substring (string, start, i);
+
+  return (value);
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*                 Tilde Initialization and Expansion              */
+/*                                                                 */
+/* **************************************************************** */
+
+#if defined (PUSHD_AND_POPD)
+extern char *get_dirstack_from_string __P((char *));
+#endif
+
+static char **bash_tilde_prefixes;
+static char **bash_tilde_suffixes;
+
+/* If tilde_expand hasn't been able to expand the text, perhaps it
+   is a special shell expansion.  This function is installed as the
+   tilde_expansion_preexpansion_hook.  It knows how to expand ~- and ~+.
+   If PUSHD_AND_POPD is defined, ~[+-]N expands to directories from the
+   directory stack. */
+static char *
+bash_special_tilde_expansions (text)
+     char *text;
+{
+  char *result;
+
+  result = (char *)NULL;
+
+  if (text[0] == '+' && text[1] == '\0')
+    result = get_string_value ("PWD");
+  else if (text[0] == '-' && text[1] == '\0')
+    result = get_string_value ("OLDPWD");
+#if defined (PUSHD_AND_POPD)
+  else if (DIGIT (*text) || ((*text == '+' || *text == '-') && DIGIT (text[1])))
+    result = get_dirstack_from_string (text);
+#endif
+
+  return (result ? savestring (result) : (char *)NULL);
+}
+
+/* Initialize the tilde expander.  In Bash, we handle `~-' and `~+', as
+   well as handling special tilde prefixes; `:~" and `=~' are indications
+   that we should do tilde expansion. */
+void
+tilde_initialize ()
+{
+  static int times_called = 0;
+
+  /* Tell the tilde expander that we want a crack first. */
+  tilde_expansion_preexpansion_hook = bash_special_tilde_expansions;
+
+  /* Tell the tilde expander about special strings which start a tilde
+     expansion, and the special strings that end one.  Only do this once.
+     tilde_initialize () is called from within bashline_reinitialize (). */
+  if (times_called++ == 0)
+    {
+      bash_tilde_prefixes = strvec_create (3);
+      bash_tilde_prefixes[0] = "=~";
+      bash_tilde_prefixes[1] = ":~";
+      bash_tilde_prefixes[2] = (char *)NULL;
+
+      tilde_additional_prefixes = bash_tilde_prefixes;
+
+      bash_tilde_suffixes = strvec_create (3);
+      bash_tilde_suffixes[0] = ":";
+      bash_tilde_suffixes[1] = "=~";   /* XXX - ?? */
+      bash_tilde_suffixes[2] = (char *)NULL;
+
+      tilde_additional_suffixes = bash_tilde_suffixes;
+    }
+}
+
+/* POSIX.2, 3.6.1:  A tilde-prefix consists of an unquoted tilde character
+   at the beginning of the word, followed by all of the characters preceding
+   the first unquoted slash in the word, or all the characters in the word
+   if there is no slash...If none of the characters in the tilde-prefix are
+   quoted, the characters in the tilde-prefix following the tilde shell be
+   treated as a possible login name. */
+
+#define TILDE_END(c)   ((c) == '\0' || (c) == '/' || (c) == ':')
+
+static int
+unquoted_tilde_word (s)
+     const char *s;
+{
+  const char *r;
+
+  for (r = s; TILDE_END(*r) == 0; r++)
+    {
+      switch (*r)
+       {
+       case '\\':
+       case '\'':
+       case '"':
+         return 0;
+       }
+    }
+  return 1;
+}
+
+/* Tilde-expand S by running it through the tilde expansion library.
+   ASSIGN_P is 1 if this is a variable assignment, so the alternate
+   tilde prefixes should be enabled (`=~' and `:~', see above). */
+char *
+bash_tilde_expand (s, assign_p)
+     const char *s;
+     int assign_p;
+{
+  int old_immed, r;
+  char *ret;
+
+  old_immed = interrupt_immediately;
+  interrupt_immediately = 1;
+  tilde_additional_prefixes = assign_p ? bash_tilde_prefixes : (char **)0;
+  r = (*s == '~') ? unquoted_tilde_word (s) : 1;
+  ret = r ? tilde_expand (s) : savestring (s);
+  interrupt_immediately = old_immed;
+  return (ret);
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*       Functions to manipulate and search the group list         */
+/*                                                                 */
+/* **************************************************************** */
+
+static int ngroups, maxgroups;
+
+/* The set of groups that this user is a member of. */
+static GETGROUPS_T *group_array = (GETGROUPS_T *)NULL;
+
+#if !defined (NOGROUP)
+#  define NOGROUP (gid_t) -1
+#endif
+
+static void
+initialize_group_array ()
+{
+  register int i;
+
+  if (maxgroups == 0)
+    maxgroups = getmaxgroups ();
+
+  ngroups = 0;
+  group_array = (GETGROUPS_T *)xrealloc (group_array, maxgroups * sizeof (GETGROUPS_T));
+
+#if defined (HAVE_GETGROUPS)
+  ngroups = getgroups (maxgroups, group_array);
+#endif
+
+  /* If getgroups returns nothing, or the OS does not support getgroups(),
+     make sure the groups array includes at least the current gid. */
+  if (ngroups == 0)
+    {
+      group_array[0] = current_user.gid;
+      ngroups = 1;
+    }
+
+  /* If the primary group is not in the groups array, add it as group_array[0]
+     and shuffle everything else up 1, if there's room. */
+  for (i = 0; i < ngroups; i++)
+    if (current_user.gid == (gid_t)group_array[i])
+      break;
+  if (i == ngroups && ngroups < maxgroups)
+    {
+      for (i = ngroups; i > 0; i--)
+       group_array[i] = group_array[i - 1];
+      group_array[0] = current_user.gid;
+      ngroups++;
+    }
+
+  /* If the primary group is not group_array[0], swap group_array[0] and
+     whatever the current group is.  The vast majority of systems should
+     not need this; a notable exception is Linux. */
+  if (group_array[0] != current_user.gid)
+    {
+      for (i = 0; i < ngroups; i++)
+       if (group_array[i] == current_user.gid)
+         break;
+      if (i < ngroups)
+       {
+         group_array[i] = group_array[0];
+         group_array[0] = current_user.gid;
+       }
+    }
+}
+
+/* Return non-zero if GID is one that we have in our groups list. */
+int
+#if defined (__STDC__) || defined ( _MINIX)
+group_member (gid_t gid)
+#else
+group_member (gid)
+     gid_t gid;
+#endif /* !__STDC__ && !_MINIX */
+{
+#if defined (HAVE_GETGROUPS)
+  register int i;
+#endif
+
+  /* Short-circuit if possible, maybe saving a call to getgroups(). */
+  if (gid == current_user.gid || gid == current_user.egid)
+    return (1);
+
+#if defined (HAVE_GETGROUPS)
+  if (ngroups == 0)
+    initialize_group_array ();
+
+  /* In case of error, the user loses. */
+  if (ngroups <= 0)
+    return (0);
+
+  /* Search through the list looking for GID. */
+  for (i = 0; i < ngroups; i++)
+    if (gid == (gid_t)group_array[i])
+      return (1);
+#endif
+
+  return (0);
+}
+
+char **
+get_group_list (ngp)
+     int *ngp;
+{
+  static char **group_vector = (char **)NULL;
+  register int i;
+
+  if (group_vector)
+    {
+      if (ngp)
+       *ngp = ngroups;
+      return group_vector;
+    }
+
+  if (ngroups == 0)
+    initialize_group_array ();
+
+  if (ngroups <= 0)
+    {
+      if (ngp)
+       *ngp = 0;
+      return (char **)NULL;
+    }
+
+  group_vector = strvec_create (ngroups);
+  for (i = 0; i < ngroups; i++)
+    group_vector[i] = itos (group_array[i]);
+
+  if (ngp)
+    *ngp = ngroups;
+  return group_vector;
+}
+
+int *
+get_group_array (ngp)
+     int *ngp;
+{
+  int i;
+  static int *group_iarray = (int *)NULL;
+
+  if (group_iarray)
+    {
+      if (ngp)
+       *ngp = ngroups;
+      return (group_iarray);
+    }
+
+  if (ngroups == 0)
+    initialize_group_array ();    
+
+  if (ngroups <= 0)
+    {
+      if (ngp)
+       *ngp = 0;
+      return (int *)NULL;
+    }
+
+  group_iarray = (int *)xmalloc (ngroups * sizeof (int));
+  for (i = 0; i < ngroups; i++)
+    group_iarray[i] = (int)group_array[i];
+
+  if (ngp)
+    *ngp = ngroups;
+  return group_iarray;
+}
index a737780cdd229e0d178a331023bf40cf7cd2a6f2..d296242e7918fb2f324610cceecfa50d0039c282 100644 (file)
@@ -31,6 +31,8 @@
 extern size_t xmbsrtowcs __P((wchar_t *, const char **, size_t, mbstate_t *));
 extern size_t xdupmbstowcs __P((wchar_t **, char ***, const char *));
 
+extern size_t mbstrlen __P((const char *));
+
 extern char *xstrchr __P((const char *, int));
 
 #ifndef MB_INVALIDCH
@@ -38,6 +40,8 @@ extern char *xstrchr __P((const char *, int));
 #define MB_NULLWCH(x)          ((x) == 0)
 #endif
 
+#define MB_STRLEN(s)   ((MB_CUR_MAX > 1) ? mbstrlen (s) : STRLEN (s))
+
 #else /* !HANDLE_MULTIBYTE */
 
 #undef MB_LEN_MAX
@@ -54,6 +58,8 @@ extern char *xstrchr __P((const char *, int));
 #define MB_NULLWCH(x)          (0)
 #endif
 
+#define MB_STRLEN(s)           (STRLEN(s))
+
 #endif /* !HANDLE_MULTIBYTE */
 
 /* Declare and initialize a multibyte state.  Call must be terminated
diff --git a/include/shmbutil.h~ b/include/shmbutil.h~
new file mode 100644 (file)
index 0000000..1d15240
--- /dev/null
@@ -0,0 +1,438 @@
+/* shmbutil.h -- utility functions for multibyte characters. */
+
+/* Copyright (C) 2002-2004 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 2, 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, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+                                 
+#if !defined (_SH_MBUTIL_H_)
+#define _SH_MBUTIL_H_
+
+#include "stdc.h"
+
+/* Include config.h for HANDLE_MULTIBYTE */
+#include <config.h>
+
+#if defined (HANDLE_MULTIBYTE)
+
+extern size_t xmbsrtowcs __P((wchar_t *, const char **, size_t, mbstate_t *));
+extern size_t xdupmbstowcs __P((wchar_t **, char ***, const char *));
+
+extern size_t mbstrlen __P((const char *));
+
+extern char *xstrchr __P((const char *, int));
+
+#ifndef MB_INVALIDCH
+#define MB_INVALIDCH(x)                ((x) == (size_t)-1 || (x) == (size_t)-2)
+#define MB_NULLWCH(x)          ((x) == 0)
+#endif
+
+#define MB_STRLEN(s)   ((MB_CUR_MAX > 1) ? mb_strlen (s) : STRLEN (s))
+
+#else /* !HANDLE_MULTIBYTE */
+
+#undef MB_LEN_MAX
+#undef MB_CUR_MAX
+
+#define MB_LEN_MAX     1
+#define MB_CUR_MAX     1
+
+#undef xstrchr
+#define xstrchr(s, c)  strchr(s, c)
+
+#ifndef MB_INVALIDCH
+#define MB_INVALIDCH(x)                (0)
+#define MB_NULLWCH(x)          (0)
+#endif
+
+#define MB_STRLEN(s)           (STRLEN(s))
+
+#endif /* !HANDLE_MULTIBYTE */
+
+/* Declare and initialize a multibyte state.  Call must be terminated
+   with `;'. */
+#if defined (HANDLE_MULTIBYTE)
+#  define DECLARE_MBSTATE \
+       mbstate_t state; \
+       memset (&state, '\0', sizeof (mbstate_t))
+#else
+#  define DECLARE_MBSTATE
+#endif  /* !HANDLE_MULTIBYTE */
+
+/* Initialize or reinitialize a multibyte state named `state'.  Call must be
+   terminated with `;'. */
+#if defined (HANDLE_MULTIBYTE)
+#  define INITIALIZE_MBSTATE memset (&state, '\0', sizeof (mbstate_t))
+#else
+#  define INITIALIZE_MBSTATE
+#endif  /* !HANDLE_MULTIBYTE */
+
+/* Advance one (possibly multi-byte) character in string _STR of length
+   _STRSIZE, starting at index _I.  STATE must have already been declared. */
+#if defined (HANDLE_MULTIBYTE)
+#  define ADVANCE_CHAR(_str, _strsize, _i) \
+    do \
+      { \
+       if (MB_CUR_MAX > 1) \
+         { \
+           mbstate_t state_bak; \
+           size_t mblength; \
+\
+           state_bak = state; \
+           mblength = mbrlen ((_str) + (_i), (_strsize) - (_i), &state); \
+\
+           if (mblength == (size_t)-2 || mblength == (size_t)-1) \
+             { \
+               state = state_bak; \
+               (_i)++; \
+             } \
+           else if (mblength == 0) \
+             (_i)++; \
+           else \
+             (_i) += mblength; \
+         } \
+       else \
+         (_i)++; \
+      } \
+    while (0)
+#else
+#  define ADVANCE_CHAR(_str, _strsize, _i)     (_i)++
+#endif  /* !HANDLE_MULTIBYTE */
+
+/* Advance one (possibly multibyte) character in the string _STR of length
+   _STRSIZE.
+   SPECIAL:  assume that _STR will be incremented by 1 after this call. */
+#if defined (HANDLE_MULTIBYTE)
+#  define ADVANCE_CHAR_P(_str, _strsize) \
+    do \
+      { \
+       if (MB_CUR_MAX > 1) \
+         { \
+           mbstate_t state_bak; \
+           size_t mblength; \
+\
+           state_bak = state; \
+           mblength = mbrlen ((_str), (_strsize), &state); \
+\
+           if (mblength == (size_t)-2 || mblength == (size_t)-1) \
+             { \
+               state = state_bak; \
+               mblength = 1; \
+             } \
+           else \
+             (_str) += (mblength < 1) ? 0 : (mblength - 1); \
+         } \
+      } \
+    while (0)
+#else
+#  define ADVANCE_CHAR_P(_str, _strsize)
+#endif  /* !HANDLE_MULTIBYTE */
+
+/* Back up one (possibly multi-byte) character in string _STR of length
+   _STRSIZE, starting at index _I.  STATE must have already been declared. */
+#if defined (HANDLE_MULTIBYTE)
+#  define BACKUP_CHAR(_str, _strsize, _i) \
+    do \
+      { \
+       if (MB_CUR_MAX > 1) \
+         { \
+           mbstate_t state_bak; \
+           size_t mblength; \
+           int _x, _p; /* _x == temp index into string, _p == prev index */ \
+\
+           _x = _p = 0; \
+           while (_x < (_i)) \
+             { \
+               state_bak = state; \
+               mblength = mbrlen ((_str) + (_x), (_strsize) - (_x), &state); \
+\
+               if (mblength == (size_t)-2 || mblength == (size_t)-1) \
+                 { \
+                   state = state_bak; \
+                   _x++; \
+                 } \
+               else if (mblength == 0) \
+                 _x++; \
+               else \
+                 { \
+                   _p = _x; /* _p == start of prev mbchar */ \
+                   _x += mblength; \
+                 } \
+             } \
+           (_i) = _p; \
+         } \
+       else \
+         (_i)--; \
+      } \
+    while (0)
+#else
+#  define BACKUP_CHAR(_str, _strsize, _i)      (_i)--
+#endif  /* !HANDLE_MULTIBYTE */
+
+/* Back up one (possibly multibyte) character in the string _BASE of length
+   _STRSIZE starting at _STR (_BASE <= _STR <= (_BASE + _STRSIZE) ).
+   SPECIAL: DO NOT assume that _STR will be decremented by 1 after this call. */
+#if defined (HANDLE_MULTIBYTE)
+#  define BACKUP_CHAR_P(_base, _strsize, _str) \
+    do \
+      { \
+       if (MB_CUR_MAX > 1) \
+         { \
+           mbstate_t state_bak; \
+           size_t mblength; \
+           char *_x, _p; /* _x == temp pointer into string, _p == prev pointer */ \
+\
+           _x = _p = _base; \
+           while (_x < (_str)) \
+             { \
+               state_bak = state; \
+               mblength = mbrlen (_x, (_strsize) - _x, &state); \
+\
+               if (mblength == (size_t)-2 || mblength == (size_t)-1) \
+                 { \
+                   state = state_bak; \
+                   _x++; \
+                 } \
+               else if (mblength == 0) \
+                 _x++; \
+               else \
+                 { \
+                   _p = _x; /* _p == start of prev mbchar */ \
+                   _x += mblength; \
+                 } \
+             } \
+           (_str) = _p; \
+         } \
+       else \
+         (_str)--; \
+      } \
+    while (0)
+#else
+#  define BACKUP_CHAR_P(_base, _strsize, _str) (_str)--
+#endif  /* !HANDLE_MULTIBYTE */
+
+/* Copy a single character from the string _SRC to the string _DST.
+   _SRCEND is a pointer to the end of _SRC. */
+#if defined (HANDLE_MULTIBYTE)
+#  define COPY_CHAR_P(_dst, _src, _srcend) \
+    do \
+      { \
+       if (MB_CUR_MAX > 1) \
+         { \
+           mbstate_t state_bak; \
+           size_t mblength; \
+           int _k; \
+\
+           state_bak = state; \
+           mblength = mbrlen ((_src), (_srcend) - (_src), &state); \
+           if (mblength == (size_t)-2 || mblength == (size_t)-1) \
+             { \
+               state = state_bak; \
+               mblength = 1; \
+             } \
+           else \
+             mblength = (mblength < 1) ? 1 : mblength; \
+\
+           for (_k = 0; _k < mblength; _k++) \
+             *(_dst)++ = *(_src)++; \
+         } \
+       else \
+         *(_dst)++ = *(_src)++; \
+      } \
+    while (0)
+#else
+#  define COPY_CHAR_P(_dst, _src, _srcend)     *(_dst)++ = *(_src)++
+#endif  /* !HANDLE_MULTIBYTE */
+
+/* Copy a single character from the string _SRC at index _SI to the string
+   _DST at index _DI.  _SRCEND is a pointer to the end of _SRC. */
+#if defined (HANDLE_MULTIBYTE)
+#  define COPY_CHAR_I(_dst, _di, _src, _srcend, _si) \
+    do \
+      { \
+       if (MB_CUR_MAX > 1) \
+         { \
+           mbstate_t state_bak; \
+           size_t mblength; \
+           int _k; \
+\
+           state_bak = state; \
+           mblength = mbrlen ((_src) + (_si), (_srcend) - ((_src)+(_si)), &state); \
+           if (mblength == (size_t)-2 || mblength == (size_t)-1) \
+             { \
+               state = state_bak; \
+               mblength = 1; \
+             } \
+           else \
+             mblength = (mblength < 1) ? 1 : mblength; \
+\
+           for (_k = 0; _k < mblength; _k++) \
+             _dst[_di++] = _src[_si++]; \
+         } \
+       else \
+         _dst[_di++] = _src[_si++]; \
+      } \
+    while (0)
+#else
+#  define COPY_CHAR_I(_dst, _di, _src, _srcend, _si)   _dst[_di++] = _src[_si++]
+#endif  /* !HANDLE_MULTIBYTE */
+
+/****************************************************************
+ *                                                             *
+ * The following are only guaranteed to work in subst.c                *
+ *                                                             *
+ ****************************************************************/
+
+#if defined (HANDLE_MULTIBYTE)
+#  define SCOPY_CHAR_I(_dst, _escchar, _sc, _src, _si, _slen) \
+    do \
+      { \
+       if (MB_CUR_MAX > 1) \
+         { \
+           mbstate_t state_bak; \
+           size_t mblength; \
+           int _i; \
+\
+           state_bak = state; \
+           mblength = mbrlen ((_src) + (_si), (_slen) - (_si), &state); \
+           if (mblength == (size_t)-2 || mblength == (size_t)-1) \
+             { \
+               state = state_bak; \
+               mblength = 1; \
+             } \
+           else \
+             mblength = (mblength < 1) ? 1 : mblength; \
+\
+           temp = xmalloc (mblength + 2); \
+           temp[0] = _escchar; \
+           for (_i = 0; _i < mblength; _i++) \
+             temp[_i + 1] = _src[_si++]; \
+           temp[mblength + 1] = '\0'; \
+\
+           goto add_string; \
+         } \
+       else \
+         { \
+           _dst[0] = _escchar; \
+           _dst[1] = _sc; \
+         } \
+      } \
+    while (0)
+#else
+#  define SCOPY_CHAR_I(_dst, _escchar, _sc, _src, _si, _slen) \
+    _dst[0] = _escchar; \
+    _dst[1] = _sc
+#endif  /* !HANDLE_MULTIBYTE */
+
+#if defined (HANDLE_MULTIBYTE)
+#  define SCOPY_CHAR_M(_dst, _src, _srcend, _si) \
+    do \
+      { \
+       if (MB_CUR_MAX > 1) \
+         { \
+           mbstate_t state_bak; \
+           size_t mblength; \
+\
+           state_bak = state; \
+           mblength = mbrlen ((_src) + (_si), (_srcend) - ((_src) + (_si)), &state); \
+           if (mblength == (size_t)-2 || mblength == (size_t)-1) \
+             { \
+               state = state_bak; \
+               mblength = 1; \
+             } \
+           else \
+             mblength = (mblength < 1) ? 1 : mblength; \
+\
+           FASTCOPY(((_src) + (_si)), (_dst), mblength); \
+\
+           (_dst) += mblength; \
+           (_si) += mblength; \
+         } \
+       else \
+         { \
+           *(_dst)++ = _src[(_si)]; \
+           (_si)++; \
+         } \
+      } \
+    while (0)
+#else
+#  define SCOPY_CHAR_M(_dst, _src, _srcend, _si) \
+       *(_dst)++ = _src[(_si)]; \
+       (_si)++
+#endif  /* !HANDLE_MULTIBYTE */
+
+#if HANDLE_MULTIBYTE
+#  define SADD_MBCHAR(_dst, _src, _si, _srcsize) \
+    do \
+      { \
+       if (MB_CUR_MAX > 1) \
+         { \
+           int i; \
+           mbstate_t state_bak; \
+           size_t mblength; \
+\
+           state_bak = state; \
+           mblength = mbrlen ((_src) + (_si), (_srcsize) - (_si), &state); \
+           if (mblength == (size_t)-1 || mblength == (size_t)-2) \
+             { \
+               state = state_bak; \
+               mblength = 1; \
+             } \
+           if (mblength < 1) \
+             mblength = 1; \
+\
+           _dst = (char *)xmalloc (mblength + 1); \
+           for (i = 0; i < mblength; i++) \
+             (_dst)[i] = (_src)[(_si)++]; \
+           (_dst)[mblength] = '\0'; \
+\
+           goto add_string; \
+         } \
+      } \
+    while (0)
+
+#else
+#  define SADD_MBCHAR(_dst, _src, _si, _srcsize)
+#endif
+
+/* Watch out when using this -- it's just straight textual subsitution */
+#if defined (HANDLE_MULTIBYTE)
+#  define SADD_MBQCHAR_BODY(_dst, _src, _si, _srcsize) \
+\
+           int i; \
+           mbstate_t state_bak; \
+           size_t mblength; \
+\
+           state_bak = state; \
+           mblength = mbrlen ((_src) + (_si), (_srcsize) - (_si), &state); \
+           if (mblength == (size_t)-1 || mblength == (size_t)-2) \
+             { \
+               state = state_bak; \
+               mblength = 1; \
+             } \
+           if (mblength < 1) \
+             mblength = 1; \
+\
+           (_dst) = (char *)xmalloc (mblength + 2); \
+           (_dst)[0] = CTLESC; \
+           for (i = 0; i < mblength; i++) \
+             (_dst)[i+1] = (_src)[(_si)++]; \
+           (_dst)[mblength+1] = '\0'; \
+\
+           goto add_string
+
+#endif /* HANDLE_MULTIBYTE */
+#endif /* _SH_MBUTIL_H_ */
diff --git a/jobs.c b/jobs.c
index 8418267b638cc783f385b8f06adadc6739ff253f..f2da4ae4b5535fee51821f3eec8007c31b51dee4 100644 (file)
--- a/jobs.c
+++ b/jobs.c
@@ -997,7 +997,7 @@ describe_pid (pid)
   job = find_job (pid, 0);
 
   if (job != NO_JOB)
-    printf ("[%d] %ld\n", job + 1, (long)pid);
+    fprintf (stderr, "[%d] %ld\n", job + 1, (long)pid);
   else
     programming_error (_("describe_pid: %ld: no such pid"), (long)pid);
 
@@ -2311,12 +2311,12 @@ start_job (job, foreground)
   p = jobs[job]->pipe;
 
   if (foreground == 0)
-    fprintf (stderr, "[%d]%c ", job + 1,
+    printf ("[%d]%c ", job + 1,
           (job == current_job) ? '+': ((job == previous_job) ? '-' : ' '));
 
   do
     {
-      fprintf (stderr, "%s%s",
+      printf ("%s%s",
               p->command ? p->command : "",
               p->next != jobs[job]->pipe? " | " : "");
       p = p->next;
@@ -2324,12 +2324,12 @@ start_job (job, foreground)
   while (p != jobs[job]->pipe);
 
   if (foreground == 0)
-    fprintf (stderr, " &");
+    printf (" &");
 
   if (strcmp (wd, jobs[job]->wd) != 0)
-    fprintf (stderr, " (wd: %s)", polite_directory_format (jobs[job]->wd));
+    printf ("  (wd: %s)", polite_directory_format (jobs[job]->wd));
 
-  fprintf (stderr, "\n");
+  printf ("\n");
 
   /* Run the job. */
   if (already_running == 0)
diff --git a/jobs.c~ b/jobs.c~
new file mode 100644 (file)
index 0000000..7ad91c8
--- /dev/null
+++ b/jobs.c~
@@ -0,0 +1,3535 @@
+/* The thing that makes children, remembers them, and contains wait loops. */
+
+/* This file works with both POSIX and BSD systems.  It implements job
+   control. */
+
+/* Copyright (C) 1989-2003 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 2, 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, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+
+#include "config.h"
+
+#include "bashtypes.h"
+#include "trap.h"
+#include <stdio.h>
+#include <signal.h>
+#include <errno.h>
+
+#if defined (HAVE_UNISTD_H)
+#  include <unistd.h>
+#endif
+
+#include "posixtime.h"
+
+#if defined (HAVE_SYS_RESOURCE_H) && defined (HAVE_WAIT3) && !defined (_POSIX_VERSION) && !defined (RLIMTYPE)
+#  include <sys/resource.h>
+#endif /* !_POSIX_VERSION && HAVE_SYS_RESOURCE_H && HAVE_WAIT3 && !RLIMTYPE */
+
+#if defined (HAVE_SYS_FILE_H)
+#  include <sys/file.h>
+#endif
+
+#include "filecntl.h"
+#include <sys/ioctl.h>
+#include <sys/param.h>
+
+#if defined (BUFFERED_INPUT)
+#  include "input.h"
+#endif
+
+/* Need to include this up here for *_TTY_DRIVER definitions. */
+#include "shtty.h"
+
+/* Define this if your output is getting swallowed.  It's a no-op on
+   machines with the termio or termios tty drivers. */
+/* #define DRAIN_OUTPUT */
+
+/* For the TIOCGPGRP and TIOCSPGRP ioctl parameters on HP-UX */
+#if defined (hpux) && !defined (TERMIOS_TTY_DRIVER)
+#  include <bsdtty.h>
+#endif /* hpux && !TERMIOS_TTY_DRIVER */
+
+#if !defined (STRUCT_WINSIZE_IN_SYS_IOCTL)
+/* For struct winsize on SCO */
+/*   sys/ptem.h has winsize but needs mblk_t from sys/stream.h */
+#  if defined (HAVE_SYS_PTEM_H) && defined (TIOCGWINSZ) && defined (SIGWINCH)
+#    if defined (HAVE_SYS_STREAM_H)
+#      include <sys/stream.h>
+#    endif
+#    include <sys/ptem.h>
+#  endif /* HAVE_SYS_PTEM_H && TIOCGWINSZ && SIGWINCH */
+#endif /* !STRUCT_WINSIZE_IN_SYS_IOCTL */
+
+#include "bashansi.h"
+#include "bashintl.h"
+#include "shell.h"
+#include "jobs.h"
+#include "flags.h"
+
+#include "builtins/builtext.h"
+#include "builtins/common.h"
+
+#if !defined (errno)
+extern int errno;
+#endif /* !errno */
+
+#define DEFAULT_CHILD_MAX 32
+#define MAX_JOBS_IN_ARRAY 4096         /* testing */
+
+/* Take care of system dependencies that must be handled when waiting for
+   children.  The arguments to the WAITPID macro match those to the Posix.1
+   waitpid() function. */
+
+#if defined (ultrix) && defined (mips) && defined (_POSIX_VERSION)
+#  define WAITPID(pid, statusp, options) \
+       wait3 ((union wait *)statusp, options, (struct rusage *)0)
+#else
+#  if defined (_POSIX_VERSION) || defined (HAVE_WAITPID)
+#    define WAITPID(pid, statusp, options) \
+       waitpid ((pid_t)pid, statusp, options)
+#  else
+#    if defined (HAVE_WAIT3)
+#      define WAITPID(pid, statusp, options) \
+       wait3 (statusp, options, (struct rusage *)0)
+#    else
+#      define WAITPID(pid, statusp, options) \
+       wait3 (statusp, options, (int *)0)
+#    endif /* HAVE_WAIT3 */
+#  endif /* !_POSIX_VERSION && !HAVE_WAITPID*/
+#endif /* !(Ultrix && mips && _POSIX_VERSION) */
+
+/* getpgrp () varies between systems.  Even systems that claim to be
+   Posix.1 compatible lie sometimes (Ultrix, SunOS4, apollo). */
+#if defined (GETPGRP_VOID)
+#  define getpgid(p) getpgrp ()
+#else
+#  define getpgid(p) getpgrp (p)
+#endif /* !GETPGRP_VOID */
+
+/* If the system needs it, REINSTALL_SIGCHLD_HANDLER will reinstall the
+   handler for SIGCHLD. */
+#if defined (MUST_REINSTALL_SIGHANDLERS)
+#  define REINSTALL_SIGCHLD_HANDLER signal (SIGCHLD, sigchld_handler)
+#else
+#  define REINSTALL_SIGCHLD_HANDLER
+#endif /* !MUST_REINSTALL_SIGHANDLERS */
+
+/* Some systems let waitpid(2) tell callers about stopped children. */
+#if !defined (WCONTINUED)
+#  define WCONTINUED 0
+#endif
+#if !defined (WIFCONTINUED)
+#  define WIFCONTINUED(s)      (0)
+#endif
+
+/* The number of additional slots to allocate when we run out. */
+#define JOB_SLOTS 8
+
+typedef int sh_job_map_func_t __P((JOB *, int, int, int));
+
+#if defined (READLINE)
+extern void rl_set_screen_size __P((int, int));
+#endif
+
+/* Variables used here but defined in other files. */
+extern int subshell_environment, line_number;
+extern int posixly_correct, shell_level;
+extern int interrupt_immediately;
+extern int last_command_exit_value, last_command_exit_signal;
+extern int loop_level, breaking;
+extern int sourcelevel;
+extern sh_builtin_func_t *this_shell_builtin;
+extern char *shell_name, *this_command_name;
+extern sigset_t top_level_mask;
+extern procenv_t wait_intr_buf;
+extern int wait_signal_received;
+extern WORD_LIST *subst_assign_varlist;
+
+/* The array of known jobs. */
+JOB **jobs = (JOB **)NULL;
+
+/* The number of slots currently allocated to JOBS. */
+int job_slots = 0;
+
+/* The controlling tty for this shell. */
+int shell_tty = -1;
+
+/* The shell's process group. */
+pid_t shell_pgrp = NO_PID;
+
+/* The terminal's process group. */
+pid_t terminal_pgrp = NO_PID;
+
+/* The process group of the shell's parent. */
+pid_t original_pgrp = NO_PID;
+
+/* The process group of the pipeline currently being made. */
+pid_t pipeline_pgrp = (pid_t)0;
+
+#if defined (PGRP_PIPE)
+/* Pipes which each shell uses to communicate with the process group leader
+   until all of the processes in a pipeline have been started.  Then the
+   process leader is allowed to continue. */
+int pgrp_pipe[2] = { -1, -1 };
+#endif
+
+/* The job which is current; i.e. the one that `%+' stands for. */
+int current_job = NO_JOB;
+
+/* The previous job; i.e. the one that `%-' stands for. */
+int previous_job = NO_JOB;
+
+/* Last child made by the shell.  */
+pid_t last_made_pid = NO_PID;
+
+/* Pid of the last asynchronous child. */
+pid_t last_asynchronous_pid = NO_PID;
+
+/* The pipeline currently being built. */
+PROCESS *the_pipeline = (PROCESS *)NULL;
+
+/* If this is non-zero, do job control. */
+int job_control = 1;
+
+/* Call this when you start making children. */
+int already_making_children = 0;
+
+/* If this is non-zero, $LINES and $COLUMNS are reset after every process
+   exits from get_tty_state(). */
+int check_window_size;
+
+/* Functions local to this file. */
+
+static void get_new_window_size __P((int));
+
+static void run_sigchld_trap __P((int));
+
+static sighandler wait_sigint_handler __P((int));
+static sighandler sigchld_handler __P((int));
+static sighandler sigwinch_sighandler __P((int));
+static sighandler sigcont_sighandler __P((int));
+static sighandler sigstop_sighandler __P((int));
+
+static int waitchld __P((pid_t, int));
+
+static PROCESS *find_pipeline __P((pid_t, int, int *));
+
+static char *current_working_directory __P((void));
+static char *job_working_directory __P((void));
+static char *j_strsignal __P((int));
+static char *printable_job_status __P((int, PROCESS *, int));
+
+static pid_t find_last_pid __P((int, int));
+
+static int set_new_line_discipline __P((int));
+static int map_over_jobs __P((sh_job_map_func_t *, int, int));
+static int job_last_stopped __P((int));
+static int job_last_running __P((int));
+static int most_recent_job_in_state __P((int, JOB_STATE));
+static int find_job __P((pid_t, int));
+static int print_job __P((JOB *, int, int, int));
+static int process_exit_status __P((WAIT));
+static int process_exit_signal __P((WAIT));
+static int job_exit_status __P((int));
+static int job_exit_signal __P((int));
+static int set_job_status_and_cleanup __P((int));
+
+static WAIT raw_job_exit_status __P((int));
+
+static void notify_of_job_status __P((void));
+static void cleanup_dead_jobs __P((void));
+static int compact_jobs_list __P((int));
+static void discard_pipeline __P((PROCESS *));
+static void add_process __P((char *, pid_t));
+static void print_pipeline __P((PROCESS *, int, int, FILE *));
+static void pretty_print_job __P((int, int, FILE *));
+static void set_current_job __P((int));
+static void reset_current __P((void));
+static void set_job_running __P((int));
+static void setjstatus __P((int));
+static void mark_all_jobs_as_dead __P((void));
+static void mark_dead_jobs_as_notified __P((int));
+static void restore_sigint_handler __P((void));
+#if defined (PGRP_PIPE)
+static void pipe_read __P((int *));
+static void pipe_close __P((int *));
+#endif
+
+#if defined (ARRAY_VARS)
+static int *pstatuses;         /* list of pipeline statuses */
+static int statsize;
+#endif
+
+/* Used to synchronize between wait_for and other functions and the SIGCHLD
+   signal handler. */
+static int sigchld;
+static int queue_sigchld;
+
+#define QUEUE_SIGCHLD(os)      (os) = sigchld, queue_sigchld++
+
+#define UNQUEUE_SIGCHLD(os) \
+       do { \
+         queue_sigchld--; \
+         if (queue_sigchld == 0 && os != sigchld) \
+           waitchld (-1, 0); \
+       } while (0)
+
+static SigHandler *old_tstp, *old_ttou, *old_ttin;
+static SigHandler *old_cont = (SigHandler *)SIG_DFL;
+
+#if defined (TIOCGWINSZ) && defined (SIGWINCH)
+static SigHandler *old_winch = (SigHandler *)SIG_DFL;
+#endif
+
+/* A place to temporarily save the current pipeline. */
+static PROCESS *saved_pipeline;
+static int saved_already_making_children;
+
+/* Set this to non-zero whenever you don't want the jobs list to change at
+   all: no jobs deleted and no status change notifications.  This is used,
+   for example, when executing SIGCHLD traps, which may run arbitrary
+   commands. */
+static int jobs_list_frozen;
+
+static char retcode_name_buffer[64];
+
+static long child_max = -1L;
+
+#if !defined (_POSIX_VERSION)
+
+/* These are definitions to map POSIX 1003.1 functions onto existing BSD
+   library functions and system calls. */
+#define setpgid(pid, pgrp)     setpgrp (pid, pgrp)
+#define tcsetpgrp(fd, pgrp)    ioctl ((fd), TIOCSPGRP, &(pgrp))
+
+pid_t
+tcgetpgrp (fd)
+     int fd;
+{
+  pid_t pgrp;
+
+  /* ioctl will handle setting errno correctly. */
+  if (ioctl (fd, TIOCGPGRP, &pgrp) < 0)
+    return (-1);
+  return (pgrp);
+}
+
+#endif /* !_POSIX_VERSION */
+
+/* Return the working directory for the current process.  Unlike
+   job_working_directory, this does not call malloc (), nor do any
+   of the functions it calls.  This is so that it can safely be called
+   from a signal handler. */
+static char *
+current_working_directory ()
+{
+  char *dir;
+  static char d[PATH_MAX];
+
+  dir = get_string_value ("PWD");
+
+  if (dir == 0 && the_current_working_directory && no_symbolic_links)
+    dir = the_current_working_directory;
+
+  if (dir == 0)
+    {
+      dir = getcwd (d, sizeof(d));
+      if (dir)
+       dir = d;
+    }
+
+  return (dir == 0) ? "<unknown>" : dir;
+}
+
+/* Return the working directory for the current process. */
+static char *
+job_working_directory ()
+{
+  char *dir;
+
+  dir = get_string_value ("PWD");
+  if (dir)
+    return (savestring (dir));
+
+  dir = get_working_directory ("job-working-directory");
+  if (dir)
+    return (dir);
+
+  return (savestring ("<unknown>"));
+}
+
+void
+making_children ()
+{
+  if (already_making_children)
+    return;
+
+  already_making_children = 1;
+  start_pipeline ();
+}
+
+void
+stop_making_children ()
+{
+  already_making_children = 0;
+}
+
+void
+cleanup_the_pipeline ()
+{
+  if (the_pipeline)
+    {
+      discard_pipeline (the_pipeline);
+      the_pipeline = (PROCESS *)NULL;
+    }
+}
+
+void
+save_pipeline (clear)
+     int clear;
+{
+  saved_pipeline = the_pipeline;
+  saved_already_making_children = already_making_children;
+  if (clear)
+    the_pipeline = (PROCESS *)NULL;
+}
+
+void
+restore_pipeline (discard)
+     int discard;
+{
+  PROCESS *old_pipeline;
+
+  old_pipeline = the_pipeline;
+  the_pipeline = saved_pipeline;
+  already_making_children = saved_already_making_children;
+  if (discard)
+    discard_pipeline (old_pipeline);
+}
+
+/* Start building a pipeline.  */
+void
+start_pipeline ()
+{
+  if (the_pipeline)
+    {
+      cleanup_the_pipeline ();
+      pipeline_pgrp = 0;
+#if defined (PGRP_PIPE)
+      pipe_close (pgrp_pipe);
+#endif
+    }
+
+#if defined (PGRP_PIPE)
+  if (job_control)
+    {
+      if (pipe (pgrp_pipe) == -1)
+       sys_error ("start_pipeline: pgrp pipe");
+    }
+#endif
+}
+
+/* Stop building a pipeline.  Install the process list in the job array.
+   This returns the index of the newly installed job.
+   DEFERRED is a command structure to be executed upon satisfactory
+   execution exit of this pipeline. */
+int
+stop_pipeline (async, deferred)
+     int async;
+     COMMAND *deferred;
+{
+  register int i, j;
+  JOB *newjob;
+  sigset_t set, oset;
+
+  BLOCK_CHILD (set, oset);
+
+#if defined (PGRP_PIPE)
+  /* The parent closes the process group synchronization pipe. */
+  pipe_close (pgrp_pipe);
+#endif
+
+  cleanup_dead_jobs ();
+
+  if (job_slots == 0)
+    {
+      job_slots = JOB_SLOTS;
+      jobs = (JOB **)xmalloc (job_slots * sizeof (JOB *));
+
+      /* Now blank out these new entries. */
+      for (i = 0; i < job_slots; i++)
+       jobs[i] = (JOB *)NULL;
+    }
+
+  /* Scan from the last slot backward, looking for the next free one. */
+  /* XXX - revisit this interactive assumption */
+  if (interactive)
+    {
+      for (i = job_slots; i; i--)
+       if (jobs[i - 1])
+         break;
+    }
+  else
+    {
+      /* If we're not interactive, we don't need to monotonically increase
+        the job number (in fact, we don't care about the job number at all),
+        so we can simply scan for the first free slot.  This helps to keep
+        us from continuously reallocating the jobs array when running
+        certain kinds of shell loops, and saves time spent searching. */
+      for (i = 0; i < job_slots; i++)
+       if (jobs[i] == 0)
+         break;
+    }
+
+  /* Do we need more room? */
+
+  /* First try compaction */
+  if (subshell_environment && interactive_shell && i == job_slots && job_slots >= MAX_JOBS_IN_ARRAY)
+    i = compact_jobs_list (0);
+
+  /* If we can't compact, reallocate */
+  if (i == job_slots)
+    {
+      job_slots += JOB_SLOTS;
+      jobs = (JOB **)xrealloc (jobs, ((1 + job_slots) * sizeof (JOB *)));
+
+      for (j = i; j < job_slots; j++)
+       jobs[j] = (JOB *)NULL;
+    }
+
+  /* Add the current pipeline to the job list. */
+  if (the_pipeline)
+    {
+      register PROCESS *p;
+      int any_alive, any_stopped;
+
+      newjob = (JOB *)xmalloc (sizeof (JOB));
+
+      for (p = the_pipeline; p->next != the_pipeline; p = p->next)
+       ;
+      p->next = (PROCESS *)NULL;
+      newjob->pipe = REVERSE_LIST (the_pipeline, PROCESS *);
+      for (p = newjob->pipe; p->next; p = p->next)
+       ;
+      p->next = newjob->pipe;
+
+      the_pipeline = (PROCESS *)NULL;
+      newjob->pgrp = pipeline_pgrp;
+      pipeline_pgrp = 0;
+
+      newjob->flags = 0;
+
+      /* Flag to see if in another pgrp. */
+      if (job_control)
+       newjob->flags |= J_JOBCONTROL;
+
+      /* Set the state of this pipeline. */
+      p = newjob->pipe;
+      any_alive = any_stopped = 0;
+      do
+       {
+         any_alive |= p->running;
+         any_stopped |= WIFSTOPPED (p->status);
+         p = p->next;
+       }
+      while (p != newjob->pipe);
+
+      newjob->state = any_alive ? JRUNNING : (any_stopped ? JSTOPPED : JDEAD);
+      newjob->wd = job_working_directory ();
+      newjob->deferred = deferred;
+
+      newjob->j_cleanup = (sh_vptrfunc_t *)NULL;
+      newjob->cleanarg = (PTR_T) NULL;
+
+      jobs[i] = newjob;
+      if (newjob->state == JDEAD && (newjob->flags & J_FOREGROUND))
+       setjstatus (i);
+    }
+  else
+    newjob = (JOB *)NULL;
+
+  if (async)
+    {
+      if (newjob)
+       newjob->flags &= ~J_FOREGROUND;
+      reset_current ();
+    }
+  else
+    {
+      if (newjob)
+       {
+         newjob->flags |= J_FOREGROUND;
+         /*
+          *            !!!!! NOTE !!!!!  (chet@ins.cwru.edu)
+          *
+          * The currently-accepted job control wisdom says to set the
+          * terminal's process group n+1 times in an n-step pipeline:
+          * once in the parent and once in each child.  This is where
+          * the parent gives it away.
+          *
+          */
+         if (job_control && newjob->pgrp)
+           give_terminal_to (newjob->pgrp, 0);
+       }
+    }
+
+  stop_making_children ();
+  UNBLOCK_CHILD (oset);
+  return (current_job);
+}
+
+/* Delete all DEAD jobs that the user had received notification about. */
+static void
+cleanup_dead_jobs ()
+{
+  register int i;
+  int os;
+
+  if (job_slots == 0 || jobs_list_frozen)
+    return;
+
+  QUEUE_SIGCHLD(os);
+
+  for (i = 0; i < job_slots; i++)
+    if (jobs[i] && DEADJOB (i) && IS_NOTIFIED (i))
+      delete_job (i, 0);
+
+  UNQUEUE_SIGCHLD(os);
+}
+
+/* Compact the jobs list by removing dead jobs.  Assumed that we have filled
+   the jobs array to some predefined maximum.  Called when the shell is not
+   the foreground process (subshell_environment != 0).  Returns the first
+   available slot in the compacted list.  If that value is job_slots, then
+   the list needs to be reallocated.  The jobs array is in new memory if
+   this returns > 0 and < job_slots.  FLAGS is reserved for future use. */
+static int
+compact_jobs_list (flags)
+     int flags;
+{
+  sigset_t set, oset;
+  register int i, j;
+  int nremove, ndel;
+  JOB **newlist;
+
+  if (job_slots == 0 || jobs_list_frozen)
+    return job_slots;
+
+  if (child_max < 0)
+    child_max = getmaxchild ();
+
+  /* Take out at most a quarter of the jobs in the jobs array, but leave at
+     least child_max */
+  nremove = job_slots >> 2;
+  if ((job_slots - nremove) < child_max)
+    nremove = job_slots - child_max;
+
+  /* need to increase jobs list to at least CHILD_MAX entries */
+  if (nremove < 0)
+    return job_slots;
+
+  BLOCK_CHILD (set, oset);  
+
+  for (ndel = i = 0; i < job_slots; i++)
+    if (jobs[i])
+      {
+       if (DEADJOB (i) && (find_last_pid (i, 0) != last_asynchronous_pid))
+         {
+           delete_job (i, 0);
+           ndel++;
+           if (ndel == nremove)
+             break;
+         }
+      }
+
+  if (ndel == 0)
+    {
+      UNBLOCK_CHILD (oset);
+      return job_slots;
+    }
+
+  newlist = (JOB **)xmalloc ((1 + job_slots) * sizeof (JOB *));
+  for (i = j = 0; i < job_slots; i++)
+    if (jobs[i])
+      newlist[j++] = jobs[i];
+
+  ndel = j;
+  for ( ; j < job_slots; j++)
+    newlist[j] = (JOB *)NULL;
+
+  free (jobs);
+  jobs = newlist;
+
+  UNBLOCK_CHILD (oset);
+
+  return ndel;
+}
+
+/* Delete the job at INDEX from the job list.  Must be called
+   with SIGCHLD blocked. */
+void
+delete_job (job_index, warn_stopped)
+     int job_index, warn_stopped;
+{
+  register JOB *temp;
+
+  if (job_slots == 0 || jobs_list_frozen)
+    return;
+
+  if (warn_stopped && subshell_environment == 0 && STOPPED (job_index))
+    internal_warning (_("deleting stopped job %d with process group %ld"), job_index+1, (long)jobs[job_index]->pgrp);
+
+  temp = jobs[job_index];
+  if (job_index == current_job || job_index == previous_job)
+    reset_current ();
+
+  jobs[job_index] = (JOB *)NULL;
+
+  free (temp->wd);
+  discard_pipeline (temp->pipe);
+
+  if (temp->deferred)
+    dispose_command (temp->deferred);
+
+  free (temp);
+}
+
+/* Must be called with SIGCHLD blocked. */
+void
+nohup_job (job_index)
+     int job_index;
+{
+  register JOB *temp;
+
+  if (job_slots == 0)
+    return;
+
+  if (temp = jobs[job_index])
+    temp->flags |= J_NOHUP;
+}
+
+/* Get rid of the data structure associated with a process chain. */
+static void
+discard_pipeline (chain)
+     register PROCESS *chain;
+{
+  register PROCESS *this, *next;
+
+  this = chain;
+  do
+    {
+      next = this->next;
+      FREE (this->command);
+      free (this);
+      this = next;
+    }
+  while (this != chain);
+}
+
+/* Add this process to the chain being built in the_pipeline.
+   NAME is the command string that will be exec'ed later.
+   PID is the process id of the child. */
+static void
+add_process (name, pid)
+     char *name;
+     pid_t pid;
+{
+  PROCESS *t, *p;
+
+  t = (PROCESS *)xmalloc (sizeof (PROCESS));
+  t->next = the_pipeline;
+  t->pid = pid;
+  WSTATUS (t->status) = 0;
+  t->running = PS_RUNNING;
+  t->command = name;
+  the_pipeline = t;
+
+  if (t->next == 0)
+    t->next = t;
+  else
+    {
+      p = t->next;
+      while (p->next != t->next)
+       p = p->next;
+      p->next = t;
+    }
+}
+
+#if 0
+/* Take the last job and make it the first job.  Must be called with
+   SIGCHLD blocked. */
+int
+rotate_the_pipeline ()
+{
+  PROCESS *p;
+
+  if (the_pipeline->next == the_pipeline)
+    return;
+  for (p = the_pipeline; p->next != the_pipeline; p = p->next)
+    ;
+  the_pipeline = p;
+}
+
+/* Reverse the order of the processes in the_pipeline.  Must be called with
+   SIGCHLD blocked. */
+int
+reverse_the_pipeline ()
+{
+  PROCESS *p, *n;
+
+  if (the_pipeline->next == the_pipeline)
+    return;
+
+  for (p = the_pipeline; p->next != the_pipeline; p = p->next)
+    ;
+  p->next = (PROCESS *)NULL;
+
+  n = REVERSE_LIST (the_pipeline, PROCESS *);
+
+  the_pipeline = n;
+  for (p = the_pipeline; p->next; p = p->next)
+    ;
+  p->next = the_pipeline;
+}
+#endif
+
+/* Map FUNC over the list of jobs.  If FUNC returns non-zero,
+   then it is time to stop mapping, and that is the return value
+   for map_over_jobs.  FUNC is called with a JOB, arg1, arg2,
+   and INDEX. */
+static int
+map_over_jobs (func, arg1, arg2)
+     sh_job_map_func_t *func;
+     int arg1, arg2;
+{
+  register int i;
+  int result;
+  sigset_t set, oset;
+
+  if (job_slots == 0)
+    return 0;
+
+  BLOCK_CHILD (set, oset);
+
+  for (i = result = 0; i < job_slots; i++)
+    {
+      if (jobs[i])
+       {
+         result = (*func)(jobs[i], arg1, arg2, i);
+         if (result)
+           break;
+       }
+    }
+
+  UNBLOCK_CHILD (oset);
+
+  return (result);
+}
+
+/* Cause all the jobs in the current pipeline to exit. */
+void
+terminate_current_pipeline ()
+{
+  if (pipeline_pgrp && pipeline_pgrp != shell_pgrp)
+    {
+      killpg (pipeline_pgrp, SIGTERM);
+      killpg (pipeline_pgrp, SIGCONT);
+    }
+}
+
+/* Cause all stopped jobs to exit. */
+void
+terminate_stopped_jobs ()
+{
+  register int i;
+
+  for (i = 0; i < job_slots; i++)
+    {
+      if (jobs[i] && STOPPED (i))
+       {
+         killpg (jobs[i]->pgrp, SIGTERM);
+         killpg (jobs[i]->pgrp, SIGCONT);
+       }
+    }
+}
+
+/* Cause all jobs, running or stopped, to receive a hangup signal.  If
+   a job is marked J_NOHUP, don't send the SIGHUP. */
+void
+hangup_all_jobs ()
+{
+  register int i;
+
+  for (i = 0; i < job_slots; i++)
+    {
+      if (jobs[i])
+       {
+         if  ((jobs[i]->flags & J_NOHUP) == 0)
+           killpg (jobs[i]->pgrp, SIGHUP);
+         if (STOPPED (i))
+           killpg (jobs[i]->pgrp, SIGCONT);
+       }
+    }
+}
+
+void
+kill_current_pipeline ()
+{
+  stop_making_children ();
+  start_pipeline ();
+}
+
+/* Return the pipeline that PID belongs to.  Note that the pipeline
+   doesn't have to belong to a job.  Must be called with SIGCHLD blocked. */
+static PROCESS *
+find_pipeline (pid, running_only, jobp)
+     pid_t pid;
+     int running_only;
+     int *jobp;                /* index into jobs list or NO_JOB */
+{
+  int job;
+  register PROCESS *p;
+
+  /* See if this process is in the pipeline that we are building. */
+  if (jobp)
+    *jobp = NO_JOB;
+  if (the_pipeline)
+    {
+      p = the_pipeline;
+      do
+       {
+         /* Return it if we found it. */
+         if (p->pid == pid)
+           {
+             if ((running_only && PRUNNING(p)) || (running_only == 0))
+               return (p);
+           }
+
+         p = p->next;
+       }
+      while (p != the_pipeline);
+    }
+
+  job = find_job (pid, running_only);
+  if (jobp)
+    *jobp = job;
+  return (job == NO_JOB) ? (PROCESS *)NULL : jobs[job]->pipe;
+}
+
+/* Return the job index that PID belongs to, or NO_JOB if it doesn't
+   belong to any job.  Must be called with SIGCHLD blocked. */
+static int
+find_job (pid, running_only)
+     pid_t pid;
+     int running_only;
+{
+  register int i;
+  register PROCESS *p;
+
+  for (i = 0; i < job_slots; i++)
+    {
+      if (jobs[i])
+       {
+         p = jobs[i]->pipe;
+
+         do
+           {
+             if (p->pid == pid)
+               {
+                 if ((running_only && PRUNNING(p)) || (running_only == 0))
+                   return (i);
+               }
+
+             p = p->next;
+           }
+         while (p != jobs[i]->pipe);
+       }
+    }
+
+  return (NO_JOB);
+}
+
+/* Find a job given a PID.  If BLOCK is non-zero, block SIGCHLD as
+   required by find_job. */
+int
+get_job_by_pid (pid, block)
+     pid_t pid;
+     int block;
+{
+  int job;
+  sigset_t set, oset;
+
+  if (block)
+    BLOCK_CHILD (set, oset);
+
+  job = find_job (pid, 0);
+
+  if (block)
+    UNBLOCK_CHILD (oset);
+
+  return job;
+}
+
+/* Print descriptive information about the job with leader pid PID. */
+void
+describe_pid (pid)
+     pid_t pid;
+{
+  int job;
+  sigset_t set, oset;
+
+  BLOCK_CHILD (set, oset);
+
+  job = find_job (pid, 0);
+
+  if (job != NO_JOB)
+    fprintf (stderr, "[%d] %ld\n", job + 1, (long)pid);
+  else
+    programming_error (_("describe_pid: %ld: no such pid"), (long)pid);
+
+  UNBLOCK_CHILD (oset);
+}
+
+static char *
+j_strsignal (s)
+     int s;
+{
+  char *x;
+
+  x = strsignal (s);
+  if (x == 0)
+    {
+      x = retcode_name_buffer;
+      sprintf (x, "Signal %d", s);
+    }
+  return x;
+}
+
+static char *
+printable_job_status (j, p, format)
+     int j;
+     PROCESS *p;
+     int format;
+{
+  static char *temp;
+  int es;
+
+  temp = "Done";
+
+  if (STOPPED (j) && format == 0)
+    {
+      if (posixly_correct == 0 || p == 0 || (WIFSTOPPED (p->status) == 0))
+       temp = "Stopped";
+      else
+       {
+         temp = retcode_name_buffer;
+         sprintf (temp, "Stopped(%s)", signal_name (WSTOPSIG (p->status)));
+       }
+    }
+  else if (RUNNING (j))
+    temp = "Running";
+  else
+    {
+      if (WIFSTOPPED (p->status))
+       temp = j_strsignal (WSTOPSIG (p->status));
+      else if (WIFSIGNALED (p->status))
+       temp = j_strsignal (WTERMSIG (p->status));
+      else if (WIFEXITED (p->status))
+       {
+         temp = retcode_name_buffer;
+         es = WEXITSTATUS (p->status);
+         if (es == 0)
+           strcpy (temp, "Done");
+         else if (posixly_correct)
+           sprintf (temp, "Done(%d)", es);
+         else
+           sprintf (temp, "Exit %d", es);
+       }
+      else
+       temp = "Unknown status";
+    }
+
+  return temp;
+}
+
+/* This is the way to print out information on a job if you
+   know the index.  FORMAT is:
+
+    JLIST_NORMAL)   [1]+ Running          emacs
+    JLIST_LONG  )   [1]+ 2378 Running      emacs
+    -1   )   [1]+ 2378       emacs
+
+    JLIST_NORMAL)   [1]+ Stopped          ls | more
+    JLIST_LONG  )   [1]+ 2369 Stopped      ls
+                        2367       | more
+    JLIST_PID_ONLY)
+       Just list the pid of the process group leader (really
+       the process group).
+    JLIST_CHANGED_ONLY)
+       Use format JLIST_NORMAL, but list only jobs about which
+       the user has not been notified. */
+
+/* Print status for pipeline P.  If JOB_INDEX is >= 0, it is the index into
+   the JOBS array corresponding to this pipeline.  FORMAT is as described
+   above.  Must be called with SIGCHLD blocked.
+
+   If you're printing a pipeline that's not in the jobs array, like the
+   current pipeline as it's being created, pass -1 for JOB_INDEX */
+static void
+print_pipeline (p, job_index, format, stream)
+     PROCESS *p;
+     int job_index, format;
+     FILE *stream;
+{
+  PROCESS *first, *last, *show;
+  int es, name_padding;
+  char *temp;
+
+  if (p == 0)
+    return;
+
+  first = last = p;
+  while (last->next != first)
+    last = last->next;
+
+  for (;;)
+    {
+      if (p != first)
+       fprintf (stream, format ? "     " : " |");
+
+      if (format != JLIST_STANDARD)
+       fprintf (stream, "%5ld", (long)p->pid);
+
+      fprintf (stream, " ");
+
+      if (format > -1 && job_index >= 0)
+       {
+         show = format ? p : last;
+         temp = printable_job_status (job_index, show, format);
+
+         if (p != first)
+           {
+             if (format)
+               {
+                 if (show->running == first->running &&
+                     WSTATUS (show->status) == WSTATUS (first->status))
+                   temp = "";
+               }
+             else
+               temp = (char *)NULL;
+           }
+
+         if (temp)
+           {
+             fprintf (stream, "%s", temp);
+
+             es = STRLEN (temp);
+             if (es == 0)
+               es = 2; /* strlen ("| ") */
+             name_padding = LONGEST_SIGNAL_DESC - es;
+
+             fprintf (stream, "%*s", name_padding, "");
+
+             if ((WIFSTOPPED (show->status) == 0) &&
+                 (WIFCONTINUED (show->status) == 0) &&
+                 WIFCORED (show->status))
+               fprintf (stream, "(core dumped) ");
+           }
+       }
+
+      if (p != first && format)
+       fprintf (stream, "| ");
+
+      if (p->command)
+       fprintf (stream, "%s", p->command);
+
+      if (p == last && job_index >= 0)
+       {
+         temp = current_working_directory ();
+
+         if (RUNNING (job_index) && (IS_FOREGROUND (job_index) == 0))
+           fprintf (stream, " &");
+
+         if (strcmp (temp, jobs[job_index]->wd) != 0)
+           fprintf (stream,
+             "  (wd: %s)", polite_directory_format (jobs[job_index]->wd));
+       }
+
+      if (format || (p == last))
+       {
+         /* We need to add a CR only if this is an interactive shell, and
+            we're reporting the status of a completed job asynchronously.
+            We can't really check whether this particular job is being
+            reported asynchronously, so just add the CR if the shell is
+            currently interactive and asynchronous notification is enabled. */
+         if (asynchronous_notification && interactive)
+           fprintf (stream, "\r\n");
+         else
+           fprintf (stream, "\n");
+       }
+
+      if (p == last)
+       break;
+      p = p->next;
+    }
+  fflush (stream);
+}
+
+/* Print information to STREAM about jobs[JOB_INDEX] according to FORMAT.
+   Must be called with SIGCHLD blocked or queued with queue_sigchld */
+static void
+pretty_print_job (job_index, format, stream)
+     int job_index, format;
+     FILE *stream;
+{
+  register PROCESS *p;
+
+  /* Format only pid information about the process group leader? */
+  if (format == JLIST_PID_ONLY)
+    {
+      fprintf (stream, "%ld\n", (long)jobs[job_index]->pipe->pid);
+      return;
+    }
+
+  if (format == JLIST_CHANGED_ONLY)
+    {
+      if (IS_NOTIFIED (job_index))
+       return;
+      format = JLIST_STANDARD;
+    }
+
+  if (format != JLIST_NONINTERACTIVE)
+    fprintf (stream, "[%d]%c ", job_index + 1,
+             (job_index == current_job) ? '+':
+               (job_index == previous_job) ? '-' : ' ');
+
+  if (format == JLIST_NONINTERACTIVE)
+    format = JLIST_LONG;
+
+  p = jobs[job_index]->pipe;
+
+  print_pipeline (p, job_index, format, stream);
+
+  /* We have printed information about this job.  When the job's
+     status changes, waitchld () sets the notification flag to 0. */
+  jobs[job_index]->flags |= J_NOTIFIED;
+}
+
+static int
+print_job (job, format, state, job_index)
+     JOB *job;
+     int format, state, job_index;
+{
+  if (state == -1 || (JOB_STATE)state == job->state)
+    pretty_print_job (job_index, format, stdout);
+  return (0);
+}
+
+void
+list_one_job (job, format, ignore, job_index)
+     JOB *job;
+     int format, ignore, job_index;
+{
+  pretty_print_job (job_index, format, stdout);
+}
+
+void
+list_stopped_jobs (format)
+     int format;
+{
+  cleanup_dead_jobs ();
+  map_over_jobs (print_job, format, (int)JSTOPPED);
+}
+
+void
+list_running_jobs (format)
+     int format;
+{
+  cleanup_dead_jobs ();
+  map_over_jobs (print_job, format, (int)JRUNNING);
+}
+
+/* List jobs.  If FORMAT is non-zero, then the long form of the information
+   is printed, else just a short version. */
+void
+list_all_jobs (format)
+     int format;
+{
+  cleanup_dead_jobs ();
+  map_over_jobs (print_job, format, -1);
+}
+
+/* Fork, handling errors.  Returns the pid of the newly made child, or 0.
+   COMMAND is just for remembering the name of the command; we don't do
+   anything else with it.  ASYNC_P says what to do with the tty.  If
+   non-zero, then don't give it away. */
+pid_t
+make_child (command, async_p)
+     char *command;
+     int async_p;
+{
+  sigset_t set, oset;
+  pid_t pid;
+
+  sigemptyset (&set);
+  sigaddset (&set, SIGCHLD);
+  sigaddset (&set, SIGINT);
+  sigemptyset (&oset);
+  sigprocmask (SIG_BLOCK, &set, &oset);
+
+  making_children ();
+
+#if defined (BUFFERED_INPUT)
+  /* If default_buffered_input is active, we are reading a script.  If
+     the command is asynchronous, we have already duplicated /dev/null
+     as fd 0, but have not changed the buffered stream corresponding to
+     the old fd 0.  We don't want to sync the stream in this case. */
+  if (default_buffered_input != -1 &&
+      (!async_p || default_buffered_input > 0))
+    sync_buffered_stream (default_buffered_input);
+#endif /* BUFFERED_INPUT */
+
+  /* Create the child, handle severe errors. */
+  if ((pid = fork ()) < 0)
+    {
+      sys_error ("fork");
+
+      /* Kill all of the processes in the current pipeline. */
+      terminate_current_pipeline ();
+
+      /* Discard the current pipeline, if any. */
+      if (the_pipeline)
+       kill_current_pipeline ();
+
+      throw_to_top_level ();   /* Reset signals, etc. */
+    }
+
+  if (pid == 0)
+    {
+      /* In the child.  Give this child the right process group, set the
+        signals to the default state for a new process. */
+      pid_t mypid;
+
+      mypid = getpid ();
+#if defined (BUFFERED_INPUT)
+      /* Close default_buffered_input if it's > 0.  We don't close it if it's
+        0 because that's the file descriptor used when redirecting input,
+        and it's wrong to close the file in that case. */
+      unset_bash_input (0);
+#endif /* BUFFERED_INPUT */
+
+      /* Restore top-level signal mask. */
+      sigprocmask (SIG_SETMASK, &top_level_mask, (sigset_t *)NULL);
+
+      if (job_control)
+       {
+         /* All processes in this pipeline belong in the same
+            process group. */
+
+         if (pipeline_pgrp == 0)       /* This is the first child. */
+           pipeline_pgrp = mypid;
+
+         /* Check for running command in backquotes. */
+         if (pipeline_pgrp == shell_pgrp)
+           ignore_tty_job_signals ();
+         else
+           default_tty_job_signals ();
+
+         /* Set the process group before trying to mess with the terminal's
+            process group.  This is mandated by POSIX. */
+         /* This is in accordance with the Posix 1003.1 standard,
+            section B.7.2.4, which says that trying to set the terminal
+            process group with tcsetpgrp() to an unused pgrp value (like
+            this would have for the first child) is an error.  Section
+            B.4.3.3, p. 237 also covers this, in the context of job control
+            shells. */
+         if (setpgid (mypid, pipeline_pgrp) < 0)
+           sys_error ("child setpgid (%ld to %ld)", (long)mypid, (long)pipeline_pgrp);
+
+         /* By convention (and assumption above), if
+            pipeline_pgrp == shell_pgrp, we are making a child for
+            command substitution.
+            In this case, we don't want to give the terminal to the
+            shell's process group (we could be in the middle of a
+            pipeline, for example). */
+         if (async_p == 0 && pipeline_pgrp != shell_pgrp)
+           give_terminal_to (pipeline_pgrp, 0);
+
+#if defined (PGRP_PIPE)
+         if (pipeline_pgrp == mypid)
+           pipe_read (pgrp_pipe);
+#endif
+       }
+      else                     /* Without job control... */
+       {
+         if (pipeline_pgrp == 0)
+           pipeline_pgrp = shell_pgrp;
+
+         /* If these signals are set to SIG_DFL, we encounter the curious
+            situation of an interactive ^Z to a running process *working*
+            and stopping the process, but being unable to do anything with
+            that process to change its state.  On the other hand, if they
+            are set to SIG_IGN, jobs started from scripts do not stop when
+            the shell running the script gets a SIGTSTP and stops. */
+
+         default_tty_job_signals ();
+       }
+
+#if defined (PGRP_PIPE)
+      /* Release the process group pipe, since our call to setpgid ()
+        is done.  The last call to pipe_close is done in stop_pipeline. */
+      pipe_close (pgrp_pipe);
+#endif /* PGRP_PIPE */
+
+      if (async_p)
+       last_asynchronous_pid = getpid ();
+    }
+  else
+    {
+      /* In the parent.  Remember the pid of the child just created
+        as the proper pgrp if this is the first child. */
+
+      if (job_control)
+       {
+         if (pipeline_pgrp == 0)
+           {
+             pipeline_pgrp = pid;
+             /* Don't twiddle terminal pgrps in the parent!  This is the bug,
+                not the good thing of twiddling them in the child! */
+             /* give_terminal_to (pipeline_pgrp, 0); */
+           }
+         /* This is done on the recommendation of the Rationale section of
+            the POSIX 1003.1 standard, where it discusses job control and
+            shells.  It is done to avoid possible race conditions. (Ref.
+            1003.1 Rationale, section B.4.3.3, page 236). */
+         setpgid (pid, pipeline_pgrp);
+       }
+      else
+       {
+         if (pipeline_pgrp == 0)
+           pipeline_pgrp = shell_pgrp;
+       }
+
+      /* Place all processes into the jobs array regardless of the
+        state of job_control. */
+      add_process (command, pid);
+
+      if (async_p)
+       last_asynchronous_pid = pid;
+
+      last_made_pid = pid;
+
+      /* Unblock SIGINT and SIGCHLD. */
+      sigprocmask (SIG_SETMASK, &oset, (sigset_t *)NULL);
+    }
+
+  return (pid);
+}
+
+/* These two functions are called only in child processes. */
+void
+ignore_tty_job_signals ()
+{
+  set_signal_handler (SIGTSTP, SIG_IGN);
+  set_signal_handler (SIGTTIN, SIG_IGN);
+  set_signal_handler (SIGTTOU, SIG_IGN);
+}
+
+void
+default_tty_job_signals ()
+{
+  set_signal_handler (SIGTSTP, SIG_DFL);
+  set_signal_handler (SIGTTIN, SIG_DFL);
+  set_signal_handler (SIGTTOU, SIG_DFL);
+}
+
+/* When we end a job abnormally, or if we stop a job, we set the tty to the
+   state kept in here.  When a job ends normally, we set the state in here
+   to the state of the tty. */
+
+static TTYSTRUCT shell_tty_info;
+
+#if defined (NEW_TTY_DRIVER)
+static struct tchars shell_tchars;
+static struct ltchars shell_ltchars;
+#endif /* NEW_TTY_DRIVER */
+
+#if defined (NEW_TTY_DRIVER) && defined (DRAIN_OUTPUT)
+/* Since the BSD tty driver does not allow us to change the tty modes
+   while simultaneously waiting for output to drain and preserving
+   typeahead, we have to drain the output ourselves before calling
+   ioctl.  We cheat by finding the length of the output queue, and
+   using select to wait for an appropriate length of time.  This is
+   a hack, and should be labeled as such (it's a hastily-adapted
+   mutation of a `usleep' implementation).  It's only reason for
+   existing is the flaw in the BSD tty driver. */
+
+static int ttspeeds[] =
+{
+  0, 50, 75, 110, 134, 150, 200, 300, 600, 1200,
+  1800, 2400, 4800, 9600, 19200, 38400
+};
+
+static void
+draino (fd, ospeed)
+     int fd, ospeed;
+{
+  register int delay = ttspeeds[ospeed];
+  int n;
+
+  if (!delay)
+    return;
+
+  while ((ioctl (fd, TIOCOUTQ, &n) == 0) && n)
+    {
+      if (n > (delay / 100))
+       {
+         struct timeval tv;
+
+         n *= 10;              /* 2 bits more for conservativeness. */
+         tv.tv_sec = n / delay;
+         tv.tv_usec = ((n % delay) * 1000000) / delay;
+         select (fd, (fd_set *)0, (fd_set *)0, (fd_set *)0, &tv);
+       }
+      else
+       break;
+    }
+}
+#endif /* NEW_TTY_DRIVER && DRAIN_OUTPUT */
+
+/* Return the fd from which we are actually getting input. */
+#define input_tty() (shell_tty != -1) ? shell_tty : fileno (stderr)
+
+/* Fill the contents of shell_tty_info with the current tty info. */
+int
+get_tty_state ()
+{
+  int tty;
+
+  tty = input_tty ();
+  if (tty != -1)
+    {
+#if defined (NEW_TTY_DRIVER)
+      ioctl (tty, TIOCGETP, &shell_tty_info);
+      ioctl (tty, TIOCGETC, &shell_tchars);
+      ioctl (tty, TIOCGLTC, &shell_ltchars);
+#endif /* NEW_TTY_DRIVER */
+
+#if defined (TERMIO_TTY_DRIVER)
+      ioctl (tty, TCGETA, &shell_tty_info);
+#endif /* TERMIO_TTY_DRIVER */
+
+#if defined (TERMIOS_TTY_DRIVER)
+      if (tcgetattr (tty, &shell_tty_info) < 0)
+       {
+#if 0
+         /* Only print an error message if we're really interactive at
+            this time. */
+         if (interactive)
+           sys_error ("[%ld: %d] tcgetattr", (long)getpid (), shell_level);
+#endif
+         return -1;
+       }
+#endif /* TERMIOS_TTY_DRIVER */
+      if (check_window_size)
+       get_new_window_size (0);
+    }
+  return 0;
+}
+
+/* Make the current tty use the state in shell_tty_info. */
+int
+set_tty_state ()
+{
+  int tty;
+
+  tty = input_tty ();
+  if (tty != -1)
+    {
+#if defined (NEW_TTY_DRIVER)
+#  if defined (DRAIN_OUTPUT)
+      draino (tty, shell_tty_info.sg_ospeed);
+#  endif /* DRAIN_OUTPUT */
+      ioctl (tty, TIOCSETN, &shell_tty_info);
+      ioctl (tty, TIOCSETC, &shell_tchars);
+      ioctl (tty, TIOCSLTC, &shell_ltchars);
+#endif /* NEW_TTY_DRIVER */
+
+#if defined (TERMIO_TTY_DRIVER)
+      ioctl (tty, TCSETAW, &shell_tty_info);
+#endif /* TERMIO_TTY_DRIVER */
+
+#if defined (TERMIOS_TTY_DRIVER)
+      if (tcsetattr (tty, TCSADRAIN, &shell_tty_info) < 0)
+       {
+         /* Only print an error message if we're really interactive at
+            this time. */
+         if (interactive)
+           sys_error ("[%ld: %d] tcsetattr", (long)getpid (), shell_level);
+         return -1;
+       }
+#endif /* TERMIOS_TTY_DRIVER */
+    }
+  return 0;
+}
+
+/* Given an index into the jobs array JOB, return the pid of the last
+   process in that job's pipeline.  This is the one whose exit status
+   counts.  Must be called with SIGCHLD blocked or queued. */
+static pid_t
+find_last_pid (job, block)
+     int job;
+     int block;
+{
+  register PROCESS *p;
+  sigset_t set, oset;
+
+  if (block)
+    BLOCK_CHILD (set, oset);
+
+  p = jobs[job]->pipe;
+  while (p->next != jobs[job]->pipe)
+    p = p->next;
+
+  if (block)
+    UNBLOCK_CHILD (oset);
+
+  return (p->pid);
+}
+
+/* Wait for a particular child of the shell to finish executing.
+   This low-level function prints an error message if PID is not
+   a child of this shell.  It returns -1 if it fails, or whatever
+   wait_for returns otherwise.  If the child is not found in the
+   jobs table, it returns 127. */
+int
+wait_for_single_pid (pid)
+     pid_t pid;
+{
+  register PROCESS *child;
+  sigset_t set, oset;
+  int r, job;
+
+  BLOCK_CHILD (set, oset);
+  child = find_pipeline (pid, 0, (int *)NULL);
+  UNBLOCK_CHILD (oset);
+
+  if (child == 0)
+    {
+      internal_error (_("wait: pid %ld is not a child of this shell"), (long)pid);
+      return (127);
+    }
+
+  r = wait_for (pid);
+
+  /* POSIX.2: if we just waited for a job, we can remove it from the jobs
+     table. */
+  BLOCK_CHILD (set, oset);
+  job = find_job (pid, 0);
+  if (job != NO_JOB && jobs[job] && DEADJOB (job))
+    jobs[job]->flags |= J_NOTIFIED;
+  UNBLOCK_CHILD (oset);
+
+  return r;
+}
+
+/* Wait for all of the backgrounds of this shell to finish. */
+void
+wait_for_background_pids ()
+{
+  register int i, r, waited_for;
+  sigset_t set, oset;
+  pid_t pid;
+
+  for (waited_for = 0;;)
+    {
+      BLOCK_CHILD (set, oset);
+
+      /* find first running job; if none running in foreground, break */
+      for (i = 0; i < job_slots; i++)
+       if (jobs[i] && RUNNING (i) && IS_FOREGROUND (i) == 0)
+         break;
+
+      if (i == job_slots)
+       {
+         UNBLOCK_CHILD (oset);
+         break;
+       }
+
+      /* now wait for the last pid in that job. */
+      pid = find_last_pid (i, 0);
+      UNBLOCK_CHILD (oset);
+      QUIT;
+      errno = 0;               /* XXX */
+      r = wait_for_single_pid (pid);
+      if (r == -1)
+       {
+         /* If we're mistaken about job state, compensate. */
+         if (errno == ECHILD)
+           mark_all_jobs_as_dead ();
+       }
+      else
+       waited_for++;
+    }
+
+  /* POSIX.2 says the shell can discard the statuses of all completed jobs if
+     `wait' is called with no arguments. */
+  mark_dead_jobs_as_notified (1);
+  cleanup_dead_jobs ();
+}
+
+/* Make OLD_SIGINT_HANDLER the SIGINT signal handler. */
+#define INVALID_SIGNAL_HANDLER (SigHandler *)wait_for_background_pids
+static SigHandler *old_sigint_handler = INVALID_SIGNAL_HANDLER;
+
+static void
+restore_sigint_handler ()
+{
+  if (old_sigint_handler != INVALID_SIGNAL_HANDLER)
+    {
+      set_signal_handler (SIGINT, old_sigint_handler);
+      old_sigint_handler = INVALID_SIGNAL_HANDLER;
+    }
+}
+
+static int wait_sigint_received;
+
+/* Handle SIGINT while we are waiting for children in a script to exit.
+   The `wait' builtin should be interruptible, but all others should be
+   effectively ignored (i.e. not cause the shell to exit). */
+static sighandler
+wait_sigint_handler (sig)
+     int sig;
+{
+  SigHandler *sigint_handler;
+
+  if (interrupt_immediately ||
+      (this_shell_builtin && this_shell_builtin == wait_builtin))
+    {
+      last_command_exit_value = EXECUTION_FAILURE;
+      restore_sigint_handler ();
+      /* If we got a SIGINT while in `wait', and SIGINT is trapped, do
+        what POSIX.2 says (see builtins/wait.def for more info). */
+      if (this_shell_builtin && this_shell_builtin == wait_builtin &&
+         signal_is_trapped (SIGINT) &&
+         ((sigint_handler = trap_to_sighandler (SIGINT)) == trap_handler))
+       {
+         interrupt_immediately = 0;
+         trap_handler (SIGINT);        /* set pending_traps[SIGINT] */
+         wait_signal_received = SIGINT;
+         longjmp (wait_intr_buf, 1);
+       }
+      
+      ADDINTERRUPT;
+      QUIT;
+    }
+
+  /* XXX - should this be interrupt_state?  If it is, the shell will act
+     as if it got the SIGINT interrupt. */
+  wait_sigint_received = 1;
+
+  /* Otherwise effectively ignore the SIGINT and allow the running job to
+     be killed. */
+  SIGRETURN (0);
+}
+
+static int
+process_exit_signal (status)
+     WAIT status;
+{
+  return (WIFSIGNALED (status) ? WTERMSIG (status) : 0);
+}
+
+static int
+process_exit_status (status)
+     WAIT status;
+{
+  if (WIFSIGNALED (status))
+    return (128 + WTERMSIG (status));
+  else if (WIFSTOPPED (status) == 0)
+    return (WEXITSTATUS (status));
+  else
+    return (EXECUTION_SUCCESS);
+}
+
+/* Return the exit status of the last process in the pipeline for job JOB.
+   This is the exit status of the entire job. */
+static WAIT
+raw_job_exit_status (job)
+     int job;
+{
+  register PROCESS *p;
+  int fail;
+
+  if (pipefail_opt)
+    {
+      fail = 0;
+      for (p = jobs[job]->pipe; p->next != jobs[job]->pipe; p = p->next)
+        if (p->status != EXECUTION_SUCCESS) fail = p->status;
+      return fail;
+    }
+
+  for (p = jobs[job]->pipe; p->next != jobs[job]->pipe; p = p->next)
+    ;
+  return (p->status);
+}
+
+/* Return the exit status of job JOB.  This is the exit status of the last
+   (rightmost) process in the job's pipeline, modified if the job was killed
+   by a signal or stopped. */
+static int
+job_exit_status (job)
+     int job;
+{
+  return (process_exit_status (raw_job_exit_status (job)));
+}
+
+static int
+job_exit_signal (job)
+     int job;
+{
+  return (process_exit_signal (raw_job_exit_status (job)));
+}
+
+#define FIND_CHILD(pid, child) \
+  do \
+    { \
+      child = find_pipeline (pid, 0, (int *)NULL); \
+      if (child == 0) \
+       { \
+         give_terminal_to (shell_pgrp, 0); \
+         UNBLOCK_CHILD (oset); \
+         internal_error (_("wait_for: No record of process %ld"), (long)pid); \
+         restore_sigint_handler (); \
+         return (termination_state = 127); \
+       } \
+    } \
+  while (0)
+
+/* Wait for pid (one of our children) to terminate, then
+   return the termination state.  Returns 127 if PID is not found in
+   the jobs table.  Returns -1 if waitchld() returns -1, indicating
+   that there are no unwaited-for child processes. */
+int
+wait_for (pid)
+     pid_t pid;
+{
+  int job, termination_state, r;
+  WAIT s;
+  register PROCESS *child;
+  sigset_t set, oset;
+  register PROCESS *p;
+
+  /* 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. */
+  BLOCK_CHILD (set, oset);
+
+  /* Ignore interrupts while waiting for a job run without job control
+     to finish.  We don't want the shell to exit if an interrupt is
+     received, only if one of the jobs run is killed via SIGINT.  If
+     job control is not set, the job will be run in the same pgrp as
+     the shell, and the shell will see any signals the job gets. */
+
+  /* This is possibly a race condition -- should it go in stop_pipeline? */
+  wait_sigint_received = 0;
+  if (job_control == 0)
+    old_sigint_handler = set_signal_handler (SIGINT, wait_sigint_handler);
+
+  termination_state = last_command_exit_value;
+
+  if (interactive && job_control == 0)
+    QUIT;
+
+  /* If we say wait_for (), then we have a record of this child somewhere.
+     If it and none of its peers are running, don't call waitchld(). */
+
+  job = NO_JOB;
+  do
+    {
+      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.
+        We check for JDEAD in case the job state has been set by waitchld
+        after receipt of a SIGCHLD. */
+      if (job == NO_JOB)
+       job = find_job (pid, 0);
+
+      /* waitchld() takes care of setting the state of the job.  If the job
+        has already exited before this is called, sigchld_handler will have
+        called waitchld and the state will be set to JDEAD. */
+
+      if (child->running || (job != NO_JOB && RUNNING (job)))
+       {
+#if defined (WAITPID_BROKEN)    /* SCOv4 */
+         sigset_t suspend_set;
+         sigemptyset (&suspend_set);
+         sigsuspend (&suspend_set);
+#else /* !WAITPID_BROKEN */
+#  if defined (MUST_UNBLOCK_CHLD)
+         struct sigaction act, oact;
+         sigset_t nullset, chldset;
+
+         sigemptyset (&nullset);
+         sigemptyset (&chldset);
+         sigprocmask (SIG_SETMASK, &nullset, &chldset);
+         act.sa_handler = SIG_DFL;
+         sigemptyset (&act.sa_mask);
+         sigemptyset (&oact.sa_mask);
+         act.sa_flags = 0;
+         sigaction (SIGCHLD, &act, &oact);
+#  endif
+         queue_sigchld = 1;
+         r = waitchld (pid, 1);
+#  if defined (MUST_UNBLOCK_CHLD)
+         sigaction (SIGCHLD, &oact, (struct sigaction *)NULL);
+         sigprocmask (SIG_SETMASK, &chldset, (sigset_t *)NULL);
+#  endif
+         queue_sigchld = 0;
+         if (r == -1 && errno == ECHILD && this_shell_builtin == wait_builtin)
+           {
+             termination_state = -1;
+             goto wait_for_return;
+           }
+
+         /* If child is marked as running, but waitpid() returns -1/ECHILD,
+            there is something wrong.  Somewhere, wait should have returned
+            that child's pid.  Mark the child as not running and the job,
+            if it exists, as JDEAD. */
+         if (r == -1 && errno == ECHILD)
+           {
+             child->running = PS_DONE;
+             child->status = 0;        /* XXX -- can't find true status */
+             if (job != NO_JOB)
+               jobs[job]->state = JDEAD;
+           }
+#endif /* WAITPID_BROKEN */
+       }
+
+      /* If the shell is interactive, and job control is disabled, see
+        if the foreground process has died due to SIGINT and jump out
+        of the wait loop if it has.  waitchld has already restored the
+        old SIGINT signal handler. */
+      if (interactive && job_control == 0)
+       QUIT;
+    }
+  while (child->running || (job != NO_JOB && RUNNING (job)));
+
+  /* The exit state of the command is either the termination state of the
+     child, or the termination state of the job.  If a job, the status
+     of the last child in the pipeline is the significant one.  If the command
+     or job was terminated by a signal, note that value also. */
+  termination_state = (job != NO_JOB) ? job_exit_status (job)
+                                     : process_exit_status (child->status);
+  last_command_exit_signal = (job != NO_JOB) ? job_exit_signal (job)
+                                            : process_exit_signal (child->status);
+
+  if (job == NO_JOB || IS_JOBCONTROL (job))
+    {
+      /* XXX - under what circumstances is a job not present in the jobs
+        table (job == NO_JOB)?
+               1.  command substitution
+
+        In the case of command substitution, at least, it's probably not
+        the right thing to give the terminal to the shell's process group,
+        even though there is code in subst.c:command_substitute to work
+        around it.
+
+        Things that don't:
+               $PROMPT_COMMAND execution
+               process substitution
+       */
+#if 0
+if (job == NO_JOB)
+  itrace("wait_for: job == NO_JOB, giving the terminal to shell_pgrp (%ld)", (long)shell_pgrp);
+#endif
+
+      give_terminal_to (shell_pgrp, 0);
+    }
+
+  /* If the command did not exit cleanly, or the job is just
+     being stopped, then reset the tty state back to what it
+     was before this command.  Reset the tty state and notify
+     the user of the job termination only if the shell is
+     interactive.  Clean up any dead jobs in either case. */
+  if (job != NO_JOB)
+    {
+      if (interactive_shell && subshell_environment == 0)
+       {
+         /* This used to use `child->status'.  That's wrong, however, for
+            pipelines.  `child' is the first process in the pipeline.  It's
+            likely that the process we want to check for abnormal termination
+            or stopping is the last process in the pipeline, especially if
+            it's long-lived and the first process is short-lived.  Since we
+            know we have a job here, we can check all the processes in this
+            job's pipeline and see if one of them stopped or terminated due
+            to a signal.  We might want to change this later to just check
+            the last process in the pipeline.  If no process exits due to a
+            signal, S is left as the status of the last job in the pipeline. */
+         p = jobs[job]->pipe;
+         do
+           {
+             s = p->status;
+             if (WIFSIGNALED(s) || WIFSTOPPED(s))
+               break;
+             p = p->next;
+           }
+         while (p != jobs[job]->pipe);
+
+         if (WIFSIGNALED (s) || WIFSTOPPED (s))
+           {
+             set_tty_state ();
+
+             /* If the current job was stopped or killed by a signal, and
+                the user has requested it, get a possibly new window size */
+             if (check_window_size && (job == current_job || IS_FOREGROUND (job)))
+               get_new_window_size (0);
+           }
+         else
+           get_tty_state ();
+
+         /* If job control is enabled, the job was started with job
+            control, the job was the foreground job, and it was killed
+            by SIGINT, then print a newline to compensate for the kernel
+            printing the ^C without a trailing newline. */
+         if (job_control && IS_JOBCONTROL (job) && IS_FOREGROUND (job) &&
+               WIFSIGNALED (s) && WTERMSIG (s) == SIGINT)
+           {
+             /* If SIGINT is not trapped and the shell is in a for, while,
+                or until loop, act as if the shell received SIGINT as
+                well, so the loop can be broken.  This doesn't call the
+                SIGINT signal handler; maybe it should. */
+             if (signal_is_trapped (SIGINT) == 0 && loop_level)
+               ADDINTERRUPT;
+             else
+               {
+                 putchar ('\n');
+                 fflush (stdout);
+               }
+           }
+       }
+
+      /* Moved here from set_job_status_and_cleanup, which is in the SIGCHLD
+         signal handler path */
+      if (DEADJOB (job) && IS_FOREGROUND (job) /*&& subshell_environment == 0*/)
+       setjstatus (job);
+
+      /* If this job is dead, notify the user of the status.  If the shell
+        is interactive, this will display a message on the terminal.  If
+        the shell is not interactive, make sure we turn on the notify bit
+        so we don't get an unwanted message about the job's termination,
+        and so delete_job really clears the slot in the jobs table. */
+      notify_and_cleanup ();
+    }
+
+wait_for_return:
+
+  UNBLOCK_CHILD (oset);
+
+  /* Restore the original SIGINT signal handler before we return. */
+  restore_sigint_handler ();
+
+  return (termination_state);
+}
+
+/* Wait for the last process in the pipeline for JOB.  Returns whatever
+   wait_for returns: the last process's termination state or -1 if there
+   are no unwaited-for child processes or an error occurs. */
+int
+wait_for_job (job)
+     int job;
+{
+  pid_t pid;
+  int r;
+  sigset_t set, oset;
+
+  BLOCK_CHILD(set, oset);
+  if (JOBSTATE (job) == JSTOPPED)
+    internal_warning (_("wait_for_job: job %d is stopped"), job+1);
+
+  pid = find_last_pid (job, 0);
+  UNBLOCK_CHILD(oset);
+  r = wait_for (pid);
+
+  /* POSIX.2: we can remove the job from the jobs table if we just waited
+     for it. */
+  BLOCK_CHILD (set, oset);
+  if (job != NO_JOB && jobs[job] && DEADJOB (job))
+    jobs[job]->flags |= J_NOTIFIED;
+  UNBLOCK_CHILD (oset);
+
+  return r;
+}
+
+/* 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
+   as notified. */
+void
+notify_and_cleanup ()
+{
+  if (jobs_list_frozen)
+    return;
+
+  if (interactive || interactive_shell == 0 || sourcelevel)
+    notify_of_job_status ();
+
+  cleanup_dead_jobs ();
+}
+
+/* Make dead jobs disappear from the jobs array without notification.
+   This is used when the shell is not interactive. */
+void
+reap_dead_jobs ()
+{
+  mark_dead_jobs_as_notified (0);
+  cleanup_dead_jobs ();
+}
+
+/* Return the next closest (chronologically) job to JOB which is in
+   STATE.  STATE can be JSTOPPED, JRUNNING.  NO_JOB is returned if
+   there is no next recent job. */
+static int
+most_recent_job_in_state (job, state)
+     int job;
+     JOB_STATE state;
+{
+  register int i, result;
+  sigset_t set, oset;
+
+  BLOCK_CHILD (set, oset);
+
+  for (result = NO_JOB, i = job - 1; i >= 0; i--)
+    {
+      if (jobs[i] && (JOBSTATE (i) == state))
+       {
+         result = i;
+         break;
+       }
+    }
+
+  UNBLOCK_CHILD (oset);
+
+  return (result);
+}
+
+/* Return the newest *stopped* job older than JOB, or NO_JOB if not
+   found. */
+static int
+job_last_stopped (job)
+     int job;
+{
+  return (most_recent_job_in_state (job, JSTOPPED));
+}
+
+/* Return the newest *running* job older than JOB, or NO_JOB if not
+   found. */
+static int
+job_last_running (job)
+     int job;
+{
+  return (most_recent_job_in_state (job, JRUNNING));
+}
+
+/* Make JOB be the current job, and make previous be useful.  Must be
+   called with SIGCHLD blocked. */
+static void
+set_current_job (job)
+     int job;
+{
+  int candidate;
+
+  if (current_job != job)
+    {
+      previous_job = current_job;
+      current_job = job;
+    }
+
+  /* First choice for previous_job is the old current_job. */
+  if (previous_job != current_job &&
+      previous_job != NO_JOB &&
+      jobs[previous_job] &&
+      STOPPED (previous_job))
+    return;
+
+  /* Second choice:  Newest stopped job that is older than
+     the current job. */
+  candidate = NO_JOB;
+  if (STOPPED (current_job))
+    {
+      candidate = job_last_stopped (current_job);
+
+      if (candidate != NO_JOB)
+       {
+         previous_job = candidate;
+         return;
+       }
+    }
+
+  /* If we get here, there is either only one stopped job, in which case it is
+     the current job and the previous job should be set to the newest running
+     job, or there are only running jobs and the previous job should be set to
+     the newest running job older than the current job.  We decide on which
+     alternative to use based on whether or not JOBSTATE(current_job) is
+     JSTOPPED. */
+
+  candidate = RUNNING (current_job) ? job_last_running (current_job)
+                                   : job_last_running (job_slots);
+
+  if (candidate != NO_JOB)
+    {
+      previous_job = candidate;
+      return;
+    }
+
+  /* There is only a single job, and it is both `+' and `-'. */
+  previous_job = current_job;
+}
+
+/* Make current_job be something useful, if it isn't already. */
+
+/* Here's the deal:  The newest non-running job should be `+', and the
+   next-newest non-running job should be `-'.  If there is only a single
+   stopped job, the previous_job is the newest non-running job.  If there
+   are only running jobs, the newest running job is `+' and the
+   next-newest running job is `-'.  Must be called with SIGCHLD blocked. */
+
+static void
+reset_current ()
+{
+  int candidate;
+
+  if (job_slots && current_job != NO_JOB && jobs[current_job] && STOPPED (current_job))
+    candidate = current_job;
+  else
+    {
+      candidate = NO_JOB;
+
+      /* First choice: the previous job. */
+      if (previous_job != NO_JOB && jobs[previous_job] && STOPPED (previous_job))
+       candidate = previous_job;
+
+      /* Second choice: the most recently stopped job. */
+      if (candidate == NO_JOB)
+       candidate = job_last_stopped (job_slots);
+
+      /* Third choice: the newest running job. */
+      if (candidate == NO_JOB)
+       candidate = job_last_running (job_slots);
+    }
+
+  /* If we found a job to use, then use it.  Otherwise, there
+     are no jobs period. */
+  if (candidate != NO_JOB)
+    set_current_job (candidate);
+  else
+    current_job = previous_job = NO_JOB;
+}
+
+/* Set up the job structures so we know the job and its processes are
+   all running. */
+static void
+set_job_running (job)
+     int job;
+{
+  register PROCESS *p;
+
+  /* Each member of the pipeline is now running. */
+  p = jobs[job]->pipe;
+
+  do
+    {
+      if (WIFSTOPPED (p->status))
+       p->running = PS_RUNNING;        /* XXX - could be PS_STOPPED */
+      p = p->next;
+    }
+  while (p != jobs[job]->pipe);
+
+  /* This means that the job is running. */
+  JOBSTATE (job) = JRUNNING;
+}
+
+/* Start a job.  FOREGROUND if non-zero says to do that.  Otherwise,
+   start the job in the background.  JOB is a zero-based index into
+   JOBS.  Returns -1 if it is unable to start a job, and the return
+   status of the job otherwise. */
+int
+start_job (job, foreground)
+     int job, foreground;
+{
+  register PROCESS *p;
+  int already_running;
+  sigset_t set, oset;
+  char *wd;
+  static TTYSTRUCT save_stty;
+
+  BLOCK_CHILD (set, oset);
+
+  if (DEADJOB (job))
+    {
+      internal_error (_("%s: job has terminated"), this_command_name);
+      UNBLOCK_CHILD (oset);
+      return (-1);
+    }
+
+  already_running = RUNNING (job);
+
+  if (foreground == 0 && already_running)
+    {
+      internal_error (_("%s: job %d already in background"), this_command_name, job + 1);
+      UNBLOCK_CHILD (oset);
+      return (-1);
+    }
+
+  wd = current_working_directory ();
+
+  /* You don't know about the state of this job.  Do you? */
+  jobs[job]->flags &= ~J_NOTIFIED;
+
+  if (foreground)
+    {
+      set_current_job (job);
+      jobs[job]->flags |= J_FOREGROUND;
+    }
+
+  /* Tell the outside world what we're doing. */
+  p = jobs[job]->pipe;
+
+  if (foreground == 0)
+    fprintf (stderr, "[%d]%c ", job + 1,
+          (job == current_job) ? '+': ((job == previous_job) ? '-' : ' '));
+
+  do
+    {
+      fprintf (stderr, "%s%s",
+              p->command ? p->command : "",
+              p->next != jobs[job]->pipe? " | " : "");
+      p = p->next;
+    }
+  while (p != jobs[job]->pipe);
+
+  if (foreground == 0)
+    fprintf (stderr, " &");
+
+  if (strcmp (wd, jobs[job]->wd) != 0)
+    fprintf (stderr, " (wd: %s)", polite_directory_format (jobs[job]->wd));
+
+  fprintf (stderr, "\n");
+
+  /* Run the job. */
+  if (already_running == 0)
+    set_job_running (job);
+
+  /* Save the tty settings before we start the job in the foreground. */
+  if (foreground)
+    {
+      get_tty_state ();
+      save_stty = shell_tty_info;
+      /* Give the terminal to this job. */
+      if (IS_JOBCONTROL (job))
+       give_terminal_to (jobs[job]->pgrp, 0);
+    }
+  else
+    jobs[job]->flags &= ~J_FOREGROUND;
+
+  /* If the job is already running, then don't bother jump-starting it. */
+  if (already_running == 0)
+    {
+      jobs[job]->flags |= J_NOTIFIED;
+      killpg (jobs[job]->pgrp, SIGCONT);
+    }
+
+  if (foreground)
+    {
+      pid_t pid;
+      int s;
+
+      pid = find_last_pid (job, 0);
+      UNBLOCK_CHILD (oset);
+      s = wait_for (pid);
+      shell_tty_info = save_stty;
+      set_tty_state ();
+      return (s);
+    }
+  else
+    {
+      reset_current ();
+      UNBLOCK_CHILD (oset);
+      return (0);
+    }
+}
+
+/* Give PID SIGNAL.  This determines what job the pid belongs to (if any).
+   If PID does belong to a job, and the job is stopped, then CONTinue the
+   job after giving it SIGNAL.  Returns -1 on failure.  If GROUP is non-null,
+   then kill the process group associated with PID. */
+int
+kill_pid (pid, sig, group)
+     pid_t pid;
+     int sig, group;
+{
+  register PROCESS *p;
+  int job, result;
+  sigset_t set, oset;
+
+  result = EXECUTION_SUCCESS;
+  if (group)
+    {
+      BLOCK_CHILD (set, oset);
+      p = find_pipeline (pid, 0, &job);
+
+      if (job != NO_JOB)
+       {
+         jobs[job]->flags &= ~J_NOTIFIED;
+
+         /* Kill process in backquotes or one started without job control? */
+         if (jobs[job]->pgrp == shell_pgrp)
+           {
+             p = jobs[job]->pipe;
+
+             do
+               {
+                 kill (p->pid, sig);
+                 if (p->running == PS_DONE && (sig == SIGTERM || sig == SIGHUP))
+                   kill (p->pid, SIGCONT);
+                 p = p->next;
+               }
+             while (p != jobs[job]->pipe);
+           }
+         else
+           {
+             result = killpg (jobs[job]->pgrp, sig);
+             if (p && STOPPED (job) && (sig == SIGTERM || sig == SIGHUP))
+               killpg (jobs[job]->pgrp, SIGCONT);
+             /* If we're continuing a stopped job via kill rather than bg or
+                fg, emulate the `bg' behavior. */
+             if (p && STOPPED (job) && (sig == SIGCONT))
+               {
+                 set_job_running (job);
+                 jobs[job]->flags &= ~J_FOREGROUND;
+                 jobs[job]->flags |= J_NOTIFIED;
+               }
+           }
+       }
+      else
+       result = killpg (pid, sig);
+
+      UNBLOCK_CHILD (oset);
+    }
+  else
+    result = kill (pid, sig);
+
+  return (result);
+}
+
+/* sigchld_handler () flushes at least one of the children that we are
+   waiting for.  It gets run when we have gotten a SIGCHLD signal. */
+static sighandler
+sigchld_handler (sig)
+     int sig;
+{
+  int n, oerrno;
+
+  oerrno = errno;
+  REINSTALL_SIGCHLD_HANDLER;
+  sigchld++;
+  n = 0;
+  if (queue_sigchld == 0)
+    n = waitchld (-1, 0);
+  errno = oerrno;
+  SIGRETURN (n);
+}
+
+/* waitchld() reaps dead or stopped children.  It's called by wait_for and
+   sigchld_handler, and runs until there aren't any children terminating any
+   more.
+   If BLOCK is 1, this is to be a blocking wait for a single child, although
+   an arriving SIGCHLD could cause the wait to be non-blocking.  It returns
+   the number of children reaped, or -1 if there are no unwaited-for child
+   processes. */
+static int
+waitchld (wpid, block)
+     pid_t wpid;
+     int block;
+{
+  WAIT status;
+  PROCESS *child;
+  pid_t pid;
+  int call_set_current, last_stopped_job, job, children_exited, waitpid_flags;
+
+  call_set_current = children_exited = 0;
+  last_stopped_job = NO_JOB;
+
+  do
+    {
+      /* We don't want to be notified about jobs stopping if job control
+        is not active.  XXX - was interactive_shell instead of job_control */
+      waitpid_flags = (job_control && subshell_environment == 0)
+                       ? (WUNTRACED|WCONTINUED)
+                       : 0;
+      if (sigchld || block == 0)
+       waitpid_flags |= WNOHANG;
+      pid = WAITPID (-1, &status, waitpid_flags);
+
+      /* The check for WNOHANG is to make sure we decrement sigchld only
+        if it was non-zero before we called waitpid. */
+      if (sigchld > 0 && (waitpid_flags & WNOHANG))
+       sigchld--;
+  
+      /* If waitpid returns -1 with errno == ECHILD, there are no more
+        unwaited-for child processes of this shell. */
+      if (pid < 0 && errno == ECHILD)
+       {
+         if (children_exited == 0)
+           return -1;
+         else
+           break;
+       }
+
+      /* If waitpid returns 0, there are running children.  If it returns -1,
+        the only other error POSIX says it can return is EINTR. */
+      if (pid <= 0)
+       continue;       /* jumps right to the test */
+
+      /* children_exited is used to run traps on SIGCHLD.  We don't want to
+         run the trap if a process is just being continued. */
+      if (WIFCONTINUED(status) == 0)
+       children_exited++;
+
+      /* Locate our PROCESS for this pid. */
+      child = find_pipeline (pid, 1, &job);    /* want running procs only */
+
+      /* It is not an error to have a child terminate that we did
+        not have a record of.  This child could have been part of
+        a pipeline in backquote substitution.  Even so, I'm not
+        sure child is ever non-zero. */
+      if (child == 0)
+       continue;
+
+      while (child->pid != pid)
+       child = child->next;
+
+      /* Remember status, and whether or not the process is running. */
+      child->status = status;
+      child->running = WIFCONTINUED(status) ? PS_RUNNING : PS_DONE;
+
+      if (job == NO_JOB)
+       continue;
+
+      call_set_current += set_job_status_and_cleanup (job);
+
+      if (STOPPED (job))
+       last_stopped_job = job;
+      else if (DEADJOB (job) && last_stopped_job == job)
+       last_stopped_job = NO_JOB;
+    }
+  while ((sigchld || block == 0) && pid > (pid_t)0);
+
+  /* If a job was running and became stopped, then set the current
+     job.  Otherwise, don't change a thing. */
+  if (call_set_current)
+    {
+      if (last_stopped_job != NO_JOB)
+       set_current_job (last_stopped_job);
+      else
+       reset_current ();
+    }
+
+  /* Call a SIGCHLD trap handler for each child that exits, if one is set. */
+  if (job_control && signal_is_trapped (SIGCHLD) && children_exited &&
+      trap_list[SIGCHLD] != (char *)IGNORE_SIG)
+    run_sigchld_trap (children_exited);
+
+  /* We have successfully recorded the useful information about this process
+     that has just changed state.  If we notify asynchronously, and the job
+     that this process belongs to is no longer running, then notify the user
+     of that fact now. */
+  if (asynchronous_notification && interactive)
+    notify_of_job_status ();
+
+  return (children_exited);
+}
+
+/* Set the status of JOB and perform any necessary cleanup if the job is
+   marked as JDEAD.
+
+   Currently, the cleanup activity is restricted to handling any SIGINT
+   received while waiting for a foreground job to finish. */
+static int
+set_job_status_and_cleanup (job)
+     int job;
+{
+  PROCESS *child;
+  int tstatus, job_state, any_stopped, any_tstped, call_set_current;
+  SigHandler *temp_handler;
+
+  child = jobs[job]->pipe;
+  jobs[job]->flags &= ~J_NOTIFIED;
+
+  call_set_current = 0;
+
+  /*
+   * COMPUTE JOB STATUS
+   */
+
+  /* If all children are not running, but any of them is  stopped, then
+     the job is stopped, not dead. */
+  job_state = any_stopped = any_tstped = 0;
+  do
+    {
+      job_state |= child->running;
+      if (child->running == PS_DONE && (WIFSTOPPED (child->status)))
+       {
+         any_stopped = 1;
+         any_tstped |= interactive && job_control &&
+                           (WSTOPSIG (child->status) == SIGTSTP);
+       }
+      child = child->next;
+    }
+  while (child != jobs[job]->pipe);
+
+  /* If job_state != 0, the job is still running, so don't bother with
+     setting the process exit status and job state unless we're
+     transitioning from stopped to running. */
+  if (job_state != 0 && JOBSTATE(job) != JSTOPPED)
+    return 0;
+
+  /*
+   * SET JOB STATUS
+   */
+
+  /* The job is either stopped or dead.  Set the state of the job accordingly. */
+  if (any_stopped)
+    {
+      jobs[job]->state = JSTOPPED;
+      jobs[job]->flags &= ~J_FOREGROUND;
+      call_set_current++;
+      /* Suspending a job with SIGTSTP breaks all active loops. */
+      if (any_tstped && loop_level)
+       breaking = loop_level;
+    }
+  else if (job_state != 0)     /* was stopped, now running */
+    {
+      jobs[job]->state = JRUNNING;
+      call_set_current++;
+    }
+  else
+    {
+      jobs[job]->state = JDEAD;
+
+#if 0
+      if (IS_FOREGROUND (job))
+       setjstatus (job);
+#endif
+
+      /* If this job has a cleanup function associated with it, call it
+        with `cleanarg' as the single argument, then set the function
+        pointer to NULL so it is not inadvertently called twice.  The
+        cleanup function is responsible for deallocating cleanarg. */
+      if (jobs[job]->j_cleanup)
+       {
+         (*jobs[job]->j_cleanup) (jobs[job]->cleanarg);
+         jobs[job]->j_cleanup = (sh_vptrfunc_t *)NULL;
+       }
+    }
+
+  /*
+   * CLEANUP
+   *
+   * Currently, we just do special things if we got a SIGINT while waiting
+   * for a foreground job to complete
+   */
+
+  if (jobs[job]->state == JDEAD)
+    {
+      /* If we're running a shell script and we get a SIGINT with a
+        SIGINT trap handler, but the foreground job handles it and
+        does not exit due to SIGINT, run the trap handler but do not
+        otherwise act as if we got the interrupt. */
+      if (wait_sigint_received && interactive_shell == 0 &&
+         WIFSIGNALED (child->status) == 0 && IS_FOREGROUND (job) &&
+         signal_is_trapped (SIGINT))
+       {
+         int old_frozen;
+         wait_sigint_received = 0;
+         last_command_exit_value = process_exit_status (child->status);
+
+         old_frozen = jobs_list_frozen;
+         jobs_list_frozen = 1;
+         tstatus = maybe_call_trap_handler (SIGINT);
+         jobs_list_frozen = old_frozen;
+       }
+
+      /* If the foreground job is killed by SIGINT when job control is not
+        active, we need to perform some special handling.
+
+        The check of wait_sigint_received is a way to determine if the
+        SIGINT came from the keyboard (in which case the shell has already
+        seen it, and wait_sigint_received is non-zero, because keyboard
+        signals are sent to process groups) or via kill(2) to the foreground
+        process by another process (or itself).  If the shell did receive the
+        SIGINT, it needs to perform normal SIGINT processing. */
+      else if (wait_sigint_received && (WTERMSIG (child->status) == SIGINT) &&
+             IS_FOREGROUND (job) && IS_JOBCONTROL (job) == 0)
+       {
+         int old_frozen;
+
+         wait_sigint_received = 0;
+
+         /* If SIGINT is trapped, set the exit status so that the trap
+            handler can see it. */
+         if (signal_is_trapped (SIGINT))
+           last_command_exit_value = process_exit_status (child->status);
+
+         /* If the signal is trapped, let the trap handler get it no matter
+            what and simply return if the trap handler returns.
+           maybe_call_trap_handler() may cause dead jobs to be removed from
+           the job table because of a call to execute_command.  We work
+           around this by setting JOBS_LIST_FROZEN. */
+         old_frozen = jobs_list_frozen;
+         jobs_list_frozen = 1;
+         tstatus = maybe_call_trap_handler (SIGINT);
+         jobs_list_frozen = old_frozen;
+         if (tstatus == 0 && old_sigint_handler != INVALID_SIGNAL_HANDLER)
+           {
+             /* wait_sigint_handler () has already seen SIGINT and
+                allowed the wait builtin to jump out.  We need to
+                call the original SIGINT handler, if necessary.  If
+                the original handler is SIG_DFL, we need to resend
+                the signal to ourselves. */
+
+             temp_handler = old_sigint_handler;
+
+             /* Bogus.  If we've reset the signal handler as the result
+                of a trap caught on SIGINT, then old_sigint_handler
+                will point to trap_handler, which now knows nothing about
+                SIGINT (if we reset the sighandler to the default).
+                In this case, we have to fix things up.  What a crock. */
+             if (temp_handler == trap_handler && signal_is_trapped (SIGINT) == 0)
+                 temp_handler = trap_to_sighandler (SIGINT);
+               restore_sigint_handler ();
+               if (temp_handler == SIG_DFL)
+                 termination_unwind_protect (SIGINT);
+               else if (temp_handler != SIG_IGN)
+                 (*temp_handler) (SIGINT);
+           }
+       }
+    }
+
+  return call_set_current;
+}
+
+/* Build the array of values for the $PIPESTATUS variable from the set of
+   exit statuses of all processes in the job J. */
+static void
+setjstatus (j)
+     int j;
+{
+#if defined (ARRAY_VARS)
+  register int i;
+  register PROCESS *p;
+
+  for (i = 1, p = jobs[j]->pipe; p->next != jobs[j]->pipe; p = p->next, i++)
+    ;
+  i++;
+  if (statsize < i)
+    {
+      pstatuses = (int *)xrealloc (pstatuses, i * sizeof (int));
+      statsize = i;
+    }
+  i = 0;
+  p = jobs[j]->pipe;
+  do
+    {
+      pstatuses[i++] = process_exit_status (p->status);
+      p = p->next;
+    }
+  while (p != jobs[j]->pipe);
+
+  pstatuses[i] = -1;   /* sentinel */
+  set_pipestatus_array (pstatuses, i);
+#endif
+}
+
+static void
+run_sigchld_trap (nchild)
+     int nchild;
+{
+  char *trap_command;
+  int i;
+
+  /* Turn off the trap list during the call to parse_and_execute ()
+     to avoid potentially infinite recursive calls.  Preserve the
+     values of last_command_exit_value, last_made_pid, and the_pipeline
+     around the execution of the trap commands. */
+  trap_command = savestring (trap_list[SIGCHLD]);
+
+  begin_unwind_frame ("SIGCHLD trap");
+  unwind_protect_int (last_command_exit_value);
+  unwind_protect_int (last_command_exit_signal);
+  unwind_protect_var (last_made_pid);
+  unwind_protect_int (interrupt_immediately);
+  unwind_protect_int (jobs_list_frozen);
+  unwind_protect_pointer (the_pipeline);
+  unwind_protect_pointer (subst_assign_varlist);
+
+  /* We have to add the commands this way because they will be run
+     in reverse order of adding.  We don't want maybe_set_sigchld_trap ()
+     to reference freed memory. */
+  add_unwind_protect (xfree, trap_command);
+  add_unwind_protect (maybe_set_sigchld_trap, trap_command);
+
+  subst_assign_varlist = (WORD_LIST *)NULL;
+  the_pipeline = (PROCESS *)NULL;
+
+  restore_default_signal (SIGCHLD);
+  jobs_list_frozen = 1;
+  for (i = 0; i < nchild; i++)
+    {
+      interrupt_immediately = 1;
+      parse_and_execute (savestring (trap_command), "trap", SEVAL_NOHIST|SEVAL_RESETLINE);
+    }
+
+  run_unwind_frame ("SIGCHLD trap");
+}
+
+/* Function to call when you want to notify people of changes
+   in job status.  This prints out all jobs which are pending
+   notification to stderr, and marks those printed as already
+   notified, thus making them candidates for cleanup. */
+static void
+notify_of_job_status ()
+{
+  register int job, termsig;
+  char *dir;
+  sigset_t set, oset;
+  WAIT s;
+
+  if (jobs == 0 || job_slots == 0)
+    return;
+
+  if (old_ttou != 0)
+    {
+      sigemptyset (&set);
+      sigaddset (&set, SIGCHLD);
+      sigaddset (&set, SIGTTOU);
+      sigemptyset (&oset);
+      sigprocmask (SIG_BLOCK, &set, &oset);
+    }
+  else
+    queue_sigchld++;
+
+  for (job = 0, dir = (char *)NULL; job < job_slots; job++)
+    {
+      if (jobs[job] && IS_NOTIFIED (job) == 0)
+       {
+         s = raw_job_exit_status (job);
+         termsig = WTERMSIG (s);
+
+         /* POSIX.2 says we have to hang onto the statuses of at most the
+            last CHILD_MAX background processes if the shell is running a
+            script.  If the shell is not interactive, don't print anything
+            unless the job was killed by a signal. */
+         if (startup_state == 0 && WIFSIGNALED (s) == 0 &&
+               ((DEADJOB (job) && IS_FOREGROUND (job) == 0) || STOPPED (job)))
+           continue;
+         
+#if 0
+         /* If job control is disabled, don't print the status messages.
+            Mark dead jobs as notified so that they get cleaned up.  If
+            startup_state == 2, we were started to run `-c command', so
+            don't print anything. */
+         if ((job_control == 0 && interactive_shell) || startup_state == 2)
+#else
+         /* If job control is disabled, don't print the status messages.
+            Mark dead jobs as notified so that they get cleaned up.  If
+            startup_state == 2 and subshell_environment has the
+            SUBSHELL_COMSUB bit turned on, we were started to run a command
+            substitution, so don't print anything. */
+         if ((job_control == 0 && interactive_shell) ||
+             (startup_state == 2 && (subshell_environment & SUBSHELL_COMSUB)))
+#endif
+           {
+             /* POSIX.2 compatibility:  if the shell is not interactive,
+                hang onto the job corresponding to the last asynchronous
+                pid until the user has been notified of its status or does
+                a `wait'. */
+             if (DEADJOB (job) && (interactive_shell || (find_last_pid (job, 0) != last_asynchronous_pid)))
+               jobs[job]->flags |= J_NOTIFIED;
+             continue;
+           }
+
+         /* Print info on jobs that are running in the background,
+            and on foreground jobs that were killed by anything
+            except SIGINT (and possibly SIGPIPE). */
+         switch (JOBSTATE (job))
+           {
+           case JDEAD:
+             if (interactive_shell == 0 && termsig && WIFSIGNALED (s) &&
+                 termsig != SIGINT &&
+#if defined (DONT_REPORT_SIGPIPE)
+                 termsig != SIGPIPE &&
+#endif
+                 signal_is_trapped (termsig) == 0)
+               {
+                 /* Don't print `0' for a line number. */
+                 fprintf (stderr, "%s: line %d: ", get_name_for_error (), (line_number == 0) ? 1 : line_number);
+                 pretty_print_job (job, JLIST_NONINTERACTIVE, stderr);
+               }
+             else if (IS_FOREGROUND (job))
+               {
+#if !defined (DONT_REPORT_SIGPIPE)
+                 if (termsig && WIFSIGNALED (s) && termsig != SIGINT)
+#else
+                 if (termsig && WIFSIGNALED (s) && termsig != SIGINT && termsig != SIGPIPE)
+#endif
+                   {
+                     fprintf (stderr, "%s", j_strsignal (termsig));
+
+                     if (WIFCORED (s))
+                       fprintf (stderr, " (core dumped)");
+
+                     fprintf (stderr, "\n");
+                   }
+               }
+             else
+               {
+                 if (dir == 0)
+                   dir = current_working_directory ();
+                 pretty_print_job (job, JLIST_STANDARD, stderr);
+                 if (dir && strcmp (dir, jobs[job]->wd) != 0)
+                   fprintf (stderr,
+                            "(wd now: %s)\n", polite_directory_format (dir));
+               }
+
+             jobs[job]->flags |= J_NOTIFIED;
+             break;
+
+           case JSTOPPED:
+             fprintf (stderr, "\n");
+             if (dir == 0)
+               dir = current_working_directory ();
+             pretty_print_job (job, JLIST_STANDARD, stderr);
+             if (dir && (strcmp (dir, jobs[job]->wd) != 0))
+               fprintf (stderr,
+                        "(wd now: %s)\n", polite_directory_format (dir));
+             jobs[job]->flags |= J_NOTIFIED;
+             break;
+
+           case JRUNNING:
+           case JMIXED:
+             break;
+
+           default:
+             programming_error ("notify_of_job_status");
+           }
+       }
+    }
+  if (old_ttou != 0)
+    sigprocmask (SIG_SETMASK, &oset, (sigset_t *)NULL);
+  else
+    queue_sigchld--;
+}
+
+/* Initialize the job control mechanism, and set up the tty stuff. */
+int
+initialize_job_control (force)
+     int force;
+{
+  shell_pgrp = getpgid (0);
+
+  if (shell_pgrp == -1)
+    {
+      sys_error ("initialize_job_control: getpgrp failed");
+      exit (1);
+    }
+
+  /* We can only have job control if we are interactive. */
+  if (interactive == 0)
+    {
+      job_control = 0;
+      original_pgrp = NO_PID;
+      shell_tty = fileno (stderr);
+    }
+  else
+    {
+      /* Get our controlling terminal.  If job_control is set, or
+        interactive is set, then this is an interactive shell no
+        matter where fd 2 is directed. */
+      shell_tty = dup (fileno (stderr));       /* fd 2 */
+
+      shell_tty = move_to_high_fd (shell_tty, 1, -1);
+
+      /* Compensate for a bug in systems that compiled the BSD
+        rlogind with DEBUG defined, like NeXT and Alliant. */
+      if (shell_pgrp == 0)
+       {
+         shell_pgrp = getpid ();
+         setpgid (0, shell_pgrp);
+         tcsetpgrp (shell_tty, shell_pgrp);
+       }
+
+      while ((terminal_pgrp = tcgetpgrp (shell_tty)) != -1)
+       {
+         if (shell_pgrp != terminal_pgrp)
+           {
+             SigHandler *ottin;
+
+             ottin = set_signal_handler(SIGTTIN, SIG_DFL);
+             kill (0, SIGTTIN);
+             set_signal_handler (SIGTTIN, ottin);
+             continue;
+           }
+         break;
+       }
+
+      /* Make sure that we are using the new line discipline. */
+      if (set_new_line_discipline (shell_tty) < 0)
+       {
+         sys_error ("initialize_job_control: line discipline");
+         job_control = 0;
+       }
+      else
+       {
+         original_pgrp = shell_pgrp;
+         shell_pgrp = getpid ();
+
+         if ((original_pgrp != shell_pgrp) && (setpgid (0, shell_pgrp) < 0))
+           {
+             sys_error ("initialize_job_control: setpgid");
+             shell_pgrp = original_pgrp;
+           }
+
+         job_control = 1;
+
+         /* If (and only if) we just set our process group to our pid,
+            thereby becoming a process group leader, and the terminal
+            is not in the same process group as our (new) process group,
+            then set the terminal's process group to our (new) process
+            group.  If that fails, set our process group back to what it
+            was originally (so we can still read from the terminal) and
+            turn off job control.  */
+         if (shell_pgrp != original_pgrp && shell_pgrp != terminal_pgrp)
+           {
+             if (give_terminal_to (shell_pgrp, 0) < 0)
+               {
+                 setpgid (0, original_pgrp);
+                 shell_pgrp = original_pgrp;
+                 job_control = 0;
+               }
+           }
+       }
+      if (job_control == 0)
+       internal_error (_("no job control in this shell"));
+    }
+
+  if (shell_tty != fileno (stderr))
+    SET_CLOSE_ON_EXEC (shell_tty);
+
+  set_signal_handler (SIGCHLD, sigchld_handler);
+
+  change_flag ('m', job_control ? '-' : '+');
+
+  if (interactive)
+    get_tty_state ();
+
+  return job_control;
+}
+
+#ifdef DEBUG
+void
+debug_print_pgrps ()
+{
+  itrace("original_pgrp = %ld shell_pgrp = %ld terminal_pgrp = %ld",
+        (long)original_pgrp, (long)shell_pgrp, (long)terminal_pgrp);
+  itrace("tcgetpgrp(%d) -> %ld, getpgid(0) -> %ld",
+        shell_tty, (long)tcgetpgrp (shell_tty), (long)getpgid(0));
+}
+#endif
+
+/* Set the line discipline to the best this system has to offer.
+   Return -1 if this is not possible. */
+static int
+set_new_line_discipline (tty)
+     int tty;
+{
+#if defined (NEW_TTY_DRIVER)
+  int ldisc;
+
+  if (ioctl (tty, TIOCGETD, &ldisc) < 0)
+    return (-1);
+
+  if (ldisc != NTTYDISC)
+    {
+      ldisc = NTTYDISC;
+
+      if (ioctl (tty, TIOCSETD, &ldisc) < 0)
+       return (-1);
+    }
+  return (0);
+#endif /* NEW_TTY_DRIVER */
+
+#if defined (TERMIO_TTY_DRIVER)
+#  if defined (TERMIO_LDISC) && (NTTYDISC)
+  if (ioctl (tty, TCGETA, &shell_tty_info) < 0)
+    return (-1);
+
+  if (shell_tty_info.c_line != NTTYDISC)
+    {
+      shell_tty_info.c_line = NTTYDISC;
+      if (ioctl (tty, TCSETAW, &shell_tty_info) < 0)
+       return (-1);
+    }
+#  endif /* TERMIO_LDISC && NTTYDISC */
+  return (0);
+#endif /* TERMIO_TTY_DRIVER */
+
+#if defined (TERMIOS_TTY_DRIVER)
+#  if defined (TERMIOS_LDISC) && defined (NTTYDISC)
+  if (tcgetattr (tty, &shell_tty_info) < 0)
+    return (-1);
+
+  if (shell_tty_info.c_line != NTTYDISC)
+    {
+      shell_tty_info.c_line = NTTYDISC;
+      if (tcsetattr (tty, TCSADRAIN, &shell_tty_info) < 0)
+       return (-1);
+    }
+#  endif /* TERMIOS_LDISC && NTTYDISC */
+  return (0);
+#endif /* TERMIOS_TTY_DRIVER */
+
+#if !defined (NEW_TTY_DRIVER) && !defined (TERMIO_TTY_DRIVER) && !defined (TERMIOS_TTY_DRIVER)
+  return (-1);
+#endif
+}
+
+#if defined (TIOCGWINSZ) && defined (SIGWINCH)
+static void
+get_new_window_size (from_sig)
+     int from_sig;
+{
+  struct winsize win;
+
+  if ((ioctl (shell_tty, TIOCGWINSZ, &win) == 0) &&
+      win.ws_row > 0 && win.ws_col > 0)
+    {
+#if defined (aixpc)
+      shell_tty_info.c_winsize = win;  /* structure copying */
+#endif
+      sh_set_lines_and_columns (win.ws_row, win.ws_col);
+#if defined (READLINE)
+      rl_set_screen_size (win.ws_row, win.ws_col);
+#endif
+    }
+}
+
+static sighandler
+sigwinch_sighandler (sig)
+     int sig;
+{
+#if defined (MUST_REINSTALL_SIGHANDLERS)
+  set_signal_handler (SIGWINCH, sigwinch_sighandler);
+#endif /* MUST_REINSTALL_SIGHANDLERS */
+  get_new_window_size (1);
+  SIGRETURN (0);
+}
+#else
+static void
+get_new_window_size (from_sig)
+     int from_sig;
+{
+}
+#endif /* TIOCGWINSZ && SIGWINCH */
+
+void
+set_sigwinch_handler ()
+{
+#if defined (TIOCGWINSZ) && defined (SIGWINCH)
+ old_winch = set_signal_handler (SIGWINCH, sigwinch_sighandler);
+#endif
+}
+
+void
+unset_sigwinch_handler ()
+{
+#if defined (TIOCGWINSZ) && defined (SIGWINCH)
+  set_signal_handler (SIGWINCH, old_winch);
+#endif
+}
+
+/* Setup this shell to handle C-C, etc. */
+void
+initialize_job_signals ()
+{
+  if (interactive)
+    {
+      set_signal_handler (SIGINT, sigint_sighandler);
+      set_signal_handler (SIGTSTP, SIG_IGN);
+      set_signal_handler (SIGTTOU, SIG_IGN);
+      set_signal_handler (SIGTTIN, SIG_IGN);
+      set_sigwinch_handler ();
+    }
+  else if (job_control)
+    {
+      old_tstp = set_signal_handler (SIGTSTP, sigstop_sighandler);
+      old_ttin = set_signal_handler (SIGTTIN, sigstop_sighandler);
+      old_ttou = set_signal_handler (SIGTTOU, sigstop_sighandler);
+    }
+  /* Leave these things alone for non-interactive shells without job
+     control. */
+}
+
+/* Here we handle CONT signals. */
+static sighandler
+sigcont_sighandler (sig)
+     int sig;
+{
+  initialize_job_signals ();
+  set_signal_handler (SIGCONT, old_cont);
+  kill (getpid (), SIGCONT);
+
+  SIGRETURN (0);
+}
+
+/* Here we handle stop signals while we are running not as a login shell. */
+static sighandler
+sigstop_sighandler (sig)
+     int sig;
+{
+  set_signal_handler (SIGTSTP, old_tstp);
+  set_signal_handler (SIGTTOU, old_ttou);
+  set_signal_handler (SIGTTIN, old_ttin);
+
+  old_cont = set_signal_handler (SIGCONT, sigcont_sighandler);
+
+  give_terminal_to (shell_pgrp, 0);
+
+  kill (getpid (), sig);
+
+  SIGRETURN (0);
+}
+
+/* Give the terminal to PGRP.  */
+int
+give_terminal_to (pgrp, force)
+     pid_t pgrp;
+     int force;
+{
+  sigset_t set, oset;
+  int r;
+
+  r = 0;
+  if (job_control || force)
+    {
+      sigemptyset (&set);
+      sigaddset (&set, SIGTTOU);
+      sigaddset (&set, SIGTTIN);
+      sigaddset (&set, SIGTSTP);
+      sigaddset (&set, SIGCHLD);
+      sigemptyset (&oset);
+      sigprocmask (SIG_BLOCK, &set, &oset);
+
+      if (tcsetpgrp (shell_tty, pgrp) < 0)
+       {
+         /* Maybe we should print an error message? */
+#if 0
+         sys_error ("tcsetpgrp(%d) failed: pid %ld to pgrp %ld",
+           shell_tty, (long)getpid(), (long)pgrp);
+#endif
+         r = -1;
+       }
+      else
+       terminal_pgrp = pgrp;
+      sigprocmask (SIG_SETMASK, &oset, (sigset_t *)NULL);
+    }
+
+  return r;
+}
+
+/* Clear out any jobs in the job array.  This is intended to be used by
+   children of the shell, who should not have any job structures as baggage
+   when they start executing (forking subshells for parenthesized execution
+   and functions with pipes are the two that spring to mind).  If RUNNING_ONLY
+   is nonzero, only running jobs are removed from the table. */
+void
+delete_all_jobs (running_only)
+     int running_only;
+{
+  register int i;
+  sigset_t set, oset;
+
+  BLOCK_CHILD (set, oset);
+
+  if (job_slots)
+    {
+      current_job = previous_job = NO_JOB;
+
+      for (i = 0; i < job_slots; i++)
+       if (jobs[i] && (running_only == 0 || (running_only && RUNNING(i))))
+         delete_job (i, 1);
+
+      if (running_only == 0)
+       {
+         free ((char *)jobs);
+         job_slots = 0;
+       }
+    }
+
+  UNBLOCK_CHILD (oset);
+}
+
+/* Mark all jobs in the job array so that they don't get a SIGHUP when the
+   shell gets one.  If RUNNING_ONLY is nonzero, mark only running jobs. */
+void
+nohup_all_jobs (running_only)
+     int running_only;
+{
+  register int i;
+  sigset_t set, oset;
+
+  BLOCK_CHILD (set, oset);
+
+  if (job_slots)
+    {
+      for (i = 0; i < job_slots; i++)
+       if (jobs[i] && (running_only == 0 || (running_only && RUNNING(i))))
+         nohup_job (i);
+    }
+
+  UNBLOCK_CHILD (oset);
+}
+
+int
+count_all_jobs ()
+{
+  int i, n;
+  sigset_t set, oset;
+
+  BLOCK_CHILD (set, oset);
+  for (i = n = 0; i < job_slots; i++)
+    if (jobs[i] && DEADJOB(i) == 0)
+      n++;
+  UNBLOCK_CHILD (oset);
+  return n;
+}
+
+static void
+mark_all_jobs_as_dead ()
+{
+  register int i;
+  sigset_t set, oset;
+
+  if (job_slots == 0)
+    return;
+
+  BLOCK_CHILD (set, oset);
+
+  for (i = 0; i < job_slots; i++)
+    if (jobs[i])
+      jobs[i]->state = JDEAD;
+
+  UNBLOCK_CHILD (oset);
+}
+
+/* Mark all dead jobs as notified, so delete_job () cleans them out
+   of the job table properly.  POSIX.2 says we need to save the
+   status of the last CHILD_MAX jobs, so we count the number of dead
+   jobs and mark only enough as notified to save CHILD_MAX statuses. */
+static void
+mark_dead_jobs_as_notified (force)
+     int force;
+{
+  register int i, ndead;
+  sigset_t set, oset;
+
+  if (job_slots == 0)
+    return;
+
+  BLOCK_CHILD (set, oset);
+
+  /* If FORCE is non-zero, we don't have to keep CHILD_MAX statuses
+     around; just run through the array. */
+  if (force)
+    {
+      for (i = 0; i < job_slots; i++)
+       {
+         if (jobs[i] && DEADJOB (i) && (interactive_shell || (find_last_pid (i, 0) != last_asynchronous_pid)))
+           jobs[i]->flags |= J_NOTIFIED;
+       }
+      UNBLOCK_CHILD (oset);
+      return;
+    }
+
+  /* Mark enough dead jobs as notified to keep CHILD_MAX jobs left in the
+     array not marked as notified. */
+          
+  /* Count the number of dead jobs */
+  for (i = ndead = 0; i < job_slots; i++)
+    {
+      if (jobs[i] && DEADJOB (i))
+       ndead++;
+    }
+
+  if (child_max < 0)
+    child_max = getmaxchild ();
+  if (child_max < 0)
+    child_max = DEFAULT_CHILD_MAX;
+
+  /* Don't do anything if the number of dead jobs is less than CHILD_MAX and
+     we're not forcing a cleanup. */
+  if (ndead <= child_max)
+    {
+      UNBLOCK_CHILD (oset);
+      return;
+    }
+
+  /* Mark enough dead jobs as notified that we keep CHILD_MAX jobs in
+     the list.  This isn't exactly right yet; changes need to be made
+     to stop_pipeline so we don't mark the newer jobs after we've
+     created CHILD_MAX slots in the jobs array. */
+  for (i = 0; i < job_slots; i++)
+    {
+      if (jobs[i] && DEADJOB (i) && (interactive_shell || (find_last_pid (i, 0) != last_asynchronous_pid)))
+       {
+         jobs[i]->flags |= J_NOTIFIED;
+         if (--ndead <= child_max)
+           break;
+       }
+    }
+
+  UNBLOCK_CHILD (oset);
+}
+
+/* Here to allow other parts of the shell (like the trap stuff) to
+   unfreeze the jobs list. */
+void
+unfreeze_jobs_list ()
+{
+  jobs_list_frozen = 0;
+}
+
+/* Allow or disallow job control to take place.  Returns the old value
+   of job_control. */
+int
+set_job_control (arg)
+     int arg;
+{
+  int old;
+
+  old = job_control;
+  job_control = arg;
+
+  /* If we're turning on job control, reset pipeline_pgrp so make_child will
+     put new child processes into the right pgrp */
+  if (job_control != old && job_control)
+    pipeline_pgrp = 0;
+
+  return (old);
+}
+
+/* Turn off all traces of job control.  This is run by children of the shell
+   which are going to do shellsy things, like wait (), etc. */
+void
+without_job_control ()
+{
+  stop_making_children ();
+  start_pipeline ();
+  delete_all_jobs (0);
+  set_job_control (0);
+}
+
+/* If this shell is interactive, terminate all stopped jobs and
+   restore the original terminal process group.  This is done
+   before the `exec' builtin calls shell_execve. */
+void
+end_job_control ()
+{
+  if (interactive_shell)               /* XXX - should it be interactive? */
+    {
+      terminate_stopped_jobs ();
+
+      if (original_pgrp >= 0)
+       give_terminal_to (original_pgrp, 1);
+    }
+
+  if (original_pgrp >= 0)
+    setpgid (0, original_pgrp);
+}
+
+/* Restart job control by closing shell tty and reinitializing.  This is
+   called after an exec fails in an interactive shell and we do not exit. */
+void
+restart_job_control ()
+{
+  if (shell_tty != -1)
+    close (shell_tty);
+  initialize_job_control (0);
+}
+
+/* Set the handler to run when the shell receives a SIGCHLD signal. */
+void
+set_sigchld_handler ()
+{
+  set_signal_handler (SIGCHLD, sigchld_handler);
+}
+
+#if defined (PGRP_PIPE)
+/* Read from the read end of a pipe.  This is how the process group leader
+   blocks until all of the processes in a pipeline have been made. */
+static void
+pipe_read (pp)
+     int *pp;
+{
+  char ch;
+
+  if (pp[1] >= 0)
+    {
+      close (pp[1]);
+      pp[1] = -1;
+    }
+
+  if (pp[0] >= 0)
+    {
+      while (read (pp[0], &ch, 1) == -1 && errno == EINTR)
+       ;
+    }
+}
+
+/* Close the read and write ends of PP, an array of file descriptors. */
+static void
+pipe_close (pp)
+     int *pp;
+{
+  if (pp[0] >= 0)
+    close (pp[0]);
+
+  if (pp[1] >= 0)
+    close (pp[1]);
+
+  pp[0] = pp[1] = -1;
+}
+
+/* Functional interface closes our local-to-job-control pipes. */
+void
+close_pgrp_pipe ()
+{
+  pipe_close (pgrp_pipe);
+}
+
+#endif /* PGRP_PIPE */
index ab1e1337fd3ab88b1a8a5094fe96d65e7d1d3aad..3a6578f317e510cef8316e47be1b4ac1e626bcb2 100644 (file)
@@ -276,12 +276,14 @@ rl_maybe_save_line ()
       _rl_saved_line_for_history->line = savestring (rl_line_buffer);
       _rl_saved_line_for_history->data = (char *)rl_undo_list;
     }
+#if 0
   else if (STREQ (rl_line_buffer, _rl_saved_line_for_history->line) == 0)
     {
       free (_rl_saved_line_for_history->line);
       _rl_saved_line_for_history->line = savestring (rl_line_buffer);
       _rl_saved_line_for_history->data = (char *)rl_undo_list; /* XXX possible memleak */
     }
+#endif
 
   return 0;
 }
diff --git a/lib/readline/misc.c~ b/lib/readline/misc.c~
new file mode 100644 (file)
index 0000000..ab1e133
--- /dev/null
@@ -0,0 +1,508 @@
+/* misc.c -- miscellaneous bindable readline functions. */
+
+/* Copyright (C) 1987-2004 Free Software Foundation, Inc.
+
+   This file is part of the GNU Readline Library, a library for
+   reading lines of text with interactive input and history editing.
+
+   The GNU Readline Library 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 2, or
+   (at your option) any later version.
+
+   The GNU Readline Library 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.
+
+   The GNU General Public License is often shipped with GNU software, and
+   is generally kept in a file called COPYING or LICENSE.  If you do not
+   have a copy of the license, write to the Free Software Foundation,
+   59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+#define READLINE_LIBRARY
+
+#if defined (HAVE_CONFIG_H)
+#  include <config.h>
+#endif
+
+#if defined (HAVE_UNISTD_H)
+#  include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+
+#if defined (HAVE_STDLIB_H)
+#  include <stdlib.h>
+#else
+#  include "ansi_stdlib.h"
+#endif /* HAVE_STDLIB_H */
+
+#if defined (HAVE_LOCALE_H)
+#  include <locale.h>
+#endif
+
+#include <stdio.h>
+
+/* System-specific feature definitions and include files. */
+#include "rldefs.h"
+#include "rlmbutil.h"
+
+/* Some standard library routines. */
+#include "readline.h"
+#include "history.h"
+
+#include "rlprivate.h"
+#include "rlshell.h"
+#include "xmalloc.h"
+
+static int rl_digit_loop PARAMS((void));
+static void _rl_history_set_point PARAMS((void));
+
+/* Forward declarations used in this file */
+void _rl_free_history_entry PARAMS((HIST_ENTRY *));
+
+/* If non-zero, rl_get_previous_history and rl_get_next_history attempt
+   to preserve the value of rl_point from line to line. */
+int _rl_history_preserve_point = 0;
+
+/* Saved target point for when _rl_history_preserve_point is set.  Special
+   value of -1 means that point is at the end of the line. */
+int _rl_history_saved_point = -1;
+
+/* **************************************************************** */
+/*                                                                 */
+/*                     Numeric Arguments                           */
+/*                                                                 */
+/* **************************************************************** */
+
+/* Handle C-u style numeric args, as well as M--, and M-digits. */
+static int
+rl_digit_loop ()
+{
+  int key, c, sawminus, sawdigits;
+
+  rl_save_prompt ();
+
+  RL_SETSTATE(RL_STATE_NUMERICARG);
+  sawminus = sawdigits = 0;
+  while (1)
+    {
+      if (rl_numeric_arg > 1000000)
+       {
+         sawdigits = rl_explicit_arg = rl_numeric_arg = 0;
+         rl_ding ();
+         rl_restore_prompt ();
+         rl_clear_message ();
+         RL_UNSETSTATE(RL_STATE_NUMERICARG);
+         return 1;
+       }
+      rl_message ("(arg: %d) ", rl_arg_sign * rl_numeric_arg);
+      RL_SETSTATE(RL_STATE_MOREINPUT);
+      key = c = rl_read_key ();
+      RL_UNSETSTATE(RL_STATE_MOREINPUT);
+
+      if (c < 0)
+       {
+         _rl_abort_internal ();
+         return -1;
+       }
+
+      /* If we see a key bound to `universal-argument' after seeing digits,
+        it ends the argument but is otherwise ignored. */
+      if (_rl_keymap[c].type == ISFUNC &&
+         _rl_keymap[c].function == rl_universal_argument)
+       {
+         if (sawdigits == 0)
+           {
+             rl_numeric_arg *= 4;
+             continue;
+           }
+         else
+           {
+             RL_SETSTATE(RL_STATE_MOREINPUT);
+             key = rl_read_key ();
+             RL_UNSETSTATE(RL_STATE_MOREINPUT);
+             rl_restore_prompt ();
+             rl_clear_message ();
+             RL_UNSETSTATE(RL_STATE_NUMERICARG);
+             return (_rl_dispatch (key, _rl_keymap));
+           }
+       }
+
+      c = UNMETA (c);
+
+      if (_rl_digit_p (c))
+       {
+         rl_numeric_arg = rl_explicit_arg ? (rl_numeric_arg * 10) + c - '0' : c - '0';
+         sawdigits = rl_explicit_arg = 1;
+       }
+      else if (c == '-' && rl_explicit_arg == 0)
+       {
+         rl_numeric_arg = sawminus = 1;
+         rl_arg_sign = -1;
+       }
+      else
+       {
+         /* Make M-- command equivalent to M--1 command. */
+         if (sawminus && rl_numeric_arg == 1 && rl_explicit_arg == 0)
+           rl_explicit_arg = 1;
+         rl_restore_prompt ();
+         rl_clear_message ();
+         RL_UNSETSTATE(RL_STATE_NUMERICARG);
+         return (_rl_dispatch (key, _rl_keymap));
+       }
+    }
+
+  /*NOTREACHED*/
+}
+
+/* Add the current digit to the argument in progress. */
+int
+rl_digit_argument (ignore, key)
+     int ignore, key;
+{
+  rl_execute_next (key);
+  return (rl_digit_loop ());
+}
+
+/* What to do when you abort reading an argument. */
+int
+rl_discard_argument ()
+{
+  rl_ding ();
+  rl_clear_message ();
+  _rl_init_argument ();
+  return 0;
+}
+
+/* Create a default argument. */
+int
+_rl_init_argument ()
+{
+  rl_numeric_arg = rl_arg_sign = 1;
+  rl_explicit_arg = 0;
+  return 0;
+}
+
+/* C-u, universal argument.  Multiply the current argument by 4.
+   Read a key.  If the key has nothing to do with arguments, then
+   dispatch on it.  If the key is the abort character then abort. */
+int
+rl_universal_argument (count, key)
+     int count, key;
+{
+  rl_numeric_arg *= 4;
+  return (rl_digit_loop ());
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*                     History Utilities                           */
+/*                                                                 */
+/* **************************************************************** */
+
+/* We already have a history library, and that is what we use to control
+   the history features of readline.  This is our local interface to
+   the history mechanism. */
+
+/* While we are editing the history, this is the saved
+   version of the original line. */
+HIST_ENTRY *_rl_saved_line_for_history = (HIST_ENTRY *)NULL;
+
+/* Set the history pointer back to the last entry in the history. */
+void
+_rl_start_using_history ()
+{
+  using_history ();
+  if (_rl_saved_line_for_history)
+    _rl_free_history_entry (_rl_saved_line_for_history);
+
+  _rl_saved_line_for_history = (HIST_ENTRY *)NULL;
+}
+
+/* Free the contents (and containing structure) of a HIST_ENTRY. */
+void
+_rl_free_history_entry (entry)
+     HIST_ENTRY *entry;
+{
+  if (entry == 0)
+    return;
+  if (entry->line)
+    free (entry->line);
+  free (entry);
+}
+
+/* Perhaps put back the current line if it has changed. */
+int
+rl_maybe_replace_line ()
+{
+  HIST_ENTRY *temp;
+
+  temp = current_history ();
+  /* If the current line has changed, save the changes. */
+  if (temp && ((UNDO_LIST *)(temp->data) != rl_undo_list))
+    {
+      temp = replace_history_entry (where_history (), rl_line_buffer, (histdata_t)rl_undo_list);
+      free (temp->line);
+      free (temp);
+    }
+  return 0;
+}
+
+/* Restore the _rl_saved_line_for_history if there is one. */
+int
+rl_maybe_unsave_line ()
+{
+  if (_rl_saved_line_for_history)
+    {
+      /* Can't call with `1' because rl_undo_list might point to an undo
+        list from a history entry, as in rl_replace_from_history() below. */
+      rl_replace_line (_rl_saved_line_for_history->line, 0);
+      rl_undo_list = (UNDO_LIST *)_rl_saved_line_for_history->data;
+      _rl_free_history_entry (_rl_saved_line_for_history);
+      _rl_saved_line_for_history = (HIST_ENTRY *)NULL;
+      rl_point = rl_end;       /* rl_replace_line sets rl_end */
+    }
+  else
+    rl_ding ();
+  return 0;
+}
+
+/* Save the current line in _rl_saved_line_for_history. */
+int
+rl_maybe_save_line ()
+{
+  if (_rl_saved_line_for_history == 0)
+    {
+      _rl_saved_line_for_history = (HIST_ENTRY *)xmalloc (sizeof (HIST_ENTRY));
+      _rl_saved_line_for_history->line = savestring (rl_line_buffer);
+      _rl_saved_line_for_history->data = (char *)rl_undo_list;
+    }
+  else if (STREQ (rl_line_buffer, _rl_saved_line_for_history->line) == 0)
+    {
+      free (_rl_saved_line_for_history->line);
+      _rl_saved_line_for_history->line = savestring (rl_line_buffer);
+      _rl_saved_line_for_history->data = (char *)rl_undo_list; /* XXX possible memleak */
+    }
+
+  return 0;
+}
+
+int
+_rl_free_saved_history_line ()
+{
+  if (_rl_saved_line_for_history)
+    {
+      _rl_free_history_entry (_rl_saved_line_for_history);
+      _rl_saved_line_for_history = (HIST_ENTRY *)NULL;
+    }
+  return 0;
+}
+
+static void
+_rl_history_set_point ()
+{
+  rl_point = (_rl_history_preserve_point && _rl_history_saved_point != -1)
+               ? _rl_history_saved_point
+               : rl_end;
+  if (rl_point > rl_end)
+    rl_point = rl_end;
+
+#if defined (VI_MODE)
+  if (rl_editing_mode == vi_mode && _rl_keymap != vi_insertion_keymap)
+    rl_point = 0;
+#endif /* VI_MODE */
+
+  if (rl_editing_mode == emacs_mode)
+    rl_mark = (rl_point == rl_end ? 0 : rl_end);
+}
+
+void
+rl_replace_from_history (entry, flags)
+     HIST_ENTRY *entry;
+     int flags;                        /* currently unused */
+{
+  /* Can't call with `1' because rl_undo_list might point to an undo list
+     from a history entry, just like we're setting up here. */
+  rl_replace_line (entry->line, 0);
+  rl_undo_list = (UNDO_LIST *)entry->data;
+  rl_point = rl_end;
+  rl_mark = 0;
+
+#if defined (VI_MODE)
+  if (rl_editing_mode == vi_mode)
+    {
+      rl_point = 0;
+      rl_mark = rl_end;
+    }
+#endif
+}  
+
+/* **************************************************************** */
+/*                                                                 */
+/*                     History Commands                            */
+/*                                                                 */
+/* **************************************************************** */
+
+/* Meta-< goes to the start of the history. */
+int
+rl_beginning_of_history (count, key)
+     int count, key;
+{
+  return (rl_get_previous_history (1 + where_history (), key));
+}
+
+/* Meta-> goes to the end of the history.  (The current line). */
+int
+rl_end_of_history (count, key)
+     int count, key;
+{
+  rl_maybe_replace_line ();
+  using_history ();
+  rl_maybe_unsave_line ();
+  return 0;
+}
+
+/* Move down to the next history line. */
+int
+rl_get_next_history (count, key)
+     int count, key;
+{
+  HIST_ENTRY *temp;
+
+  if (count < 0)
+    return (rl_get_previous_history (-count, key));
+
+  if (count == 0)
+    return 0;
+
+  rl_maybe_replace_line ();
+
+  /* either not saved by rl_newline or at end of line, so set appropriately. */
+  if (_rl_history_saved_point == -1 && (rl_point || rl_end))
+    _rl_history_saved_point = (rl_point == rl_end) ? -1 : rl_point;
+
+  temp = (HIST_ENTRY *)NULL;
+  while (count)
+    {
+      temp = next_history ();
+      if (!temp)
+       break;
+      --count;
+    }
+
+  if (temp == 0)
+    rl_maybe_unsave_line ();
+  else
+    {
+      rl_replace_from_history (temp, 0);
+      _rl_history_set_point ();
+    }
+  return 0;
+}
+
+/* Get the previous item out of our interactive history, making it the current
+   line.  If there is no previous history, just ding. */
+int
+rl_get_previous_history (count, key)
+     int count, key;
+{
+  HIST_ENTRY *old_temp, *temp;
+
+  if (count < 0)
+    return (rl_get_next_history (-count, key));
+
+  if (count == 0)
+    return 0;
+
+  /* either not saved by rl_newline or at end of line, so set appropriately. */
+  if (_rl_history_saved_point == -1 && (rl_point || rl_end))
+    _rl_history_saved_point = (rl_point == rl_end) ? -1 : rl_point;
+
+  /* If we don't have a line saved, then save this one. */
+  rl_maybe_save_line ();
+
+  /* If the current line has changed, save the changes. */
+  rl_maybe_replace_line ();
+
+  temp = old_temp = (HIST_ENTRY *)NULL;
+  while (count)
+    {
+      temp = previous_history ();
+      if (temp == 0)
+       break;
+
+      old_temp = temp;
+      --count;
+    }
+
+  /* If there was a large argument, and we moved back to the start of the
+     history, that is not an error.  So use the last value found. */
+  if (!temp && old_temp)
+    temp = old_temp;
+
+  if (temp == 0)
+    rl_ding ();
+  else
+    {
+      rl_replace_from_history (temp, 0);
+      _rl_history_set_point ();
+    }
+
+  return 0;
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*                         Editing Modes                           */
+/*                                                                 */
+/* **************************************************************** */
+/* How to toggle back and forth between editing modes. */
+int
+rl_vi_editing_mode (count, key)
+     int count, key;
+{
+#if defined (VI_MODE)
+  _rl_set_insert_mode (RL_IM_INSERT, 1);       /* vi mode ignores insert mode */
+  rl_editing_mode = vi_mode;
+  rl_vi_insertion_mode (1, key);
+#endif /* VI_MODE */
+
+  return 0;
+}
+
+int
+rl_emacs_editing_mode (count, key)
+     int count, key;
+{
+  rl_editing_mode = emacs_mode;
+  _rl_set_insert_mode (RL_IM_INSERT, 1); /* emacs mode default is insert mode */
+  _rl_keymap = emacs_standard_keymap;
+  return 0;
+}
+
+/* Function for the rest of the library to use to set insert/overwrite mode. */
+void
+_rl_set_insert_mode (im, force)
+     int im, force;
+{
+#ifdef CURSOR_MODE
+  _rl_set_cursor (im, force);
+#endif
+
+  rl_insert_mode = im;
+}
+
+/* Toggle overwrite mode.  A positive explicit argument selects overwrite
+   mode.  A negative or zero explicit argument selects insert mode. */
+int
+rl_overwrite_mode (count, key)
+     int count, key;
+{
+  if (rl_explicit_arg == 0)
+    _rl_set_insert_mode (rl_insert_mode ^ 1, 0);
+  else if (count > 0)
+    _rl_set_insert_mode (RL_IM_OVERWRITE, 0);
+  else
+    _rl_set_insert_mode (RL_IM_INSERT, 0);
+
+  return 0;
+}
index 0c4772a9f10f0d4664933023a3038d5db90814a5..48e44ec4865efb93c93856f20f89365955f80639 100644 (file)
--- a/locale.c
+++ b/locale.c
@@ -71,8 +71,8 @@ set_default_locale ()
   textdomain (PACKAGE);
 }
 
-/* Set default values for LC_CTYPE, LC_COLLATE, LC_MESSAGES and LC_NUMERIC
-   if they are not specified in the environment, but LC_ALL is.  This
+/* Set default values for LC_CTYPE, LC_COLLATE, LC_MESSAGES, LC_NUMERIC and
+   LC_TIME if they are not specified in the environment, but LC_ALL is.  This
    should be called from main() after parsing the environment. */
 void
 set_default_locale_vars ()
@@ -109,6 +109,12 @@ set_default_locale_vars ()
     setlocale (LC_NUMERIC, lc_all);
 #  endif /* LC_NUMERIC */
 
+#  if defined (LC_TIME)
+  val = get_string_value ("LC_TIME");
+  if (val == 0 && lc_all && *lc_all)
+    setlocale (LC_TIME, lc_all);
+#  endif /* LC_TIME */
+
 #endif /* HAVE_SETLOCALE */
 
   val = get_string_value ("TEXTDOMAIN");
@@ -213,7 +219,15 @@ set_locale_var (var, value)
        return (setlocale (LC_NUMERIC, get_locale_var ("LC_NUMERIC")) != 0);
 #  endif /* LC_NUMERIC */
     }
+  else if (var[3] == 'T' && var[4] == 'I')     /* LC_TIME */
+    {
+#  if defined (LC_TIME)
+      if (lc_all == 0 || *lc_all == '\0')
+       return (setlocale (LC_TIME, get_locale_var ("LC_TIME")) != 0);
+#  endif /* LC_TIME */
+    }
 #endif /* HAVE_SETLOCALE */
+  
 
   return (0);
 }
@@ -285,6 +299,9 @@ reset_locale_vars ()
 #  if defined (LC_NUMERIC)
   setlocale (LC_NUMERIC, get_locale_var ("LC_NUMERIC"));
 #  endif
+#  if defined (LC_TIME)
+  setlocale (LC_TIME, get_locale_var ("LC_TIME"));
+#  endif
 
   locale_setblanks ();  
 
diff --git a/locale.c~ b/locale.c~
new file mode 100644 (file)
index 0000000..0c4772a
--- /dev/null
+++ b/locale.c~
@@ -0,0 +1,479 @@
+/* locale.c - Miscellaneous internationalization functions. */
+
+/* Copyright (C) 1996-2004 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 2, 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, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+
+#include "config.h"
+
+#include "bashtypes.h"
+
+#if defined (HAVE_UNISTD_H)
+#  include <unistd.h>
+#endif
+
+#include "bashintl.h"
+#include "bashansi.h"
+#include <stdio.h>
+#include "chartypes.h"
+
+#include "shell.h"
+#include "input.h"     /* For bash_input */
+
+extern int dump_translatable_strings, dump_po_strings;
+
+/* The current locale when the program begins */
+static char *default_locale;
+
+/* The current domain for textdomain(3). */
+static char *default_domain;
+static char *default_dir;
+
+/* tracks the value of LC_ALL; used to override values for other locale
+   categories */
+static char *lc_all;
+
+/* tracks the value of LC_ALL; used to provide defaults for locale
+   categories */
+static char *lang;
+
+/* Called to reset all of the locale variables to their appropriate values
+   if (and only if) LC_ALL has not been assigned a value. */
+static int reset_locale_vars __P((void));
+
+static void locale_setblanks __P((void));
+
+/* Set the value of default_locale and make the current locale the
+   system default locale.  This should be called very early in main(). */
+void
+set_default_locale ()
+{
+#if defined (HAVE_SETLOCALE)
+  default_locale = setlocale (LC_ALL, "");
+  if (default_locale)
+    default_locale = savestring (default_locale);
+#endif /* HAVE_SETLOCALE */
+  bindtextdomain (PACKAGE, LOCALEDIR);
+  textdomain (PACKAGE);
+}
+
+/* Set default values for LC_CTYPE, LC_COLLATE, LC_MESSAGES and LC_NUMERIC
+   if they are not specified in the environment, but LC_ALL is.  This
+   should be called from main() after parsing the environment. */
+void
+set_default_locale_vars ()
+{
+  char *val;
+  int r;
+
+#if defined (HAVE_SETLOCALE)
+
+#  if defined (LC_CTYPE)
+  val = get_string_value ("LC_CTYPE");
+  if (val == 0 && lc_all && *lc_all)
+    {
+      setlocale (LC_CTYPE, lc_all);
+      locale_setblanks ();
+    }
+#  endif
+
+#  if defined (LC_COLLATE)
+  val = get_string_value ("LC_COLLATE");
+  if (val == 0 && lc_all && *lc_all)
+    setlocale (LC_COLLATE, lc_all);
+#  endif /* LC_COLLATE */
+
+#  if defined (LC_MESSAGES)
+  val = get_string_value ("LC_MESSAGES");
+  if (val == 0 && lc_all && *lc_all)
+    setlocale (LC_MESSAGES, lc_all);
+#  endif /* LC_MESSAGES */
+
+#  if defined (LC_NUMERIC)
+  val = get_string_value ("LC_NUMERIC");
+  if (val == 0 && lc_all && *lc_all)
+    setlocale (LC_NUMERIC, lc_all);
+#  endif /* LC_NUMERIC */
+
+#endif /* HAVE_SETLOCALE */
+
+  val = get_string_value ("TEXTDOMAIN");
+  if (val && *val)
+    {
+      FREE (default_domain);
+      default_domain = savestring (val);
+#if 0
+      /* Don't want to override the shell's textdomain as the default */
+      textdomain (default_domain);
+#endif
+    }
+
+  val = get_string_value ("TEXTDOMAINDIR");
+  if (val && *val)
+    {
+      FREE (default_dir);
+      default_dir = savestring (val);
+      if (default_domain && *default_domain)
+       bindtextdomain (default_domain, default_dir);
+    }
+}
+
+/* Set one of the locale categories (specified by VAR) to VALUE.  Returns 1
+  if successful, 0 otherwise. */
+int
+set_locale_var (var, value)
+     char *var, *value;
+{
+  int r;
+
+  if (var[0] == 'T' && var[10] == 0)           /* TEXTDOMAIN */
+    {
+      FREE (default_domain);
+      default_domain = value ? savestring (value) : (char *)NULL;
+#if 0
+      /* Don't want to override the shell's textdomain as the default */
+      textdomain (default_domain);
+#endif
+      return (1);
+    }
+  else if (var[0] == 'T')                      /* TEXTDOMAINDIR */
+    {
+      FREE (default_dir);
+      default_dir = value ? savestring (value) : (char *)NULL;
+      if (default_domain && *default_domain)
+       bindtextdomain (default_domain, default_dir);
+      return (1);
+    }
+
+  /* var[0] == 'L' && var[1] == 'C' && var[2] == '_' */
+
+  else if (var[3] == 'A')                      /* LC_ALL */
+    {
+      FREE (lc_all);
+      if (value)
+       lc_all = savestring (value);
+      else
+       {
+         lc_all = (char *)xmalloc (1);
+         lc_all[0] = '\0';
+       }
+#if defined (HAVE_SETLOCALE)
+      r = *lc_all ? (setlocale (LC_ALL, lc_all) != 0) : reset_locale_vars ();
+      locale_setblanks ();
+      return r;
+#else
+      return (1);
+#endif
+    }
+
+#if defined (HAVE_SETLOCALE)
+  else if (var[3] == 'C' && var[4] == 'T')     /* LC_CTYPE */
+    {
+#  if defined (LC_CTYPE)
+      if (lc_all == 0 || *lc_all == '\0')
+       {
+         r = (setlocale (LC_CTYPE, get_locale_var ("LC_CTYPE")) != 0);
+         locale_setblanks ();
+         return r;
+       }
+#  endif
+    }
+  else if (var[3] == 'C' && var[4] == 'O')     /* LC_COLLATE */
+    {
+#  if defined (LC_COLLATE)
+      if (lc_all == 0 || *lc_all == '\0')
+       return (setlocale (LC_COLLATE, get_locale_var ("LC_COLLATE")) != 0);
+#  endif /* LC_COLLATE */
+    }
+  else if (var[3] == 'M' && var[4] == 'E')     /* LC_MESSAGES */
+    {
+#  if defined (LC_MESSAGES)
+      if (lc_all == 0 || *lc_all == '\0')
+       return (setlocale (LC_MESSAGES, get_locale_var ("LC_MESSAGES")) != 0);
+#  endif /* LC_MESSAGES */
+    }
+  else if (var[3] == 'N' && var[4] == 'U')     /* LC_NUMERIC */
+    {
+#  if defined (LC_NUMERIC)
+      if (lc_all == 0 || *lc_all == '\0')
+       return (setlocale (LC_NUMERIC, get_locale_var ("LC_NUMERIC")) != 0);
+#  endif /* LC_NUMERIC */
+    }
+#endif /* HAVE_SETLOCALE */
+
+  return (0);
+}
+
+/* Called when LANG is assigned a value.  Tracks value in `lang'.  Calls
+   reset_locale_vars() to reset any default values if LC_ALL is unset or
+   null. */
+int
+set_lang (var, value)
+     char *var, *value;
+{
+  FREE (lang);
+  if (value)
+    lang = savestring (value);
+  else
+    {
+      lang = (char *)xmalloc (1);
+      lang[0] = '\0';
+    }
+    
+  return ((lc_all == 0 || *lc_all == 0) ? reset_locale_vars () : 0);
+}
+
+/* Get the value of one of the locale variables (LC_MESSAGES, LC_CTYPE).
+   The precedence is as POSIX.2 specifies:  LC_ALL has precedence over
+   the specific locale variables, and LANG, if set, is used as the default. */
+char *
+get_locale_var (var)
+     char *var;
+{
+  char *locale;
+
+  locale = lc_all;
+
+  if (locale == 0 || *locale == 0)
+    locale = get_string_value (var);
+  if (locale == 0 || *locale == 0)
+    locale = lang;
+  if (locale == 0 || *locale == 0)
+    locale = default_locale;   /* system-dependent; not really portable */
+
+  return (locale);
+}
+
+/* Called to reset all of the locale variables to their appropriate values
+   if (and only if) LC_ALL has not been assigned a value.  DO NOT CALL THIS
+   IF LC_ALL HAS BEEN ASSIGNED A VALUE. */
+static int
+reset_locale_vars ()
+{
+#if defined (HAVE_SETLOCALE)
+  char *locale;
+
+  locale = lang;
+  if (locale == 0 || *locale == '\0')
+    locale = default_locale;
+  if (setlocale (LC_ALL, locale) == 0)
+    return 0;
+
+#  if defined (LC_CTYPE)
+  setlocale (LC_CTYPE, get_locale_var ("LC_CTYPE"));
+#  endif
+#  if defined (LC_COLLATE)
+  setlocale (LC_COLLATE, get_locale_var ("LC_COLLATE"));
+#  endif
+#  if defined (LC_MESSAGES)
+  setlocale (LC_MESSAGES, get_locale_var ("LC_MESSAGES"));
+#  endif
+#  if defined (LC_NUMERIC)
+  setlocale (LC_NUMERIC, get_locale_var ("LC_NUMERIC"));
+#  endif
+
+  locale_setblanks ();  
+
+#endif
+  return 1;
+}
+
+/* Translate the contents of STRING, a $"..." quoted string, according
+   to the current locale.  In the `C' or `POSIX' locale, or if gettext()
+   is not available, the passed string is returned unchanged.  The
+   length of the translated string is returned in LENP, if non-null. */
+char *
+localetrans (string, len, lenp)
+     char *string;
+     int len, *lenp;
+{
+  char *locale, *t;
+  char *translated;
+  int tlen;
+
+  /* Don't try to translate null strings. */
+  if (string == 0 || *string == 0)
+    {
+      if (lenp)
+       *lenp = 0;
+      return ((char *)NULL);
+    }
+
+  locale = get_locale_var ("LC_MESSAGES");
+
+  /* If we don't have setlocale() or the current locale is `C' or `POSIX',
+     just return the string.  If we don't have gettext(), there's no use
+     doing anything else. */
+  if (locale == 0 || locale[0] == '\0' ||
+      (locale[0] == 'C' && locale[1] == '\0') || STREQ (locale, "POSIX"))
+    {
+      t = (char *)xmalloc (len + 1);
+      strcpy (t, string);
+      if (lenp)
+       *lenp = len;
+      return (t);
+    }
+
+  /* Now try to translate it. */
+  if (default_domain && *default_domain)
+    translated = dgettext (default_domain, string);
+  else
+    translated = string;
+
+  if (translated == string)    /* gettext returns its argument if untranslatable */
+    {
+      t = (char *)xmalloc (len + 1);
+      strcpy (t, string);
+      if (lenp)
+       *lenp = len;
+    }
+  else
+    {
+      tlen = strlen (translated);
+      t = (char *)xmalloc (tlen + 1);
+      strcpy (t, translated);
+      if (lenp)
+       *lenp = tlen;
+    }
+  return (t);
+}
+
+/* Change a bash string into a string suitable for inclusion in a `po' file.
+   This backslash-escapes `"' and `\' and changes newlines into \\\n"\n". */
+char *
+mk_msgstr (string, foundnlp)
+     char *string;
+     int *foundnlp;
+{
+  register int c, len;
+  char *result, *r, *s;
+
+  for (len = 0, s = string; s && *s; s++)
+    {
+      len++;
+      if (*s == '"' || *s == '\\')
+       len++;
+      else if (*s == '\n')
+       len += 5;
+    }
+  
+  r = result = (char *)xmalloc (len + 3);
+  *r++ = '"';
+
+  for (s = string; s && (c = *s); s++)
+    {
+      if (c == '\n')   /* <NL> -> \n"<NL>" */
+       {
+         *r++ = '\\';
+         *r++ = 'n';
+         *r++ = '"';
+         *r++ = '\n';
+         *r++ = '"';
+         if (foundnlp)
+           *foundnlp = 1;
+         continue;
+       }
+      if (c == '"' || c == '\\')
+       *r++ = '\\';
+      *r++ = c;
+    }
+
+  *r++ = '"';
+  *r++ = '\0';
+
+  return result;
+}
+
+/* $"..." -- Translate the portion of STRING between START and END
+   according to current locale using gettext (if available) and return
+   the result.  The caller will take care of leaving the quotes intact.
+   The string will be left without the leading `$' by the caller.
+   If translation is performed, the translated string will be double-quoted
+   by the caller.  The length of the translated string is returned in LENP,
+   if non-null. */
+char *
+localeexpand (string, start, end, lineno, lenp)
+     char *string;
+     int start, end, lineno, *lenp;
+{
+  int len, tlen, foundnl;
+  char *temp, *t, *t2;
+
+  temp = (char *)xmalloc (end - start + 1);
+  for (tlen = 0, len = start; len < end; )
+    temp[tlen++] = string[len++];
+  temp[tlen] = '\0';
+
+  /* If we're just dumping translatable strings, don't do anything with the
+     string itself, but if we're dumping in `po' file format, convert it into
+     a form more palatable to gettext(3) and friends by quoting `"' and `\'
+     with backslashes and converting <NL> into `\n"<NL>"'.  If we find a
+     newline in TEMP, we first output a `msgid ""' line and then the
+     translated string; otherwise we output the `msgid' and translated
+     string all on one line. */
+  if (dump_translatable_strings)
+    {
+      if (dump_po_strings)
+       {
+         foundnl = 0;
+         t = mk_msgstr (temp, &foundnl);
+         t2 = foundnl ? "\"\"\n" : "";
+
+         printf ("#: %s:%d\nmsgid %s%s\nmsgstr \"\"\n",
+                       yy_input_name (), lineno, t2, t);
+         free (t);
+       }
+      else
+       printf ("\"%s\"\n", temp);
+
+      if (lenp)
+       *lenp = tlen;
+      return (temp);
+    }
+  else if (*temp)
+    {
+      t = localetrans (temp, tlen, &len);
+      free (temp);
+      if (lenp)
+       *lenp = len;
+      return (t);
+    }
+  else
+    {
+      if (lenp)
+       *lenp = 0;
+      return (temp);
+    }
+}
+
+/* Set every character in the <blank> character class to be a shell break
+   character for the lexical analyzer when the locale changes. */
+static void
+locale_setblanks ()
+{
+  int x;
+
+  for (x = 0; x < sh_syntabsiz; x++)
+    {
+      if (isblank (x))
+       sh_syntaxtab[x] |= CSHBRK;
+      else if (member (x, shell_break_chars))
+       sh_syntaxtab[x] |= CSHBRK;
+      else
+       sh_syntaxtab[x] &= ~CSHBRK;
+    }
+}
diff --git a/sig.c b/sig.c
index 0771f14beac0347982c380d4d29b5e1c453e95b0..15af34a4e363b4dfb73b8e762eaba32303a02f80 100644 (file)
--- a/sig.c
+++ b/sig.c
@@ -93,6 +93,7 @@ initialize_signals (reinit)
 struct termsig {
      int signum;
      SigHandler *orig_handler;
+     int orig_flags;
 };
 
 #define NULL_HANDLER (SigHandler *)SIG_DFL
@@ -102,89 +103,89 @@ struct termsig {
    and so forth. */
 static struct termsig terminating_signals[] = {
 #ifdef SIGHUP
-{  SIGHUP, NULL_HANDLER },
+{  SIGHUP, NULL_HANDLER, 0 },
 #endif
 
 #ifdef SIGINT
-{  SIGINT, NULL_HANDLER },
+{  SIGINT, NULL_HANDLER, 0 },
 #endif
 
 #ifdef SIGILL
-{  SIGILL, NULL_HANDLER },
+{  SIGILL, NULL_HANDLER, 0 },
 #endif
 
 #ifdef SIGTRAP
-{  SIGTRAP, NULL_HANDLER },
+{  SIGTRAP, NULL_HANDLER, 0 },
 #endif
 
 #ifdef SIGIOT
-{  SIGIOT, NULL_HANDLER },
+{  SIGIOT, NULL_HANDLER, 0 },
 #endif
 
 #ifdef SIGDANGER
-{  SIGDANGER, NULL_HANDLER },
+{  SIGDANGER, NULL_HANDLER, 0 },
 #endif
 
 #ifdef SIGEMT
-{  SIGEMT, NULL_HANDLER },
+{  SIGEMT, NULL_HANDLER, 0 },
 #endif
 
 #ifdef SIGFPE
-{  SIGFPE, NULL_HANDLER },
+{  SIGFPE, NULL_HANDLER, 0 },
 #endif
 
 #ifdef SIGBUS
-{  SIGBUS, NULL_HANDLER },
+{  SIGBUS, NULL_HANDLER, 0 },
 #endif
 
 #ifdef SIGSEGV
-{  SIGSEGV, NULL_HANDLER },
+{  SIGSEGV, NULL_HANDLER, 0 },
 #endif
 
 #ifdef SIGSYS
-{  SIGSYS, NULL_HANDLER },
+{  SIGSYS, NULL_HANDLER, 0 },
 #endif
 
 #ifdef SIGPIPE
-{  SIGPIPE, NULL_HANDLER },
+{  SIGPIPE, NULL_HANDLER, 0 },
 #endif
 
 #ifdef SIGALRM
-{  SIGALRM, NULL_HANDLER },
+{  SIGALRM, NULL_HANDLER, 0 },
 #endif
 
 #ifdef SIGTERM
-{  SIGTERM, NULL_HANDLER },
+{  SIGTERM, NULL_HANDLER, 0 },
 #endif
 
 #ifdef SIGXCPU
-{  SIGXCPU, NULL_HANDLER },
+{  SIGXCPU, NULL_HANDLER, 0 },
 #endif
 
 #ifdef SIGXFSZ
-{  SIGXFSZ, NULL_HANDLER },
+{  SIGXFSZ, NULL_HANDLER, 0 },
 #endif
 
 #ifdef SIGVTALRM
-{  SIGVTALRM, NULL_HANDLER },
+{  SIGVTALRM, NULL_HANDLER, 0 },
 #endif
 
 #if 0
 #ifdef SIGPROF
-{  SIGPROF, NULL_HANDLER },
+{  SIGPROF, NULL_HANDLER, 0 },
 #endif
 #endif
 
 #ifdef SIGLOST
-{  SIGLOST, NULL_HANDLER },
+{  SIGLOST, NULL_HANDLER, 0 },
 #endif
 
 #ifdef SIGUSR1
-{  SIGUSR1, NULL_HANDLER },
+{  SIGUSR1, NULL_HANDLER, 0 },
 #endif
 
 #ifdef SIGUSR2
-{  SIGUSR2, NULL_HANDLER },
+{  SIGUSR2, NULL_HANDLER, 0 },
 #endif
 };
 
@@ -192,6 +193,7 @@ static struct termsig terminating_signals[] = {
 
 #define XSIG(x) (terminating_signals[x].signum)
 #define XHANDLER(x) (terminating_signals[x].orig_handler)
+#define XSAFLAGS(x) (terminating_signals[x].orig_flags)
 
 static int termsigs_initialized = 0;
 
@@ -228,6 +230,7 @@ initialize_terminating_signals ()
 
       sigaction (XSIG (i), &act, &oact);
       XHANDLER(i) = oact.sa_handler;
+      XSAFLAGS(i) = oact.sa_flags;
       /* Don't do anything with signals that are ignored at shell entry
         if the shell is not interactive. */
       if (!interactive_shell && XHANDLER (i) == SIG_IGN)
@@ -250,6 +253,7 @@ initialize_terminating_signals ()
        continue;
 
       XHANDLER(i) = signal (XSIG (i), termination_unwind_protect);
+      XSAFLAGS(i) = 0;
       /* Don't do anything with signals that are ignored at shell entry
         if the shell is not interactive. */
       if (!interactive_shell && XHANDLER (i) == SIG_IGN)
@@ -316,6 +320,7 @@ reset_terminating_signals ()
        continue;
 
       act.sa_handler = XHANDLER (i);
+      act.sa_flags = XSAFLAGS (i);
       sigaction (XSIG (i), &act, (struct sigaction *) NULL);
     }
 #else /* !HAVE_POSIX_SIGNALS */
diff --git a/sig.c~ b/sig.c~
new file mode 100644 (file)
index 0000000..1759639
--- /dev/null
+++ b/sig.c~
@@ -0,0 +1,525 @@
+/* sig.c - interface for shell signal handlers and signal initialization. */
+
+/* Copyright (C) 1994 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 2, 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, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+
+#include "config.h"
+
+#include "bashtypes.h"
+
+#if defined (HAVE_UNISTD_H)
+#  ifdef _MINIX
+#    include <sys/types.h>
+#  endif
+#  include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include <signal.h>
+
+#include "bashintl.h"
+
+#include "shell.h"
+#if defined (JOB_CONTROL)
+#include "jobs.h"
+#endif /* JOB_CONTROL */
+#include "siglist.h"
+#include "sig.h"
+#include "trap.h"
+
+#include "builtins/common.h"
+
+#if defined (READLINE)
+#  include "bashline.h"
+#endif
+
+#if defined (HISTORY)
+#  include "bashhist.h"
+#endif
+
+extern int last_command_exit_value;
+extern int last_command_exit_signal;
+extern int return_catch_flag;
+extern int loop_level, continuing, breaking;
+extern int parse_and_execute_level, shell_initialized;
+
+/* Non-zero after SIGINT. */
+int interrupt_state;
+
+/* The environment at the top-level R-E loop.  We use this in
+   the case of error return. */
+procenv_t top_level;
+
+#if defined (JOB_CONTROL) || defined (HAVE_POSIX_SIGNALS)
+/* The signal masks that this shell runs with. */
+sigset_t top_level_mask;
+#endif /* JOB_CONTROL */
+
+/* When non-zero, we throw_to_top_level (). */
+int interrupt_immediately = 0;
+
+static void initialize_shell_signals __P((void));
+
+void
+initialize_signals (reinit)
+     int reinit;
+{
+  initialize_shell_signals ();
+  initialize_job_signals ();
+#if !defined (HAVE_SYS_SIGLIST) && !defined (HAVE_UNDER_SYS_SIGLIST) && !defined (HAVE_STRSIGNAL)
+  if (reinit == 0)
+    initialize_siglist ();
+#endif /* !HAVE_SYS_SIGLIST && !HAVE_UNDER_SYS_SIGLIST && !HAVE_STRSIGNAL */
+}
+
+/* A structure describing a signal that terminates the shell if not
+   caught.  The orig_handler member is present so children can reset
+   these signals back to their original handlers. */
+struct termsig {
+     int signum;
+     SigHandler *orig_handler;
+     int orig_flags;
+};
+
+#define NULL_HANDLER (SigHandler *)SIG_DFL
+
+/* The list of signals that would terminate the shell if not caught.
+   We catch them, but just so that we can write the history file,
+   and so forth. */
+static struct termsig terminating_signals[] = {
+#ifdef SIGHUP
+{  SIGHUP, NULL_HANDLER, 0 },
+#endif
+
+#ifdef SIGINT
+{  SIGINT, NULL_HANDLER, 0 },
+#endif
+
+#ifdef SIGILL
+{  SIGILL, NULL_HANDLER, 0 },
+#endif
+
+#ifdef SIGTRAP
+{  SIGTRAP, NULL_HANDLER, 0 },
+#endif
+
+#ifdef SIGIOT
+{  SIGIOT, NULL_HANDLER, 0 },
+#endif
+
+#ifdef SIGDANGER
+{  SIGDANGER, NULL_HANDLER, 0 },
+#endif
+
+#ifdef SIGEMT
+{  SIGEMT, NULL_HANDLER, 0 },
+#endif
+
+#ifdef SIGFPE
+{  SIGFPE, NULL_HANDLER, 0 },
+#endif
+
+#ifdef SIGBUS
+{  SIGBUS, NULL_HANDLER, 0 },
+#endif
+
+#ifdef SIGSEGV
+{  SIGSEGV, NULL_HANDLER, 0 },
+#endif
+
+#ifdef SIGSYS
+{  SIGSYS, NULL_HANDLER, 0 },
+#endif
+
+#ifdef SIGPIPE
+{  SIGPIPE, NULL_HANDLER, 0 },
+#endif
+
+#ifdef SIGALRM
+{  SIGALRM, NULL_HANDLER, 0 },
+#endif
+
+#ifdef SIGTERM
+{  SIGTERM, NULL_HANDLER, 0 },
+#endif
+
+#ifdef SIGXCPU
+{  SIGXCPU, NULL_HANDLER, 0 },
+#endif
+
+#ifdef SIGXFSZ
+{  SIGXFSZ, NULL_HANDLER, 0 },
+#endif
+
+#ifdef SIGVTALRM
+{  SIGVTALRM, NULL_HANDLER, 0 },
+#endif
+
+#if 0
+#ifdef SIGPROF
+{  SIGPROF, NULL_HANDLER, 0 },
+#endif
+#endif
+
+#ifdef SIGLOST
+{  SIGLOST, NULL_HANDLER, 0 },
+#endif
+
+#ifdef SIGUSR1
+{  SIGUSR1, NULL_HANDLER, 0 },
+#endif
+
+#ifdef SIGUSR2
+{  SIGUSR2, NULL_HANDLER, 0 },
+#endif
+};
+
+#define TERMSIGS_LENGTH (sizeof (terminating_signals) / sizeof (struct termsig))
+
+#define XSIG(x) (terminating_signals[x].signum)
+#define XHANDLER(x) (terminating_signals[x].orig_handler)
+#define XSAFLAGS(x) (terminating_signals[x].orig_flags)
+
+static int termsigs_initialized = 0;
+
+/* Initialize signals that will terminate the shell to do some
+   unwind protection.  For non-interactive shells, we only call
+   this when a trap is defined for EXIT (0). */
+void
+initialize_terminating_signals ()
+{
+  register int i;
+#if defined (HAVE_POSIX_SIGNALS)
+  struct sigaction act, oact;
+#endif
+
+  if (termsigs_initialized)
+    return;
+
+  /* The following code is to avoid an expensive call to
+     set_signal_handler () for each terminating_signals.  Fortunately,
+     this is possible in Posix.  Unfortunately, we have to call signal ()
+     on non-Posix systems for each signal in terminating_signals. */
+#if defined (HAVE_POSIX_SIGNALS)
+  act.sa_handler = termination_unwind_protect;
+  act.sa_flags = 0;
+  sigemptyset (&act.sa_mask);
+  sigemptyset (&oact.sa_mask);
+  for (i = 0; i < TERMSIGS_LENGTH; i++)
+    sigaddset (&act.sa_mask, XSIG (i));
+  for (i = 0; i < TERMSIGS_LENGTH; i++)
+    {
+      /* If we've already trapped it, don't do anything. */
+      if (signal_is_trapped (XSIG (i)))
+       continue;
+
+      sigaction (XSIG (i), &act, &oact);
+      XHANDLER(i) = oact.sa_handler;
+      /* Don't do anything with signals that are ignored at shell entry
+        if the shell is not interactive. */
+      if (!interactive_shell && XHANDLER (i) == SIG_IGN)
+       {
+         sigaction (XSIG (i), &oact, &act);
+         set_signal_ignored (XSIG (i));
+       }
+#if defined (SIGPROF) && !defined (_MINIX)
+      if (XSIG (i) == SIGPROF && XHANDLER (i) != SIG_DFL && XHANDLER (i) != SIG_IGN)
+       sigaction (XSIG (i), &oact, (struct sigaction *)NULL);
+#endif /* SIGPROF && !_MINIX */
+    }
+
+#else /* !HAVE_POSIX_SIGNALS */
+
+  for (i = 0; i < TERMSIGS_LENGTH; i++)
+    {
+      /* If we've already trapped it, don't do anything. */
+      if (signal_is_trapped (XSIG (i)))
+       continue;
+
+      XHANDLER(i) = signal (XSIG (i), termination_unwind_protect);
+      /* Don't do anything with signals that are ignored at shell entry
+        if the shell is not interactive. */
+      if (!interactive_shell && XHANDLER (i) == SIG_IGN)
+       {
+         signal (XSIG (i), SIG_IGN);
+         set_signal_ignored (XSIG (i));
+       }
+#ifdef SIGPROF
+      if (XSIG (i) == SIGPROF && XHANDLER (i) != SIG_DFL && XHANDLER (i) != SIG_IGN)
+       signal (XSIG (i), XHANDLER (i));
+#endif
+    }
+
+#endif /* !HAVE_POSIX_SIGNALS */
+
+  termsigs_initialized = 1;
+}
+
+static void
+initialize_shell_signals ()
+{
+  if (interactive)
+    initialize_terminating_signals ();
+
+#if defined (JOB_CONTROL) || defined (HAVE_POSIX_SIGNALS)
+  /* All shells use the signal mask they inherit, and pass it along
+     to child processes.  Children will never block SIGCHLD, though. */
+  sigemptyset (&top_level_mask);
+  sigprocmask (SIG_BLOCK, (sigset_t *)NULL, &top_level_mask);
+#  if defined (SIGCHLD)
+  sigdelset (&top_level_mask, SIGCHLD);
+#  endif
+#endif /* JOB_CONTROL || HAVE_POSIX_SIGNALS */
+
+  /* And, some signals that are specifically ignored by the shell. */
+  set_signal_handler (SIGQUIT, SIG_IGN);
+
+  if (interactive)
+    {
+      set_signal_handler (SIGINT, sigint_sighandler);
+      set_signal_handler (SIGTERM, SIG_IGN);
+    }
+}
+
+void
+reset_terminating_signals ()
+{
+  register int i;
+#if defined (HAVE_POSIX_SIGNALS)
+  struct sigaction act;
+#endif
+
+  if (termsigs_initialized == 0)
+    return;
+
+#if defined (HAVE_POSIX_SIGNALS)
+  act.sa_flags = 0;
+  sigemptyset (&act.sa_mask);
+  for (i = 0; i < TERMSIGS_LENGTH; i++)
+    {
+      /* Skip a signal if it's trapped or handled specially, because the
+        trap code will restore the correct value. */
+      if (signal_is_trapped (XSIG (i)) || signal_is_special (XSIG (i)))
+       continue;
+
+      act.sa_handler = XHANDLER (i);
+      sigaction (XSIG (i), &act, (struct sigaction *) NULL);
+    }
+#else /* !HAVE_POSIX_SIGNALS */
+  for (i = 0; i < TERMSIGS_LENGTH; i++)
+    {
+      if (signal_is_trapped (XSIG (i)) || signal_is_special (XSIG (i)))
+       continue;
+
+      signal (XSIG (i), XHANDLER (i));
+    }
+#endif /* !HAVE_POSIX_SIGNALS */
+}
+#undef XSIG
+#undef XHANDLER
+
+/* What to do when we've been interrupted, and it is safe to handle it. */
+void
+throw_to_top_level ()
+{
+  int print_newline = 0;
+
+  if (interrupt_state)
+    {
+      print_newline = 1;
+      DELINTERRUPT;
+    }
+
+  if (interrupt_state)
+    return;
+
+  last_command_exit_signal = (last_command_exit_value > 128) ?
+                               (last_command_exit_value - 128) : 0;
+  last_command_exit_value |= 128;
+
+  /* Run any traps set on SIGINT. */
+  run_interrupt_trap ();
+
+  /* Cleanup string parser environment. */
+  while (parse_and_execute_level)
+    parse_and_execute_cleanup ();
+
+#if defined (JOB_CONTROL)
+  give_terminal_to (shell_pgrp, 0);
+#endif /* JOB_CONTROL */
+
+#if defined (JOB_CONTROL) || defined (HAVE_POSIX_SIGNALS)
+  /* This should not be necessary on systems using sigsetjmp/siglongjmp. */
+  sigprocmask (SIG_SETMASK, &top_level_mask, (sigset_t *)NULL);
+#endif
+
+  reset_parser ();
+
+#if defined (READLINE)
+  if (interactive)
+    bashline_reinitialize ();
+#endif /* READLINE */
+
+#if defined (PROCESS_SUBSTITUTION)
+  unlink_fifo_list ();
+#endif /* PROCESS_SUBSTITUTION */
+
+  run_unwind_protects ();
+  loop_level = continuing = breaking = 0;
+  return_catch_flag = 0;
+
+  if (interactive && print_newline)
+    {
+      fflush (stdout);
+      fprintf (stderr, "\n");
+      fflush (stderr);
+    }
+
+  /* An interrupted `wait' command in a script does not exit the script. */
+  if (interactive || (interactive_shell && !shell_initialized) ||
+      (print_newline && signal_is_trapped (SIGINT)))
+    jump_to_top_level (DISCARD);
+  else
+    jump_to_top_level (EXITPROG);
+}
+
+/* This is just here to isolate the longjmp calls. */
+void
+jump_to_top_level (value)
+     int value;
+{
+  longjmp (top_level, value);
+}
+
+sighandler
+termination_unwind_protect (sig)
+     int sig;
+{
+  if (sig == SIGINT && signal_is_trapped (SIGINT))
+    run_interrupt_trap ();
+
+#if defined (HISTORY)
+  if (interactive_shell && sig != SIGABRT)
+    maybe_save_shell_history ();
+#endif /* HISTORY */
+
+#if defined (JOB_CONTROL)
+  if (interactive && sig == SIGHUP)
+    hangup_all_jobs ();
+  end_job_control ();
+#endif /* JOB_CONTROL */
+
+#if defined (PROCESS_SUBSTITUTION)
+  unlink_fifo_list ();
+#endif /* PROCESS_SUBSTITUTION */
+
+  run_exit_trap ();
+  set_signal_handler (sig, SIG_DFL);
+  kill (getpid (), sig);
+
+  SIGRETURN (0);
+}
+
+/* What we really do when SIGINT occurs. */
+sighandler
+sigint_sighandler (sig)
+     int sig;
+{
+#if defined (MUST_REINSTALL_SIGHANDLERS)
+  signal (sig, sigint_sighandler);
+#endif
+
+  /* interrupt_state needs to be set for the stack of interrupts to work
+     right.  Should it be set unconditionally? */
+  if (interrupt_state == 0)
+    ADDINTERRUPT;
+
+  if (interrupt_immediately)
+    {
+      interrupt_immediately = 0;
+      throw_to_top_level ();
+    }
+
+  SIGRETURN (0);
+}
+
+/* Signal functions used by the rest of the code. */
+#if !defined (HAVE_POSIX_SIGNALS)
+
+#if defined (JOB_CONTROL)
+/* Perform OPERATION on NEWSET, perhaps leaving information in OLDSET. */
+sigprocmask (operation, newset, oldset)
+     int operation, *newset, *oldset;
+{
+  int old, new;
+
+  if (newset)
+    new = *newset;
+  else
+    new = 0;
+
+  switch (operation)
+    {
+    case SIG_BLOCK:
+      old = sigblock (new);
+      break;
+
+    case SIG_SETMASK:
+      sigsetmask (new);
+      break;
+
+    default:
+      internal_error (_("sigprocmask: %d: invalid operation"), operation);
+    }
+
+  if (oldset)
+    *oldset = old;
+}
+#endif /* JOB_CONTROL */
+
+#else
+
+#if !defined (SA_INTERRUPT)
+#  define SA_INTERRUPT 0
+#endif
+
+#if !defined (SA_RESTART)
+#  define SA_RESTART 0
+#endif
+
+SigHandler *
+set_signal_handler (sig, handler)
+     int sig;
+     SigHandler *handler;
+{
+  struct sigaction act, oact;
+
+  act.sa_handler = handler;
+  act.sa_flags = 0;
+#if 0
+  if (sig == SIGALRM)
+    act.sa_flags |= SA_INTERRUPT;      /* XXX */
+  else
+    act.sa_flags |= SA_RESTART;                /* XXX */
+#endif
+  sigemptyset (&act.sa_mask);
+  sigemptyset (&oact.sa_mask);
+  sigaction (sig, &act, &oact);
+  return (oact.sa_handler);
+}
+#endif /* HAVE_POSIX_SIGNALS */
diff --git a/subst.c b/subst.c
index fcc024bf2f1048b18183aa86666a43b2ff58e370..4fde3b841e193e6a9da4bf9291e6c118a1323814 100644 (file)
--- a/subst.c
+++ b/subst.c
@@ -4691,6 +4691,26 @@ valid_length_expression (name)
          legal_identifier (name + 1));                         /* ${#PS1} */
 }
 
+#if defined (HANDLE_MULTIBYTE)
+size_t
+mbstrlen (s)
+     const char *s;
+{
+  size_t clen, nc;
+  mbstate_t mbs;
+
+  nc = 0;
+  memset (&mbs, 0, sizeof (mbs));
+  while ((clen = mbrlen(s, MB_CUR_MAX, &mbs)) != 0 && (MB_INVALIDCH(clen) == 0))
+    {
+      s += clen;
+      nc++;
+    }
+  return nc;
+}
+#endif
+      
+
 /* Handle the parameter brace expansion that requires us to return the
    length of a parameter. */
 static intmax_t
@@ -4746,14 +4766,14 @@ parameter_brace_expand_length (name)
       if (legal_number (name + 1, &arg_index))         /* ${#1} */
        {
          t = get_dollar_var_value (arg_index);
-         number = STRLEN (t);
+         number = MB_STRLEN (t);
          FREE (t);
        }
 #if defined (ARRAY_VARS)
       else if ((var = find_variable (name + 1)) && array_p (var))
        {
          t = array_reference (array_cell (var), 0);
-         number = STRLEN (t);
+         number = MB_STRLEN (t);
        }
 #endif
       else                             /* ${#PS1} */
@@ -4766,7 +4786,7 @@ parameter_brace_expand_length (name)
          if (list)
            dispose_words (list);
 
-         number = STRLEN (t);
+         number = MB_STRLEN (t);
          FREE (t);
        }
     }
@@ -4871,7 +4891,7 @@ verify_substring_values (value, substr, vtype, e1p, e2p)
     {
     case VT_VARIABLE:
     case VT_ARRAYMEMBER:
-      len = strlen (value);
+      len = MB_STRLEN (value);
       break;
     case VT_POSPARMS:
       len = number_of_args () + 1;
diff --git a/subst.c~ b/subst.c~
new file mode 100644 (file)
index 0000000..02bdc57
--- /dev/null
+++ b/subst.c~
@@ -0,0 +1,7410 @@
+/* subst.c -- The part of the shell that does parameter, command, and
+   globbing substitutions. */
+
+/* ``Have a little faith, there's magic in the night.  You ain't a
+     beauty, but, hey, you're alright.'' */
+
+/* Copyright (C) 1987-2004 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 2, 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, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+
+#include "config.h"
+
+#include "bashtypes.h"
+#include <stdio.h>
+#include "chartypes.h"
+#include <pwd.h>
+#include <signal.h>
+#include <errno.h>
+
+#if defined (HAVE_UNISTD_H)
+#  include <unistd.h>
+#endif
+
+#include "bashansi.h"
+#include "posixstat.h"
+#include "bashintl.h"
+
+#include "shell.h"
+#include "flags.h"
+#include "jobs.h"
+#include "execute_cmd.h"
+#include "filecntl.h"
+#include "trap.h"
+#include "pathexp.h"
+#include "mailcheck.h"
+
+#include "shmbutil.h"
+
+#include "builtins/getopt.h"
+#include "builtins/common.h"
+
+#include <tilde/tilde.h>
+#include <glob/strmatch.h>
+
+#if !defined (errno)
+extern int errno;
+#endif /* !errno */
+
+/* The size that strings change by. */
+#define DEFAULT_INITIAL_ARRAY_SIZE 112
+#define DEFAULT_ARRAY_SIZE 128
+
+/* Variable types. */
+#define VT_VARIABLE    0
+#define VT_POSPARMS    1
+#define VT_ARRAYVAR    2
+#define VT_ARRAYMEMBER 3
+
+#define VT_STARSUB     128     /* $* or ${array[*]} -- used to split */
+
+/* Flags for quoted_strchr */
+#define ST_BACKSL      0x01
+#define ST_CTLESC      0x02
+#define ST_SQUOTE      0x04    /* unused yet */
+#define ST_DQUOTE      0x08    /* unused yet */
+
+/* Flags for the string extraction functions. */
+#define EX_NOALLOC     0x01    /* just skip; don't return substring */
+#define EX_VARNAME     0x02    /* variable name; for string_extract () */
+
+/* Flags for the `pflags' argument to param_expand() */
+#define PF_NOCOMSUB    0x01    /* Do not perform command substitution */
+
+/* These defs make it easier to use the editor. */
+#define LBRACE         '{'
+#define RBRACE         '}'
+#define LPAREN         '('
+#define RPAREN         ')'
+
+/* Evaluates to 1 if C is one of the shell's special parameters whose length
+   can be taken, but is also one of the special expansion characters. */
+#define VALID_SPECIAL_LENGTH_PARAM(c) \
+  ((c) == '-' || (c) == '?' || (c) == '#')
+
+/* Evaluates to 1 if C is one of the shell's special parameters for which an
+   indirect variable reference may be made. */
+#define VALID_INDIR_PARAM(c) \
+  ((c) == '#' || (c) == '?' || (c) == '@' || (c) == '*')
+
+/* Evaluates to 1 if C is one of the OP characters that follows the parameter
+   in ${parameter[:]OPword}. */
+#define VALID_PARAM_EXPAND_CHAR(c) (sh_syntaxtab[(unsigned char)c] & CSUBSTOP)
+
+/* Evaluates to 1 if this is one of the shell's special variables. */
+#define SPECIAL_VAR(name, wi) \
+ ((DIGIT (*name) && all_digits (name)) || \
+      (name[1] == '\0' && (sh_syntaxtab[(unsigned char)*name] & CSPECVAR)) || \
+      (wi && name[2] == '\0' && VALID_INDIR_PARAM (name[1])))
+
+/* An expansion function that takes a string and a quoted flag and returns
+   a WORD_LIST *.  Used as the type of the third argument to
+   expand_string_if_necessary(). */
+typedef WORD_LIST *EXPFUNC __P((char *, int));
+
+/* Process ID of the last command executed within command substitution. */
+pid_t last_command_subst_pid = NO_PID;
+pid_t current_command_subst_pid = NO_PID;
+
+/* Variables used to keep track of the characters in IFS. */
+SHELL_VAR *ifs_var;
+char *ifs_value;
+unsigned char ifs_cmap[UCHAR_MAX + 1];
+unsigned char ifs_firstc;
+
+/* Extern functions and variables from different files. */
+extern int last_command_exit_value, last_command_exit_signal;
+extern int subshell_environment;
+extern int subshell_level;
+extern int eof_encountered;
+extern int return_catch_flag, return_catch_value;
+extern pid_t dollar_dollar_pid;
+extern int posixly_correct;
+extern char *this_command_name;
+extern struct fd_bitmap *current_fds_to_close;
+extern int wordexp_only;
+extern int expanding_redir;
+extern int tempenv_assign_error;
+
+/* Non-zero means to allow unmatched globbed filenames to expand to
+   a null file. */
+int allow_null_glob_expansion;
+
+/* Non-zero means to throw an error when globbing fails to match anything. */
+int fail_glob_expansion;
+
+#if 0
+/* Variables to keep track of which words in an expanded word list (the
+   output of expand_word_list_internal) are the result of globbing
+   expansions.  GLOB_ARGV_FLAGS is used by execute_cmd.c.
+   (CURRENTLY UNUSED). */
+char *glob_argv_flags;
+static int glob_argv_flags_size;
+#endif
+
+static WORD_LIST expand_word_error, expand_word_fatal;
+static char expand_param_error, expand_param_fatal;
+
+/* Tell the expansion functions to not longjmp back to top_level on fatal
+   errors.  Enabled when doing completion and prompt string expansion. */
+static int no_longjmp_on_fatal_error = 0;
+
+/* Set by expand_word_unsplit; used to inhibit splitting and re-joining
+   $* on $IFS, primarily when doing assignment statements. */
+static int expand_no_split_dollar_star = 0;
+
+/* Used to hold a list of variable assignments preceding a command.  Global
+   so the SIGCHLD handler in jobs.c can unwind-protect it when it runs a
+   SIGCHLD trap. */
+WORD_LIST *subst_assign_varlist = (WORD_LIST *)NULL;
+
+/* A WORD_LIST of words to be expanded by expand_word_list_internal,
+   without any leading variable assignments. */
+static WORD_LIST *garglist = (WORD_LIST *)NULL;
+
+static char *quoted_substring __P((char *, int, int));
+static int quoted_strlen __P((char *));
+static char *quoted_strchr __P((char *, int, int));
+
+static char *expand_string_if_necessary __P((char *, int, EXPFUNC *));
+static inline char *expand_string_to_string_internal __P((char *, int, EXPFUNC *));
+static WORD_LIST *call_expand_word_internal __P((WORD_DESC *, int, int, int *, int *));
+static WORD_LIST *expand_string_internal __P((char *, int));
+static WORD_LIST *expand_string_leave_quoted __P((char *, int));
+static WORD_LIST *expand_string_for_rhs __P((char *, int, int *, int *));
+
+static WORD_LIST *list_quote_escapes __P((WORD_LIST *));
+static char *dequote_escapes __P((char *));
+static char *make_quoted_char __P((int));
+static WORD_LIST *quote_list __P((WORD_LIST *));
+static WORD_LIST *dequote_list __P((WORD_LIST *));
+static char *remove_quoted_escapes __P((char *));
+static char *remove_quoted_nulls __P((char *));
+
+static int unquoted_substring __P((char *, char *));
+static int unquoted_member __P((int, char *));
+
+static int do_assignment_internal __P((const char *, int));
+
+static char *string_extract_verbatim __P((char *, int *, char *));
+static char *string_extract __P((char *, int *, char *, int));
+static char *string_extract_double_quoted __P((char *, int *, int));
+static inline char *string_extract_single_quoted __P((char *, int *));
+static inline int skip_single_quoted __P((char *, size_t, int));
+static int skip_double_quoted __P((char *, size_t, int));
+static char *extract_delimited_string __P((char *, int *, char *, char *, char *, int));
+static char *extract_dollar_brace_string __P((char *, int *, int, int));
+
+static char *pos_params __P((char *, int, int, int));
+
+static unsigned char *mb_getcharlens __P((char *, int));
+
+static char *remove_upattern __P((char *, char *, int));
+#if defined (HANDLE_MULTIBYTE)
+#  if !defined (HAVE_WCSDUP)
+static wchar_t *wcsdup __P((wchar_t *));
+#  endif
+static wchar_t *remove_wpattern __P((wchar_t *, size_t, wchar_t *, int));
+#endif
+static char *remove_pattern __P((char *, char *, int));
+
+static int match_pattern_char __P((char *, char *));
+static int match_upattern __P((char *, char *, int, char **, char **));
+#if defined (HANDLE_MULTIBYTE)
+static int match_pattern_wchar __P((wchar_t *, wchar_t *));
+static int match_wpattern __P((wchar_t *, char **, size_t, wchar_t *, int, char **, char **));
+#endif
+static int match_pattern __P((char *, char *, int, char **, char **));
+static int getpatspec __P((int, char *));
+static char *getpattern __P((char *, int, int));
+static char *variable_remove_pattern __P((char *, char *, int, int));
+static char *list_remove_pattern __P((WORD_LIST *, char *, int, int, int));
+static char *parameter_list_remove_pattern __P((int, char *, int, int));
+#ifdef ARRAY_VARS
+static char *array_remove_pattern __P((ARRAY *, char *, int, char *, int));
+#endif
+static char *parameter_brace_remove_pattern __P((char *, char *, char *, int, int));
+
+static char *process_substitute __P((char *, int));
+
+static char *read_comsub __P((int, int));
+
+#ifdef ARRAY_VARS
+static arrayind_t array_length_reference __P((char *));
+#endif
+
+static int valid_brace_expansion_word __P((char *, int));
+static int chk_atstar __P((char *, int, int *, int *));
+
+static char *parameter_brace_expand_word __P((char *, int, int));
+static char *parameter_brace_expand_indir __P((char *, int, int, int *, int *));
+static char *parameter_brace_expand_rhs __P((char *, char *, int, int, int *, int *));
+static void parameter_brace_expand_error __P((char *, char *));
+
+static int valid_length_expression __P((char *));
+static intmax_t parameter_brace_expand_length __P((char *));
+
+static char *skiparith __P((char *, int));
+static int verify_substring_values __P((char *, char *, int, intmax_t *, intmax_t *));
+static int get_var_and_type __P((char *, char *, int, SHELL_VAR **, char **));
+static char *mb_substring __P((char *, int, int));
+static char *parameter_brace_substring __P((char *, char *, char *, int));
+
+static char *pos_params_pat_subst __P((char *, char *, char *, int));
+
+static char *parameter_brace_patsub __P((char *, char *, char *, int));
+
+static char *parameter_brace_expand __P((char *, int *, int, int *, int *));
+static char *param_expand __P((char *, int *, int, int *, int *, int *, int *, int));
+
+static WORD_LIST *expand_word_internal __P((WORD_DESC *, int, int, int *, int *));
+
+static WORD_LIST *word_list_split __P((WORD_LIST *));
+
+static void exp_jump_to_top_level __P((int));
+
+static WORD_LIST *separate_out_assignments __P((WORD_LIST *));
+static WORD_LIST *glob_expand_word_list __P((WORD_LIST *, int));
+#ifdef BRACE_EXPANSION
+static WORD_LIST *brace_expand_word_list __P((WORD_LIST *, int));
+#endif
+static WORD_LIST *shell_expand_word_list __P((WORD_LIST *, int));
+static WORD_LIST *expand_word_list_internal __P((WORD_LIST *, int));
+
+/* **************************************************************** */
+/*                                                                 */
+/*                     Utility Functions                           */
+/*                                                                 */
+/* **************************************************************** */
+
+#ifdef INCLUDE_UNUSED
+static char *
+quoted_substring (string, start, end)
+     char *string;
+     int start, end;
+{
+  register int len, l;
+  register char *result, *s, *r;
+
+  len = end - start;
+
+  /* Move to string[start], skipping quoted characters. */
+  for (s = string, l = 0; *s && l < start; )
+    {
+      if (*s == CTLESC)
+       {
+         s++;
+         continue;
+       }
+      l++;
+      if (*s == 0)
+       break;
+    }
+
+  r = result = (char *)xmalloc (2*len + 1);      /* save room for quotes */
+
+  /* Copy LEN characters, including quote characters. */
+  s = string + l;
+  for (l = 0; l < len; s++)
+    {
+      if (*s == CTLESC)
+       *r++ = *s++;
+      *r++ = *s;
+      l++;
+      if (*s == 0)
+       break;
+    }
+  *r = '\0';
+  return result;
+}
+#endif
+
+#ifdef INCLUDE_UNUSED
+/* Return the length of S, skipping over quoted characters */
+static int
+quoted_strlen (s)
+     char *s;
+{
+  register char *p;
+  int i;
+
+  i = 0;
+  for (p = s; *p; p++)
+    {
+      if (*p == CTLESC)
+       {
+         p++;
+         if (*p == 0)
+           return (i + 1);
+       }
+      i++;
+    }
+
+  return i;
+}
+#endif
+
+/* Find the first occurrence of character C in string S, obeying shell
+   quoting rules.  If (FLAGS & ST_BACKSL) is non-zero, backslash-escaped
+   characters are skipped.  If (FLAGS & ST_CTLESC) is non-zero, characters
+   escaped with CTLESC are skipped. */
+static char *
+quoted_strchr (s, c, flags)
+     char *s;
+     int c, flags;
+{
+  register char *p;
+
+  for (p = s; *p; p++)
+    {
+      if (((flags & ST_BACKSL) && *p == '\\')
+           || ((flags & ST_CTLESC) && *p == CTLESC))
+       {
+         p++;
+         if (*p == '\0')
+           return ((char *)NULL);
+         continue;
+       }
+      else if (*p == c)
+       return p;
+    }
+  return ((char *)NULL);
+}
+
+/* Return 1 if CHARACTER appears in an unquoted portion of
+   STRING.  Return 0 otherwise.  CHARACTER must be a single-byte character. */
+static int
+unquoted_member (character, string)
+     int character;
+     char *string;
+{
+  size_t slen;
+  int sindex, c;
+  DECLARE_MBSTATE;
+
+  slen = strlen (string);
+  sindex = 0;
+  while (c = string[sindex])
+    {
+      if (c == character)
+       return (1);
+
+      switch (c)
+       {
+       default:
+         ADVANCE_CHAR (string, slen, sindex);
+         break;
+
+       case '\\':
+         sindex++;
+         if (string[sindex])
+           ADVANCE_CHAR (string, slen, sindex);
+         break;
+
+       case '\'':
+         sindex = skip_single_quoted (string, slen, ++sindex);
+         break;
+
+       case '"':
+         sindex = skip_double_quoted (string, slen, ++sindex);
+         break;
+       }
+    }
+  return (0);
+}
+
+/* Return 1 if SUBSTR appears in an unquoted portion of STRING. */
+static int
+unquoted_substring (substr, string)
+     char *substr, *string;
+{
+  size_t slen;
+  int sindex, c, sublen;
+  DECLARE_MBSTATE;
+
+  if (substr == 0 || *substr == '\0')
+    return (0);
+
+  slen = strlen (string);
+  sublen = strlen (substr);
+  for (sindex = 0; c = string[sindex]; )
+    {
+      if (STREQN (string + sindex, substr, sublen))
+       return (1);
+
+      switch (c)
+       {
+       case '\\':
+         sindex++;
+
+         if (string[sindex])
+           ADVANCE_CHAR (string, slen, sindex);
+         break;
+
+       case '\'':
+         sindex = skip_single_quoted (string, slen, ++sindex);
+         break;
+
+       case '"':
+         sindex = skip_double_quoted (string, slen, ++sindex);
+         break;
+
+       default:
+         ADVANCE_CHAR (string, slen, sindex);
+         break;
+       }
+    }
+  return (0);
+}
+
+/* Most of the substitutions must be done in parallel.  In order
+   to avoid using tons of unclear goto's, I have some functions
+   for manipulating malloc'ed strings.  They all take INDX, a
+   pointer to an integer which is the offset into the string
+   where manipulation is taking place.  They also take SIZE, a
+   pointer to an integer which is the current length of the
+   character array for this string. */
+
+/* Append SOURCE to TARGET at INDEX.  SIZE is the current amount
+   of space allocated to TARGET.  SOURCE can be NULL, in which
+   case nothing happens.  Gets rid of SOURCE by freeing it.
+   Returns TARGET in case the location has changed. */
+INLINE char *
+sub_append_string (source, target, indx, size)
+     char *source, *target;
+     int *indx, *size;
+{
+  if (source)
+    {
+      int srclen, n;
+
+      srclen = STRLEN (source);
+      if (srclen >= (int)(*size - *indx))
+       {
+         n = srclen + *indx;
+         n = (n + DEFAULT_ARRAY_SIZE) - (n % DEFAULT_ARRAY_SIZE);
+         target = (char *)xrealloc (target, (*size = n));
+       }
+
+      FASTCOPY (source, target + *indx, srclen);
+      *indx += srclen;
+      target[*indx] = '\0';
+
+      free (source);
+    }
+  return (target);
+}
+
+#if 0
+/* UNUSED */
+/* Append the textual representation of NUMBER to TARGET.
+   INDX and SIZE are as in SUB_APPEND_STRING. */
+char *
+sub_append_number (number, target, indx, size)
+     intmax_t number;
+     int *indx, *size;
+     char *target;
+{
+  char *temp;
+
+  temp = itos (number);
+  return (sub_append_string (temp, target, indx, size));
+}
+#endif
+
+/* Extract a substring from STRING, starting at SINDEX and ending with
+   one of the characters in CHARLIST.  Don't make the ending character
+   part of the string.  Leave SINDEX pointing at the ending character.
+   Understand about backslashes in the string.  If (flags & EX_VARNAME)
+   is non-zero, and array variables have been compiled into the shell,
+   everything between a `[' and a corresponding `]' is skipped over.
+   If (flags & EX_NOALLOC) is non-zero, don't return the substring, just
+   update SINDEX. */
+static char *
+string_extract (string, sindex, charlist, flags)
+     char *string;
+     int *sindex;
+     char *charlist;
+     int flags;
+{
+  register int c, i;
+  size_t slen;
+  char *temp;
+  DECLARE_MBSTATE;
+
+  slen = strlen (string + *sindex) + *sindex;
+  i = *sindex;
+  while (c = string[i])
+    {
+      if (c == '\\')
+       {
+         if (string[i + 1])
+           i++;
+         else
+           break;
+       }
+#if defined (ARRAY_VARS)
+      else if ((flags & EX_VARNAME) && c == '[')
+       {
+         int ni;
+         /* If this is an array subscript, skip over it and continue. */
+         ni = skipsubscript (string, i);
+         if (string[ni] == ']')
+           i = ni;
+       }
+#endif
+      else if (MEMBER (c, charlist))
+         break;
+
+      ADVANCE_CHAR (string, slen, i);
+    }
+
+  temp = (flags & EX_NOALLOC) ? (char *)NULL : substring (string, *sindex, i);
+  *sindex = i;
+  return (temp);
+}
+
+/* Extract the contents of STRING as if it is enclosed in double quotes.
+   SINDEX, when passed in, is the offset of the character immediately
+   following the opening double quote; on exit, SINDEX is left pointing after
+   the closing double quote.  If STRIPDQ is non-zero, unquoted double
+   quotes are stripped and the string is terminated by a null byte.
+   Backslashes between the embedded double quotes are processed.  If STRIPDQ
+   is zero, an unquoted `"' terminates the string. */
+static char *
+string_extract_double_quoted (string, sindex, stripdq)
+     char *string;
+     int *sindex, stripdq;
+{
+  size_t slen;
+  char *send;
+  int j, i, t;
+  unsigned char c;
+  char *temp, *ret;            /* The new string we return. */
+  int pass_next, backquote, si;        /* State variables for the machine. */
+  int dquote;
+  DECLARE_MBSTATE;
+
+  slen = strlen (string + *sindex) + *sindex;
+  send = string + slen;
+
+  pass_next = backquote = dquote = 0;
+  temp = (char *)xmalloc (1 + slen - *sindex);
+
+  j = 0;
+  i = *sindex;
+  while (c = string[i])
+    {
+      /* Process a character that was quoted by a backslash. */
+      if (pass_next)
+       {
+         /* Posix.2 sez:
+
+            ``The backslash shall retain its special meaning as an escape
+            character only when followed by one of the characters:
+               $       `       "       \       <newline>''.
+
+            If STRIPDQ is zero, we handle the double quotes here and let
+            expand_word_internal handle the rest.  If STRIPDQ is non-zero,
+            we have already been through one round of backslash stripping,
+            and want to strip these backslashes only if DQUOTE is non-zero,
+            indicating that we are inside an embedded double-quoted string. */
+
+            /* If we are in an embedded quoted string, then don't strip
+               backslashes before characters for which the backslash
+               retains its special meaning, but remove backslashes in
+               front of other characters.  If we are not in an
+               embedded quoted string, don't strip backslashes at all.
+               This mess is necessary because the string was already
+               surrounded by double quotes (and sh has some really weird
+               quoting rules).
+               The returned string will be run through expansion as if
+               it were double-quoted. */
+         if ((stripdq == 0 && c != '"') ||
+             (stripdq && ((dquote && (sh_syntaxtab[c] & CBSDQUOTE)) || dquote == 0)))
+           temp[j++] = '\\';
+         pass_next = 0;
+
+add_one_character:
+         COPY_CHAR_I (temp, j, string, send, i);
+         continue;
+       }
+
+      /* A backslash protects the next character.  The code just above
+        handles preserving the backslash in front of any character but
+        a double quote. */
+      if (c == '\\')
+       {
+         pass_next++;
+         i++;
+         continue;
+       }
+
+      /* Inside backquotes, ``the portion of the quoted string from the
+        initial backquote and the characters up to the next backquote
+        that is not preceded by a backslash, having escape characters
+        removed, defines that command''. */
+      if (backquote)
+       {
+         if (c == '`')
+           backquote = 0;
+         temp[j++] = c;
+         i++;
+         continue;
+       }
+
+      if (c == '`')
+       {
+         temp[j++] = c;
+         backquote++;
+         i++;
+         continue;
+       }
+
+      /* Pass everything between `$(' and the matching `)' or a quoted
+        ${ ... } pair through according to the Posix.2 specification. */
+      if (c == '$' && ((string[i + 1] == LPAREN) || (string[i + 1] == LBRACE)))
+       {
+         int free_ret = 1;
+
+         si = i + 2;
+         if (string[i + 1] == LPAREN)
+           ret = extract_delimited_string (string, &si, "$(", "(", ")", 0); /*)*/
+         else
+           ret = extract_dollar_brace_string (string, &si, 1, 0);
+
+         temp[j++] = '$';
+         temp[j++] = string[i + 1];
+
+         /* Just paranoia; ret will not be 0 unless no_longjmp_on_fatal_error
+            is set. */
+         if (ret == 0 && no_longjmp_on_fatal_error)
+           {
+             free_ret = 0;
+             ret = string + i + 2;
+           }
+
+         for (t = 0; ret[t]; t++, j++)
+           temp[j] = ret[t];
+         temp[j] = string[si];
+
+         if (string[si])
+           {
+             j++;
+             i = si + 1;
+           }
+         else
+           i = si;
+
+         if (free_ret)
+           free (ret);
+         continue;
+       }
+
+      /* Add any character but a double quote to the quoted string we're
+        accumulating. */
+      if (c != '"')
+       goto add_one_character;
+
+      /* c == '"' */
+      if (stripdq)
+       {
+         dquote ^= 1;
+         i++;
+         continue;
+       }
+
+      break;
+    }
+  temp[j] = '\0';
+
+  /* Point to after the closing quote. */
+  if (c)
+    i++;
+  *sindex = i;
+
+  return (temp);
+}
+
+/* This should really be another option to string_extract_double_quoted. */
+static int
+skip_double_quoted (string, slen, sind)
+     char *string;
+     size_t slen;
+     int sind;
+{
+  int c, i;
+  char *ret;
+  int pass_next, backquote, si;
+  DECLARE_MBSTATE;
+
+  pass_next = backquote = 0;
+  i = sind;
+  while (c = string[i])
+    {
+      if (pass_next)
+       {
+         pass_next = 0;
+         ADVANCE_CHAR (string, slen, i);
+         continue;
+       }
+      else if (c == '\\')
+       {
+         pass_next++;
+         i++;
+         continue;
+       }
+      else if (backquote)
+       {
+         if (c == '`')
+           backquote = 0;
+         ADVANCE_CHAR (string, slen, i);
+         continue;
+       }
+      else if (c == '`')
+       {
+         backquote++;
+         i++;
+         continue;
+       }
+      else if (c == '$' && ((string[i + 1] == LPAREN) || (string[i + 1] == LBRACE)))
+       {
+         si = i + 2;
+         if (string[i + 1] == LPAREN)
+           ret = extract_delimited_string (string, &si, "$(", "(", ")", EX_NOALLOC);
+         else
+           ret = extract_dollar_brace_string (string, &si, 0, EX_NOALLOC);
+
+         i = si + 1;
+         continue;
+       }
+      else if (c != '"')
+       {
+         ADVANCE_CHAR (string, slen, i);
+         continue;
+       }
+      else
+       break;
+    }
+
+  if (c)
+    i++;
+
+  return (i);
+}
+
+/* Extract the contents of STRING as if it is enclosed in single quotes.
+   SINDEX, when passed in, is the offset of the character immediately
+   following the opening single quote; on exit, SINDEX is left pointing after
+   the closing single quote. */
+static inline char *
+string_extract_single_quoted (string, sindex)
+     char *string;
+     int *sindex;
+{
+  register int i;
+  size_t slen;
+  char *t;
+  DECLARE_MBSTATE;
+
+  slen = strlen (string + *sindex) + *sindex;
+  i = *sindex;
+  while (string[i] && string[i] != '\'')
+    ADVANCE_CHAR (string, slen, i);
+
+  t = substring (string, *sindex, i);
+
+  if (string[i])
+    i++;
+  *sindex = i;
+
+  return (t);
+}
+
+static inline int
+skip_single_quoted (string, slen, sind)
+     char *string;
+     size_t slen;
+     int sind;
+{
+  register int c;
+  DECLARE_MBSTATE;
+
+  c = sind;
+  while (string[c] && string[c] != '\'')
+    ADVANCE_CHAR (string, slen, c);
+
+  if (string[c])
+    c++;
+  return c;
+}
+
+/* Just like string_extract, but doesn't hack backslashes or any of
+   that other stuff.  Obeys CTLESC quoting.  Used to do splitting on $IFS. */
+static char *
+string_extract_verbatim (string, sindex, charlist)
+     char *string;
+     int *sindex;
+     char *charlist;
+{
+  register int i = *sindex;
+  int c;
+  char *temp;
+
+  if (charlist[0] == '\'' && charlist[1] == '\0')
+    {
+      temp = string_extract_single_quoted (string, sindex);
+      --*sindex;       /* leave *sindex at separator character */
+      return temp;
+    }
+
+  for (i = *sindex; c = string[i]; i++)
+    {
+      if (c == CTLESC)
+       {
+         i++;
+         continue;
+       }
+
+      if (MEMBER (c, charlist))
+       break;
+    }
+
+  temp = substring (string, *sindex, i);
+  *sindex = i;
+
+  return (temp);
+}
+
+/* Extract the $( construct in STRING, and return a new string.
+   Start extracting at (SINDEX) as if we had just seen "$(".
+   Make (SINDEX) get the position of the matching ")". */
+char *
+extract_command_subst (string, sindex)
+     char *string;
+     int *sindex;
+{
+  return (extract_delimited_string (string, sindex, "$(", "(", ")", 0));
+}
+
+/* Extract the $[ construct in STRING, and return a new string. (])
+   Start extracting at (SINDEX) as if we had just seen "$[".
+   Make (SINDEX) get the position of the matching "]". */
+char *
+extract_arithmetic_subst (string, sindex)
+     char *string;
+     int *sindex;
+{
+  return (extract_delimited_string (string, sindex, "$[", "[", "]", 0)); /*]*/
+}
+
+#if defined (PROCESS_SUBSTITUTION)
+/* Extract the <( or >( construct in STRING, and return a new string.
+   Start extracting at (SINDEX) as if we had just seen "<(".
+   Make (SINDEX) get the position of the matching ")". */ /*))*/
+char *
+extract_process_subst (string, starter, sindex)
+     char *string;
+     char *starter;
+     int *sindex;
+{
+  return (extract_delimited_string (string, sindex, starter, "(", ")", 0));
+}
+#endif /* PROCESS_SUBSTITUTION */
+
+#if defined (ARRAY_VARS)
+char *
+extract_array_assignment_list (string, sindex)
+     char *string;
+     int *sindex;
+{
+  return (extract_delimited_string (string, sindex, "(", (char *)NULL, ")", 0));
+}
+#endif
+
+/* Extract and create a new string from the contents of STRING, a
+   character string delimited with OPENER and CLOSER.  SINDEX is
+   the address of an int describing the current offset in STRING;
+   it should point to just after the first OPENER found.  On exit,
+   SINDEX gets the position of the last character of the matching CLOSER.
+   If OPENER is more than a single character, ALT_OPENER, if non-null,
+   contains a character string that can also match CLOSER and thus
+   needs to be skipped. */
+static char *
+extract_delimited_string (string, sindex, opener, alt_opener, closer, flags)
+     char *string;
+     int *sindex;
+     char *opener, *alt_opener, *closer;
+     int flags;
+{
+  int i, c, si;
+  size_t slen;
+  char *t, *result;
+  int pass_character, nesting_level;
+  int len_closer, len_opener, len_alt_opener;
+  DECLARE_MBSTATE;
+
+  slen = strlen (string + *sindex) + *sindex;
+  len_opener = STRLEN (opener);
+  len_alt_opener = STRLEN (alt_opener);
+  len_closer = STRLEN (closer);
+
+  pass_character = 0;
+
+  nesting_level = 1;
+  i = *sindex;
+
+  while (nesting_level)
+    {
+      c = string[i];
+
+      if (c == 0)
+       break;
+
+      if (pass_character)      /* previous char was backslash */
+       {
+         pass_character = 0;
+         ADVANCE_CHAR (string, slen, i);
+         continue;
+       }
+
+      if (c == CTLESC || c == '\\')
+       {
+         pass_character++;
+         i++;
+         continue;
+       }
+
+      /* Process a nested OPENER. */
+      if (STREQN (string + i, opener, len_opener))
+       {
+         si = i + len_opener;
+         t = extract_delimited_string (string, &si, opener, alt_opener, closer, flags|EX_NOALLOC);
+         i = si + 1;
+         continue;
+       }
+
+      /* Process a nested ALT_OPENER */
+      if (len_alt_opener && STREQN (string + i, alt_opener, len_alt_opener))
+       {
+         si = i + len_alt_opener;
+         t = extract_delimited_string (string, &si, alt_opener, alt_opener, closer, flags|EX_NOALLOC);
+         i = si + 1;
+         continue;
+       }
+
+      /* If the current substring terminates the delimited string, decrement
+        the nesting level. */
+      if (STREQN (string + i, closer, len_closer))
+       {
+         i += len_closer - 1;  /* move to last byte of the closer */
+         nesting_level--;
+         if (nesting_level == 0)
+           break;
+       }
+
+      /* Pass old-style command substitution through verbatim. */
+      if (c == '`')
+       {
+         si = i + 1;
+         t = string_extract (string, &si, "`", flags|EX_NOALLOC);
+         i = si + 1;
+         continue;
+       }
+
+      /* Pass single-quoted and double-quoted strings through verbatim. */
+      if (c == '\'' || c == '"')
+       {
+         si = i + 1;
+         i = (c == '\'') ? skip_single_quoted (string, slen, si)
+                         : skip_double_quoted (string, slen, si);
+         continue;
+       }
+
+      /* move past this character, which was not special. */
+      ADVANCE_CHAR (string, slen, i);
+    }
+
+  if (c == 0 && nesting_level)
+    {
+      if (no_longjmp_on_fatal_error == 0)
+       {
+         report_error (_("bad substitution: no closing `%s' in %s"), closer, string);
+         last_command_exit_value = EXECUTION_FAILURE;
+         exp_jump_to_top_level (DISCARD);
+       }
+      else
+       {
+         *sindex = i;
+         return (char *)NULL;
+       }
+    }
+
+  si = i - *sindex - len_closer + 1;
+  if (flags & EX_NOALLOC)
+    result = (char *)NULL;
+  else    
+    {
+      result = (char *)xmalloc (1 + si);
+      strncpy (result, string + *sindex, si);
+      result[si] = '\0';
+    }
+  *sindex = i;
+
+  return (result);
+}
+
+/* Extract a parameter expansion expression within ${ and } from STRING.
+   Obey the Posix.2 rules for finding the ending `}': count braces while
+   skipping over enclosed quoted strings and command substitutions.
+   SINDEX is the address of an int describing the current offset in STRING;
+   it should point to just after the first `{' found.  On exit, SINDEX
+   gets the position of the matching `}'.  QUOTED is non-zero if this
+   occurs inside double quotes. */
+/* XXX -- this is very similar to extract_delimited_string -- XXX */
+static char *
+extract_dollar_brace_string (string, sindex, quoted, flags)
+     char *string;
+     int *sindex, quoted, flags;
+{
+  register int i, c;
+  size_t slen;
+  int pass_character, nesting_level, si;
+  char *result, *t;
+  DECLARE_MBSTATE;
+
+  pass_character = 0;
+  nesting_level = 1;
+  slen = strlen (string + *sindex) + *sindex;
+
+  i = *sindex;
+  while (c = string[i])
+    {
+      if (pass_character)
+       {
+         pass_character = 0;
+         ADVANCE_CHAR (string, slen, i);
+         continue;
+       }
+
+      /* CTLESCs and backslashes quote the next character. */
+      if (c == CTLESC || c == '\\')
+       {
+         pass_character++;
+         i++;
+         continue;
+       }
+
+      if (string[i] == '$' && string[i+1] == LBRACE)
+       {
+         nesting_level++;
+         i += 2;
+         continue;
+       }
+
+      if (c == RBRACE)
+       {
+         nesting_level--;
+         if (nesting_level == 0)
+           break;
+         i++;
+         continue;
+       }
+
+      /* Pass the contents of old-style command substitutions through
+        verbatim. */
+      if (c == '`')
+       {
+         si = i + 1;
+         t = string_extract (string, &si, "`", flags|EX_NOALLOC);
+         i = si + 1;
+         continue;
+       }
+
+      /* Pass the contents of new-style command substitutions and
+        arithmetic substitutions through verbatim. */
+      if (string[i] == '$' && string[i+1] == LPAREN)
+       {
+         si = i + 2;
+         t = extract_delimited_string (string, &si, "$(", "(", ")", flags|EX_NOALLOC); /*)*/
+         i = si + 1;
+         continue;
+       }
+
+      /* Pass the contents of single-quoted and double-quoted strings
+        through verbatim. */
+      if (c == '\'' || c == '"')
+       {
+         si = i + 1;
+         i = (c == '\'') ? skip_single_quoted (string, slen, si)
+                         : skip_double_quoted (string, slen, si);
+         /* skip_XXX_quoted leaves index one past close quote */
+         continue;
+       }
+
+      /* move past this character, which was not special. */
+      ADVANCE_CHAR (string, slen, i);
+    }
+
+  if (c == 0 && nesting_level)
+    {
+      if (no_longjmp_on_fatal_error == 0)
+       {                       /* { */
+         report_error ("bad substitution: no closing `%s' in %s", "}", string);
+         last_command_exit_value = EXECUTION_FAILURE;
+         exp_jump_to_top_level (DISCARD);
+       }
+      else
+       {
+         *sindex = i;
+         return ((char *)NULL);
+       }
+    }
+
+  result = (flags & EX_NOALLOC) ? (char *)NULL : substring (string, *sindex, i);
+  *sindex = i;
+
+  return (result);
+}
+
+/* Remove backslashes which are quoting backquotes from STRING.  Modifies
+   STRING, and returns a pointer to it. */
+char *
+de_backslash (string)
+     char *string;
+{
+  register size_t slen;
+  register int i, j, prev_i;
+  DECLARE_MBSTATE;
+
+  slen = strlen (string);
+  i = j = 0;
+
+  /* Loop copying string[i] to string[j], i >= j. */
+  while (i < slen)
+    {
+      if (string[i] == '\\' && (string[i + 1] == '`' || string[i + 1] == '\\' ||
+                             string[i + 1] == '$'))
+       i++;
+      prev_i = i;
+      ADVANCE_CHAR (string, slen, i);
+      if (j < prev_i)
+       do string[j++] = string[prev_i++]; while (prev_i < i);
+      else
+       j = i;
+    }
+  string[j] = '\0';
+
+  return (string);
+}
+
+#if 0
+/*UNUSED*/
+/* Replace instances of \! in a string with !. */
+void
+unquote_bang (string)
+     char *string;
+{
+  register int i, j;
+  register char *temp;
+
+  temp = (char *)xmalloc (1 + strlen (string));
+
+  for (i = 0, j = 0; (temp[j] = string[i]); i++, j++)
+    {
+      if (string[i] == '\\' && string[i + 1] == '!')
+       {
+         temp[j] = '!';
+         i++;
+       }
+    }
+  strcpy (string, temp);
+  free (temp);
+}
+#endif
+
+#if defined (READLINE)
+/* Return 1 if the portion of STRING ending at EINDEX is quoted (there is
+   an unclosed quoted string), or if the character at EINDEX is quoted
+   by a backslash. NO_LONGJMP_ON_FATAL_ERROR is used to flag that the various
+   single and double-quoted string parsing functions should not return an
+   error if there are unclosed quotes or braces.  The characters that this
+   recognizes need to be the same as the contents of
+   rl_completer_quote_characters. */
+
+#define CQ_RETURN(x) do { no_longjmp_on_fatal_error = 0; return (x); } while (0)
+
+int
+char_is_quoted (string, eindex)
+     char *string;
+     int eindex;
+{
+  int i, pass_next, c;
+  size_t slen;
+  DECLARE_MBSTATE;
+
+  slen = strlen (string);
+  no_longjmp_on_fatal_error = 1;
+  i = pass_next = 0;
+  while (i <= eindex)
+    {
+      c = string[i];
+
+      if (pass_next)
+       {
+         pass_next = 0;
+         if (i >= eindex)      /* XXX was if (i >= eindex - 1) */
+           CQ_RETURN(1);
+         ADVANCE_CHAR (string, slen, i);
+         continue;
+       }
+      else if (c == '\\')
+       {
+         pass_next = 1;
+         i++;
+         continue;
+       }
+      else if (c == '\'' || c == '"')
+       {
+         i = (c == '\'') ? skip_single_quoted (string, slen, ++i)
+                         : skip_double_quoted (string, slen, ++i);
+         if (i > eindex)
+           CQ_RETURN(1);
+         /* no increment, the skip_xxx functions go one past end */
+       }
+      else
+       ADVANCE_CHAR (string, slen, i);
+    }
+
+  CQ_RETURN(0);
+}
+
+int
+unclosed_pair (string, eindex, openstr)
+     char *string;
+     int eindex;
+     char *openstr;
+{
+  int i, pass_next, openc, olen;
+  size_t slen;
+  DECLARE_MBSTATE;
+
+  slen = strlen (string);
+  olen = strlen (openstr);
+  i = pass_next = openc = 0;
+  while (i <= eindex)
+    {
+      if (pass_next)
+       {
+         pass_next = 0;
+         if (i >= eindex)      /* XXX was if (i >= eindex - 1) */
+           return 0;
+         ADVANCE_CHAR (string, slen, i);
+         continue;
+       }
+      else if (string[i] == '\\')
+       {
+         pass_next = 1;
+         i++;
+         continue;
+       }
+      else if (STREQN (string + i, openstr, olen))
+       {
+         openc = 1 - openc;
+         i += olen;
+       }
+      else if (string[i] == '\'' || string[i] == '"')
+       {
+         i = (string[i] == '\'') ? skip_single_quoted (string, slen, i)
+                                 : skip_double_quoted (string, slen, i);
+         if (i > eindex)
+           return 0;
+       }
+      else
+       ADVANCE_CHAR (string, slen, i);
+    }
+  return (openc);
+}
+
+/* Skip characters in STRING until we find a character in DELIMS, and return
+   the index of that character.  START is the index into string at which we
+   begin.  This is similar in spirit to strpbrk, but it returns an index into
+   STRING and takes a starting index.  This little piece of code knows quite
+   a lot of shell syntax.  It's very similar to skip_double_quoted and other
+   functions of that ilk. */
+int
+skip_to_delim (string, start, delims)
+     char *string;
+     int start;
+     char *delims;
+{
+  int i, pass_next, backq, si, c;
+  size_t slen;
+  char *temp;
+  DECLARE_MBSTATE;
+
+  slen = strlen (string + start) + start;
+  no_longjmp_on_fatal_error = 1;
+  i = start;
+  pass_next = backq = 0;
+  while (c = string[i])
+    {
+      if (pass_next)
+       {
+         pass_next = 0;
+         if (c == 0)
+           CQ_RETURN(i);
+         ADVANCE_CHAR (string, slen, i);
+         continue;
+       }
+      else if (c == '\\')
+       {
+         pass_next = 1;
+         i++;
+         continue;
+       }
+      else if (backq)
+       {
+         if (c == '`')
+           backq = 0;
+         ADVANCE_CHAR (string, slen, i);
+         continue;
+       }
+      else if (c == '`')
+       {
+         backq = 1;
+         i++;
+         continue;
+       }
+      else if (c == '\'' || c == '"')
+       {
+         i = (c == '\'') ? skip_single_quoted (string, slen, ++i)
+                         : skip_double_quoted (string, slen, ++i);
+         /* no increment, the skip functions increment past the closing quote. */
+       }
+      else if (c == '$' && (string[i+1] == LPAREN || string[i+1] == LBRACE))
+       {
+         si = i + 2;
+         if (string[si] == '\0')
+           CQ_RETURN(si);
+
+         if (string[i+1] == LPAREN)
+           temp = extract_delimited_string (string, &si, "$(", "(", ")", EX_NOALLOC); /* ) */
+         else
+           temp = extract_dollar_brace_string (string, &si, 0, EX_NOALLOC);
+         i = si;
+         if (string[i] == '\0')        /* don't increment i past EOS in loop */
+           break;
+         i++;
+         continue;
+       }
+      else if (member (c, delims))
+       break;
+      else
+       ADVANCE_CHAR (string, slen, i);
+    }
+
+  CQ_RETURN(i);
+}
+
+/* Split STRING (length SLEN) at DELIMS, and return a WORD_LIST with the
+   individual words.  If DELIMS is NULL, the current value of $IFS is used
+   to split the string, and the function follows the shell field splitting
+   rules.  SENTINEL is an index to look for.  NWP, if non-NULL,
+   gets the number of words in the returned list.  CWP, if non-NULL, gets
+   the index of the word containing SENTINEL.  Non-whitespace chars in
+   DELIMS delimit separate fields. */
+WORD_LIST *
+split_at_delims (string, slen, delims, sentinel, nwp, cwp)
+     char *string;
+     int slen;
+     char *delims;
+     int sentinel;
+     int *nwp, *cwp;
+{
+  int ts, te, i, nw, cw, ifs_split;
+  char *token, *d, *d2;
+  WORD_LIST *ret, *tl;
+
+  if (string == 0 || *string == '\0')
+    {
+      if (nwp)
+       *nwp = 0;
+      if (cwp)
+       *cwp = 0;       
+      return ((WORD_LIST *)NULL);
+    }
+
+  d = (delims == 0) ? ifs_value : delims;
+  ifs_split = delims == 0;
+
+  /* Make d2 the non-whitespace characters in delims */
+  d2 = 0;
+  if (delims)
+    {
+      d2 = (char *)xmalloc (strlen (delims) + 1);
+      for (i = ts = 0; delims[i]; i++)
+       {
+         if (whitespace(delims[i]) == 0)
+           d2[ts++] = delims[i];
+       }
+      d2[ts] = '\0';
+    }
+
+  ret = (WORD_LIST *)NULL;
+
+  /* Remove sequences of whitspace characters at the start of the string, as
+     long as those characters are delimiters. */
+  for (i = 0; member (string[i], d) && spctabnl (string[i]); i++)
+    ;
+  if (string[i] == '\0')
+    return (ret);
+
+  ts = i;
+  nw = 0;
+  cw = -1;
+  while (1)
+    {
+      te = skip_to_delim (string, ts, d);
+
+      /* If we have a non-whitespace delimiter character, use it to make a
+        separate field.  This is just about what $IFS splitting does and
+        is closer to the behavior of the shell parser. */
+      if (ts == te && d2 && member (string[ts], d2))
+       {
+         te = ts + 1;
+         /* If we're using IFS splitting, the non-whitespace delimiter char
+            and any additional IFS whitespace delimits a field. */
+         if (ifs_split)
+           while (member (string[te], d) && spctabnl (string[te]))
+             te++;
+         else
+           while (member (string[te], d2))
+             te++;
+       }
+
+      token = substring (string, ts, te);
+
+      ret = add_string_to_list (token, ret);
+      free (token);
+      nw++;
+
+      if (sentinel >= ts && sentinel <= te)
+       cw = nw;
+
+      /* If the cursor is at whitespace just before word start, set the
+        sentinel word to the current word. */
+      if (cwp && cw == -1 && sentinel == ts-1)
+       cw = nw;
+
+      /* If the cursor is at whitespace between two words, make a new, empty
+        word, add it before (well, after, since the list is in reverse order)
+        the word we just added, and set the current word to that one. */
+      if (cwp && cw == -1 && sentinel < ts)
+       {
+         tl = make_word_list (make_word (""), ret->next);
+         ret->next = tl;
+         cw = nw;
+         nw++;
+       }
+
+      if (string[te] == 0)
+       break;
+
+      i = te;
+      while (member (string[i], d) && (ifs_split || spctabnl(string[i])))
+       i++;
+
+      if (string[i])
+       ts = i;
+      else
+       break;
+    }
+
+  /* Special case for SENTINEL at the end of STRING.  If we haven't found
+     the word containing SENTINEL yet, and the index we're looking for is at
+     the end of STRING, add an additional null argument and set the current
+     word pointer to that. */
+  if (cwp && cw == -1 && sentinel >= slen)
+    {
+      if (whitespace (string[sentinel - 1]))
+       {
+         token = "";
+         ret = add_string_to_list (token, ret);
+         nw++;
+       }
+      cw = nw;
+    }
+
+  if (nwp)
+    *nwp = nw;
+  if (cwp)
+    *cwp = cw;
+
+  return (REVERSE_LIST (ret, WORD_LIST *));
+}
+#endif /* READLINE */
+
+#if 0
+/* UNUSED */
+/* Extract the name of the variable to bind to from the assignment string. */
+char *
+assignment_name (string)
+     char *string;
+{
+  int offset;
+  char *temp;
+
+  offset = assignment (string, 0);
+  if (offset == 0)
+    return (char *)NULL;
+  temp = substring (string, 0, offset);
+  return (temp);
+}
+#endif
+
+/* **************************************************************** */
+/*                                                                 */
+/*     Functions to convert strings to WORD_LISTs and vice versa    */
+/*                                                                 */
+/* **************************************************************** */
+
+/* Return a single string of all the words in LIST.  SEP is the separator
+   to put between individual elements of LIST in the output string. */
+char *
+string_list_internal (list, sep)
+     WORD_LIST *list;
+     char *sep;
+{
+  register WORD_LIST *t;
+  char *result, *r;
+  int word_len, sep_len, result_size;
+
+  if (list == 0)
+    return ((char *)NULL);
+
+  /* Short-circuit quickly if we don't need to separate anything. */
+  if (list->next == 0)
+    return (savestring (list->word->word));
+
+  /* This is nearly always called with either sep[0] == 0 or sep[1] == 0. */
+  sep_len = STRLEN (sep);
+  result_size = 0;
+
+  for (t = list; t; t = t->next)
+    {
+      if (t != list)
+       result_size += sep_len;
+      result_size += strlen (t->word->word);
+    }
+
+  r = result = (char *)xmalloc (result_size + 1);
+
+  for (t = list; t; t = t->next)
+    {
+      if (t != list && sep_len)
+       {
+         if (sep_len > 1)
+           {
+             FASTCOPY (sep, r, sep_len);
+             r += sep_len;
+           }
+         else
+           *r++ = sep[0];
+       }
+
+      word_len = strlen (t->word->word);
+      FASTCOPY (t->word->word, r, word_len);
+      r += word_len;
+    }
+
+  *r = '\0';
+  return (result);
+}
+
+/* Return a single string of all the words present in LIST, separating
+   each word with a space. */
+char *
+string_list (list)
+     WORD_LIST *list;
+{
+  return (string_list_internal (list, " "));
+}
+
+/* Return a single string of all the words present in LIST, obeying the
+   quoting rules for "$*", to wit: (P1003.2, draft 11, 3.5.2) "If the
+   expansion [of $*] appears within a double quoted string, it expands
+   to a single field with the value of each parameter separated by the
+   first character of the IFS variable, or by a <space> if IFS is unset." */
+char *
+string_list_dollar_star (list)
+     WORD_LIST *list;
+{
+  char sep[2];
+
+  sep[0] = ifs_firstc;
+  sep[1] = '\0';
+
+  return (string_list_internal (list, sep));
+}
+
+/* Turn $@ into a string.  If (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))
+   is non-zero, the $@ appears within double quotes, and we should quote
+   the list before converting it into a string.  If IFS is unset, and the
+   word is not quoted, we just need to quote CTLESC and CTLNUL characters
+   in the words in the list, because the default value of $IFS is
+   <space><tab><newline>, IFS characters in the words in the list should
+   also be split.  If IFS is null, and the word is not quoted, we need
+   to quote the words in the list to preserve the positional parameters
+   exactly. */
+char *
+string_list_dollar_at (list, quoted)
+     WORD_LIST *list;
+     int quoted;
+{
+  char *ifs, sep[2];
+  WORD_LIST *tlist;
+
+  /* XXX this could just be ifs = ifs_value; */
+  ifs = ifs_var ? value_cell (ifs_var) : (char *)0;
+
+  sep[0] = (ifs == 0 || *ifs == 0) ? ' ' : *ifs;
+  sep[1] = '\0';
+
+  tlist = ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) || (ifs && *ifs == 0))
+               ? quote_list (list)
+               : list_quote_escapes (list);
+  return (string_list_internal (tlist, sep));
+}
+
+/* Return the list of words present in STRING.  Separate the string into
+   words at any of the characters found in SEPARATORS.  If QUOTED is
+   non-zero then word in the list will have its quoted flag set, otherwise
+   the quoted flag is left as make_word () deemed fit.
+
+   This obeys the P1003.2 word splitting semantics.  If `separators' is
+   exactly <space><tab><newline>, then the splitting algorithm is that of
+   the Bourne shell, which treats any sequence of characters from `separators'
+   as a delimiter.  If IFS is unset, which results in `separators' being set
+   to "", no splitting occurs.  If separators has some other value, the
+   following rules are applied (`IFS white space' means zero or more
+   occurrences of <space>, <tab>, or <newline>, as long as those characters
+   are in `separators'):
+
+       1) IFS white space is ignored at the start and the end of the
+          string.
+       2) Each occurrence of a character in `separators' that is not
+          IFS white space, along with any adjacent occurrences of
+          IFS white space delimits a field.
+       3) Any nonzero-length sequence of IFS white space delimits a field.
+   */
+
+/* BEWARE!  list_string strips null arguments.  Don't call it twice and
+   expect to have "" preserved! */
+
+/* This performs word splitting and quoted null character removal on
+   STRING. */
+#define issep(c) \
+       (((separators)[0]) ? ((separators)[1] ? isifs(c) \
+                                             : (c) == (separators)[0]) \
+                          : 0)
+
+WORD_LIST *
+list_string (string, separators, quoted)
+     register char *string, *separators;
+     int quoted;
+{
+  WORD_LIST *result;
+  WORD_DESC *t;
+  char *current_word, *s;
+  int sindex, sh_style_split, whitesep;
+
+  if (!string || !*string)
+    return ((WORD_LIST *)NULL);
+
+  sh_style_split = separators && separators[0] == ' ' &&
+                                separators[1] == '\t' &&
+                                separators[2] == '\n' &&
+                                separators[3] == '\0';
+
+  /* Remove sequences of whitespace at the beginning of STRING, as
+     long as those characters appear in IFS.  Do not do this if
+     STRING is quoted or if there are no separator characters. */
+  if (!quoted || !separators || !*separators)
+    {
+      for (s = string; *s && spctabnl (*s) && issep (*s); s++);
+
+      if (!*s)
+       return ((WORD_LIST *)NULL);
+
+      string = s;
+    }
+
+  /* OK, now STRING points to a word that does not begin with white space.
+     The splitting algorithm is:
+       extract a word, stopping at a separator
+       skip sequences of spc, tab, or nl as long as they are separators
+     This obeys the field splitting rules in Posix.2. */
+  for (result = (WORD_LIST *)NULL, sindex = 0; string[sindex]; )
+    {
+      current_word = string_extract_verbatim (string, &sindex, separators);
+      if (current_word == 0)
+       break;
+
+      /* If we have a quoted empty string, add a quoted null argument.  We
+        want to preserve the quoted null character iff this is a quoted
+        empty string; otherwise the quoted null characters are removed
+        below. */
+      if (QUOTED_NULL (current_word))
+       {
+         t = make_bare_word ("");
+         t->flags |= W_QUOTED;
+         free (t->word);
+         t->word = make_quoted_char ('\0');
+         result = make_word_list (t, result);
+       }
+      else if (current_word[0] != '\0')
+       {
+         /* If we have something, then add it regardless.  However,
+            perform quoted null character removal on the current word. */
+         remove_quoted_nulls (current_word);
+         result = add_string_to_list (current_word, result);
+         if (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))
+           result->word->flags |= W_QUOTED;
+       }
+
+      /* If we're not doing sequences of separators in the traditional
+        Bourne shell style, then add a quoted null argument. */
+      else if (!sh_style_split && !spctabnl (string[sindex]))
+       {
+         t = make_bare_word ("");
+         t->flags |= W_QUOTED;
+         free (t->word);
+         t->word = make_quoted_char ('\0');
+         result = make_word_list (t, result);
+       }
+
+      free (current_word);
+
+      /* Note whether or not the separator is IFS whitespace, used later. */
+      whitesep = string[sindex] && spctabnl (string[sindex]);
+
+      /* Move past the current separator character. */
+      if (string[sindex])
+       sindex++;
+
+      /* Now skip sequences of space, tab, or newline characters if they are
+        in the list of separators. */
+      while (string[sindex] && spctabnl (string[sindex]) && issep (string[sindex]))
+       sindex++;
+
+      /* If the first separator was IFS whitespace and the current character
+        is a non-whitespace IFS character, it should be part of the current
+        field delimiter, not a separate delimiter that would result in an
+        empty field.  Look at POSIX.2, 3.6.5, (3)(b). */
+      if (string[sindex] && whitesep && issep (string[sindex]) && !spctabnl (string[sindex]))
+       sindex++;
+    }
+  return (REVERSE_LIST (result, WORD_LIST *));
+}
+
+/* Parse a single word from STRING, using SEPARATORS to separate fields.
+   ENDPTR is set to the first character after the word.  This is used by
+   the `read' builtin.  This is never called with SEPARATORS != $IFS;
+   it should be simplified.
+
+   XXX - this function is very similar to list_string; they should be
+        combined - XXX */
+char *
+get_word_from_string (stringp, separators, endptr)
+     char **stringp, *separators, **endptr;
+{
+  register char *s;
+  char *current_word;
+  int sindex, sh_style_split, whitesep;
+
+  if (!stringp || !*stringp || !**stringp)
+    return ((char *)NULL);
+
+  s = *stringp;
+
+  sh_style_split = separators && separators[0] == ' ' &&
+                                separators[1] == '\t' &&
+                                separators[2] == '\n' &&
+                                separators[3] == '\0';
+
+  /* Remove sequences of whitespace at the beginning of STRING, as
+     long as those characters appear in IFS. */
+  if (sh_style_split || !separators || !*separators)
+    {
+      for (; *s && spctabnl (*s) && isifs (*s); s++);
+
+      /* If the string is nothing but whitespace, update it and return. */
+      if (!*s)
+       {
+         *stringp = s;
+         if (endptr)
+           *endptr = s;
+         return ((char *)NULL);
+       }
+    }
+
+  /* OK, S points to a word that does not begin with white space.
+     Now extract a word, stopping at a separator, save a pointer to
+     the first character after the word, then skip sequences of spc,
+     tab, or nl as long as they are separators.
+
+     This obeys the field splitting rules in Posix.2. */
+  sindex = 0;
+  current_word = string_extract_verbatim (s, &sindex, separators);
+
+  /* Set ENDPTR to the first character after the end of the word. */
+  if (endptr)
+    *endptr = s + sindex;
+
+  /* Note whether or not the separator is IFS whitespace, used later. */
+  whitesep = s[sindex] && spctabnl (s[sindex]);
+
+  /* Move past the current separator character. */
+  if (s[sindex])
+    sindex++;
+
+  /* Now skip sequences of space, tab, or newline characters if they are
+     in the list of separators. */
+  while (s[sindex] && spctabnl (s[sindex]) && isifs (s[sindex]))
+    sindex++;
+
+  /* If the first separator was IFS whitespace and the current character is
+     a non-whitespace IFS character, it should be part of the current field
+     delimiter, not a separate delimiter that would result in an empty field.
+     Look at POSIX.2, 3.6.5, (3)(b). */
+  if (s[sindex] && whitesep && isifs (s[sindex]) && !spctabnl (s[sindex]))
+    sindex++;
+
+  /* Update STRING to point to the next field. */
+  *stringp = s + sindex;
+  return (current_word);
+}
+
+/* Remove IFS white space at the end of STRING.  Start at the end
+   of the string and walk backwards until the beginning of the string
+   or we find a character that's not IFS white space and not CTLESC.
+   Only let CTLESC escape a white space character if SAW_ESCAPE is
+   non-zero.  */
+char *
+strip_trailing_ifs_whitespace (string, separators, saw_escape)
+     char *string, *separators;
+     int saw_escape;
+{
+  char *s;
+
+  s = string + STRLEN (string) - 1;
+  while (s > string && ((spctabnl (*s) && isifs (*s)) ||
+                       (saw_escape && *s == CTLESC && spctabnl (s[1]))))
+    s--;
+  *++s = '\0';
+  return string;
+}
+
+#if 0
+/* UNUSED */
+/* Split STRING into words at whitespace.  Obeys shell-style quoting with
+   backslashes, single and double quotes. */
+WORD_LIST *
+list_string_with_quotes (string)
+     char *string;
+{
+  WORD_LIST *list;
+  char *token, *s;
+  size_t s_len;
+  int c, i, tokstart, len;
+
+  for (s = string; s && *s && spctabnl (*s); s++)
+    ;
+  if (s == 0 || *s == 0)
+    return ((WORD_LIST *)NULL);
+
+  s_len = strlen (s);
+  tokstart = i = 0;
+  list = (WORD_LIST *)NULL;
+  while (1)
+    {
+      c = s[i];
+      if (c == '\\')
+       {
+         i++;
+         if (s[i])
+           i++;
+       }
+      else if (c == '\'')
+       i = skip_single_quoted (s, s_len, ++i);
+      else if (c == '"')
+       i = skip_double_quoted (s, s_len, ++i);
+      else if (c == 0 || spctabnl (c))
+       {
+         /* We have found the end of a token.  Make a word out of it and
+            add it to the word list. */
+         token = substring (s, tokstart, i);
+         list = add_string_to_list (token, list);
+         free (token);
+         while (spctabnl (s[i]))
+           i++;
+         if (s[i])
+           tokstart = i;
+         else
+           break;
+       }
+      else
+       i++;    /* normal character */
+    }
+  return (REVERSE_LIST (list, WORD_LIST *));
+}
+#endif
+
+/********************************************************/
+/*                                                     */
+/*     Functions to perform assignment statements      */
+/*                                                     */
+/********************************************************/
+
+/* Given STRING, an assignment string, get the value of the right side
+   of the `=', and bind it to the left side.  If EXPAND is true, then
+   perform parameter expansion, command substitution, and arithmetic
+   expansion on the right-hand side.  Perform tilde expansion in any
+   case.  Do not perform word splitting on the result of expansion. */
+static int
+do_assignment_internal (string, expand)
+     const char *string;
+     int expand;
+{
+  int offset;
+  char *name, *value;
+  SHELL_VAR *entry;
+#if defined (ARRAY_VARS)
+  char *t;
+  int ni;
+#endif
+  int assign_list = 0;
+
+  offset = assignment (string, 0);
+  name = savestring (string);
+  value = (char *)NULL;
+
+  if (name[offset] == '=')
+    {
+      char *temp;
+
+      name[offset] = 0;
+      temp = name + offset + 1;
+
+#if defined (ARRAY_VARS)
+      if (expand && temp[0] == LPAREN && xstrchr (temp, RPAREN))
+       {
+         assign_list = ni = 1;
+         value = extract_delimited_string (temp, &ni, "(", (char *)NULL, ")", 0);
+       }
+      else
+#endif
+
+      /* Perform tilde expansion. */
+      if (expand && temp[0])
+       {
+         temp = (xstrchr (temp, '~') && unquoted_member ('~', temp))
+                       ? bash_tilde_expand (temp, 1)
+                       : savestring (temp);
+
+         value = expand_string_if_necessary (temp, 0, expand_string_unsplit);
+         free (temp);
+       }
+      else
+       value = savestring (temp);
+    }
+
+  if (value == 0)
+    {
+      value = (char *)xmalloc (1);
+      value[0] = '\0';
+    }
+
+  if (echo_command_at_execute)
+     xtrace_print_assignment (name, value, assign_list, 1);
+
+#define ASSIGN_RETURN(r)       do { FREE (value); free (name); return (r); } while (0)
+
+#if defined (ARRAY_VARS)
+  if (t = xstrchr (name, '[')) /*]*/
+    {
+      if (assign_list)
+       {
+         report_error (_("%s: cannot assign list to array member"), name);
+         ASSIGN_RETURN (0);
+       }
+      entry = assign_array_element (name, value);
+      if (entry == 0)
+       ASSIGN_RETURN (0);
+    }
+  else if (assign_list)
+    entry = assign_array_from_string (name, value);
+  else
+#endif /* ARRAY_VARS */
+  entry = bind_variable (name, value);
+
+  stupidly_hack_special_variables (name);
+
+  if (entry)
+    VUNSETATTR (entry, att_invisible);
+
+  /* Return 1 if the assignment seems to have been performed correctly. */
+  ASSIGN_RETURN (entry ? ((readonly_p (entry) == 0) && noassign_p (entry) == 0) : 0);
+}
+
+/* Perform the assignment statement in STRING, and expand the
+   right side by doing command and parameter expansion. */
+int
+do_assignment (string)
+     const char *string;
+{
+  return do_assignment_internal (string, 1);
+}
+
+/* Given STRING, an assignment string, get the value of the right side
+   of the `=', and bind it to the left side.  Do not do command and
+   parameter substitution on the right hand side. */
+int
+do_assignment_no_expand (string)
+     const char *string;
+{
+  return do_assignment_internal (string, 0);
+}
+
+/***************************************************
+ *                                                *
+ *  Functions to manage the positional parameters  *
+ *                                                *
+ ***************************************************/
+
+/* Return the word list that corresponds to `$*'. */
+WORD_LIST *
+list_rest_of_args ()
+{
+  register WORD_LIST *list, *args;
+  int i;
+
+  /* Break out of the loop as soon as one of the dollar variables is null. */
+  for (i = 1, list = (WORD_LIST *)NULL; i < 10 && dollar_vars[i]; i++)
+    list = make_word_list (make_bare_word (dollar_vars[i]), list);
+
+  for (args = rest_of_args; args; args = args->next)
+    list = make_word_list (make_bare_word (args->word->word), list);
+
+  return (REVERSE_LIST (list, WORD_LIST *));
+}
+
+int
+number_of_args ()
+{
+  register WORD_LIST *list;
+  int n;
+
+  for (n = 0; n < 9 && dollar_vars[n+1]; n++)
+    ;
+  for (list = rest_of_args; list; list = list->next)
+    n++;
+  return n;
+}
+
+/* Return the value of a positional parameter.  This handles values > 10. */
+char *
+get_dollar_var_value (ind)
+     intmax_t ind;
+{
+  char *temp;
+  WORD_LIST *p;
+
+  if (ind < 10)
+    temp = dollar_vars[ind] ? savestring (dollar_vars[ind]) : (char *)NULL;
+  else /* We want something like ${11} */
+    {
+      ind -= 10;
+      for (p = rest_of_args; p && ind--; p = p->next)
+       ;
+      temp = p ? savestring (p->word->word) : (char *)NULL;
+    }
+  return (temp);
+}
+
+/* Make a single large string out of the dollar digit variables,
+   and the rest_of_args.  If DOLLAR_STAR is 1, then obey the special
+   case of "$*" with respect to IFS. */
+char *
+string_rest_of_args (dollar_star)
+     int dollar_star;
+{
+  register WORD_LIST *list;
+  char *string;
+
+  list = list_rest_of_args ();
+  string = dollar_star ? string_list_dollar_star (list) : string_list (list);
+  dispose_words (list);
+  return (string);
+}
+
+/* Return a string containing the positional parameters from START to
+   END, inclusive.  If STRING[0] == '*', we obey the rules for $*,
+   which only makes a difference if QUOTED is non-zero.  If QUOTED includes
+   Q_HERE_DOCUMENT or Q_DOUBLE_QUOTES, this returns a quoted list, otherwise
+   no quoting chars are added. */
+static char *
+pos_params (string, start, end, quoted)
+     char *string;
+     int start, end, quoted;
+{
+  WORD_LIST *save, *params, *h, *t;
+  char *ret;
+  int i;
+
+  /* see if we can short-circuit.  if start == end, we want 0 parameters. */
+  if (start == end)
+    return ((char *)NULL);
+
+  save = params = list_rest_of_args ();
+  if (save == 0)
+    return ((char *)NULL);
+
+  for (i = 1; params && i < start; i++)
+    params = params->next;
+  if (params == 0)
+    return ((char *)NULL);
+  for (h = t = params; params && i < end; i++)
+    {
+      t = params;
+      params = params->next;
+    }
+
+  t->next = (WORD_LIST *)NULL;
+  if (string[0] == '*')
+#if 0
+    ret = (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) ? string_list_dollar_star (quote_list (h)) : string_list (h);
+#else
+    {
+      if (quoted & Q_DOUBLE_QUOTES)
+       ret = string_list_dollar_star (quote_list (h));
+      else if (quoted & Q_HERE_DOCUMENT)
+       ret = string_list (quote_list (h));
+      else
+       ret = string_list (h);
+    }
+#endif
+  else
+    ret = string_list ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) ? quote_list (h) : h);
+  if (t != params)
+    t->next = params;
+
+  dispose_words (save);
+  return (ret);
+}
+
+/******************************************************************/
+/*                                                               */
+/*     Functions to expand strings to strings or WORD_LISTs      */
+/*                                                               */
+/******************************************************************/
+
+#if defined (PROCESS_SUBSTITUTION)
+#define EXP_CHAR(s) (s == '$' || s == '`' || s == '<' || s == '>' || s == CTLESC)
+#else
+#define EXP_CHAR(s) (s == '$' || s == '`' || s == CTLESC)
+#endif
+
+/* If there are any characters in STRING that require full expansion,
+   then call FUNC to expand STRING; otherwise just perform quote
+   removal if necessary.  This returns a new string. */
+static char *
+expand_string_if_necessary (string, quoted, func)
+     char *string;
+     int quoted;
+     EXPFUNC *func;
+{
+  WORD_LIST *list;
+  size_t slen;
+  int i, saw_quote;
+  char *ret;
+  DECLARE_MBSTATE;
+
+  slen = strlen (string);
+  i = saw_quote = 0;
+  while (string[i])
+    {
+      if (EXP_CHAR (string[i]))
+       break;
+      else if (string[i] == '\'' || string[i] == '\\' || string[i] == '"')
+       saw_quote = 1;
+      ADVANCE_CHAR (string, slen, i);
+    }
+
+  if (string[i])
+    {
+      list = (*func) (string, quoted);
+      if (list)
+       {
+         ret = string_list (list);
+         dispose_words (list);
+       }
+      else
+       ret = (char *)NULL;
+    }
+  else if (saw_quote && ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) == 0))
+    ret = string_quote_removal (string, quoted);
+  else
+    ret = savestring (string);
+
+  return ret;
+}
+
+static inline char *
+expand_string_to_string_internal (string, quoted, func)
+     char *string;
+     int quoted;
+     EXPFUNC *func;
+{
+  WORD_LIST *list;
+  char *ret;
+
+  if (string == 0 || *string == '\0')
+    return ((char *)NULL);
+
+  list = (*func) (string, quoted);
+  if (list)
+    {
+      ret = string_list (list);
+      dispose_words (list);
+    }
+  else
+    ret = (char *)NULL;
+
+  return (ret);
+}
+
+char *
+expand_string_to_string (string, quoted)
+     char *string;
+     int quoted;
+{
+  return (expand_string_to_string_internal (string, quoted, expand_string));
+}
+
+char *
+expand_string_unsplit_to_string (string, quoted)
+     char *string;
+     int quoted;
+{
+  return (expand_string_to_string_internal (string, quoted, expand_string_unsplit));
+}
+
+#if defined (COND_COMMAND)
+/* Just remove backslashes in STRING.  Returns a new string. */
+char *
+remove_backslashes (string)
+     char *string;
+{
+  char *r, *ret, *s;
+
+  r = ret = (char *)xmalloc (strlen (string) + 1);
+  for (s = string; s && *s; )
+    {
+      if (*s == '\\')
+       s++;
+      if (*s == 0)
+       break;
+      *r++ = *s++;
+    }
+  *r = '\0';
+  return ret;
+}
+
+/* This needs better error handling. */
+/* Expand W for use as an argument to a unary or binary operator in a
+   [[...]] expression.  If SPECIAL is nonzero, this is the rhs argument
+   to the != or == operator, and should be treated as a pattern.  In
+   this case, we quote the string specially for the globbing code.  The
+   caller is responsible for removing the backslashes if the unquoted
+   words is needed later. */   
+char *
+cond_expand_word (w, special)
+     WORD_DESC *w;
+     int special;
+{
+  char *r, *p;
+  WORD_LIST *l;
+
+  if (w->word == 0 || w->word[0] == '\0')
+    return ((char *)NULL);
+
+  if (xstrchr (w->word, '~') && unquoted_member ('~', w->word))
+    {
+      p = bash_tilde_expand (w->word, 0);
+      free (w->word);
+      w->word = p;
+    }
+
+  l = call_expand_word_internal (w, 0, 0, (int *)0, (int *)0);
+  if (l)
+    {
+      if (special == 0)
+       {
+         dequote_list (l);
+         r = string_list (l);
+       }
+      else
+       {
+         p = string_list (l);
+         r = quote_string_for_globbing (p, QGLOB_CVTNULL);
+         free (p);
+       }
+      dispose_words (l);
+    }
+  else
+    r = (char *)NULL;
+
+  return r;
+}
+#endif
+
+/* Call expand_word_internal to expand W and handle error returns.
+   A convenience function for functions that don't want to handle
+   any errors or free any memory before aborting. */
+static WORD_LIST *
+call_expand_word_internal (w, q, i, c, e)
+     WORD_DESC *w;
+     int q, i, *c, *e;
+{
+  WORD_LIST *result;
+
+  result = expand_word_internal (w, q, i, c, e);
+  if (result == &expand_word_error || result == &expand_word_fatal)
+    {
+      /* By convention, each time this error is returned, w->word has
+        already been freed (it sometimes may not be in the fatal case,
+        but that doesn't result in a memory leak because we're going
+        to exit in most cases). */
+      w->word = (char *)NULL;
+      last_command_exit_value = EXECUTION_FAILURE;
+      exp_jump_to_top_level ((result == &expand_word_error) ? DISCARD : FORCE_EOF);
+      /* NOTREACHED */
+    }
+  else
+    return (result);
+}
+
+/* Perform parameter expansion, command substitution, and arithmetic
+   expansion on STRING, as if it were a word.  Leave the result quoted. */
+static WORD_LIST *
+expand_string_internal (string, quoted)
+     char *string;
+     int quoted;
+{
+  WORD_DESC td;
+  WORD_LIST *tresult;
+
+  if (string == 0 || *string == 0)
+    return ((WORD_LIST *)NULL);
+
+  td.flags = 0;
+  td.word = savestring (string);
+
+  tresult = call_expand_word_internal (&td, quoted, 0, (int *)NULL, (int *)NULL);
+
+  FREE (td.word);
+  return (tresult);
+}
+
+/* Expand STRING by performing parameter expansion, command substitution,
+   and arithmetic expansion.  Dequote the resulting WORD_LIST before
+   returning it, but do not perform word splitting.  The call to
+   remove_quoted_nulls () is in here because word splitting normally
+   takes care of quote removal. */
+WORD_LIST *
+expand_string_unsplit (string, quoted)
+     char *string;
+     int quoted;
+{
+  WORD_LIST *value;
+
+  if (string == 0 || *string == '\0')
+    return ((WORD_LIST *)NULL);
+
+  expand_no_split_dollar_star = 1;
+  value = expand_string_internal (string, quoted);
+  expand_no_split_dollar_star = 0;
+
+  if (value)
+    {
+      if (value->word)
+       remove_quoted_nulls (value->word->word);
+      dequote_list (value);
+    }
+  return (value);
+}
+
+
+/* Expand one of the PS? prompt strings. This is a sort of combination of
+   expand_string_unsplit and expand_string_internal, but returns the
+   passed string when an error occurs.  Might want to trap other calls
+   to jump_to_top_level here so we don't endlessly loop. */
+WORD_LIST *
+expand_prompt_string (string, quoted)
+     char *string;
+     int quoted;
+{
+  WORD_LIST *value;
+  WORD_DESC td;
+
+  if (string == 0 || *string == 0)
+    return ((WORD_LIST *)NULL);
+
+  td.flags = 0;
+  td.word = savestring (string);
+
+  no_longjmp_on_fatal_error = 1;
+  value = expand_word_internal (&td, quoted, 0, (int *)NULL, (int *)NULL);
+  no_longjmp_on_fatal_error = 0;
+
+  if (value == &expand_word_error || value == &expand_word_fatal)
+    {
+      value = make_word_list (make_bare_word (string), (WORD_LIST *)NULL);
+      return value;
+    }
+  FREE (td.word);
+  if (value)
+    {
+      if (value->word)
+       remove_quoted_nulls (value->word->word);
+      dequote_list (value);
+    }
+  return (value);
+}
+
+/* Expand STRING just as if you were expanding a word, but do not dequote
+   the resultant WORD_LIST.  This is called only from within this file,
+   and is used to correctly preserve quoted characters when expanding
+   things like ${1+"$@"}.  This does parameter expansion, command
+   substitution, arithmetic expansion, and word splitting. */
+static WORD_LIST *
+expand_string_leave_quoted (string, quoted)
+     char *string;
+     int quoted;
+{
+  WORD_LIST *tlist;
+  WORD_LIST *tresult;
+
+  if (string == 0 || *string == '\0')
+    return ((WORD_LIST *)NULL);
+
+  tlist = expand_string_internal (string, quoted);
+
+  if (tlist)
+    {
+      tresult = word_list_split (tlist);
+      dispose_words (tlist);
+      return (tresult);
+    }
+  return ((WORD_LIST *)NULL);
+}
+
+/* This does not perform word splitting or dequote the WORD_LIST
+   it returns. */
+static WORD_LIST *
+expand_string_for_rhs (string, quoted, dollar_at_p, has_dollar_at)
+     char *string;
+     int quoted, *dollar_at_p, *has_dollar_at;
+{
+  WORD_DESC td;
+  WORD_LIST *tresult;
+
+  if (string == 0 || *string == '\0')
+    return (WORD_LIST *)NULL;
+
+  td.flags = 0;
+  td.word = string;
+  tresult = call_expand_word_internal (&td, quoted, 1, dollar_at_p, has_dollar_at);
+  return (tresult);
+}
+
+/* Expand STRING just as if you were expanding a word.  This also returns
+   a list of words.  Note that filename globbing is *NOT* done for word
+   or string expansion, just when the shell is expanding a command.  This
+   does parameter expansion, command substitution, arithmetic expansion,
+   and word splitting.  Dequote the resultant WORD_LIST before returning. */
+WORD_LIST *
+expand_string (string, quoted)
+     char *string;
+     int quoted;
+{
+  WORD_LIST *result;
+
+  if (string == 0 || *string == '\0')
+    return ((WORD_LIST *)NULL);
+
+  result = expand_string_leave_quoted (string, quoted);
+  return (result ? dequote_list (result) : result);
+}
+
+/***************************************************
+ *                                                *
+ *     Functions to handle quoting chars          *
+ *                                                *
+ ***************************************************/
+
+/* Conventions:
+
+     A string with s[0] == CTLNUL && s[1] == 0 is a quoted null string.
+     The parser passes CTLNUL as CTLESC CTLNUL. */
+
+/* Quote escape characters in string s, but no other characters.  This is
+   used to protect CTLESC and CTLNUL in variable values from the rest of
+   the word expansion process after the variable is expanded. */
+char *
+quote_escapes (string)
+     char *string;
+{
+  register char *s, *t;
+  size_t slen;
+  char *result, *send;
+  DECLARE_MBSTATE; 
+
+  slen = strlen (string);
+  send = string + slen;
+
+  t = result = (char *)xmalloc ((slen * 2) + 1);
+  s = string;
+
+  while (*s)
+    {
+      if (*s == CTLESC || *s == CTLNUL)
+       *t++ = CTLESC;
+      COPY_CHAR_P (t, s, send);
+    }
+  *t = '\0';
+  return (result);
+}
+
+static WORD_LIST *
+list_quote_escapes (list)
+     WORD_LIST *list;
+{
+  register WORD_LIST *w;
+  char *t;
+
+  for (w = list; w; w = w->next)
+    {
+      t = w->word->word;
+      w->word->word = quote_escapes (t);
+      free (t);
+    }
+  return list;
+}
+
+/* Inverse of quote_escapes; remove CTLESC protecting CTLESC or CTLNUL.
+
+   The parser passes us CTLESC as CTLESC CTLESC and CTLNUL as CTLESC CTLNUL.
+   This is necessary to make unquoted CTLESC and CTLNUL characters in the
+   data stream pass through properly.
+
+   We need to remove doubled CTLESC characters inside quoted strings before
+   quoting the entire string, so we do not double the number of CTLESC
+   characters.
+
+   Also used by parts of the pattern substitution code. */
+static char *
+dequote_escapes (string)
+     char *string;
+{
+  register char *s, *t;
+  size_t slen;
+  char *result, *send;
+  DECLARE_MBSTATE;
+
+  if (string == 0)
+    return string;
+
+  slen = strlen (string);
+  send = string + slen;
+
+  t = result = (char *)xmalloc (slen + 1);
+  s = string;
+
+  if (strchr (string, CTLESC) == 0)
+    return (strcpy (result, s));
+
+  while (*s)
+    {
+      if (*s == CTLESC && (s[1] == CTLESC || s[1] == CTLNUL))
+       {
+         s++;
+         if (*s == '\0')
+           break;
+       }
+      COPY_CHAR_P (t, s, send);
+    }
+  *t = '\0';
+  return result;
+}
+
+/* Return a new string with the quoted representation of character C. */
+static char *
+make_quoted_char (c)
+     int c;
+{
+  char *temp;
+
+  temp = (char *)xmalloc (3);
+  if (c == 0)
+    {
+      temp[0] = CTLNUL;
+      temp[1] = '\0';
+    }
+  else
+    {
+      temp[0] = CTLESC;
+      temp[1] = c;
+      temp[2] = '\0';
+    }
+  return (temp);
+}
+
+/* Quote STRING.  Return a new string. */
+char *
+quote_string (string)
+     char *string;
+{
+  register char *t;
+  size_t slen;
+  char *result, *send;
+
+  if (*string == 0)
+    {
+      result = (char *)xmalloc (2);
+      result[0] = CTLNUL;
+      result[1] = '\0';
+    }
+  else
+    {
+      DECLARE_MBSTATE;
+
+      slen = strlen (string);
+      send = string + slen;
+
+      result = (char *)xmalloc ((slen * 2) + 1);
+
+      for (t = result; string < send; )
+       {
+         *t++ = CTLESC;
+         COPY_CHAR_P (t, string, send);
+       }
+      *t = '\0';
+    }
+  return (result);
+}
+
+/* De-quoted quoted characters in STRING. */
+char *
+dequote_string (string)
+     char *string;
+{
+  register char *s, *t;
+  size_t slen;
+  char *result, *send;
+  DECLARE_MBSTATE;
+
+  slen = strlen (string);
+
+  t = result = (char *)xmalloc (slen + 1);
+
+  if (QUOTED_NULL (string))
+    {
+      result[0] = '\0';
+      return (result);
+    }
+
+  /* If no character in the string can be quoted, don't bother examining
+     each character.  Just return a copy of the string passed to us. */
+  if (strchr (string, CTLESC) == NULL)
+    return (strcpy (result, string));
+
+  send = string + slen;
+  s = string;
+  while (*s)
+    {
+      if (*s == CTLESC)
+       {
+         s++;
+         if (*s == '\0')
+           break;
+       }
+      COPY_CHAR_P (t, s, send);
+    }
+
+  *t = '\0';
+  return (result);
+}
+
+/* Quote the entire WORD_LIST list. */
+static WORD_LIST *
+quote_list (list)
+     WORD_LIST *list;
+{
+  register WORD_LIST *w;
+  char *t;
+
+  for (w = list; w; w = w->next)
+    {
+      t = w->word->word;
+      w->word->word = quote_string (t);
+      free (t);
+      w->word->flags |= W_QUOTED;
+    }
+  return list;
+}
+
+static WORD_LIST *
+dequote_list (list)
+     WORD_LIST *list;
+{
+  register char *s;
+  register WORD_LIST *tlist;
+
+  for (tlist = list; tlist; tlist = tlist->next)
+    {
+      s = dequote_string (tlist->word->word);
+      free (tlist->word->word);
+      tlist->word->word = s;
+    }
+  return list;
+}
+
+/* Remove CTLESC protecting a CTLESC or CTLNUL in place.  Return the passed
+   string. */
+static char *
+remove_quoted_escapes (string)
+     char *string;
+{
+  char *t;
+
+  if (string)
+    {
+      t = dequote_escapes (string);
+      strcpy (string, t);
+      free (t);
+    }
+
+  return (string);
+}
+
+/* Perform quoted null character removal on STRING.  We don't allow any
+   quoted null characters in the middle or at the ends of strings because
+   of how expand_word_internal works.  remove_quoted_nulls () turns
+   STRING into an empty string iff it only consists of a quoted null,
+   and removes all unquoted CTLNUL characters. */
+static char *
+remove_quoted_nulls (string)
+     char *string;
+{
+  register size_t slen;
+  register int i, j, prev_i;
+  DECLARE_MBSTATE;
+
+  if (strchr (string, CTLNUL) == 0)            /* XXX */
+    return string;                             /* XXX */
+
+  slen = strlen (string);
+  i = j = 0;
+
+  while (i < slen)
+    {
+      if (string[i] == CTLESC)
+       {
+         /* Old code had j++, but we cannot assume that i == j at this
+            point -- what if a CTLNUL has already been removed from the
+            string?  We don't want to drop the CTLESC or recopy characters
+            that we've already copied down. */
+         i++; string[j++] = CTLESC;
+         if (i == slen)
+           break;
+       }
+      else if (string[i] == CTLNUL)
+       i++;
+
+      prev_i = i;
+      ADVANCE_CHAR (string, slen, i);
+      if (j < prev_i)
+       {
+         do string[j++] = string[prev_i++]; while (prev_i < i);
+       }
+      else
+       j = i;
+    }
+  string[j] = '\0';
+
+  return (string);
+}
+
+/* Perform quoted null character removal on each element of LIST.
+   This modifies LIST. */
+void
+word_list_remove_quoted_nulls (list)
+     WORD_LIST *list;
+{
+  register WORD_LIST *t;
+
+  for (t = list; t; t = t->next)
+    remove_quoted_nulls (t->word->word);
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*        Functions for Matching and Removing Patterns             */
+/*                                                                 */
+/* **************************************************************** */
+
+#if defined (HANDLE_MULTIBYTE)
+#if 0 /* Currently unused */
+static unsigned char *
+mb_getcharlens (string, len)
+     char *string;
+     int len;
+{
+  int i, offset, last;
+  unsigned char *ret;
+  char *p;
+  DECLARE_MBSTATE;
+
+  i = offset = 0;
+  last = 0;
+  ret = (unsigned char *)xmalloc (len);
+  memset (ret, 0, len);
+  while (string[last])
+    {
+      ADVANCE_CHAR (string, len, offset);
+      ret[last] = offset - last;
+      last = offset;
+    }
+  return ret;
+}
+#endif
+#endif
+
+/* Remove the portion of PARAM matched by PATTERN according to OP, where OP
+   can have one of 4 values:
+       RP_LONG_LEFT    remove longest matching portion at start of PARAM
+       RP_SHORT_LEFT   remove shortest matching portion at start of PARAM
+       RP_LONG_RIGHT   remove longest matching portion at end of PARAM
+       RP_SHORT_RIGHT  remove shortest matching portion at end of PARAM
+*/
+
+#define RP_LONG_LEFT   1
+#define RP_SHORT_LEFT  2
+#define RP_LONG_RIGHT  3
+#define RP_SHORT_RIGHT 4
+
+static char *
+remove_upattern (param, pattern, op)
+     char *param, *pattern;
+     int op;
+{
+  register int len;
+  register char *end;
+  register char *p, *ret, c;
+
+  len = STRLEN (param);
+  end = param + len;
+
+  switch (op)
+    {
+      case RP_LONG_LEFT:       /* remove longest match at start */
+       for (p = end; p >= param; p--)
+         {
+           c = *p; *p = '\0';
+           if (strmatch (pattern, param, FNMATCH_EXTFLAG) != FNM_NOMATCH)
+             {
+               *p = c;
+               return (savestring (p));
+             }
+           *p = c;
+
+         }
+       break;
+
+      case RP_SHORT_LEFT:      /* remove shortest match at start */
+       for (p = param; p <= end; p++)
+         {
+           c = *p; *p = '\0';
+           if (strmatch (pattern, param, FNMATCH_EXTFLAG) != FNM_NOMATCH)
+             {
+               *p = c;
+               return (savestring (p));
+             }
+           *p = c;
+         }
+       break;
+
+      case RP_LONG_RIGHT:      /* remove longest match at end */
+       for (p = param; p <= end; p++)
+         {
+           if (strmatch (pattern, p, FNMATCH_EXTFLAG) != FNM_NOMATCH)
+             {
+               c = *p; *p = '\0';
+               ret = savestring (param);
+               *p = c;
+               return (ret);
+             }
+         }
+       break;
+
+      case RP_SHORT_RIGHT:     /* remove shortest match at end */
+       for (p = end; p >= param; p--)
+         {
+           if (strmatch (pattern, p, FNMATCH_EXTFLAG) != FNM_NOMATCH)
+             {
+               c = *p; *p = '\0';
+               ret = savestring (param);
+               *p = c;
+               return (ret);
+             }
+         }
+       break;
+    }
+
+  return (savestring (param)); /* no match, return original string */
+}
+
+#if defined (HANDLE_MULTIBYTE)
+
+#if !defined (HAVE_WCSDUP)
+static wchar_t *
+wcsdup (ws)
+     wchar_t *ws;
+{
+  wchar_t *ret;
+  size_t len;
+
+  len = wcslen (ws);
+  ret = xmalloc ((len + 1) * sizeof (wchar_t));
+  if (ret == 0)
+    return ret;
+  return (wcscpy (ret, ws));
+}
+#endif /* !HAVE_WCSDUP */
+
+static wchar_t *
+remove_wpattern (wparam, wstrlen, wpattern, op)
+     wchar_t *wparam;
+     size_t wstrlen;
+     wchar_t *wpattern;
+     int op;
+{
+  wchar_t wc;
+  int n, n1;
+  wchar_t *ret;
+
+  switch (op)
+    {
+      case RP_LONG_LEFT:       /* remove longest match at start */
+        for (n = wstrlen; n >= 0; n--)
+         {
+           wc = wparam[n]; wparam[n] = L'\0';
+           if (wcsmatch (wpattern, wparam, FNMATCH_EXTFLAG) != FNM_NOMATCH)
+             {
+               wparam[n] = wc;
+               return (wcsdup (wparam + n));
+             }
+           wparam[n] = wc;
+         }
+       break;
+
+      case RP_SHORT_LEFT:      /* remove shortest match at start */
+       for (n = 0; n <= wstrlen; n++)
+         {
+           wc = wparam[n]; wparam[n] = L'\0';
+           if (wcsmatch (wpattern, wparam, FNMATCH_EXTFLAG) != FNM_NOMATCH)
+             {
+               wparam[n] = wc;
+               return (wcsdup (wparam + n));
+             }
+           wparam[n] = wc;
+         }
+       break;
+
+      case RP_LONG_RIGHT:      /* remove longest match at end */
+        for (n = 0; n <= wstrlen; n++)
+         {
+           if (wcsmatch (wpattern, wparam + n, FNMATCH_EXTFLAG) != FNM_NOMATCH)
+             {
+               wc = wparam[n]; wparam[n] = L'\0';
+               ret = wcsdup (wparam);
+               wparam[n] = wc;
+               return (ret);
+             }
+         }
+       break;
+
+      case RP_SHORT_RIGHT:     /* remove shortest match at end */
+       for (n = wstrlen; n >= 0; n--)
+         {
+           if (wcsmatch (wpattern, wparam + n, FNMATCH_EXTFLAG) != FNM_NOMATCH)
+             {
+               wc = wparam[n]; wparam[n] = L'\0';
+               ret = wcsdup (wparam);
+               wparam[n] = wc;
+               return (ret);
+             }
+         }
+       break;
+    }
+
+  return (wcsdup (wparam));    /* no match, return original string */
+}
+#endif /* HANDLE_MULTIBYTE */
+
+static char *
+remove_pattern (param, pattern, op)
+     char *param, *pattern;
+     int op;
+{
+  if (param == NULL)
+    return (param);
+  if (*param == '\0' || pattern == NULL || *pattern == '\0')   /* minor optimization */
+    return (savestring (param));
+
+#if defined (HANDLE_MULTIBYTE)
+  if (MB_CUR_MAX > 1)
+    {
+      wchar_t *ret, *oret;
+      size_t n;
+      wchar_t *wparam, *wpattern;
+      mbstate_t ps;
+      char *xret;
+
+      n = xdupmbstowcs (&wpattern, NULL, pattern);
+      if (n == (size_t)-1)
+       return (remove_upattern (param, pattern, op));
+      n = xdupmbstowcs (&wparam, NULL, param);
+      if (n == (size_t)-1)
+       {
+         free (wpattern);
+         return (remove_upattern (param, pattern, op));
+       }
+      oret = ret = remove_wpattern (wparam, n, wpattern, op);
+
+      free (wparam);
+      free (wpattern);
+
+      n = strlen (param);
+      xret = xmalloc (n + 1);
+      memset (&ps, '\0', sizeof (mbstate_t));
+      n = wcsrtombs (xret, (const wchar_t **)&ret, n, &ps);
+      xret[n] = '\0';          /* just to make sure */
+      free (oret);
+      return xret;      
+    }
+  else
+#endif
+    return (remove_upattern (param, pattern, op));
+}
+
+/* Return 1 of the first character of STRING could match the first
+   character of pattern PAT.  Used to avoid n2 calls to strmatch(). */
+static int
+match_pattern_char (pat, string)
+     char *pat, *string;
+{
+  char c;
+
+  if (*string == 0)
+    return (0);
+
+  switch (c = *pat++)
+    {
+    default:
+      return (*string == c);
+    case '\\':
+      return (*string == *pat);
+    case '?':
+      return (*pat == LPAREN ? 1 : (*string != '\0'));
+    case '*':
+      return (1);
+    case '+':
+    case '!':
+    case '@':
+      return (*pat == LPAREN ? 1 : (*string == c));
+    case '[':
+      return (*string != '\0');
+    }
+}
+
+/* Match PAT anywhere in STRING and return the match boundaries.
+   This returns 1 in case of a successful match, 0 otherwise.  SP
+   and EP are pointers into the string where the match begins and
+   ends, respectively.  MTYPE controls what kind of match is attempted.
+   MATCH_BEG and MATCH_END anchor the match at the beginning and end
+   of the string, respectively.  The longest match is returned. */
+static int
+match_upattern (string, pat, mtype, sp, ep)
+     char *string, *pat;
+     int mtype;
+     char **sp, **ep;
+{
+  int c, len;
+  register char *p, *p1;
+  char *end;
+
+  len = STRLEN (string);
+  end = string + len;
+
+  switch (mtype)
+    {
+    case MATCH_ANY:
+      for (p = string; p <= end; p++)
+       {
+         if (match_pattern_char (pat, p))
+           {
+             for (p1 = end; p1 >= p; p1--)
+               {
+                 c = *p1; *p1 = '\0';
+                 if (strmatch (pat, p, FNMATCH_EXTFLAG) == 0)
+                   {
+                     *p1 = c;
+                     *sp = p;
+                     *ep = p1;
+                     return 1;
+                   }
+                 *p1 = c;
+               }
+           }
+       }
+
+      return (0);
+
+    case MATCH_BEG:
+      if (match_pattern_char (pat, string) == 0)
+       return (0);
+
+      for (p = end; p >= string; p--)
+       {
+         c = *p; *p = '\0';
+         if (strmatch (pat, string, FNMATCH_EXTFLAG) == 0)
+           {
+             *p = c;
+             *sp = string;
+             *ep = p;
+             return 1;
+           }
+         *p = c;
+       }
+
+      return (0);
+
+    case MATCH_END:
+      for (p = string; p <= end; p++)
+       {
+         if (strmatch (pat, p, FNMATCH_EXTFLAG) == 0)
+           {
+             *sp = p;
+             *ep = end;
+             return 1;
+           }
+
+       }
+
+      return (0);
+    }
+
+  return (0);
+}
+
+#if defined (HANDLE_MULTIBYTE)
+/* Return 1 of the first character of WSTRING could match the first
+   character of pattern WPAT.  Wide character version. */
+static int
+match_pattern_wchar (wpat, wstring)
+     wchar_t *wpat, *wstring;
+{
+  wchar_t wc;
+
+  if (*wstring == 0)
+    return (0);
+
+  switch (wc = *wpat++)
+    {
+    default:
+      return (*wstring == wc);
+    case L'\\':
+      return (*wstring == *wpat);
+    case L'?':
+      return (*wpat == LPAREN ? 1 : (*wstring != L'\0'));
+    case L'*':
+      return (1);
+    case L'+':
+    case L'!':
+    case L'@':
+      return (*wpat == LPAREN ? 1 : (*wstring == wc));
+    case L'[':
+      return (*wstring != L'\0');
+    }
+}
+
+/* Match WPAT anywhere in WSTRING and return the match boundaries.
+   This returns 1 in case of a successful match, 0 otherwise.  Wide
+   character version. */
+static int
+match_wpattern (wstring, indices, wstrlen, wpat, mtype, sp, ep)
+     wchar_t *wstring;
+     char **indices;
+     size_t wstrlen;
+     wchar_t *wpat;
+     int mtype;
+     char **sp, **ep;
+{
+  wchar_t wc;
+  int len;
+#if 0
+  size_t n, n1;        /* Apple's gcc seems to miscompile this badly */
+#else
+  int n, n1;
+#endif
+
+  switch (mtype)
+    {
+    case MATCH_ANY:
+      for (n = 0; n <= wstrlen; n++)
+       {
+         if (match_pattern_wchar (wpat, wstring + n))
+           {
+             for (n1 = wstrlen; n1 >= n; n1--)
+               {
+                 wc = wstring[n1]; wstring[n1] = L'\0';
+                 if (wcsmatch (wpat, wstring + n, FNMATCH_EXTFLAG) == 0)
+                   {
+                     wstring[n1] = wc;
+                     *sp = indices[n];
+                     *ep = indices[n1];
+                     return 1;
+                   }
+                 wstring[n1] = wc;
+               }
+           }
+       }
+
+      return (0);
+
+    case MATCH_BEG:
+      if (match_pattern_wchar (wpat, wstring) == 0)
+       return (0);
+
+      for (n = wstrlen; n >= 0; n--)
+       {
+         wc = wstring[n]; wstring[n] = L'\0';
+         if (wcsmatch (wpat, wstring, FNMATCH_EXTFLAG) == 0)
+           {
+             wstring[n] = wc;
+             *sp = indices[0];
+             *ep = indices[n];
+             return 1;
+           }
+         wstring[n] = wc;
+       }
+
+      return (0);
+
+    case MATCH_END:
+      for (n = 0; n <= wstrlen; n++)
+       {
+         if (wcsmatch (wpat, wstring + n, FNMATCH_EXTFLAG) == 0)
+           {
+             *sp = indices[n];
+             *ep = indices[wstrlen];
+             return 1;
+           }
+       }
+
+      return (0);
+    }
+
+  return (0);
+}
+#endif /* HANDLE_MULTIBYTE */
+
+static int
+match_pattern (string, pat, mtype, sp, ep)
+     char *string, *pat;
+     int mtype;
+     char **sp, **ep;
+{
+#if defined (HANDLE_MULTIBYTE)
+  int ret;
+  size_t n;
+  wchar_t *wstring, *wpat;
+  char **indices;
+#endif
+
+  if (string == 0 || *string == 0 || pat == 0 || *pat == 0)
+    return (0);
+
+#if defined (HANDLE_MULTIBYTE)
+  if (MB_CUR_MAX > 1)
+    {
+      n = xdupmbstowcs (&wpat, NULL, pat);
+      if (n == (size_t)-1)
+       return (match_upattern (string, pat, mtype, sp, ep));
+      n = xdupmbstowcs (&wstring, &indices, string);
+      if (n == (size_t)-1)
+       {
+         free (wpat);
+         return (match_upattern (string, pat, mtype, sp, ep));
+       }
+      ret = match_wpattern (wstring, indices, n, wpat, mtype, sp, ep);
+
+      free (wpat);
+      free (wstring);
+      free (indices);
+
+      return (ret);
+    }
+  else
+#endif
+    return (match_upattern (string, pat, mtype, sp, ep));
+}
+
+static int
+getpatspec (c, value)
+     int c;
+     char *value;
+{
+  if (c == '#')
+    return ((*value == '#') ? RP_LONG_LEFT : RP_SHORT_LEFT);
+  else /* c == '%' */
+    return ((*value == '%') ? RP_LONG_RIGHT : RP_SHORT_RIGHT);
+}
+
+/* Posix.2 says that the WORD should be run through tilde expansion,
+   parameter expansion, command substitution and arithmetic expansion.
+   This leaves the result quoted, so quote_string_for_globbing () has
+   to be called to fix it up for strmatch ().  If QUOTED is non-zero,
+   it means that the entire expression was enclosed in double quotes.
+   This means that quoting characters in the pattern do not make any
+   special pattern characters quoted.  For example, the `*' in the
+   following retains its special meaning: "${foo#'*'}". */
+static char *
+getpattern (value, quoted, expandpat)
+     char *value;
+     int quoted, expandpat;
+{
+  char *pat, *tword;
+  WORD_LIST *l;
+  int i;
+
+  tword = xstrchr (value, '~') ? bash_tilde_expand (value, 0) : savestring (value);
+
+  /* There is a problem here:  how to handle single or double quotes in the
+     pattern string when the whole expression is between double quotes?
+     POSIX.2 says that enclosing double quotes do not cause the pattern to
+     be quoted, but does that leave us a problem with @ and array[@] and their
+     expansions inside a pattern? */
+#if 0
+  if (expandpat && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && *tword)
+    {
+      i = 0;
+      pat = string_extract_double_quoted (tword, &i, 1);
+      free (tword);
+      tword = pat;
+    }
+#endif
+
+  /* expand_string_for_rhs () leaves WORD quoted and does not perform
+     word splitting. */
+  l = *tword ? expand_string_for_rhs (tword,
+                                     (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) ? Q_PATQUOTE : quoted,
+                                     (int *)NULL, (int *)NULL)
+            : (WORD_LIST *)0;
+  free (tword);
+  pat = string_list (l);
+  dispose_words (l);
+  if (pat)
+    {
+      tword = quote_string_for_globbing (pat, QGLOB_CVTNULL);
+      free (pat);
+      pat = tword;
+    }
+  return (pat);
+}
+
+#if 0
+/* Handle removing a pattern from a string as a result of ${name%[%]value}
+   or ${name#[#]value}. */
+static char *
+variable_remove_pattern (value, pattern, patspec, quoted)
+     char *value, *pattern;
+     int patspec, quoted;
+{
+  char *tword;
+
+  tword = remove_pattern (value, pattern, patspec);
+
+  return (tword);
+}
+#endif
+
+static char *
+list_remove_pattern (list, pattern, patspec, itype, quoted)
+     WORD_LIST *list;
+     char *pattern;
+     int patspec, itype, quoted;
+{
+  WORD_LIST *new, *l;
+  WORD_DESC *w;
+  char *tword;
+
+  for (new = (WORD_LIST *)NULL, l = list; l; l = l->next)
+    {
+      tword = remove_pattern (l->word->word, pattern, patspec);
+      w = make_bare_word (tword);
+      FREE (tword);
+      new = make_word_list (w, new);
+    }
+
+  l = REVERSE_LIST (new, WORD_LIST *);
+  if (itype == '*')
+#if 0
+    tword = (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) ? string_list_dollar_star (l) : string_list (l);
+#else
+    tword = (quoted & Q_DOUBLE_QUOTES) ? string_list_dollar_star (l) : string_list (l);
+#endif
+  else
+    tword = string_list ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) ? quote_list (l) : l);
+
+  dispose_words (l);
+  return (tword);
+}
+
+static char *
+parameter_list_remove_pattern (itype, pattern, patspec, quoted)
+     int itype;
+     char *pattern;
+     int patspec, quoted;
+{
+  char *ret;
+  WORD_LIST *list;
+
+  list = list_rest_of_args ();
+  if (list == 0)
+    return ((char *)NULL);
+  ret = list_remove_pattern (list, pattern, patspec, itype, quoted);
+  dispose_words (list);
+  return (ret);
+}
+
+#if defined (ARRAY_VARS)
+static char *
+array_remove_pattern (a, pattern, patspec, varname, quoted)
+     ARRAY *a;
+     char *pattern;
+     int patspec;
+     char *varname;    /* so we can figure out how it's indexed */
+     int quoted;
+{
+  int itype;
+  char *ret;
+  WORD_LIST *list;
+  SHELL_VAR *v;
+
+  /* compute itype from varname here */
+  v = array_variable_part (varname, &ret, 0);
+  itype = ret[0];
+
+  list = array_to_word_list (a);
+  if (list == 0)
+   return ((char *)NULL);
+  ret = list_remove_pattern (list, pattern, patspec, itype, quoted);
+  dispose_words (list);
+
+  return ret;
+}
+#endif /* ARRAY_VARS */
+
+static char *
+parameter_brace_remove_pattern (varname, value, patstr, rtype, quoted)
+     char *varname, *value, *patstr;
+     int rtype, quoted;
+{
+  int vtype, patspec, starsub;
+  char *temp1, *val, *pattern;
+  SHELL_VAR *v;
+
+  if (value == 0)
+    return ((char *)NULL);
+
+  this_command_name = varname;
+
+  vtype = get_var_and_type (varname, value, quoted, &v, &val);
+  if (vtype == -1)
+    return ((char *)NULL);
+
+  starsub = vtype & VT_STARSUB;
+  vtype &= ~VT_STARSUB;
+
+  patspec = getpatspec (rtype, patstr);
+  if (patspec == RP_LONG_LEFT || patspec == RP_LONG_RIGHT)
+    patstr++;
+
+  pattern = getpattern (patstr, quoted, 1);
+
+  temp1 = (char *)NULL;                /* shut up gcc */
+  switch (vtype)
+    {
+    case VT_VARIABLE:
+    case VT_ARRAYMEMBER:
+      temp1 = remove_pattern (val, pattern, patspec);
+      if (vtype == VT_VARIABLE)
+       FREE (val);
+      if (temp1)
+       {
+         val = quote_escapes (temp1);
+         free (temp1);
+         temp1 = val;
+       }
+      break;
+#if defined (ARRAY_VARS)
+    case VT_ARRAYVAR:
+      temp1 = array_remove_pattern (array_cell (v), pattern, patspec, varname, quoted);
+      if (temp1 && ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) == 0))
+       {
+         val = quote_escapes (temp1);
+         free (temp1);
+         temp1 = val;
+       }
+      break;
+#endif
+    case VT_POSPARMS:
+      temp1 = parameter_list_remove_pattern (varname[0], pattern, patspec, quoted);
+      if (temp1 && ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) == 0))
+       {
+         val = quote_escapes (temp1);
+         free (temp1);
+         temp1 = val;
+       }
+      break;
+    }
+
+  FREE (pattern);
+  return temp1;
+}    
+
+/*******************************************
+ *                                        *
+ *     Functions to expand WORD_DESCs     *
+ *                                        *
+ *******************************************/
+
+/* Expand WORD, performing word splitting on the result.  This does
+   parameter expansion, command substitution, arithmetic expansion,
+   word splitting, and quote removal. */
+
+WORD_LIST *
+expand_word (word, quoted)
+     WORD_DESC *word;
+     int quoted;
+{
+  WORD_LIST *result, *tresult;
+
+  tresult = call_expand_word_internal (word, quoted, 0, (int *)NULL, (int *)NULL);
+  result = word_list_split (tresult);
+  dispose_words (tresult);
+  return (result ? dequote_list (result) : result);
+}
+
+/* Expand WORD, but do not perform word splitting on the result.  This
+   does parameter expansion, command substitution, arithmetic expansion,
+   and quote removal. */
+WORD_LIST *
+expand_word_unsplit (word, quoted)
+     WORD_DESC *word;
+     int quoted;
+{
+  WORD_LIST *result;
+
+  expand_no_split_dollar_star = 1;
+  result = call_expand_word_internal (word, quoted, 0, (int *)NULL, (int *)NULL);
+  expand_no_split_dollar_star = 0;
+
+  return (result ? dequote_list (result) : result);
+}
+
+/* Perform shell expansions on WORD, but do not perform word splitting or
+   quote removal on the result. */
+WORD_LIST *
+expand_word_leave_quoted (word, quoted)
+     WORD_DESC *word;
+     int quoted;
+{
+  return (call_expand_word_internal (word, quoted, 0, (int *)NULL, (int *)NULL));
+}
+
+#if defined (PROCESS_SUBSTITUTION)
+
+/*****************************************************************/
+/*                                                              */
+/*                 Hacking Process Substitution                 */
+/*                                                              */
+/*****************************************************************/
+
+#if !defined (HAVE_DEV_FD)
+/* Named pipes must be removed explicitly with `unlink'.  This keeps a list
+   of FIFOs the shell has open.  unlink_fifo_list will walk the list and
+   unlink all of them. add_fifo_list adds the name of an open FIFO to the
+   list.  NFIFO is a count of the number of FIFOs in the list. */
+#define FIFO_INCR 20
+
+struct temp_fifo {
+  char *file;
+  pid_t proc;
+};
+
+static struct temp_fifo *fifo_list = (struct temp_fifo *)NULL;
+static int nfifo;
+static int fifo_list_size;
+
+static void
+add_fifo_list (pathname)
+     char *pathname;
+{
+  if (nfifo >= fifo_list_size - 1)
+    {
+      fifo_list_size += FIFO_INCR;
+      fifo_list = (struct temp_fifo *)xrealloc (fifo_list,
+                               fifo_list_size * sizeof (struct temp_fifo));
+    }
+
+  fifo_list[nfifo].file = savestring (pathname);
+  nfifo++;
+}
+
+void
+unlink_fifo_list ()
+{
+  int saved, i, j;
+
+  if (nfifo == 0)
+    return;
+
+  for (i = saved = 0; i < nfifo; i++)
+    {
+      if ((fifo_list[i].proc == -1) || (kill(fifo_list[i].proc, 0) == -1))
+       {
+         unlink (fifo_list[i].file);
+         free (fifo_list[i].file);
+         fifo_list[i].file = (char *)NULL;
+         fifo_list[i].proc = -1;
+       }
+      else
+       saved++;
+    }
+
+  /* If we didn't remove some of the FIFOs, compact the list. */
+  if (saved)
+    {
+      for (i = j = 0; i < nfifo; i++)
+       if (fifo_list[i].file)
+         {
+           fifo_list[j].file = fifo_list[i].file;
+           fifo_list[j].proc = fifo_list[i].proc;
+           j++;
+         }
+      nfifo = j;
+    }
+  else
+    nfifo = 0;
+}
+
+static char *
+make_named_pipe ()
+{
+  char *tname;
+
+  tname = sh_mktmpname ("sh-np", MT_USERANDOM);
+  if (mkfifo (tname, 0600) < 0)
+    {
+      free (tname);
+      return ((char *)NULL);
+    }
+
+  add_fifo_list (tname);
+  return (tname);
+}
+
+#else /* HAVE_DEV_FD */
+
+/* DEV_FD_LIST is a bitmap of file descriptors attached to pipes the shell
+   has open to children.  NFDS is a count of the number of bits currently
+   set in DEV_FD_LIST.  TOTFDS is a count of the highest possible number
+   of open files. */
+static char *dev_fd_list = (char *)NULL;
+static int nfds;
+static int totfds;     /* The highest possible number of open files. */
+
+static void
+add_fifo_list (fd)
+     int fd;
+{
+  if (!dev_fd_list || fd >= totfds)
+    {
+      int ofds;
+
+      ofds = totfds;
+      totfds = getdtablesize ();
+      if (totfds < 0 || totfds > 256)
+       totfds = 256;
+      if (fd > totfds)
+       totfds = fd + 2;
+
+      dev_fd_list = (char *)xrealloc (dev_fd_list, totfds);
+      memset (dev_fd_list + ofds, '\0', totfds - ofds);
+    }
+
+  dev_fd_list[fd] = 1;
+  nfds++;
+}
+
+void
+unlink_fifo_list ()
+{
+  register int i;
+
+  if (nfds == 0)
+    return;
+
+  for (i = 0; nfds && i < totfds; i++)
+    if (dev_fd_list[i])
+      {
+       close (i);
+       dev_fd_list[i] = 0;
+       nfds--;
+      }
+
+  nfds = 0;
+}
+
+#if defined (NOTDEF)
+print_dev_fd_list ()
+{
+  register int i;
+
+  fprintf (stderr, "pid %ld: dev_fd_list:", (long)getpid ());
+  fflush (stderr);
+
+  for (i = 0; i < totfds; i++)
+    {
+      if (dev_fd_list[i])
+       fprintf (stderr, " %d", i);
+    }
+  fprintf (stderr, "\n");
+}
+#endif /* NOTDEF */
+
+static char *
+make_dev_fd_filename (fd)
+     int fd;
+{
+  char *ret, intbuf[INT_STRLEN_BOUND (int) + 1], *p;
+
+  ret = (char *)xmalloc (sizeof (DEV_FD_PREFIX) + 4);
+
+  strcpy (ret, DEV_FD_PREFIX);
+  p = inttostr (fd, intbuf, sizeof (intbuf));
+  strcpy (ret + sizeof (DEV_FD_PREFIX) - 1, p);
+
+  add_fifo_list (fd);
+  return (ret);
+}
+
+#endif /* HAVE_DEV_FD */
+
+/* Return a filename that will open a connection to the process defined by
+   executing STRING.  HAVE_DEV_FD, if defined, means open a pipe and return
+   a filename in /dev/fd corresponding to a descriptor that is one of the
+   ends of the pipe.  If not defined, we use named pipes on systems that have
+   them.  Systems without /dev/fd and named pipes are out of luck.
+
+   OPEN_FOR_READ_IN_CHILD, if 1, means open the named pipe for reading or
+   use the read end of the pipe and dup that file descriptor to fd 0 in
+   the child.  If OPEN_FOR_READ_IN_CHILD is 0, we open the named pipe for
+   writing or use the write end of the pipe in the child, and dup that
+   file descriptor to fd 1 in the child.  The parent does the opposite. */
+
+static char *
+process_substitute (string, open_for_read_in_child)
+     char *string;
+     int open_for_read_in_child;
+{
+  char *pathname;
+  int fd, result;
+  pid_t old_pid, pid;
+#if defined (HAVE_DEV_FD)
+  int parent_pipe_fd, child_pipe_fd;
+  int fildes[2];
+#endif /* HAVE_DEV_FD */
+#if defined (JOB_CONTROL)
+  pid_t old_pipeline_pgrp;
+#endif
+
+  if (!string || !*string || wordexp_only)
+    return ((char *)NULL);
+
+#if !defined (HAVE_DEV_FD)
+  pathname = make_named_pipe ();
+#else /* HAVE_DEV_FD */
+  if (pipe (fildes) < 0)
+    {
+      sys_error (_("cannot make pipe for process substitution"));
+      return ((char *)NULL);
+    }
+  /* If OPEN_FOR_READ_IN_CHILD == 1, we want to use the write end of
+     the pipe in the parent, otherwise the read end. */
+  parent_pipe_fd = fildes[open_for_read_in_child];
+  child_pipe_fd = fildes[1 - open_for_read_in_child];
+  /* Move the parent end of the pipe to some high file descriptor, to
+     avoid clashes with FDs used by the script. */
+  parent_pipe_fd = move_to_high_fd (parent_pipe_fd, 1, 64);
+
+  pathname = make_dev_fd_filename (parent_pipe_fd);
+#endif /* HAVE_DEV_FD */
+
+  if (!pathname)
+    {
+      sys_error (_("cannot make pipe for process substitution"));
+      return ((char *)NULL);
+    }
+
+  old_pid = last_made_pid;
+
+#if defined (JOB_CONTROL)
+  old_pipeline_pgrp = pipeline_pgrp;
+  pipeline_pgrp = shell_pgrp;
+  save_pipeline (1);
+#endif /* JOB_CONTROL */
+
+  pid = make_child ((char *)NULL, 1);
+  if (pid == 0)
+    {
+      reset_terminating_signals ();    /* XXX */
+      free_pushed_string_input ();
+      /* Cancel traps, in trap.c. */
+      restore_original_signals ();
+      setup_async_signals ();
+      subshell_environment |= SUBSHELL_COMSUB;
+    }
+
+#if defined (JOB_CONTROL)
+  set_sigchld_handler ();
+  stop_making_children ();
+  pipeline_pgrp = old_pipeline_pgrp;
+#endif /* JOB_CONTROL */
+
+  if (pid < 0)
+    {
+      sys_error (_("cannot make child for process substitution"));
+      free (pathname);
+#if defined (HAVE_DEV_FD)
+      close (parent_pipe_fd);
+      close (child_pipe_fd);
+#endif /* HAVE_DEV_FD */
+      return ((char *)NULL);
+    }
+
+  if (pid > 0)
+    {
+#if defined (JOB_CONTROL)
+      restore_pipeline (1);
+#endif
+
+#if !defined (HAVE_DEV_FD)
+      fifo_list[nfifo-1].proc = pid;
+#endif
+
+      last_made_pid = old_pid;
+
+#if defined (JOB_CONTROL) && defined (PGRP_PIPE)
+      close_pgrp_pipe ();
+#endif /* JOB_CONTROL && PGRP_PIPE */
+
+#if defined (HAVE_DEV_FD)
+      close (child_pipe_fd);
+#endif /* HAVE_DEV_FD */
+
+      return (pathname);
+    }
+
+  set_sigint_handler ();
+
+#if defined (JOB_CONTROL)
+  set_job_control (0);
+#endif /* JOB_CONTROL */
+
+#if !defined (HAVE_DEV_FD)
+  /* Open the named pipe in the child. */
+  fd = open (pathname, open_for_read_in_child ? O_RDONLY|O_NONBLOCK : O_WRONLY);
+  if (fd < 0)
+    {
+      /* Two separate strings for ease of translation. */
+      if (open_for_read_in_child)
+       sys_error (_("cannot open named pipe %s for reading"), pathname);
+      else
+       sys_error (_("cannot open named pipe %s for writing"), pathname);
+
+      exit (127);
+    }
+  if (open_for_read_in_child)
+    {
+      if (sh_unset_nodelay_mode (fd) < 0)
+       {
+         sys_error (_("cannout reset nodelay mode for fd %d"), fd);
+         exit (127);
+       }
+    }
+#else /* HAVE_DEV_FD */
+  fd = child_pipe_fd;
+#endif /* HAVE_DEV_FD */
+
+  if (dup2 (fd, open_for_read_in_child ? 0 : 1) < 0)
+    {
+      sys_error (_("cannot duplicate named pipe %s as fd %d"), pathname,
+       open_for_read_in_child ? 0 : 1);
+      exit (127);
+    }
+
+  if (fd != (open_for_read_in_child ? 0 : 1))
+    close (fd);
+
+  /* Need to close any files that this process has open to pipes inherited
+     from its parent. */
+  if (current_fds_to_close)
+    {
+      close_fd_bitmap (current_fds_to_close);
+      current_fds_to_close = (struct fd_bitmap *)NULL;
+    }
+
+#if defined (HAVE_DEV_FD)
+  /* Make sure we close the parent's end of the pipe and clear the slot
+     in the fd list so it is not closed later, if reallocated by, for
+     instance, pipe(2). */
+  close (parent_pipe_fd);
+  dev_fd_list[parent_pipe_fd] = 0;
+#endif /* HAVE_DEV_FD */
+
+  result = parse_and_execute (string, "process substitution", (SEVAL_NONINT|SEVAL_NOHIST));
+
+#if !defined (HAVE_DEV_FD)
+  /* Make sure we close the named pipe in the child before we exit. */
+  close (open_for_read_in_child ? 0 : 1);
+#endif /* !HAVE_DEV_FD */
+
+  exit (result);
+  /*NOTREACHED*/
+}
+#endif /* PROCESS_SUBSTITUTION */
+
+/***********************************/
+/*                                */
+/*     Command Substitution       */
+/*                                */
+/***********************************/
+
+static char *
+read_comsub (fd, quoted)
+     int fd, quoted;
+{
+  char *istring, buf[128], *bufp;
+  int istring_index, istring_size, c;
+  ssize_t bufn;
+
+  istring = (char *)NULL;
+  istring_index = istring_size = bufn = 0;
+
+#ifdef __CYGWIN__
+  setmode (fd, O_TEXT);                /* we don't want CR/LF, we want Unix-style */
+#endif
+
+  /* Read the output of the command through the pipe. */
+  while (1)
+    {
+      if (fd < 0)
+       break;
+      if (--bufn <= 0)
+       {
+         bufn = zread (fd, buf, sizeof (buf));
+         if (bufn <= 0) 
+           break;
+         bufp = buf;
+       }
+      c = *bufp++;
+
+      if (c == 0)
+       {
+#if 0
+         internal_warning ("read_comsub: ignored null byte in input");
+#endif
+         continue;
+       }
+
+      /* Add the character to ISTRING, possibly after resizing it. */
+      RESIZE_MALLOCED_BUFFER (istring, istring_index, 2, istring_size, DEFAULT_ARRAY_SIZE);
+
+      if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) || c == CTLESC || c == CTLNUL)
+       istring[istring_index++] = CTLESC;
+
+      istring[istring_index++] = c;
+
+#if 0
+#if defined (__CYGWIN__)
+      if (c == '\n' && istring_index > 1 && istring[istring_index - 2] == '\r')
+       {
+         istring_index--;
+         istring[istring_index - 1] = '\n';
+       }
+#endif
+#endif
+    }
+
+  if (istring)
+    istring[istring_index] = '\0';
+
+  /* If we read no output, just return now and save ourselves some
+     trouble. */
+  if (istring_index == 0)
+    {
+      FREE (istring);
+      return (char *)NULL;
+    }
+
+  /* Strip trailing newlines from the output of the command. */
+  if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))
+    {
+      while (istring_index > 0)
+       {
+         if (istring[istring_index - 1] == '\n')
+           {
+             --istring_index;
+
+             /* If the newline was quoted, remove the quoting char. */
+             if (istring[istring_index - 1] == CTLESC)
+               --istring_index;
+           }
+         else
+           break;
+       }
+      istring[istring_index] = '\0';
+    }
+  else
+    strip_trailing (istring, istring_index - 1, 1);
+
+  return istring;
+}
+
+/* Perform command substitution on STRING.  This returns a string,
+   possibly quoted. */
+char *
+command_substitute (string, quoted)
+     char *string;
+     int quoted;
+{
+  pid_t pid, old_pid, old_pipeline_pgrp;
+  char *istring;
+  int result, fildes[2], function_value, pflags, rc;
+
+  istring = (char *)NULL;
+
+  /* Don't fork () if there is no need to.  In the case of no command to
+     run, just return NULL. */
+  if (!string || !*string || (string[0] == '\n' && !string[1]))
+    return ((char *)NULL);
+
+  if (wordexp_only && read_but_dont_execute)
+    {
+      last_command_exit_value = 125;
+      jump_to_top_level (EXITPROG);
+    }
+
+  /* We're making the assumption here that the command substitution will
+     eventually run a command from the file system.  Since we'll run
+     maybe_make_export_env in this subshell before executing that command,
+     the parent shell and any other shells it starts will have to remake
+     the environment.  If we make it before we fork, other shells won't
+     have to.  Don't bother if we have any temporary variable assignments,
+     though, because the export environment will be remade after this
+     command completes anyway, but do it if all the words to be expanded
+     are variable assignments. */
+  if (subst_assign_varlist == 0 || garglist == 0)
+    maybe_make_export_env ();  /* XXX */
+
+  /* Flags to pass to parse_and_execute() */
+  pflags = interactive ? SEVAL_RESETLINE : 0;
+
+  /* Pipe the output of executing STRING into the current shell. */
+  if (pipe (fildes) < 0)
+    {
+      sys_error (_("cannot make pipe for command substitution"));
+      goto error_exit;
+    }
+
+  old_pid = last_made_pid;
+#if defined (JOB_CONTROL)
+  old_pipeline_pgrp = pipeline_pgrp;
+  /* Don't reset the pipeline pgrp if we're already a subshell in a pipeline. */
+  if ((subshell_environment & SUBSHELL_PIPE) == 0)
+    pipeline_pgrp = shell_pgrp;
+  cleanup_the_pipeline ();
+#endif
+
+  pid = make_child ((char *)NULL, 0);
+  if (pid == 0)
+    /* Reset the signal handlers in the child, but don't free the
+       trap strings. */
+    reset_signal_handlers ();
+
+#if defined (JOB_CONTROL)
+  set_sigchld_handler ();
+  stop_making_children ();
+  pipeline_pgrp = old_pipeline_pgrp;
+#else
+  stop_making_children ();
+#endif /* JOB_CONTROL */
+
+  if (pid < 0)
+    {
+      sys_error (_("cannot make child for command substitution"));
+    error_exit:
+
+      FREE (istring);
+      close (fildes[0]);
+      close (fildes[1]);
+      return ((char *)NULL);
+    }
+
+  if (pid == 0)
+    {
+      set_sigint_handler ();   /* XXX */
+
+      free_pushed_string_input ();
+
+      if (dup2 (fildes[1], 1) < 0)
+       {
+         sys_error (_("command_substitute: cannot duplicate pipe as fd 1"));
+         exit (EXECUTION_FAILURE);
+       }
+
+      /* If standard output is closed in the parent shell
+        (such as after `exec >&-'), file descriptor 1 will be
+        the lowest available file descriptor, and end up in
+        fildes[0].  This can happen for stdin and stderr as well,
+        but stdout is more important -- it will cause no output
+        to be generated from this command. */
+      if ((fildes[1] != fileno (stdin)) &&
+         (fildes[1] != fileno (stdout)) &&
+         (fildes[1] != fileno (stderr)))
+       close (fildes[1]);
+
+      if ((fildes[0] != fileno (stdin)) &&
+         (fildes[0] != fileno (stdout)) &&
+         (fildes[0] != fileno (stderr)))
+       close (fildes[0]);
+
+      /* The currently executing shell is not interactive. */
+      interactive = 0;
+
+      /* This is a subshell environment. */
+      subshell_environment |= SUBSHELL_COMSUB;
+
+      /* When not in POSIX mode, command substitution does not inherit
+        the -e flag. */
+      if (posixly_correct == 0)
+       exit_immediately_on_error = 0;
+
+      remove_quoted_escapes (string);
+
+      startup_state = 2;       /* see if we can avoid a fork */
+      /* Give command substitution a place to jump back to on failure,
+        so we don't go back up to main (). */
+      result = setjmp (top_level);
+
+      /* If we're running a command substitution inside a shell function,
+        trap `return' so we don't return from the function in the subshell
+        and go off to never-never land. */
+      if (result == 0 && return_catch_flag)
+       function_value = setjmp (return_catch);
+      else
+       function_value = 0;
+
+      if (result == ERREXIT)
+       rc = last_command_exit_value;
+      else if (result == EXITPROG)
+       rc = last_command_exit_value;
+      else if (result)
+       rc = EXECUTION_FAILURE;
+      else if (function_value)
+       rc = return_catch_value;
+      else
+       {
+         subshell_level++;
+         rc = parse_and_execute (string, "command substitution", pflags|SEVAL_NOHIST);
+         subshell_level--;
+       }
+
+      last_command_exit_value = rc;
+      rc = run_exit_trap ();
+      exit (rc);
+    }
+  else
+    {
+#if defined (JOB_CONTROL) && defined (PGRP_PIPE)
+      close_pgrp_pipe ();
+#endif /* JOB_CONTROL && PGRP_PIPE */
+
+      close (fildes[1]);
+
+      istring = read_comsub (fildes[0], quoted);
+
+      close (fildes[0]);
+
+      current_command_subst_pid = pid;
+      last_command_exit_value = wait_for (pid);
+      last_command_subst_pid = pid;
+      last_made_pid = old_pid;
+
+#if defined (JOB_CONTROL)
+      /* If last_command_exit_value > 128, then the substituted command
+        was terminated by a signal.  If that signal was SIGINT, then send
+        SIGINT to ourselves.  This will break out of loops, for instance. */
+      if (last_command_exit_value == (128 + SIGINT) && last_command_exit_signal == SIGINT)
+       kill (getpid (), SIGINT);
+
+      /* wait_for gives the terminal back to shell_pgrp.  If some other
+        process group should have it, give it away to that group here.
+        pipeline_pgrp is non-zero only while we are constructing a
+        pipline, so what we are concerned about is whether or not that
+        pipeline was started in the background.  A pipeline started in
+        the background should never get the tty back here. */
+#if 0
+      if (interactive && pipeline_pgrp != (pid_t)0 && pipeline_pgrp != last_asynchronous_pid)
+#else
+      if (interactive && pipeline_pgrp != (pid_t)0 && (subshell_environment & SUBSHELL_ASYNC) == 0)
+#endif
+       give_terminal_to (pipeline_pgrp, 0);
+#endif /* JOB_CONTROL */
+
+      return (istring);
+    }
+}
+
+/********************************************************
+ *                                                     *
+ *     Utility functions for parameter expansion       *
+ *                                                     *
+ ********************************************************/
+
+#if defined (ARRAY_VARS)
+
+static arrayind_t
+array_length_reference (s)
+     char *s;
+{
+  int len;
+  arrayind_t ind;
+  char *t, c;
+  ARRAY *array;
+  SHELL_VAR *var;
+
+  var = array_variable_part (s, &t, &len);
+
+  /* If unbound variables should generate an error, report one and return
+     failure. */
+  if ((var == 0 || array_p (var) == 0) && unbound_vars_is_error)
+    {
+      c = *--t;
+      *t = '\0';
+      err_unboundvar (s);
+      *t = c;
+      return (-1);
+    }
+  else if (var == 0)
+    return 0;
+
+  /* We support a couple of expansions for variables that are not arrays.
+     We'll return the length of the value for v[0], and 1 for v[@] or
+     v[*].  Return 0 for everything else. */
+
+  array = array_p (var) ? array_cell (var) : (ARRAY *)NULL;
+
+  if (ALL_ELEMENT_SUB (t[0]) && t[1] == ']')
+    return (array_p (var) ? array_num_elements (array) : 1);
+
+  ind = array_expand_index (t, len);
+  if (ind < 0)
+    {
+      err_badarraysub (t);
+      return (-1);
+    }
+
+  if (array_p (var))
+    t = array_reference (array, ind);
+  else
+    t = (ind == 0) ? value_cell (var) : (char *)NULL;
+
+  len = STRLEN (t);
+  return (len);
+}
+#endif /* ARRAY_VARS */
+
+static int
+valid_brace_expansion_word (name, var_is_special)
+     char *name;
+     int var_is_special;
+{
+  if (DIGIT (*name) && all_digits (name))
+    return 1;
+  else if (var_is_special)
+    return 1;
+#if defined (ARRAY_VARS)
+  else if (valid_array_reference (name))
+    return 1;
+#endif /* ARRAY_VARS */
+  else if (legal_identifier (name))
+    return 1;
+  else
+    return 0;
+}
+
+static int
+chk_atstar (name, quoted, quoted_dollar_atp, contains_dollar_at)
+     char *name;
+     int quoted;
+     int *quoted_dollar_atp, *contains_dollar_at;
+{
+  char *temp1;
+
+  if (name == 0)
+    {
+      if (quoted_dollar_atp)
+       *quoted_dollar_atp = 0;
+      if (contains_dollar_at)
+       *contains_dollar_at = 0;
+      return 0;
+    }
+
+  /* check for $@ and $* */
+  if (name[0] == '@' && name[1] == 0)
+    {
+      if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp)
+       *quoted_dollar_atp = 1;
+      if (contains_dollar_at)
+       *contains_dollar_at = 1;
+      return 1;
+    }
+  else if (name[0] == '*' && name[1] == '\0' && quoted == 0)
+    {
+      if (contains_dollar_at)
+       *contains_dollar_at = 1;
+      return 1;
+    }
+
+  /* Now check for ${array[@]} and ${array[*]} */
+#if defined (ARRAY_VARS)
+  else if (valid_array_reference (name))
+    {
+      temp1 = xstrchr (name, '[');
+      if (temp1 && temp1[1] == '@' && temp1[2] == ']')
+       {
+         if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp)
+           *quoted_dollar_atp = 1;
+         if (contains_dollar_at)
+           *contains_dollar_at = 1;
+         return 1;
+       }       /* [ */
+      /* ${array[*]}, when unquoted, should be treated like ${array[@]},
+        which should result in separate words even when IFS is unset. */
+      if (temp1 && temp1[1] == '*' && temp1[2] == ']' && quoted == 0)
+       {
+         if (contains_dollar_at)
+           *contains_dollar_at = 1;
+         return 1;
+       }
+    }
+#endif
+  return 0;
+}
+
+/* Parameter expand NAME, and return a new string which is the expansion,
+   or NULL if there was no expansion.
+   VAR_IS_SPECIAL is non-zero if NAME is one of the special variables in
+   the shell, e.g., "@", "$", "*", etc.  QUOTED, if non-zero, means that
+   NAME was found inside of a double-quoted expression. */
+static char *
+parameter_brace_expand_word (name, var_is_special, quoted)
+     char *name;
+     int var_is_special, quoted;
+{
+  char *temp, *tt;
+  intmax_t arg_index;
+  SHELL_VAR *var;
+  int atype;
+
+  /* Handle multiple digit arguments, as in ${11}. */
+  
+  if (legal_number (name, &arg_index))
+    {
+      tt = get_dollar_var_value (arg_index);
+      if (tt)
+       temp = (*tt && (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)))
+                 ? quote_string (tt)
+                 : quote_escapes (tt);
+      else
+        temp = (char *)NULL;
+      FREE (tt);
+    }
+  else if (var_is_special)      /* ${@} */
+    {
+      int sindex;
+      tt = (char *)xmalloc (2 + strlen (name));
+      tt[sindex = 0] = '$';
+      strcpy (tt + 1, name);
+
+      temp = param_expand (tt, &sindex, quoted, (int *)NULL, (int *)NULL,
+                          (int *)NULL, (int *)NULL, 0);
+      free (tt);
+    }
+#if defined (ARRAY_VARS)
+  else if (valid_array_reference (name))
+    {
+      temp = array_value (name, quoted, &atype);
+      if (atype == 0 && temp)
+       temp = (*temp && (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)))
+                 ? quote_string (temp)
+                 : quote_escapes (temp);
+    }
+#endif
+  else if (var = find_variable (name))
+    {
+      if (var_isset (var) && invisible_p (var) == 0)
+       {
+#if defined (ARRAY_VARS)
+         temp = array_p (var) ? array_reference (array_cell (var), 0) : value_cell (var);
+#else
+         temp = value_cell (var);
+#endif
+
+         if (temp)
+           temp = (*temp && (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)))
+                     ? quote_string (temp)
+                     : quote_escapes (temp);
+       }
+      else
+       temp = (char *)NULL;
+    }
+  else
+    temp = (char *)NULL;
+
+  return (temp);
+}
+
+/* Expand an indirect reference to a variable: ${!NAME} expands to the
+   value of the variable whose name is the value of NAME. */
+static char *
+parameter_brace_expand_indir (name, var_is_special, quoted, quoted_dollar_atp, contains_dollar_at)
+     char *name;
+     int var_is_special, quoted;
+     int *quoted_dollar_atp, *contains_dollar_at;
+{
+  char *temp, *t;
+
+  t = parameter_brace_expand_word (name, var_is_special, quoted);
+  /* Have to dequote here if necessary */
+  if (t)
+    {
+      temp = (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))
+               ? dequote_string (t)
+               : dequote_escapes (t);
+      free (t);
+      t = temp;
+    }
+  chk_atstar (t, quoted, quoted_dollar_atp, contains_dollar_at);
+  if (t == 0)
+    return (t);
+  temp = parameter_brace_expand_word (t, SPECIAL_VAR(t, 0), quoted);
+  free (t);
+  return temp;
+}
+
+/* Expand the right side of a parameter expansion of the form ${NAMEcVALUE},
+   depending on the value of C, the separating character.  C can be one of
+   "-", "+", or "=".  QUOTED is true if the entire brace expression occurs
+   between double quotes. */
+static char *
+parameter_brace_expand_rhs (name, value, c, quoted, qdollaratp, hasdollarat)
+     char *name, *value;
+     int c, quoted, *qdollaratp, *hasdollarat;
+{
+  WORD_LIST *l;
+  char *t, *t1, *temp;
+  int hasdol;
+
+  /* XXX - Should we tilde expand in an assignment context if C is `='? */
+  if (*value == '~')
+    temp = bash_tilde_expand (value, 0);
+  else if (xstrchr (value, '~')  && unquoted_substring ("=~", value))
+    temp = bash_tilde_expand (value, 1);
+  else
+    temp = savestring (value);
+
+  /* If the entire expression is between double quotes, we want to treat
+     the value as a double-quoted string, with the exception that we strip
+     embedded unescaped double quotes. */
+  if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && *temp)
+    {
+      hasdol = 0;
+      t = string_extract_double_quoted (temp, &hasdol, 1);
+      free (temp);
+      temp = t;
+    }
+
+  hasdol = 0;
+  /* XXX was 0 not quoted */
+  l = *temp ? expand_string_for_rhs (temp, quoted, &hasdol, (int *)NULL)
+           : (WORD_LIST *)0;
+  if (hasdollarat)
+    *hasdollarat = hasdol || (l && l->next);
+  free (temp);
+  if (l)
+    {
+      /* The expansion of TEMP returned something.  We need to treat things
+         slightly differently if HASDOL is non-zero.  If we have "$@", the
+         individual words have already been quoted.  We need to turn them
+         into a string with the words separated by the first character of
+         $IFS without any additional quoting, so string_list_dollar_at won't
+         do the right thing.  We use string_list_dollar_star instead. */
+      temp = (hasdol || l->next) ? string_list_dollar_star (l) : string_list (l);
+
+      /* If l->next is not null, we know that TEMP contained "$@", since that
+        is the only expansion that creates more than one word. */
+      if (qdollaratp && ((hasdol && quoted) || l->next))
+       *qdollaratp = 1;
+      dispose_words (l);
+    }
+  else if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && hasdol)
+    {
+      /* The brace expansion occurred between double quotes and there was
+        a $@ in TEMP.  It does not matter if the $@ is quoted, as long as
+        it does not expand to anything.  In this case, we want to return
+        a quoted empty string. */
+      temp = (char *)xmalloc (2);
+      temp[0] = CTLNUL;
+      temp[1] = '\0';
+    }
+  else
+    temp = (char *)NULL;
+
+  if (c == '-' || c == '+')
+    return (temp);
+
+  /* c == '=' */
+  t = temp ? savestring (temp) : savestring ("");
+  t1 = dequote_string (t);
+  free (t);
+#if defined (ARRAY_VARS)
+  if (valid_array_reference (name))
+    assign_array_element (name, t1);
+  else
+#endif /* ARRAY_VARS */
+  bind_variable (name, t1);
+  free (t1);
+  return (temp);
+}
+
+/* Deal with the right hand side of a ${name:?value} expansion in the case
+   that NAME is null or not set.  If VALUE is non-null it is expanded and
+   used as the error message to print, otherwise a standard message is
+   printed. */
+static void
+parameter_brace_expand_error (name, value)
+     char *name, *value;
+{
+  WORD_LIST *l;
+  char *temp;
+
+  if (value && *value)
+    {
+      if (*value == '~')
+       temp = bash_tilde_expand (value, 0);
+      else if (xstrchr (value, '~')  && unquoted_substring ("=~", value))
+       temp = bash_tilde_expand (value, 1);
+      else
+       temp = savestring (value);
+
+      l = expand_string (temp, 0);
+      FREE (temp);
+      temp =  string_list (l);
+      report_error ("%s: %s", name, temp ? temp : ""); /* XXX was value not "" */
+      FREE (temp);
+      dispose_words (l);
+    }
+  else
+    report_error (_("%s: parameter null or not set"), name);
+
+  /* Free the data we have allocated during this expansion, since we
+     are about to longjmp out. */
+  free (name);
+  FREE (value);
+}
+
+/* Return 1 if NAME is something for which parameter_brace_expand_length is
+   OK to do. */
+static int
+valid_length_expression (name)
+     char *name;
+{
+  return (name[1] == '\0' ||                                   /* ${#} */
+         ((sh_syntaxtab[(unsigned char) name[1]] & CSPECVAR) && name[2] == '\0') ||  /* special param */
+         (DIGIT (name[1]) && all_digits (name + 1)) || /* ${#11} */
+#if defined (ARRAY_VARS)
+         valid_array_reference (name + 1) ||                   /* ${#a[7]} */
+#endif
+         legal_identifier (name + 1));                         /* ${#PS1} */
+}
+
+#if defined (HANDLE_MULTIBYTE)
+size_t
+mbstrlen (s)
+     const char *s;
+{
+  size_t clen, nc;
+  mbstate_t mbs;
+
+  nc = 0;
+  memset (&mbs, 0, sizeof (mbs));
+  while ((clen = mbrlen(s, MB_CUR_MAX, &mbs)) != 0 && (MB_INVALIDCH(clen) == 0))
+    {
+      s += clen;
+      nc++;
+    }
+  return nc;
+}
+#endif
+      
+
+/* Handle the parameter brace expansion that requires us to return the
+   length of a parameter. */
+static intmax_t
+parameter_brace_expand_length (name)
+     char *name;
+{
+  char *t, *newname;
+  intmax_t number, arg_index;
+  WORD_LIST *list;
+#if defined (ARRAY_VARS)
+  SHELL_VAR *var;
+#endif
+
+  if (name[1] == '\0')                 /* ${#} */
+    number = number_of_args ();
+  else if ((name[1] == '@' || name[1] == '*') && name[2] == '\0')      /* ${#@}, ${#*} */
+    number = number_of_args ();
+  else if ((sh_syntaxtab[(unsigned char) name[1]] & CSPECVAR) && name[2] == '\0')
+    {
+      /* Take the lengths of some of the shell's special parameters. */
+      switch (name[1])
+       {
+       case '-':
+         t = which_set_flags ();
+         break;
+       case '?':
+         t = itos (last_command_exit_value);
+         break;
+       case '$':
+         t = itos (dollar_dollar_pid);
+         break;
+       case '!':
+         if (last_asynchronous_pid == NO_PID)
+           t = (char *)NULL;
+         else
+           t = itos (last_asynchronous_pid);
+         break;
+       case '#':
+         t = itos (number_of_args ());
+         break;
+       }
+      number = STRLEN (t);
+      FREE (t);
+    }
+#if defined (ARRAY_VARS)
+  else if (valid_array_reference (name + 1))
+    number = array_length_reference (name + 1);
+#endif /* ARRAY_VARS */
+  else
+    {
+      number = 0;
+
+      if (legal_number (name + 1, &arg_index))         /* ${#1} */
+       {
+         t = get_dollar_var_value (arg_index);
+         number = MB_STRLEN (t);
+         FREE (t);
+       }
+#if defined (ARRAY_VARS)
+      else if ((var = find_variable (name + 1)) && array_p (var))
+       {
+         t = array_reference (array_cell (var), 0);
+         number = MB_STRLEN (t);
+       }
+#endif
+      else                             /* ${#PS1} */
+       {
+         newname = savestring (name);
+         newname[0] = '$';
+         list = expand_string (newname, Q_DOUBLE_QUOTES);
+         t = list ? string_list (list) : (char *)NULL;
+         free (newname);
+         if (list)
+           dispose_words (list);
+
+         number = MB_STRLEN (t);
+         FREE (t);
+       }
+    }
+
+  return (number);
+}
+
+/* Skip characters in SUBSTR until DELIM.  SUBSTR is an arithmetic expression,
+   so we do some ad-hoc parsing of an arithmetic expression to find
+   the first DELIM, instead of using strchr(3).  Two rules:
+       1.  If the substring contains a `(', read until closing `)'.
+       2.  If the substring contains a `?', read past one `:' for each `?'.
+*/
+
+static char *
+skiparith (substr, delim)
+     char *substr;
+     int delim;
+{
+  size_t sublen;
+  int skipcol, pcount, i;
+  DECLARE_MBSTATE;
+
+  sublen = strlen (substr);
+  i = skipcol = pcount = 0;
+  while (substr[i])
+    {
+      /* Balance parens */
+      if (substr[i] == LPAREN)
+       {
+         pcount++;
+         i++;
+         continue;
+       }
+      if (substr[i] == RPAREN && pcount)
+       {
+         pcount--;
+         i++;
+         continue;
+       }
+      if (pcount)
+       {
+         ADVANCE_CHAR (substr, sublen, i);
+         continue;
+       }
+
+      /* Skip one `:' for each `?' */
+      if (substr[i] == ':' && skipcol)
+       {
+         skipcol--;
+         i++;
+         continue;
+       }
+      if (substr[i] == delim)
+       break;
+      if (substr[i] == '?')
+       {
+         skipcol++;
+         i++;
+         continue;
+       }
+      ADVANCE_CHAR (substr, sublen, i);
+    }
+
+  return (substr + i);
+}
+
+/* Verify and limit the start and end of the desired substring.  If
+   VTYPE == 0, a regular shell variable is being used; if it is 1,
+   then the positional parameters are being used; if it is 2, then
+   VALUE is really a pointer to an array variable that should be used.
+   Return value is 1 if both values were OK, 0 if there was a problem
+   with an invalid expression, or -1 if the values were out of range. */
+static int
+verify_substring_values (value, substr, vtype, e1p, e2p)
+     char *value, *substr;
+     int vtype;
+     intmax_t *e1p, *e2p;
+{
+  char *t, *temp1, *temp2;
+  arrayind_t len;
+  int expok;
+#if defined (ARRAY_VARS)
+ ARRAY *a;
+#endif
+
+  /* duplicate behavior of strchr(3) */
+  t = skiparith (substr, ':');
+  if (*t && *t == ':')
+    *t = '\0';
+  else
+    t = (char *)0;
+
+  temp1 = expand_string_if_necessary (substr, Q_DOUBLE_QUOTES, expand_string);
+  *e1p = evalexp (temp1, &expok);
+  free (temp1);
+  if (expok == 0)
+    return (0);
+
+  len = -1;    /* paranoia */
+  switch (vtype)
+    {
+    case VT_VARIABLE:
+    case VT_ARRAYMEMBER:
+      len = strlen (value);
+      break;
+    case VT_POSPARMS:
+      len = number_of_args () + 1;
+      break;
+#if defined (ARRAY_VARS)
+    case VT_ARRAYVAR:
+      a = (ARRAY *)value;
+      /* For arrays, the first value deals with array indices. */
+      len = array_max_index (a);       /* arrays index from 0 to n - 1 */
+      break;
+#endif
+    }
+
+  if (len == -1)       /* paranoia */
+    return -1;
+
+  if (*e1p < 0)                /* negative offsets count from end */
+    *e1p += len;
+
+  if (*e1p >= len || *e1p < 0)
+    return (-1);
+
+#if defined (ARRAY_VARS)
+  /* For arrays, the second offset deals with the number of elements. */
+  if (vtype == VT_ARRAYVAR)
+    len = array_num_elements (a);
+#endif
+
+  if (t)
+    {
+      t++;
+      temp2 = savestring (t);
+      temp1 = expand_string_if_necessary (temp2, Q_DOUBLE_QUOTES, expand_string);
+      free (temp2);
+      t[-1] = ':';
+      *e2p = evalexp (temp1, &expok);
+      free (temp1);
+      if (expok == 0)
+       return (0);
+      if (*e2p < 0)
+       {
+         internal_error (_("%s: substring expression < 0"), t);
+         return (0);
+       }
+#if defined (ARRAY_VARS)
+      /* In order to deal with sparse arrays, push the intelligence about how
+        to deal with the number of elements desired down to the array-
+        specific functions.  */
+      if (vtype != VT_ARRAYVAR)
+#endif
+       {
+         *e2p += *e1p;         /* want E2 chars starting at E1 */
+         if (*e2p > len)
+           *e2p = len;
+       }
+    }
+  else
+    *e2p = len;
+
+  return (1);
+}
+
+/* Return the type of variable specified by VARNAME (simple variable,
+   positional param, or array variable).  Also return the value specified
+   by VARNAME (value of a variable or a reference to an array element).
+   If this returns VT_VARIABLE, the caller assumes that CTLESC and CTLNUL
+   characters in the value are quoted with CTLESC and takes appropriate
+   steps.  For convenience, *VALP is set to the dequoted VALUE. */
+static int
+get_var_and_type (varname, value, quoted, varp, valp)
+     char *varname, *value;
+     int quoted;
+     SHELL_VAR **varp;
+     char **valp;
+{
+  int vtype;
+  char *temp;
+#if defined (ARRAY_VARS)
+  SHELL_VAR *v;
+#endif
+
+  /* This sets vtype to VT_VARIABLE or VT_POSPARMS */
+  vtype = (varname[0] == '@' || varname[0] == '*') && varname[1] == '\0';
+  if (vtype == VT_POSPARMS && varname[0] == '*')
+    vtype |= VT_STARSUB;
+  *varp = (SHELL_VAR *)NULL;
+
+#if defined (ARRAY_VARS)
+  if (valid_array_reference (varname))
+    {
+      v = array_variable_part (varname, &temp, (int *)0);
+      if (v && array_p (v))
+       { /* [ */
+         if (ALL_ELEMENT_SUB (temp[0]) && temp[1] == ']')
+           {
+             vtype = VT_ARRAYVAR;
+             if (temp[0] == '*')
+               vtype |= VT_STARSUB;
+             *valp = (char *)array_cell (v);
+           }
+         else
+           {
+             vtype = VT_ARRAYMEMBER;
+             *valp = array_value (varname, 1, (int *)NULL);
+           }
+         *varp = v;
+       }
+      else
+       return -1;
+    }
+  else if ((v = find_variable (varname)) && array_p (v))
+    {
+      vtype = VT_ARRAYMEMBER;
+      *varp = v;
+      *valp = array_reference (array_cell (v), 0);
+    }
+  else
+#endif
+#if 1
+    {
+      if (value && vtype == VT_VARIABLE)
+       {
+         if (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))
+           *valp = dequote_string (value);
+         else
+           *valp = dequote_escapes (value);
+       }
+      else
+       *valp = value;
+    }
+#else
+  *valp = (value && vtype == VT_VARIABLE) ? dequote_escapes (value) : value;
+#endif
+
+  return vtype;
+}
+
+/******************************************************/
+/*                                                   */
+/* Functions to extract substrings of variable values */
+/*                                                   */
+/******************************************************/
+
+#if defined (HANDLE_MULTIBYTE)
+/* Character-oriented rather than strictly byte-oriented substrings.  S and
+   E, rather being strict indices into STRING, indicate character (possibly
+   multibyte character) positions that require calculation.
+   Used by the ${param:offset[:length]} expansion. */
+static char *
+mb_substring (string, s, e)
+     char *string;
+     int s, e;
+{
+  char *tt;
+  int start, stop, i, slen;
+  DECLARE_MBSTATE;
+
+  start = 0;
+  slen = STRLEN (string);
+
+  i = s;
+  while (string[start] && i--)
+    ADVANCE_CHAR (string, slen, start);
+  stop = start;
+  i = e - s;
+  while (string[stop] && i--)
+    ADVANCE_CHAR (string, slen, stop);
+  tt = substring (string, start, stop);
+  return tt;
+}
+#endif
+  
+/* Process a variable substring expansion: ${name:e1[:e2]}.  If VARNAME
+   is `@', use the positional parameters; otherwise, use the value of
+   VARNAME.  If VARNAME is an array variable, use the array elements. */
+
+static char *
+parameter_brace_substring (varname, value, substr, quoted)
+     char *varname, *value, *substr;
+     int quoted;
+{
+  intmax_t e1, e2;
+  int vtype, r, starsub;
+  char *temp, *val, *tt;
+  SHELL_VAR *v;
+
+  if (value == 0)
+    return ((char *)NULL);
+
+  this_command_name = varname;
+
+  vtype = get_var_and_type (varname, value, quoted, &v, &val);
+  if (vtype == -1)
+    return ((char *)NULL);
+
+  starsub = vtype & VT_STARSUB;
+  vtype &= ~VT_STARSUB;
+
+  r = verify_substring_values (val, substr, vtype, &e1, &e2);
+  if (r <= 0)
+    return ((r == 0) ? &expand_param_error : (char *)NULL);
+
+  switch (vtype)
+    {
+    case VT_VARIABLE:
+    case VT_ARRAYMEMBER:
+#if defined (HANDLE_MULTIBYTE)
+      if (MB_CUR_MAX > 1)
+       tt = mb_substring (val, e1, e2);
+      else
+#endif
+      tt = substring (val, e1, e2);
+
+      if (vtype == VT_VARIABLE)
+       FREE (val);
+      if (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))
+       temp = quote_string (tt);
+      else
+       temp = tt ? quote_escapes (tt) : (char *)NULL;
+      FREE (tt);
+      break;
+    case VT_POSPARMS:
+      tt = pos_params (varname, e1, e2, quoted);
+      if ((quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) == 0)
+       {
+         temp = tt ? quote_escapes (tt) : (char *)NULL;
+         FREE (tt);
+       }
+      else
+       temp = tt;
+      break;
+#if defined (ARRAY_VARS)
+    case VT_ARRAYVAR:
+      /* We want E2 to be the number of elements desired (arrays can be sparse,
+        so verify_substring_values just returns the numbers specified and we
+        rely on array_subrange to understand how to deal with them). */
+      tt = array_subrange (array_cell (v), e1, e2, starsub, quoted);
+      if ((quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) == 0)
+       {
+         temp = tt ? quote_escapes (tt) : (char *)NULL;
+         FREE (tt);
+       }
+      else
+       temp = tt;
+      break;
+#endif
+    default:
+      temp = (char *)NULL;
+    }
+
+  return temp;
+}
+
+/****************************************************************/
+/*                                                             */
+/* Functions to perform pattern substitution on variable values */
+/*                                                             */
+/****************************************************************/
+
+char *
+pat_subst (string, pat, rep, mflags)
+     char *string, *pat, *rep;
+     int mflags;
+{
+  char *ret, *s, *e, *str;
+  int rsize, rptr, l, replen, mtype;
+
+  mtype = mflags & MATCH_TYPEMASK;
+
+  /* Special cases:
+   *   1.  A null pattern with mtype == MATCH_BEG means to prefix STRING
+   *       with REP and return the result.
+   *   2.  A null pattern with mtype == MATCH_END means to append REP to
+   *       STRING and return the result.
+   */
+  if ((pat == 0 || *pat == 0) && (mtype == MATCH_BEG || mtype == MATCH_END))
+    {
+      replen = STRLEN (rep);
+      l = strlen (string);
+      ret = (char *)xmalloc (replen + l + 2);
+      if (replen == 0)
+       strcpy (ret, string);
+      else if (mtype == MATCH_BEG)
+       {
+         strcpy (ret, rep);
+         strcpy (ret + replen, string);
+       }
+      else
+       {
+         strcpy (ret, string);
+         strcpy (ret + l, rep);
+       }
+      return (ret);
+    }
+
+  ret = (char *)xmalloc (rsize = 64);
+  ret[0] = '\0';
+
+  for (replen = STRLEN (rep), rptr = 0, str = string;;)
+    {
+      if (match_pattern (str, pat, mtype, &s, &e) == 0)
+       break;
+      l = s - str;
+      RESIZE_MALLOCED_BUFFER (ret, rptr, (l + replen), rsize, 64);
+
+      /* OK, now copy the leading unmatched portion of the string (from
+        str to s) to ret starting at rptr (the current offset).  Then copy
+        the replacement string at ret + rptr + (s - str).  Increment
+        rptr (if necessary) and str and go on. */
+      if (l)
+       {
+         strncpy (ret + rptr, str, l);
+         rptr += l;
+       }
+      if (replen)
+       {
+         strncpy (ret + rptr, rep, replen);
+         rptr += replen;
+       }
+      str = e;         /* e == end of match */
+
+      if (((mflags & MATCH_GLOBREP) == 0) || mtype != MATCH_ANY)
+       break;
+
+      if (s == e)
+       e++, str++;             /* avoid infinite recursion on zero-length match */
+    }
+
+  /* Now copy the unmatched portion of the input string */
+  if (*str)
+    {
+      RESIZE_MALLOCED_BUFFER (ret, rptr, STRLEN(str) + 1, rsize, 64);
+      strcpy (ret + rptr, str);
+    }
+  else
+    ret[rptr] = '\0';
+
+  return ret;
+}
+
+/* Do pattern match and replacement on the positional parameters. */
+static char *
+pos_params_pat_subst (string, pat, rep, mflags)
+     char *string, *pat, *rep;
+     int mflags;
+{
+  WORD_LIST *save, *params;
+  WORD_DESC *w;
+  char *ret, *tt;
+
+  save = params = list_rest_of_args ();
+  if (save == 0)
+    return ((char *)NULL);
+
+  for ( ; params; params = params->next)
+    {
+      ret = pat_subst (params->word->word, pat, rep, mflags);
+      w = make_bare_word (ret);
+      dispose_word (params->word);
+      params->word = w;
+      FREE (ret);
+    }
+
+  if ((mflags & (MATCH_QUOTED|MATCH_STARSUB)) == (MATCH_QUOTED|MATCH_STARSUB))
+    ret = string_list_dollar_star (quote_list (save));
+  else
+    ret = string_list ((mflags & MATCH_QUOTED) ? quote_list (save) : save);
+  dispose_words (save);
+
+  return (ret);
+}
+
+/* Perform pattern substitution on VALUE, which is the expansion of
+   VARNAME.  PATSUB is an expression supplying the pattern to match
+   and the string to substitute.  QUOTED is a flags word containing
+   the type of quoting currently in effect. */
+static char *
+parameter_brace_patsub (varname, value, patsub, quoted)
+     char *varname, *value, *patsub;
+     int quoted;
+{
+  int vtype, mflags, starsub;
+  char *val, *temp, *pat, *rep, *p, *lpatsub, *tt;
+  SHELL_VAR *v;
+
+  if (value == 0)
+    return ((char *)NULL);
+
+  this_command_name = varname;
+
+  vtype = get_var_and_type (varname, value, quoted, &v, &val);
+  if (vtype == -1)
+    return ((char *)NULL);
+
+  starsub = vtype & VT_STARSUB;
+  vtype &= ~VT_STARSUB;
+
+  mflags = 0;
+  if (*patsub == '/')
+    {
+      mflags |= MATCH_GLOBREP;
+      patsub++;
+    }
+
+  /* Malloc this because expand_string_if_necessary or one of the expansion
+     functions in its call chain may free it on a substitution error. */
+  lpatsub = savestring (patsub);
+
+  if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))
+    mflags |= MATCH_QUOTED;
+
+  if (starsub)
+    mflags |= MATCH_STARSUB;
+
+  if (rep = quoted_strchr (lpatsub, '/', ST_BACKSL))
+    *rep++ = '\0';
+  else
+    rep = (char *)NULL;
+
+  if (rep && *rep == '\0')
+    rep = (char *)NULL;
+
+#if 0
+  /* Expand PAT and REP for command, variable and parameter, arithmetic,
+     and process substitution.  Also perform quote removal.  Do not
+     perform word splitting or filename generation. */
+  pat = expand_string_if_necessary (lpatsub, (quoted & ~Q_DOUBLE_QUOTES), expand_string_unsplit);
+#else
+  /* Perform the same expansions on the pattern as performed by the
+     pattern removal expansions. */
+  pat = getpattern (lpatsub, quoted, 1);
+#endif
+
+  if (rep)
+    {
+      if ((mflags & MATCH_QUOTED) == 0)
+       rep = expand_string_if_necessary (rep, quoted, expand_string_unsplit);
+      else
+       rep = expand_string_to_string_internal (rep, quoted, expand_string_unsplit);
+    }
+
+  p = pat;
+  if (pat && pat[0] == '#')
+    {
+      mflags |= MATCH_BEG;
+      p++;
+    }
+  else if (pat && pat[0] == '%')
+    {
+      mflags |= MATCH_END;
+      p++;
+    }
+  else
+    mflags |= MATCH_ANY;
+
+  /* OK, we now want to substitute REP for PAT in VAL.  If
+     flags & MATCH_GLOBREP is non-zero, the substitution is done
+     everywhere, otherwise only the first occurrence of PAT is
+     replaced.  The pattern matching code doesn't understand
+     CTLESC quoting CTLESC and CTLNUL so we use the dequoted variable
+     values passed in (VT_VARIABLE) so the pattern substitution
+     code works right.  We need to requote special chars after
+     we're done for VT_VARIABLE and VT_ARRAYMEMBER, and for the
+     other cases if QUOTED == 0, since the posparams and arrays
+     indexed by * or @ do special things when QUOTED != 0. */
+
+  switch (vtype)
+    {
+    case VT_VARIABLE:
+    case VT_ARRAYMEMBER:
+      temp = pat_subst (val, p, rep, mflags);
+      if (vtype == VT_VARIABLE)
+       FREE (val);
+      if (temp)
+       {
+         tt = quote_escapes (temp);
+         free (temp);
+         temp = tt;
+       }
+      break;
+    case VT_POSPARMS:
+      temp = pos_params_pat_subst (val, p, rep, mflags);
+      if (temp && (mflags & MATCH_QUOTED) == 0)
+       {
+         tt = quote_escapes (temp);
+         free (temp);
+         temp = tt;
+       }
+      break;
+#if defined (ARRAY_VARS)
+    case VT_ARRAYVAR:
+      temp = array_patsub (array_cell (v), p, rep, mflags);
+      if (temp && (mflags & MATCH_QUOTED) == 0)
+       {
+         tt = quote_escapes (temp);
+         free (temp);
+         temp = tt;
+       }
+      break;
+#endif
+    }
+
+  FREE (pat);
+  FREE (rep);
+  free (lpatsub);
+
+  return temp;
+}
+
+/****************************************************************/
+/*                                                             */
+/*     Functions to perform parameter expansion on a string    */
+/*                                                             */
+/****************************************************************/
+
+/* ${[#][!]name[[:]#[#]%[%]-=?+[word][:e1[:e2]]]} */
+static char *
+parameter_brace_expand (string, indexp, quoted, quoted_dollar_atp, contains_dollar_at)
+     char *string;
+     int *indexp, quoted, *quoted_dollar_atp, *contains_dollar_at;
+{
+  int check_nullness, var_is_set, var_is_null, var_is_special;
+  int want_substring, want_indir, want_patsub;
+  char *name, *value, *temp, *temp1;
+  int t_index, sindex, c;
+  intmax_t number;
+
+  value = (char *)NULL;
+  var_is_set = var_is_null = var_is_special = check_nullness = 0;
+  want_substring = want_indir = want_patsub = 0;
+
+  sindex = *indexp;
+  t_index = ++sindex;
+  name = string_extract (string, &t_index, "#%:-=?+/}", EX_VARNAME);
+
+  /* If the name really consists of a special variable, then make sure
+     that we have the entire name.  We don't allow indirect references
+     to special variables except `#', `?', `@' and `*'. */
+  if ((sindex == t_index &&
+       (string[t_index] == '-' ||
+        string[t_index] == '?' ||
+        string[t_index] == '#')) ||
+      (sindex == t_index - 1 && string[sindex] == '!' &&
+       (string[t_index] == '#' ||
+        string[t_index] == '?' ||
+        string[t_index] == '@' ||
+        string[t_index] == '*')))
+    {
+      t_index++;
+      free (name);
+      temp1 = string_extract (string, &t_index, "#%:-=?+/}", 0);
+      name = (char *)xmalloc (3 + (strlen (temp1)));
+      *name = string[sindex];
+      if (string[sindex] == '!')
+       {
+         /* indirect reference of $#, $?, $@, or $* */
+         name[1] = string[sindex + 1];
+         strcpy (name + 2, temp1);
+       }
+      else     
+       strcpy (name + 1, temp1);
+      free (temp1);
+    }
+  sindex = t_index;
+
+  /* Find out what character ended the variable name.  Then
+     do the appropriate thing. */
+  if (c = string[sindex])
+    sindex++;
+
+  /* If c is followed by one of the valid parameter expansion
+     characters, move past it as normal.  If not, assume that
+     a substring specification is being given, and do not move
+     past it. */
+  if (c == ':' && VALID_PARAM_EXPAND_CHAR (string[sindex]))
+    {
+      check_nullness++;
+      if (c = string[sindex])
+       sindex++;
+    }
+  else if (c == ':' && string[sindex] != RBRACE)
+    want_substring = 1;
+  else if (c == '/' && string[sindex] != RBRACE)
+    want_patsub = 1;
+
+  /* Catch the valid and invalid brace expressions that made it through the
+     tests above. */
+  /* ${#-} is a valid expansion and means to take the length of $-.
+     Similarly for ${#?} and ${##}... */
+  if (name[0] == '#' && name[1] == '\0' && check_nullness == 0 &&
+       VALID_SPECIAL_LENGTH_PARAM (c) && string[sindex] == RBRACE)
+    {
+      name = (char *)xrealloc (name, 3);
+      name[1] = c;
+      name[2] = '\0';
+      c = string[sindex++];
+    }
+
+  /* ...but ${#%}, ${#:}, ${#=}, ${#+}, and ${#/} are errors. */
+  if (name[0] == '#' && name[1] == '\0' && check_nullness == 0 &&
+       member (c, "%:=+/") && string[sindex] == RBRACE)
+    {
+      temp = (char *)NULL;
+      goto bad_substitution;
+    }
+
+  /* Indirect expansion begins with a `!'.  A valid indirect expansion is
+     either a variable name, one of the positional parameters or a special
+     variable that expands to one of the positional parameters. */
+  want_indir = *name == '!' &&
+    (legal_variable_starter ((unsigned char)name[1]) || DIGIT (name[1])
+                                       || VALID_INDIR_PARAM (name[1]));
+
+  /* Determine the value of this variable. */
+
+  /* Check for special variables, directly referenced. */
+  if (SPECIAL_VAR (name, want_indir))
+    var_is_special++;
+
+  /* Check for special expansion things, like the length of a parameter */
+  if (*name == '#' && name[1])
+    {
+      /* If we are not pointing at the character just after the
+        closing brace, then we haven't gotten all of the name.
+        Since it begins with a special character, this is a bad
+        substitution.  Also check NAME for validity before trying
+        to go on. */
+      if (string[sindex - 1] != RBRACE || (valid_length_expression (name) == 0))
+       {
+         temp = (char *)NULL;
+         goto bad_substitution;
+       }
+
+      number = parameter_brace_expand_length (name);
+      free (name);
+
+      *indexp = sindex;
+      return ((number < 0) ? &expand_param_error : itos (number));
+    }
+
+  /* ${@} is identical to $@. */
+  if (name[0] == '@' && name[1] == '\0')
+    {
+      if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp)
+       *quoted_dollar_atp = 1;
+
+      if (contains_dollar_at)
+       *contains_dollar_at = 1;
+    }
+
+  /* Process ${!PREFIX*} expansion. */
+  if (want_indir && string[sindex - 1] == RBRACE &&
+      (string[sindex - 2] == '*' || string[sindex - 2] == '@') &&
+      legal_variable_starter ((unsigned char) name[1]))
+    {
+      char **x;
+      WORD_LIST *xlist;
+
+      temp1 = savestring (name + 1);
+      number = strlen (temp1);
+      temp1[number - 1] = '\0';
+      x = all_variables_matching_prefix (temp1);
+      xlist = strvec_to_word_list (x, 0, 0);
+      if (string[sindex - 2] == '*')
+       temp = string_list_dollar_star (xlist);
+      else
+       {
+         temp = string_list_dollar_at (xlist, quoted);
+         if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp)
+           *quoted_dollar_atp = 1;
+         if (contains_dollar_at)
+           *contains_dollar_at = 1;
+       }
+      free (x);
+      free (xlist);
+      free (temp1);
+      *indexp = sindex;
+      return (temp);
+    }
+
+#if defined (ARRAY_VARS)      
+  /* Process ${!ARRAY[@]} and ${!ARRAY[*]} expansion. */ /* [ */
+  if (want_indir && string[sindex - 1] == RBRACE &&
+      string[sindex - 2] == ']' && valid_array_reference (name+1))
+    {
+      char *x, *x1;
+
+      temp1 = savestring (name + 1);
+      x = array_variable_name (temp1, &x1, (int *)0);  /* [ */
+      FREE (x);
+      if (ALL_ELEMENT_SUB (x1[0]) && x1[1] == ']')
+       {
+         temp = array_keys (temp1, quoted);
+         if (x1[0] == '@')
+           {
+             if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp)
+               *quoted_dollar_atp = 1;
+             if (contains_dollar_at)
+               *contains_dollar_at = 1;
+           }       
+
+         free (temp1);
+         *indexp = sindex;
+         return (temp);
+       }
+
+      free (temp1);
+    }
+#endif /* ARRAY_VARS */
+      
+  /* Make sure that NAME is valid before trying to go on. */
+  if (valid_brace_expansion_word (want_indir ? name + 1 : name,
+                                       var_is_special) == 0)
+    {
+      temp = (char *)NULL;
+      goto bad_substitution;
+    }
+
+  if (want_indir)
+    temp = parameter_brace_expand_indir (name + 1, var_is_special, quoted, quoted_dollar_atp, contains_dollar_at);
+  else
+    temp = parameter_brace_expand_word (name, var_is_special, quoted);
+
+#if defined (ARRAY_VARS)
+  if (valid_array_reference (name))
+    chk_atstar (name, quoted, quoted_dollar_atp, contains_dollar_at);
+#endif
+
+  var_is_set = temp != (char *)0;
+  var_is_null = check_nullness && (var_is_set == 0 || *temp == 0);
+
+  /* Get the rest of the stuff inside the braces. */
+  if (c && c != RBRACE)
+    {
+      /* Extract the contents of the ${ ... } expansion
+        according to the Posix.2 rules. */
+      value = extract_dollar_brace_string (string, &sindex, quoted, 0);
+      if (string[sindex] == RBRACE)
+       sindex++;
+      else
+       goto bad_substitution;
+    }
+  else
+    value = (char *)NULL;
+
+  *indexp = sindex;
+
+  /* If this is a substring spec, process it and add the result. */
+  if (want_substring)
+    {
+      temp1 = parameter_brace_substring (name, temp, value, quoted);
+      FREE (name);
+      FREE (value);
+      FREE (temp);
+      return (temp1);
+    }
+  else if (want_patsub)
+    {
+      temp1 = parameter_brace_patsub (name, temp, value, quoted);
+      FREE (name);
+      FREE (value);
+      FREE (temp);
+      return (temp1);
+    }
+
+  /* Do the right thing based on which character ended the variable name. */
+  switch (c)
+    {
+    default:
+    case '\0':
+    bad_substitution:
+      report_error (_("%s: bad substitution"), string ? string : "??");
+      FREE (value);
+      FREE (temp);
+      free (name);
+      return &expand_param_error;
+
+    case RBRACE:
+      if (var_is_set == 0 && unbound_vars_is_error)
+       {
+         err_unboundvar (name);
+         FREE (value);
+         FREE (temp);
+         free (name);
+         last_command_exit_value = EXECUTION_FAILURE;
+         return (interactive_shell ? &expand_param_error : &expand_param_fatal);
+       }
+      break;
+
+    case '#':  /* ${param#[#]pattern} */
+    case '%':  /* ${param%[%]pattern} */
+      if (value == 0 || *value == '\0' || temp == 0 || *temp == '\0')
+       {
+         FREE (value);
+         break;
+       }
+      temp1 = parameter_brace_remove_pattern (name, temp, value, c, quoted);
+      free (temp);
+      free (value);
+      temp = temp1;
+      break;
+
+    case '-':
+    case '=':
+    case '?':
+    case '+':
+      if (var_is_set && var_is_null == 0)
+       {
+         /* If the operator is `+', we don't want the value of the named
+            variable for anything, just the value of the right hand side. */
+
+         if (c == '+')
+           {
+             /* XXX -- if we're double-quoted and the named variable is "$@",
+                       we want to turn off any special handling of "$@" --
+                       we're not using it, so whatever is on the rhs applies. */
+             if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp)
+               *quoted_dollar_atp = 0;
+             if (contains_dollar_at)
+               *contains_dollar_at = 0;
+
+             FREE (temp);
+             if (value)
+               {
+                 temp = parameter_brace_expand_rhs (name, value, c,
+                                                    quoted,
+                                                    quoted_dollar_atp,
+                                                    contains_dollar_at);
+                 free (value);
+               }
+             else
+               temp = (char *)NULL;
+           }
+         else
+           {
+             FREE (value);
+           }
+         /* Otherwise do nothing; just use the value in TEMP. */
+       }
+      else     /* VAR not set or VAR is NULL. */
+       {
+         FREE (temp);
+         temp = (char *)NULL;
+         if (c == '=' && var_is_special)
+           {
+             report_error (_("$%s: cannot assign in this way"), name);
+             free (name);
+             free (value);
+             return &expand_param_error;
+           }
+         else if (c == '?')
+           {
+             parameter_brace_expand_error (name, value);
+             return (interactive_shell ? &expand_param_error : &expand_param_fatal);
+           }
+         else if (c != '+')
+           {
+             /* XXX -- if we're double-quoted and the named variable is "$@",
+                       we want to turn off any special handling of "$@" --
+                       we're not using it, so whatever is on the rhs applies. */
+             if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp)
+               *quoted_dollar_atp = 0;
+             if (contains_dollar_at)
+               *contains_dollar_at = 0;
+
+             temp = parameter_brace_expand_rhs (name, value, c, quoted,
+                                                quoted_dollar_atp,
+                                                contains_dollar_at);
+           }
+         free (value);
+       }
+
+      break;
+    }
+  free (name);
+  return (temp);
+}
+
+/* Expand a single ${xxx} expansion.  The braces are optional.  When
+   the braces are used, parameter_brace_expand() does the work,
+   possibly calling param_expand recursively. */
+static char *
+param_expand (string, sindex, quoted, expanded_something,
+             contains_dollar_at, quoted_dollar_at_p, had_quoted_null_p,
+             pflags)
+     char *string;
+     int *sindex, quoted, *expanded_something, *contains_dollar_at;
+     int *quoted_dollar_at_p, *had_quoted_null_p, pflags;
+{
+  char *temp, *temp1, uerror[3];
+  int zindex, t_index, expok;
+  unsigned char c;
+  intmax_t number;
+  SHELL_VAR *var;
+  WORD_LIST *list;
+
+  zindex = *sindex;
+  c = string[++zindex];
+
+  temp = (char *)NULL;
+
+  /* Do simple cases first. Switch on what follows '$'. */
+  switch (c)
+    {
+    /* $0 .. $9? */
+    case '0':
+    case '1':
+    case '2':
+    case '3':
+    case '4':
+    case '5':
+    case '6':
+    case '7':
+    case '8':
+    case '9':
+      temp1 = dollar_vars[TODIGIT (c)];
+      if (unbound_vars_is_error && temp1 == (char *)NULL)
+       {
+         uerror[0] = '$';
+         uerror[1] = c;
+         uerror[2] = '\0';
+         err_unboundvar (uerror);
+         last_command_exit_value = EXECUTION_FAILURE;
+         return (interactive_shell ? &expand_param_error : &expand_param_fatal);
+       }
+#if 1
+      if (temp1)
+       temp = (*temp1 && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
+                 ? quote_string (temp1)
+                 : quote_escapes (temp1);
+      else
+       temp = (char *)NULL;
+#else
+      temp = temp1 ? quote_escapes (temp1) : (char *)NULL;
+#endif
+      break;
+
+    /* $$ -- pid of the invoking shell. */
+    case '$':
+      temp = itos (dollar_dollar_pid);
+      break;
+
+    /* $# -- number of positional parameters. */
+    case '#':
+      temp = itos (number_of_args ());
+      break;
+
+    /* $? -- return value of the last synchronous command. */
+    case '?':
+      temp = itos (last_command_exit_value);
+      break;
+
+    /* $- -- flags supplied to the shell on invocation or by `set'. */
+    case '-':
+      temp = which_set_flags ();
+      break;
+
+      /* $! -- Pid of the last asynchronous command. */
+    case '!':
+      /* If no asynchronous pids have been created, expand to nothing.
+        If `set -u' has been executed, and no async processes have
+        been created, this is an expansion error. */
+      if (last_asynchronous_pid == NO_PID)
+       {
+         if (expanded_something)
+           *expanded_something = 0;
+         temp = (char *)NULL;
+         if (unbound_vars_is_error)
+           {
+             uerror[0] = '$';
+             uerror[1] = c;
+             uerror[2] = '\0';
+             err_unboundvar (uerror);
+             last_command_exit_value = EXECUTION_FAILURE;
+             return (interactive_shell ? &expand_param_error : &expand_param_fatal);
+           }
+       }
+      else
+       temp = itos (last_asynchronous_pid);
+      break;
+
+    /* The only difference between this and $@ is when the arg is quoted. */
+    case '*':          /* `$*' */
+      list = list_rest_of_args ();
+
+      /* If there are no command-line arguments, this should just
+        disappear if there are other characters in the expansion,
+        even if it's quoted. */
+      if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && list == 0)
+       temp = (char *)NULL;
+      else if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))
+       {
+         /* If we have "$*" we want to make a string of the positional
+            parameters, separated by the first character of $IFS, and
+            quote the whole string, including the separators.  If IFS
+            is unset, the parameters are separated by ' '; if $IFS is
+            null, the parameters are concatenated. */
+#if 0
+         temp = string_list_dollar_star (list);
+#else
+         temp = (quoted & Q_DOUBLE_QUOTES) ? string_list_dollar_star (list) : string_list (list);
+#endif
+         temp1 = quote_string (temp);
+         free (temp);
+         temp = temp1;
+       }
+      else
+       {
+         /* If the $* is not quoted it is identical to $@ */
+         temp = string_list_dollar_at (list, quoted);
+         if (expand_no_split_dollar_star == 0 && contains_dollar_at)
+           *contains_dollar_at = 1;
+       }
+
+      dispose_words (list);
+      break;
+
+    /* When we have "$@" what we want is "$1" "$2" "$3" ... This
+       means that we have to turn quoting off after we split into
+       the individually quoted arguments so that the final split
+       on the first character of $IFS is still done.  */
+    case '@':          /* `$@' */
+      list = list_rest_of_args ();
+
+      /* We want to flag the fact that we saw this.  We can't turn
+        off quoting entirely, because other characters in the
+        string might need it (consider "\"$@\""), but we need some
+        way to signal that the final split on the first character
+        of $IFS should be done, even though QUOTED is 1. */
+      if (quoted_dollar_at_p && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
+       *quoted_dollar_at_p = 1;
+      if (contains_dollar_at)
+       *contains_dollar_at = 1;
+
+      /* We want to separate the positional parameters with the first
+        character of $IFS in case $IFS is something other than a space.
+        We also want to make sure that splitting is done no matter what --
+        according to POSIX.2, this expands to a list of the positional
+        parameters no matter what IFS is set to. */
+      temp = string_list_dollar_at (list, quoted);
+
+      dispose_words (list);
+      break;
+
+    case LBRACE:
+      temp = parameter_brace_expand (string, &zindex, quoted,
+                                    quoted_dollar_at_p,
+                                    contains_dollar_at);
+      if (temp == &expand_param_error || temp == &expand_param_fatal)
+       return (temp);
+
+      /* XXX */
+      /* Quoted nulls should be removed if there is anything else
+        in the string. */
+      /* Note that we saw the quoted null so we can add one back at
+        the end of this function if there are no other characters
+        in the string, discard TEMP, and go on.  The exception to
+        this is when we have "${@}" and $1 is '', since $@ needs
+        special handling. */
+      if (temp && QUOTED_NULL (temp))
+       {
+         if (had_quoted_null_p)
+           *had_quoted_null_p = 1;
+         if (*quoted_dollar_at_p == 0)
+           {
+             free (temp);
+             temp = (char *)NULL;
+           }
+           
+       }
+
+      goto return0;
+
+    /* Do command or arithmetic substitution. */
+    case LPAREN:
+      /* We have to extract the contents of this paren substitution. */
+      t_index = zindex + 1;
+      temp = extract_command_subst (string, &t_index);
+      zindex = t_index;
+
+      /* For Posix.2-style `$(( ))' arithmetic substitution,
+        extract the expression and pass it to the evaluator. */
+      if (temp && *temp == LPAREN)
+       {
+         char *temp2;
+         temp1 = temp + 1;
+         temp2 = savestring (temp1);
+         t_index = strlen (temp2) - 1;
+
+         if (temp2[t_index] != RPAREN)
+           {
+             free (temp2);
+             goto comsub;
+           }
+
+         /* Cut off ending `)' */
+         temp2[t_index] = '\0';
+
+         /* Expand variables found inside the expression. */
+         temp1 = expand_string_if_necessary (temp2, Q_DOUBLE_QUOTES, expand_string);
+         free (temp2);
+
+arithsub:
+         /* No error messages. */
+         this_command_name = (char *)NULL;
+         number = evalexp (temp1, &expok);
+         free (temp);
+         free (temp1);
+         if (expok == 0)
+           {
+             if (interactive_shell == 0 && posixly_correct)
+               {
+                 last_command_exit_value = EXECUTION_FAILURE;
+                 return (&expand_param_fatal);
+               }
+             else
+               return (&expand_param_error);
+           }
+         temp = itos (number);
+         break;
+       }
+
+comsub:
+      if (pflags & PF_NOCOMSUB)
+       /* we need zindex+1 because string[zindex] == RPAREN */
+       temp1 = substring (string, *sindex, zindex+1);
+      else
+       temp1 = command_substitute (temp, quoted);
+      FREE (temp);
+      temp = temp1;
+      break;
+
+    /* Do POSIX.2d9-style arithmetic substitution.  This will probably go
+       away in a future bash release. */
+    case '[':
+      /* Extract the contents of this arithmetic substitution. */
+      t_index = zindex + 1;
+      temp = extract_arithmetic_subst (string, &t_index);
+      zindex = t_index;
+
+       /* Do initial variable expansion. */
+      temp1 = expand_string_if_necessary (temp, Q_DOUBLE_QUOTES, expand_string);
+
+      goto arithsub;
+
+    default:
+      /* Find the variable in VARIABLE_LIST. */
+      temp = (char *)NULL;
+
+      for (t_index = zindex; (c = string[zindex]) && legal_variable_char (c); zindex++)
+       ;
+      temp1 = (zindex > t_index) ? substring (string, t_index, zindex) : (char *)NULL;
+
+      /* If this isn't a variable name, then just output the `$'. */
+      if (temp1 == 0 || *temp1 == '\0')
+       {
+         FREE (temp1);
+         temp = (char *)xmalloc (2);
+         temp[0] = '$';
+         temp[1] = '\0';
+         if (expanded_something)
+           *expanded_something = 0;
+         goto return0;
+       }
+
+      /* If the variable exists, return its value cell. */
+      var = find_variable (temp1);
+
+      if (var && invisible_p (var) == 0 && var_isset (var))
+       {
+#if defined (ARRAY_VARS)
+         if (array_p (var))
+           {
+             temp = array_reference (array_cell (var), 0);
+             if (temp)
+               temp = (*temp && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
+                         ? quote_string (temp)
+                         : quote_escapes (temp);
+             else if (unbound_vars_is_error)
+               goto unbound_variable;
+           }
+         else
+#endif
+           {
+             temp = value_cell (var);
+
+             temp = (*temp && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
+                       ? quote_string (temp)
+                       : quote_escapes (temp);
+           }
+
+         free (temp1);
+
+         goto return0;
+       }
+
+      temp = (char *)NULL;
+
+unbound_variable:
+      if (unbound_vars_is_error)
+       err_unboundvar (temp1);
+      else
+       {
+         free (temp1);
+         goto return0;
+       }
+
+      free (temp1);
+      last_command_exit_value = EXECUTION_FAILURE;
+      return ((unbound_vars_is_error && interactive_shell == 0)
+               ? &expand_param_fatal
+               : &expand_param_error);
+    }
+
+  if (string[zindex])
+    zindex++;
+
+return0:
+  *sindex = zindex;
+  return (temp);
+}
+
+/* Make a word list which is the result of parameter and variable
+   expansion, command substitution, arithmetic substitution, and
+   quote removal of WORD.  Return a pointer to a WORD_LIST which is
+   the result of the expansion.  If WORD contains a null word, the
+   word list returned is also null.
+
+   QUOTED contains flag values defined in shell.h.
+
+   ISEXP is used to tell expand_word_internal that the word should be
+   treated as the result of an expansion.  This has implications for
+   how IFS characters in the word are treated.
+
+   CONTAINS_DOLLAR_AT and EXPANDED_SOMETHING are return values; when non-null
+   they point to an integer value which receives information about expansion.
+   CONTAINS_DOLLAR_AT gets non-zero if WORD contained "$@", else zero.
+   EXPANDED_SOMETHING get non-zero if WORD contained any parameter expansions,
+   else zero.
+
+   This only does word splitting in the case of $@ expansion.  In that
+   case, we split on ' '. */
+
+/* Values for the local variable quoted_state. */
+#define UNQUOTED        0
+#define PARTIALLY_QUOTED 1
+#define WHOLLY_QUOTED    2
+
+static WORD_LIST *
+expand_word_internal (word, quoted, isexp, contains_dollar_at, expanded_something)
+     WORD_DESC *word;
+     int quoted, isexp;
+     int *contains_dollar_at;
+     int *expanded_something;
+{
+  WORD_LIST *list;
+  WORD_DESC *tword;
+
+  /* The intermediate string that we build while expanding. */
+  char *istring;
+
+  /* The current size of the above object. */
+  int istring_size;
+
+  /* Index into ISTRING. */
+  int istring_index;
+
+  /* Temporary string storage. */
+  char *temp, *temp1;
+
+  /* The text of WORD. */
+  register char *string;
+
+  /* The size of STRING. */
+  size_t string_size;
+
+  /* The index into STRING. */
+  int sindex;
+
+  /* This gets 1 if we see a $@ while quoted. */
+  int quoted_dollar_at;
+
+  /* One of UNQUOTED, PARTIALLY_QUOTED, or WHOLLY_QUOTED, depending on
+     whether WORD contains no quoting characters, a partially quoted
+     string (e.g., "xx"ab), or is fully quoted (e.g., "xxab"). */
+  int quoted_state;
+
+  int had_quoted_null;
+  int has_dollar_at;
+  int tflag;
+
+  register unsigned char c;    /* Current character. */
+  int t_index;                 /* For calls to string_extract_xxx. */
+
+  char twochars[2];
+
+  DECLARE_MBSTATE;
+
+  istring = (char *)xmalloc (istring_size = DEFAULT_INITIAL_ARRAY_SIZE);
+  istring[istring_index = 0] = '\0';
+  quoted_dollar_at = had_quoted_null = has_dollar_at = 0;
+  quoted_state = UNQUOTED;
+
+  string = word->word;
+  if (string == 0)
+    goto finished_with_string;
+  string_size = strlen (string);
+
+  if (contains_dollar_at)
+    *contains_dollar_at = 0;
+
+  /* Begin the expansion. */
+
+  for (sindex = 0; ;)
+    {
+      c = string[sindex];
+
+      /* Case on toplevel character. */
+      switch (c)
+       {
+       case '\0':
+         goto finished_with_string;
+
+       case CTLESC:
+         sindex++;
+#if HANDLE_MULTIBYTE
+         if (MB_CUR_MAX > 1 && string[sindex])
+           {
+             SADD_MBQCHAR_BODY(temp, string, sindex, string_size);
+           }
+         else
+#endif
+           {
+             temp = (char *)xmalloc (3);
+             temp[0] = CTLESC;
+             temp[1] = c = string[sindex];
+             temp[2] = '\0';
+           }
+
+dollar_add_string:
+         if (string[sindex])
+           sindex++;
+
+add_string:
+         if (temp)
+           {
+             istring = sub_append_string (temp, istring, &istring_index, &istring_size);
+             temp = (char *)0;
+           }
+
+         break;
+
+#if defined (PROCESS_SUBSTITUTION)
+         /* Process substitution. */
+       case '<':
+       case '>':
+         {
+           if (string[++sindex] != LPAREN || (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) || posixly_correct)
+             {
+               sindex--;       /* add_character: label increments sindex */
+               goto add_character;
+             }
+           else
+             t_index = sindex + 1; /* skip past both '<' and LPAREN */
+
+           temp1 = extract_process_subst (string, (c == '<') ? "<(" : ">(", &t_index); /*))*/
+           sindex = t_index;
+
+           /* If the process substitution specification is `<()', we want to
+              open the pipe for writing in the child and produce output; if
+              it is `>()', we want to open the pipe for reading in the child
+              and consume input. */
+           temp = temp1 ? process_substitute (temp1, (c == '>')) : (char *)0;
+
+           FREE (temp1);
+
+           goto dollar_add_string;
+         }
+#endif /* PROCESS_SUBSTITUTION */
+
+       case '$':
+         if (expanded_something)
+           *expanded_something = 1;
+
+         has_dollar_at = 0;
+         temp = param_expand (string, &sindex, quoted, expanded_something,
+                              &has_dollar_at, &quoted_dollar_at,
+                              &had_quoted_null,
+                              (word->flags & W_NOCOMSUB) ? PF_NOCOMSUB : 0);
+
+         if (temp == &expand_param_error || temp == &expand_param_fatal)
+           {
+             free (string);
+             free (istring);
+             return ((temp == &expand_param_error) ? &expand_word_error
+                                                   : &expand_word_fatal);
+           }
+         if (contains_dollar_at && has_dollar_at)
+           *contains_dollar_at = 1;
+         goto add_string;
+         break;
+
+       case '`':               /* Backquoted command substitution. */
+         {
+           t_index = sindex++;
+
+           if (expanded_something)
+             *expanded_something = 1;
+
+           temp = string_extract (string, &sindex, "`", 0);
+           if (word->flags & W_NOCOMSUB)
+             /* sindex + 1 because string[sindex] == '`' */
+             temp1 = substring (string, t_index, sindex + 1);
+           else
+             {
+               de_backslash (temp);
+               temp1 = command_substitute (temp, quoted);
+             }
+           FREE (temp);
+           temp = temp1;
+           goto dollar_add_string;
+         }
+
+       case '\\':
+         if (string[sindex + 1] == '\n')
+           {
+             sindex += 2;
+             continue;
+           }
+
+         c = string[++sindex];
+
+         if (quoted & Q_HERE_DOCUMENT)
+           tflag = CBSHDOC;
+         else if (quoted & Q_DOUBLE_QUOTES)
+           tflag = CBSDQUOTE;
+         else
+           tflag = 0;
+
+         if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && ((sh_syntaxtab[c] & tflag) == 0))
+           {
+             SCOPY_CHAR_I (twochars, '\\', c, string, sindex, string_size);
+           }
+         else if (c == 0)
+           {
+             c = CTLNUL;
+             sindex--;         /* add_character: label increments sindex */
+             goto add_character;
+           }
+         else
+           {
+             SCOPY_CHAR_I (twochars, CTLESC, c, string, sindex, string_size);
+           }
+
+         sindex++;
+add_twochars:
+         /* BEFORE jumping here, we need to increment sindex if appropriate */
+         RESIZE_MALLOCED_BUFFER (istring, istring_index, 2, istring_size,
+                                 DEFAULT_ARRAY_SIZE);
+         istring[istring_index++] = twochars[0];
+         istring[istring_index++] = twochars[1];
+         istring[istring_index] = '\0';
+
+         break;
+
+       case '"':
+#if 0
+         if (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT|Q_PATQUOTE))
+#else
+         if (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))
+#endif
+           goto add_character;
+
+         t_index = ++sindex;
+         temp = string_extract_double_quoted (string, &sindex, 0);
+
+         /* If the quotes surrounded the entire string, then the
+            whole word was quoted. */
+         quoted_state = (t_index == 1 && string[sindex] == '\0')
+                           ? WHOLLY_QUOTED
+                           : PARTIALLY_QUOTED;
+
+         if (temp && *temp)
+           {
+             tword = make_word (temp);         /* XXX */
+             free (temp);
+             temp = (char *)NULL;
+
+             has_dollar_at = 0;
+             list = expand_word_internal (tword, Q_DOUBLE_QUOTES, 0, &has_dollar_at, (int *)NULL);
+
+             if (list == &expand_word_error || list == &expand_word_fatal)
+               {
+                 free (istring);
+                 free (string);
+                 /* expand_word_internal has already freed temp_word->word
+                    for us because of the way it prints error messages. */
+                 tword->word = (char *)NULL;
+                 dispose_word (tword);
+                 return list;
+               }
+
+             dispose_word (tword);
+
+             /* "$@" (a double-quoted dollar-at) expands into nothing,
+                not even a NULL word, when there are no positional
+                parameters. */
+             if (list == 0 && has_dollar_at)
+               {
+                 quoted_dollar_at++;
+                 break;
+               }
+
+             /* If we get "$@", we know we have expanded something, so we
+                need to remember it for the final split on $IFS.  This is
+                a special case; it's the only case where a quoted string
+                can expand into more than one word.  It's going to come back
+                from the above call to expand_word_internal as a list with
+                a single word, in which all characters are quoted and
+                separated by blanks.  What we want to do is to turn it back
+                into a list for the next piece of code. */
+             if (list)
+               dequote_list (list);
+
+             if (has_dollar_at)
+               {
+                 quoted_dollar_at++;
+                 if (contains_dollar_at)
+                   *contains_dollar_at = 1;
+                 if (expanded_something)
+                   *expanded_something = 1;
+               }
+           }
+         else
+           {
+             /* What we have is "".  This is a minor optimization. */
+             FREE (temp);
+             list = (WORD_LIST *)NULL;
+           }
+
+         /* The code above *might* return a list (consider the case of "$@",
+            where it returns "$1", "$2", etc.).  We can't throw away the
+            rest of the list, and we have to make sure each word gets added
+            as quoted.  We test on tresult->next:  if it is non-NULL, we
+            quote the whole list, save it to a string with string_list, and
+            add that string. We don't need to quote the results of this
+            (and it would be wrong, since that would quote the separators
+            as well), so we go directly to add_string. */
+         if (list)
+           {
+             if (list->next)
+               {
+                 /* Testing quoted_dollar_at makes sure that "$@" is
+                    split correctly when $IFS does not contain a space. */
+                 temp = quoted_dollar_at
+                               ? string_list_dollar_at (list, Q_DOUBLE_QUOTES)
+                               : string_list (quote_list (list));
+                 dispose_words (list);
+                 goto add_string;
+               }
+             else
+               {
+                 temp = savestring (list->word->word);
+                 dispose_words (list);
+#if 1
+                 /* If the string is not a quoted null string, we want
+                    to remove any embedded unquoted CTLNUL characters.
+                    We do not want to turn quoted null strings back into
+                    the empty string, though.  We do this because we
+                    want to remove any quoted nulls from expansions that
+                    contain other characters.  For example, if we have
+                    x"$*"y or "x$*y" and there are no positional parameters,
+                    the $* should expand into nothing. */
+                 /* HOWEVER, this fails if the string contains a literal
+                    CTLNUL or CTLNUL is contained in the (non-null) expansion
+                    of some variable.  I'm not sure what to do about this
+                    yet.  There has to be some way to indicate the difference
+                    between the two.  An auxiliary data structure might be
+                    necessary. */
+                 if (QUOTED_NULL (temp) == 0)
+                   remove_quoted_nulls (temp); /* XXX */
+#endif
+               }
+           }
+         else
+           temp = (char *)NULL;
+
+         /* We do not want to add quoted nulls to strings that are only
+            partially quoted; we can throw them away. */
+         if (temp == 0 && quoted_state == PARTIALLY_QUOTED)
+           continue;
+
+       add_quoted_string:
+
+         if (temp)
+           {
+             temp1 = temp;
+             temp = quote_string (temp);
+             free (temp1);
+             goto add_string;
+           }
+         else
+           {
+             /* Add NULL arg. */
+             c = CTLNUL;
+             sindex--;         /* add_character: label increments sindex */
+             goto add_character;
+           }
+
+         /* break; */
+
+       case '\'':
+#if 0
+         if (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT|Q_PATQUOTE))
+#else
+         if (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))
+#endif
+           goto add_character;
+
+         t_index = ++sindex;
+         temp = string_extract_single_quoted (string, &sindex);
+
+         /* If the entire STRING was surrounded by single quotes,
+            then the string is wholly quoted. */
+         quoted_state = (t_index == 1 && string[sindex] == '\0')
+                           ? WHOLLY_QUOTED
+                           : PARTIALLY_QUOTED;
+
+         /* If all we had was '', it is a null expansion. */
+         if (*temp == '\0')
+           {
+             free (temp);
+             temp = (char *)NULL;
+           }
+         else
+           remove_quoted_escapes (temp);       /* ??? */
+
+         /* We do not want to add quoted nulls to strings that are only
+            partially quoted; such nulls are discarded. */
+         if (temp == 0 && (quoted_state == PARTIALLY_QUOTED))
+           continue;
+
+         /* If we have a quoted null expansion, add a quoted NULL to istring. */
+         if (temp == 0)
+           {
+             c = CTLNUL;
+             sindex--;         /* add_character: label increments sindex */
+             goto add_character;
+           }
+         else
+           goto add_quoted_string;
+
+         /* break; */
+
+       default:
+         /* This is the fix for " $@ " */
+         if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) || (isexp == 0 && isifs (c)))
+           {
+             if (string[sindex])       /* from old goto dollar_add_string */
+               sindex++;
+             if (c == 0)
+               {
+                 c = CTLNUL;
+                 goto add_character;
+               }
+             else
+               {
+#if HANDLE_MULTIBYTE
+                 if (MB_CUR_MAX > 1)
+                   sindex--;
+
+                 if (MB_CUR_MAX > 1)
+                   {
+                     SADD_MBQCHAR_BODY(temp, string, sindex, string_size);
+                   }
+                 else
+#endif
+                   {
+                     twochars[0] = CTLESC;
+                     twochars[1] = c;
+                     goto add_twochars;
+                   }
+               }
+           }
+
+         SADD_MBCHAR (temp, string, sindex, string_size);
+
+       add_character:
+         RESIZE_MALLOCED_BUFFER (istring, istring_index, 1, istring_size,
+                                 DEFAULT_ARRAY_SIZE);
+         istring[istring_index++] = c;
+         istring[istring_index] = '\0';
+
+         /* Next character. */
+         sindex++;
+       }
+    }
+
+finished_with_string:
+  /* OK, we're ready to return.  If we have a quoted string, and
+     quoted_dollar_at is not set, we do no splitting at all; otherwise
+     we split on ' '.  The routines that call this will handle what to
+     do if nothing has been expanded. */
+
+  /* Partially and wholly quoted strings which expand to the empty
+     string are retained as an empty arguments.  Unquoted strings
+     which expand to the empty string are discarded.  The single
+     exception is the case of expanding "$@" when there are no
+     positional parameters.  In that case, we discard the expansion. */
+
+  /* Because of how the code that handles "" and '' in partially
+     quoted strings works, we need to make ISTRING into a QUOTED_NULL
+     if we saw quoting characters, but the expansion was empty.
+     "" and '' are tossed away before we get to this point when
+     processing partially quoted strings.  This makes "" and $xxx""
+     equivalent when xxx is unset.  We also look to see whether we
+     saw a quoted null from a ${} expansion and add one back if we
+     need to. */
+
+  /* If we expand to nothing and there were no single or double quotes
+     in the word, we throw it away.  Otherwise, we return a NULL word.
+     The single exception is for $@ surrounded by double quotes when
+     there are no positional parameters.  In that case, we also throw
+     the word away. */
+
+  if (*istring == '\0')
+    {
+      if (quoted_dollar_at == 0 && (had_quoted_null || quoted_state == PARTIALLY_QUOTED))
+       {
+         istring[0] = CTLNUL;
+         istring[1] = '\0';
+         tword = make_bare_word (istring);
+         list = make_word_list (tword, (WORD_LIST *)NULL);
+         if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))
+           tword->flags |= W_QUOTED;
+       }
+      /* According to sh, ksh, and Posix.2, if a word expands into nothing
+        and a double-quoted "$@" appears anywhere in it, then the entire
+        word is removed. */
+      else  if (quoted_state == UNQUOTED || quoted_dollar_at)
+       list = (WORD_LIST *)NULL;
+#if 0
+      else
+       {
+         tword = make_bare_word (istring);
+         list = make_word_list (tword, (WORD_LIST *)NULL);
+         if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))
+           tword->flags |= W_QUOTED;
+       }
+#else
+      else
+       list = (WORD_LIST *)NULL;
+#endif
+    }
+  else if (word->flags & W_NOSPLIT)
+    {
+      tword = make_bare_word (istring);
+      list = make_word_list (tword, (WORD_LIST *)NULL);
+      if (word->flags & W_ASSIGNMENT)
+       tword->flags |= W_ASSIGNMENT;   /* XXX */
+      if (word->flags & W_NOGLOB)
+       tword->flags |= W_NOGLOB;       /* XXX */
+      if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))
+       tword->flags |= W_QUOTED;
+    }
+  else
+    {
+      char *ifs_chars;
+
+      ifs_chars = (quoted_dollar_at || has_dollar_at) ? ifs_value : (char *)NULL;
+
+      /* If we have $@, we need to split the results no matter what.  If
+        IFS is unset or NULL, string_list_dollar_at has separated the
+        positional parameters with a space, so we split on space (we have
+        set ifs_chars to " \t\n" above if ifs is unset).  If IFS is set,
+        string_list_dollar_at has separated the positional parameters
+        with the first character of $IFS, so we split on $IFS. */
+      if (has_dollar_at && ifs_chars)
+       list = list_string (istring, *ifs_chars ? ifs_chars : " ", 1);
+      else
+       {
+         tword = make_bare_word (istring);
+         list = make_word_list (tword, (WORD_LIST *)NULL);
+         if ((quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) || (quoted_state == WHOLLY_QUOTED))
+           tword->flags |= W_QUOTED;
+         if (word->flags & W_ASSIGNMENT)
+           tword->flags |= W_ASSIGNMENT;
+         if (word->flags & W_NOGLOB)
+           tword->flags |= W_NOGLOB;
+       }
+    }
+
+  free (istring);
+  return (list);
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*                Functions for Quote Removal                      */
+/*                                                                 */
+/* **************************************************************** */
+
+/* Perform quote removal on STRING.  If QUOTED > 0, assume we are obeying the
+   backslash quoting rules for within double quotes or a here document. */
+char *
+string_quote_removal (string, quoted)
+     char *string;
+     int quoted;
+{
+  size_t slen;
+  char *r, *result_string, *temp, *send;
+  int sindex, tindex, dquote;
+  unsigned char c;
+  DECLARE_MBSTATE;
+
+  /* The result can be no longer than the original string. */
+  slen = strlen (string);
+  send = string + slen;
+
+  r = result_string = (char *)xmalloc (slen + 1);
+
+  for (dquote = sindex = 0; c = string[sindex];)
+    {
+      switch (c)
+       {
+       case '\\':
+         c = string[++sindex];
+         if (((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) || dquote) && (sh_syntaxtab[c] & CBSDQUOTE) == 0)
+           *r++ = '\\';
+         /* FALLTHROUGH */
+
+       default:
+         SCOPY_CHAR_M (r, string, send, sindex);
+         break;
+
+       case '\'':
+         if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) || dquote)
+           {
+             *r++ = c;
+             sindex++;
+             break;
+           }
+         tindex = sindex + 1;
+         temp = string_extract_single_quoted (string, &tindex);
+         if (temp)
+           {
+             strcpy (r, temp);
+             r += strlen (r);
+             free (temp);
+           }
+         sindex = tindex;
+         break;
+
+       case '"':
+         dquote = 1 - dquote;
+         sindex++;
+         break;
+       }
+    }
+    *r = '\0';
+    return (result_string);
+}
+
+#if 0
+/* UNUSED */
+/* Perform quote removal on word WORD.  This allocates and returns a new
+   WORD_DESC *. */
+WORD_DESC *
+word_quote_removal (word, quoted)
+     WORD_DESC *word;
+     int quoted;
+{
+  WORD_DESC *w;
+  char *t;
+
+  t = string_quote_removal (word->word, quoted);
+  w = make_bare_word (t);
+  free (t);
+  return (w);
+}
+
+/* Perform quote removal on all words in LIST.  If QUOTED is non-zero,
+   the members of the list are treated as if they are surrounded by
+   double quotes.  Return a new list, or NULL if LIST is NULL. */
+WORD_LIST *
+word_list_quote_removal (list, quoted)
+     WORD_LIST *list;
+     int quoted;
+{
+  WORD_LIST *result, *t, *tresult;
+
+  for (t = list, result = (WORD_LIST *)NULL; t; t = t->next)
+    {
+      tresult = make_word_list (word_quote_removal (t->word, quoted), (WORD_LIST *)NULL);
+      result = (WORD_LIST *) list_append (result, tresult);
+    }
+  return (result);
+}
+#endif
+
+/*******************************************
+ *                                        *
+ *    Functions to perform word splitting  *
+ *                                        *
+ *******************************************/
+
+void
+setifs (v)
+     SHELL_VAR *v;
+{
+  char *t;
+  unsigned char uc;
+
+  ifs_var = v;
+  ifs_value = v ? value_cell (v) : " \t\n";
+
+  /* Should really merge ifs_cmap with sh_syntaxtab. */
+  memset (ifs_cmap, '\0', sizeof (ifs_cmap));
+  for (t = ifs_value ; t && *t; t++)
+    {
+      uc = *t;
+      ifs_cmap[uc] = 1;
+    }
+
+  ifs_firstc = ifs_value ? *ifs_value : 0;
+}
+
+char *
+getifs ()
+{
+  return ifs_value;
+}
+
+/* This splits a single word into a WORD LIST on $IFS, but only if the word
+   is not quoted.  list_string () performs quote removal for us, even if we
+   don't do any splitting. */
+WORD_LIST *
+word_split (w, ifs_chars)
+     WORD_DESC *w;
+     char *ifs_chars;
+{
+  WORD_LIST *result;
+
+  if (w)
+    {
+      char *xifs;
+
+      xifs = ((w->flags & W_QUOTED) || ifs_chars == 0) ? "" : ifs_chars;
+      result = list_string (w->word, xifs, w->flags & W_QUOTED);
+    }
+  else
+    result = (WORD_LIST *)NULL;
+
+  return (result);
+}
+
+/* Perform word splitting on LIST and return the RESULT.  It is possible
+   to return (WORD_LIST *)NULL. */
+static WORD_LIST *
+word_list_split (list)
+     WORD_LIST *list;
+{
+  WORD_LIST *result, *t, *tresult;
+
+  for (t = list, result = (WORD_LIST *)NULL; t; t = t->next)
+    {
+      tresult = word_split (t->word, ifs_value);
+      result = (WORD_LIST *) list_append (result, tresult);
+    }
+  return (result);
+}
+
+/**************************************************
+ *                                               *
+ *    Functions to expand an entire WORD_LIST    *
+ *                                               *
+ **************************************************/
+
+/* Do any word-expansion-specific cleanup and jump to top_level */
+static void
+exp_jump_to_top_level (v)
+     int v;
+{
+  /* Cleanup code goes here. */
+  expand_no_split_dollar_star = 0;     /* XXX */
+  expanding_redir = 0;
+
+  jump_to_top_level (v);
+}
+
+/* Put NLIST (which is a WORD_LIST * of only one element) at the front of
+   ELIST, and set ELIST to the new list. */
+#define PREPEND_LIST(nlist, elist) \
+       do { nlist->next = elist; elist = nlist; } while (0)
+
+/* Separate out any initial variable assignments from TLIST.  If set -k has
+   been executed, remove all assignment statements from TLIST.  Initial
+   variable assignments and other environment assignments are placed
+   on SUBST_ASSIGN_VARLIST. */
+static WORD_LIST *
+separate_out_assignments (tlist)
+     WORD_LIST *tlist;
+{
+  register WORD_LIST *vp, *lp;
+
+  if (!tlist)
+    return ((WORD_LIST *)NULL);
+
+  if (subst_assign_varlist)
+    dispose_words (subst_assign_varlist);      /* Clean up after previous error */
+
+  subst_assign_varlist = (WORD_LIST *)NULL;
+  vp = lp = tlist;
+
+  /* Separate out variable assignments at the start of the command.
+     Loop invariant: vp->next == lp
+     Loop postcondition:
+       lp = list of words left after assignment statements skipped
+       tlist = original list of words
+  */
+  while (lp && (lp->word->flags & W_ASSIGNMENT))
+    {
+      vp = lp;
+      lp = lp->next;
+    }
+
+  /* If lp != tlist, we have some initial assignment statements.
+     We make SUBST_ASSIGN_VARLIST point to the list of assignment
+     words and TLIST point to the remaining words.  */
+  if (lp != tlist)
+    {
+      subst_assign_varlist = tlist;
+      /* ASSERT(vp->next == lp); */
+      vp->next = (WORD_LIST *)NULL;    /* terminate variable list */
+      tlist = lp;                      /* remainder of word list */
+    }
+
+  /* vp == end of variable list */
+  /* tlist == remainder of original word list without variable assignments */
+  if (!tlist)
+    /* All the words in tlist were assignment statements */
+    return ((WORD_LIST *)NULL);
+
+  /* ASSERT(tlist != NULL); */
+  /* ASSERT((tlist->word->flags & W_ASSIGNMENT) == 0); */
+
+  /* If the -k option is in effect, we need to go through the remaining
+     words, separate out the assignment words, and place them on
+     SUBST_ASSIGN_VARLIST. */
+  if (place_keywords_in_env)
+    {
+      WORD_LIST *tp;   /* tp == running pointer into tlist */
+
+      tp = tlist;
+      lp = tlist->next;
+
+      /* Loop Invariant: tp->next == lp */
+      /* Loop postcondition: tlist == word list without assignment statements */
+      while (lp)
+       {
+         if (lp->word->flags & W_ASSIGNMENT)
+           {
+             /* Found an assignment statement, add this word to end of
+                subst_assign_varlist (vp). */
+             if (!subst_assign_varlist)
+               subst_assign_varlist = vp = lp;
+             else
+               {
+                 vp->next = lp;
+                 vp = lp;
+               }
+
+             /* Remove the word pointed to by LP from TLIST. */
+             tp->next = lp->next;
+             /* ASSERT(vp == lp); */
+             lp->next = (WORD_LIST *)NULL;
+             lp = tp->next;
+           }
+         else
+           {
+             tp = lp;
+             lp = lp->next;
+           }
+       }
+    }
+  return (tlist);
+}
+
+#define WEXP_VARASSIGN 0x001
+#define WEXP_BRACEEXP  0x002
+#define WEXP_TILDEEXP  0x004
+#define WEXP_PARAMEXP  0x008
+#define WEXP_PATHEXP   0x010
+
+/* All of the expansions, including variable assignments at the start of
+   the list. */
+#define WEXP_ALL       (WEXP_VARASSIGN|WEXP_BRACEEXP|WEXP_TILDEEXP|WEXP_PARAMEXP|WEXP_PATHEXP)
+
+/* All of the expansions except variable assignments at the start of
+   the list. */
+#define WEXP_NOVARS    (WEXP_BRACEEXP|WEXP_TILDEEXP|WEXP_PARAMEXP|WEXP_PATHEXP)
+
+/* All of the `shell expansions': brace expansion, tilde expansion, parameter
+   expansion, command substitution, arithmetic expansion, word splitting, and
+   quote removal. */
+#define WEXP_SHELLEXP  (WEXP_BRACEEXP|WEXP_TILDEEXP|WEXP_PARAMEXP)
+
+/* Take the list of words in LIST and do the various substitutions.  Return
+   a new list of words which is the expanded list, and without things like
+   variable assignments. */
+
+WORD_LIST *
+expand_words (list)
+     WORD_LIST *list;
+{
+  return (expand_word_list_internal (list, WEXP_ALL));
+}
+
+/* Same as expand_words (), but doesn't hack variable or environment
+   variables. */
+WORD_LIST *
+expand_words_no_vars (list)
+     WORD_LIST *list;
+{
+  return (expand_word_list_internal (list, WEXP_NOVARS));
+}
+
+WORD_LIST *
+expand_words_shellexp (list)
+     WORD_LIST *list;
+{
+  return (expand_word_list_internal (list, WEXP_SHELLEXP));
+}
+
+static WORD_LIST *
+glob_expand_word_list (tlist, eflags)
+     WORD_LIST *tlist;
+     int eflags;
+{
+  char **glob_array, *temp_string;
+  register int glob_index;
+  WORD_LIST *glob_list, *output_list, *disposables, *next;
+  WORD_DESC *tword;
+
+  output_list = disposables = (WORD_LIST *)NULL;
+  glob_array = (char **)NULL;
+  while (tlist)
+    {
+      /* For each word, either globbing is attempted or the word is
+        added to orig_list.  If globbing succeeds, the results are
+        added to orig_list and the word (tlist) is added to the list
+        of disposable words.  If globbing fails and failed glob
+        expansions are left unchanged (the shell default), the
+        original word is added to orig_list.  If globbing fails and
+        failed glob expansions are removed, the original word is
+        added to the list of disposable words.  orig_list ends up
+        in reverse order and requires a call to REVERSE_LIST to
+        be set right.  After all words are examined, the disposable
+        words are freed. */
+      next = tlist->next;
+
+      /* If the word isn't an assignment and contains an unquoted
+        pattern matching character, then glob it. */
+      if ((tlist->word->flags & W_NOGLOB) == 0 &&
+         unquoted_glob_pattern_p (tlist->word->word))
+       {
+         glob_array = shell_glob_filename (tlist->word->word);
+
+         /* Handle error cases.
+            I don't think we should report errors like "No such file
+            or directory".  However, I would like to report errors
+            like "Read failed". */
+
+         if (glob_array == 0 || GLOB_FAILED (glob_array))
+           {
+             glob_array = (char **)xmalloc (sizeof (char *));
+             glob_array[0] = (char *)NULL;
+           }
+
+         /* Dequote the current word in case we have to use it. */
+         if (glob_array[0] == NULL)
+           {
+             temp_string = dequote_string (tlist->word->word);
+             free (tlist->word->word);
+             tlist->word->word = temp_string;
+           }
+
+         /* Make the array into a word list. */
+         glob_list = (WORD_LIST *)NULL;
+         for (glob_index = 0; glob_array[glob_index]; glob_index++)
+           {
+             tword = make_bare_word (glob_array[glob_index]);
+             tword->flags |= W_GLOBEXP;        /* XXX */
+             glob_list = make_word_list (tword, glob_list);
+           }
+
+         if (glob_list)
+           {
+             output_list = (WORD_LIST *)list_append (glob_list, output_list);
+             PREPEND_LIST (tlist, disposables);
+           }
+         else if (fail_glob_expansion != 0)
+           {
+             report_error (_("no match: %s"), tlist->word->word);
+             jump_to_top_level (DISCARD);
+           }
+         else if (allow_null_glob_expansion == 0)
+           {
+             /* Failed glob expressions are left unchanged. */
+             PREPEND_LIST (tlist, output_list);
+           }
+         else
+           {
+             /* Failed glob expressions are removed. */
+             PREPEND_LIST (tlist, disposables);
+           }
+       }
+      else
+       {
+         /* Dequote the string. */
+         temp_string = dequote_string (tlist->word->word);
+         free (tlist->word->word);
+         tlist->word->word = temp_string;
+         PREPEND_LIST (tlist, output_list);
+       }
+
+      strvec_dispose (glob_array);
+      glob_array = (char **)NULL;
+
+      tlist = next;
+    }
+
+  if (disposables)
+    dispose_words (disposables);
+
+  if (output_list)
+    output_list = REVERSE_LIST (output_list, WORD_LIST *);
+
+  return (output_list);
+}
+
+#if defined (BRACE_EXPANSION)
+static WORD_LIST *
+brace_expand_word_list (tlist, eflags)
+     WORD_LIST *tlist;
+     int eflags;
+{
+  register char **expansions;
+  char *temp_string;
+  WORD_LIST *disposables, *output_list, *next;
+  WORD_DESC *w;
+  int eindex;
+
+  for (disposables = output_list = (WORD_LIST *)NULL; tlist; tlist = next)
+    {
+      next = tlist->next;
+
+      /* Only do brace expansion if the word has a brace character.  If
+        not, just add the word list element to BRACES and continue.  In
+        the common case, at least when running shell scripts, this will
+        degenerate to a bunch of calls to `xstrchr', and then what is
+        basically a reversal of TLIST into BRACES, which is corrected
+        by a call to REVERSE_LIST () on BRACES when the end of TLIST
+        is reached. */
+      if (xstrchr (tlist->word->word, LBRACE))
+       {
+         expansions = brace_expand (tlist->word->word);
+
+         for (eindex = 0; temp_string = expansions[eindex]; eindex++)
+           {
+             w = make_word (temp_string);
+             /* If brace expansion didn't change the word, preserve
+                the flags.  We may want to preserve the flags
+                unconditionally someday -- XXX */
+             if (STREQ (temp_string, tlist->word->word))
+               w->flags = tlist->word->flags;
+             output_list = make_word_list (w, output_list);
+             free (expansions[eindex]);
+           }
+         free (expansions);
+
+         /* Add TLIST to the list of words to be freed after brace
+            expansion has been performed. */
+         PREPEND_LIST (tlist, disposables);
+       }
+      else
+       PREPEND_LIST (tlist, output_list);
+    }
+
+  if (disposables)
+    dispose_words (disposables);
+
+  if (output_list)
+    output_list = REVERSE_LIST (output_list, WORD_LIST *);
+
+  return (output_list);
+}
+#endif
+
+static WORD_LIST *
+shell_expand_word_list (tlist, eflags)
+     WORD_LIST *tlist;
+     int eflags;
+{
+  WORD_LIST *expanded, *orig_list, *new_list, *next, *temp_list;
+  int expanded_something, has_dollar_at;
+  char *temp_string;
+
+  /* We do tilde expansion all the time.  This is what 1003.2 says. */
+  new_list = (WORD_LIST *)NULL;
+  for (orig_list = tlist; tlist; tlist = next)
+    {
+      temp_string = tlist->word->word;
+
+      next = tlist->next;
+
+      /* Posix.2 section 3.6.1 says that tildes following `=' in words
+        which are not assignment statements are not expanded.  If the
+        shell isn't in posix mode, though, we perform tilde expansion
+        on `likely candidate' unquoted assignment statements (flags
+        include W_ASSIGNMENT but not W_QUOTED).  A likely candidate
+        contains an unquoted :~ or =~.  Something to think about: we
+        now have a flag that says  to perform tilde expansion on arguments
+        to `assignment builtins' like declare and export that look like
+        assignment statements.  We now do tilde expansion on such words
+        even in POSIX mode. */
+      if (((tlist->word->flags & (W_ASSIGNMENT|W_QUOTED)) == W_ASSIGNMENT) &&
+            (posixly_correct == 0 || (tlist->word->flags & W_TILDEEXP)) &&
+            (unquoted_substring ("=~", temp_string) || unquoted_substring (":~", temp_string)))
+       {
+         tlist->word->word = bash_tilde_expand (temp_string, 1);
+         free (temp_string);
+       }
+      else if (temp_string[0] == '~')
+       {
+         tlist->word->word = bash_tilde_expand (temp_string, 0);
+         free (temp_string);
+       }
+
+      expanded_something = 0;
+      expanded = expand_word_internal
+       (tlist->word, 0, 0, &has_dollar_at, &expanded_something);
+
+      if (expanded == &expand_word_error || expanded == &expand_word_fatal)
+       {
+         /* By convention, each time this error is returned,
+            tlist->word->word has already been freed. */
+         tlist->word->word = (char *)NULL;
+
+         /* Dispose our copy of the original list. */
+         dispose_words (orig_list);
+         /* Dispose the new list we're building. */
+         dispose_words (new_list);
+
+         last_command_exit_value = EXECUTION_FAILURE;
+         if (expanded == &expand_word_error)
+           exp_jump_to_top_level (DISCARD);
+         else
+           exp_jump_to_top_level (FORCE_EOF);
+       }
+
+      /* Don't split words marked W_NOSPLIT. */
+      if (expanded_something && (tlist->word->flags & W_NOSPLIT) == 0)
+       {
+         temp_list = word_list_split (expanded);
+         dispose_words (expanded);
+       }
+      else
+       {
+         /* If no parameter expansion, command substitution, process
+            substitution, or arithmetic substitution took place, then
+            do not do word splitting.  We still have to remove quoted
+            null characters from the result. */
+         word_list_remove_quoted_nulls (expanded);
+         temp_list = expanded;
+       }
+
+      expanded = REVERSE_LIST (temp_list, WORD_LIST *);
+      new_list = (WORD_LIST *)list_append (expanded, new_list);
+    }
+
+  if (orig_list)  
+    dispose_words (orig_list);
+
+  if (new_list)
+    new_list = REVERSE_LIST (new_list, WORD_LIST *);
+
+  return (new_list);
+}
+
+/* The workhorse for expand_words () and expand_words_no_vars ().
+   First arg is LIST, a WORD_LIST of words.
+   Second arg EFLAGS is a flags word controlling which expansions are
+   performed.
+
+   This does all of the substitutions: brace expansion, tilde expansion,
+   parameter expansion, command substitution, arithmetic expansion,
+   process substitution, word splitting, and pathname expansion, according
+   to the bits set in EFLAGS.  Words with the W_QUOTED or W_NOSPLIT bits
+   set, or for which no expansion is done, do not undergo word splitting.
+   Words with the W_NOGLOB bit set do not undergo pathname expansion. */
+static WORD_LIST *
+expand_word_list_internal (list, eflags)
+     WORD_LIST *list;
+     int eflags;
+{
+  WORD_LIST *new_list, *temp_list;
+  int tint;
+
+  if (list == 0)
+    return ((WORD_LIST *)NULL);
+
+  garglist = new_list = copy_word_list (list);
+  if (eflags & WEXP_VARASSIGN)
+    {
+      garglist = new_list = separate_out_assignments (new_list);
+      if (new_list == 0)
+       {
+         if (subst_assign_varlist)
+           {
+             /* All the words were variable assignments, so they are placed
+                into the shell's environment. */
+             for (temp_list = subst_assign_varlist; temp_list; temp_list = temp_list->next)
+               {
+                 this_command_name = (char *)NULL;     /* no arithmetic errors */
+                 tint = do_assignment (temp_list->word->word);
+                 /* Variable assignment errors in non-interactive shells
+                    running in Posix.2 mode cause the shell to exit. */
+                 if (tint == 0)
+                   {
+                     last_command_exit_value = EXECUTION_FAILURE;
+                     if (interactive_shell == 0 && posixly_correct)
+                       exp_jump_to_top_level (FORCE_EOF);
+                     else
+                       exp_jump_to_top_level (DISCARD);
+                   }
+               }
+             dispose_words (subst_assign_varlist);
+             subst_assign_varlist = (WORD_LIST *)NULL;
+           }
+         return ((WORD_LIST *)NULL);
+       }
+    }
+
+  /* Begin expanding the words that remain.  The expansions take place on
+     things that aren't really variable assignments. */
+
+#if defined (BRACE_EXPANSION)
+  /* Do brace expansion on this word if there are any brace characters
+     in the string. */
+  if ((eflags & WEXP_BRACEEXP) && brace_expansion && new_list)
+    new_list = brace_expand_word_list (new_list, eflags);
+#endif /* BRACE_EXPANSION */
+
+  /* Perform the `normal' shell expansions: tilde expansion, parameter and
+     variable substitution, command substitution, arithmetic expansion,
+     and word splitting. */
+  new_list = shell_expand_word_list (new_list, eflags);
+
+  /* Okay, we're almost done.  Now let's just do some filename
+     globbing. */
+  if (new_list)
+    {
+      if ((eflags & WEXP_PATHEXP) && disallow_filename_globbing == 0)
+       /* Glob expand the word list unless globbing has been disabled. */
+       new_list = glob_expand_word_list (new_list, eflags);
+      else
+       /* Dequote the words, because we're not performing globbing. */
+       new_list = dequote_list (new_list);
+    }
+
+  if ((eflags & WEXP_VARASSIGN) && subst_assign_varlist)
+    {
+      sh_assign_func_t *assign_func;
+
+      /* If the remainder of the words expand to nothing, Posix.2 requires
+        that the variable and environment assignments affect the shell's
+        environment. */
+      assign_func = new_list ? assign_in_env : do_assignment;
+      tempenv_assign_error = 0;
+
+      for (temp_list = subst_assign_varlist; temp_list; temp_list = temp_list->next)
+       {
+         this_command_name = (char *)NULL;
+         tint = (*assign_func) (temp_list->word->word);
+         /* Variable assignment errors in non-interactive shells running
+            in Posix.2 mode cause the shell to exit. */
+         if (tint == 0)
+           {
+             if (assign_func == do_assignment)
+               {
+                 last_command_exit_value = EXECUTION_FAILURE;
+                 if (interactive_shell == 0 && posixly_correct)
+                   exp_jump_to_top_level (FORCE_EOF);
+                 else
+                   exp_jump_to_top_level (DISCARD);
+               }
+             else
+               tempenv_assign_error++;
+           }
+       }
+
+      dispose_words (subst_assign_varlist);
+      subst_assign_varlist = (WORD_LIST *)NULL;
+    }
+
+#if 0
+  tint = list_length (new_list) + 1;
+  RESIZE_MALLOCED_BUFFER (glob_argv_flags, 0, tint, glob_argv_flags_size, 16);
+  for (tint = 0, temp_list = new_list; temp_list; temp_list = temp_list->next)
+    glob_argv_flags[tint++] = (temp_list->word->flags & W_GLOBEXP) ? '1' : '0';
+  glob_argv_flags[tint] = '\0';
+#endif
+
+  return (new_list);
+}
index 3efcf32d68e9722024b6ca9d67f9e81b2aa5ac04..72ec06a2c1fd8dde92acea5e8ac773e35f1d061b 100755 (executable)
@@ -1,4 +1,4 @@
-BUILD_DIR=/usr/local/build/chet/bash/bash-current
+BUILD_DIR=/usr/local/build/bash/bash-current
 THIS_SH=$BUILD_DIR/bash
 PATH=$PATH:$BUILD_DIR
 
diff --git a/tests/RUN-ONE-TEST~ b/tests/RUN-ONE-TEST~
new file mode 100755 (executable)
index 0000000..3efcf32
--- /dev/null
@@ -0,0 +1,9 @@
+BUILD_DIR=/usr/local/build/chet/bash/bash-current
+THIS_SH=$BUILD_DIR/bash
+PATH=$PATH:$BUILD_DIR
+
+export THIS_SH PATH
+
+rm -f /tmp/xx
+
+/bin/sh "$@"
index 193e9d4f8baf7d7bd401f33837f05e971168b399..f1c9e9d9adc09af5ed287d94fc5d7d8a883fd81c 100644 (file)
@@ -1,5 +1,5 @@
 echo $BASH_VERSION
-./histexp.tests: line 22: history: !!:z: history expansion failed
+./histexp.tests: line 24: history: !!:z: history expansion failed
     1  for i in one two three; do echo $i; done
     2  /bin/sh -c 'echo this is $0'
     3  ls
index 11596ddd516ea68f6f49b88a28c6bcafd2b38303..721208cb23e43ecb5b923e77e8008f9c5993d1af 100644 (file)
@@ -5,6 +5,8 @@ trap 'rm /tmp/newhistory' 0
 file=bax
 histchars='!^#'        # make sure history comment char is set correctly
 
+unset HISTFILESIZE
+
 history -c
 
 HISTFILE=history.list
index 619d34ca3c1e1addc7dd337d2ad58ab86bf637a7..556a3129b36bf81836ae2171fd6712ca6d328726 100644 (file)
@@ -97,7 +97,7 @@ line 2 for history
 6       HISTFILE=/tmp/newhistory
 7       echo displaying \$HISTFILE after history -a
 8       cat $HISTFILE
-./history.tests: line 73: fc: history specification out of range
+./history.tests: line 75: fc: history specification out of range
    14  set -H
    15  echo line 2 for history
    16  unset HISTSIZE
@@ -107,5 +107,5 @@ echo xx xb xc
 xx xb xc
 echo 44 48 4c
 44 48 4c
-./history.tests: line 88: fc: no command found
+./history.tests: line 90: fc: no command found
 1
index 76ea56190e77d3addb3a59e4a8af0cd8a5f116a9..833cf3a996e7b1113f795f9e4ea42ff75003fda3 100644 (file)
@@ -8,6 +8,8 @@ history -r -w /dev/null
 # bad option
 fc -v
 
+unset HISTFILESIZE
+
 # all of these should result in an empty history list
 history -c
 history -r /dev/null
index dc876de6a1a81ad6f71c889e330532e4c3cf69e6..0a806707eb30e3a94cea2b28d5adfa7f791532e2 100644 (file)
@@ -3646,6 +3646,7 @@ static struct name_and_function special_vars[] = {
   { "LC_CTYPE", sv_locale },
   { "LC_MESSAGES", sv_locale },
   { "LC_NUMERIC", sv_locale },
+  { "LC_TIME", sv_locale },
 
   { "MAIL", sv_mail },
   { "MAILCHECK", sv_mail },
diff --git a/variables.c~ b/variables.c~
new file mode 100644 (file)
index 0000000..dc876de
--- /dev/null
@@ -0,0 +1,4090 @@
+/* variables.c -- Functions for hacking shell variables. */
+
+/* Copyright (C) 1987-2004 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 2, 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, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+
+#include "config.h"
+
+#include "bashtypes.h"
+#include "posixstat.h"
+#include "posixtime.h"
+
+#if defined (qnx)
+#  include <sys/vc.h>
+#endif
+
+#if defined (HAVE_UNISTD_H)
+#  include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include "chartypes.h"
+#include <pwd.h>
+#include "bashansi.h"
+#include "bashintl.h"
+
+#include "shell.h"
+#include "flags.h"
+#include "execute_cmd.h"
+#include "findcmd.h"
+#include "mailcheck.h"
+#include "input.h"
+#include "hashcmd.h"
+#include "pathexp.h"
+
+#include "builtins/getopt.h"
+#include "builtins/common.h"
+
+#if defined (READLINE)
+#  include "bashline.h"
+#  include <readline/readline.h>
+#else
+#  include <tilde/tilde.h>
+#endif
+
+#if defined (HISTORY)
+#  include "bashhist.h"
+#  include <readline/history.h>
+#endif /* HISTORY */
+
+#if defined (PROGRAMMABLE_COMPLETION)
+#  include "pcomplete.h"
+#endif
+
+#define TEMPENV_HASH_BUCKETS   4       /* must be power of two */
+
+#define ifsname(s)     ((s)[0] == 'I' && (s)[1] == 'F' && (s)[2] == 'S' && (s)[3] == '\0')
+
+/* Variables used here and defined in other files. */
+extern int posixly_correct;
+extern int line_number;
+extern int subshell_environment, indirection_level, subshell_level;
+extern int build_version, patch_level;
+extern int expanding_redir;
+extern char *dist_version, *release_status;
+extern char *shell_name;
+extern char *primary_prompt, *secondary_prompt;
+extern char *current_host_name;
+extern sh_builtin_func_t *this_shell_builtin;
+extern SHELL_VAR *this_shell_function;
+extern char *the_printed_command_except_trap;
+extern char *this_command_name;
+extern char *command_execution_string;
+extern time_t shell_start_time;
+
+#if defined (READLINE)
+extern int perform_hostname_completion;
+#endif
+
+/* The list of shell variables that the user has created at the global
+   scope, or that came from the environment. */
+VAR_CONTEXT *global_variables = (VAR_CONTEXT *)NULL;
+
+/* The current list of shell variables, including function scopes */
+VAR_CONTEXT *shell_variables = (VAR_CONTEXT *)NULL;
+
+/* The list of shell functions that the user has created, or that came from
+   the environment. */
+HASH_TABLE *shell_functions = (HASH_TABLE *)NULL;
+
+#if defined (DEBUGGER)
+/* The table of shell function definitions that the user defined or that
+   came from the environment. */
+HASH_TABLE *shell_function_defs = (HASH_TABLE *)NULL;
+#endif
+
+/* The current variable context.  This is really a count of how deep into
+   executing functions we are. */
+int variable_context = 0;
+
+/* The set of shell assignments which are made only in the environment
+   for a single command. */
+HASH_TABLE *temporary_env = (HASH_TABLE *)NULL;
+
+/* Set to non-zero if an assignment error occurs while putting variables
+   into the temporary environment. */
+int tempenv_assign_error;
+
+/* Some funky variables which are known about specially.  Here is where
+   "$*", "$1", and all the cruft is kept. */
+char *dollar_vars[10];
+WORD_LIST *rest_of_args = (WORD_LIST *)NULL;
+
+/* The value of $$. */
+pid_t dollar_dollar_pid;
+
+/* An array which is passed to commands as their environment.  It is
+   manufactured from the union of the initial environment and the
+   shell variables that are marked for export. */
+char **export_env = (char **)NULL;
+static int export_env_index;
+static int export_env_size;
+
+/* Non-zero means that we have to remake EXPORT_ENV. */
+int array_needs_making = 1;
+
+/* The number of times BASH has been executed.  This is set
+   by initialize_variables (). */
+int shell_level = 0;
+
+/* Some forward declarations. */
+static void set_machine_vars __P((void));
+static void set_home_var __P((void));
+static void set_shell_var __P((void));
+static char *get_bash_name __P((void));
+static void initialize_shell_level __P((void));
+static void uidset __P((void));
+#if defined (ARRAY_VARS)
+static void make_vers_array __P((void));
+#endif
+
+static SHELL_VAR *null_assign __P((SHELL_VAR *, char *, arrayind_t));
+#if defined (ARRAY_VARS)
+static SHELL_VAR *null_array_assign __P((SHELL_VAR *, char *, arrayind_t));
+#endif
+static SHELL_VAR *get_self __P((SHELL_VAR *));
+
+#if defined (ARRAY_VARS)
+static SHELL_VAR *init_dynamic_array_var __P((char *, sh_var_value_func_t *, sh_var_assign_func_t *, int));
+#endif
+
+static SHELL_VAR *assign_seconds __P((SHELL_VAR *, char *, arrayind_t));
+static SHELL_VAR *get_seconds __P((SHELL_VAR *));
+static SHELL_VAR *init_seconds_var __P((void));
+
+static int brand __P((void));
+static void sbrand __P((unsigned long));               /* set bash random number generator. */
+static SHELL_VAR *assign_random __P((SHELL_VAR *, char *, arrayind_t));
+static SHELL_VAR *get_random __P((SHELL_VAR *));
+
+static SHELL_VAR *assign_lineno __P((SHELL_VAR *, char *, arrayind_t));
+static SHELL_VAR *get_lineno __P((SHELL_VAR *));
+
+static SHELL_VAR *assign_subshell __P((SHELL_VAR *, char *, arrayind_t));
+static SHELL_VAR *get_subshell __P((SHELL_VAR *));
+
+#if defined (HISTORY)
+static SHELL_VAR *get_histcmd __P((SHELL_VAR *));
+#endif
+
+#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS)
+static SHELL_VAR *assign_dirstack __P((SHELL_VAR *, char *, arrayind_t));
+static SHELL_VAR *get_dirstack __P((SHELL_VAR *));
+#endif
+
+#if defined (ARRAY_VARS)
+static SHELL_VAR *get_groupset __P((SHELL_VAR *));
+#endif
+
+static SHELL_VAR *get_funcname __P((SHELL_VAR *));
+static SHELL_VAR *init_funcname_var __P((void));
+
+static void initialize_dynamic_variables __P((void));
+
+static SHELL_VAR *hash_lookup __P((const char *, HASH_TABLE *));
+static SHELL_VAR *new_shell_variable __P((const char *));
+static SHELL_VAR *make_new_variable __P((const char *, HASH_TABLE *));
+static SHELL_VAR *bind_variable_internal __P((const char *, char *, HASH_TABLE *, int));
+
+static void free_variable_hash_data __P((PTR_T));
+
+static VARLIST *vlist_alloc __P((int));
+static VARLIST *vlist_realloc __P((VARLIST *, int));
+static void vlist_add __P((VARLIST *, SHELL_VAR *, int));
+
+static void flatten __P((HASH_TABLE *, sh_var_map_func_t *, VARLIST *, int));
+
+static int qsort_var_comp __P((SHELL_VAR **, SHELL_VAR **));
+
+static SHELL_VAR **vapply __P((sh_var_map_func_t *));
+static SHELL_VAR **fapply __P((sh_var_map_func_t *));
+
+static int visible_var __P((SHELL_VAR *));
+static int visible_and_exported __P((SHELL_VAR *));
+static int local_and_exported __P((SHELL_VAR *));
+static int variable_in_context __P((SHELL_VAR *));
+#if defined (ARRAY_VARS)
+static int visible_array_vars __P((SHELL_VAR *));
+#endif
+
+static SHELL_VAR *bind_tempenv_variable __P((const char *, char *));
+static void push_temp_var __P((PTR_T));
+static void propagate_temp_var __P((PTR_T));
+static void dispose_temporary_env __P((sh_free_func_t *));     
+
+static inline char *mk_env_string __P((const char *, const char *));
+static char **make_env_array_from_var_list __P((SHELL_VAR **));
+static char **make_var_export_array __P((VAR_CONTEXT *));
+static char **make_func_export_array __P((void));
+static void add_temp_array_to_env __P((char **, int, int));
+
+static int n_shell_variables __P((void));
+static int set_context __P((SHELL_VAR *));
+
+static void push_func_var __P((PTR_T));
+static void push_exported_var __P((PTR_T));
+
+static inline int find_special_var __P((const char *));
+              
+/* Initialize the shell variables from the current environment.
+   If PRIVMODE is nonzero, don't import functions from ENV or
+   parse $SHELLOPTS. */
+void
+initialize_shell_variables (env, privmode)
+     char **env;
+     int privmode;
+{
+  char *name, *string, *temp_string;
+  int c, char_index, string_index, string_length;
+  SHELL_VAR *temp_var;
+
+  if (shell_variables == 0)
+    {
+      shell_variables = global_variables = new_var_context ((char *)NULL, 0);
+      shell_variables->scope = 0;
+      shell_variables->table = hash_create (0);
+    }
+
+  if (shell_functions == 0)
+    shell_functions = hash_create (0);
+
+#if defined (DEBUGGER)
+  if (shell_function_defs == 0)
+    shell_function_defs = hash_create (0);
+#endif
+
+  for (string_index = 0; string = env[string_index++]; )
+    {
+      char_index = 0;
+      name = string;
+      while ((c = *string++) && c != '=')
+       ;
+      if (string[-1] == '=')
+       char_index = string - name - 1;
+
+      /* If there are weird things in the environment, like `=xxx' or a
+        string without an `=', just skip them. */
+      if (char_index == 0)
+       continue;
+
+      /* ASSERT(name[char_index] == '=') */
+      name[char_index] = '\0';
+      /* Now, name = env variable name, string = env variable value, and
+        char_index == strlen (name) */
+
+      /* If exported function, define it now.  Don't import functions from
+        the environment in privileged mode. */
+      if (privmode == 0 && read_but_dont_execute == 0 && STREQN ("() {", string, 4))
+       {
+         string_length = strlen (string);
+         temp_string = (char *)xmalloc (3 + string_length + char_index);
+
+         strcpy (temp_string, name);
+         temp_string[char_index] = ' ';
+         strcpy (temp_string + char_index + 1, string);
+
+         parse_and_execute (temp_string, name, SEVAL_NONINT|SEVAL_NOHIST);
+
+         /* Ancient backwards compatibility.  Old versions of bash exported
+            functions like name()=() {...} */
+         if (name[char_index - 1] == ')' && name[char_index - 2] == '(')
+           name[char_index - 2] = '\0';
+
+         if (temp_var = find_function (name))
+           {
+             VSETATTR (temp_var, (att_exported|att_imported));
+             array_needs_making = 1;
+           }
+         else
+           report_error (_("error importing function definition for `%s'"), name);
+
+         /* ( */
+         if (name[char_index - 1] == ')' && name[char_index - 2] == '\0')
+           name[char_index - 2] = '(';         /* ) */
+       }
+#if defined (ARRAY_VARS)
+#  if 0
+      /* Array variables may not yet be exported. */
+      else if (*string == '(' && string[1] == '[' && xstrchr (string, ')'))
+       {
+         string_length = 1;
+         temp_string = extract_array_assignment_list (string, &string_length);
+         temp_var = assign_array_from_string (name, temp_string);
+         FREE (temp_string);
+         VSETATTR (temp_var, (att_exported | att_imported));
+         array_needs_making = 1;
+       }
+#  endif
+#endif
+      else
+       {
+         temp_var = bind_variable (name, string);
+         VSETATTR (temp_var, (att_exported | att_imported));
+         array_needs_making = 1;
+       }
+
+      name[char_index] = '=';
+      /* temp_var can be NULL if it was an exported function with a syntax
+        error (a different bug, but it still shouldn't dump core). */
+      if (temp_var && function_p (temp_var) == 0)      /* XXX not yet */
+       {
+         CACHE_IMPORTSTR (temp_var, name);
+       }
+    }
+
+  set_pwd ();
+
+  /* Set up initial value of $_ */
+  temp_var = bind_variable ("_", dollar_vars[0]);
+
+  /* Remember this pid. */
+  dollar_dollar_pid = getpid ();
+
+  /* Now make our own defaults in case the vars that we think are
+     important are missing. */
+  temp_var = set_if_not ("PATH", DEFAULT_PATH_VALUE);
+#if 0
+  set_auto_export (temp_var);  /* XXX */
+#endif
+
+  temp_var = set_if_not ("TERM", "dumb");
+#if 0
+  set_auto_export (temp_var);  /* XXX */
+#endif
+
+#if defined (qnx)
+  /* set node id -- don't import it from the environment */
+  {
+    char node_name[22];
+    qnx_nidtostr (getnid (), node_name, sizeof (node_name));
+    temp_var = bind_variable ("NODE", node_name);
+    set_auto_export (temp_var);
+  }
+#endif
+
+  /* set up the prompts. */
+  if (interactive_shell)
+    {
+#if defined (PROMPT_STRING_DECODE)
+      set_if_not ("PS1", primary_prompt);
+#else
+      if (current_user.uid == -1)
+       get_current_user_info ();
+      set_if_not ("PS1", current_user.euid == 0 ? "# " : primary_prompt);
+#endif
+      set_if_not ("PS2", secondary_prompt);
+    }
+  set_if_not ("PS4", "+ ");
+
+  /* Don't allow IFS to be imported from the environment. */
+  temp_var = bind_variable ("IFS", " \t\n");
+  setifs (temp_var);
+
+  /* Magic machine types.  Pretty convenient. */
+  set_machine_vars ();
+
+  /* Default MAILCHECK for interactive shells.  Defer the creation of a
+     default MAILPATH until the startup files are read, because MAIL
+     names a mail file if MAILPATH is not set, and we should provide a
+     default only if neither is set. */
+  if (interactive_shell)
+    set_if_not ("MAILCHECK", posixly_correct ? "600" : "60");
+
+  /* Do some things with shell level. */
+  initialize_shell_level ();
+
+  set_ppid ();
+
+  /* Initialize the `getopts' stuff. */
+  bind_variable ("OPTIND", "1");
+  getopts_reset (0);
+  bind_variable ("OPTERR", "1");
+  sh_opterr = 1;
+
+  if (login_shell == 1)
+    set_home_var ();
+
+  /* Get the full pathname to THIS shell, and set the BASH variable
+     to it. */
+  name = get_bash_name ();
+  temp_var = bind_variable ("BASH", name);
+  free (name);
+
+  /* Make the exported environment variable SHELL be the user's login
+     shell.  Note that the `tset' command looks at this variable
+     to determine what style of commands to output; if it ends in "csh",
+     then C-shell commands are output, else Bourne shell commands. */
+  set_shell_var ();
+
+  /* Make a variable called BASH_VERSION which contains the version info. */
+  bind_variable ("BASH_VERSION", shell_version_string ());
+#if defined (ARRAY_VARS)
+  make_vers_array ();
+#endif
+
+  if (command_execution_string)
+    bind_variable ("BASH_EXECUTION_STRING", command_execution_string);
+
+  /* Find out if we're supposed to be in Posix.2 mode via an
+     environment variable. */
+  temp_var = find_variable ("POSIXLY_CORRECT");
+  if (!temp_var)
+    temp_var = find_variable ("POSIX_PEDANTIC");
+  if (temp_var && imported_p (temp_var))
+    sv_strict_posix (temp_var->name);
+
+#if defined (HISTORY)
+  /* Set history variables to defaults, and then do whatever we would
+     do if the variable had just been set.  Do this only in the case
+     that we are remembering commands on the history list. */
+  if (remember_on_history)
+    {
+      name = bash_tilde_expand (posixly_correct ? "~/.sh_history" : "~/.bash_history", 0);
+
+      set_if_not ("HISTFILE", name);
+      free (name);
+
+      set_if_not ("HISTSIZE", "500");
+      sv_histsize ("HISTSIZE");
+    }
+#endif /* HISTORY */
+
+  /* Seed the random number generator. */
+  sbrand (dollar_dollar_pid + shell_start_time);
+
+  /* Handle some "special" variables that we may have inherited from a
+     parent shell. */
+  if (interactive_shell)
+    {
+      temp_var = find_variable ("IGNOREEOF");
+      if (!temp_var)
+       temp_var = find_variable ("ignoreeof");
+      if (temp_var && imported_p (temp_var))
+       sv_ignoreeof (temp_var->name);
+    }
+
+#if defined (HISTORY)
+  if (interactive_shell && remember_on_history)
+    {
+      sv_history_control ("HISTCONTROL");
+      sv_histignore ("HISTIGNORE");
+    }
+#endif /* HISTORY */
+
+     /*
+      * 24 October 2001
+      *
+      * I'm tired of the arguing and bug reports.  Bash now leaves SSH_CLIENT
+      * and SSH2_CLIENT alone.  I'm going to rely on the shell_level check in
+      * isnetconn() to avoid running the startup files more often than wanted.
+      * That will, of course, only work if the user's login shell is bash, so
+      * I've made that behavior conditional on SSH_SOURCE_BASHRC being defined
+      * in config-top.h.
+      */
+#if 0
+  temp_var = find_variable ("SSH_CLIENT");
+  if (temp_var && imported_p (temp_var))
+    {
+      VUNSETATTR (temp_var, att_exported);
+      array_needs_making = 1;
+    }
+  temp_var = find_variable ("SSH2_CLIENT");
+  if (temp_var && imported_p (temp_var))
+    {
+      VUNSETATTR (temp_var, att_exported);
+      array_needs_making = 1;
+    }
+#endif
+
+  /* Get the user's real and effective user ids. */
+  uidset ();
+
+  /* Initialize the dynamic variables, and seed their values. */
+  initialize_dynamic_variables ();
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*          Setting values for special shell variables             */
+/*                                                                 */
+/* **************************************************************** */
+
+static void
+set_machine_vars ()
+{
+  SHELL_VAR *temp_var;
+
+  temp_var = set_if_not ("HOSTTYPE", HOSTTYPE);
+  temp_var = set_if_not ("OSTYPE", OSTYPE);
+  temp_var = set_if_not ("MACHTYPE", MACHTYPE);
+
+  temp_var = set_if_not ("HOSTNAME", current_host_name);
+}
+
+/* Set $HOME to the information in the password file if we didn't get
+   it from the environment. */
+
+/* This function is not static so the tilde and readline libraries can
+   use it. */
+char *
+sh_get_home_dir ()
+{
+  if (current_user.home_dir == 0)
+    get_current_user_info ();
+  return current_user.home_dir;
+}
+
+static void
+set_home_var ()
+{
+  SHELL_VAR *temp_var;
+
+  temp_var = find_variable ("HOME");
+  if (temp_var == 0)
+    temp_var = bind_variable ("HOME", sh_get_home_dir ());
+#if 0
+  VSETATTR (temp_var, att_exported);
+#endif
+}
+
+/* Set $SHELL to the user's login shell if it is not already set.  Call
+   get_current_user_info if we haven't already fetched the shell. */
+static void
+set_shell_var ()
+{
+  SHELL_VAR *temp_var;
+
+  temp_var = find_variable ("SHELL");
+  if (temp_var == 0)
+    {
+      if (current_user.shell == 0)
+       get_current_user_info ();
+      temp_var = bind_variable ("SHELL", current_user.shell);
+    }
+#if 0
+  VSETATTR (temp_var, att_exported);
+#endif
+}
+
+static char *
+get_bash_name ()
+{
+  char *name;
+
+  if ((login_shell == 1) && RELPATH(shell_name))
+    {
+      if (current_user.shell == 0)
+       get_current_user_info ();
+      name = savestring (current_user.shell);
+    }
+  else if (ABSPATH(shell_name))
+    name = savestring (shell_name);
+  else if (shell_name[0] == '.' && shell_name[1] == '/')
+    {
+      /* Fast path for common case. */
+      char *cdir;
+      int len;
+
+      cdir = get_string_value ("PWD");
+      if (cdir)
+       {
+         len = strlen (cdir);
+         name = (char *)xmalloc (len + strlen (shell_name) + 1);
+         strcpy (name, cdir);
+         strcpy (name + len, shell_name + 1);
+       }
+      else
+       name = savestring (shell_name);
+    }
+  else
+    {
+      char *tname;
+      int s;
+
+      tname = find_user_command (shell_name);
+
+      if (tname == 0)
+       {
+         /* Try the current directory.  If there is not an executable
+            there, just punt and use the login shell. */
+         s = file_status (shell_name);
+         if (s & FS_EXECABLE)
+           {
+             tname = make_absolute (shell_name, get_string_value ("PWD"));
+             if (*shell_name == '.')
+               {
+                 name = sh_canonpath (tname, PATH_CHECKDOTDOT|PATH_CHECKEXISTS);
+                 if (name == 0)
+                   name = tname;
+                 else
+                   free (tname);
+               }
+            else
+               name = tname;
+           }
+         else
+           {
+             if (current_user.shell == 0)
+               get_current_user_info ();
+             name = savestring (current_user.shell);
+           }
+       }
+      else
+       {
+         name = full_pathname (tname);
+         free (tname);
+       }
+    }
+
+  return (name);
+}
+
+void
+adjust_shell_level (change)
+     int change;
+{
+  char new_level[5], *old_SHLVL;
+  intmax_t old_level;
+  SHELL_VAR *temp_var;
+
+  old_SHLVL = get_string_value ("SHLVL");
+  if (old_SHLVL == 0 || *old_SHLVL == '\0' || legal_number (old_SHLVL, &old_level) == 0)
+    old_level = 0;
+
+  shell_level = old_level + change;
+  if (shell_level < 0)
+    shell_level = 0;
+  else if (shell_level > 1000)
+    {
+      internal_warning (_("shell level (%d) too high, resetting to 1"), shell_level);
+      shell_level = 1;
+    }
+
+  /* We don't need the full generality of itos here. */
+  if (shell_level < 10)
+    {
+      new_level[0] = shell_level + '0';
+      new_level[1] = '\0';
+    }
+  else if (shell_level < 100)
+    {
+      new_level[0] = (shell_level / 10) + '0';
+      new_level[1] = (shell_level % 10) + '0';
+      new_level[2] = '\0';
+    }
+  else if (shell_level < 1000)
+    {
+      new_level[0] = (shell_level / 100) + '0';
+      old_level = shell_level % 100;
+      new_level[1] = (old_level / 10) + '0';
+      new_level[2] = (old_level % 10) + '0';
+      new_level[3] = '\0';
+    }
+
+  temp_var = bind_variable ("SHLVL", new_level);
+  set_auto_export (temp_var);
+}
+
+static void
+initialize_shell_level ()
+{
+  adjust_shell_level (1);
+}
+
+/* If we got PWD from the environment, update our idea of the current
+   working directory.  In any case, make sure that PWD exists before
+   checking it.  It is possible for getcwd () to fail on shell startup,
+   and in that case, PWD would be undefined.  If this is an interactive
+   login shell, see if $HOME is the current working directory, and if
+   that's not the same string as $PWD, set PWD=$HOME. */
+
+void
+set_pwd ()
+{
+  SHELL_VAR *temp_var, *home_var;
+  char *temp_string, *home_string;
+
+  home_var = find_variable ("HOME");
+  home_string = home_var ? value_cell (home_var) : (char *)NULL;
+
+  temp_var = find_variable ("PWD");
+  if (temp_var && imported_p (temp_var) &&
+      (temp_string = value_cell (temp_var)) &&
+      same_file (temp_string, ".", (struct stat *)NULL, (struct stat *)NULL))
+    set_working_directory (temp_string);
+  else if (home_string && interactive_shell && login_shell &&
+          same_file (home_string, ".", (struct stat *)NULL, (struct stat *)NULL))
+    {
+      set_working_directory (home_string);
+      temp_var = bind_variable ("PWD", home_string);
+      set_auto_export (temp_var);
+    }
+  else
+    {
+      temp_string = get_working_directory ("shell-init");
+      if (temp_string)
+       {
+         temp_var = bind_variable ("PWD", temp_string);
+         set_auto_export (temp_var);
+         free (temp_string);
+       }
+    }
+
+  /* According to the Single Unix Specification, v2, $OLDPWD is an
+     `environment variable' and therefore should be auto-exported.
+     Make a dummy invisible variable for OLDPWD, and mark it as exported. */
+  temp_var = bind_variable ("OLDPWD", (char *)NULL);
+  VSETATTR (temp_var, (att_exported | att_invisible));
+}
+
+/* Make a variable $PPID, which holds the pid of the shell's parent.  */
+void
+set_ppid ()
+{
+  char namebuf[INT_STRLEN_BOUND(pid_t) + 1], *name;
+  SHELL_VAR *temp_var;
+
+  name = inttostr (getppid (), namebuf, sizeof(namebuf));
+  temp_var = find_variable ("PPID");
+  if (temp_var)
+    VUNSETATTR (temp_var, (att_readonly | att_exported));
+  temp_var = bind_variable ("PPID", name);
+  VSETATTR (temp_var, (att_readonly | att_integer));
+}
+
+static void
+uidset ()
+{
+  char buff[INT_STRLEN_BOUND(uid_t) + 1], *b;
+  register SHELL_VAR *v;
+
+  b = inttostr (current_user.uid, buff, sizeof (buff));
+  v = find_variable ("UID");
+  if (v == 0)
+    {
+      v = bind_variable ("UID", b);
+      VSETATTR (v, (att_readonly | att_integer));
+    }
+
+  if (current_user.euid != current_user.uid)
+    b = inttostr (current_user.euid, buff, sizeof (buff));
+
+  v = find_variable ("EUID");
+  if (v == 0)
+    {
+      v = bind_variable ("EUID", b);
+      VSETATTR (v, (att_readonly | att_integer));
+    }
+}
+
+#if defined (ARRAY_VARS)
+static void
+make_vers_array ()
+{
+  SHELL_VAR *vv;
+  ARRAY *av;
+  char *s, d[32], b[INT_STRLEN_BOUND(int) + 1];
+
+  unbind_variable ("BASH_VERSINFO");
+
+  vv = make_new_array_variable ("BASH_VERSINFO");
+  av = array_cell (vv);
+  strcpy (d, dist_version);
+  s = xstrchr (d, '.');
+  if (s)
+    *s++ = '\0';
+  array_insert (av, 0, d);
+  array_insert (av, 1, s);
+  s = inttostr (patch_level, b, sizeof (b));
+  array_insert (av, 2, s);
+  s = inttostr (build_version, b, sizeof (b));
+  array_insert (av, 3, s);
+  array_insert (av, 4, release_status);
+  array_insert (av, 5, MACHTYPE);
+
+  VSETATTR (vv, att_readonly);
+}
+#endif /* ARRAY_VARS */
+
+/* Set the environment variables $LINES and $COLUMNS in response to
+   a window size change. */
+void
+sh_set_lines_and_columns (lines, cols)
+     int lines, cols;
+{
+  char val[INT_STRLEN_BOUND(int) + 1], *v;
+
+  v = inttostr (lines, val, sizeof (val));
+  bind_variable ("LINES", v);
+
+  v = inttostr (cols, val, sizeof (val));
+  bind_variable ("COLUMNS", v);
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*                Printing variables and values                    */
+/*                                                                 */
+/* **************************************************************** */
+
+/* Print LIST (a list of shell variables) to stdout in such a way that
+   they can be read back in. */
+void
+print_var_list (list)
+     register SHELL_VAR **list;
+{
+  register int i;
+  register SHELL_VAR *var;
+
+  for (i = 0; list && (var = list[i]); i++)
+    if (invisible_p (var) == 0)
+      print_assignment (var);
+}
+
+/* Print LIST (a list of shell functions) to stdout in such a way that
+   they can be read back in. */
+void
+print_func_list (list)
+     register SHELL_VAR **list;
+{
+  register int i;
+  register SHELL_VAR *var;
+
+  for (i = 0; list && (var = list[i]); i++)
+    {
+      printf ("%s ", var->name);
+      print_var_function (var);
+      printf ("\n");
+    }
+}
+      
+/* Print the value of a single SHELL_VAR.  No newline is
+   output, but the variable is printed in such a way that
+   it can be read back in. */
+void
+print_assignment (var)
+     SHELL_VAR *var;
+{
+  if (var_isset (var) == 0)
+    return;
+
+  if (function_p (var))
+    {
+      printf ("%s", var->name);
+      print_var_function (var);
+      printf ("\n");
+    }
+#if defined (ARRAY_VARS)
+  else if (array_p (var))
+    print_array_assignment (var, 0);
+#endif /* ARRAY_VARS */
+  else
+    {
+      printf ("%s=", var->name);
+      print_var_value (var, 1);
+      printf ("\n");
+    }
+}
+
+/* Print the value cell of VAR, a shell variable.  Do not print
+   the name, nor leading/trailing newline.  If QUOTE is non-zero,
+   and the value contains shell metacharacters, quote the value
+   in such a way that it can be read back in. */
+void
+print_var_value (var, quote)
+     SHELL_VAR *var;
+     int quote;
+{
+  char *t;
+
+  if (var_isset (var) == 0)
+    return;
+
+  if (quote && posixly_correct == 0 && ansic_shouldquote (value_cell (var)))
+    {
+      t = ansic_quote (value_cell (var), 0, (int *)0);
+      printf ("%s", t);
+      free (t);
+    }
+  else if (quote && sh_contains_shell_metas (value_cell (var)))
+    {
+      t = sh_single_quote (value_cell (var));
+      printf ("%s", t);
+      free (t);
+    }
+  else
+    printf ("%s", value_cell (var));
+}
+
+/* Print the function cell of VAR, a shell variable.  Do not
+   print the name, nor leading/trailing newline. */
+void
+print_var_function (var)
+     SHELL_VAR *var;
+{
+  if (function_p (var) && var_isset (var))
+    printf ("%s", named_function_string ((char *)NULL, function_cell(var), 1));
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*                     Dynamic Variables                           */
+/*                                                                 */
+/* **************************************************************** */
+
+/* DYNAMIC VARIABLES
+
+   These are variables whose values are generated anew each time they are
+   referenced.  These are implemented using a pair of function pointers
+   in the struct variable: assign_func, which is called from bind_variable
+   and, if arrays are compiled into the shell, some of the functions in
+   arrayfunc.c, and dynamic_value, which is called from find_variable.
+
+   assign_func is called from bind_variable_internal, if
+   bind_variable_internal discovers that the variable being assigned to
+   has such a function.  The function is called as
+       SHELL_VAR *temp = (*(entry->assign_func)) (entry, value, ind)
+   and the (SHELL_VAR *)temp is returned as the value of bind_variable.  It
+   is usually ENTRY (self).  IND is an index for an array variable, and
+   unused otherwise.
+
+   dynamic_value is called from find_variable_internal to return a `new'
+   value for the specified dynamic varible.  If this function is NULL,
+   the variable is treated as a `normal' shell variable.  If it is not,
+   however, then this function is called like this:
+       tempvar = (*(var->dynamic_value)) (var);
+
+   Sometimes `tempvar' will replace the value of `var'.  Other times, the
+   shell will simply use the string value.  Pretty object-oriented, huh?
+
+   Be warned, though: if you `unset' a special variable, it loses its
+   special meaning, even if you subsequently set it.
+
+   The special assignment code would probably have been better put in
+   subst.c: do_assignment_internal, in the same style as
+   stupidly_hack_special_variables, but I wanted the changes as
+   localized as possible.  */
+
+#define INIT_DYNAMIC_VAR(var, val, gfunc, afunc) \
+  do \
+    { \
+      v = bind_variable (var, (val)); \
+      v->dynamic_value = gfunc; \
+      v->assign_func = afunc; \
+    } \
+  while (0)
+
+#define INIT_DYNAMIC_ARRAY_VAR(var, gfunc, afunc) \
+  do \
+    { \
+      v = make_new_array_variable (var); \
+      v->dynamic_value = gfunc; \
+      v->assign_func = afunc; \
+    } \
+  while (0)
+
+static SHELL_VAR *
+null_assign (self, value, unused)
+     SHELL_VAR *self;
+     char *value;
+     arrayind_t unused;
+{
+  return (self);
+}
+
+#if defined (ARRAY_VARS)
+static SHELL_VAR *
+null_array_assign (self, value, ind)
+     SHELL_VAR *self;
+     char *value;
+     arrayind_t ind;
+{
+  return (self);
+}
+#endif
+
+/* Degenerate `dynamic_value' function; just returns what's passed without
+   manipulation. */
+static SHELL_VAR *
+get_self (self)
+     SHELL_VAR *self;
+{
+  return (self);
+}
+
+#if defined (ARRAY_VARS)
+/* A generic dynamic array variable initializer.  Intialize array variable
+   NAME with dynamic value function GETFUNC and assignment function SETFUNC. */
+static SHELL_VAR *
+init_dynamic_array_var (name, getfunc, setfunc, attrs)
+     char *name;
+     sh_var_value_func_t *getfunc;
+     sh_var_assign_func_t *setfunc;
+     int attrs;
+{
+  SHELL_VAR *v;
+
+  v = find_variable (name);
+  if (v)
+    return (v);
+  INIT_DYNAMIC_ARRAY_VAR (name, getfunc, setfunc);
+  if (attrs)
+    VSETATTR (v, attrs);
+  return v;
+}
+#endif
+
+
+/* The value of $SECONDS.  This is the number of seconds since shell
+   invocation, or, the number of seconds since the last assignment + the
+   value of the last assignment. */
+static intmax_t seconds_value_assigned;
+
+static SHELL_VAR *
+assign_seconds (self, value, unused)
+     SHELL_VAR *self;
+     char *value;
+     arrayind_t unused;
+{
+  if (legal_number (value, &seconds_value_assigned) == 0)
+    seconds_value_assigned = 0;
+  shell_start_time = NOW;
+  return (self);
+}
+
+static SHELL_VAR *
+get_seconds (var)
+     SHELL_VAR *var;
+{
+  time_t time_since_start;
+  char *p;
+
+  time_since_start = NOW - shell_start_time;
+  p = itos(seconds_value_assigned + time_since_start);
+
+  FREE (value_cell (var));
+
+  VSETATTR (var, att_integer);
+  var_setvalue (var, p);
+  return (var);
+}
+
+static SHELL_VAR *
+init_seconds_var ()
+{
+  SHELL_VAR *v;
+
+  v = find_variable ("SECONDS");
+  if (v)
+    {
+      if (legal_number (value_cell(v), &seconds_value_assigned) == 0)
+       seconds_value_assigned = 0;
+    }
+  INIT_DYNAMIC_VAR ("SECONDS", (v ? value_cell (v) : (char *)NULL), get_seconds, assign_seconds);
+  return v;      
+}
+     
+/* The random number seed.  You can change this by setting RANDOM. */
+static unsigned long rseed = 1;
+static int last_random_value;
+
+/* A linear congruential random number generator based on the example
+   one in the ANSI C standard.  This one isn't very good, but a more
+   complicated one is overkill. */
+
+/* Returns a pseudo-random number between 0 and 32767. */
+static int
+brand ()
+{
+  rseed = rseed * 1103515245 + 12345;
+  return ((unsigned int)((rseed >> 16) & 32767));      /* was % 32768 */
+}
+
+/* Set the random number generator seed to SEED. */
+static void
+sbrand (seed)
+     unsigned long seed;
+{
+  rseed = seed;
+  last_random_value = 0;
+}
+
+static SHELL_VAR *
+assign_random (self, value, unused)
+     SHELL_VAR *self;
+     char *value;
+     arrayind_t unused;
+{
+  sbrand (strtoul (value, (char **)NULL, 10));
+  return (self);
+}
+
+int
+get_random_number ()
+{
+  int rv;
+
+  /* Reset for command and process substitution. */
+  if (subshell_environment)
+    sbrand (rseed + getpid() + NOW);
+
+  do
+    rv = brand ();
+  while (rv == last_random_value);
+  return rv;
+}
+
+static SHELL_VAR *
+get_random (var)
+     SHELL_VAR *var;
+{
+  int rv;
+  char *p;
+
+  rv = get_random_number ();
+  last_random_value = rv;
+  p = itos (rv);
+
+  FREE (value_cell (var));
+
+  VSETATTR (var, att_integer);
+  var_setvalue (var, p);
+  return (var);
+}
+
+static SHELL_VAR *
+assign_lineno (var, value, unused)
+     SHELL_VAR *var;
+     char *value;
+     arrayind_t unused;
+{
+  intmax_t new_value;
+
+  if (value == 0 || *value == '\0' || legal_number (value, &new_value) == 0)
+    new_value = 0;
+  line_number = new_value;
+  return var;
+}
+
+/* Function which returns the current line number. */
+static SHELL_VAR *
+get_lineno (var)
+     SHELL_VAR *var;
+{
+  char *p;
+  int ln;
+
+  ln = executing_line_number ();
+  p = itos (ln);
+  FREE (value_cell (var));
+  var_setvalue (var, p);
+  return (var);
+}
+
+static SHELL_VAR *
+assign_subshell (var, value, unused)
+     SHELL_VAR *var;
+     char *value;
+     arrayind_t unused;
+{
+  intmax_t new_value;
+
+  if (value == 0 || *value == '\0' || legal_number (value, &new_value) == 0)
+    new_value = 0;
+  subshell_level = new_value;
+  return var;
+}
+
+static SHELL_VAR *
+get_subshell (var)
+     SHELL_VAR *var;
+{
+  char *p;
+
+  p = itos (subshell_level);
+  FREE (value_cell (var));
+  var_setvalue (var, p);
+  return (var);
+}
+
+static SHELL_VAR *
+get_bash_command (var)
+     SHELL_VAR *var;
+{
+  char *p;
+
+  p = savestring (the_printed_command_except_trap);
+  FREE (value_cell (var));
+  var_setvalue (var, p);
+  return (var);
+}
+
+#if defined (HISTORY)
+static SHELL_VAR *
+get_histcmd (var)
+     SHELL_VAR *var;
+{
+  char *p;
+
+  p = itos (history_number ());
+  FREE (value_cell (var));
+  var_setvalue (var, p);
+  return (var);
+}
+#endif
+
+#if defined (READLINE)
+/* When this function returns, VAR->value points to malloced memory. */
+static SHELL_VAR *
+get_comp_wordbreaks (var)
+     SHELL_VAR *var;
+{
+  char *p;
+
+  /* If we don't have anything yet, assign a default value. */
+  if (rl_completer_word_break_characters == 0 && bash_readline_initialized == 0)
+    enable_hostname_completion (perform_hostname_completion);
+
+#if 0
+  FREE (value_cell (var));
+  p = savestring (rl_completer_word_break_characters);
+  
+  var_setvalue (var, p);
+#else
+  var_setvalue (var, rl_completer_word_break_characters);
+#endif
+
+  return (var);
+}
+
+/* When this function returns, rl_completer_word_break_characters points to
+   malloced memory. */
+static SHELL_VAR *
+assign_comp_wordbreaks (self, value, unused)
+     SHELL_VAR *self;
+     char *value;
+     arrayind_t unused;
+{
+  if (rl_completer_word_break_characters &&
+      rl_completer_word_break_characters != rl_basic_word_break_characters)
+    free (rl_completer_word_break_characters);
+
+  rl_completer_word_break_characters = savestring (value);
+  return self;
+}
+#endif /* READLINE */
+
+#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS)
+static SHELL_VAR *
+assign_dirstack (self, value, ind)
+     SHELL_VAR *self;
+     char *value;
+     arrayind_t ind;
+{
+  set_dirstack_element (ind, 1, value);
+  return self;
+}
+
+static SHELL_VAR *
+get_dirstack (self)
+     SHELL_VAR *self;
+{
+  ARRAY *a;
+  WORD_LIST *l;
+
+  l = get_directory_stack ();
+  a = array_from_word_list (l);
+  array_dispose (array_cell (self));
+  dispose_words (l);
+  var_setarray (self, a);
+  return self;
+}
+#endif /* PUSHD AND POPD && ARRAY_VARS */
+
+#if defined (ARRAY_VARS)
+/* We don't want to initialize the group set with a call to getgroups()
+   unless we're asked to, but we only want to do it once. */
+static SHELL_VAR *
+get_groupset (self)
+     SHELL_VAR *self;
+{
+  register int i;
+  int ng;
+  ARRAY *a;
+  static char **group_set = (char **)NULL;
+
+  if (group_set == 0)
+    {
+      group_set = get_group_list (&ng);
+      a = array_cell (self);
+      for (i = 0; i < ng; i++)
+       array_insert (a, i, group_set[i]);
+    }
+  return (self);
+}
+#endif /* ARRAY_VARS */
+
+/* If ARRAY_VARS is not defined, this just returns the name of any
+   currently-executing function.  If we have arrays, it's a call stack. */
+static SHELL_VAR *
+get_funcname (self)
+     SHELL_VAR *self;
+{
+#if ! defined (ARRAY_VARS)
+  char *t;
+  if (variable_context && this_shell_function)
+    {
+      FREE (value_cell (self));
+      t = savestring (this_shell_function->name);
+      var_setvalue (self, t);
+    }
+#endif
+  return (self);
+}
+
+void
+make_funcname_visible (on_or_off)
+     int on_or_off;
+{
+  SHELL_VAR *v;
+
+  v = find_variable ("FUNCNAME");
+  if (v == 0 || v->dynamic_value == 0)
+    return;
+
+  if (on_or_off)
+    VUNSETATTR (v, att_invisible);
+  else
+    VSETATTR (v, att_invisible);
+}
+
+static SHELL_VAR *
+init_funcname_var ()
+{
+  SHELL_VAR *v;
+
+  v = find_variable ("FUNCNAME");
+  if (v)
+    return v;
+#if defined (ARRAY_VARS)
+  INIT_DYNAMIC_ARRAY_VAR ("FUNCNAME", get_funcname, null_array_assign);
+#else
+  INIT_DYNAMIC_VAR ("FUNCNAME", (char *)NULL, get_funcname, null_assign);
+#endif
+  VSETATTR (v, att_invisible|att_noassign);
+  return v;
+}
+
+static void
+initialize_dynamic_variables ()
+{
+  SHELL_VAR *v;
+
+  v = init_seconds_var ();
+
+  INIT_DYNAMIC_VAR ("BASH_COMMAND", (char *)NULL, get_bash_command, (sh_var_assign_func_t *)NULL);
+  INIT_DYNAMIC_VAR ("BASH_SUBSHELL", (char *)NULL, get_subshell, assign_subshell);
+
+  INIT_DYNAMIC_VAR ("RANDOM", (char *)NULL, get_random, assign_random);
+  INIT_DYNAMIC_VAR ("LINENO", (char *)NULL, get_lineno, assign_lineno);
+
+#if defined (HISTORY)
+  INIT_DYNAMIC_VAR ("HISTCMD", (char *)NULL, get_histcmd, (sh_var_assign_func_t *)NULL);
+#endif
+
+#if defined (READLINE)
+  INIT_DYNAMIC_VAR ("COMP_WORDBREAKS", (char *)NULL, get_comp_wordbreaks, assign_comp_wordbreaks);
+#endif
+
+#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS)
+  v = init_dynamic_array_var ("DIRSTACK", get_dirstack, assign_dirstack, 0);
+#endif /* PUSHD_AND_POPD && ARRAY_VARS */
+
+#if defined (ARRAY_VARS)
+  v = init_dynamic_array_var ("GROUPS", get_groupset, null_array_assign, att_noassign);
+
+#  if defined (DEBUGGER)
+  v = init_dynamic_array_var ("BASH_ARGC", get_self, null_array_assign, (att_invisible|att_noassign));
+  v = init_dynamic_array_var ("BASH_ARGV", get_self, null_array_assign, (att_invisible|att_noassign));
+#  endif /* DEBUGGER */
+  v = init_dynamic_array_var ("BASH_SOURCE", get_self, null_array_assign, (att_invisible|att_noassign));
+  v = init_dynamic_array_var ("BASH_LINENO", get_self, null_array_assign, (att_invisible|att_noassign));
+#endif
+
+  v = init_funcname_var ();
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*             Retrieving variables and values                     */
+/*                                                                 */
+/* **************************************************************** */
+
+/* How to get a pointer to the shell variable or function named NAME.
+   HASHED_VARS is a pointer to the hash table containing the list
+   of interest (either variables or functions). */
+
+static SHELL_VAR *
+hash_lookup (name, hashed_vars)
+     const char *name;
+     HASH_TABLE *hashed_vars;
+{
+  BUCKET_CONTENTS *bucket;
+
+  bucket = hash_search (name, hashed_vars, 0);
+  return (bucket ? (SHELL_VAR *)bucket->data : (SHELL_VAR *)NULL);
+}
+
+SHELL_VAR *
+var_lookup (name, vcontext)
+     const char *name;
+     VAR_CONTEXT *vcontext;
+{
+  VAR_CONTEXT *vc;
+  SHELL_VAR *v;
+
+  v = (SHELL_VAR *)NULL;
+  for (vc = vcontext; vc; vc = vc->down)
+    if (v = hash_lookup (name, vc->table))
+      break;
+
+  return v;
+}
+
+/* Look up the variable entry named NAME.  If SEARCH_TEMPENV is non-zero,
+   then also search the temporarily built list of exported variables.
+   The lookup order is:
+       temporary_env
+        shell_variables list
+*/
+
+SHELL_VAR *
+find_variable_internal (name, force_tempenv)
+     const char *name;
+     int force_tempenv;
+{
+  SHELL_VAR *var;
+  int search_tempenv;
+
+  var = (SHELL_VAR *)NULL;
+
+  /* If explicitly requested, first look in the temporary environment for
+     the variable.  This allows constructs such as "foo=x eval 'echo $foo'"
+     to get the `exported' value of $foo.  This happens if we are executing
+     a function or builtin, or if we are looking up a variable in a
+     "subshell environment". */
+  search_tempenv = force_tempenv || (expanding_redir == 0 && subshell_environment);
+
+  if (search_tempenv && temporary_env)         
+    var = hash_lookup (name, temporary_env);
+
+  if (var == 0)
+    var = var_lookup (name, shell_variables);
+
+  if (var == 0)
+    return ((SHELL_VAR *)NULL);
+
+  return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var);
+}
+
+/* Look up the variable entry named NAME.  Returns the entry or NULL. */
+SHELL_VAR *
+find_variable (name)
+     const char *name;
+{
+  return (find_variable_internal (name, (expanding_redir == 0 && this_shell_builtin != 0)));
+}
+
+/* Look up the function entry whose name matches STRING.
+   Returns the entry or NULL. */
+SHELL_VAR *
+find_function (name)
+     const char *name;
+{
+  return (hash_lookup (name, shell_functions));
+}
+
+/* Find the function definition for the shell function named NAME.  Returns
+   the entry or NULL. */
+FUNCTION_DEF *
+find_function_def (name)
+     const char *name;
+{
+  return ((FUNCTION_DEF *)hash_lookup (name, shell_function_defs));
+}
+
+/* Return the value of VAR.  VAR is assumed to have been the result of a
+   lookup without any subscript, if arrays are compiled into the shell. */
+char *
+get_variable_value (var)
+     SHELL_VAR *var;
+{
+  if (var == 0)
+    return ((char *)NULL);
+#if defined (ARRAY_VARS)
+  else if (array_p (var))
+    return (array_reference (array_cell (var), 0));
+#endif
+  else
+    return (value_cell (var));
+}
+
+/* Return the string value of a variable.  Return NULL if the variable
+   doesn't exist.  Don't cons a new string.  This is a potential memory
+   leak if the variable is found in the temporary environment.  Since
+   functions and variables have separate name spaces, returns NULL if
+   var_name is a shell function only. */
+char *
+get_string_value (var_name)
+     const char *var_name;
+{
+  SHELL_VAR *var;
+
+  var = find_variable (var_name);
+  return ((var) ? get_variable_value (var) : (char *)NULL);
+}
+
+/* This is present for use by the tilde and readline libraries. */
+char *
+sh_get_env_value (v)
+     const char *v;
+{
+  return get_string_value (v);
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*               Creating and setting variables                    */
+/*                                                                 */
+/* **************************************************************** */
+
+/* Set NAME to VALUE if NAME has no value. */
+SHELL_VAR *
+set_if_not (name, value)
+     char *name, *value;
+{
+  SHELL_VAR *v;
+
+  v = find_variable (name);
+  if (v == 0)
+    v = bind_variable_internal (name, value, global_variables->table, HASH_NOSRCH);
+  return (v);
+}
+
+/* Create a local variable referenced by NAME. */
+SHELL_VAR *
+make_local_variable (name)
+     const char *name;
+{
+  SHELL_VAR *new_var, *old_var;
+  VAR_CONTEXT *vc;
+  int was_tmpvar;
+  char *tmp_value;
+
+  /* local foo; local foo;  is a no-op. */
+  old_var = find_variable (name);
+  if (old_var && local_p (old_var) && old_var->context == variable_context)
+    return (old_var);
+
+  was_tmpvar = old_var && tempvar_p (old_var);
+  if (was_tmpvar)
+    tmp_value = value_cell (old_var);
+
+  for (vc = shell_variables; vc; vc = vc->down)
+    if (vc_isfuncenv (vc) && vc->scope == variable_context)
+      break;
+
+  if (vc == 0)
+    {
+      internal_error (_("make_local_variable: no function context at current scope"));
+      return ((SHELL_VAR *)NULL);
+    }
+  else if (vc->table == 0)
+    vc->table = hash_create (TEMPENV_HASH_BUCKETS);
+
+  /* Since this is called only from the local/declare/typeset code, we can
+     call builtin_error here without worry (of course, it will also work
+     for anything that sets this_command_name).  Variables with the `noassign'
+     attribute may not be made local.  The test against old_var's context
+     level is to disallow local copies of readonly global variables (since I
+     believe that this could be a security hole).  Readonly copies of calling
+     function local variables are OK. */
+  if (old_var && (noassign_p (old_var) ||
+                (readonly_p (old_var) && old_var->context == 0)))
+    {
+      if (readonly_p (old_var))
+       sh_readonly (name);
+      return ((SHELL_VAR *)NULL);
+    }
+
+  if (old_var == 0)
+    new_var = bind_variable_internal (name, "", vc->table, HASH_NOSRCH);
+  else
+    {
+      new_var = make_new_variable (name, vc->table);
+
+      /* If we found this variable in one of the temporary environments,
+        inherit its value.  Watch to see if this causes problems with
+        things like `x=4 local x'. */
+      if (was_tmpvar)
+        var_setvalue (new_var, savestring (tmp_value));
+
+      new_var->attributes = exported_p (old_var) ? att_exported : 0;
+    }
+
+  vc->flags |= VC_HASLOCAL;
+
+  new_var->context = variable_context;
+  VSETATTR (new_var, att_local);
+
+  if (ifsname (name))
+    setifs (new_var);
+
+  return (new_var);
+}
+
+#if defined (ARRAY_VARS)
+SHELL_VAR *
+make_local_array_variable (name)
+     char *name;
+{
+  SHELL_VAR *var;
+  ARRAY *array;
+
+  var = make_local_variable (name);
+  if (var == 0 || array_p (var))
+    return var;
+
+  array = array_create ();
+
+  FREE (value_cell(var));
+  var_setarray (var, array);
+  VSETATTR (var, att_array);
+  return var;
+}
+#endif /* ARRAY_VARS */
+
+/* Create a new shell variable with name NAME. */
+static SHELL_VAR *
+new_shell_variable (name)
+     const char *name;
+{
+  SHELL_VAR *entry;
+
+  entry = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
+
+  entry->name = savestring (name);
+  var_setvalue (entry, (char *)NULL);
+  CLEAR_EXPORTSTR (entry);
+
+  entry->dynamic_value = (sh_var_value_func_t *)NULL;
+  entry->assign_func = (sh_var_assign_func_t *)NULL;
+
+  entry->attributes = 0;
+
+  /* Always assume variables are to be made at toplevel!
+     make_local_variable has the responsibilty of changing the
+     variable context. */
+  entry->context = 0;
+
+  return (entry);
+}
+
+/* Create a new shell variable with name NAME and add it to the hash table
+   TABLE. */
+static SHELL_VAR *
+make_new_variable (name, table)
+     const char *name;
+     HASH_TABLE *table;
+{
+  SHELL_VAR *entry;
+  BUCKET_CONTENTS *elt;
+
+  entry = new_shell_variable (name);
+
+  /* Make sure we have a shell_variables hash table to add to. */
+  if (shell_variables == 0)
+    {
+      shell_variables = global_variables = new_var_context ((char *)NULL, 0);
+      shell_variables->scope = 0;
+      shell_variables->table = hash_create (0);
+    }
+
+  elt = hash_insert (savestring (name), table, HASH_NOSRCH);
+  elt->data = (PTR_T)entry;
+
+  return entry;
+}
+
+#if defined (ARRAY_VARS)
+SHELL_VAR *
+make_new_array_variable (name)
+     char *name;
+{
+  SHELL_VAR *entry;
+  ARRAY *array;
+
+  entry = make_new_variable (name, global_variables->table);
+  array = array_create ();
+  var_setarray (entry, array);
+  VSETATTR (entry, att_array);
+  return entry;
+}
+#endif
+
+char *
+make_variable_value (var, value)
+     SHELL_VAR *var;
+     char *value;
+{
+  char *retval;
+  intmax_t lval;
+  int expok;
+
+  /* If this variable has had its type set to integer (via `declare -i'),
+     then do expression evaluation on it and store the result.  The
+     functions in expr.c (evalexp()) and bind_int_variable() are responsible
+     for turning off the integer flag if they don't want further
+     evaluation done. */
+  if (integer_p (var))
+    {
+      lval = evalexp (value, &expok);
+      if (expok == 0)
+       jump_to_top_level (DISCARD);
+      retval = itos (lval);
+    }
+  else if (value)
+    {
+      if (*value)
+       retval = savestring (value);
+      else
+       {
+         retval = (char *)xmalloc (1);
+         retval[0] = '\0';
+       }
+    }
+  else
+    retval = (char *)NULL;
+
+  return retval;
+}
+
+/* Bind a variable NAME to VALUE in the HASH_TABLE TABLE, which may be the
+   temporary environment (but usually is not). */
+static SHELL_VAR *
+bind_variable_internal (name, value, table, hflags)
+     const char *name;
+     char *value;
+     HASH_TABLE *table;
+     int hflags;
+{
+  char *newval;
+  SHELL_VAR *entry;
+
+  entry = (hflags & HASH_NOSRCH) ? (SHELL_VAR *)NULL : hash_lookup (name, table);
+
+  if (entry == 0)
+    {
+      entry = make_new_variable (name, table);
+      var_setvalue (entry, make_variable_value (entry, value));
+    }
+  else if (entry->assign_func) /* array vars have assign functions now */
+    {
+      INVALIDATE_EXPORTSTR (entry);
+      return ((*(entry->assign_func)) (entry, value, -1));
+    }
+  else
+    {
+      if (readonly_p (entry) || noassign_p (entry))
+       {
+         if (readonly_p (entry))
+           err_readonly (name);
+         return (entry);
+       }
+
+      /* Variables which are bound are visible. */
+      VUNSETATTR (entry, att_invisible);
+
+      newval = make_variable_value (entry, value);
+
+      /* Invalidate any cached export string */
+      INVALIDATE_EXPORTSTR (entry);
+
+#if defined (ARRAY_VARS)
+      /* XXX -- this bears looking at again -- XXX */
+      /* If an existing array variable x is being assigned to with x=b or
+        `read x' or something of that nature, silently convert it to
+        x[0]=b or `read x[0]'. */
+      if (array_p (entry))
+       {
+         array_insert (array_cell (entry), 0, newval);
+         free (newval);
+       }
+      else
+#endif
+       {
+         FREE (value_cell (entry));
+         var_setvalue (entry, newval);
+       }
+    }
+
+  if (mark_modified_vars)
+    VSETATTR (entry, att_exported);
+
+  if (exported_p (entry))
+    array_needs_making = 1;
+
+  return (entry);
+}
+       
+/* Bind a variable NAME to VALUE.  This conses up the name
+   and value strings.  If we have a temporary environment, we bind there
+   first, then we bind into shell_variables. */
+
+SHELL_VAR *
+bind_variable (name, value)
+     const char *name;
+     char *value;
+{
+  SHELL_VAR *v;
+  VAR_CONTEXT *vc;
+
+  if (shell_variables == 0)
+    {
+      shell_variables = global_variables = new_var_context ((char *)NULL, 0);
+      shell_variables->scope = 0;
+      shell_variables->table = hash_create (0);
+    }
+
+  /* If we have a temporary environment, look there first for the variable,
+     and, if found, modify the value there before modifying it in the
+     shell_variables table.  This allows sourced scripts to modify values
+     given to them in a temporary environment while modifying the variable
+     value that the caller sees. */
+  if (temporary_env)
+    bind_tempenv_variable (name, value);
+
+  /* XXX -- handle local variables here. */
+  for (vc = shell_variables; vc; vc = vc->down)
+    {
+      if (vc_isfuncenv (vc) || vc_isbltnenv (vc))
+        {
+          v = hash_lookup (name, vc->table);
+          if (v)
+           return (bind_variable_internal (name, value, vc->table, 0));
+        }
+    }
+  return (bind_variable_internal (name, value, global_variables->table, 0));
+}
+
+/* Make VAR, a simple shell variable, have value VALUE.  Once assigned a
+   value, variables are no longer invisible.  This is a duplicate of part
+   of the internals of bind_variable.  If the variable is exported, or
+   all modified variables should be exported, mark the variable for export
+   and note that the export environment needs to be recreated. */
+SHELL_VAR *
+bind_variable_value (var, value)
+     SHELL_VAR *var;
+     char *value;
+{
+  char *t;
+
+  VUNSETATTR (var, att_invisible);
+
+  t = make_variable_value (var, value);
+  FREE (value_cell (var));
+  var_setvalue (var, t);
+
+  INVALIDATE_EXPORTSTR (var);
+
+  if (mark_modified_vars)
+    VSETATTR (var, att_exported);
+
+  if (exported_p (var))
+    array_needs_making = 1;
+
+  return (var);
+}
+
+/* Bind/create a shell variable with the name LHS to the RHS.
+   This creates or modifies a variable such that it is an integer.
+
+   This used to be in expr.c, but it is here so that all of the
+   variable binding stuff is localized.  Since we don't want any
+   recursive evaluation from bind_variable() (possible without this code,
+   since bind_variable() calls the evaluator for variables with the integer
+   attribute set), we temporarily turn off the integer attribute for each
+   variable we set here, then turn it back on after binding as necessary. */
+
+SHELL_VAR *
+bind_int_variable (lhs, rhs)
+     char *lhs, *rhs;
+{
+  register SHELL_VAR *v;
+  char *t;
+  int isint, isarr;
+
+  isint = isarr = 0;
+#if defined (ARRAY_VARS)
+#  if 0
+  if (t = xstrchr (lhs, '['))  /*]*/
+#  else
+  if (valid_array_reference (lhs))
+#  endif
+    {
+      isarr = 1;
+      v = array_variable_part (lhs, (char **)0, (int *)0);
+    }
+  else
+#endif
+    v = find_variable (lhs);
+
+  if (v)
+    {
+      isint = integer_p (v);
+      VUNSETATTR (v, att_integer);
+    }
+
+#if defined (ARRAY_VARS)
+  if (isarr)
+    v = assign_array_element (lhs, rhs);
+  else
+#endif
+    v = bind_variable (lhs, rhs);
+
+  if (isint)
+    VSETATTR (v, att_integer);
+
+  return (v);
+}
+
+SHELL_VAR *
+bind_var_to_int (var, val)
+     char *var;
+     intmax_t val;
+{
+  char ibuf[INT_STRLEN_BOUND (intmax_t) + 1], *p;
+
+  p = fmtulong (val, 10, ibuf, sizeof (ibuf), 0);
+  return (bind_int_variable (var, p));
+}
+
+/* Do a function binding to a variable.  You pass the name and
+   the command to bind to.  This conses the name and command. */
+SHELL_VAR *
+bind_function (name, value)
+     const char *name;
+     COMMAND *value;
+{
+  SHELL_VAR *entry;
+
+  entry = find_function (name);
+  if (entry == 0)
+    {
+      BUCKET_CONTENTS *elt;
+
+      elt = hash_insert (savestring (name), shell_functions, HASH_NOSRCH);
+      entry = new_shell_variable (name);
+      elt->data = (PTR_T)entry;
+    }
+  else
+    INVALIDATE_EXPORTSTR (entry);
+
+  if (var_isset (entry))
+    dispose_command (function_cell (entry));
+
+  if (value)
+    var_setfunc (entry, copy_command (value));
+  else
+    var_setfunc (entry, 0);
+
+  VSETATTR (entry, att_function);
+
+  if (mark_modified_vars)
+    VSETATTR (entry, att_exported);
+
+  VUNSETATTR (entry, att_invisible);           /* Just to be sure */
+
+  if (exported_p (entry))
+    array_needs_making = 1;
+
+#if defined (PROGRAMMABLE_COMPLETION)
+  set_itemlist_dirty (&it_functions);
+#endif
+
+  return (entry);
+}
+
+/* Bind a function definition, which includes source file and line number
+   information in addition to the command, into the FUNCTION_DEF hash table.*/
+void
+bind_function_def (name, value)
+     const char *name;
+     FUNCTION_DEF *value;
+{
+  FUNCTION_DEF *entry;
+  BUCKET_CONTENTS *elt;
+  COMMAND *cmd;
+
+  entry = find_function_def (name);
+  if (entry)
+    {
+      dispose_function_def_contents (entry);
+      entry = copy_function_def_contents (value, entry);
+    }
+  else
+    {
+      cmd = value->command;
+      value->command = 0;
+      entry = copy_function_def (value);
+      value->command = cmd;
+
+      elt = hash_insert (savestring (name), shell_function_defs, HASH_NOSRCH);
+      elt->data = (PTR_T *)entry;
+    }
+}
+
+/* Add STRING, which is of the form foo=bar, to the temporary environment
+   HASH_TABLE (temporary_env).  The functions in execute_cmd.c are
+   responsible for moving the main temporary env to one of the other
+   temporary environments.  The expansion code in subst.c calls this. */
+int
+assign_in_env (string)
+     const char *string;
+{
+  int offset;
+  char *name, *temp, *value;
+  SHELL_VAR *var;
+
+  offset = assignment (string, 0);
+  name = savestring (string);
+  value = (char *)NULL;
+
+  if (name[offset] == '=')
+    {
+      name[offset] = 0;
+
+      var = find_variable (name);
+      if (var && (readonly_p (var) || noassign_p (var)))
+       {
+         if (readonly_p (var))
+           err_readonly (name);
+         free (name);
+         return (0);
+       }
+
+      temp = name + offset + 1;
+      temp = (xstrchr (temp, '~') != 0) ? bash_tilde_expand (temp, 1) : savestring (temp);
+
+      value = expand_string_unsplit_to_string (temp, 0);
+      free (temp);
+    }
+
+  if (temporary_env == 0)
+    temporary_env = hash_create (TEMPENV_HASH_BUCKETS);
+
+  var = hash_lookup (name, temporary_env);
+  if (var == 0)
+    var = make_new_variable (name, temporary_env);
+  else
+    FREE (value_cell (var));
+
+  if (value == 0)
+    {
+      value = (char *)xmalloc (1);     /* like do_assignment_internal */
+      value[0] = '\0';
+    }
+
+  var_setvalue (var, value);
+  var->attributes |= (att_exported|att_tempvar);
+  var->context = variable_context;     /* XXX */
+
+  INVALIDATE_EXPORTSTR (var);
+  var->exportstr = mk_env_string (name, value);
+
+  array_needs_making = 1;
+
+  if (ifsname (name))
+    setifs (var);
+
+  if (echo_command_at_execute)
+    /* The Korn shell prints the `+ ' in front of assignment statements,
+       so we do too. */
+    xtrace_print_assignment (name, value, 0, 1);
+
+  free (name);
+  return 1;
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*                     Copying variables                           */
+/*                                                                 */
+/* **************************************************************** */
+
+#ifdef INCLUDE_UNUSED
+/* Copy VAR to a new data structure and return that structure. */
+SHELL_VAR *
+copy_variable (var)
+     SHELL_VAR *var;
+{
+  SHELL_VAR *copy = (SHELL_VAR *)NULL;
+
+  if (var)
+    {
+      copy = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
+
+      copy->attributes = var->attributes;
+      copy->name = savestring (var->name);
+
+      if (function_p (var))
+       var_setfunc (copy, copy_command (function_cell (var)));
+#if defined (ARRAY_VARS)
+      else if (array_p (var))
+       var_setarray (copy, dup_array (array_cell (var)));
+#endif
+      else if (value_cell (var))
+       var_setvalue (copy, savestring (value_cell (var)));
+      else
+       var_setvalue (copy, (char *)NULL);
+
+      copy->dynamic_value = var->dynamic_value;
+      copy->assign_func = var->assign_func;
+
+      copy->exportstr = COPY_EXPORTSTR (var);
+
+      copy->context = var->context;
+    }
+  return (copy);
+}
+#endif
+
+/* **************************************************************** */
+/*                                                                 */
+/*               Deleting and unsetting variables                  */
+/*                                                                 */
+/* **************************************************************** */
+
+/* Dispose of the information attached to VAR. */
+void
+dispose_variable (var)
+     SHELL_VAR *var;
+{
+  if (var == 0)
+    return;
+
+  if (function_p (var))
+    dispose_command (function_cell (var));
+#if defined (ARRAY_VARS)
+  else if (array_p (var))
+    array_dispose (array_cell (var));
+#endif
+  else
+    FREE (value_cell (var));
+
+  FREE_EXPORTSTR (var);
+
+  free (var->name);
+
+  if (exported_p (var))
+    array_needs_making = 1;
+
+  free (var);
+}
+
+/* Unset the shell variable referenced by NAME. */
+int
+unbind_variable (name)
+     const char *name;
+{
+  return makunbound (name, shell_variables);
+}
+
+/* Unset the shell function named NAME. */
+int
+unbind_func (name)
+     const char *name;
+{
+  BUCKET_CONTENTS *elt;
+  SHELL_VAR *func;
+
+  elt = hash_remove (name, shell_functions, 0);
+
+  if (elt == 0)
+    return -1;
+
+#if defined (PROGRAMMABLE_COMPLETION)
+  set_itemlist_dirty (&it_functions);
+#endif
+
+  func = (SHELL_VAR *)elt->data;
+  if (func)
+    {
+      if (exported_p (func))
+       array_needs_making++;
+      dispose_variable (func);
+    }
+
+  free (elt->key);
+  free (elt);
+
+  return 0;  
+}
+
+int
+unbind_function_def (name)
+     const char *name;
+{
+  BUCKET_CONTENTS *elt;
+  FUNCTION_DEF *funcdef;
+
+  elt = hash_remove (name, shell_function_defs, 0);
+
+  if (elt == 0)
+    return -1;
+
+  funcdef = (FUNCTION_DEF *)elt->data;
+  if (funcdef)
+    dispose_function_def (funcdef);
+
+  free (elt->key);
+  free (elt);
+
+  return 0;  
+}
+
+/* Make the variable associated with NAME go away.  HASH_LIST is the
+   hash table from which this variable should be deleted (either
+   shell_variables or shell_functions).
+   Returns non-zero if the variable couldn't be found. */
+int
+makunbound (name, vc)
+     const char *name;
+     VAR_CONTEXT *vc;
+{
+  BUCKET_CONTENTS *elt, *new_elt;
+  SHELL_VAR *old_var;
+  VAR_CONTEXT *v;
+  char *t;
+
+  for (elt = (BUCKET_CONTENTS *)NULL, v = vc; v; v = v->down)
+    if (elt = hash_remove (name, v->table, 0))
+      break;
+
+  if (elt == 0)
+    return (-1);
+
+  old_var = (SHELL_VAR *)elt->data;
+
+  if (old_var && exported_p (old_var))
+    array_needs_making++;
+
+  /* If we're unsetting a local variable and we're still executing inside
+     the function, just mark the variable as invisible.  The function
+     eventually called by pop_var_context() will clean it up later.  This
+     must be done so that if the variable is subsequently assigned a new
+     value inside the function, the `local' attribute is still present.
+     We also need to add it back into the correct hash table. */
+  if (old_var && local_p (old_var) && variable_context == old_var->context)
+    {
+      /* Reset the attributes.  Preserve the export attribute if the variable
+         came from a temporary environment.  Make sure it stays local, and
+         make it invisible. */ 
+      old_var->attributes = (exported_p (old_var) && tempvar_p (old_var)) ? att_exported : 0;
+      VSETATTR (old_var, att_local);
+      VSETATTR (old_var, att_invisible);
+      FREE (value_cell (old_var));
+      var_setvalue (old_var, (char *)NULL);
+      INVALIDATE_EXPORTSTR (old_var);
+
+      new_elt = hash_insert (savestring (old_var->name), v->table, 0);
+      new_elt->data = (PTR_T)old_var;
+      stupidly_hack_special_variables (old_var->name);
+
+      free (elt->key);
+      free (elt);
+      return (0);
+    }
+
+  /* Have to save a copy of name here, because it might refer to
+     old_var->name.  If so, stupidly_hack_special_variables will
+     reference freed memory. */
+  t = savestring (name);
+
+  free (elt->key);
+  free (elt);
+
+  dispose_variable (old_var);
+  stupidly_hack_special_variables (t);
+  free (t);
+
+  return (0);
+}
+
+/* Get rid of all of the variables in the current context. */
+void
+kill_all_local_variables ()
+{
+  VAR_CONTEXT *vc;
+
+  for (vc = shell_variables; vc; vc = vc->down)
+    if (vc_isfuncenv (vc) && vc->scope == variable_context)
+      break;
+  if (vc == 0)
+    return;            /* XXX */
+
+  if (vc->table && vc_haslocals (vc))
+    {
+      delete_all_variables (vc->table);
+      hash_dispose (vc->table);
+    }
+  vc->table = (HASH_TABLE *)NULL;
+}
+
+static void
+free_variable_hash_data (data)
+     PTR_T data;
+{
+  SHELL_VAR *var;
+
+  var = (SHELL_VAR *)data;
+  dispose_variable (var);
+}
+
+/* Delete the entire contents of the hash table. */
+void
+delete_all_variables (hashed_vars)
+     HASH_TABLE *hashed_vars;
+{
+  hash_flush (hashed_vars, free_variable_hash_data);
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*                  Setting variable attributes                    */
+/*                                                                 */
+/* **************************************************************** */
+
+#define FIND_OR_MAKE_VARIABLE(name, entry) \
+  do \
+    { \
+      entry = find_variable (name); \
+      if (!entry) \
+       { \
+         entry = bind_variable (name, ""); \
+         if (!no_invisible_vars) entry->attributes |= att_invisible; \
+       } \
+    } \
+  while (0)
+
+/* Make the variable associated with NAME be readonly.
+   If NAME does not exist yet, create it. */
+void
+set_var_read_only (name)
+     char *name;
+{
+  SHELL_VAR *entry;
+
+  FIND_OR_MAKE_VARIABLE (name, entry);
+  VSETATTR (entry, att_readonly);
+}
+
+#ifdef INCLUDE_UNUSED
+/* Make the function associated with NAME be readonly.
+   If NAME does not exist, we just punt, like auto_export code below. */
+void
+set_func_read_only (name)
+     const char *name;
+{
+  SHELL_VAR *entry;
+
+  entry = find_function (name);
+  if (entry)
+    VSETATTR (entry, att_readonly);
+}
+
+/* Make the variable associated with NAME be auto-exported.
+   If NAME does not exist yet, create it. */
+void
+set_var_auto_export (name)
+     char *name;
+{
+  SHELL_VAR *entry;
+
+  FIND_OR_MAKE_VARIABLE (name, entry);
+  set_auto_export (entry);
+}
+
+/* Make the function associated with NAME be auto-exported. */
+void
+set_func_auto_export (name)
+     const char *name;
+{
+  SHELL_VAR *entry;
+
+  entry = find_function (name);
+  if (entry)
+    set_auto_export (entry);
+}
+#endif
+
+/* **************************************************************** */
+/*                                                                 */
+/*                  Creating lists of variables                    */
+/*                                                                 */
+/* **************************************************************** */
+
+static VARLIST *
+vlist_alloc (nentries)
+     int nentries;
+{
+  VARLIST  *vlist;
+
+  vlist = (VARLIST *)xmalloc (sizeof (VARLIST));
+  vlist->list = (SHELL_VAR **)xmalloc ((nentries + 1) * sizeof (SHELL_VAR *));
+  vlist->list_size = nentries;
+  vlist->list_len = 0;
+  vlist->list[0] = (SHELL_VAR *)NULL;
+
+  return vlist;
+}
+
+static VARLIST *
+vlist_realloc (vlist, n)
+     VARLIST *vlist;
+     int n;
+{
+  if (vlist == 0)
+    return (vlist = vlist_alloc (n));
+  if (n > vlist->list_size)
+    {
+      vlist->list_size = n;
+      vlist->list = (SHELL_VAR **)xrealloc (vlist->list, (vlist->list_size + 1) * sizeof (SHELL_VAR *));
+    }
+  return vlist;
+}
+
+static void
+vlist_add (vlist, var, flags)
+     VARLIST *vlist;
+     SHELL_VAR *var;
+     int flags;
+{
+  register int i;
+
+  for (i = 0; i < vlist->list_len; i++)
+    if (STREQ (var->name, vlist->list[i]->name))
+      break;
+  if (i < vlist->list_len)
+    return;
+
+  if (i >= vlist->list_size)
+    vlist = vlist_realloc (vlist, vlist->list_size + 16);
+
+  vlist->list[vlist->list_len++] = var;
+  vlist->list[vlist->list_len] = (SHELL_VAR *)NULL;
+}
+
+/* Map FUNCTION over the variables in VAR_HASH_TABLE.  Return an array of the
+   variables for which FUNCTION returns a non-zero value.  A NULL value
+   for FUNCTION means to use all variables. */
+SHELL_VAR **
+map_over (function, vc)
+     sh_var_map_func_t *function;
+     VAR_CONTEXT *vc;
+{
+  VAR_CONTEXT *v;
+  VARLIST *vlist;
+  SHELL_VAR **ret;
+  int nentries;
+
+  for (nentries = 0, v = vc; v; v = v->down)
+    nentries += HASH_ENTRIES (v->table);
+
+  if (nentries == 0)
+    return (SHELL_VAR **)NULL;
+
+  vlist = vlist_alloc (nentries);
+
+  for (v = vc; v; v = v->down)
+    flatten (v->table, function, vlist, 0);
+
+  ret = vlist->list;
+  free (vlist);
+  return ret;
+}
+
+SHELL_VAR **
+map_over_funcs (function)
+     sh_var_map_func_t *function;
+{
+  VARLIST *vlist;
+  SHELL_VAR **ret;
+
+  if (shell_functions == 0 || HASH_ENTRIES (shell_functions) == 0)
+    return ((SHELL_VAR **)NULL);
+
+  vlist = vlist_alloc (HASH_ENTRIES (shell_functions));
+
+  flatten (shell_functions, function, vlist, 0);
+
+  ret = vlist->list;
+  free (vlist);
+  return ret;
+}
+
+/* Flatten VAR_HASH_TABLE, applying FUNC to each member and adding those
+   elements for which FUNC succeeds to VLIST->list.  FLAGS is reserved
+   for future use.  Only unique names are added to VLIST.  If FUNC is
+   NULL, each variable in VAR_HASH_TABLE is added to VLIST.  If VLIST is
+   NULL, FUNC is applied to each SHELL_VAR in VAR_HASH_TABLE.  If VLIST
+   and FUNC are both NULL, nothing happens. */
+static void
+flatten (var_hash_table, func, vlist, flags)
+     HASH_TABLE *var_hash_table;
+     sh_var_map_func_t *func;
+     VARLIST *vlist;
+     int flags;
+{
+  register int i;
+  register BUCKET_CONTENTS *tlist;
+  int r;
+  SHELL_VAR *var;
+
+  if (var_hash_table == 0 || (HASH_ENTRIES (var_hash_table) == 0) || (vlist == 0 && func == 0))
+    return;
+
+  for (i = 0; i < var_hash_table->nbuckets; i++)
+    {
+      for (tlist = hash_items (i, var_hash_table); tlist; tlist = tlist->next)
+       {
+         var = (SHELL_VAR *)tlist->data;
+
+         r = func ? (*func) (var) : 1;
+         if (r && vlist)
+           vlist_add (vlist, var, flags);
+       }
+    }
+}
+
+void
+sort_variables (array)
+     SHELL_VAR **array;
+{
+  qsort (array, strvec_len ((char **)array), sizeof (SHELL_VAR *), (QSFUNC *)qsort_var_comp);
+}
+
+static int
+qsort_var_comp (var1, var2)
+     SHELL_VAR **var1, **var2;
+{
+  int result;
+
+  if ((result = (*var1)->name[0] - (*var2)->name[0]) == 0)
+    result = strcmp ((*var1)->name, (*var2)->name);
+
+  return (result);
+}
+
+/* Apply FUNC to each variable in SHELL_VARIABLES, adding each one for
+   which FUNC succeeds to an array of SHELL_VAR *s.  Returns the array. */
+static SHELL_VAR **
+vapply (func)
+     sh_var_map_func_t *func;
+{
+  SHELL_VAR **list;
+
+  list = map_over (func, shell_variables);
+  if (list /* && posixly_correct */)
+    sort_variables (list);
+  return (list);
+}
+
+/* Apply FUNC to each variable in SHELL_FUNCTIONS, adding each one for
+   which FUNC succeeds to an array of SHELL_VAR *s.  Returns the array. */
+static SHELL_VAR **
+fapply (func)
+     sh_var_map_func_t *func;
+{
+  SHELL_VAR **list;
+
+  list = map_over_funcs (func);
+  if (list /* && posixly_correct */)
+    sort_variables (list);
+  return (list);
+}
+
+/* Create a NULL terminated array of all the shell variables. */
+SHELL_VAR **
+all_shell_variables ()
+{
+  return (vapply ((sh_var_map_func_t *)NULL));
+}
+
+/* Create a NULL terminated array of all the shell functions. */
+SHELL_VAR **
+all_shell_functions ()
+{
+  return (fapply ((sh_var_map_func_t *)NULL));
+}
+
+static int
+visible_var (var)
+     SHELL_VAR *var;
+{
+  return (invisible_p (var) == 0);
+}
+
+SHELL_VAR **
+all_visible_functions ()
+{
+  return (fapply (visible_var));
+}
+
+SHELL_VAR **
+all_visible_variables ()
+{
+  return (vapply (visible_var));
+}
+
+/* Return non-zero if the variable VAR is visible and exported.  Array
+   variables cannot be exported. */
+static int
+visible_and_exported (var)
+     SHELL_VAR *var;
+{
+  return (invisible_p (var) == 0 && exported_p (var));
+}
+
+/* Return non-zero if VAR is a local variable in the current context and
+   is exported. */
+static int
+local_and_exported (var)
+     SHELL_VAR *var;
+{
+  return (invisible_p (var) == 0 && local_p (var) && var->context == variable_context && exported_p (var));
+}
+
+SHELL_VAR **
+all_exported_variables ()
+{
+  return (vapply (visible_and_exported));
+}
+
+SHELL_VAR **
+local_exported_variables ()
+{
+  return (vapply (local_and_exported));
+}
+
+static int
+variable_in_context (var)
+     SHELL_VAR *var;
+{
+  return (invisible_p (var) == 0 && local_p (var) && var->context == variable_context);
+}
+
+SHELL_VAR **
+all_local_variables ()
+{
+  VARLIST *vlist;
+  SHELL_VAR **ret;
+  VAR_CONTEXT *vc;
+
+  vc = shell_variables;
+  for (vc = shell_variables; vc; vc = vc->down)
+    if (vc_isfuncenv (vc) && vc->scope == variable_context)
+      break;
+
+  if (vc == 0)
+    {
+      internal_error (_("all_local_variables: no function context at current scope"));
+      return (SHELL_VAR **)NULL;
+    }
+  if (vc->table == 0 || HASH_ENTRIES (vc->table) == 0 || vc_haslocals (vc) == 0)
+    return (SHELL_VAR **)NULL;
+    
+  vlist = vlist_alloc (HASH_ENTRIES (vc->table));
+
+  flatten (vc->table, variable_in_context, vlist, 0);
+
+  ret = vlist->list;
+  free (vlist);
+  if (ret)
+    sort_variables (ret);
+  return ret;
+}
+
+#if defined (ARRAY_VARS)
+/* Return non-zero if the variable VAR is visible and an array. */
+static int
+visible_array_vars (var)
+     SHELL_VAR *var;
+{
+  return (invisible_p (var) == 0 && array_p (var));
+}
+
+SHELL_VAR **
+all_array_variables ()
+{
+  return (vapply (visible_array_vars));
+}
+#endif /* ARRAY_VARS */
+
+char **
+all_variables_matching_prefix (prefix)
+     const char *prefix;
+{
+  SHELL_VAR **varlist;
+  char **rlist;
+  int vind, rind, plen;
+
+  plen = STRLEN (prefix);
+  varlist = all_visible_variables ();
+  for (vind = 0; varlist && varlist[vind]; vind++)
+    ;
+  if (varlist == 0 || vind == 0)
+    return ((char **)NULL);
+  rlist = strvec_create (vind + 1);
+  for (vind = rind = 0; varlist[vind]; vind++)
+    {
+      if (plen == 0 || STREQN (prefix, varlist[vind]->name, plen))
+       rlist[rind++] = savestring (varlist[vind]->name);
+    }
+  rlist[rind] = (char *)0;
+  free (varlist);
+
+  return rlist;
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*              Managing temporary variable scopes                 */
+/*                                                                 */
+/* **************************************************************** */
+
+/* Make variable NAME have VALUE in the temporary environment. */
+static SHELL_VAR *
+bind_tempenv_variable (name, value)
+     const char *name;
+     char *value;
+{
+  SHELL_VAR *var;
+
+  var = temporary_env ? hash_lookup (name, temporary_env) : (SHELL_VAR *)NULL;
+
+  if (var)
+    {
+      FREE (value_cell (var));
+      var_setvalue (var, savestring (value));
+      INVALIDATE_EXPORTSTR (var);
+    }
+
+  return (var);
+}
+
+/* Find a variable in the temporary environment that is named NAME.
+   Return the SHELL_VAR *, or NULL if not found. */
+SHELL_VAR *
+find_tempenv_variable (name)
+     const char *name;
+{
+  return (temporary_env ? hash_lookup (name, temporary_env) : (SHELL_VAR *)NULL);
+}
+
+/* Push the variable described by (SHELL_VAR *)DATA down to the next
+   variable context from the temporary environment. */
+static void
+push_temp_var (data)
+     PTR_T data;
+{
+  SHELL_VAR *var, *v;
+  HASH_TABLE *binding_table;
+
+  var = (SHELL_VAR *)data;
+
+  binding_table = shell_variables->table;
+  if (binding_table == 0)
+    {
+      if (shell_variables == global_variables)
+       /* shouldn't happen */
+       binding_table = shell_variables->table = global_variables->table = hash_create (0);
+      else
+       binding_table = shell_variables->table = hash_create (TEMPENV_HASH_BUCKETS);
+    }
+
+  v = bind_variable_internal (var->name, value_cell (var), binding_table, 0);
+
+  /* XXX - should we set the context here?  It shouldn't matter because of how
+     assign_in_env works, but might want to check. */
+  if (binding_table == global_variables->table)                /* XXX */
+    var->attributes &= ~(att_tempvar|att_propagate);
+  else
+    {
+      var->attributes |= att_propagate;
+      if  (binding_table == shell_variables->table)
+       shell_variables->flags |= VC_HASTMPVAR;
+    }
+  v->attributes |= var->attributes;
+
+  dispose_variable (var);
+}
+
+static void
+propagate_temp_var (data)
+     PTR_T data;
+{
+  SHELL_VAR *var;
+
+  var = (SHELL_VAR *)data;
+  if (tempvar_p (var) && (var->attributes & att_propagate))
+    push_temp_var (data);
+  else
+    dispose_variable (var);
+}
+
+/* Free the storage used in the hash table for temporary
+   environment variables.  PUSHF is a function to be called
+   to free each hash table entry.  It takes care of pushing variables
+   to previous scopes if appropriate. */
+static void
+dispose_temporary_env (pushf)
+     sh_free_func_t *pushf;
+{
+  hash_flush (temporary_env, pushf);
+  hash_dispose (temporary_env);
+  temporary_env  = (HASH_TABLE *)NULL;
+
+  array_needs_making = 1;
+
+  sv_ifs ("IFS");              /* XXX here for now */
+}
+
+void
+dispose_used_env_vars ()
+{
+  if (temporary_env)
+    dispose_temporary_env (propagate_temp_var);
+}
+
+/* Take all of the shell variables in the temporary environment HASH_TABLE
+   and make shell variables from them at the current variable context. */
+void
+merge_temporary_env ()
+{
+  if (temporary_env)
+    dispose_temporary_env (push_temp_var);
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*          Creating and manipulating the environment              */
+/*                                                                 */
+/* **************************************************************** */
+
+static inline char *
+mk_env_string (name, value)
+     const char *name, *value;
+{
+  int name_len, value_len;
+  char *p;
+
+  name_len = strlen (name);
+  value_len = STRLEN (value);
+  p = (char *)xmalloc (2 + name_len + value_len);
+  strcpy (p, name);
+  p[name_len] = '=';
+  if (value && *value)
+    strcpy (p + name_len + 1, value);
+  else
+    p[name_len + 1] = '\0';
+  return (p);
+}
+
+#ifdef DEBUG
+/* Debugging */
+static int
+valid_exportstr (v)
+     SHELL_VAR *v;
+{
+  char *s;
+
+  s = v->exportstr;
+  if (legal_variable_starter ((unsigned char)*s) == 0)
+    {
+      internal_error (_("invalid character %d in exportstr for %s"), *s, v->name);
+      return (0);
+    }
+  for (s = v->exportstr + 1; s && *s; s++)
+    {
+      if (*s == '=')
+       break;
+      if (legal_variable_char ((unsigned char)*s) == 0)
+       {
+         internal_error (_("invalid character %d in exportstr for %s"), *s, v->name);
+         return (0);
+       }
+    }
+  if (*s != '=')
+    {
+      internal_error (_("no `=' in exportstr for %s"), v->name);
+      return (0);
+    }
+  return (1);
+}
+#endif
+
+static char **
+make_env_array_from_var_list (vars)
+     SHELL_VAR **vars;
+{
+  register int i, list_index;
+  register SHELL_VAR *var;
+  char **list, *value;
+
+  list = strvec_create ((1 + strvec_len ((char **)vars)));
+
+#define USE_EXPORTSTR (value == var->exportstr)
+
+  for (i = 0, list_index = 0; var = vars[i]; i++)
+    {
+#if defined (__CYGWIN__)
+      /* We don't use the exportstr stuff on Cygwin at all. */
+      INVALIDATE_EXPORTSTR (var);
+#endif
+      if (var->exportstr)
+       value = var->exportstr;
+      else if (function_p (var))
+       value = named_function_string ((char *)NULL, function_cell (var), 0);
+#if defined (ARRAY_VARS)
+      else if (array_p (var))
+#  if 0
+       value = array_to_assignment_string (array_cell (var));
+#  else
+       continue;       /* XXX array vars cannot yet be exported */
+#  endif
+#endif
+      else
+       value = value_cell (var);
+
+      if (value)
+       {
+         /* Gee, I'd like to get away with not using savestring() if we're
+            using the cached exportstr... */
+         list[list_index] = USE_EXPORTSTR ? savestring (value)
+                                          : mk_env_string (var->name, value);
+
+         if (USE_EXPORTSTR == 0)
+           SAVE_EXPORTSTR (var, list[list_index]);
+
+         list_index++;
+#undef USE_EXPORTSTR
+
+#if 0  /* not yet */
+#if defined (ARRAY_VARS)
+         if (array_p (var))
+           free (value);
+#endif
+#endif
+       }
+    }
+
+  list[list_index] = (char *)NULL;
+  return (list);
+}
+
+/* Make an array of assignment statements from the hash table
+   HASHED_VARS which contains SHELL_VARs.  Only visible, exported
+   variables are eligible. */
+static char **
+make_var_export_array (vcxt)
+     VAR_CONTEXT *vcxt;
+{
+  char **list;
+  SHELL_VAR **vars;
+
+  vars = map_over (visible_and_exported, vcxt);
+
+  if (vars == 0)
+    return (char **)NULL;
+
+  list = make_env_array_from_var_list (vars);
+
+  free (vars);
+  return (list);
+}
+
+static char **
+make_func_export_array ()
+{
+  char **list;
+  SHELL_VAR **vars;
+
+  vars = map_over_funcs (visible_and_exported);
+  if (vars == 0)
+    return (char **)NULL;
+
+  list = make_env_array_from_var_list (vars);
+
+  free (vars);
+  return (list);
+}
+
+/* Add ENVSTR to the end of the exported environment, EXPORT_ENV. */
+#define add_to_export_env(envstr,do_alloc) \
+do \
+  { \
+    if (export_env_index >= (export_env_size - 1)) \
+      { \
+       export_env_size += 16; \
+       export_env = strvec_resize (export_env, export_env_size); \
+      } \
+    export_env[export_env_index++] = (do_alloc) ? savestring (envstr) : envstr; \
+    export_env[export_env_index] = (char *)NULL; \
+  } while (0)
+
+/* Add ASSIGN to EXPORT_ENV, or supercede a previous assignment in the
+   array with the same left-hand side.  Return the new EXPORT_ENV. */
+char **
+add_or_supercede_exported_var (assign, do_alloc)
+     char *assign;
+     int do_alloc;
+{
+  register int i;
+  int equal_offset;
+
+  equal_offset = assignment (assign, 0);
+  if (equal_offset == 0)
+    return (export_env);
+
+  /* If this is a function, then only supersede the function definition.
+     We do this by including the `=() {' in the comparison, like
+     initialize_shell_variables does. */
+  if (assign[equal_offset + 1] == '(' &&
+     strncmp (assign + equal_offset + 2, ") {", 3) == 0)               /* } */
+    equal_offset += 4;
+
+  for (i = 0; i < export_env_index; i++)
+    {
+      if (STREQN (assign, export_env[i], equal_offset + 1))
+       {
+         free (export_env[i]);
+         export_env[i] = do_alloc ? savestring (assign) : assign;
+         return (export_env);
+       }
+    }
+  add_to_export_env (assign, do_alloc);
+  return (export_env);
+}
+
+static void
+add_temp_array_to_env (temp_array, do_alloc, do_supercede)
+     char **temp_array;
+     int do_alloc, do_supercede;
+{
+  register int i;
+
+  if (temp_array == 0)
+    return;
+
+  for (i = 0; temp_array[i]; i++)
+    {
+      if (do_supercede)
+       export_env = add_or_supercede_exported_var (temp_array[i], do_alloc);
+      else
+       add_to_export_env (temp_array[i], do_alloc);
+    }
+
+  free (temp_array);
+}
+
+/* Make the environment array for the command about to be executed, if the
+   array needs making.  Otherwise, do nothing.  If a shell action could
+   change the array that commands receive for their environment, then the
+   code should `array_needs_making++'.
+
+   The order to add to the array is:
+       temporary_env
+       list of var contexts whose head is shell_variables
+       shell_functions
+
+  This is the shell variable lookup order.  We add only new variable
+  names at each step, which allows local variables and variables in
+  the temporary environments to shadow variables in the global (or
+  any previous) scope.
+*/
+
+static int
+n_shell_variables ()
+{
+  VAR_CONTEXT *vc;
+  int n;
+
+  for (n = 0, vc = shell_variables; vc; vc = vc->down)
+    n += HASH_ENTRIES (vc->table);
+  return n;
+}
+
+void
+maybe_make_export_env ()
+{
+  register char **temp_array;
+  int new_size;
+  VAR_CONTEXT *tcxt;
+
+  if (array_needs_making)
+    {
+      if (export_env)
+        strvec_flush (export_env);
+
+      /* Make a guess based on how many shell variables and functions we
+        have.  Since there will always be array variables, and array
+        variables are not (yet) exported, this will always be big enough
+        for the exported variables and functions. */
+      new_size = n_shell_variables () + HASH_ENTRIES (shell_functions) + 1 +
+                HASH_ENTRIES (temporary_env);
+      if (new_size > export_env_size)
+       {
+         export_env_size = new_size;
+         export_env = strvec_resize (export_env, export_env_size);
+       }
+      export_env[export_env_index = 0] = (char *)NULL;
+
+      /* Make a dummy variable context from the  temporary_env, stick it on
+         the front of shell_variables, call make_var_export_array on the
+         whole thing to flatten it, and convert the list of SHELL_VAR *s
+         to the form needed by the environment. */
+      if (temporary_env)
+        {
+          tcxt = new_var_context ((char *)NULL, 0);
+          tcxt->table = temporary_env;
+          tcxt->down = shell_variables;
+        }
+      else
+        tcxt = shell_variables;
+      
+      temp_array = make_var_export_array (tcxt);
+      if (temp_array)
+       add_temp_array_to_env (temp_array, 0, 0);
+
+      if (tcxt != shell_variables)
+        free (tcxt);
+
+#if defined (RESTRICTED_SHELL)
+      /* Restricted shells may not export shell functions. */
+      temp_array = restricted ? (char **)0 : make_func_export_array ();
+#else
+      temp_array = make_func_export_array ();
+#endif
+      if (temp_array)
+       add_temp_array_to_env (temp_array, 0, 0);
+
+      array_needs_making = 0;
+    }
+}
+
+/* This is an efficiency hack.  PWD and OLDPWD are auto-exported, so
+   we will need to remake the exported environment every time we
+   change directories.  `_' is always put into the environment for
+   every external command, so without special treatment it will always
+   cause the environment to be remade.
+
+   If there is no other reason to make the exported environment, we can
+   just update the variables in place and mark the exported environment
+   as no longer needing a remake. */
+void
+update_export_env_inplace (env_prefix, preflen, value)
+     char *env_prefix;
+     int preflen;
+     char *value;
+{
+  char *evar;
+
+  evar = (char *)xmalloc (STRLEN (value) + preflen + 1);
+  strcpy (evar, env_prefix);
+  if (value)
+    strcpy (evar + preflen, value);
+  export_env = add_or_supercede_exported_var (evar, 0);
+}
+
+/* We always put _ in the environment as the name of this command. */
+void
+put_command_name_into_env (command_name)
+     char *command_name;
+{
+  update_export_env_inplace ("_=", 2, command_name);
+}
+
+#if 0  /* UNUSED -- it caused too many problems */
+void
+put_gnu_argv_flags_into_env (pid, flags_string)
+     intmax_t pid;
+     char *flags_string;
+{
+  char *dummy, *pbuf;
+  int l, fl;
+
+  pbuf = itos (pid);
+  l = strlen (pbuf);
+
+  fl = strlen (flags_string);
+
+  dummy = (char *)xmalloc (l + fl + 30);
+  dummy[0] = '_';
+  strcpy (dummy + 1, pbuf);
+  strcpy (dummy + 1 + l, "_GNU_nonoption_argv_flags_");
+  dummy[l + 27] = '=';
+  strcpy (dummy + l + 28, flags_string);
+
+  free (pbuf);
+
+  export_env = add_or_supercede_exported_var (dummy, 0);
+}
+#endif
+
+/* **************************************************************** */
+/*                                                                 */
+/*                   Managing variable contexts                    */
+/*                                                                 */
+/* **************************************************************** */
+
+/* Allocate and return a new variable context with NAME and FLAGS.
+   NAME can be NULL. */
+
+VAR_CONTEXT *
+new_var_context (name, flags)
+     char *name;
+     int flags;
+{
+  VAR_CONTEXT *vc;
+
+  vc = (VAR_CONTEXT *)xmalloc (sizeof (VAR_CONTEXT));
+  vc->name = name ? savestring (name) : (char *)NULL;
+  vc->scope = variable_context;
+  vc->flags = flags;
+
+  vc->up = vc->down = (VAR_CONTEXT *)NULL;
+  vc->table = (HASH_TABLE *)NULL;
+
+  return vc;
+}
+
+/* Free a variable context and its data, including the hash table.  Dispose
+   all of the variables. */
+void
+dispose_var_context (vc)
+     VAR_CONTEXT *vc;
+{
+  FREE (vc->name);
+
+  if (vc->table)
+    {
+      delete_all_variables (vc->table);
+      hash_dispose (vc->table);
+    }
+
+  free (vc);
+}
+
+/* Set VAR's scope level to the current variable context. */
+static int
+set_context (var)
+     SHELL_VAR *var;
+{
+  return (var->context = variable_context);
+}
+
+/* Make a new variable context with NAME and FLAGS and a HASH_TABLE of
+   temporary variables, and push it onto shell_variables.  This is
+   for shell functions. */
+VAR_CONTEXT *
+push_var_context (name, flags, tempvars)
+     char *name;
+     int flags;
+     HASH_TABLE *tempvars;
+{
+  VAR_CONTEXT *vc;
+
+  vc = new_var_context (name, flags);
+  vc->table = tempvars;
+  if (tempvars)
+    {
+      /* Have to do this because the temp environment was created before
+        variable_context was incremented. */
+      flatten (tempvars, set_context, (VARLIST *)NULL, 0);
+      vc->flags |= VC_HASTMPVAR;
+    }
+  vc->down = shell_variables;
+  shell_variables->up = vc;
+
+  return (shell_variables = vc);
+}
+
+static void
+push_func_var (data)
+     PTR_T data;
+{
+  SHELL_VAR *var, *v;
+
+  var = (SHELL_VAR *)data;
+
+  if (tempvar_p (var) && (posixly_correct || (var->attributes & att_propagate)))
+    {
+      /* XXX - should we set v->context here? */
+      v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0);
+      if (shell_variables == global_variables)
+       var->attributes &= ~(att_tempvar|att_propagate);
+      else
+        shell_variables->flags |= VC_HASTMPVAR;
+      v->attributes |= var->attributes;
+    }
+
+  dispose_variable (var);
+}
+
+/* Pop the top context off of VCXT and dispose of it, returning the rest of
+   the stack. */
+void
+pop_var_context ()
+{
+  VAR_CONTEXT *ret, *vcxt;
+
+  vcxt = shell_variables;
+  if (vc_isfuncenv (vcxt) == 0)
+    {
+      internal_error (_("pop_var_context: head of shell_variables not a function context"));
+      return;
+    }
+
+  if (ret = vcxt->down)
+    {
+      ret->up = (VAR_CONTEXT *)NULL;
+      shell_variables = ret;
+      if (vcxt->table)
+       hash_flush (vcxt->table, push_func_var);
+      dispose_var_context (vcxt);
+    }
+  else
+    internal_error (_("pop_var_context: no global_variables context"));
+}
+
+/* Delete the HASH_TABLEs for all variable contexts beginning at VCXT, and
+   all of the VAR_CONTEXTs except GLOBAL_VARIABLES. */
+void
+delete_all_contexts (vcxt)
+     VAR_CONTEXT *vcxt;
+{
+  VAR_CONTEXT *v, *t;
+
+  for (v = vcxt; v != global_variables; v = t)
+    {
+      t = v->down;
+      dispose_var_context (v);
+    }            
+
+  delete_all_variables (global_variables->table);
+  shell_variables = global_variables;
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*        Pushing and Popping temporary variable scopes            */
+/*                                                                 */
+/* **************************************************************** */
+
+VAR_CONTEXT *
+push_scope (flags, tmpvars)
+     int flags;
+     HASH_TABLE *tmpvars;
+{
+  return (push_var_context ((char *)NULL, flags, tmpvars));
+}
+
+static void
+push_exported_var (data)
+     PTR_T data;
+{
+  SHELL_VAR *var, *v;
+
+  var = (SHELL_VAR *)data;
+
+  /* If a temp var had its export attribute set, or it's marked to be
+     propagated, bind it in the previous scope before disposing it. */
+  if (exported_p (var) || (var->attributes & att_propagate))
+    {
+      var->attributes &= ~att_tempvar;         /* XXX */
+      v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0);
+      if (shell_variables == global_variables)
+       var->attributes &= ~att_propagate;
+      v->attributes |= var->attributes;
+    }
+
+  dispose_variable (var);
+}
+
+void
+pop_scope (is_special)
+     int is_special;
+{
+  VAR_CONTEXT *vcxt, *ret;
+
+  vcxt = shell_variables;
+  if (vc_istempscope (vcxt) == 0)
+    {
+      internal_error (_("pop_scope: head of shell_variables not a temporary environment scope"));
+      return;
+    }
+
+  ret = vcxt->down;
+  if (ret)
+    ret->up = (VAR_CONTEXT *)NULL;
+
+  shell_variables = ret;
+
+  /* Now we can take care of merging variables in VCXT into set of scopes
+     whose head is RET (shell_variables). */
+  FREE (vcxt->name);
+  if (vcxt->table)
+    {
+      if (is_special)
+       hash_flush (vcxt->table, push_func_var);
+      else
+       hash_flush (vcxt->table, push_exported_var);
+      hash_dispose (vcxt->table);
+    }
+  free (vcxt);
+
+  sv_ifs ("IFS");      /* XXX here for now */
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*              Pushing and Popping function contexts              */
+/*                                                                 */
+/* **************************************************************** */
+
+static WORD_LIST **dollar_arg_stack = (WORD_LIST **)NULL;
+static int dollar_arg_stack_slots;
+static int dollar_arg_stack_index;
+
+/* XXX - we might want to consider pushing and popping the `getopts' state
+   when we modify the positional parameters. */
+void
+push_context (name, is_subshell, tempvars)
+     char *name;       /* function name */
+     int is_subshell;
+     HASH_TABLE *tempvars;
+{
+  if (is_subshell == 0)
+    push_dollar_vars ();
+  variable_context++;
+  push_var_context (name, VC_FUNCENV, tempvars);
+}
+
+/* Only called when subshell == 0, so we don't need to check, and can
+   unconditionally pop the dollar vars off the stack. */
+void
+pop_context ()
+{
+  pop_dollar_vars ();
+  variable_context--;
+  pop_var_context ();
+
+  sv_ifs ("IFS");              /* XXX here for now */
+}
+
+/* Save the existing positional parameters on a stack. */
+void
+push_dollar_vars ()
+{
+  if (dollar_arg_stack_index + 2 > dollar_arg_stack_slots)
+    {
+      dollar_arg_stack = (WORD_LIST **)
+       xrealloc (dollar_arg_stack, (dollar_arg_stack_slots += 10)
+                 * sizeof (WORD_LIST **));
+    }
+  dollar_arg_stack[dollar_arg_stack_index++] = list_rest_of_args ();
+  dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
+}
+
+/* Restore the positional parameters from our stack. */
+void
+pop_dollar_vars ()
+{
+  if (!dollar_arg_stack || dollar_arg_stack_index == 0)
+    return;
+
+  remember_args (dollar_arg_stack[--dollar_arg_stack_index], 1);
+  dispose_words (dollar_arg_stack[dollar_arg_stack_index]);
+  dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
+  set_dollar_vars_unchanged ();
+}
+
+void
+dispose_saved_dollar_vars ()
+{
+  if (!dollar_arg_stack || dollar_arg_stack_index == 0)
+    return;
+
+  dispose_words (dollar_arg_stack[dollar_arg_stack_index]);
+  dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
+}
+
+/* Manipulate the special BASH_ARGV and BASH_ARGC variables. */
+
+void
+push_args (list)
+     WORD_LIST *list;
+{
+#if defined (ARRAY_VARS) && defined (DEBUGGER)
+  SHELL_VAR *bash_argv_v, *bash_argc_v;
+  ARRAY *bash_argv_a, *bash_argc_a;
+  WORD_LIST *l;
+  arrayind_t i;
+  char *t;
+
+  GET_ARRAY_FROM_VAR ("BASH_ARGV", bash_argv_v, bash_argv_a);
+  GET_ARRAY_FROM_VAR ("BASH_ARGC", bash_argc_v, bash_argc_a);
+
+  for (l = list, i = 0; l; l = l->next, i++)
+    array_push (bash_argv_a, l->word->word);
+
+  t = itos (i);
+  array_push (bash_argc_a, t);
+  free (t);
+#endif /* ARRAY_VARS && DEBUGGER */
+}
+
+/* Remove arguments from BASH_ARGV array.  Pop top element off BASH_ARGC
+   array and use that value as the count of elements to remove from
+   BASH_ARGV. */
+void
+pop_args ()
+{
+#if defined (ARRAY_VARS) && defined (DEBUGGER)
+  SHELL_VAR *bash_argv_v, *bash_argc_v;
+  ARRAY *bash_argv_a, *bash_argc_a;
+  ARRAY_ELEMENT *ce;
+  intmax_t i;
+
+  GET_ARRAY_FROM_VAR ("BASH_ARGV", bash_argv_v, bash_argv_a);
+  GET_ARRAY_FROM_VAR ("BASH_ARGC", bash_argc_v, bash_argc_a);
+
+  ce = array_shift (bash_argc_a, 1, 0);
+  if (ce == 0 || legal_number (element_value (ce), &i) == 0)
+    i = 0;
+
+  for ( ; i > 0; i--)
+    array_pop (bash_argv_a);
+  array_dispose_element (ce);
+#endif /* ARRAY_VARS && DEBUGGER */
+}
+
+/*************************************************
+ *                                              *
+ *     Functions to manage special variables    *
+ *                                              *
+ *************************************************/
+
+/* Extern declarations for variables this code has to manage. */
+extern int eof_encountered, eof_encountered_limit, ignoreeof;
+
+#if defined (READLINE)
+extern int no_line_editing;
+extern int hostname_list_initialized;
+#endif
+
+/* An alist of name.function for each special variable.  Most of the
+   functions don't do much, and in fact, this would be faster with a
+   switch statement, but by the end of this file, I am sick of switch
+   statements. */
+
+#define SET_INT_VAR(name, intvar)  intvar = find_variable (name) != 0
+
+/* This table will be sorted with qsort() the first time it's accessed. */
+struct name_and_function {
+  char *name;
+  sh_sv_func_t *function;
+};
+
+static struct name_and_function special_vars[] = {
+  { "GLOBIGNORE", sv_globignore },
+
+#if defined (HISTORY)
+  { "HISTCONTROL", sv_history_control },
+  { "HISTFILESIZE", sv_histsize },
+  { "HISTIGNORE", sv_histignore },
+  { "HISTSIZE", sv_histsize },
+  { "HISTTIMEFORMAT", sv_histtimefmt },
+#endif
+
+#if defined (READLINE)
+  { "HOSTFILE", sv_hostfile },
+#endif
+
+  { "IFS", sv_ifs },
+  { "IGNOREEOF", sv_ignoreeof },
+
+  { "LANG", sv_locale },
+  { "LC_ALL", sv_locale },
+  { "LC_COLLATE", sv_locale },
+  { "LC_CTYPE", sv_locale },
+  { "LC_MESSAGES", sv_locale },
+  { "LC_NUMERIC", sv_locale },
+
+  { "MAIL", sv_mail },
+  { "MAILCHECK", sv_mail },
+  { "MAILPATH", sv_mail },
+
+  { "OPTERR", sv_opterr },
+  { "OPTIND", sv_optind },
+
+  { "PATH", sv_path },
+  { "POSIXLY_CORRECT", sv_strict_posix },
+
+#if defined (READLINE)
+  { "TERM", sv_terminal },
+  { "TERMCAP", sv_terminal },
+  { "TERMINFO", sv_terminal },
+#endif /* READLINE */
+
+  { "TEXTDOMAIN", sv_locale },
+  { "TEXTDOMAINDIR", sv_locale },
+
+#if defined (HAVE_TZSET) && defined (PROMPT_STRING_DECODE)
+  { "TZ", sv_tz },
+#endif
+
+#if defined (HISTORY) && defined (BANG_HISTORY)
+  { "histchars", sv_histchars },
+#endif /* HISTORY && BANG_HISTORY */
+
+  { "ignoreeof", sv_ignoreeof },
+
+  { (char *)0, (sh_sv_func_t *)0 }
+};
+
+#define N_SPECIAL_VARS (sizeof (special_vars) / sizeof (special_vars[0]) - 1)
+
+static int
+sv_compare (sv1, sv2)
+     struct name_and_function *sv1, *sv2;
+{
+  int r;
+
+  if ((r = sv1->name[0] - sv2->name[0]) == 0)
+    r = strcmp (sv1->name, sv2->name);
+  return r;
+}
+
+static inline int
+find_special_var (name)
+     const char *name;
+{
+  register int i, r;
+
+  for (i = 0; special_vars[i].name; i++)
+    {
+      r = special_vars[i].name[0] - name[0];
+      if (r == 0)
+       r = strcmp (special_vars[i].name, name);
+      if (r == 0)
+       return i;
+      else if (r > 0)
+       /* Can't match any of rest of elements in sorted list.  Take this out
+          if it causes problems in certain environments. */
+        break;
+    }
+  return -1;
+}
+
+/* The variable in NAME has just had its state changed.  Check to see if it
+   is one of the special ones where something special happens. */
+void
+stupidly_hack_special_variables (name)
+     char *name;
+{
+  static int sv_sorted = 0;
+  int i;
+
+  if (sv_sorted == 0)  /* shouldn't need, but it's fairly cheap. */
+    {
+      qsort (special_vars, N_SPECIAL_VARS, sizeof (special_vars[0]),
+               (QSFUNC *)sv_compare);
+      sv_sorted = 1;
+    }
+
+  i = find_special_var (name);
+  if (i != -1)
+    (*(special_vars[i].function)) (name);
+}
+
+void
+sv_ifs (name)
+     char *name;
+{
+  SHELL_VAR *v;
+
+  v = find_variable ("IFS");
+  setifs (v);
+}
+
+/* What to do just after the PATH variable has changed. */
+void
+sv_path (name)
+     char *name;
+{
+  /* hash -r */
+  phash_flush ();
+}
+
+/* What to do just after one of the MAILxxxx variables has changed.  NAME
+   is the name of the variable.  This is called with NAME set to one of
+   MAIL, MAILCHECK, or MAILPATH.  */
+void
+sv_mail (name)
+     char *name;
+{
+  /* If the time interval for checking the files has changed, then
+     reset the mail timer.  Otherwise, one of the pathname vars
+     to the users mailbox has changed, so rebuild the array of
+     filenames. */
+  if (name[4] == 'C')  /* if (strcmp (name, "MAILCHECK") == 0) */
+    reset_mail_timer ();
+  else
+    {
+      free_mail_files ();
+      remember_mail_dates ();
+    }
+}
+
+/* What to do when GLOBIGNORE changes. */
+void
+sv_globignore (name)
+     char *name;
+{
+  setup_glob_ignore (name);
+}
+
+#if defined (READLINE)
+/* What to do just after one of the TERMxxx variables has changed.
+   If we are an interactive shell, then try to reset the terminal
+   information in readline. */
+void
+sv_terminal (name)
+     char *name;
+{
+  if (interactive_shell && no_line_editing == 0)
+    rl_reset_terminal (get_string_value ("TERM"));
+}
+
+void
+sv_hostfile (name)
+     char *name;
+{
+  SHELL_VAR *v;
+
+  v = find_variable (name);
+  if (v == 0)
+    clear_hostname_list ();
+  else
+    hostname_list_initialized = 0;
+}
+#endif /* READLINE */
+
+#if defined (HISTORY)
+/* What to do after the HISTSIZE or HISTFILESIZE variables change.
+   If there is a value for this HISTSIZE (and it is numeric), then stifle
+   the history.  Otherwise, if there is NO value for this variable,
+   unstifle the history.  If name is HISTFILESIZE, and its value is
+   numeric, truncate the history file to hold no more than that many
+   lines. */
+void
+sv_histsize (name)
+     char *name;
+{
+  char *temp;
+  intmax_t num;
+
+  temp = get_string_value (name);
+
+  if (temp && *temp)
+    {
+      if (legal_number (temp, &num))
+       {
+         if (name[4] == 'S')
+           {
+             stifle_history (num);
+             num = where_history ();
+             if (history_lines_this_session > num)
+               history_lines_this_session = num;
+           }
+         else
+           {
+             history_truncate_file (get_string_value ("HISTFILE"), (int)num);
+             if (num <= history_lines_in_file)
+               history_lines_in_file = num;
+           }
+       }
+    }
+  else if (name[4] == 'S')
+    unstifle_history ();
+}
+
+/* What to do after the HISTIGNORE variable changes. */
+void
+sv_histignore (name)
+     char *name;
+{
+  setup_history_ignore (name);
+}
+
+/* What to do after the HISTCONTROL variable changes. */
+void
+sv_history_control (name)
+     char *name;
+{
+  char *temp;
+  char *val;
+  int tptr;
+
+  history_control = 0;
+  temp = get_string_value (name);
+
+  if (temp == 0 || *temp == 0)
+    return;
+
+  tptr = 0;
+  while (val = extract_colon_unit (temp, &tptr))
+    {
+      if (STREQ (val, "ignorespace"))
+       history_control |= HC_IGNSPACE;
+      else if (STREQ (val, "ignoredups"))
+       history_control |= HC_IGNDUPS;
+      else if (STREQ (val, "ignoreboth"))
+       history_control |= HC_IGNBOTH;
+      else if (STREQ (val, "erasedups"))
+       history_control |= HC_ERASEDUPS;
+
+      free (val);
+    }
+}
+
+#if defined (BANG_HISTORY)
+/* Setting/unsetting of the history expansion character. */
+void
+sv_histchars (name)
+     char *name;
+{
+  char *temp;
+
+  temp = get_string_value (name);
+  if (temp)
+    {
+      history_expansion_char = *temp;
+      if (temp[0] && temp[1])
+       {
+         history_subst_char = temp[1];
+         if (temp[2])
+             history_comment_char = temp[2];
+       }
+    }
+  else
+    {
+      history_expansion_char = '!';
+      history_subst_char = '^';
+      history_comment_char = '#';
+    }
+}
+#endif /* BANG_HISTORY */
+
+void
+sv_histtimefmt (name)
+     char *name;
+{
+  SHELL_VAR *v;
+
+  v = find_variable (name);
+  history_write_timestamps = (v != 0);
+}
+#endif /* HISTORY */
+
+#if defined (HAVE_TZSET) && defined (PROMPT_STRING_DECODE)
+void
+sv_tz (name)
+     char *name;
+{
+  tzset ();
+}
+#endif
+
+/* If the variable exists, then the value of it can be the number
+   of times we actually ignore the EOF.  The default is small,
+   (smaller than csh, anyway). */
+void
+sv_ignoreeof (name)
+     char *name;
+{
+  SHELL_VAR *tmp_var;
+  char *temp;
+
+  eof_encountered = 0;
+
+  tmp_var = find_variable (name);
+  ignoreeof = tmp_var != 0;
+  temp = tmp_var ? value_cell (tmp_var) : (char *)NULL;
+  if (temp)
+    eof_encountered_limit = (*temp && all_digits (temp)) ? atoi (temp) : 10;
+  set_shellopts ();    /* make sure `ignoreeof' is/is not in $SHELLOPTS */
+}
+
+void
+sv_optind (name)
+     char *name;
+{
+  char *tt;
+  int s;
+
+  tt = get_string_value ("OPTIND");
+  if (tt && *tt)
+    {
+      s = atoi (tt);
+
+      /* According to POSIX, setting OPTIND=1 resets the internal state
+        of getopt (). */
+      if (s < 0 || s == 1)
+       s = 0;
+    }
+  else
+    s = 0;
+  getopts_reset (s);
+}
+
+void
+sv_opterr (name)
+     char *name;
+{
+  char *tt;
+
+  tt = get_string_value ("OPTERR");
+  sh_opterr = (tt && *tt) ? atoi (tt) : 1;
+}
+
+void
+sv_strict_posix (name)
+     char *name;
+{
+  SET_INT_VAR (name, posixly_correct);
+  posix_initialize (posixly_correct);
+#if defined (READLINE)
+  if (interactive_shell)
+    posix_readline_initialize (posixly_correct);
+#endif /* READLINE */
+  set_shellopts ();    /* make sure `posix' is/is not in $SHELLOPTS */
+}
+
+void
+sv_locale (name)
+     char *name;
+{
+  char *v;
+
+  v = get_string_value (name);
+  if (name[0] == 'L' && name[1] == 'A')        /* LANG */
+    set_lang (name, v);
+  else
+    set_locale_var (name, v);          /* LC_*, TEXTDOMAIN* */
+}
+
+#if defined (ARRAY_VARS)
+void
+set_pipestatus_array (ps, nproc)
+     int *ps;
+     int nproc;
+{
+  SHELL_VAR *v;
+  ARRAY *a;
+  ARRAY_ELEMENT *ae;
+  register int i;
+  char *t, tbuf[INT_STRLEN_BOUND(int) + 1];
+
+  v = find_variable ("PIPESTATUS");
+  if (v == 0)
+    v = make_new_array_variable ("PIPESTATUS");
+  if (array_p (v) == 0)
+    return;            /* Do nothing if not an array variable. */
+  a = array_cell (v);
+
+  if (a == 0 || array_num_elements (a) == 0)
+    {
+      for (i = 0; i < nproc; i++)      /* was ps[i] != -1, not i < nproc */
+       {
+         t = inttostr (ps[i], tbuf, sizeof (tbuf));
+         array_insert (a, i, t);
+       }
+      return;
+    }
+
+  /* Fast case */
+  if (array_num_elements (a) == nproc && nproc == 1)
+    {
+      ae = element_forw (a->head);
+      free (element_value (ae));
+      ae->value = itos (ps[0]);
+    }
+  else if (array_num_elements (a) <= nproc)
+    {
+      /* modify in array_num_elements members in place, then add */
+      ae = a->head;
+      for (i = 0; i < array_num_elements (a); i++)
+       {
+         ae = element_forw (ae);
+         free (element_value (ae));
+         ae->value = itos (ps[i]);
+       }
+      /* add any more */
+      for ( ; i < nproc; i++)
+       {
+         t = inttostr (ps[i], tbuf, sizeof (tbuf));
+         array_insert (a, i, t);
+       }
+    }
+  else
+    {
+      /* deleting elements.  it's faster to rebuild the array. */        
+      array_flush (a);
+      for (i = 0; ps[i] != -1; i++)
+       {
+         t = inttostr (ps[i], tbuf, sizeof (tbuf));
+         array_insert (a, i, t);
+       }
+    }
+}
+#endif
+
+void
+set_pipestatus_from_exit (s)
+     int s;
+{
+#if defined (ARRAY_VARS)
+  static int v[2] = { 0, -1 };
+
+  v[0] = s;
+  set_pipestatus_array (v, 1);
+#endif
+}