]> git.ipfire.org Git - thirdparty/bash.git/commitdiff
commit bash-20150123 snapshot
authorChet Ramey <chet.ramey@case.edu>
Tue, 27 Jan 2015 16:11:42 +0000 (11:11 -0500)
committerChet Ramey <chet.ramey@case.edu>
Tue, 27 Jan 2015 16:11:42 +0000 (11:11 -0500)
73 files changed:
CWRU/CWRU.chlog
CWRU/CWRU.chlog~
aclocal.m4
alias.c
arrayfunc.c
autom4te.cache/output.0
autom4te.cache/requests
autom4te.cache/traces.0
bashline.c
builtins/alias.def
builtins/bind.def
builtins/cd.def
builtins/command.def
builtins/common.c
builtins/common.c~ [new file with mode: 0644]
builtins/complete.def
builtins/declare.def
builtins/echo.def
builtins/exec.def
builtins/hash.def
builtins/help.def
builtins/history.def
builtins/jobs.def
builtins/kill.def
builtins/mapfile.def
builtins/printf.def
builtins/pushd.def
builtins/read.def
builtins/set.def
builtins/setattr.def
builtins/test.def
builtins/type.def
builtins/ulimit.def
builtins/umask.def
builtins/umask.def~ [new file with mode: 0644]
builtins/wait.def
builtins/wait.def~ [new file with mode: 0644]
config.h.in
configure
execute_cmd.c
general.c
include/chartypes.h
include/chartypes.h~ [new file with mode: 0644]
include/posixjmp.h
include/posixjmp.h~ [new file with mode: 0644]
lib/readline/bind.c
lib/readline/bind.c~ [new file with mode: 0644]
lib/readline/chardefs.h
lib/readline/chardefs.h~ [new file with mode: 0644]
lib/readline/histfile.c
lib/readline/histfile.c~ [new file with mode: 0644]
lib/readline/readline.c
lib/readline/readline.c~ [new file with mode: 0644]
lib/readline/rlmbutil.h
lib/readline/rlmbutil.h~ [new file with mode: 0644]
lib/readline/text.c
lib/readline/text.c~ [new file with mode: 0644]
lib/sh/casemod.c
lib/sh/casemod.c~ [new file with mode: 0644]
lib/sh/shquote.c
locale.c
locale.c~ [new file with mode: 0644]
parse.y
po/._fr.po
shell.c
shell.c~ [new file with mode: 0644]
subst.c
test.c
tests/RUN-ONE-TEST
tests/RUN-ONE-TEST~ [new file with mode: 0755]
tests/new-exp.right
variables.c
variables.c~ [new file with mode: 0644]

index 59157ae8232d0d231aa3609afeb16bdc36fae16f..1028fd39312c044c946897b632cc1003c2669eec 100644 (file)
@@ -7902,3 +7902,38 @@ lib/readline/funmap.c
 
 lib/readline/readline.h
        - rl_vi_yank_pop: extern declaration
+
+                                  1/21
+                                  ----
+lib/sh/shquote.c
+       - sh_backslash_quote: change to understand and handle multibyte
+         characters, using is_basic, COPY_CHAR_P.  See
+         https://bugzilla.redhat.com/show_bug.cgi?id=1184320 for original
+         report
+
+                                  1/23
+                                  ----
+include/posixjmp.h
+       - use setjmp_sigs instead of redefined setjmp, it fails on Cygwin.
+         Report from Eric Blake <eblake@redhat.com>
+
+builtins/wait.def,shell.c
+       - use setjmp_sigs instead of setjmp call, since setjmp no longer
+         redefined to something known
+
+include/chartypes.h, lib/readline/chardefs.h
+       - make sure all ctype.h macros are called with unsigned char args,
+         casting to make sure
+
+lib/sh/casemod.c
+       - sh_modcase: don't assume that the upper and lower case versions of a
+         character are the same width, so don't try to do the conversion in
+         place: convert and copy the converted characters one or more at a
+         time to the returned string
+       - sh_modcase: since upper and lower case versions of same character
+         may have different widths in some locales, don't shortcut and assume
+         that we can do single-byte case modification (toupper) with a
+         single-byte input character (e.g., `i').  Fix for problem reported
+         by Stephane Chazelas <stephane.chazelas@gmail.com>
+
+       
index 9bafa04629f4226d75c66ed799ac0cd5535254e2..817082eaa99b6532e9703b4f33a25d8b32ebb444 100644 (file)
@@ -7899,3 +7899,28 @@ lib/readline/kill.c
 
 lib/readline/funmap.c
        - `vi-yank-pop': bindable name mapped to rl_vi_yank_pop
+
+lib/readline/readline.h
+       - rl_vi_yank_pop: extern declaration
+
+                                  1/21
+                                  ----
+lib/sh/shquote.c
+       - sh_backslash_quote: change to understand and handle multibyte
+         characters, using is_basic, COPY_CHAR_P.  See
+         https://bugzilla.redhat.com/show_bug.cgi?id=1184320 for original
+         report
+
+                                  1/23
+                                  ----
+include/posixjmp.h
+       - use setjmp_sigs instead of redefined setjmp, it fails on Cygwin.
+         Report from Eric Blake <eblake@redhat.com>
+
+builtins/wait.def,shell.c
+       - use setjmp_sigs instead of setjmp call, since setjmp no longer
+         redefined to something known
+
+include/chartypes.h, lib/readline/chardefs.h
+       - make sure all ctype.h macros are called with unsigned char args,
+         casting to make sure
index ff16dffa78d426c3302ea8f10db2b9d254203b68..105b886e3bd1b36bd5c87c46d9143da054c8e099 100644 (file)
@@ -1692,6 +1692,8 @@ AC_CHECK_HEADERS(wctype.h)
 AC_CHECK_HEADERS(wchar.h)
 AC_CHECK_HEADERS(langinfo.h)
 
+AC_CHECK_HEADERS(mbstr.h)
+
 AC_CHECK_FUNC(mbrlen, AC_DEFINE(HAVE_MBRLEN))
 AC_CHECK_FUNC(mbscasecmp, AC_DEFINE(HAVE_MBSCMP))
 AC_CHECK_FUNC(mbscmp, AC_DEFINE(HAVE_MBSCMP))
diff --git a/alias.c b/alias.c
index 7839229659e9c3159c2a5c2e3b823e7757d25ba1..3df3ae226c86f3ef7c9d7f417752b8c095997883 100644 (file)
--- a/alias.c
+++ b/alias.c
 #  include "pcomplete.h"
 #endif
 
+#if defined (HAVE_MBSTR_H) && defined (HAVE_MBSCHR)
+#  include <mbstr.h>           /* mbschr */
+#endif
+
 #define ALIAS_HASH_BUCKETS     16      /* must be power of two */
 
 typedef int sh_alias_map_func_t __P((alias_t *));
index a7294f85539c2f6ff51451a468026eb0f355afe6..c2ea0e5f1989c4539f03fc8b53406ac114b6a1fd 100644 (file)
@@ -33,6 +33,9 @@
 #include "pathexp.h"
 
 #include "shmbutil.h"
+#if defined (HAVE_MBSTR_H) && defined (HAVE_MBSCHR)
+#  include <mbstr.h>           /* mbschr */
+#endif
 
 #include "builtins/common.h"
 
index e72620cee3599870786274ae48d63140f76efb95..8c9e0c36dd550db1204a20621b3944d60ddb68d0 100644 (file)
 done
 
 
+for ac_header in mbstr.h
+do :
+  ac_fn_c_check_header_mongrel "$LINENO" "mbstr.h" "ac_cv_header_mbstr_h" "$ac_includes_default"
+if test "x$ac_cv_header_mbstr_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_MBSTR_H 1
+_ACEOF
+fi
+
+done
+
+
 ac_fn_c_check_func "$LINENO" "mbrlen" "ac_cv_func_mbrlen"
 if test "x$ac_cv_func_mbrlen" = xyes; then :
   $as_echo "@%:@define HAVE_MBRLEN 1" >>confdefs.h
index 5cbb2090b31fef8e5c01b74bfc92877061d69429..02e81938ed3a69686d913522f29509518de0c711 100644 (file)
                         'configure.ac'
                       ],
                       {
-                        'AM_PROG_F77_C_O' => 1,
                         '_LT_AC_TAGCONFIG' => 1,
-                        'm4_pattern_forbid' => 1,
+                        'AM_PROG_F77_C_O' => 1,
                         'AC_INIT' => 1,
-                        'AC_CANONICAL_TARGET' => 1,
+                        'm4_pattern_forbid' => 1,
                         '_AM_COND_IF' => 1,
-                        'AC_CONFIG_LIBOBJ_DIR' => 1,
+                        'AC_CANONICAL_TARGET' => 1,
                         'AC_SUBST' => 1,
-                        'AC_CANONICAL_HOST' => 1,
+                        'AC_CONFIG_LIBOBJ_DIR' => 1,
                         'AC_FC_SRCEXT' => 1,
+                        'AC_CANONICAL_HOST' => 1,
                         'AC_PROG_LIBTOOL' => 1,
                         'AM_INIT_AUTOMAKE' => 1,
-                        'AC_CONFIG_SUBDIRS' => 1,
                         'AM_PATH_GUILE' => 1,
+                        'AC_CONFIG_SUBDIRS' => 1,
                         'AM_AUTOMAKE_VERSION' => 1,
                         'LT_CONFIG_LTDL_DIR' => 1,
-                        'AC_CONFIG_LINKS' => 1,
                         'AC_REQUIRE_AUX_FILE' => 1,
-                        'LT_SUPPORTED_TAG' => 1,
+                        'AC_CONFIG_LINKS' => 1,
                         'm4_sinclude' => 1,
+                        'LT_SUPPORTED_TAG' => 1,
                         'AM_MAINTAINER_MODE' => 1,
                         'AM_NLS' => 1,
                         'AC_FC_PP_DEFINE' => 1,
                         'AM_GNU_GETTEXT_INTL_SUBDIR' => 1,
-                        '_m4_warn' => 1,
                         'AM_MAKEFILE_INCLUDE' => 1,
+                        '_m4_warn' => 1,
                         'AM_PROG_CXX_C_O' => 1,
-                        '_AM_MAKEFILE_INCLUDE' => 1,
                         '_AM_COND_ENDIF' => 1,
+                        '_AM_MAKEFILE_INCLUDE' => 1,
                         'AM_ENABLE_MULTILIB' => 1,
                         'AM_SILENT_RULES' => 1,
                         'AM_PROG_MOC' => 1,
                         'AC_CONFIG_FILES' => 1,
-                        'LT_INIT' => 1,
                         'include' => 1,
-                        'AM_GNU_GETTEXT' => 1,
+                        'LT_INIT' => 1,
                         'AM_PROG_AR' => 1,
+                        'AM_GNU_GETTEXT' => 1,
                         'AC_LIBSOURCE' => 1,
-                        'AC_CANONICAL_BUILD' => 1,
                         'AM_PROG_FC_C_O' => 1,
+                        'AC_CANONICAL_BUILD' => 1,
                         'AC_FC_FREEFORM' => 1,
-                        'AC_FC_PP_SRCEXT' => 1,
                         'AH_OUTPUT' => 1,
-                        'AC_CONFIG_AUX_DIR' => 1,
+                        'AC_FC_PP_SRCEXT' => 1,
                         '_AM_SUBST_NOTMAKE' => 1,
-                        'm4_pattern_allow' => 1,
-                        'AM_PROG_CC_C_O' => 1,
+                        'AC_CONFIG_AUX_DIR' => 1,
                         'sinclude' => 1,
-                        'AM_CONDITIONAL' => 1,
-                        'AC_CANONICAL_SYSTEM' => 1,
+                        'AM_PROG_CC_C_O' => 1,
+                        'm4_pattern_allow' => 1,
                         'AM_XGETTEXT_OPTION' => 1,
+                        'AC_CANONICAL_SYSTEM' => 1,
+                        'AM_CONDITIONAL' => 1,
                         'AC_CONFIG_HEADERS' => 1,
                         'AC_DEFINE_TRACE_LITERAL' => 1,
                         'AM_POT_TOOLS' => 1,
index 6df9bc619644d9395a84b6037266720cf7efe631..b92884da74520778403a8bf9242cc3c0a231c8b5 100644 (file)
@@ -620,7 +620,7 @@ m4trace:configure.ac:553: -1- _m4_warn([obsolete], [The macro `AC_TRY_RUN' is ob
 You should run autoupdate.], [../../lib/autoconf/general.m4:2764: AC_TRY_RUN is expanded from...
 ../../lib/m4sugar/m4sh.m4:639: AS_IF is expanded from...
 ../../lib/autoconf/general.m4:2031: AC_CACHE_VAL is expanded from...
-aclocal.m4:1806: RL_LIB_READLINE_VERSION is expanded from...
+aclocal.m4:1808: RL_LIB_READLINE_VERSION is expanded from...
 configure.ac:553: the top level])
 m4trace:configure.ac:553: -1- AC_DEFINE_TRACE_LITERAL([RL_READLINE_VERSION])
 m4trace:configure.ac:553: -1- m4_pattern_allow([^RL_READLINE_VERSION$])
@@ -815,8 +815,8 @@ m4trace:configure.ac:707: -1- AC_SUBST_TRACE([MSGMERGE])
 m4trace:configure.ac:707: -1- m4_pattern_allow([^MSGMERGE$])
 m4trace:configure.ac:707: -1- _m4_warn([obsolete], [The macro `AC_OUTPUT_COMMANDS' is obsolete.
 You should run autoupdate.], [../../lib/autoconf/status.m4:1026: AC_OUTPUT_COMMANDS is expanded from...
-aclocal.m4:3707: AM_PO_SUBDIRS is expanded from...
-aclocal.m4:2111: AM_GNU_GETTEXT is expanded from...
+aclocal.m4:3709: AM_PO_SUBDIRS is expanded from...
+aclocal.m4:2113: AM_GNU_GETTEXT is expanded from...
 configure.ac:707: the top level])
 m4trace:configure.ac:707: -1- AC_DEFINE_TRACE_LITERAL([off_t])
 m4trace:configure.ac:707: -1- m4_pattern_allow([^off_t$])
@@ -880,9 +880,9 @@ You should run autoupdate.], [../../lib/autoconf/general.m4:2764: AC_TRY_RUN is
 ../../lib/m4sugar/m4sh.m4:639: AS_IF is expanded from...
 ../../lib/autoconf/general.m4:2031: AC_CACHE_VAL is expanded from...
 ../../lib/autoconf/general.m4:2052: AC_CACHE_CHECK is expanded from...
-aclocal.m4:2613: gt_INTDIV0 is expanded from...
-aclocal.m4:2399: AM_INTL_SUBDIR is expanded from...
-aclocal.m4:2111: AM_GNU_GETTEXT is expanded from...
+aclocal.m4:2615: gt_INTDIV0 is expanded from...
+aclocal.m4:2401: AM_INTL_SUBDIR is expanded from...
+aclocal.m4:2113: AM_GNU_GETTEXT is expanded from...
 configure.ac:707: the top level])
 m4trace:configure.ac:707: -1- AC_DEFINE_TRACE_LITERAL([INTDIV0_RAISES_SIGFPE])
 m4trace:configure.ac:707: -1- m4_pattern_allow([^INTDIV0_RAISES_SIGFPE$])
@@ -893,10 +893,10 @@ You should run autoupdate.], [../../lib/autoconf/general.m4:2614: AC_TRY_COMPILE
 ../../lib/m4sugar/m4sh.m4:639: AS_IF is expanded from...
 ../../lib/autoconf/general.m4:2031: AC_CACHE_VAL is expanded from...
 ../../lib/autoconf/general.m4:2052: AC_CACHE_CHECK is expanded from...
-aclocal.m4:2715: jm_AC_HEADER_INTTYPES_H is expanded from...
-aclocal.m4:4016: jm_AC_TYPE_UINTMAX_T is expanded from...
-aclocal.m4:2399: AM_INTL_SUBDIR is expanded from...
-aclocal.m4:2111: AM_GNU_GETTEXT is expanded from...
+aclocal.m4:2717: jm_AC_HEADER_INTTYPES_H is expanded from...
+aclocal.m4:4018: jm_AC_TYPE_UINTMAX_T is expanded from...
+aclocal.m4:2401: AM_INTL_SUBDIR is expanded from...
+aclocal.m4:2113: AM_GNU_GETTEXT is expanded from...
 configure.ac:707: the top level])
 m4trace:configure.ac:707: -1- AC_DEFINE_TRACE_LITERAL([HAVE_INTTYPES_H_WITH_UINTMAX])
 m4trace:configure.ac:707: -1- m4_pattern_allow([^HAVE_INTTYPES_H_WITH_UINTMAX$])
@@ -908,10 +908,10 @@ You should run autoupdate.], [../../lib/autoconf/general.m4:2614: AC_TRY_COMPILE
 ../../lib/m4sugar/m4sh.m4:639: AS_IF is expanded from...
 ../../lib/autoconf/general.m4:2031: AC_CACHE_VAL is expanded from...
 ../../lib/autoconf/general.m4:2052: AC_CACHE_CHECK is expanded from...
-aclocal.m4:3986: jm_AC_HEADER_STDINT_H is expanded from...
-aclocal.m4:4016: jm_AC_TYPE_UINTMAX_T is expanded from...
-aclocal.m4:2399: AM_INTL_SUBDIR is expanded from...
-aclocal.m4:2111: AM_GNU_GETTEXT is expanded from...
+aclocal.m4:3988: jm_AC_HEADER_STDINT_H is expanded from...
+aclocal.m4:4018: jm_AC_TYPE_UINTMAX_T is expanded from...
+aclocal.m4:2401: AM_INTL_SUBDIR is expanded from...
+aclocal.m4:2113: AM_GNU_GETTEXT is expanded from...
 configure.ac:707: the top level])
 m4trace:configure.ac:707: -1- AC_DEFINE_TRACE_LITERAL([HAVE_STDINT_H_WITH_UINTMAX])
 m4trace:configure.ac:707: -1- m4_pattern_allow([^HAVE_STDINT_H_WITH_UINTMAX$])
@@ -923,10 +923,10 @@ You should run autoupdate.], [../../lib/autoconf/general.m4:2687: AC_TRY_LINK is
 ../../lib/m4sugar/m4sh.m4:639: AS_IF is expanded from...
 ../../lib/autoconf/general.m4:2031: AC_CACHE_VAL is expanded from...
 ../../lib/autoconf/general.m4:2052: AC_CACHE_CHECK is expanded from...
-aclocal.m4:4043: jm_AC_TYPE_UNSIGNED_LONG_LONG is expanded from...
-aclocal.m4:4016: jm_AC_TYPE_UINTMAX_T is expanded from...
-aclocal.m4:2399: AM_INTL_SUBDIR is expanded from...
-aclocal.m4:2111: AM_GNU_GETTEXT is expanded from...
+aclocal.m4:4045: jm_AC_TYPE_UNSIGNED_LONG_LONG is expanded from...
+aclocal.m4:4018: jm_AC_TYPE_UINTMAX_T is expanded from...
+aclocal.m4:2401: AM_INTL_SUBDIR is expanded from...
+aclocal.m4:2113: AM_GNU_GETTEXT is expanded from...
 configure.ac:707: the top level])
 m4trace:configure.ac:707: -1- AC_DEFINE_TRACE_LITERAL([HAVE_UNSIGNED_LONG_LONG])
 m4trace:configure.ac:707: -1- m4_pattern_allow([^HAVE_UNSIGNED_LONG_LONG$])
@@ -946,9 +946,9 @@ You should run autoupdate.], [../../lib/autoconf/general.m4:2614: AC_TRY_COMPILE
 ../../lib/m4sugar/m4sh.m4:639: AS_IF is expanded from...
 ../../lib/autoconf/general.m4:2031: AC_CACHE_VAL is expanded from...
 ../../lib/autoconf/general.m4:2052: AC_CACHE_CHECK is expanded from...
-aclocal.m4:2688: gt_HEADER_INTTYPES_H is expanded from...
-aclocal.m4:2399: AM_INTL_SUBDIR is expanded from...
-aclocal.m4:2111: AM_GNU_GETTEXT is expanded from...
+aclocal.m4:2690: gt_HEADER_INTTYPES_H is expanded from...
+aclocal.m4:2401: AM_INTL_SUBDIR is expanded from...
+aclocal.m4:2113: AM_GNU_GETTEXT is expanded from...
 configure.ac:707: the top level])
 m4trace:configure.ac:707: -1- AC_DEFINE_TRACE_LITERAL([HAVE_INTTYPES_H])
 m4trace:configure.ac:707: -1- m4_pattern_allow([^HAVE_INTTYPES_H$])
@@ -959,9 +959,9 @@ You should run autoupdate.], [../../lib/autoconf/general.m4:2614: AC_TRY_COMPILE
 ../../lib/m4sugar/m4sh.m4:639: AS_IF is expanded from...
 ../../lib/autoconf/general.m4:2031: AC_CACHE_VAL is expanded from...
 ../../lib/autoconf/general.m4:2052: AC_CACHE_CHECK is expanded from...
-aclocal.m4:2743: gt_INTTYPES_PRI is expanded from...
-aclocal.m4:2399: AM_INTL_SUBDIR is expanded from...
-aclocal.m4:2111: AM_GNU_GETTEXT is expanded from...
+aclocal.m4:2745: gt_INTTYPES_PRI is expanded from...
+aclocal.m4:2401: AM_INTL_SUBDIR is expanded from...
+aclocal.m4:2113: AM_GNU_GETTEXT is expanded from...
 configure.ac:707: the top level])
 m4trace:configure.ac:707: -1- AC_DEFINE_TRACE_LITERAL([PRI_MACROS_BROKEN])
 m4trace:configure.ac:707: -1- m4_pattern_allow([^PRI_MACROS_BROKEN$])
@@ -1038,20 +1038,20 @@ You should run autoupdate.], [../../lib/autoconf/general.m4:2687: AC_TRY_LINK is
 ../../lib/m4sugar/m4sh.m4:639: AS_IF is expanded from...
 ../../lib/autoconf/general.m4:2031: AC_CACHE_VAL is expanded from...
 ../../lib/autoconf/general.m4:2052: AC_CACHE_CHECK is expanded from...
-aclocal.m4:2521: AM_ICONV_LINK is expanded from...
-aclocal.m4:2576: AM_ICONV is expanded from...
-aclocal.m4:2399: AM_INTL_SUBDIR is expanded from...
-aclocal.m4:2111: AM_GNU_GETTEXT is expanded from...
+aclocal.m4:2523: AM_ICONV_LINK is expanded from...
+aclocal.m4:2578: AM_ICONV is expanded from...
+aclocal.m4:2401: AM_INTL_SUBDIR is expanded from...
+aclocal.m4:2113: AM_GNU_GETTEXT is expanded from...
 configure.ac:707: the top level])
 m4trace:configure.ac:707: -1- _m4_warn([obsolete], [The macro `AC_TRY_LINK' is obsolete.
 You should run autoupdate.], [../../lib/autoconf/general.m4:2687: AC_TRY_LINK is expanded from...
 ../../lib/m4sugar/m4sh.m4:639: AS_IF is expanded from...
 ../../lib/autoconf/general.m4:2031: AC_CACHE_VAL is expanded from...
 ../../lib/autoconf/general.m4:2052: AC_CACHE_CHECK is expanded from...
-aclocal.m4:2521: AM_ICONV_LINK is expanded from...
-aclocal.m4:2576: AM_ICONV is expanded from...
-aclocal.m4:2399: AM_INTL_SUBDIR is expanded from...
-aclocal.m4:2111: AM_GNU_GETTEXT is expanded from...
+aclocal.m4:2523: AM_ICONV_LINK is expanded from...
+aclocal.m4:2578: AM_ICONV is expanded from...
+aclocal.m4:2401: AM_INTL_SUBDIR is expanded from...
+aclocal.m4:2113: AM_GNU_GETTEXT is expanded from...
 configure.ac:707: the top level])
 m4trace:configure.ac:707: -1- AC_DEFINE_TRACE_LITERAL([HAVE_ICONV])
 m4trace:configure.ac:707: -1- m4_pattern_allow([^HAVE_ICONV$])
@@ -1067,9 +1067,9 @@ m4trace:configure.ac:707: -1- _m4_warn([obsolete], [The macro `AC_TRY_COMPILE' i
 You should run autoupdate.], [../../lib/autoconf/general.m4:2614: AC_TRY_COMPILE is expanded from...
 ../../lib/m4sugar/m4sh.m4:639: AS_IF is expanded from...
 ../../lib/autoconf/general.m4:2031: AC_CACHE_VAL is expanded from...
-aclocal.m4:2576: AM_ICONV is expanded from...
-aclocal.m4:2399: AM_INTL_SUBDIR is expanded from...
-aclocal.m4:2111: AM_GNU_GETTEXT is expanded from...
+aclocal.m4:2578: AM_ICONV is expanded from...
+aclocal.m4:2401: AM_INTL_SUBDIR is expanded from...
+aclocal.m4:2113: AM_GNU_GETTEXT is expanded from...
 configure.ac:707: the top level])
 m4trace:configure.ac:707: -1- AC_DEFINE_TRACE_LITERAL([ICONV_CONST])
 m4trace:configure.ac:707: -1- m4_pattern_allow([^ICONV_CONST$])
@@ -1080,9 +1080,9 @@ You should run autoupdate.], [../../lib/autoconf/general.m4:2687: AC_TRY_LINK is
 ../../lib/m4sugar/m4sh.m4:639: AS_IF is expanded from...
 ../../lib/autoconf/general.m4:2031: AC_CACHE_VAL is expanded from...
 ../../lib/autoconf/general.m4:2052: AC_CACHE_CHECK is expanded from...
-aclocal.m4:2040: AM_LANGINFO_CODESET is expanded from...
-aclocal.m4:2399: AM_INTL_SUBDIR is expanded from...
-aclocal.m4:2111: AM_GNU_GETTEXT is expanded from...
+aclocal.m4:2042: AM_LANGINFO_CODESET is expanded from...
+aclocal.m4:2401: AM_INTL_SUBDIR is expanded from...
+aclocal.m4:2113: AM_GNU_GETTEXT is expanded from...
 configure.ac:707: the top level])
 m4trace:configure.ac:707: -1- AC_DEFINE_TRACE_LITERAL([HAVE_LANGINFO_CODESET])
 m4trace:configure.ac:707: -1- m4_pattern_allow([^HAVE_LANGINFO_CODESET$])
@@ -1093,9 +1093,9 @@ You should run autoupdate.], [../../lib/autoconf/general.m4:2687: AC_TRY_LINK is
 ../../lib/m4sugar/m4sh.m4:639: AS_IF is expanded from...
 ../../lib/autoconf/general.m4:2031: AC_CACHE_VAL is expanded from...
 ../../lib/autoconf/general.m4:2052: AC_CACHE_CHECK is expanded from...
-aclocal.m4:2810: AM_LC_MESSAGES is expanded from...
-aclocal.m4:2399: AM_INTL_SUBDIR is expanded from...
-aclocal.m4:2111: AM_GNU_GETTEXT is expanded from...
+aclocal.m4:2812: AM_LC_MESSAGES is expanded from...
+aclocal.m4:2401: AM_INTL_SUBDIR is expanded from...
+aclocal.m4:2113: AM_GNU_GETTEXT is expanded from...
 configure.ac:707: the top level])
 m4trace:configure.ac:707: -1- AC_DEFINE_TRACE_LITERAL([HAVE_LC_MESSAGES])
 m4trace:configure.ac:707: -1- m4_pattern_allow([^HAVE_LC_MESSAGES$])
@@ -1113,21 +1113,21 @@ You should run autoupdate.], [../../lib/autoconf/general.m4:2687: AC_TRY_LINK is
 ../../lib/m4sugar/m4sh.m4:639: AS_IF is expanded from...
 ../../lib/autoconf/general.m4:2031: AC_CACHE_VAL is expanded from...
 ../../lib/autoconf/general.m4:2052: AC_CACHE_CHECK is expanded from...
-aclocal.m4:2111: AM_GNU_GETTEXT is expanded from...
+aclocal.m4:2113: AM_GNU_GETTEXT is expanded from...
 configure.ac:707: the top level])
 m4trace:configure.ac:707: -1- _m4_warn([obsolete], [The macro `AC_TRY_LINK' is obsolete.
 You should run autoupdate.], [../../lib/autoconf/general.m4:2687: AC_TRY_LINK is expanded from...
 ../../lib/m4sugar/m4sh.m4:639: AS_IF is expanded from...
 ../../lib/autoconf/general.m4:2031: AC_CACHE_VAL is expanded from...
 ../../lib/autoconf/general.m4:2052: AC_CACHE_CHECK is expanded from...
-aclocal.m4:2111: AM_GNU_GETTEXT is expanded from...
+aclocal.m4:2113: AM_GNU_GETTEXT is expanded from...
 configure.ac:707: the top level])
 m4trace:configure.ac:707: -1- _m4_warn([obsolete], [The macro `AC_TRY_LINK' is obsolete.
 You should run autoupdate.], [../../lib/autoconf/general.m4:2687: AC_TRY_LINK is expanded from...
 ../../lib/m4sugar/m4sh.m4:639: AS_IF is expanded from...
 ../../lib/autoconf/general.m4:2031: AC_CACHE_VAL is expanded from...
 ../../lib/autoconf/general.m4:2052: AC_CACHE_CHECK is expanded from...
-aclocal.m4:2111: AM_GNU_GETTEXT is expanded from...
+aclocal.m4:2113: AM_GNU_GETTEXT is expanded from...
 configure.ac:707: the top level])
 m4trace:configure.ac:707: -1- AC_DEFINE_TRACE_LITERAL([ENABLE_NLS])
 m4trace:configure.ac:707: -1- m4_pattern_allow([^ENABLE_NLS$])
@@ -1810,6 +1810,10 @@ m4trace:configure.ac:862: -1- AH_OUTPUT([HAVE_LANGINFO_H], [/* Define to 1 if yo
 @%:@undef HAVE_LANGINFO_H])
 m4trace:configure.ac:862: -1- AC_DEFINE_TRACE_LITERAL([HAVE_LANGINFO_H])
 m4trace:configure.ac:862: -1- m4_pattern_allow([^HAVE_LANGINFO_H$])
+m4trace:configure.ac:862: -1- AH_OUTPUT([HAVE_MBSTR_H], [/* Define to 1 if you have the <mbstr.h> header file. */
+@%:@undef HAVE_MBSTR_H])
+m4trace:configure.ac:862: -1- AC_DEFINE_TRACE_LITERAL([HAVE_MBSTR_H])
+m4trace:configure.ac:862: -1- m4_pattern_allow([^HAVE_MBSTR_H$])
 m4trace:configure.ac:862: -2- AC_DEFINE_TRACE_LITERAL([HAVE_MBRLEN])
 m4trace:configure.ac:862: -2- m4_pattern_allow([^HAVE_MBRLEN$])
 m4trace:configure.ac:862: -2- AC_DEFINE_TRACE_LITERAL([HAVE_MBSCMP])
@@ -2178,7 +2182,7 @@ m4trace:configure.ac:943: -1- _m4_warn([obsolete], [The macro `AC_TRY_RUN' is ob
 You should run autoupdate.], [../../lib/autoconf/general.m4:2764: AC_TRY_RUN is expanded from...
 ../../lib/m4sugar/m4sh.m4:639: AS_IF is expanded from...
 ../../lib/autoconf/general.m4:2031: AC_CACHE_VAL is expanded from...
-aclocal.m4:1920: BASH_FUNC_CTYPE_NONASCII is expanded from...
+aclocal.m4:1922: BASH_FUNC_CTYPE_NONASCII is expanded from...
 configure.ac:943: the top level])
 m4trace:configure.ac:943: -1- AC_DEFINE_TRACE_LITERAL([CTYPE_NON_ASCII])
 m4trace:configure.ac:943: -1- m4_pattern_allow([^CTYPE_NON_ASCII$])
@@ -2400,7 +2404,7 @@ m4trace:configure.ac:981: -1- _m4_warn([obsolete], [The macro `AC_TRY_RUN' is ob
 You should run autoupdate.], [../../lib/autoconf/general.m4:2764: AC_TRY_RUN is expanded from...
 ../../lib/m4sugar/m4sh.m4:639: AS_IF is expanded from...
 ../../lib/autoconf/general.m4:2031: AC_CACHE_VAL is expanded from...
-aclocal.m4:4149: BASH_STRUCT_WEXITSTATUS_OFFSET is expanded from...
+aclocal.m4:4151: BASH_STRUCT_WEXITSTATUS_OFFSET is expanded from...
 configure.ac:981: the top level])
 m4trace:configure.ac:981: -1- AC_DEFINE_TRACE_LITERAL([WEXITSTATUS_OFFSET])
 m4trace:configure.ac:981: -1- m4_pattern_allow([^WEXITSTATUS_OFFSET$])
@@ -2528,7 +2532,7 @@ You should run autoupdate.], [../../lib/autoconf/general.m4:2764: AC_TRY_RUN is
 ../../lib/m4sugar/m4sh.m4:639: AS_IF is expanded from...
 ../../lib/autoconf/general.m4:2031: AC_CACHE_VAL is expanded from...
 ../../lib/autoconf/general.m4:2052: AC_CACHE_CHECK is expanded from...
-aclocal.m4:4065: BASH_FUNC_SNPRINTF is expanded from...
+aclocal.m4:4067: BASH_FUNC_SNPRINTF is expanded from...
 configure.ac:997: the top level])
 m4trace:configure.ac:997: -1- AC_DEFINE_TRACE_LITERAL([HAVE_SNPRINTF])
 m4trace:configure.ac:997: -1- m4_pattern_allow([^HAVE_SNPRINTF$])
@@ -2541,7 +2545,7 @@ You should run autoupdate.], [../../lib/autoconf/general.m4:2764: AC_TRY_RUN is
 ../../lib/m4sugar/m4sh.m4:639: AS_IF is expanded from...
 ../../lib/autoconf/general.m4:2031: AC_CACHE_VAL is expanded from...
 ../../lib/autoconf/general.m4:2052: AC_CACHE_CHECK is expanded from...
-aclocal.m4:4093: BASH_FUNC_VSNPRINTF is expanded from...
+aclocal.m4:4095: BASH_FUNC_VSNPRINTF is expanded from...
 configure.ac:998: the top level])
 m4trace:configure.ac:998: -1- AC_DEFINE_TRACE_LITERAL([HAVE_VSNPRINTF])
 m4trace:configure.ac:998: -1- m4_pattern_allow([^HAVE_VSNPRINTF$])
@@ -2625,7 +2629,7 @@ m4trace:configure.ac:1026: -1- _m4_warn([obsolete], [The macro `AC_TRY_RUN' is o
 You should run autoupdate.], [../../lib/autoconf/general.m4:2764: AC_TRY_RUN is expanded from...
 ../../lib/m4sugar/m4sh.m4:639: AS_IF is expanded from...
 ../../lib/autoconf/general.m4:2031: AC_CACHE_VAL is expanded from...
-aclocal.m4:1964: BASH_CHECK_WCONTINUED is expanded from...
+aclocal.m4:1966: BASH_CHECK_WCONTINUED is expanded from...
 configure.ac:1026: the top level])
 m4trace:configure.ac:1026: -1- AC_DEFINE_TRACE_LITERAL([WCONTINUED_BROKEN])
 m4trace:configure.ac:1026: -1- m4_pattern_allow([^WCONTINUED_BROKEN$])
index 7f200778ff8daa316dfb443ee16a8990b4a7dedd..f81103aff6cab6d14a599e57f20a71285923c75b 100644 (file)
 #include "shmbutil.h"
 #include "trap.h"
 
+#if defined (HAVE_MBSTR_H) && defined (HAVE_MBSCHR)
+#  include <mbstr.h>           /* mbschr */
+#endif
+
 #include "builtins/common.h"
 
 #include <readline/rlconf.h>
index d760cebe07b9ebedef84a61139cb162a6495afba..d720ee4b513bbfbfb456927e77bba9193ae83700 100644 (file)
@@ -33,7 +33,7 @@ A trailing space in VALUE causes the next word to be checked for
 alias substitution when the alias is expanded.
 
 Options:
-  -p   Print all defined aliases in a reusable format
+  -p   print all defined aliases in a reusable format
 
 Exit Status:
 alias returns true unless a NAME is supplied for which no alias has been
@@ -160,7 +160,7 @@ $SHORT_DOC unalias [-a] name [name ...]
 Remove each NAME from the list of defined aliases.
 
 Options:
-  -a   remove all alias definitions.
+  -a   remove all alias definitions
 
 Return success unless a NAME is not an existing alias.
 $END
index 2e27151522c1de5f5271a2fe8775abe9ca32f8d3..22c294098203a8d4128b4b73c21bb87936537356 100644 (file)
@@ -54,7 +54,7 @@ Options:
   -f  filename       Read key bindings from FILENAME.
   -x  keyseq:shell-command     Cause SHELL-COMMAND to be executed when
                                KEYSEQ is entered.
-  -X                List key sequences bound with -x and associated commands
+  -X                 List key sequences bound with -x and associated commands
                      in a form that can be reused as input.
 
 Exit Status:
index a440fca908a73d96fd0e0094fc8228fe44b61eae..a2ffd3330acced87b9b3301c7667587146d462da 100644 (file)
@@ -93,16 +93,17 @@ the word is assumed to be  a variable name.  If that variable has a value,
 its value is used for DIR.
 
 Options:
-    -L force symbolic links to be followed: resolve symbolic links in
-       DIR after processing instances of `..'
-    -P use the physical directory structure without following symbolic
-       links: resolve symbolic links in DIR before processing instances
-       of `..'
-    -e if the -P option is supplied, and the current working directory
-       cannot be determined successfully, exit with a non-zero status
+  -L   force symbolic links to be followed: resolve symbolic
+               links in DIR after processing instances of `..'
+  -P   use the physical directory structure without following
+               symbolic links: resolve symbolic links in DIR before
+               processing instances of `..'
+  -e   if the -P option is supplied, and the current working
+               directory cannot be determined successfully, exit with
+               a non-zero status
 #if defined (O_XATTR)
-    -@  on systems that support it, present a file with extended attributes
-        as a directory containing the file attributes
+  -@   on systems that support it, present a file with extended
+               attributes as a directory containing the file attributes
 #endif
 
 The default is to follow symbolic links, as if `-L' were specified.
@@ -450,7 +451,7 @@ Print the name of the current working directory.
 
 Options:
   -L   print the value of $PWD if it names the current working
-       directory
+               directory
   -P   print the physical directory, without any symbolic links
 
 By default, `pwd' behaves as if `-L' were specified.
index 310b35993a3777fccce4421cf66c647427337071..1f7e272e348ff37f3d18498431a9aca052430728 100644 (file)
@@ -30,10 +30,10 @@ information about the specified COMMANDs.  Can be used to invoke commands
 on disk when a function with the same name exists.
 
 Options:
-  -p   use a default value for PATH that is guaranteed to find all of
-       the standard utilities
-  -v   print a description of COMMAND similar to the `type' builtin
-  -V   print a more verbose description of each COMMAND
+  -p    use a default value for PATH that is guaranteed to find all of
+        the standard utilities
+  -v    print a description of COMMAND similar to the `type' builtin
+  -V    print a more verbose description of each COMMAND
 
 Exit Status:
 Returns exit status of COMMAND, or failure if COMMAND is not found.
index cfa14215ad71f594837e8d8ba7d6bd83e011cb6b..0cb809be41b27950bdf1d441931f4085cab4eb52 100644 (file)
@@ -242,7 +242,7 @@ sh_invalidnum (s)
 {
   char *msg;
 
-  if (*s == '0' && isdigit (s[1]))
+  if (*s == '0' && isdigit ((unsigned char)s[1]))
     msg = _("invalid octal number");
   else if (*s == '0' && s[1] == 'x')
     msg = _("invalid hex number");
diff --git a/builtins/common.c~ b/builtins/common.c~
new file mode 100644 (file)
index 0000000..cfa1421
--- /dev/null
@@ -0,0 +1,918 @@
+/* common.c - utility functions for all builtins */
+
+/* Copyright (C) 1987-2010 Free Software Foundation, Inc.
+
+   This file is part of GNU Bash, the Bourne Again SHell.
+
+   Bash is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   Bash is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with Bash.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#if defined (HAVE_UNISTD_H)
+#  ifdef _MINIX
+#    include <sys/types.h>
+#  endif
+#  include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include <chartypes.h>
+#include "../bashtypes.h"
+#include "posixstat.h"
+#include <signal.h>
+
+#include <errno.h>
+
+#if defined (PREFER_STDARG)
+#  include <stdarg.h>
+#else
+#  include <varargs.h>
+#endif
+
+#include "../bashansi.h"
+#include "../bashintl.h"
+
+#define NEED_FPURGE_DECL
+
+#include "../shell.h"
+#include "maxpath.h"
+#include "../flags.h"
+#include "../jobs.h"
+#include "../builtins.h"
+#include "../input.h"
+#include "../execute_cmd.h"
+#include "../trap.h"
+#include "bashgetopt.h"
+#include "common.h"
+#include "builtext.h"
+#include <tilde/tilde.h>
+
+#if defined (HISTORY)
+#  include "../bashhist.h"
+#endif
+
+#if !defined (errno)
+extern int errno;   
+#endif /* !errno */
+
+extern int indirection_level, subshell_environment;
+extern int line_number;
+extern int last_command_exit_value;
+extern int trap_saved_exit_value;
+extern int running_trap;
+extern int posixly_correct;
+extern char *this_command_name, *shell_name;
+extern const char * const bash_getcwd_errstr;
+
+/* Used by some builtins and the mainline code. */
+sh_builtin_func_t *last_shell_builtin = (sh_builtin_func_t *)NULL;
+sh_builtin_func_t *this_shell_builtin = (sh_builtin_func_t *)NULL;
+
+/* **************************************************************** */
+/*                                                                 */
+/*          Error reporting, usage, and option processing          */
+/*                                                                 */
+/* **************************************************************** */
+
+/* This is a lot like report_error (), but it is for shell builtins
+   instead of shell control structures, and it won't ever exit the
+   shell. */
+
+static void
+builtin_error_prolog ()
+{
+  char *name;
+
+  name = get_name_for_error ();
+  fprintf (stderr, "%s: ", name);
+
+  if (interactive_shell == 0)
+    fprintf (stderr, _("line %d: "), executing_line_number ());
+
+  if (this_command_name && *this_command_name)
+    fprintf (stderr, "%s: ", this_command_name);
+}
+
+void
+#if defined (PREFER_STDARG)
+builtin_error (const char *format, ...)
+#else
+builtin_error (format, va_alist)
+     const char *format;
+     va_dcl
+#endif
+{
+  va_list args;
+
+  builtin_error_prolog ();
+
+  SH_VA_START (args, format);
+
+  vfprintf (stderr, format, args);
+  va_end (args);
+  fprintf (stderr, "\n");
+}
+
+void
+#if defined (PREFER_STDARG)
+builtin_warning (const char *format, ...)
+#else
+builtin_warning (format, va_alist)
+     const char *format;
+     va_dcl
+#endif
+{
+  va_list args;
+
+  builtin_error_prolog ();
+  fprintf (stderr, _("warning: "));
+
+  SH_VA_START (args, format);
+
+  vfprintf (stderr, format, args);
+  va_end (args);
+  fprintf (stderr, "\n");
+}
+
+/* Print a usage summary for the currently-executing builtin command. */
+void
+builtin_usage ()
+{
+  if (this_command_name && *this_command_name)
+    fprintf (stderr, _("%s: usage: "), this_command_name);
+  fprintf (stderr, "%s\n", _(current_builtin->short_doc));
+  fflush (stderr);
+}
+
+/* Return if LIST is NULL else barf and jump to top_level.  Used by some
+   builtins that do not accept arguments. */
+void
+no_args (list)
+     WORD_LIST *list;
+{
+  if (list)
+    {
+      builtin_error (_("too many arguments"));
+      top_level_cleanup ();
+      jump_to_top_level (DISCARD);
+    }
+}
+
+/* Check that no options were given to the currently-executing builtin,
+   and return 0 if there were options. */
+int
+no_options (list)
+     WORD_LIST *list;
+{
+  int opt;
+
+  reset_internal_getopt ();
+  if ((opt = internal_getopt (list, "")) != -1)
+    {
+      if (opt == GETOPT_HELP)
+       {
+         builtin_help ();
+         return (2);
+       }
+      builtin_usage ();
+      return (1);
+    }
+  return (0);
+}
+
+void
+sh_needarg (s)
+     char *s;
+{
+  builtin_error (_("%s: option requires an argument"), s);
+}
+
+void
+sh_neednumarg (s)
+     char *s;
+{
+  builtin_error (_("%s: numeric argument required"), s);
+}
+
+void
+sh_notfound (s)
+     char *s;
+{
+  builtin_error (_("%s: not found"), s);
+}
+
+/* Function called when one of the builtin commands detects an invalid
+   option. */
+void
+sh_invalidopt (s)
+     char *s;
+{
+  builtin_error (_("%s: invalid option"), s);
+}
+
+void
+sh_invalidoptname (s)
+     char *s;
+{
+  builtin_error (_("%s: invalid option name"), s);
+}
+
+void
+sh_invalidid (s)
+     char *s;
+{
+  builtin_error (_("`%s': not a valid identifier"), s);
+}
+
+void
+sh_invalidnum (s)
+     char *s;
+{
+  char *msg;
+
+  if (*s == '0' && isdigit (s[1]))
+    msg = _("invalid octal number");
+  else if (*s == '0' && s[1] == 'x')
+    msg = _("invalid hex number");
+  else
+    msg = _("invalid number");
+  builtin_error ("%s: %s", s, msg);
+}
+
+void
+sh_invalidsig (s)
+     char *s;
+{
+  builtin_error (_("%s: invalid signal specification"), s);
+}
+
+void
+sh_badpid (s)
+     char *s;
+{
+  builtin_error (_("`%s': not a pid or valid job spec"), s);
+}
+
+void
+sh_readonly (s)
+     const char *s;
+{
+  builtin_error (_("%s: readonly variable"), s);
+}
+
+void
+sh_erange (s, desc)
+     char *s, *desc;
+{
+  if (s)
+    builtin_error (_("%s: %s out of range"), s, desc ? desc : _("argument"));
+  else
+    builtin_error (_("%s out of range"), desc ? desc : _("argument"));
+}
+
+#if defined (JOB_CONTROL)
+void
+sh_badjob (s)
+     char *s;
+{
+  builtin_error (_("%s: no such job"), s);
+}
+
+void
+sh_nojobs (s)
+     char *s;
+{
+  if (s)
+    builtin_error (_("%s: no job control"), s);
+  else
+    builtin_error (_("no job control"));
+}
+#endif
+
+#if defined (RESTRICTED_SHELL)
+void
+sh_restricted (s)
+     char *s;
+{
+  if (s)
+    builtin_error (_("%s: restricted"), s);
+  else
+    builtin_error (_("restricted"));
+}
+#endif
+
+void
+sh_notbuiltin (s)
+     char *s;
+{
+  builtin_error (_("%s: not a shell builtin"), s);
+}
+
+void
+sh_wrerror ()
+{
+#if defined (DONT_REPORT_BROKEN_PIPE_WRITE_ERRORS) && defined (EPIPE)
+  if (errno != EPIPE)
+#endif /* DONT_REPORT_BROKEN_PIPE_WRITE_ERRORS && EPIPE */
+  builtin_error (_("write error: %s"), strerror (errno));
+}
+
+void
+sh_ttyerror (set)
+     int set;
+{
+  if (set)
+    builtin_error (_("error setting terminal attributes: %s"), strerror (errno));
+  else
+    builtin_error (_("error getting terminal attributes: %s"), strerror (errno));
+}
+
+int
+sh_chkwrite (s)
+     int s;
+{
+  fflush (stdout);
+  if (ferror (stdout))
+    {
+      sh_wrerror ();
+      fpurge (stdout);
+      clearerr (stdout);
+      return (EXECUTION_FAILURE);
+    }
+  return (s);
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*          Shell positional parameter manipulation                */
+/*                                                                 */
+/* **************************************************************** */
+
+/* Convert a WORD_LIST into a C-style argv.  Return the number of elements
+   in the list in *IP, if IP is non-null.  A convenience function for
+   loadable builtins; also used by `test'. */
+char **
+make_builtin_argv (list, ip)
+     WORD_LIST *list;
+     int *ip;
+{
+  char **argv;
+
+  argv = strvec_from_word_list (list, 0, 1, ip);
+  argv[0] = this_command_name;
+  return argv;
+}
+
+/* Remember LIST in $1 ... $9, and REST_OF_ARGS.  If DESTRUCTIVE is
+   non-zero, then discard whatever the existing arguments are, else
+   only discard the ones that are to be replaced. */
+void
+remember_args (list, destructive)
+     WORD_LIST *list;
+     int destructive;
+{
+  register int i;
+
+  for (i = 1; i < 10; i++)
+    {
+      if ((destructive || list) && dollar_vars[i])
+       {
+         free (dollar_vars[i]);
+         dollar_vars[i] = (char *)NULL;
+       }
+
+      if (list)
+       {
+         dollar_vars[i] = savestring (list->word->word);
+         list = list->next;
+       }
+    }
+
+  /* If arguments remain, assign them to REST_OF_ARGS.
+     Note that copy_word_list (NULL) returns NULL, and
+     that dispose_words (NULL) does nothing. */
+  if (destructive || list)
+    {
+      dispose_words (rest_of_args);
+      rest_of_args = copy_word_list (list);
+    }
+
+  if (destructive)
+    set_dollar_vars_changed ();
+
+  invalidate_cached_quoted_dollar_at ();
+}
+
+static int changed_dollar_vars;
+
+/* Have the dollar variables been reset to new values since we last
+   checked? */
+int
+dollar_vars_changed ()
+{
+  return (changed_dollar_vars);
+}
+
+void
+set_dollar_vars_unchanged ()
+{
+  changed_dollar_vars = 0;
+}
+
+void
+set_dollar_vars_changed ()
+{
+  if (variable_context)
+    changed_dollar_vars |= ARGS_FUNC;
+  else if (this_shell_builtin == set_builtin)
+    changed_dollar_vars |= ARGS_SETBLTIN;
+  else
+    changed_dollar_vars |= ARGS_INVOC;
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*             Validating numeric input and arguments              */
+/*                                                                 */
+/* **************************************************************** */
+
+/* Read a numeric arg for this_command_name, the name of the shell builtin
+   that wants it.  LIST is the word list that the arg is to come from.
+   Accept only the numeric argument; report an error if other arguments
+   follow.  If FATAL is 1, call throw_to_top_level, which exits the
+   shell; if it's 2, call jump_to_top_level (DISCARD), which aborts the
+   current command; if FATAL is 0, return an indication of an invalid
+   number by setting *NUMOK == 0 and return -1. */
+int
+get_numeric_arg (list, fatal, count)
+     WORD_LIST *list;
+     int fatal;
+     intmax_t *count;
+{
+  char *arg;
+
+  if (count)
+    *count = 1;
+
+  if (list && list->word && ISOPTION (list->word->word, '-'))
+    list = list->next;
+
+  if (list)
+    {
+      arg = list->word->word;
+      if (arg == 0 || (legal_number (arg, count) == 0))
+       {
+         sh_neednumarg (list->word->word ? list->word->word : "`'");
+         if (fatal == 0)
+           return 0;
+         else if (fatal == 1)          /* fatal == 1; abort */
+           throw_to_top_level ();
+         else                          /* fatal == 2; discard current command */
+           {
+             top_level_cleanup ();
+             jump_to_top_level (DISCARD);
+           }
+       }
+      no_args (list->next);
+    }
+
+  return (1);
+}
+
+/* Get an eight-bit status value from LIST */
+int
+get_exitstat (list)
+     WORD_LIST *list;
+{
+  int status;
+  intmax_t sval;
+  char *arg;
+
+  if (list && list->word && ISOPTION (list->word->word, '-'))
+    list = list->next;
+
+  if (list == 0)
+    {
+      /* If we're not running the DEBUG trap, the return builtin, when not
+        given any arguments, uses the value of $? before the trap ran.  If
+        given an argument, return uses it.  This means that the trap can't
+        change $?.  The DEBUG trap gets to change $?, though, since that is
+        part of its reason for existing, and because the extended debug mode
+        does things with the return value. */
+      if (this_shell_builtin == return_builtin && running_trap > 0 && running_trap != DEBUG_TRAP+1)
+       return (trap_saved_exit_value);
+      return (last_command_exit_value);
+    }
+
+  arg = list->word->word;
+  if (arg == 0 || legal_number (arg, &sval) == 0)
+    {
+      sh_neednumarg (list->word->word ? list->word->word : "`'");
+      return EX_BADUSAGE;
+    }
+  no_args (list->next);
+
+  status = sval & 255;
+  return status;
+}
+
+/* Return the octal number parsed from STRING, or -1 to indicate
+   that the string contained a bad number. */
+int
+read_octal (string)
+     char *string;
+{
+  int result, digits;
+
+  result = digits = 0;
+  while (*string && ISOCTAL (*string))
+    {
+      digits++;
+      result = (result * 8) + (*string++ - '0');
+      if (result > 0777)
+       return -1;
+    }
+
+  if (digits == 0 || *string)
+    result = -1;
+
+  return (result);
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*          Manipulating the current working directory             */
+/*                                                                 */
+/* **************************************************************** */
+
+/* Return a consed string which is the current working directory.
+   FOR_WHOM is the name of the caller for error printing.  */
+char *the_current_working_directory = (char *)NULL;
+
+char *
+get_working_directory (for_whom)
+     char *for_whom;
+{
+  if (no_symbolic_links)
+    {
+      FREE (the_current_working_directory);
+      the_current_working_directory = (char *)NULL;
+    }
+
+  if (the_current_working_directory == 0)
+    {
+#if defined (GETCWD_BROKEN)
+      the_current_working_directory = getcwd (0, PATH_MAX);
+#else
+      the_current_working_directory = getcwd (0, 0);
+#endif
+      if (the_current_working_directory == 0)
+       {
+         fprintf (stderr, _("%s: error retrieving current directory: %s: %s\n"),
+                  (for_whom && *for_whom) ? for_whom : get_name_for_error (),
+                  _(bash_getcwd_errstr), strerror (errno));
+         return (char *)NULL;
+       }
+    }
+
+  return (savestring (the_current_working_directory));
+}
+
+/* Make NAME our internal idea of the current working directory. */
+void
+set_working_directory (name)
+     char *name;
+{
+  FREE (the_current_working_directory);
+  the_current_working_directory = savestring (name);
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*             Job control support functions                       */
+/*                                                                 */
+/* **************************************************************** */
+
+#if defined (JOB_CONTROL)
+int
+get_job_by_name (name, flags)
+     const char *name;
+     int flags;
+{
+  register int i, wl, cl, match, job;
+  register PROCESS *p;
+  register JOB *j;
+
+  job = NO_JOB;
+  wl = strlen (name);
+  for (i = js.j_jobslots - 1; i >= 0; i--)
+    {
+      j = get_job_by_jid (i);
+      if (j == 0 || ((flags & JM_STOPPED) && J_JOBSTATE(j) != JSTOPPED))
+        continue;
+
+      p = j->pipe;
+      do
+        {
+         if (flags & JM_EXACT)
+           {
+             cl = strlen (p->command);
+             match = STREQN (p->command, name, cl);
+           }
+         else if (flags & JM_SUBSTRING)
+           match = strcasestr (p->command, name) != (char *)0;
+         else
+           match = STREQN (p->command, name, wl);
+
+         if (match == 0)
+           {
+             p = p->next;
+             continue;
+           }
+         else if (flags & JM_FIRSTMATCH)
+           return i;           /* return first match */
+         else if (job != NO_JOB)
+           {
+             if (this_shell_builtin)
+               builtin_error (_("%s: ambiguous job spec"), name);
+             else
+               internal_error (_("%s: ambiguous job spec"), name);
+             return (DUP_JOB);
+           }
+         else
+           job = i;
+        }
+      while (p != j->pipe);
+    }
+
+  return (job);
+}
+
+/* Return the job spec found in LIST. */
+int
+get_job_spec (list)
+     WORD_LIST *list;
+{
+  register char *word;
+  int job, jflags;
+
+  if (list == 0)
+    return (js.j_current);
+
+  word = list->word->word;
+
+  if (*word == '\0')
+    return (NO_JOB);
+
+  if (*word == '%')
+    word++;
+
+  if (DIGIT (*word) && all_digits (word))
+    {
+      job = atoi (word);
+      return (job > js.j_jobslots ? NO_JOB : job - 1);
+    }
+
+  jflags = 0;
+  switch (*word)
+    {
+    case 0:
+    case '%':
+    case '+':
+      return (js.j_current);
+
+    case '-':
+      return (js.j_previous);
+
+    case '?':                  /* Substring search requested. */
+      jflags |= JM_SUBSTRING;
+      word++;
+      /* FALLTHROUGH */
+
+    default:
+      return get_job_by_name (word, jflags);
+    }
+}
+#endif /* JOB_CONTROL */
+
+/*
+ * NOTE:  `kill' calls this function with forcecols == 0
+ */
+int
+display_signal_list (list, forcecols)
+     WORD_LIST *list;
+     int forcecols;
+{
+  register int i, column;
+  char *name;
+  int result, signum, dflags;
+  intmax_t lsignum;
+
+  result = EXECUTION_SUCCESS;
+  if (!list)
+    {
+      for (i = 1, column = 0; i < NSIG; i++)
+       {
+         name = signal_name (i);
+         if (STREQN (name, "SIGJUNK", 7) || STREQN (name, "Unknown", 7))
+           continue;
+
+         if (posixly_correct && !forcecols)
+           {
+             /* This is for the kill builtin.  POSIX.2 says the signal names
+                are displayed without the `SIG' prefix. */
+             if (STREQN (name, "SIG", 3))
+               name += 3;
+             printf ("%s%s", name, (i == NSIG - 1) ? "" : " ");
+           }
+         else
+           {
+             printf ("%2d) %s", i, name);
+
+             if (++column < 5)
+               printf ("\t");
+             else
+               {
+                 printf ("\n");
+                 column = 0;
+               }
+           }
+       }
+
+      if ((posixly_correct && !forcecols) || column != 0)
+       printf ("\n");
+      return result;
+    }
+
+  /* List individual signal names or numbers. */
+  while (list)
+    {
+      if (legal_number (list->word->word, &lsignum))
+       {
+         /* This is specified by Posix.2 so that exit statuses can be
+            mapped into signal numbers. */
+         if (lsignum > 128)
+           lsignum -= 128;
+         if (lsignum < 0 || lsignum >= NSIG)
+           {
+             sh_invalidsig (list->word->word);
+             result = EXECUTION_FAILURE;
+             list = list->next;
+             continue;
+           }
+
+         signum = lsignum;
+         name = signal_name (signum);
+         if (STREQN (name, "SIGJUNK", 7) || STREQN (name, "Unknown", 7))
+           {
+             list = list->next;
+             continue;
+           }
+#if defined (JOB_CONTROL)
+         /* POSIX.2 says that `kill -l signum' prints the signal name without
+            the `SIG' prefix. */
+         printf ("%s\n", (this_shell_builtin == kill_builtin) ? name + 3 : name);
+#else
+         printf ("%s\n", name);
+#endif
+       }
+      else
+       {
+         dflags = DSIG_NOCASE;
+         if (posixly_correct == 0 || this_shell_builtin != kill_builtin)
+           dflags |= DSIG_SIGPREFIX;
+         signum = decode_signal (list->word->word, dflags);
+         if (signum == NO_SIG)
+           {
+             sh_invalidsig (list->word->word);
+             result = EXECUTION_FAILURE;
+             list = list->next;
+             continue;
+           }
+         printf ("%d\n", signum);
+       }
+      list = list->next;
+    }
+  return (result);
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*         Finding builtin commands and their functions            */
+/*                                                                 */
+/* **************************************************************** */
+
+/* Perform a binary search and return the address of the builtin function
+   whose name is NAME.  If the function couldn't be found, or the builtin
+   is disabled or has no function associated with it, return NULL.
+   Return the address of the builtin.
+   DISABLED_OKAY means find it even if the builtin is disabled. */
+struct builtin *
+builtin_address_internal (name, disabled_okay)
+     char *name;
+     int disabled_okay;
+{
+  int hi, lo, mid, j;
+
+  hi = num_shell_builtins - 1;
+  lo = 0;
+
+  while (lo <= hi)
+    {
+      mid = (lo + hi) / 2;
+
+      j = shell_builtins[mid].name[0] - name[0];
+
+      if (j == 0)
+       j = strcmp (shell_builtins[mid].name, name);
+
+      if (j == 0)
+       {
+         /* It must have a function pointer.  It must be enabled, or we
+            must have explicitly allowed disabled functions to be found,
+            and it must not have been deleted. */
+         if (shell_builtins[mid].function &&
+             ((shell_builtins[mid].flags & BUILTIN_DELETED) == 0) &&
+             ((shell_builtins[mid].flags & BUILTIN_ENABLED) || disabled_okay))
+           return (&shell_builtins[mid]);
+         else
+           return ((struct builtin *)NULL);
+       }
+      if (j > 0)
+       hi = mid - 1;
+      else
+       lo = mid + 1;
+    }
+  return ((struct builtin *)NULL);
+}
+
+/* Return the pointer to the function implementing builtin command NAME. */
+sh_builtin_func_t *
+find_shell_builtin (name)
+     char *name;
+{
+  current_builtin = builtin_address_internal (name, 0);
+  return (current_builtin ? current_builtin->function : (sh_builtin_func_t *)NULL);
+}
+
+/* Return the address of builtin with NAME, whether it is enabled or not. */
+sh_builtin_func_t *
+builtin_address (name)
+     char *name;
+{
+  current_builtin = builtin_address_internal (name, 1);
+  return (current_builtin ? current_builtin->function : (sh_builtin_func_t *)NULL);
+}
+
+/* Return the function implementing the builtin NAME, but only if it is a
+   POSIX.2 special builtin. */
+sh_builtin_func_t *
+find_special_builtin (name)
+     char *name;
+{
+  current_builtin = builtin_address_internal (name, 0);
+  return ((current_builtin && (current_builtin->flags & SPECIAL_BUILTIN)) ?
+                       current_builtin->function :
+                       (sh_builtin_func_t *)NULL);
+}
+  
+static int
+shell_builtin_compare (sbp1, sbp2)
+     struct builtin *sbp1, *sbp2;
+{
+  int result;
+
+  if ((result = sbp1->name[0] - sbp2->name[0]) == 0)
+    result = strcmp (sbp1->name, sbp2->name);
+
+  return (result);
+}
+
+/* Sort the table of shell builtins so that the binary search will work
+   in find_shell_builtin. */
+void
+initialize_shell_builtins ()
+{
+  qsort (shell_builtins, num_shell_builtins, sizeof (struct builtin),
+    (QSFUNC *)shell_builtin_compare);
+}
+
+#if !defined (HELP_BUILTIN)
+void
+builtin_help ()
+{
+  printf ("%s: %s\n", this_command_name, _("help not available in this version"));
+}
+#endif
index 4d18c1386ed8d2f603c111ff210c1f70cc8f5328..57d45f1bc8caef5553264cc087eff72759ecbbe1 100644 (file)
@@ -33,11 +33,11 @@ allows them to be reused as input.
 Options:
   -p   print existing completion specifications in a reusable format
   -r   remove a completion specification for each NAME, or, if no
-       NAMEs are supplied, all completion specifications
+               NAMEs are supplied, all completion specifications
   -D   apply the completions and actions as the default for commands
-       without any specific completion defined
+               without any specific completion defined
   -E   apply the completions and actions to "empty" commands --
-       completion attempted on a blank line
+               completion attempted on a blank line
 
 When completion is attempted, the actions are applied in the order the
 uppercase-letter options are listed above.  The -D option takes
index fbc03405995cfefbbd3f4318ab2db83dc647dce6..bdff891d8ec1c8366fa7b2c9120d897529d280df 100644 (file)
@@ -31,9 +31,9 @@ display the attributes and values of all variables.
 Options:
   -f   restrict action or display to function names and definitions
   -F   restrict display to function names only (plus line number and
-       source file when debugging)
+               source file when debugging)
   -g   create global variables when used in a shell function; otherwise
-       ignored
+               ignored
   -p   display the attributes and value of each NAME
 
 Options which set attributes:
index f4738264ce5e35b9f1032923a909903f7915dcac..d001b607f0c921255ca3981678f0c5f50df0a56f 100644 (file)
@@ -59,9 +59,9 @@ Options:
   \v   vertical tab
   \\   backslash
   \0nnn        the character whose ASCII code is NNN (octal).  NNN can be
-       0 to 3 octal digits
+               0 to 3 octal digits
   \xHH the eight-bit character whose value is HH (hexadecimal).  HH
-       can be one or two hex digits
+               can be one or two hex digits
 
 Exit Status:
 Returns success unless a write error occurs.
index 9cf76d53089a94ea34a063728e5bebb7ca190740..40dbd3ac8b7903692c582f2f1c23e386dfabfdb7 100644 (file)
@@ -31,8 +31,8 @@ any redirections take effect in the current shell.
 
 Options:
   -a name      pass NAME as the zeroth argument to COMMAND
-  -c           execute COMMAND with an empty environment
-  -l           place a dash in the zeroth argument to COMMAND
+  -c   execute COMMAND with an empty environment
+  -l   place a dash in the zeroth argument to COMMAND
 
 If the command cannot be executed, a non-interactive shell exits, unless
 the shell option `execfail' is set.
index a74bbfae0ec54935addf0801dcba1b82fa663bd0..092208b23b3f7eba4694eafb2edcbff716bdf159 100644 (file)
@@ -29,15 +29,15 @@ Determine and remember the full pathname of each command NAME.  If
 no arguments are given, information about remembered commands is displayed.
 
 Options:
-  -d           forget the remembered location of each NAME
-  -l           display in a format that may be reused as input
+  -d   forget the remembered location of each NAME
+  -l   display in a format that may be reused as input
   -p pathname  use PATHNAME as the full pathname of NAME
-  -r           forget all remembered locations
-  -t           print the remembered location of each NAME, preceding
+  -r   forget all remembered locations
+  -t   print the remembered location of each NAME, preceding
                each location with the corresponding NAME if multiple
                NAMEs are given
 Arguments:
-  NAME         Each NAME is searched for in $PATH and added to the list
+  NAME Each NAME is searched for in $PATH and added to the list
                of remembered commands.
 
 Exit Status:
index ef20c7b44b861c36c0bfd067723d1ca3ed0ac58b..4ced25cf0f67e6f2701460ba95054ccfd54a34c1 100644 (file)
@@ -34,7 +34,7 @@ Options:
   -d   output short description for each topic
   -m   display usage in pseudo-manpage format
   -s   output only a short usage synopsis for each topic matching
-       PATTERN
+               PATTERN
 
 Arguments:
   PATTERN      Pattern specifiying a help topic
index 1ac8d3ef978896273169ca6307437ba1ba48cbb7..b16251f3189e2e960b4b388924feb682e25493e9 100644 (file)
@@ -36,18 +36,18 @@ Options:
   -a   append history lines from this session to the history file
   -n   read all history lines not already read from the history file
   -r   read the history file and append the contents to the history
-       list
+               list
   -w   write the current history to the history file
-       and append them to the history list
+               and append them to the history list
 
   -p   perform history expansion on each ARG and display the result
-       without storing it in the history list
+               without storing it in the history list
   -s   append the ARGs to the history list as a single entry
 
 If FILENAME is given, it is used as the history file.  Otherwise,
-if $HISTFILE has a value, that is used, else ~/.bash_history.
+if HISTFILE has a value, that is used, else ~/.bash_history.
 
-If the $HISTTIMEFORMAT variable is set and not null, its value is used
+If the HISTTIMEFORMAT variable is set and not null, its value is used
 as a format string for strftime(3) to print the time stamp associated
 with each displayed history entry.  No time stamps are printed otherwise.
 
index 47da58e495a0dcc7b6f13a3371b4f4e42f9c51ca..e47496322bde2d3408d0c27b3ed9c25b864164b5 100644 (file)
@@ -32,7 +32,7 @@ Without options, the status of all active jobs is displayed.
 Options:
   -l   lists process IDs in addition to the normal information
   -n   lists only processes that have changed status since the last
-       notification
+               notification
   -p   lists process IDs only
   -r   restrict output to running jobs
   -s   restrict output to stopped jobs
@@ -221,7 +221,7 @@ any JOBSPECs, the shell uses its notion of the current job.
 Options:
   -a   remove all jobs if JOBSPEC is not supplied
   -h   mark each JOBSPEC so that SIGHUP is not sent to the job if the
-       shell receives a SIGHUP
+               shell receives a SIGHUP
   -r   remove only running jobs
 
 Exit Status:
index fdcffc6189544f0502578694c1e727c3304273ed..d56fed69a2e6257c8325da94d943d4c4db42794f 100644 (file)
@@ -33,7 +33,7 @@ Options:
   -s sig       SIG is a signal name
   -n sig       SIG is a signal number
   -l   list the signal names; if arguments follow `-l' they are
-       assumed to be signal numbers for which names should be listed
+               assumed to be signal numbers for which names should be listed
 
 Kill is a shell builtin for two reasons: it allows job IDs to be used
 instead of process IDs, and allows processes to be killed if the limit
index 51e37c63581d7cefd7604ec7ed803bf9ee973010..6eae23f8d074f4c3577c1dedf18248b8a6330c44 100644 (file)
@@ -31,16 +31,17 @@ from file descriptor FD if the -u option is supplied.  The variable MAPFILE
 is the default ARRAY.
 
 Options:
-  -n count     Copy at most COUNT lines.  If COUNT is 0, all lines are copied.
-  -O origin    Begin assigning to ARRAY at index ORIGIN.  The default index is 0.
-  -s count     Discard the first COUNT lines read.
-  -t           Remove a trailing newline from each line read.
-  -u fd                Read lines from file descriptor FD instead of the standard input.
-  -C callback  Evaluate CALLBACK each time QUANTUM lines are read.
-  -c quantum   Specify the number of lines read between each call to CALLBACK.
+  -n count     Copy at most COUNT lines.  If COUNT is 0, all lines are copied
+  -O origin    Begin assigning to ARRAY at index ORIGIN.  The default index is 0
+  -s count     Discard the first COUNT lines read
+  -t   Remove a trailing newline from each line read
+  -u fd        Read lines from file descriptor FD instead of the standard input
+  -C callback  Evaluate CALLBACK each time QUANTUM lines are read
+  -c quantum   Specify the number of lines read between each call to
+                       CALLBACK
 
 Arguments:
-  ARRAY                Array variable name to use for file data.
+  ARRAY        Array variable name to use for file data
 
 If -C is supplied without -c, the default quantum is 5000.  When
 CALLBACK is evaluated, it is supplied the index of the next array
index a3899bef54f222e1eedcb291571790aaab5cc292..b01958ec88bfb81305bcb4d91e5a247ba6cdd96d 100644 (file)
@@ -40,8 +40,8 @@ printf interprets:
 
   %b   expand backslash escape sequences in the corresponding argument
   %q   quote the argument in a way that can be reused as shell input
-  %(fmt)T output the date-time string resulting from using FMT as a format
-        string for strftime(3)
+  %(fmt)T      output the date-time string resulting from using FMT as a format
+               string for strftime(3)
 
 The format is re-used as necessary to consume all of the arguments.  If
 there are fewer arguments than the format requires,  extra format
index 1d435fa527c6a23ddaa5b9c026ddfc1ab1cff6a7..da8e182063e53ea998c580d9cdf6f2d5a0fe055c 100644 (file)
@@ -32,19 +32,19 @@ directory.  With no arguments, exchanges the top two directories.
 
 Options:
   -n   Suppresses the normal change of directory when adding
-       directories to the stack, so only the stack is manipulated.
+               directories to the stack, so only the stack is manipulated.
 
 Arguments:
   +N   Rotates the stack so that the Nth directory (counting
-       from the left of the list shown by `dirs', starting with
-       zero) is at the top.
+               from the left of the list shown by `dirs', starting with
+               zero) is at the top.
 
   -N   Rotates the stack so that the Nth directory (counting
-       from the right of the list shown by `dirs', starting with
-       zero) is at the top.
+               from the right of the list shown by `dirs', starting with
+               zero) is at the top.
 
   dir  Adds DIR to the directory stack at the top, making it the
-       new current working directory.
+               new current working directory.
 
 The `dirs' builtin displays the directory stack.
 
@@ -64,16 +64,16 @@ the top directory from the stack, and changes to the new top directory.
 
 Options:
   -n   Suppresses the normal change of directory when removing
-       directories from the stack, so only the stack is manipulated.
+               directories from the stack, so only the stack is manipulated.
 
 Arguments:
   +N   Removes the Nth entry counting from the left of the list
-       shown by `dirs', starting with zero.  For example: `popd +0'
-       removes the first directory, `popd +1' the second.
+               shown by `dirs', starting with zero.  For example: `popd +0'
+               removes the first directory, `popd +1' the second.
 
   -N   Removes the Nth entry counting from the right of the list
-       shown by `dirs', starting with zero.  For example: `popd -0'
-       removes the last directory, `popd -1' the next to last.
+               shown by `dirs', starting with zero.  For example: `popd -0'
+               removes the last directory, `popd -1' the next to last.
 
 The `dirs' builtin displays the directory stack.
 
@@ -95,17 +95,19 @@ back up through the list with the `popd' command.
 Options:
   -c   clear the directory stack by deleting all of the elements
   -l   do not print tilde-prefixed versions of directories relative
-       to your home directory
+               to your home directory
   -p   print the directory stack with one entry per line
   -v   print the directory stack with one entry per line prefixed
-       with its position in the stack
+               with its position in the stack
 
 Arguments:
-  +N   Displays the Nth entry counting from the left of the list shown by
-       dirs when invoked without options, starting with zero.
+  +N   Displays the Nth entry counting from the left of the list
+               shown by dirs when invoked without options, starting with
+               zero.
 
-  -N   Displays the Nth entry counting from the right of the list shown by
-       dirs when invoked without options, starting with zero.
+  -N   Displays the Nth entry counting from the right of the list
+               shown by dirs when invoked without options, starting with
+               zero.
 
 Exit Status:
 Returns success unless an invalid option is supplied or an error occurs.
index 83bcddf44a9adfd3854d3dca274f6f179ca15ae9..a878387c7ec3640b557f5096b4d27ed021e1c75b 100644 (file)
@@ -39,25 +39,27 @@ Options:
                variable ARRAY, starting at zero
   -d delim     continue until the first character of DELIM is read, rather
                than newline
-  -e           use Readline to obtain the line in an interactive shell
-  -i text      Use TEXT as the initial text for Readline
+  -e   use Readline to obtain the line in an interactive shell
+  -i text      use TEXT as the initial text for Readline
   -n nchars    return after reading NCHARS characters rather than waiting
-               for a newline, but honor a delimiter if fewer than NCHARS
-               characters are read before the delimiter
+               for a newline, but honor a delimiter if fewer than
+               NCHARS characters are read before the delimiter
   -N nchars    return only after reading exactly NCHARS characters, unless
-               EOF is encountered or read times out, ignoring any delimiter
+               EOF is encountered or read times out, ignoring any
+               delimiter
   -p prompt    output the string PROMPT without a trailing newline before
                attempting to read
-  -r           do not allow backslashes to escape any characters
-  -s           do not echo input coming from a terminal
-  -t timeout   time out and return failure if a complete line of input is
-               not read within TIMEOUT seconds.  The value of the TMOUT
-               variable is the default timeout.  TIMEOUT may be a
-               fractional number.  If TIMEOUT is 0, read returns immediately,
-               without trying to read any data, returning success only if
-               input is available on the specified file descriptor.  The
-               exit status is greater than 128 if the timeout is exceeded
-  -u fd                read from file descriptor FD instead of the standard input
+  -r   do not allow backslashes to escape any characters
+  -s   do not echo input coming from a terminal
+  -t timeout   time out and return failure if a complete line of
+               input is not read within TIMEOUT seconds.  The value of the
+               TMOUT variable is the default timeout.  TIMEOUT may be a
+               fractional number.  If TIMEOUT is 0, read returns
+               immediately, without trying to read any data, returning
+               success only if input is available on the specified
+               file descriptor.  The exit status is greater than 128
+               if the timeout is exceeded
+  -u fd        read from file descriptor FD instead of the standard input
 
 Exit Status:
 The return code is zero, unless end-of-file is encountered, read times out
index aef82e1a62719f643f1a220c4fcd63d82d634007..c2934f5415ff4e9048417c5bdce9f21f066b6ed2 100644 (file)
@@ -771,7 +771,7 @@ Options:
   -f   treat each NAME as a shell function
   -v   treat each NAME as a shell variable
   -n   treat each NAME as a name reference and unset the variable itself
-       rather than the variable it references
+               rather than the variable it references
 
 Without options, unset first tries to unset a variable, and if that fails,
 tries to unset a function.
index c5fe32ee92ff6d566d47f3fd028c4ad54998a95b..178a0b336b6db81dfd0b58d61050d76c371ab095 100644 (file)
@@ -94,8 +94,8 @@ Options:
   -a   refer to indexed array variables
   -A   refer to associative array variables
   -f   refer to shell functions
-  -p   display a list of all readonly variables or functions, depending on
-        whether or not the -f option is given
+  -p   display a list of all readonly variables or functions,
+               depending on whether or not the -f option is given
 
 An argument of `--' disables further option processing.
 
index f1444000ea2acf5b37701d3b66ba3133a438dd23..249b2c2a2f42a7db43bccf565770582bdb19ef4c 100644 (file)
@@ -83,8 +83,9 @@ String operators:
 Other operators:
 
   -o OPTION      True if the shell option OPTION is enabled.
-  -v VAR        True if the shell variable VAR is set
-  -R VAR        True if the shell variable VAR is set and is a name reference.
+  -v VAR         True if the shell variable VAR is set.
+  -R VAR         True if the shell variable VAR is set and is a name
+                 reference.
   ! EXPR         True if expr is false.
   EXPR1 -a EXPR2 True if both expr1 AND expr2 are true.
   EXPR1 -o EXPR2 True if either expr1 OR expr2 is true.
index 6cfc72c48a70b8b8317f4e9146178a21fd9547b5..67191b73f6cd5b7dcda2024676fbb3b797781199 100644 (file)
@@ -30,18 +30,18 @@ command name.
 
 Options:
   -a   display all locations containing an executable named NAME;
-       includes aliases, builtins, and functions, if and only if
-       the `-p' option is not also used
+               includes aliases, builtins, and functions, if and only if
+               the `-p' option is not also used
   -f   suppress shell function lookup
   -P   force a PATH search for each NAME, even if it is an alias,
-       builtin, or function, and returns the name of the disk file
-       that would be executed
+               builtin, or function, and returns the name of the disk file
+               that would be executed
   -p   returns either the name of the disk file that would be executed,
-       or nothing if `type -t NAME' would not return `file'.
+               or nothing if `type -t NAME' would not return `file'
   -t   output a single word which is one of `alias', `keyword',
-       `function', `builtin', `file' or `', if NAME is an alias, shell
-       reserved word, shell function, shell builtin, disk file, or not 
-       found, respectively
+               `function', `builtin', `file' or `', if NAME is an alias,
+               shell reserved word, shell function, shell builtin, disk file,
+               or not found, respectively
 
 Arguments:
   NAME Command name to be interpreted.
index 08662ad48e2854e352d78e0ad52eede933771a68..263ddd09eebe5dad76a6b79de11ea644a903dc9a 100644 (file)
@@ -52,7 +52,7 @@ Options:
   -v   the size of virtual memory
   -x   the maximum number of file locks
   -P   the maximum number of pseudoterminals
-  -T    the maximum number of threads
+  -T   the maximum number of threads
 
 Not all options are available on all platforms.
 
index d9aa041836f64a608cb71f1e0d08f5338bd2e0a4..76f2bf2279cf23a1ea2bc70011ad5a9810e8df6e 100644 (file)
@@ -145,8 +145,12 @@ umask_builtin (list)
 /* Print the umask in a symbolic form.  In the output, a letter is
    printed if the corresponding bit is clear in the umask. */
 static void
+#if defined (__STDC__)
+print_symbolic_umask (mode_t um)
+#else
 print_symbolic_umask (um)
      mode_t um;
+#endif
 {
   char ubits[4], gbits[4], obits[4];           /* u=rwx,g=rwx,o=rwx */
   int i;
diff --git a/builtins/umask.def~ b/builtins/umask.def~
new file mode 100644 (file)
index 0000000..d9aa041
--- /dev/null
@@ -0,0 +1,312 @@
+This file is umask.def, from which is created umask.c.
+It implements the builtin "umask" in Bash.
+
+Copyright (C) 1987-2009 Free Software Foundation, Inc.
+
+This file is part of GNU Bash, the Bourne Again SHell.
+
+Bash is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Bash is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Bash.  If not, see <http://www.gnu.org/licenses/>.
+
+$PRODUCES umask.c
+
+$BUILTIN umask
+$FUNCTION umask_builtin
+$SHORT_DOC umask [-p] [-S] [mode]
+Display or set file mode mask.
+
+Sets the user file-creation mask to MODE.  If MODE is omitted, prints
+the current value of the mask.
+
+If MODE begins with a digit, it is interpreted as an octal number;
+otherwise it is a symbolic mode string like that accepted by chmod(1).
+
+Options:
+  -p   if MODE is omitted, output in a form that may be reused as input
+  -S   makes the output symbolic; otherwise an octal number is output
+
+Exit Status:
+Returns success unless MODE is invalid or an invalid option is given.
+$END
+
+#include <config.h>
+
+#include "../bashtypes.h"
+#include "filecntl.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 "../bashintl.h"
+
+#include "../shell.h"
+#include "posixstat.h"
+#include "common.h"
+#include "bashgetopt.h"
+
+/* **************************************************************** */
+/*                                                                  */
+/*                     UMASK Builtin and Helpers                    */
+/*                                                                  */
+/* **************************************************************** */
+
+static void print_symbolic_umask __P((mode_t));
+static int symbolic_umask __P((WORD_LIST *));
+
+/* Set or display the mask used by the system when creating files.  Flag
+   of -S means display the umask in a symbolic mode. */
+int
+umask_builtin (list)
+     WORD_LIST *list;
+{
+  int print_symbolically, opt, umask_value, pflag;
+  mode_t umask_arg;
+
+  print_symbolically = pflag = 0;
+  reset_internal_getopt ();
+  while ((opt = internal_getopt (list, "Sp")) != -1)
+    {
+      switch (opt)
+       {
+       case 'S':
+         print_symbolically++;
+         break;
+       case 'p':
+         pflag++;
+         break;
+       default:
+         builtin_usage ();
+         return (EX_USAGE);
+       }
+    }
+
+  list = loptend;
+
+  if (list)
+    {
+      if (DIGIT (*list->word->word))
+       {
+         umask_value = read_octal (list->word->word);
+
+         /* Note that other shells just let you set the umask to zero
+            by specifying a number out of range.  This is a problem
+            with those shells.  We don't change the umask if the input
+            is lousy. */
+         if (umask_value == -1)
+           {
+             sh_erange (list->word->word, _("octal number"));
+             return (EXECUTION_FAILURE);
+           }
+       }
+      else
+       {
+         umask_value = symbolic_umask (list);
+         if (umask_value == -1)
+           return (EXECUTION_FAILURE);
+       }
+      umask_arg = (mode_t)umask_value;
+      umask (umask_arg);
+      if (print_symbolically)
+       print_symbolic_umask (umask_arg);
+    }
+  else                         /* Display the UMASK for this user. */
+    {
+      umask_arg = umask (022);
+      umask (umask_arg);
+
+      if (pflag)
+       printf ("umask%s ", (print_symbolically ? " -S" : ""));
+      if (print_symbolically)
+       print_symbolic_umask (umask_arg);
+      else
+       printf ("%04lo\n", (unsigned long)umask_arg);
+    }
+
+  return (sh_chkwrite (EXECUTION_SUCCESS));
+}
+
+/* Print the umask in a symbolic form.  In the output, a letter is
+   printed if the corresponding bit is clear in the umask. */
+static void
+print_symbolic_umask (um)
+     mode_t um;
+{
+  char ubits[4], gbits[4], obits[4];           /* u=rwx,g=rwx,o=rwx */
+  int i;
+
+  i = 0;
+  if ((um & S_IRUSR) == 0)
+    ubits[i++] = 'r';
+  if ((um & S_IWUSR) == 0)
+    ubits[i++] = 'w';
+  if ((um & S_IXUSR) == 0)
+    ubits[i++] = 'x';
+  ubits[i] = '\0';
+
+  i = 0;
+  if ((um & S_IRGRP) == 0)
+    gbits[i++] = 'r';
+  if ((um & S_IWGRP) == 0)
+    gbits[i++] = 'w';
+  if ((um & S_IXGRP) == 0)
+    gbits[i++] = 'x';
+  gbits[i] = '\0';
+
+  i = 0;
+  if ((um & S_IROTH) == 0)
+    obits[i++] = 'r';
+  if ((um & S_IWOTH) == 0)
+    obits[i++] = 'w';
+  if ((um & S_IXOTH) == 0)
+    obits[i++] = 'x';
+  obits[i] = '\0';
+
+  printf ("u=%s,g=%s,o=%s\n", ubits, gbits, obits);
+}
+
+int
+parse_symbolic_mode (mode, initial_bits)
+     char *mode;
+     int initial_bits;
+{
+  int who, op, perm, bits, c;
+  char *s;
+
+  for (s = mode, bits = initial_bits;;)
+    {
+      who = op = perm = 0;
+
+      /* Parse the `who' portion of the symbolic mode clause. */
+      while (member (*s, "agou"))
+       {
+         switch (c = *s++)
+           {
+           case 'u':
+             who |= S_IRWXU;
+             continue;
+           case 'g':
+             who |= S_IRWXG;
+             continue;
+           case 'o':
+             who |= S_IRWXO;
+             continue;
+           case 'a':
+             who |= S_IRWXU | S_IRWXG | S_IRWXO;
+             continue;
+           default:
+             break;
+           }
+       }
+
+      /* The operation is now sitting in *s. */
+      op = *s++;
+      switch (op)
+       {
+       case '+':
+       case '-':
+       case '=':
+         break;
+       default:
+         builtin_error (_("`%c': invalid symbolic mode operator"), op);
+         return (-1);
+       }
+
+      /* Parse out the `perm' section of the symbolic mode clause. */
+      while (member (*s, "rwx"))
+       {
+         c = *s++;
+
+         switch (c)
+           {
+           case 'r':
+             perm |= S_IRUGO;
+             break;
+           case 'w':
+             perm |= S_IWUGO;
+             break;
+           case 'x':
+             perm |= S_IXUGO;
+             break;
+           }
+       }
+
+      /* Now perform the operation or return an error for a
+        bad permission string. */
+      if (!*s || *s == ',')
+       {
+         if (who)
+           perm &= who;
+
+         switch (op)
+           {
+           case '+':
+             bits |= perm;
+             break;
+           case '-':
+             bits &= ~perm;
+             break;
+           case '=':
+             if (who == 0)
+               who = S_IRWXU | S_IRWXG | S_IRWXO;
+             bits &= ~who;
+             bits |= perm;
+             break;
+
+           /* No other values are possible. */
+           }
+
+         if (*s == '\0')
+           break;
+         else
+           s++;        /* skip past ',' */
+       }
+      else
+       {
+         builtin_error (_("`%c': invalid symbolic mode character"), *s);
+         return (-1);
+       }
+    }
+
+  return (bits);
+}
+
+/* Set the umask from a symbolic mode string similar to that accepted
+   by chmod.  If the -S argument is given, then print the umask in a
+   symbolic form. */
+static int
+symbolic_umask (list)
+     WORD_LIST *list;
+{
+  int um, bits;
+
+  /* Get the initial umask.  Don't change it yet. */
+  um = umask (022);
+  umask (um);
+
+  /* All work is done with the complement of the umask -- it's
+     more intuitive and easier to deal with.  It is complemented
+     again before being returned. */
+  bits = parse_symbolic_mode (list->word->word, ~um & 0777);
+  if (bits == -1)
+    return (-1);
+
+  um = ~bits & 0777;
+  return (um);
+}
index 4f197144185e3c95b7a8fc7f7b11938127577ce2..77c8b784b5623da295e671ed1e09842f7b5549cb 100644 (file)
@@ -1,7 +1,7 @@
 This file is wait.def, from which is created wait.c.
 It implements the builtin "wait" in Bash.
 
-Copyright (C) 1987-2013 Free Software Foundation, Inc.
+Copyright (C) 1987-2015 Free Software Foundation, Inc.
 
 This file is part of GNU Bash, the Bourne Again SHell.
 
@@ -131,7 +131,7 @@ wait_builtin (list)
 
      We handle SIGINT here; it's the only one that needs to be treated
      specially (I think), since it's handled specially in {no,}jobs.c. */
-  code = setjmp (wait_intr_buf);
+  code = setjmp_sigs (wait_intr_buf);
   if (code)
     {
       last_command_exit_signal = wait_signal_received;
diff --git a/builtins/wait.def~ b/builtins/wait.def~
new file mode 100644 (file)
index 0000000..2de7bfb
--- /dev/null
@@ -0,0 +1,218 @@
+This file is wait.def, from which is created wait.c.
+It implements the builtin "wait" in Bash.
+
+Copyright (C) 1987-2013 Free Software Foundation, Inc.
+
+This file is part of GNU Bash, the Bourne Again SHell.
+
+Bash is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Bash is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Bash.  If not, see <http://www.gnu.org/licenses/>.
+
+$BUILTIN wait
+$FUNCTION wait_builtin
+$DEPENDS_ON JOB_CONTROL
+$PRODUCES wait.c
+$SHORT_DOC wait [-n] [id ...]
+Wait for job completion and return exit status.
+
+Waits for each process identified by an ID, which may be a process ID or a
+job specification, and reports its termination status.  If ID is not
+given, waits for all currently active child processes, and the return
+status is zero.  If ID is a a job specification, waits for all processes
+in that job's pipeline.
+
+If the -n option is supplied, waits for the next job to terminate and
+returns its exit status.
+
+Exit Status:
+Returns the status of the last ID; fails if ID is invalid or an invalid
+option is given.
+$END
+
+$BUILTIN wait
+$FUNCTION wait_builtin
+$DEPENDS_ON !JOB_CONTROL
+$SHORT_DOC wait [pid ...]
+Wait for process completion and return exit status.
+
+Waits for each process specified by a PID and reports its termination status.
+If PID is not given, waits for all currently active child processes,
+and the return status is zero.  PID must be a process ID.
+
+Exit Status:
+Returns the status of the last PID; fails if PID is invalid or an invalid
+option is given.
+$END
+
+#include <config.h>
+
+#include "../bashtypes.h"
+#include <signal.h>
+
+#if defined (HAVE_UNISTD_H)
+#  include <unistd.h>
+#endif
+
+#include <chartypes.h>
+
+#include "../bashansi.h"
+
+#include "../shell.h"
+#include "../jobs.h"
+#include "common.h"
+#include "bashgetopt.h"
+
+extern int wait_signal_received;
+extern int last_command_exit_signal;
+
+procenv_t wait_intr_buf;
+
+/* Wait for the pid in LIST to stop or die.  If no arguments are given, then
+   wait for all of the active background processes of the shell and return
+   0.  If a list of pids or job specs are given, return the exit status of
+   the last one waited for. */
+
+#define WAIT_RETURN(s) \
+  do \
+    { \
+      interrupt_immediately = old_interrupt_immediately;\
+      wait_signal_received = 0; \
+      return (s);\
+    } \
+  while (0)
+
+int
+wait_builtin (list)
+     WORD_LIST *list;
+{
+  int status, code, opt, nflag;
+  volatile int old_interrupt_immediately;
+
+  USE_VAR(list);
+
+  nflag = 0;
+  reset_internal_getopt ();
+  while ((opt = internal_getopt (list, "n")) != -1)
+    {
+      switch (opt)
+       {
+#if defined (JOB_CONTROL)
+       case 'n':
+         nflag = 1;
+         break;
+#endif
+       default:
+         builtin_usage ();
+         return (EX_USAGE);
+       }
+    }
+  list = loptend;
+
+  old_interrupt_immediately = interrupt_immediately;
+#if 0
+  interrupt_immediately++;
+#endif
+
+  /* POSIX.2 says:  When the shell is waiting (by means of the wait utility)
+     for asynchronous commands to complete, the reception of a signal for
+     which a trap has been set shall cause the wait utility to return
+     immediately with an exit status greater than 128, after which the trap
+     associated with the signal shall be taken.
+
+     We handle SIGINT here; it's the only one that needs to be treated
+     specially (I think), since it's handled specially in {no,}jobs.c. */
+  code = setjmp_sigs (wait_intr_buf);
+  if (code)
+    {
+      last_command_exit_signal = wait_signal_received;
+      status = 128 + wait_signal_received;
+      WAIT_RETURN (status);
+    }
+
+  /* We support jobs or pids.
+     wait <pid-or-job> [pid-or-job ...] */
+
+#if defined (JOB_CONTROL)
+  if (nflag)
+    {
+      status = wait_for_any_job ();
+      if (status < 0)
+       status = 127;
+      WAIT_RETURN (status);
+    }
+#endif
+      
+  /* But wait without any arguments means to wait for all of the shell's
+     currently active background processes. */
+  if (list == 0)
+    {
+      wait_for_background_pids ();
+      WAIT_RETURN (EXECUTION_SUCCESS);
+    }
+
+  status = EXECUTION_SUCCESS;
+  while (list)
+    {
+      pid_t pid;
+      char *w;
+      intmax_t pid_value;
+
+      w = list->word->word;
+      if (DIGIT (*w))
+       {
+         if (legal_number (w, &pid_value) && pid_value == (pid_t)pid_value)
+           {
+             pid = (pid_t)pid_value;
+             status = wait_for_single_pid (pid);
+           }
+         else
+           {
+             sh_badpid (w);
+             WAIT_RETURN (EXECUTION_FAILURE);
+           }
+       }
+#if defined (JOB_CONTROL)
+      else if (*w && *w == '%')
+       /* Must be a job spec.  Check it out. */
+       {
+         int job;
+         sigset_t set, oset;
+
+         BLOCK_CHILD (set, oset);
+         job = get_job_spec (list);
+
+         if (INVALID_JOB (job))
+           {
+             if (job != DUP_JOB)
+               sh_badjob (list->word->word);
+             UNBLOCK_CHILD (oset);
+             status = 127;     /* As per Posix.2, section 4.70.2 */
+             list = list->next;
+             continue;
+           }
+
+         /* Job spec used.  Wait for the last pid in the pipeline. */
+         UNBLOCK_CHILD (oset);
+         status = wait_for_job (job);
+       }
+#endif /* JOB_CONTROL */
+      else
+       {
+         sh_badpid (w);
+         status = EXECUTION_FAILURE;
+       }
+      list = list->next;
+    }
+
+  WAIT_RETURN (status);
+}
index 92ebcb3c18f94e9eed32b8388ad2f1d08610627b..cb2c76bbe71ffa5711e363b644f0ef3263c3b1b3 100644 (file)
 
 #undef UNUSABLE_RT_SIGNALS
 
-
 /* Presence of system and C library functions. */
 
 /* Define if you have the asprintf function.  */
 /* Define if you have the <locale.h> header file.  */
 #undef HAVE_LOCALE_H
 
+/* Define if you have the <mbstr.h> header file.  */
+#undef HAVE_MBSTR_H
+
 /* Define if you have the <ndir.h> header file.  */
 #undef HAVE_NDIR_H
 
index 0319bceb188b26b072e65fe1796ccbe948f2fdf4..dce12d83afeca51baa17bf9b4d6155e95a87695f 100755 (executable)
--- a/configure
+++ b/configure
 done
 
 
+for ac_header in mbstr.h
+do :
+  ac_fn_c_check_header_mongrel "$LINENO" "mbstr.h" "ac_cv_header_mbstr_h" "$ac_includes_default"
+if test "x$ac_cv_header_mbstr_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_MBSTR_H 1
+_ACEOF
+
+fi
+
+done
+
+
 ac_fn_c_check_func "$LINENO" "mbrlen" "ac_cv_func_mbrlen"
 if test "x$ac_cv_func_mbrlen" = xyes; then :
   $as_echo "#define HAVE_MBRLEN 1" >>confdefs.h
index 929e1437f3014d6ea581d93fb6cb2844b0978b49..a65c5c241813eb4334634fb7834cee337a09ff70 100644 (file)
@@ -100,6 +100,10 @@ extern int errno;
 #  include "bashhist.h"
 #endif
 
+#if defined (HAVE_MBSTR_H) && defined (HAVE_MBSCHR)
+#  include <mbstr.h>           /* mbschr */
+#endif
+
 #if defined (ARRAY_VARS)
 struct func_array_state
   {
index d351eb3cd98c3d453824ac3ccf6f56a12b57e85a..61a5f0e2a6afb2ae6eb798f453977cb5f2de8ad3 100644 (file)
--- a/general.c
+++ b/general.c
 #include "test.h"
 #include "trap.h"
 
+#if defined (HAVE_MBSTR_H) && defined (HAVE_MBSCHR)
+#  include <mbstr.h>           /* mbschr */
+#endif
+
 #include <tilde/tilde.h>
 
 #if !defined (errno)
index c7080181cbf29b077563251ca55d8880cda30380..0333f9448d8374ed7f04a02fc52932958207fe47 100644 (file)
 #endif
 
 #if !defined (isprint) && !defined (HAVE_ISPRINT)
-#  define isprint(c) (isalpha(c) || isdigit(c) || ispunct(c))
+#  define isprint(c) (isalpha((unsigned char)c) || isdigit((unsigned char)c) || ispunct((unsigned char)c))
 #endif
 
 #if defined (isblank) || defined (HAVE_ISBLANK)
-#  define ISBLANK(c) (IN_CTYPE_DOMAIN (c) && isblank (c))
+#  define ISBLANK(c) (IN_CTYPE_DOMAIN (c) && isblank ((unsigned char)c))
 #else
 #  define ISBLANK(c) ((c) == ' ' || (c) == '\t')
 #endif
@@ -58,7 +58,7 @@
 #if defined (isgraph) || defined (HAVE_ISGRAPH)
 #  define ISGRAPH(c) (IN_CTYPE_DOMAIN (c) && isgraph (c))
 #else
-#  define ISGRAPH(c) (IN_CTYPE_DOMAIN (c) && isprint (c) && !isspace (c))
+#  define ISGRAPH(c) (IN_CTYPE_DOMAIN (c) && isprint ((unsigned char)c) && !isspace ((unsigned char)c))
 #endif
 
 #if !defined (isxdigit) && !defined (HAVE_ISXDIGIT)
 
 #undef ISPRINT
 
-#define ISPRINT(c) (IN_CTYPE_DOMAIN (c) && isprint (c))
-#define ISDIGIT(c) (IN_CTYPE_DOMAIN (c) && isdigit (c))
-#define ISALNUM(c) (IN_CTYPE_DOMAIN (c) && isalnum (c))
-#define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c))
-#define ISCNTRL(c) (IN_CTYPE_DOMAIN (c) && iscntrl (c))
-#define ISLOWER(c) (IN_CTYPE_DOMAIN (c) && islower (c))
-#define ISPUNCT(c) (IN_CTYPE_DOMAIN (c) && ispunct (c))
-#define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c))
-#define ISUPPER(c) (IN_CTYPE_DOMAIN (c) && isupper (c))
-#define ISXDIGIT(c) (IN_CTYPE_DOMAIN (c) && isxdigit (c))
+#define ISPRINT(c) (IN_CTYPE_DOMAIN (c) && isprint ((unsigned char)c))
+#define ISDIGIT(c) (IN_CTYPE_DOMAIN (c) && isdigit ((unsigned char)c))
+#define ISALNUM(c) (IN_CTYPE_DOMAIN (c) && isalnum ((unsigned char)c))
+#define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha ((unsigned char)c))
+#define ISCNTRL(c) (IN_CTYPE_DOMAIN (c) && iscntrl ((unsigned char)c))
+#define ISLOWER(c) (IN_CTYPE_DOMAIN (c) && islower ((unsigned char)c))
+#define ISPUNCT(c) (IN_CTYPE_DOMAIN (c) && ispunct ((unsigned char)c))
+#define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace ((unsigned char)c))
+#define ISUPPER(c) (IN_CTYPE_DOMAIN (c) && isupper ((unsigned char)c))
+#define ISXDIGIT(c) (IN_CTYPE_DOMAIN (c) && isxdigit ((unsigned char)c))
 
 #define ISLETTER(c)    (ISALPHA(c))
 
diff --git a/include/chartypes.h~ b/include/chartypes.h~
new file mode 100644 (file)
index 0000000..66553f6
--- /dev/null
@@ -0,0 +1,113 @@
+/* chartypes.h -- extend ctype.h */
+
+/* Copyright (C) 2001 Free Software Foundation, Inc.
+
+   This file is part of GNU Bash, the Bourne Again SHell.
+
+   Bash is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   Bash is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with Bash.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _SH_CHARTYPES_H
+#define _SH_CHARTYPES_H
+
+#include <ctype.h>
+
+/* Jim Meyering writes:
+
+   "... Some ctype macros are valid only for character codes that
+   isascii says are ASCII (SGI's IRIX-4.0.5 is one such system --when
+   using /bin/cc or gcc but without giving an ansi option).  So, all
+   ctype uses should be through macros like ISPRINT...  If
+   STDC_HEADERS is defined, then autoconf has verified that the ctype
+   macros don't need to be guarded with references to isascii. ...
+   Defining IN_CTYPE_DOMAIN to 1 should let any compiler worth its salt
+   eliminate the && through constant folding."
+   Solaris defines some of these symbols so we must undefine them first.  */
+
+#if STDC_HEADERS || (!defined (isascii) && !HAVE_ISASCII)
+#  define IN_CTYPE_DOMAIN(c) 1
+#else
+#  define IN_CTYPE_DOMAIN(c) isascii(c)
+#endif
+
+#if !defined (isspace) && !defined (HAVE_ISSPACE)
+#  define isspace(c) ((c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\f')
+#endif
+
+#if !defined (isprint) && !defined (HAVE_ISPRINT)
+#  define isprint(c) (isalpha(c) || isdigit(c) || ispunct(c))
+#endif
+
+#if defined (isblank) || defined (HAVE_ISBLANK)
+#  define ISBLANK(c) (IN_CTYPE_DOMAIN (c) && isblank (c))
+#else
+#  define ISBLANK(c) ((c) == ' ' || (c) == '\t')
+#endif
+
+#if defined (isgraph) || defined (HAVE_ISGRAPH)
+#  define ISGRAPH(c) (IN_CTYPE_DOMAIN (c) && isgraph (c))
+#else
+#  define ISGRAPH(c) (IN_CTYPE_DOMAIN (c) && isprint (c) && !isspace (c))
+#endif
+
+#if !defined (isxdigit) && !defined (HAVE_ISXDIGIT)
+#  define isxdigit(c)  (((c) >= '0' && (c) <= '9') || ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F'))
+#endif
+
+#undef ISPRINT
+
+#define ISPRINT(c) (IN_CTYPE_DOMAIN (c) && isprint ((unsigned char)c))
+#define ISDIGIT(c) (IN_CTYPE_DOMAIN (c) && isdigit ((unsigned char)c))
+#define ISALNUM(c) (IN_CTYPE_DOMAIN (c) && isalnum ((unsigned char)c))
+#define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha ((unsigned char)c))
+#define ISCNTRL(c) (IN_CTYPE_DOMAIN (c) && iscntrl ((unsigned char)c))
+#define ISLOWER(c) (IN_CTYPE_DOMAIN (c) && islower ((unsigned char)c))
+#define ISPUNCT(c) (IN_CTYPE_DOMAIN (c) && ispunct ((unsigned char)c))
+#define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace ((unsigned char)c))
+#define ISUPPER(c) (IN_CTYPE_DOMAIN (c) && isupper ((unsigned char)c))
+#define ISXDIGIT(c) (IN_CTYPE_DOMAIN (c) && isxdigit ((unsigned char)c))
+
+#define ISLETTER(c)    (ISALPHA(c))
+
+#define DIGIT(c)       ((c) >= '0' && (c) <= '9')
+
+#define ISWORD(c)      (ISLETTER(c) || DIGIT(c) || ((c) == '_'))
+
+#define HEXVALUE(c) \
+  (((c) >= 'a' && (c) <= 'f') \
+       ? (c)-'a'+10 \
+       : (c) >= 'A' && (c) <= 'F' ? (c)-'A'+10 : (c)-'0')
+                  
+#ifndef ISOCTAL
+#  define ISOCTAL(c)   ((c) >= '0' && (c) <= '7')
+#endif
+#define OCTVALUE(c)    ((c) - '0')
+
+#define TODIGIT(c)     ((c) - '0')
+#define TOCHAR(c)      ((c) + '0')
+
+#define TOLOWER(c)     (ISUPPER(c) ? tolower(c) : (c))
+#define TOUPPER(c)     (ISLOWER(c) ? toupper(c) : (c))
+
+#ifndef TOCTRL
+   /* letter to control char -- ASCII.  The TOUPPER is in there so \ce and
+      \cE will map to the same character in $'...' expansions. */
+#  define TOCTRL(x)    (TOUPPER(x) & 037)
+#endif
+#ifndef UNCTRL
+   /* control char to letter -- ASCII */
+#  define UNCTRL(x)    (TOUPPER(x) ^ 0x40)
+#endif
+
+#endif /* _SH_CHARTYPES_H */
index 98cf7185ea1192d16e884aa2355baefc5e332bde..031c8fd896598977774ea515fe4ca2af1450cbd5 100644 (file)
@@ -1,6 +1,6 @@
 /* posixjmp.h -- wrapper for setjmp.h with changes for POSIX systems. */
 
-/* Copyright (C) 1987,1991 Free Software Foundation, Inc.
+/* Copyright (C) 1987,1991-2015 Free Software Foundation, Inc.
 
    This file is part of GNU Bash, the Bourne Again SHell.
 
 #if defined (HAVE_POSIX_SIGSETJMP)
 #  define procenv_t    sigjmp_buf
 #  if !defined (__OPENNT)
-#    undef setjmp
-#    define setjmp(x)  sigsetjmp((x), 1)
 #    define setjmp_nosigs(x)   sigsetjmp((x), 0)
+#    define setjmp_sigs(x)     sigsetjmp((x), 1)
 #    undef longjmp
 #    define longjmp(x, n)      siglongjmp((x), (n))
 #  endif /* !__OPENNT */
 #else
 #  define procenv_t    jmp_buf
 #  define setjmp_nosigs        setjmp
+#  define setjmp_sigs  setjmp
 #endif
 
 #endif /* _POSIXJMP_H_ */
diff --git a/include/posixjmp.h~ b/include/posixjmp.h~
new file mode 100644 (file)
index 0000000..8f8b45e
--- /dev/null
@@ -0,0 +1,42 @@
+/* posixjmp.h -- wrapper for setjmp.h with changes for POSIX systems. */
+
+/* Copyright (C) 1987,1991 Free Software Foundation, Inc.
+
+   This file is part of GNU Bash, the Bourne Again SHell.
+
+   Bash is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   Bash is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with Bash.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _POSIXJMP_H_
+#define _POSIXJMP_H_
+
+#include <setjmp.h>
+
+/* This *must* be included *after* config.h */
+
+#if defined (HAVE_POSIX_SIGSETJMP)
+#  define procenv_t    sigjmp_buf
+#  if !defined (__OPENNT)
+#    define setjmp_nosigs(x)   sigsetjmp((x), 0)
+#    define setjmp_sigs(x)     sigsetjmp((x), 1)
+#    undef longjmp
+#    define longjmp(x, n)      siglongjmp((x), (n))
+#  endif /* !__OPENNT */
+#else
+#  define procenv_t    jmp_buf
+#  define setjmp_nosigs        setjmp
+#  define setjmp_sigs  setjmp
+#endif
+
+#endif /* _POSIXJMP_H_ */
index 8de2067bca1df26c2ba2edff5bd18acc9079c2bb..f52d9f3624e387fe90b930e308f47a89f2a5fad3 100644 (file)
@@ -542,7 +542,7 @@ rl_translate_keyseq (seq, array, len)
            case '0': case '1': case '2': case '3':
            case '4': case '5': case '6': case '7':
              i++;
-             for (temp = 2, c -= '0'; ISOCTAL (seq[i]) && temp--; i++)
+             for (temp = 2, c -= '0'; ISOCTAL ((unsigned char)seq[i]) && temp--; i++)
                c = (c * 8) + OCTVALUE (seq[i]);
              i--;      /* auto-increment in for loop */
              array[l++] = c & largest_char;
diff --git a/lib/readline/bind.c~ b/lib/readline/bind.c~
new file mode 100644 (file)
index 0000000..8de2067
--- /dev/null
@@ -0,0 +1,2586 @@
+/* bind.c -- key binding and startup file support for the readline library. */
+
+/* Copyright (C) 1987-2012 Free Software Foundation, Inc.
+
+   This file is part of the GNU Readline Library (Readline), a library
+   for reading lines of text with interactive input and history editing.
+
+   Readline is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   Readline 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 Readline.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#define READLINE_LIBRARY
+
+#if defined (__TANDEM)
+#  include <floss.h>
+#endif
+
+#if defined (HAVE_CONFIG_H)
+#  include <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#if defined (HAVE_SYS_FILE_H)
+#  include <sys/file.h>
+#endif /* HAVE_SYS_FILE_H */
+
+#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 */
+
+#include <errno.h>
+
+#if !defined (errno)
+extern int errno;
+#endif /* !errno */
+
+#include "posixstat.h"
+
+/* System-specific feature definitions and include files. */
+#include "rldefs.h"
+
+/* Some standard library routines. */
+#include "readline.h"
+#include "history.h"
+
+#include "rlprivate.h"
+#include "rlshell.h"
+#include "xmalloc.h"
+
+#if !defined (strchr) && !defined (__STDC__)
+extern char *strchr (), *strrchr ();
+#endif /* !strchr && !__STDC__ */
+
+/* Variables exported by this file. */
+Keymap rl_binding_keymap;
+
+static int _rl_skip_to_delim PARAMS((char *, int, int));
+
+static char *_rl_read_file PARAMS((char *, size_t *));
+static void _rl_init_file_error PARAMS((const char *));
+static int _rl_read_init_file PARAMS((const char *, int));
+static int glean_key_from_name PARAMS((char *));
+
+static int find_boolean_var PARAMS((const char *));
+static int find_string_var PARAMS((const char *));
+
+static char *_rl_get_string_variable_value PARAMS((const char *));
+static int substring_member_of_array PARAMS((const char *, const char * const *));
+
+static int currently_reading_init_file;
+
+/* used only in this file */
+static int _rl_prefer_visible_bell = 1;
+
+/* **************************************************************** */
+/*                                                                 */
+/*                     Binding keys                                */
+/*                                                                 */
+/* **************************************************************** */
+
+/* rl_add_defun (char *name, rl_command_func_t *function, int key)
+   Add NAME to the list of named functions.  Make FUNCTION be the function
+   that gets called.  If KEY is not -1, then bind it. */
+int
+rl_add_defun (name, function, key)
+     const char *name;
+     rl_command_func_t *function;
+     int key;
+{
+  if (key != -1)
+    rl_bind_key (key, function);
+  rl_add_funmap_entry (name, function);
+  return 0;
+}
+
+/* Bind KEY to FUNCTION.  Returns non-zero if KEY is out of range. */
+int
+rl_bind_key (key, function)
+     int key;
+     rl_command_func_t *function;
+{
+  if (key < 0)
+    return (key);
+
+  if (META_CHAR (key) && _rl_convert_meta_chars_to_ascii)
+    {
+      if (_rl_keymap[ESC].type == ISKMAP)
+       {
+         Keymap escmap;
+
+         escmap = FUNCTION_TO_KEYMAP (_rl_keymap, ESC);
+         key = UNMETA (key);
+         escmap[key].type = ISFUNC;
+         escmap[key].function = function;
+         return (0);
+       }
+      return (key);
+    }
+
+  _rl_keymap[key].type = ISFUNC;
+  _rl_keymap[key].function = function;
+  rl_binding_keymap = _rl_keymap;
+  return (0);
+}
+
+/* Bind KEY to FUNCTION in MAP.  Returns non-zero in case of invalid
+   KEY. */
+int
+rl_bind_key_in_map (key, function, map)
+     int key;
+     rl_command_func_t *function;
+     Keymap map;
+{
+  int result;
+  Keymap oldmap;
+
+  oldmap = _rl_keymap;
+  _rl_keymap = map;
+  result = rl_bind_key (key, function);
+  _rl_keymap = oldmap;
+  return (result);
+}
+
+/* Bind key sequence KEYSEQ to DEFAULT_FUNC if KEYSEQ is unbound.  Right
+   now, this is always used to attempt to bind the arrow keys, hence the
+   check for rl_vi_movement_mode. */
+int
+rl_bind_key_if_unbound_in_map (key, default_func, kmap)
+     int key;
+     rl_command_func_t *default_func;
+     Keymap kmap;
+{
+  char keyseq[2];
+
+  keyseq[0] = (unsigned char)key;
+  keyseq[1] = '\0';
+  return (rl_bind_keyseq_if_unbound_in_map (keyseq, default_func, kmap));
+}
+
+int
+rl_bind_key_if_unbound (key, default_func)
+     int key;
+     rl_command_func_t *default_func;
+{
+  char keyseq[2];
+
+  keyseq[0] = (unsigned char)key;
+  keyseq[1] = '\0';
+  return (rl_bind_keyseq_if_unbound_in_map (keyseq, default_func, _rl_keymap));
+}
+
+/* Make KEY do nothing in the currently selected keymap.
+   Returns non-zero in case of error. */
+int
+rl_unbind_key (key)
+     int key;
+{
+  return (rl_bind_key (key, (rl_command_func_t *)NULL));
+}
+
+/* Make KEY do nothing in MAP.
+   Returns non-zero in case of error. */
+int
+rl_unbind_key_in_map (key, map)
+     int key;
+     Keymap map;
+{
+  return (rl_bind_key_in_map (key, (rl_command_func_t *)NULL, map));
+}
+
+/* Unbind all keys bound to FUNCTION in MAP. */
+int
+rl_unbind_function_in_map (func, map)
+     rl_command_func_t *func;
+     Keymap map;
+{
+  register int i, rval;
+
+  for (i = rval = 0; i < KEYMAP_SIZE; i++)
+    {
+      if (map[i].type == ISFUNC && map[i].function == func)
+       {
+         map[i].function = (rl_command_func_t *)NULL;
+         rval = 1;
+       }
+    }
+  return rval;
+}
+
+int
+rl_unbind_command_in_map (command, map)
+     const char *command;
+     Keymap map;
+{
+  rl_command_func_t *func;
+
+  func = rl_named_function (command);
+  if (func == 0)
+    return 0;
+  return (rl_unbind_function_in_map (func, map));
+}
+
+/* Bind the key sequence represented by the string KEYSEQ to
+   FUNCTION, starting in the current keymap.  This makes new
+   keymaps as necessary. */
+int
+rl_bind_keyseq (keyseq, function)
+     const char *keyseq;
+     rl_command_func_t *function;
+{
+  return (rl_generic_bind (ISFUNC, keyseq, (char *)function, _rl_keymap));
+}
+
+/* Bind the key sequence represented by the string KEYSEQ to
+   FUNCTION.  This makes new keymaps as necessary.  The initial
+   place to do bindings is in MAP. */
+int
+rl_bind_keyseq_in_map (keyseq, function, map)
+     const char *keyseq;
+     rl_command_func_t *function;
+     Keymap map;
+{
+  return (rl_generic_bind (ISFUNC, keyseq, (char *)function, map));
+}
+
+/* Backwards compatibility; equivalent to rl_bind_keyseq_in_map() */
+int
+rl_set_key (keyseq, function, map)
+     const char *keyseq;
+     rl_command_func_t *function;
+     Keymap map;
+{
+  return (rl_generic_bind (ISFUNC, keyseq, (char *)function, map));
+}
+
+/* Bind key sequence KEYSEQ to DEFAULT_FUNC if KEYSEQ is unbound.  Right
+   now, this is always used to attempt to bind the arrow keys, hence the
+   check for rl_vi_movement_mode. */
+int
+rl_bind_keyseq_if_unbound_in_map (keyseq, default_func, kmap)
+     const char *keyseq;
+     rl_command_func_t *default_func;
+     Keymap kmap;
+{
+  rl_command_func_t *func;
+
+  if (keyseq)
+    {
+      func = rl_function_of_keyseq (keyseq, kmap, (int *)NULL);
+#if defined (VI_MODE)
+      if (!func || func == rl_do_lowercase_version || func == rl_vi_movement_mode)
+#else
+      if (!func || func == rl_do_lowercase_version)
+#endif
+       return (rl_bind_keyseq_in_map (keyseq, default_func, kmap));
+      else
+       return 1;
+    }
+  return 0;
+}
+
+int
+rl_bind_keyseq_if_unbound (keyseq, default_func)
+     const char *keyseq;
+     rl_command_func_t *default_func;
+{
+  return (rl_bind_keyseq_if_unbound_in_map (keyseq, default_func, _rl_keymap));
+}
+
+/* Bind the key sequence represented by the string KEYSEQ to
+   the string of characters MACRO.  This makes new keymaps as
+   necessary.  The initial place to do bindings is in MAP. */
+int
+rl_macro_bind (keyseq, macro, map)
+     const char *keyseq, *macro;
+     Keymap map;
+{
+  char *macro_keys;
+  int macro_keys_len;
+
+  macro_keys = (char *)xmalloc ((2 * strlen (macro)) + 1);
+
+  if (rl_translate_keyseq (macro, macro_keys, &macro_keys_len))
+    {
+      xfree (macro_keys);
+      return -1;
+    }
+  rl_generic_bind (ISMACR, keyseq, macro_keys, map);
+  return 0;
+}
+
+/* Bind the key sequence represented by the string KEYSEQ to
+   the arbitrary pointer DATA.  TYPE says what kind of data is
+   pointed to by DATA, right now this can be a function (ISFUNC),
+   a macro (ISMACR), or a keymap (ISKMAP).  This makes new keymaps
+   as necessary.  The initial place to do bindings is in MAP. */
+int
+rl_generic_bind (type, keyseq, data, map)
+     int type;
+     const char *keyseq;
+     char *data;
+     Keymap map;
+{
+  char *keys;
+  int keys_len;
+  register int i;
+  KEYMAP_ENTRY k;
+
+  k.function = 0;
+
+  /* If no keys to bind to, exit right away. */
+  if (keyseq == 0 || *keyseq == 0)
+    {
+      if (type == ISMACR)
+       xfree (data);
+      return -1;
+    }
+
+  keys = (char *)xmalloc (1 + (2 * strlen (keyseq)));
+
+  /* Translate the ASCII representation of KEYSEQ into an array of
+     characters.  Stuff the characters into KEYS, and the length of
+     KEYS into KEYS_LEN. */
+  if (rl_translate_keyseq (keyseq, keys, &keys_len))
+    {
+      xfree (keys);
+      return -1;
+    }
+
+  /* Bind keys, making new keymaps as necessary. */
+  for (i = 0; i < keys_len; i++)
+    {
+      unsigned char uc = keys[i];
+      int ic;
+
+      ic = uc;
+      if (ic < 0 || ic >= KEYMAP_SIZE)
+        {
+          xfree (keys);
+         return -1;
+        }
+
+      if (META_CHAR (ic) && _rl_convert_meta_chars_to_ascii)
+       {
+         ic = UNMETA (ic);
+         if (map[ESC].type == ISKMAP)
+           map = FUNCTION_TO_KEYMAP (map, ESC);
+       }
+
+      if ((i + 1) < keys_len)
+       {
+         if (map[ic].type != ISKMAP)
+           {
+             /* We allow subsequences of keys.  If a keymap is being
+                created that will `shadow' an existing function or macro
+                key binding, we save that keybinding into the ANYOTHERKEY
+                index in the new map.  The dispatch code will look there
+                to find the function to execute if the subsequence is not
+                matched.  ANYOTHERKEY was chosen to be greater than
+                UCHAR_MAX. */
+             k = map[ic];
+
+             map[ic].type = ISKMAP;
+             map[ic].function = KEYMAP_TO_FUNCTION (rl_make_bare_keymap());
+           }
+         map = FUNCTION_TO_KEYMAP (map, ic);
+         /* The dispatch code will return this function if no matching
+            key sequence is found in the keymap.  This (with a little
+            help from the dispatch code in readline.c) allows `a' to be
+            mapped to something, `abc' to be mapped to something else,
+            and the function bound  to `a' to be executed when the user
+            types `abx', leaving `bx' in the input queue. */
+         if (k.function && ((k.type == ISFUNC && k.function != rl_do_lowercase_version) || k.type == ISMACR))
+           {
+             map[ANYOTHERKEY] = k;
+             k.function = 0;
+           }
+       }
+      else
+       {
+         if (map[ic].type == ISMACR)
+           xfree ((char *)map[ic].function);
+         else if (map[ic].type == ISKMAP)
+           {
+             map = FUNCTION_TO_KEYMAP (map, ic);
+             ic = ANYOTHERKEY;
+             /* If we're trying to override a keymap with a null function
+                (e.g., trying to unbind it), we can't use a null pointer
+                here because that's indistinguishable from having not been
+                overridden.  We use a special bindable function that does
+                nothing. */
+             if (type == ISFUNC && data == 0)
+               data = (char *)_rl_null_function;
+           }
+
+         map[ic].function = KEYMAP_TO_FUNCTION (data);
+         map[ic].type = type;
+       }
+
+      rl_binding_keymap = map;
+    }
+  xfree (keys);
+  return 0;
+}
+
+/* Translate the ASCII representation of SEQ, stuffing the values into ARRAY,
+   an array of characters.  LEN gets the final length of ARRAY.  Return
+   non-zero if there was an error parsing SEQ. */
+int
+rl_translate_keyseq (seq, array, len)
+     const char *seq;
+     char *array;
+     int *len;
+{
+  register int i, c, l, temp;
+
+  for (i = l = 0; c = seq[i]; i++)
+    {
+      if (c == '\\')
+       {
+         c = seq[++i];
+
+         if (c == 0)
+           break;
+
+         /* Handle \C- and \M- prefixes. */
+         if ((c == 'C' || c == 'M') && seq[i + 1] == '-')
+           {
+             /* Handle special case of backwards define. */
+             if (strncmp (&seq[i], "C-\\M-", 5) == 0)
+               {
+                 array[l++] = ESC;     /* ESC is meta-prefix */
+                 i += 5;
+                 array[l++] = CTRL (_rl_to_upper (seq[i]));
+                 if (seq[i] == '\0')
+                   i--;
+               }
+             else if (c == 'M')
+               {
+                 i++;          /* seq[i] == '-' */
+                 /* XXX - obey convert-meta setting */
+                 if (_rl_convert_meta_chars_to_ascii && _rl_keymap[ESC].type == ISKMAP)
+                   array[l++] = ESC;   /* ESC is meta-prefix */
+                 else if (seq[i+1] == '\\' && seq[i+2] == 'C' && seq[i+3] == '-')
+                   {
+                     i += 4;
+                     temp = (seq[i] == '?') ? RUBOUT : CTRL (_rl_to_upper (seq[i]));
+                     array[l++] = META (temp);
+                   }
+                 else
+                   {
+                     /* This doesn't yet handle things like \M-\a, which may
+                        or may not have any reasonable meaning.  You're
+                        probably better off using straight octal or hex. */
+                     i++;
+                     array[l++] = META (seq[i]);
+                   }
+               }
+             else if (c == 'C')
+               {
+                 i += 2;
+                 /* Special hack for C-?... */
+                 array[l++] = (seq[i] == '?') ? RUBOUT : CTRL (_rl_to_upper (seq[i]));
+               }
+             continue;
+           }         
+
+         /* Translate other backslash-escaped characters.  These are the
+            same escape sequences that bash's `echo' and `printf' builtins
+            handle, with the addition of \d -> RUBOUT.  A backslash
+            preceding a character that is not special is stripped. */
+         switch (c)
+           {
+           case 'a':
+             array[l++] = '\007';
+             break;
+           case 'b':
+             array[l++] = '\b';
+             break;
+           case 'd':
+             array[l++] = RUBOUT;      /* readline-specific */
+             break;
+           case 'e':
+             array[l++] = ESC;
+             break;
+           case 'f':
+             array[l++] = '\f';
+             break;
+           case 'n':
+             array[l++] = NEWLINE;
+             break;
+           case 'r':
+             array[l++] = RETURN;
+             break;
+           case 't':
+             array[l++] = TAB;
+             break;
+           case 'v':
+             array[l++] = 0x0B;
+             break;
+           case '\\':
+             array[l++] = '\\';
+             break;
+           case '0': case '1': case '2': case '3':
+           case '4': case '5': case '6': case '7':
+             i++;
+             for (temp = 2, c -= '0'; ISOCTAL (seq[i]) && temp--; i++)
+               c = (c * 8) + OCTVALUE (seq[i]);
+             i--;      /* auto-increment in for loop */
+             array[l++] = c & largest_char;
+             break;
+           case 'x':
+             i++;
+             for (temp = 2, c = 0; ISXDIGIT ((unsigned char)seq[i]) && temp--; i++)
+               c = (c * 16) + HEXVALUE (seq[i]);
+             if (temp == 2)
+               c = 'x';
+             i--;      /* auto-increment in for loop */
+             array[l++] = c & largest_char;
+             break;
+           default:    /* backslashes before non-special chars just add the char */
+             array[l++] = c;
+             break;    /* the backslash is stripped */
+           }
+         continue;
+       }
+
+      array[l++] = c;
+    }
+
+  *len = l;
+  array[l] = '\0';
+  return (0);
+}
+
+static int
+_rl_isescape (c)
+     int c;
+{
+  switch (c)
+    {
+    case '\007':
+    case '\b':
+    case '\f':
+    case '\n':
+    case '\r':
+    case TAB:
+    case 0x0b:  return (1);
+    default: return (0);
+    }
+}
+
+static int
+_rl_escchar (c)
+     int c;
+{
+  switch (c)
+    {
+    case '\007':  return ('a');
+    case '\b':  return ('b');
+    case '\f':  return ('f');
+    case '\n':  return ('n');
+    case '\r':  return ('r');
+    case TAB:  return ('t');
+    case 0x0b:  return ('v');
+    default: return (c);
+    }
+}
+
+char *
+rl_untranslate_keyseq (seq)
+     int seq;
+{
+  static char kseq[16];
+  int i, c;
+
+  i = 0;
+  c = seq;
+  if (META_CHAR (c))
+    {
+      kseq[i++] = '\\';
+      kseq[i++] = 'M';
+      kseq[i++] = '-';
+      c = UNMETA (c);
+    }
+  else if (c == ESC)
+    {
+      kseq[i++] = '\\';
+      c = 'e';
+    }
+  else if (CTRL_CHAR (c))
+    {
+      kseq[i++] = '\\';
+      kseq[i++] = 'C';
+      kseq[i++] = '-';
+      c = _rl_to_lower (UNCTRL (c));
+    }
+  else if (c == RUBOUT)
+    {
+      kseq[i++] = '\\';
+      kseq[i++] = 'C';
+      kseq[i++] = '-';
+      c = '?';
+    }
+
+  if (c == ESC)
+    {
+      kseq[i++] = '\\';
+      c = 'e';
+    }
+  else if (c == '\\' || c == '"')
+    {
+      kseq[i++] = '\\';
+    }
+
+  kseq[i++] = (unsigned char) c;
+  kseq[i] = '\0';
+  return kseq;
+}
+
+char *
+_rl_untranslate_macro_value (seq, use_escapes)
+     char *seq;
+     int use_escapes;
+{
+  char *ret, *r, *s;
+  int c;
+
+  r = ret = (char *)xmalloc (7 * strlen (seq) + 1);
+  for (s = seq; *s; s++)
+    {
+      c = *s;
+      if (META_CHAR (c))
+       {
+         *r++ = '\\';
+         *r++ = 'M';
+         *r++ = '-';
+         c = UNMETA (c);
+       }
+      else if (c == ESC)
+       {
+         *r++ = '\\';
+         c = 'e';
+       }
+      else if (CTRL_CHAR (c))
+       {
+         *r++ = '\\';
+         if (use_escapes && _rl_isescape (c))
+           c = _rl_escchar (c);
+         else
+           {
+             *r++ = 'C';
+             *r++ = '-';
+             c = _rl_to_lower (UNCTRL (c));
+           }
+       }
+      else if (c == RUBOUT)
+       {
+         *r++ = '\\';
+         *r++ = 'C';
+         *r++ = '-';
+         c = '?';
+       }
+
+      if (c == ESC)
+       {
+         *r++ = '\\';
+         c = 'e';
+       }
+      else if (c == '\\' || c == '"')
+       *r++ = '\\';
+
+      *r++ = (unsigned char)c;
+    }
+  *r = '\0';
+  return ret;
+}
+
+/* Return a pointer to the function that STRING represents.
+   If STRING doesn't have a matching function, then a NULL pointer
+   is returned. */
+rl_command_func_t *
+rl_named_function (string)
+     const char *string;
+{
+  register int i;
+
+  rl_initialize_funmap ();
+
+  for (i = 0; funmap[i]; i++)
+    if (_rl_stricmp (funmap[i]->name, string) == 0)
+      return (funmap[i]->function);
+  return ((rl_command_func_t *)NULL);
+}
+
+/* Return the function (or macro) definition which would be invoked via
+   KEYSEQ if executed in MAP.  If MAP is NULL, then the current keymap is
+   used.  TYPE, if non-NULL, is a pointer to an int which will receive the
+   type of the object pointed to.  One of ISFUNC (function), ISKMAP (keymap),
+   or ISMACR (macro). */
+rl_command_func_t *
+rl_function_of_keyseq (keyseq, map, type)
+     const char *keyseq;
+     Keymap map;
+     int *type;
+{
+  register int i;
+
+  if (map == 0)
+    map = _rl_keymap;
+
+  for (i = 0; keyseq && keyseq[i]; i++)
+    {
+      unsigned char ic = keyseq[i];
+
+      if (META_CHAR (ic) && _rl_convert_meta_chars_to_ascii)
+       {
+         if (map[ESC].type == ISKMAP)
+           {
+             map = FUNCTION_TO_KEYMAP (map, ESC);
+             ic = UNMETA (ic);
+           }
+         /* XXX - should we just return NULL here, since this obviously
+            doesn't match? */
+         else
+           {
+             if (type)
+               *type = map[ESC].type;
+
+             return (map[ESC].function);
+           }
+       }
+
+      if (map[ic].type == ISKMAP)
+       {
+         /* If this is the last key in the key sequence, return the
+            map. */
+         if (keyseq[i + 1] == '\0')
+           {
+             if (type)
+               *type = ISKMAP;
+
+             return (map[ic].function);
+           }
+         else
+           map = FUNCTION_TO_KEYMAP (map, ic);
+       }
+      /* If we're not at the end of the key sequence, and the current key
+        is bound to something other than a keymap, then the entire key
+        sequence is not bound. */
+      else if (map[ic].type != ISKMAP && keyseq[i+1])
+       return ((rl_command_func_t *)NULL);
+      else     /* map[ic].type != ISKMAP && keyseq[i+1] == 0 */
+       {
+         if (type)
+           *type = map[ic].type;
+
+         return (map[ic].function);
+       }
+    }
+  return ((rl_command_func_t *) NULL);
+}
+
+/* The last key bindings file read. */
+static char *last_readline_init_file = (char *)NULL;
+
+/* The file we're currently reading key bindings from. */
+static const char *current_readline_init_file;
+static int current_readline_init_include_level;
+static int current_readline_init_lineno;
+
+/* Read FILENAME into a locally-allocated buffer and return the buffer.
+   The size of the buffer is returned in *SIZEP.  Returns NULL if any
+   errors were encountered. */
+static char *
+_rl_read_file (filename, sizep)
+     char *filename;
+     size_t *sizep;
+{
+  struct stat finfo;
+  size_t file_size;
+  char *buffer;
+  int i, file;
+
+  if ((stat (filename, &finfo) < 0) || (file = open (filename, O_RDONLY, 0666)) < 0)
+    return ((char *)NULL);
+
+  file_size = (size_t)finfo.st_size;
+
+  /* check for overflow on very large files */
+  if (file_size != finfo.st_size || file_size + 1 < file_size)
+    {
+      if (file >= 0)
+       close (file);
+#if defined (EFBIG)
+      errno = EFBIG;
+#endif
+      return ((char *)NULL);
+    }
+
+  /* Read the file into BUFFER. */
+  buffer = (char *)xmalloc (file_size + 1);
+  i = read (file, buffer, file_size);
+  close (file);
+
+  if (i < 0)
+    {
+      xfree (buffer);
+      return ((char *)NULL);
+    }
+
+  RL_CHECK_SIGNALS ();
+
+  buffer[i] = '\0';
+  if (sizep)
+    *sizep = i;
+
+  return (buffer);
+}
+
+/* Re-read the current keybindings file. */
+int
+rl_re_read_init_file (count, ignore)
+     int count, ignore;
+{
+  int r;
+  r = rl_read_init_file ((const char *)NULL);
+  rl_set_keymap_from_edit_mode ();
+  return r;
+}
+
+/* Do key bindings from a file.  If FILENAME is NULL it defaults
+   to the first non-null filename from this list:
+     1. the filename used for the previous call
+     2. the value of the shell variable `INPUTRC'
+     3. ~/.inputrc
+     4. /etc/inputrc
+   If the file existed and could be opened and read, 0 is returned,
+   otherwise errno is returned. */
+int
+rl_read_init_file (filename)
+     const char *filename;
+{
+  /* Default the filename. */
+  if (filename == 0)
+    filename = last_readline_init_file;
+  if (filename == 0)
+    filename = sh_get_env_value ("INPUTRC");
+  if (filename == 0 || *filename == 0)
+    {
+      filename = DEFAULT_INPUTRC;
+      /* Try to read DEFAULT_INPUTRC; fall back to SYS_INPUTRC on failure */
+      if (_rl_read_init_file (filename, 0) == 0)
+       return 0;
+      filename = SYS_INPUTRC;
+    }
+
+#if defined (__MSDOS__)
+  if (_rl_read_init_file (filename, 0) == 0)
+    return 0;
+  filename = "~/_inputrc";
+#endif
+  return (_rl_read_init_file (filename, 0));
+}
+
+static int
+_rl_read_init_file (filename, include_level)
+     const char *filename;
+     int include_level;
+{
+  register int i;
+  char *buffer, *openname, *line, *end;
+  size_t file_size;
+
+  current_readline_init_file = filename;
+  current_readline_init_include_level = include_level;
+
+  openname = tilde_expand (filename);
+  buffer = _rl_read_file (openname, &file_size);
+  xfree (openname);
+
+  RL_CHECK_SIGNALS ();
+  if (buffer == 0)
+    return (errno);
+  
+  if (include_level == 0 && filename != last_readline_init_file)
+    {
+      FREE (last_readline_init_file);
+      last_readline_init_file = savestring (filename);
+    }
+
+  currently_reading_init_file = 1;
+
+  /* Loop over the lines in the file.  Lines that start with `#' are
+     comments; all other lines are commands for readline initialization. */
+  current_readline_init_lineno = 1;
+  line = buffer;
+  end = buffer + file_size;
+  while (line < end)
+    {
+      /* Find the end of this line. */
+      for (i = 0; line + i != end && line[i] != '\n'; i++);
+
+#if defined (__CYGWIN__)
+      /* ``Be liberal in what you accept.'' */
+      if (line[i] == '\n' && line[i-1] == '\r')
+       line[i - 1] = '\0';
+#endif
+
+      /* Mark end of line. */
+      line[i] = '\0';
+
+      /* Skip leading whitespace. */
+      while (*line && whitespace (*line))
+        {
+         line++;
+         i--;
+        }
+
+      /* If the line is not a comment, then parse it. */
+      if (*line && *line != '#')
+       rl_parse_and_bind (line);
+
+      /* Move to the next line. */
+      line += i + 1;
+      current_readline_init_lineno++;
+    }
+
+  xfree (buffer);
+  currently_reading_init_file = 0;
+  return (0);
+}
+
+static void
+_rl_init_file_error (msg)
+     const char *msg;
+{
+  if (currently_reading_init_file)
+    _rl_errmsg ("%s: line %d: %s\n", current_readline_init_file,
+                    current_readline_init_lineno, msg);
+  else
+    _rl_errmsg ("%s", msg);
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*                     Parser Directives                           */
+/*                                                                 */
+/* **************************************************************** */
+
+typedef int _rl_parser_func_t PARAMS((char *));
+
+/* Things that mean `Control'. */
+const char * const _rl_possible_control_prefixes[] = {
+  "Control-", "C-", "CTRL-", (const char *)NULL
+};
+
+const char * const _rl_possible_meta_prefixes[] = {
+  "Meta", "M-", (const char *)NULL
+};
+
+/* Conditionals. */
+
+/* Calling programs set this to have their argv[0]. */
+const char *rl_readline_name = "other";
+
+/* Stack of previous values of parsing_conditionalized_out. */
+static unsigned char *if_stack = (unsigned char *)NULL;
+static int if_stack_depth;
+static int if_stack_size;
+
+/* Push _rl_parsing_conditionalized_out, and set parser state based
+   on ARGS. */
+static int
+parser_if (args)
+     char *args;
+{
+  register int i;
+
+  /* Push parser state. */
+  if (if_stack_depth + 1 >= if_stack_size)
+    {
+      if (!if_stack)
+       if_stack = (unsigned char *)xmalloc (if_stack_size = 20);
+      else
+       if_stack = (unsigned char *)xrealloc (if_stack, if_stack_size += 20);
+    }
+  if_stack[if_stack_depth++] = _rl_parsing_conditionalized_out;
+
+  /* If parsing is turned off, then nothing can turn it back on except
+     for finding the matching endif.  In that case, return right now. */
+  if (_rl_parsing_conditionalized_out)
+    return 0;
+
+  /* Isolate first argument. */
+  for (i = 0; args[i] && !whitespace (args[i]); i++);
+
+  if (args[i])
+    args[i++] = '\0';
+
+  /* Handle "$if term=foo" and "$if mode=emacs" constructs.  If this
+     isn't term=foo, or mode=emacs, then check to see if the first
+     word in ARGS is the same as the value stored in rl_readline_name. */
+  if (rl_terminal_name && _rl_strnicmp (args, "term=", 5) == 0)
+    {
+      char *tem, *tname;
+
+      /* Terminals like "aaa-60" are equivalent to "aaa". */
+      tname = savestring (rl_terminal_name);
+      tem = strchr (tname, '-');
+      if (tem)
+       *tem = '\0';
+
+      /* Test the `long' and `short' forms of the terminal name so that
+        if someone has a `sun-cmd' and does not want to have bindings
+        that will be executed if the terminal is a `sun', they can put
+        `$if term=sun-cmd' into their .inputrc. */
+      _rl_parsing_conditionalized_out = _rl_stricmp (args + 5, tname) &&
+                                       _rl_stricmp (args + 5, rl_terminal_name);
+      xfree (tname);
+    }
+#if defined (VI_MODE)
+  else if (_rl_strnicmp (args, "mode=", 5) == 0)
+    {
+      int mode;
+
+      if (_rl_stricmp (args + 5, "emacs") == 0)
+       mode = emacs_mode;
+      else if (_rl_stricmp (args + 5, "vi") == 0)
+       mode = vi_mode;
+      else
+       mode = no_mode;
+
+      _rl_parsing_conditionalized_out = mode != rl_editing_mode;
+    }
+#endif /* VI_MODE */
+  /* Check to see if the first word in ARGS is the same as the
+     value stored in rl_readline_name. */
+  else if (_rl_stricmp (args, rl_readline_name) == 0)
+    _rl_parsing_conditionalized_out = 0;
+  else
+    _rl_parsing_conditionalized_out = 1;
+  return 0;
+}
+
+/* Invert the current parser state if there is anything on the stack. */
+static int
+parser_else (args)
+     char *args;
+{
+  register int i;
+
+  if (if_stack_depth == 0)
+    {
+      _rl_init_file_error ("$else found without matching $if");
+      return 0;
+    }
+
+#if 0
+  /* Check the previous (n - 1) levels of the stack to make sure that
+     we haven't previously turned off parsing. */
+  for (i = 0; i < if_stack_depth - 1; i++)
+#else
+  /* Check the previous (n) levels of the stack to make sure that
+     we haven't previously turned off parsing. */
+  for (i = 0; i < if_stack_depth; i++)
+#endif
+    if (if_stack[i] == 1)
+      return 0;
+
+  /* Invert the state of parsing if at top level. */
+  _rl_parsing_conditionalized_out = !_rl_parsing_conditionalized_out;
+  return 0;
+}
+
+/* Terminate a conditional, popping the value of
+   _rl_parsing_conditionalized_out from the stack. */
+static int
+parser_endif (args)
+     char *args;
+{
+  if (if_stack_depth)
+    _rl_parsing_conditionalized_out = if_stack[--if_stack_depth];
+  else
+    _rl_init_file_error ("$endif without matching $if");
+  return 0;
+}
+
+static int
+parser_include (args)
+     char *args;
+{
+  const char *old_init_file;
+  char *e;
+  int old_line_number, old_include_level, r;
+
+  if (_rl_parsing_conditionalized_out)
+    return (0);
+
+  old_init_file = current_readline_init_file;
+  old_line_number = current_readline_init_lineno;
+  old_include_level = current_readline_init_include_level;
+
+  e = strchr (args, '\n');
+  if (e)
+    *e = '\0';
+  r = _rl_read_init_file ((const char *)args, old_include_level + 1);
+
+  current_readline_init_file = old_init_file;
+  current_readline_init_lineno = old_line_number;
+  current_readline_init_include_level = old_include_level;
+
+  return r;
+}
+  
+/* Associate textual names with actual functions. */
+static const struct {
+  const char * const name;
+  _rl_parser_func_t *function;
+} parser_directives [] = {
+  { "if", parser_if },
+  { "endif", parser_endif },
+  { "else", parser_else },
+  { "include", parser_include },
+  { (char *)0x0, (_rl_parser_func_t *)0x0 }
+};
+
+/* Handle a parser directive.  STATEMENT is the line of the directive
+   without any leading `$'. */
+static int
+handle_parser_directive (statement)
+     char *statement;
+{
+  register int i;
+  char *directive, *args;
+
+  /* Isolate the actual directive. */
+
+  /* Skip whitespace. */
+  for (i = 0; whitespace (statement[i]); i++);
+
+  directive = &statement[i];
+
+  for (; statement[i] && !whitespace (statement[i]); i++);
+
+  if (statement[i])
+    statement[i++] = '\0';
+
+  for (; statement[i] && whitespace (statement[i]); i++);
+
+  args = &statement[i];
+
+  /* Lookup the command, and act on it. */
+  for (i = 0; parser_directives[i].name; i++)
+    if (_rl_stricmp (directive, parser_directives[i].name) == 0)
+      {
+       (*parser_directives[i].function) (args);
+       return (0);
+      }
+
+  /* display an error message about the unknown parser directive */
+  _rl_init_file_error ("unknown parser directive");
+  return (1);
+}
+
+/* Start at STRING[START] and look for DELIM.  Return I where STRING[I] ==
+   DELIM or STRING[I] == 0.  DELIM is usually a double quote. */
+static int
+_rl_skip_to_delim (string, start, delim)
+     char *string;
+     int start, delim;
+{
+  int i, c, passc;
+
+  for (i = start,passc = 0; c = string[i]; i++)
+    {
+      if (passc)
+       {
+         passc = 0;
+         if (c == 0)
+           break;
+         continue;
+       }
+
+      if (c == '\\')
+       {
+         passc = 1;
+         continue;
+       }
+
+      if (c == delim)
+       break;
+    }
+
+  return i;
+}
+
+/* Read the binding command from STRING and perform it.
+   A key binding command looks like: Keyname: function-name\0,
+   a variable binding command looks like: set variable value.
+   A new-style keybinding looks like "\C-x\C-x": exchange-point-and-mark. */
+int
+rl_parse_and_bind (string)
+     char *string;
+{
+  char *funname, *kname;
+  register int c, i;
+  int key, equivalency;
+
+  while (string && whitespace (*string))
+    string++;
+
+  if (string == 0 || *string == 0 || *string == '#')
+    return 0;
+
+  /* If this is a parser directive, act on it. */
+  if (*string == '$')
+    {
+      handle_parser_directive (&string[1]);
+      return 0;
+    }
+
+  /* If we aren't supposed to be parsing right now, then we're done. */
+  if (_rl_parsing_conditionalized_out)
+    return 0;
+
+  i = 0;
+  /* If this keyname is a complex key expression surrounded by quotes,
+     advance to after the matching close quote.  This code allows the
+     backslash to quote characters in the key expression. */
+  if (*string == '"')
+    {
+      i = _rl_skip_to_delim (string, 1, '"');
+
+      /* If we didn't find a closing quote, abort the line. */
+      if (string[i] == '\0')
+        {
+          _rl_init_file_error ("no closing `\"' in key binding");
+          return 1;
+        }
+      else
+        i++;   /* skip past closing double quote */
+    }
+
+  /* Advance to the colon (:) or whitespace which separates the two objects. */
+  for (; (c = string[i]) && c != ':' && c != ' ' && c != '\t'; i++ );
+
+  equivalency = (c == ':' && string[i + 1] == '=');
+
+  /* Mark the end of the command (or keyname). */
+  if (string[i])
+    string[i++] = '\0';
+
+  /* If doing assignment, skip the '=' sign as well. */
+  if (equivalency)
+    string[i++] = '\0';
+
+  /* If this is a command to set a variable, then do that. */
+  if (_rl_stricmp (string, "set") == 0)
+    {
+      char *var, *value, *e;
+      int s;
+
+      var = string + i;
+      /* Make VAR point to start of variable name. */
+      while (*var && whitespace (*var)) var++;
+
+      /* Make VALUE point to start of value string. */
+      value = var;
+      while (*value && whitespace (*value) == 0) value++;
+      if (*value)
+       *value++ = '\0';
+      while (*value && whitespace (*value)) value++;
+
+      /* Strip trailing whitespace from values of boolean variables. */
+      if (find_boolean_var (var) >= 0)
+       {
+         /* remove trailing whitespace */
+remove_trailing:
+         e = value + strlen (value) - 1;
+         while (e >= value && whitespace (*e))
+           e--;
+         e++;          /* skip back to whitespace or EOS */
+         
+         if (*e && e >= value)
+           *e = '\0';
+       }
+      else if ((i = find_string_var (var)) >= 0)
+       {
+         /* Allow quoted strings in variable values */
+         if (*value == '"')
+           {
+             i = _rl_skip_to_delim (value, 1, *value);
+             value[i] = '\0';
+             value++;  /* skip past the quote */
+           }
+         else
+           goto remove_trailing;
+       }
+       
+      rl_variable_bind (var, value);
+      return 0;
+    }
+
+  /* Skip any whitespace between keyname and funname. */
+  for (; string[i] && whitespace (string[i]); i++);
+  funname = &string[i];
+
+  /* Now isolate funname.
+     For straight function names just look for whitespace, since
+     that will signify the end of the string.  But this could be a
+     macro definition.  In that case, the string is quoted, so skip
+     to the matching delimiter.  We allow the backslash to quote the
+     delimiter characters in the macro body. */
+  /* This code exists to allow whitespace in macro expansions, which
+     would otherwise be gobbled up by the next `for' loop.*/
+  /* XXX - it may be desirable to allow backslash quoting only if " is
+     the quoted string delimiter, like the shell. */
+  if (*funname == '\'' || *funname == '"')
+    {
+      i = _rl_skip_to_delim (string, i+1, *funname);
+      if (string[i])
+       i++;
+    }
+
+  /* Advance to the end of the string.  */
+  for (; string[i] && whitespace (string[i]) == 0; i++);
+
+  /* No extra whitespace at the end of the string. */
+  string[i] = '\0';
+
+  /* Handle equivalency bindings here.  Make the left-hand side be exactly
+     whatever the right-hand evaluates to, including keymaps. */
+  if (equivalency)
+    {
+      return 0;
+    }
+
+  /* If this is a new-style key-binding, then do the binding with
+     rl_bind_keyseq ().  Otherwise, let the older code deal with it. */
+  if (*string == '"')
+    {
+      char *seq;
+      register int j, k, passc;
+
+      seq = (char *)xmalloc (1 + strlen (string));
+      for (j = 1, k = passc = 0; string[j]; j++)
+       {
+         /* Allow backslash to quote characters, but leave them in place.
+            This allows a string to end with a backslash quoting another
+            backslash, or with a backslash quoting a double quote.  The
+            backslashes are left in place for rl_translate_keyseq (). */
+         if (passc || (string[j] == '\\'))
+           {
+             seq[k++] = string[j];
+             passc = !passc;
+             continue;
+           }
+
+         if (string[j] == '"')
+           break;
+
+         seq[k++] = string[j];
+       }
+      seq[k] = '\0';
+
+      /* Binding macro? */
+      if (*funname == '\'' || *funname == '"')
+       {
+         j = strlen (funname);
+
+         /* Remove the delimiting quotes from each end of FUNNAME. */
+         if (j && funname[j - 1] == *funname)
+           funname[j - 1] = '\0';
+
+         rl_macro_bind (seq, &funname[1], _rl_keymap);
+       }
+      else
+       rl_bind_keyseq (seq, rl_named_function (funname));
+
+      xfree (seq);
+      return 0;
+    }
+
+  /* Get the actual character we want to deal with. */
+  kname = strrchr (string, '-');
+  if (kname == 0)
+    kname = string;
+  else
+    kname++;
+
+  key = glean_key_from_name (kname);
+
+  /* Add in control and meta bits. */
+  if (substring_member_of_array (string, _rl_possible_control_prefixes))
+    key = CTRL (_rl_to_upper (key));
+
+  if (substring_member_of_array (string, _rl_possible_meta_prefixes))
+    key = META (key);
+
+  /* Temporary.  Handle old-style keyname with macro-binding. */
+  if (*funname == '\'' || *funname == '"')
+    {
+      char useq[2];
+      int fl = strlen (funname);
+
+      useq[0] = key; useq[1] = '\0';
+      if (fl && funname[fl - 1] == *funname)
+       funname[fl - 1] = '\0';
+
+      rl_macro_bind (useq, &funname[1], _rl_keymap);
+    }
+#if defined (PREFIX_META_HACK)
+  /* Ugly, but working hack to keep prefix-meta around. */
+  else if (_rl_stricmp (funname, "prefix-meta") == 0)
+    {
+      char seq[2];
+
+      seq[0] = key;
+      seq[1] = '\0';
+      rl_generic_bind (ISKMAP, seq, (char *)emacs_meta_keymap, _rl_keymap);
+    }
+#endif /* PREFIX_META_HACK */
+  else
+    rl_bind_key (key, rl_named_function (funname));
+  return 0;
+}
+
+/* Simple structure for boolean readline variables (i.e., those that can
+   have one of two values; either "On" or 1 for truth, or "Off" or 0 for
+   false. */
+
+#define V_SPECIAL      0x1
+
+static const struct {
+  const char * const name;
+  int *value;
+  int flags;
+} boolean_varlist [] = {
+  { "bind-tty-special-chars",  &_rl_bind_stty_chars,           0 },
+  { "blink-matching-paren",    &rl_blink_matching_paren,       V_SPECIAL },
+  { "byte-oriented",           &rl_byte_oriented,              0 },
+#if defined (COLOR_SUPPORT)
+  { "colored-completion-prefix",&_rl_colored_completion_prefix,        0 },
+  { "colored-stats",           &_rl_colored_stats,             0 },
+#endif
+  { "completion-ignore-case",  &_rl_completion_case_fold,      0 },
+  { "completion-map-case",     &_rl_completion_case_map,       0 },
+  { "convert-meta",            &_rl_convert_meta_chars_to_ascii, 0 },
+  { "disable-completion",      &rl_inhibit_completion,         0 },
+  { "echo-control-characters", &_rl_echo_control_chars,        0 },
+  { "enable-bracketed-paste",  &_rl_enable_bracketed_paste,    0 },
+  { "enable-keypad",           &_rl_enable_keypad,             0 },
+  { "enable-meta-key",         &_rl_enable_meta,               0 },
+  { "expand-tilde",            &rl_complete_with_tilde_expansion, 0 },
+  { "history-preserve-point",  &_rl_history_preserve_point,    0 },
+  { "horizontal-scroll-mode",  &_rl_horizontal_scroll_mode,    0 },
+  { "input-meta",              &_rl_meta_flag,                 0 },
+  { "mark-directories",                &_rl_complete_mark_directories, 0 },
+  { "mark-modified-lines",     &_rl_mark_modified_lines,       0 },
+  { "mark-symlinked-directories", &_rl_complete_mark_symlink_dirs, 0 },
+  { "match-hidden-files",      &_rl_match_hidden_files,        0 },
+  { "menu-complete-display-prefix", &_rl_menu_complete_prefix_first, 0 },
+  { "meta-flag",               &_rl_meta_flag,                 0 },
+  { "output-meta",             &_rl_output_meta_chars,         0 },
+  { "page-completions",                &_rl_page_completions,          0 },
+  { "prefer-visible-bell",     &_rl_prefer_visible_bell,       V_SPECIAL },
+  { "print-completions-horizontally", &_rl_print_completions_horizontally, 0 },
+  { "revert-all-at-newline",   &_rl_revert_all_at_newline,     0 },
+  { "show-all-if-ambiguous",   &_rl_complete_show_all,         0 },
+  { "show-all-if-unmodified",  &_rl_complete_show_unmodified,  0 },
+  { "show-mode-in-prompt",     &_rl_show_mode_in_prompt,       0 },
+  { "skip-completed-text",     &_rl_skip_completed_text,       0 },
+#if defined (VISIBLE_STATS)
+  { "visible-stats",           &rl_visible_stats,              0 },
+#endif /* VISIBLE_STATS */
+  { (char *)NULL, (int *)NULL, 0 }
+};
+
+static int
+find_boolean_var (name)
+     const char *name;
+{
+  register int i;
+
+  for (i = 0; boolean_varlist[i].name; i++)
+    if (_rl_stricmp (name, boolean_varlist[i].name) == 0)
+      return i;
+  return -1;
+}
+
+/* Hooks for handling special boolean variables, where a
+   function needs to be called or another variable needs
+   to be changed when they're changed. */
+static void
+hack_special_boolean_var (i)
+     int i;
+{
+  const char *name;
+
+  name = boolean_varlist[i].name;
+
+  if (_rl_stricmp (name, "blink-matching-paren") == 0)
+    _rl_enable_paren_matching (rl_blink_matching_paren);
+  else if (_rl_stricmp (name, "prefer-visible-bell") == 0)
+    {
+      if (_rl_prefer_visible_bell)
+       _rl_bell_preference = VISIBLE_BELL;
+      else
+       _rl_bell_preference = AUDIBLE_BELL;
+    }
+  else if (_rl_stricmp (name, "show-mode-in-prompt") == 0)
+    _rl_reset_prompt ();
+}
+
+typedef int _rl_sv_func_t PARAMS((const char *));
+
+/* These *must* correspond to the array indices for the appropriate
+   string variable.  (Though they're not used right now.) */
+#define V_BELLSTYLE    0
+#define V_COMBEGIN     1
+#define V_EDITMODE     2
+#define V_ISRCHTERM    3
+#define V_KEYMAP       4
+
+#define        V_STRING        1
+#define V_INT          2
+
+/* Forward declarations */
+static int sv_bell_style PARAMS((const char *));
+static int sv_combegin PARAMS((const char *));
+static int sv_dispprefix PARAMS((const char *));
+static int sv_compquery PARAMS((const char *));
+static int sv_compwidth PARAMS((const char *));
+static int sv_editmode PARAMS((const char *));
+static int sv_emacs_modestr PARAMS((const char *));
+static int sv_histsize PARAMS((const char *));
+static int sv_isrchterm PARAMS((const char *));
+static int sv_keymap PARAMS((const char *));
+static int sv_seqtimeout PARAMS((const char *));
+static int sv_viins_modestr PARAMS((const char *));
+static int sv_vicmd_modestr PARAMS((const char *));
+
+static const struct {
+  const char * const name;
+  int flags;
+  _rl_sv_func_t *set_func;
+} string_varlist[] = {
+  { "bell-style",      V_STRING,       sv_bell_style },
+  { "comment-begin",   V_STRING,       sv_combegin },
+  { "completion-display-width", V_INT, sv_compwidth },
+  { "completion-prefix-display-length", V_INT, sv_dispprefix },
+  { "completion-query-items", V_INT,   sv_compquery },
+  { "editing-mode",    V_STRING,       sv_editmode },
+  { "emacs-mode-string", V_STRING,     sv_emacs_modestr },  
+  { "history-size",    V_INT,          sv_histsize },
+  { "isearch-terminators", V_STRING,   sv_isrchterm },
+  { "keymap",          V_STRING,       sv_keymap },
+  { "keyseq-timeout",  V_INT,          sv_seqtimeout },
+  { "vi-cmd-mode-string", V_STRING,    sv_vicmd_modestr }, 
+  { "vi-ins-mode-string", V_STRING,    sv_viins_modestr }, 
+  { (char *)NULL,      0, (_rl_sv_func_t *)0 }
+};
+
+static int
+find_string_var (name)
+     const char *name;
+{
+  register int i;
+
+  for (i = 0; string_varlist[i].name; i++)
+    if (_rl_stricmp (name, string_varlist[i].name) == 0)
+      return i;
+  return -1;
+}
+
+/* A boolean value that can appear in a `set variable' command is true if
+   the value is null or empty, `on' (case-insensitive), or "1".  Any other
+   values result in 0 (false). */
+static int
+bool_to_int (value)
+     const char *value;
+{
+  return (value == 0 || *value == '\0' ||
+               (_rl_stricmp (value, "on") == 0) ||
+               (value[0] == '1' && value[1] == '\0'));
+}
+
+char *
+rl_variable_value (name)
+     const char *name;
+{
+  register int i;
+
+  /* Check for simple variables first. */
+  i = find_boolean_var (name);
+  if (i >= 0)
+    return (*boolean_varlist[i].value ? "on" : "off");
+
+  i = find_string_var (name);
+  if (i >= 0)
+    return (_rl_get_string_variable_value (string_varlist[i].name));
+
+  /* Unknown variable names return NULL. */
+  return 0;
+}
+
+int
+rl_variable_bind (name, value)
+     const char *name, *value;
+{
+  register int i;
+  int  v;
+
+  /* Check for simple variables first. */
+  i = find_boolean_var (name);
+  if (i >= 0)
+    {
+      *boolean_varlist[i].value = bool_to_int (value);
+      if (boolean_varlist[i].flags & V_SPECIAL)
+       hack_special_boolean_var (i);
+      return 0;
+    }
+
+  i = find_string_var (name);
+
+  /* For the time being, unknown variable names or string names without a
+     handler function are simply ignored. */
+  if (i < 0 || string_varlist[i].set_func == 0)
+    return 0;
+
+  v = (*string_varlist[i].set_func) (value);
+  return v;
+}
+
+static int
+sv_editmode (value)
+     const char *value;
+{
+  if (_rl_strnicmp (value, "vi", 2) == 0)
+    {
+#if defined (VI_MODE)
+      _rl_keymap = vi_insertion_keymap;
+      rl_editing_mode = vi_mode;
+#endif /* VI_MODE */
+      return 0;
+    }
+  else if (_rl_strnicmp (value, "emacs", 5) == 0)
+    {
+      _rl_keymap = emacs_standard_keymap;
+      rl_editing_mode = emacs_mode;
+      return 0;
+    }
+  return 1;
+}
+
+static int
+sv_combegin (value)
+     const char *value;
+{
+  if (value && *value)
+    {
+      FREE (_rl_comment_begin);
+      _rl_comment_begin = savestring (value);
+      return 0;
+    }
+  return 1;
+}
+
+static int
+sv_dispprefix (value)
+     const char *value;
+{
+  int nval = 0;
+
+  if (value && *value)
+    {
+      nval = atoi (value);
+      if (nval < 0)
+       nval = 0;
+    }
+  _rl_completion_prefix_display_length = nval;
+  return 0;
+}
+
+static int
+sv_compquery (value)
+     const char *value;
+{
+  int nval = 100;
+
+  if (value && *value)
+    {
+      nval = atoi (value);
+      if (nval < 0)
+       nval = 0;
+    }
+  rl_completion_query_items = nval;
+  return 0;
+}
+
+static int
+sv_compwidth (value)
+     const char *value;
+{
+  int nval = -1;
+
+  if (value && *value)
+    nval = atoi (value);
+
+  _rl_completion_columns = nval;
+  return 0;
+}
+
+static int
+sv_histsize (value)
+     const char *value;
+{
+  int nval;
+
+  nval = 500;
+  if (value && *value)
+    {
+      nval = atoi (value);
+      if (nval < 0)
+       {
+         unstifle_history ();
+         return 0;
+       }
+    }
+  stifle_history (nval);
+  return 0;
+}
+
+static int
+sv_keymap (value)
+     const char *value;
+{
+  Keymap kmap;
+
+  kmap = rl_get_keymap_by_name (value);
+  if (kmap)
+    {
+      rl_set_keymap (kmap);
+      return 0;
+    }
+  return 1;
+}
+
+static int
+sv_seqtimeout (value)
+     const char *value;
+{
+  int nval;
+
+  nval = 0;
+  if (value && *value)
+    {
+      nval = atoi (value);
+      if (nval < 0)
+       nval = 0;
+    }
+  _rl_keyseq_timeout = nval;
+  return 0;
+}
+
+static int
+sv_bell_style (value)
+     const char *value;
+{
+  if (value == 0 || *value == '\0')
+    _rl_bell_preference = AUDIBLE_BELL;
+  else if (_rl_stricmp (value, "none") == 0 || _rl_stricmp (value, "off") == 0)
+    _rl_bell_preference = NO_BELL;
+  else if (_rl_stricmp (value, "audible") == 0 || _rl_stricmp (value, "on") == 0)
+    _rl_bell_preference = AUDIBLE_BELL;
+  else if (_rl_stricmp (value, "visible") == 0)
+    _rl_bell_preference = VISIBLE_BELL;
+  else
+    return 1;
+  return 0;
+}
+
+static int
+sv_isrchterm (value)
+     const char *value;
+{
+  int beg, end, delim;
+  char *v;
+
+  if (value == 0)
+    return 1;
+
+  /* Isolate the value and translate it into a character string. */
+  v = savestring (value);
+  FREE (_rl_isearch_terminators);
+  if (v[0] == '"' || v[0] == '\'')
+    {
+      delim = v[0];
+      for (beg = end = 1; v[end] && v[end] != delim; end++)
+       ;
+    }
+  else
+    {
+      for (beg = end = 0; whitespace (v[end]) == 0; end++)
+       ;
+    }
+
+  v[end] = '\0';
+
+  /* The value starts at v + beg.  Translate it into a character string. */
+  _rl_isearch_terminators = (char *)xmalloc (2 * strlen (v) + 1);
+  rl_translate_keyseq (v + beg, _rl_isearch_terminators, &end);
+  _rl_isearch_terminators[end] = '\0';
+
+  xfree (v);
+  return 0;
+}
+
+extern char *_rl_emacs_mode_str;
+
+static int
+sv_emacs_modestr (value)
+     const char *value;
+{
+  if (value && *value)
+    {
+      FREE (_rl_emacs_mode_str);
+      _rl_emacs_mode_str = (char *)xmalloc (2 * strlen (value) + 1);
+      rl_translate_keyseq (value, _rl_emacs_mode_str, &_rl_emacs_modestr_len);
+      _rl_emacs_mode_str[_rl_emacs_modestr_len] = '\0';
+      return 0;
+    }
+  else if (value)
+    {
+      FREE (_rl_emacs_mode_str);
+      _rl_emacs_mode_str = (char *)xmalloc (1);
+      _rl_emacs_mode_str[_rl_emacs_modestr_len = 0] = '\0';
+      return 0;
+    }
+  else if (value == 0)
+    {
+      FREE (_rl_emacs_mode_str);
+      _rl_emacs_mode_str = 0;  /* prompt_modestr does the right thing */
+      _rl_emacs_modestr_len = 0;
+      return 0;
+    }
+  return 1;
+}
+
+static int
+sv_viins_modestr (value)
+     const char *value;
+{
+  if (value && *value)
+    {
+      FREE (_rl_vi_ins_mode_str);
+      _rl_vi_ins_mode_str = (char *)xmalloc (2 * strlen (value) + 1);
+      rl_translate_keyseq (value, _rl_vi_ins_mode_str, &_rl_vi_ins_modestr_len);
+      _rl_vi_ins_mode_str[_rl_vi_ins_modestr_len] = '\0';
+      return 0;
+    }
+  else if (value)
+    {
+      FREE (_rl_vi_ins_mode_str);
+      _rl_vi_ins_mode_str = (char *)xmalloc (1);
+      _rl_vi_ins_mode_str[_rl_vi_ins_modestr_len = 0] = '\0';
+      return 0;
+    }
+  else if (value == 0)
+    {
+      FREE (_rl_vi_ins_mode_str);
+      _rl_vi_ins_mode_str = 0; /* prompt_modestr does the right thing */
+      _rl_vi_ins_modestr_len = 0;
+      return 0;
+    }
+  return 1;
+}
+
+static int
+sv_vicmd_modestr (value)
+     const char *value;
+{
+  if (value && *value)
+    {
+      FREE (_rl_vi_cmd_mode_str);
+      _rl_vi_cmd_mode_str = (char *)xmalloc (2 * strlen (value) + 1);
+      rl_translate_keyseq (value, _rl_vi_cmd_mode_str, &_rl_vi_cmd_modestr_len);
+      _rl_vi_cmd_mode_str[_rl_vi_cmd_modestr_len] = '\0';
+      return 0;
+    }
+  else if (value)
+    {
+      FREE (_rl_vi_cmd_mode_str);
+      _rl_vi_cmd_mode_str = (char *)xmalloc (1);
+      _rl_vi_cmd_mode_str[_rl_vi_cmd_modestr_len = 0] = '\0';
+      return 0;
+    }
+  else if (value == 0)
+    {
+      FREE (_rl_vi_cmd_mode_str);
+      _rl_vi_cmd_mode_str = 0; /* prompt_modestr does the right thing */
+      _rl_vi_cmd_modestr_len = 0;
+      return 0;
+    }
+  return 1;
+}
+
+/* Return the character which matches NAME.
+   For example, `Space' returns ' '. */
+
+typedef struct {
+  const char * const name;
+  int value;
+} assoc_list;
+
+static const assoc_list name_key_alist[] = {
+  { "DEL", 0x7f },
+  { "ESC", '\033' },
+  { "Escape", '\033' },
+  { "LFD", '\n' },
+  { "Newline", '\n' },
+  { "RET", '\r' },
+  { "Return", '\r' },
+  { "Rubout", 0x7f },
+  { "SPC", ' ' },
+  { "Space", ' ' },
+  { "Tab", 0x09 },
+  { (char *)0x0, 0 }
+};
+
+static int
+glean_key_from_name (name)
+     char *name;
+{
+  register int i;
+
+  for (i = 0; name_key_alist[i].name; i++)
+    if (_rl_stricmp (name, name_key_alist[i].name) == 0)
+      return (name_key_alist[i].value);
+
+  return (*(unsigned char *)name);     /* XXX was return (*name) */
+}
+
+/* Auxiliary functions to manage keymaps. */
+static const struct {
+  const char * const name;
+  Keymap map;
+} keymap_names[] = {
+  { "emacs", emacs_standard_keymap },
+  { "emacs-standard", emacs_standard_keymap },
+  { "emacs-meta", emacs_meta_keymap },
+  { "emacs-ctlx", emacs_ctlx_keymap },
+#if defined (VI_MODE)
+  { "vi", vi_movement_keymap },
+  { "vi-move", vi_movement_keymap },
+  { "vi-command", vi_movement_keymap },
+  { "vi-insert", vi_insertion_keymap },
+#endif /* VI_MODE */
+  { (char *)0x0, (Keymap)0x0 }
+};
+
+Keymap
+rl_get_keymap_by_name (name)
+     const char *name;
+{
+  register int i;
+
+  for (i = 0; keymap_names[i].name; i++)
+    if (_rl_stricmp (name, keymap_names[i].name) == 0)
+      return (keymap_names[i].map);
+  return ((Keymap) NULL);
+}
+
+char *
+rl_get_keymap_name (map)
+     Keymap map;
+{
+  register int i;
+  for (i = 0; keymap_names[i].name; i++)
+    if (map == keymap_names[i].map)
+      return ((char *)keymap_names[i].name);
+  return ((char *)NULL);
+}
+  
+void
+rl_set_keymap (map)
+     Keymap map;
+{
+  if (map)
+    _rl_keymap = map;
+}
+
+Keymap
+rl_get_keymap ()
+{
+  return (_rl_keymap);
+}
+
+void
+rl_set_keymap_from_edit_mode ()
+{
+  if (rl_editing_mode == emacs_mode)
+    _rl_keymap = emacs_standard_keymap;
+#if defined (VI_MODE)
+  else if (rl_editing_mode == vi_mode)
+    _rl_keymap = vi_insertion_keymap;
+#endif /* VI_MODE */
+}
+
+char *
+rl_get_keymap_name_from_edit_mode ()
+{
+  if (rl_editing_mode == emacs_mode)
+    return "emacs";
+#if defined (VI_MODE)
+  else if (rl_editing_mode == vi_mode)
+    return "vi";
+#endif /* VI_MODE */
+  else
+    return "none";
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*               Key Binding and Function Information              */
+/*                                                                 */
+/* **************************************************************** */
+
+/* Each of the following functions produces information about the
+   state of keybindings and functions known to Readline.  The info
+   is always printed to rl_outstream, and in such a way that it can
+   be read back in (i.e., passed to rl_parse_and_bind ()). */
+
+/* Print the names of functions known to Readline. */
+void
+rl_list_funmap_names ()
+{
+  register int i;
+  const char **funmap_names;
+
+  funmap_names = rl_funmap_names ();
+
+  if (!funmap_names)
+    return;
+
+  for (i = 0; funmap_names[i]; i++)
+    fprintf (rl_outstream, "%s\n", funmap_names[i]);
+
+  xfree (funmap_names);
+}
+
+static char *
+_rl_get_keyname (key)
+     int key;
+{
+  char *keyname;
+  int i, c;
+
+  keyname = (char *)xmalloc (8);
+
+  c = key;
+  /* Since this is going to be used to write out keysequence-function
+     pairs for possible inclusion in an inputrc file, we don't want to
+     do any special meta processing on KEY. */
+
+#if 1
+  /* XXX - Experimental */
+  /* We might want to do this, but the old version of the code did not. */
+
+  /* If this is an escape character, we don't want to do any more processing.
+     Just add the special ESC key sequence and return. */
+  if (c == ESC)
+    {
+      keyname[0] = '\\';
+      keyname[1] = 'e';
+      keyname[2] = '\0';
+      return keyname;
+    }
+#endif
+
+  /* RUBOUT is translated directly into \C-? */
+  if (key == RUBOUT)
+    {
+      keyname[0] = '\\';
+      keyname[1] = 'C';
+      keyname[2] = '-';
+      keyname[3] = '?';
+      keyname[4] = '\0';
+      return keyname;
+    }
+
+  i = 0;
+  /* Now add special prefixes needed for control characters.  This can
+     potentially change C. */
+  if (CTRL_CHAR (c))
+    {
+      keyname[i++] = '\\';
+      keyname[i++] = 'C';
+      keyname[i++] = '-';
+      c = _rl_to_lower (UNCTRL (c));
+    }
+
+  /* XXX experimental code.  Turn the characters that are not ASCII or
+     ISO Latin 1 (128 - 159) into octal escape sequences (\200 - \237).
+     This changes C. */
+  if (c >= 128 && c <= 159)
+    {
+      keyname[i++] = '\\';
+      keyname[i++] = '2';
+      c -= 128;
+      keyname[i++] = (c / 8) + '0';
+      c = (c % 8) + '0';
+    }
+
+  /* Now, if the character needs to be quoted with a backslash, do that. */
+  if (c == '\\' || c == '"')
+    keyname[i++] = '\\';
+
+  /* Now add the key, terminate the string, and return it. */
+  keyname[i++] = (char) c;
+  keyname[i] = '\0';
+
+  return keyname;
+}
+
+/* Return a NULL terminated array of strings which represent the key
+   sequences that are used to invoke FUNCTION in MAP. */
+char **
+rl_invoking_keyseqs_in_map (function, map)
+     rl_command_func_t *function;
+     Keymap map;
+{
+  register int key;
+  char **result;
+  int result_index, result_size;
+
+  result = (char **)NULL;
+  result_index = result_size = 0;
+
+  for (key = 0; key < KEYMAP_SIZE; key++)
+    {
+      switch (map[key].type)
+       {
+       case ISMACR:
+         /* Macros match, if, and only if, the pointers are identical.
+            Thus, they are treated exactly like functions in here. */
+       case ISFUNC:
+         /* If the function in the keymap is the one we are looking for,
+            then add the current KEY to the list of invoking keys. */
+         if (map[key].function == function)
+           {
+             char *keyname;
+
+             keyname = _rl_get_keyname (key);
+
+             if (result_index + 2 > result_size)
+               {
+                 result_size += 10;
+                 result = (char **)xrealloc (result, result_size * sizeof (char *));
+               }
+
+             result[result_index++] = keyname;
+             result[result_index] = (char *)NULL;
+           }
+         break;
+
+       case ISKMAP:
+         {
+           char **seqs;
+           register int i;
+
+           /* Find the list of keyseqs in this map which have FUNCTION as
+              their target.  Add the key sequences found to RESULT. */
+           if (map[key].function)
+             seqs =
+               rl_invoking_keyseqs_in_map (function, FUNCTION_TO_KEYMAP (map, key));
+           else
+             break;
+
+           if (seqs == 0)
+             break;
+
+           for (i = 0; seqs[i]; i++)
+             {
+               char *keyname = (char *)xmalloc (6 + strlen (seqs[i]));
+
+               if (key == ESC)
+                 {
+                   /* If ESC is the meta prefix and we're converting chars
+                      with the eighth bit set to ESC-prefixed sequences, then
+                      we can use \M-.  Otherwise we need to use the sequence
+                      for ESC. */
+                   if (_rl_convert_meta_chars_to_ascii && map[ESC].type == ISKMAP)
+                     sprintf (keyname, "\\M-");
+                   else
+                     sprintf (keyname, "\\e");
+                 }
+               else if (CTRL_CHAR (key))
+                 sprintf (keyname, "\\C-%c", _rl_to_lower (UNCTRL (key)));
+               else if (key == RUBOUT)
+                 sprintf (keyname, "\\C-?");
+               else if (key == '\\' || key == '"')
+                 {
+                   keyname[0] = '\\';
+                   keyname[1] = (char) key;
+                   keyname[2] = '\0';
+                 }
+               else
+                 {
+                   keyname[0] = (char) key;
+                   keyname[1] = '\0';
+                 }
+               
+               strcat (keyname, seqs[i]);
+               xfree (seqs[i]);
+
+               if (result_index + 2 > result_size)
+                 {
+                   result_size += 10;
+                   result = (char **)xrealloc (result, result_size * sizeof (char *));
+                 }
+
+               result[result_index++] = keyname;
+               result[result_index] = (char *)NULL;
+             }
+
+           xfree (seqs);
+         }
+         break;
+       }
+    }
+  return (result);
+}
+
+/* Return a NULL terminated array of strings which represent the key
+   sequences that can be used to invoke FUNCTION using the current keymap. */
+char **
+rl_invoking_keyseqs (function)
+     rl_command_func_t *function;
+{
+  return (rl_invoking_keyseqs_in_map (function, _rl_keymap));
+}
+
+/* Print all of the functions and their bindings to rl_outstream.  If
+   PRINT_READABLY is non-zero, then print the output in such a way
+   that it can be read back in. */
+void
+rl_function_dumper (print_readably)
+     int print_readably;
+{
+  register int i;
+  const char **names;
+  const char *name;
+
+  names = rl_funmap_names ();
+
+  fprintf (rl_outstream, "\n");
+
+  for (i = 0; name = names[i]; i++)
+    {
+      rl_command_func_t *function;
+      char **invokers;
+
+      function = rl_named_function (name);
+      invokers = rl_invoking_keyseqs_in_map (function, _rl_keymap);
+
+      if (print_readably)
+       {
+         if (!invokers)
+           fprintf (rl_outstream, "# %s (not bound)\n", name);
+         else
+           {
+             register int j;
+
+             for (j = 0; invokers[j]; j++)
+               {
+                 fprintf (rl_outstream, "\"%s\": %s\n",
+                          invokers[j], name);
+                 xfree (invokers[j]);
+               }
+
+             xfree (invokers);
+           }
+       }
+      else
+       {
+         if (!invokers)
+           fprintf (rl_outstream, "%s is not bound to any keys\n",
+                    name);
+         else
+           {
+             register int j;
+
+             fprintf (rl_outstream, "%s can be found on ", name);
+
+             for (j = 0; invokers[j] && j < 5; j++)
+               {
+                 fprintf (rl_outstream, "\"%s\"%s", invokers[j],
+                          invokers[j + 1] ? ", " : ".\n");
+               }
+
+             if (j == 5 && invokers[j])
+               fprintf (rl_outstream, "...\n");
+
+             for (j = 0; invokers[j]; j++)
+               xfree (invokers[j]);
+
+             xfree (invokers);
+           }
+       }
+    }
+
+  xfree (names);
+}
+
+/* Print all of the current functions and their bindings to
+   rl_outstream.  If an explicit argument is given, then print
+   the output in such a way that it can be read back in. */
+int
+rl_dump_functions (count, key)
+     int count, key;
+{
+  if (rl_dispatching)
+    fprintf (rl_outstream, "\r\n");
+  rl_function_dumper (rl_explicit_arg);
+  rl_on_new_line ();
+  return (0);
+}
+
+static void
+_rl_macro_dumper_internal (print_readably, map, prefix)
+     int print_readably;
+     Keymap map;
+     char *prefix;
+{
+  register int key;
+  char *keyname, *out;
+  int prefix_len;
+
+  for (key = 0; key < KEYMAP_SIZE; key++)
+    {
+      switch (map[key].type)
+       {
+       case ISMACR:
+         keyname = _rl_get_keyname (key);
+         out = _rl_untranslate_macro_value ((char *)map[key].function, 0);
+
+         if (print_readably)
+           fprintf (rl_outstream, "\"%s%s\": \"%s\"\n", prefix ? prefix : "",
+                                                        keyname,
+                                                        out ? out : "");
+         else
+           fprintf (rl_outstream, "%s%s outputs %s\n", prefix ? prefix : "",
+                                                       keyname,
+                                                       out ? out : "");
+         xfree (keyname);
+         xfree (out);
+         break;
+       case ISFUNC:
+         break;
+       case ISKMAP:
+         prefix_len = prefix ? strlen (prefix) : 0;
+         if (key == ESC)
+           {
+             keyname = (char *)xmalloc (3 + prefix_len);
+             if (prefix)
+               strcpy (keyname, prefix);
+             keyname[prefix_len] = '\\';
+             keyname[prefix_len + 1] = 'e';
+             keyname[prefix_len + 2] = '\0';
+           }
+         else
+           {
+             keyname = _rl_get_keyname (key);
+             if (prefix)
+               {
+                 out = (char *)xmalloc (strlen (keyname) + prefix_len + 1);
+                 strcpy (out, prefix);
+                 strcpy (out + prefix_len, keyname);
+                 xfree (keyname);
+                 keyname = out;
+               }
+           }
+
+         _rl_macro_dumper_internal (print_readably, FUNCTION_TO_KEYMAP (map, key), keyname);
+         xfree (keyname);
+         break;
+       }
+    }
+}
+
+void
+rl_macro_dumper (print_readably)
+     int print_readably;
+{
+  _rl_macro_dumper_internal (print_readably, _rl_keymap, (char *)NULL);
+}
+
+int
+rl_dump_macros (count, key)
+     int count, key;
+{
+  if (rl_dispatching)
+    fprintf (rl_outstream, "\r\n");
+  rl_macro_dumper (rl_explicit_arg);
+  rl_on_new_line ();
+  return (0);
+}
+
+static char *
+_rl_get_string_variable_value (name)
+     const char *name;
+{
+  static char numbuf[32];
+  char *ret;
+
+  if (_rl_stricmp (name, "bell-style") == 0)
+    {
+      switch (_rl_bell_preference)
+       {
+         case NO_BELL:
+           return "none";
+         case VISIBLE_BELL:
+           return "visible";
+         case AUDIBLE_BELL:
+         default:
+           return "audible";
+       }
+    }
+  else if (_rl_stricmp (name, "comment-begin") == 0)
+    return (_rl_comment_begin ? _rl_comment_begin : RL_COMMENT_BEGIN_DEFAULT);
+  else if (_rl_stricmp (name, "completion-display-width") == 0)
+    {
+      sprintf (numbuf, "%d", _rl_completion_columns);
+      return (numbuf);
+    }
+  else if (_rl_stricmp (name, "completion-prefix-display-length") == 0)
+    {
+      sprintf (numbuf, "%d", _rl_completion_prefix_display_length);
+      return (numbuf);
+    }
+  else if (_rl_stricmp (name, "completion-query-items") == 0)
+    {
+      sprintf (numbuf, "%d", rl_completion_query_items);
+      return (numbuf);
+    }
+  else if (_rl_stricmp (name, "editing-mode") == 0)
+    return (rl_get_keymap_name_from_edit_mode ());
+  else if (_rl_stricmp (name, "history-size") == 0)
+    {
+      sprintf (numbuf, "%d", history_is_stifled() ? history_max_entries : 0);
+      return (numbuf);
+    }
+  else if (_rl_stricmp (name, "isearch-terminators") == 0)
+    {
+      if (_rl_isearch_terminators == 0)
+       return 0;
+      ret = _rl_untranslate_macro_value (_rl_isearch_terminators, 0);
+      if (ret)
+       {
+         strncpy (numbuf, ret, sizeof (numbuf) - 1);
+         xfree (ret);
+         numbuf[sizeof(numbuf) - 1] = '\0';
+       }
+      else
+       numbuf[0] = '\0';
+      return numbuf;
+    }
+  else if (_rl_stricmp (name, "keymap") == 0)
+    {
+      ret = rl_get_keymap_name (_rl_keymap);
+      if (ret == 0)
+       ret = rl_get_keymap_name_from_edit_mode ();
+      return (ret ? ret : "none");
+    }
+  else if (_rl_stricmp (name, "keyseq-timeout") == 0)
+    {
+      sprintf (numbuf, "%d", _rl_keyseq_timeout);    
+      return (numbuf);
+    }
+  else if (_rl_stricmp (name, "emacs-mode-string") == 0)
+    return (_rl_emacs_mode_str ? _rl_emacs_mode_str : RL_EMACS_MODESTR_DEFAULT);
+  else if (_rl_stricmp (name, "vi-cmd-mode-string") == 0)
+    return (_rl_emacs_mode_str ? _rl_emacs_mode_str : RL_VI_CMD_MODESTR_DEFAULT);
+  else if (_rl_stricmp (name, "vi-ins-mode-string") == 0)
+    return (_rl_emacs_mode_str ? _rl_emacs_mode_str : RL_VI_INS_MODESTR_DEFAULT);
+  else
+    return (0);
+}
+
+void
+rl_variable_dumper (print_readably)
+     int print_readably;
+{
+  int i;
+  char *v;
+
+  for (i = 0; boolean_varlist[i].name; i++)
+    {
+      if (print_readably)
+        fprintf (rl_outstream, "set %s %s\n", boolean_varlist[i].name,
+                              *boolean_varlist[i].value ? "on" : "off");
+      else
+        fprintf (rl_outstream, "%s is set to `%s'\n", boolean_varlist[i].name,
+                              *boolean_varlist[i].value ? "on" : "off");
+    }
+
+  for (i = 0; string_varlist[i].name; i++)
+    {
+      v = _rl_get_string_variable_value (string_varlist[i].name);
+      if (v == 0)      /* _rl_isearch_terminators can be NULL */
+       continue;
+      if (print_readably)
+        fprintf (rl_outstream, "set %s %s\n", string_varlist[i].name, v);
+      else
+        fprintf (rl_outstream, "%s is set to `%s'\n", string_varlist[i].name, v);
+    }
+}
+
+/* Print all of the current variables and their values to
+   rl_outstream.  If an explicit argument is given, then print
+   the output in such a way that it can be read back in. */
+int
+rl_dump_variables (count, key)
+     int count, key;
+{
+  if (rl_dispatching)
+    fprintf (rl_outstream, "\r\n");
+  rl_variable_dumper (rl_explicit_arg);
+  rl_on_new_line ();
+  return (0);
+}
+
+/* Return non-zero if any members of ARRAY are a substring in STRING. */
+static int
+substring_member_of_array (string, array)
+     const char *string;
+     const char * const *array;
+{
+  while (*array)
+    {
+      if (_rl_strindex (string, *array))
+       return (1);
+      array++;
+    }
+  return (0);
+}
index 1fa1b089d0164c6794f3b3ff14bc0afb101c0d84..43aab7a69e89f3156af2010fdfe829056ae5c504 100644 (file)
@@ -73,7 +73,7 @@
 #endif
 
 #if !defined (isxdigit) && !defined (HAVE_ISXDIGIT) && !defined (__cplusplus)
-#  define isxdigit(c)   (isdigit((c)) || ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F'))
+#  define isxdigit(c)   (isdigit((unsigned char)(c)) || ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F'))
 #endif
 
 #if defined (CTYPE_NON_ASCII)
 
 /* Beware:  these only work with single-byte ASCII characters. */
 
-#define ISALNUM(c)     (IN_CTYPE_DOMAIN (c) && isalnum (c))
-#define ISALPHA(c)     (IN_CTYPE_DOMAIN (c) && isalpha (c))
-#define ISDIGIT(c)     (IN_CTYPE_DOMAIN (c) && isdigit (c))
-#define ISLOWER(c)     (IN_CTYPE_DOMAIN (c) && islower (c))
-#define ISPRINT(c)     (IN_CTYPE_DOMAIN (c) && isprint (c))
-#define ISUPPER(c)     (IN_CTYPE_DOMAIN (c) && isupper (c))
-#define ISXDIGIT(c)    (IN_CTYPE_DOMAIN (c) && isxdigit (c))
+#define ISALNUM(c)     (IN_CTYPE_DOMAIN (c) && isalnum ((unsigned char)c))
+#define ISALPHA(c)     (IN_CTYPE_DOMAIN (c) && isalpha ((unsigned char)c))
+#define ISDIGIT(c)     (IN_CTYPE_DOMAIN (c) && isdigit ((unsigned char)c))
+#define ISLOWER(c)     (IN_CTYPE_DOMAIN (c) && islower ((unsigned char)c))
+#define ISPRINT(c)     (IN_CTYPE_DOMAIN (c) && isprint ((unsigned char)c))
+#define ISUPPER(c)     (IN_CTYPE_DOMAIN (c) && isupper ((unsigned char)c))
+#define ISXDIGIT(c)    (IN_CTYPE_DOMAIN (c) && isxdigit ((unsigned char)c))
 
 #define _rl_lowercase_p(c)     (NON_NEGATIVE(c) && ISLOWER(c))
 #define _rl_uppercase_p(c)     (NON_NEGATIVE(c) && ISUPPER(c))
diff --git a/lib/readline/chardefs.h~ b/lib/readline/chardefs.h~
new file mode 100644 (file)
index 0000000..1fa1b08
--- /dev/null
@@ -0,0 +1,164 @@
+/* chardefs.h -- Character definitions for readline. */
+
+/* Copyright (C) 1994-2009 Free Software Foundation, Inc.
+
+   This file is part of the GNU Readline Library (Readline), a library
+   for reading lines of text with interactive input and history editing.
+
+   Readline is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   Readline 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 Readline.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _CHARDEFS_H_
+#define _CHARDEFS_H_
+
+#include <ctype.h>
+
+#if defined (HAVE_CONFIG_H)
+#  if defined (HAVE_STRING_H)
+#    if ! defined (STDC_HEADERS) && defined (HAVE_MEMORY_H)
+#      include <memory.h>
+#    endif
+#    include <string.h>
+#  endif /* HAVE_STRING_H */
+#  if defined (HAVE_STRINGS_H)
+#    include <strings.h>
+#  endif /* HAVE_STRINGS_H */
+#else
+#  include <string.h>
+#endif /* !HAVE_CONFIG_H */
+
+#ifndef whitespace
+#define whitespace(c) (((c) == ' ') || ((c) == '\t'))
+#endif
+
+#ifdef CTRL
+#  undef CTRL
+#endif
+#ifdef UNCTRL
+#  undef UNCTRL
+#endif
+
+/* Some character stuff. */
+#define control_character_threshold 0x020   /* Smaller than this is control. */
+#define control_character_mask 0x1f        /* 0x20 - 1 */
+#define meta_character_threshold 0x07f     /* Larger than this is Meta. */
+#define control_character_bit 0x40         /* 0x000000, must be off. */
+#define meta_character_bit 0x080           /* x0000000, must be on. */
+#define largest_char 255                   /* Largest character value. */
+
+#define CTRL_CHAR(c) ((c) < control_character_threshold && (((c) & 0x80) == 0))
+#define META_CHAR(c) ((c) > meta_character_threshold && (c) <= largest_char)
+
+#define CTRL(c) ((c) & control_character_mask)
+#define META(c) ((c) | meta_character_bit)
+
+#define UNMETA(c) ((c) & (~meta_character_bit))
+#define UNCTRL(c) _rl_to_upper(((c)|control_character_bit))
+
+#if defined STDC_HEADERS || (!defined (isascii) && !defined (HAVE_ISASCII))
+#  define IN_CTYPE_DOMAIN(c) 1
+#else
+#  define IN_CTYPE_DOMAIN(c) isascii(c)
+#endif
+
+#if !defined (isxdigit) && !defined (HAVE_ISXDIGIT) && !defined (__cplusplus)
+#  define isxdigit(c)   (isdigit((c)) || ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F'))
+#endif
+
+#if defined (CTYPE_NON_ASCII)
+#  define NON_NEGATIVE(c) 1
+#else
+#  define NON_NEGATIVE(c) ((unsigned char)(c) == (c))
+#endif
+
+/* Some systems define these; we want our definitions. */
+#undef ISPRINT
+
+/* Beware:  these only work with single-byte ASCII characters. */
+
+#define ISALNUM(c)     (IN_CTYPE_DOMAIN (c) && isalnum (c))
+#define ISALPHA(c)     (IN_CTYPE_DOMAIN (c) && isalpha (c))
+#define ISDIGIT(c)     (IN_CTYPE_DOMAIN (c) && isdigit (c))
+#define ISLOWER(c)     (IN_CTYPE_DOMAIN (c) && islower (c))
+#define ISPRINT(c)     (IN_CTYPE_DOMAIN (c) && isprint (c))
+#define ISUPPER(c)     (IN_CTYPE_DOMAIN (c) && isupper (c))
+#define ISXDIGIT(c)    (IN_CTYPE_DOMAIN (c) && isxdigit (c))
+
+#define _rl_lowercase_p(c)     (NON_NEGATIVE(c) && ISLOWER(c))
+#define _rl_uppercase_p(c)     (NON_NEGATIVE(c) && ISUPPER(c))
+#define _rl_digit_p(c)         ((c) >= '0' && (c) <= '9')
+
+#define _rl_pure_alphabetic(c) (NON_NEGATIVE(c) && ISALPHA(c))
+#define ALPHABETIC(c)          (NON_NEGATIVE(c) && ISALNUM(c))
+
+#ifndef _rl_to_upper
+#  define _rl_to_upper(c) (_rl_lowercase_p(c) ? toupper((unsigned char)c) : (c))
+#  define _rl_to_lower(c) (_rl_uppercase_p(c) ? tolower((unsigned char)c) : (c))
+#endif
+
+#ifndef _rl_digit_value
+#  define _rl_digit_value(x) ((x) - '0')
+#endif
+
+#ifndef _rl_isident
+#  define _rl_isident(c) (ISALNUM(c) || (c) == '_')
+#endif
+
+#ifndef ISOCTAL
+#  define ISOCTAL(c)   ((c) >= '0' && (c) <= '7')
+#endif
+#define OCTVALUE(c)    ((c) - '0')
+
+#define HEXVALUE(c) \
+  (((c) >= 'a' && (c) <= 'f') \
+       ? (c)-'a'+10 \
+       : (c) >= 'A' && (c) <= 'F' ? (c)-'A'+10 : (c)-'0')
+
+#ifndef NEWLINE
+#define NEWLINE '\n'
+#endif
+
+#ifndef RETURN
+#define RETURN CTRL('M')
+#endif
+
+#ifndef RUBOUT
+#define RUBOUT 0x7f
+#endif
+
+#ifndef TAB
+#define TAB '\t'
+#endif
+
+#ifdef ABORT_CHAR
+#undef ABORT_CHAR
+#endif
+#define ABORT_CHAR CTRL('G')
+
+#ifdef PAGE
+#undef PAGE
+#endif
+#define PAGE CTRL('L')
+
+#ifdef SPACE
+#undef SPACE
+#endif
+#define SPACE ' '      /* XXX - was 0x20 */
+
+#ifdef ESC
+#undef ESC
+#endif
+#define ESC CTRL('[')
+
+#endif  /* _CHARDEFS_H_ */
index 8341c2087b8d451ecb284df34d5a9fbc59dfa8be..aaab752562df5dccb833f909bc4a6558e6a66de5 100644 (file)
@@ -114,7 +114,7 @@ int history_lines_written_to_file = 0;
 
 /* Does S look like the beginning of a history timestamp entry?  Placeholder
    for more extensive tests. */
-#define HIST_TIMESTAMP_START(s)                (*(s) == history_comment_char && isdigit ((s)[1]) )
+#define HIST_TIMESTAMP_START(s)                (*(s) == history_comment_char && isdigit ((unsigned char)(s)[1]) )
 
 /* Return the string that should be used in the place of this
    filename.  This only matters when you don't specify the
diff --git a/lib/readline/histfile.c~ b/lib/readline/histfile.c~
new file mode 100644 (file)
index 0000000..8341c20
--- /dev/null
@@ -0,0 +1,650 @@
+/* histfile.c - functions to manipulate the history file. */
+
+/* Copyright (C) 1989-2010 Free Software Foundation, Inc.
+
+   This file contains the GNU History Library (History), a set of
+   routines for managing the text of previously typed lines.
+
+   History is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   History 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 History.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* The goal is to make the implementation transparent, so that you
+   don't have to know what data types are used, just what functions
+   you can call.  I think I have done that. */
+
+#define READLINE_LIBRARY
+
+#if defined (__TANDEM)
+#  include <floss.h>
+#endif
+
+#if defined (HAVE_CONFIG_H)
+#  include <config.h>
+#endif
+
+#include <stdio.h>
+
+#include <sys/types.h>
+#if ! defined (_MINIX) && defined (HAVE_SYS_FILE_H)
+#  include <sys/file.h>
+#endif
+#include "posixstat.h"
+#include <fcntl.h>
+
+#if defined (HAVE_STDLIB_H)
+#  include <stdlib.h>
+#else
+#  include "ansi_stdlib.h"
+#endif /* HAVE_STDLIB_H */
+
+#if defined (HAVE_UNISTD_H)
+#  include <unistd.h>
+#endif
+
+#include <ctype.h>
+
+#if defined (__EMX__)
+#  undef HAVE_MMAP
+#endif
+
+#ifdef HISTORY_USE_MMAP
+#  include <sys/mman.h>
+
+#  ifdef MAP_FILE
+#    define MAP_RFLAGS (MAP_FILE|MAP_PRIVATE)
+#    define MAP_WFLAGS (MAP_FILE|MAP_SHARED)
+#  else
+#    define MAP_RFLAGS MAP_PRIVATE
+#    define MAP_WFLAGS MAP_SHARED
+#  endif
+
+#  ifndef MAP_FAILED
+#    define MAP_FAILED ((void *)-1)
+#  endif
+
+#endif /* HISTORY_USE_MMAP */
+
+/* If we're compiling for __EMX__ (OS/2) or __CYGWIN__ (cygwin32 environment
+   on win 95/98/nt), we want to open files with O_BINARY mode so that there
+   is no \n -> \r\n conversion performed.  On other systems, we don't want to
+   mess around with O_BINARY at all, so we ensure that it's defined to 0. */
+#if defined (__EMX__) || defined (__CYGWIN__)
+#  ifndef O_BINARY
+#    define O_BINARY 0
+#  endif
+#else /* !__EMX__ && !__CYGWIN__ */
+#  undef O_BINARY
+#  define O_BINARY 0
+#endif /* !__EMX__ && !__CYGWIN__ */
+
+#include <errno.h>
+#if !defined (errno)
+extern int errno;
+#endif /* !errno */
+
+#include "history.h"
+#include "histlib.h"
+
+#include "rlshell.h"
+#include "xmalloc.h"
+
+/* If non-zero, we write timestamps to the history file in history_do_write() */
+int history_write_timestamps = 0;
+
+/* Immediately after a call to read_history() or read_history_range(), this
+   will return the number of lines just read from the history file in that
+   call. */
+int history_lines_read_from_file = 0;
+
+/* Immediately after a call to write_history() or history_do_write(), this
+   will return the number of lines just written to the history file in that
+   call.  This also works with history_truncate_file. */
+int history_lines_written_to_file = 0;
+
+/* Does S look like the beginning of a history timestamp entry?  Placeholder
+   for more extensive tests. */
+#define HIST_TIMESTAMP_START(s)                (*(s) == history_comment_char && isdigit ((s)[1]) )
+
+/* Return the string that should be used in the place of this
+   filename.  This only matters when you don't specify the
+   filename to read_history (), or write_history (). */
+static char *
+history_filename (filename)
+     const char *filename;
+{
+  char *return_val;
+  const char *home;
+  int home_len;
+
+  return_val = filename ? savestring (filename) : (char *)NULL;
+
+  if (return_val)
+    return (return_val);
+  
+  home = sh_get_env_value ("HOME");
+#if defined (_WIN32)
+  if (home == 0)
+    home = sh_get_env_value ("APPDATA");
+#endif
+
+  if (home == 0)
+    return (NULL);
+  else
+    home_len = strlen (home);
+
+  return_val = (char *)xmalloc (2 + home_len + 8); /* strlen(".history") == 8 */
+  strcpy (return_val, home);
+  return_val[home_len] = '/';
+#if defined (__MSDOS__)
+  strcpy (return_val + home_len + 1, "_history");
+#else
+  strcpy (return_val + home_len + 1, ".history");
+#endif
+
+  return (return_val);
+}
+
+static char *
+history_backupfile (filename)
+     const char *filename;
+{
+  char *ret;
+  size_t len;
+
+  len = strlen (filename);
+  ret = xmalloc (len + 2);
+  strcpy (ret, filename);
+  ret[len] = '-';
+  ret[len+1] = '\0';
+  return ret;
+}
+  
+/* Add the contents of FILENAME to the history list, a line at a time.
+   If FILENAME is NULL, then read from ~/.history.  Returns 0 if
+   successful, or errno if not. */
+int
+read_history (filename)
+     const char *filename;
+{
+  return (read_history_range (filename, 0, -1));
+}
+
+/* Read a range of lines from FILENAME, adding them to the history list.
+   Start reading at the FROM'th line and end at the TO'th.  If FROM
+   is zero, start at the beginning.  If TO is less than FROM, read
+   until the end of the file.  If FILENAME is NULL, then read from
+   ~/.history.  Returns 0 if successful, or errno if not. */
+int
+read_history_range (filename, from, to)
+     const char *filename;
+     int from, to;
+{
+  register char *line_start, *line_end, *p;
+  char *input, *buffer, *bufend, *last_ts;
+  int file, current_line, chars_read;
+  struct stat finfo;
+  size_t file_size;
+#if defined (EFBIG)
+  int overflow_errno = EFBIG;
+#elif defined (EOVERFLOW)
+  int overflow_errno = EOVERFLOW;
+#else
+  int overflow_errno = EIO;
+#endif
+
+  history_lines_read_from_file = 0;
+
+  buffer = last_ts = (char *)NULL;
+  input = history_filename (filename);
+  file = input ? open (input, O_RDONLY|O_BINARY, 0666) : -1;
+
+  if ((file < 0) || (fstat (file, &finfo) == -1))
+    goto error_and_exit;
+
+  file_size = (size_t)finfo.st_size;
+
+  /* check for overflow on very large files */
+  if (file_size != finfo.st_size || file_size + 1 < file_size)
+    {
+      errno = overflow_errno;
+      goto error_and_exit;
+    }
+
+#ifdef HISTORY_USE_MMAP
+  /* We map read/write and private so we can change newlines to NULs without
+     affecting the underlying object. */
+  buffer = (char *)mmap (0, file_size, PROT_READ|PROT_WRITE, MAP_RFLAGS, file, 0);
+  if ((void *)buffer == MAP_FAILED)
+    {
+      errno = overflow_errno;
+      goto error_and_exit;
+    }
+  chars_read = file_size;
+#else
+  buffer = (char *)malloc (file_size + 1);
+  if (buffer == 0)
+    {
+      errno = overflow_errno;
+      goto error_and_exit;
+    }
+
+  chars_read = read (file, buffer, file_size);
+#endif
+  if (chars_read < 0)
+    {
+  error_and_exit:
+      if (errno != 0)
+       chars_read = errno;
+      else
+       chars_read = EIO;
+      if (file >= 0)
+       close (file);
+
+      FREE (input);
+#ifndef HISTORY_USE_MMAP
+      FREE (buffer);
+#endif
+
+      return (chars_read);
+    }
+
+  close (file);
+
+  /* Set TO to larger than end of file if negative. */
+  if (to < 0)
+    to = chars_read;
+
+  /* Start at beginning of file, work to end. */
+  bufend = buffer + chars_read;
+  current_line = 0;
+
+  /* Skip lines until we are at FROM. */
+  for (line_start = line_end = buffer; line_end < bufend && current_line < from; line_end++)
+    if (*line_end == '\n')
+      {
+       p = line_end + 1;
+       /* If we see something we think is a timestamp, continue with this
+          line.  We should check more extensively here... */
+       if (HIST_TIMESTAMP_START(p) == 0)
+         current_line++;
+       line_start = p;
+      }
+
+  /* If there are lines left to gobble, then gobble them now. */
+  for (line_end = line_start; line_end < bufend; line_end++)
+    if (*line_end == '\n')
+      {
+       /* Change to allow Windows-like \r\n end of line delimiter. */
+       if (line_end > line_start && line_end[-1] == '\r')
+         line_end[-1] = '\0';
+       else
+         *line_end = '\0';
+
+       if (*line_start)
+         {
+           if (HIST_TIMESTAMP_START(line_start) == 0)
+             {
+               add_history (line_start);
+               if (last_ts)
+                 {
+                   add_history_time (last_ts);
+                   last_ts = NULL;
+                 }
+             }
+           else
+             {
+               last_ts = line_start;
+               current_line--;
+             }
+         }
+
+       current_line++;
+
+       if (current_line >= to)
+         break;
+
+       line_start = line_end + 1;
+      }
+
+  history_lines_read_from_file = current_line;
+
+  FREE (input);
+#ifndef HISTORY_USE_MMAP
+  FREE (buffer);
+#else
+  munmap (buffer, file_size);
+#endif
+
+  return (0);
+}
+
+/* Truncate the history file FNAME, leaving only LINES trailing lines.
+   If FNAME is NULL, then use ~/.history.  Returns 0 on success, errno
+   on failure. */
+int
+history_truncate_file (fname, lines)
+     const char *fname;
+     int lines;
+{
+  char *buffer, *filename, *bakname, *bp, *bp1;                /* bp1 == bp+1 */
+  int file, chars_read, rv, orig_lines, exists;
+  struct stat finfo;
+  size_t file_size;
+
+  history_lines_written_to_file = 0;
+
+  buffer = (char *)NULL;
+  filename = history_filename (fname);
+  bakname = 0;
+  file = filename ? open (filename, O_RDONLY|O_BINARY, 0666) : -1;
+  rv = exists = 0;
+
+  /* Don't try to truncate non-regular files. */
+  if (file == -1 || fstat (file, &finfo) == -1)
+    {
+      rv = errno;
+      if (file != -1)
+       close (file);
+      goto truncate_exit;
+    }
+  exists = 1;
+
+  if (S_ISREG (finfo.st_mode) == 0)
+    {
+      close (file);
+#ifdef EFTYPE
+      rv = EFTYPE;
+#else
+      rv = EINVAL;
+#endif
+      goto truncate_exit;
+    }
+
+  file_size = (size_t)finfo.st_size;
+
+  /* check for overflow on very large files */
+  if (file_size != finfo.st_size || file_size + 1 < file_size)
+    {
+      close (file);
+#if defined (EFBIG)
+      rv = errno = EFBIG;
+#elif defined (EOVERFLOW)
+      rv = errno = EOVERFLOW;
+#else
+      rv = errno = EINVAL;
+#endif
+      goto truncate_exit;
+    }
+
+  buffer = (char *)malloc (file_size + 1);
+  if (buffer == 0)
+    {
+      rv = errno;
+      close (file);
+      goto truncate_exit;
+    }
+
+  chars_read = read (file, buffer, file_size);
+  close (file);
+
+  if (chars_read <= 0)
+    {
+      rv = (chars_read < 0) ? errno : 0;
+      goto truncate_exit;
+    }
+
+  orig_lines = lines;
+  /* Count backwards from the end of buffer until we have passed
+     LINES lines.  bp1 is set funny initially.  But since bp[1] can't
+     be a comment character (since it's off the end) and *bp can't be
+     both a newline and the history comment character, it should be OK. */
+  for (bp1 = bp = buffer + chars_read - 1; lines && bp > buffer; bp--)
+    {
+      if (*bp == '\n' && HIST_TIMESTAMP_START(bp1) == 0)
+       lines--;
+      bp1 = bp;
+    }
+
+  /* If this is the first line, then the file contains exactly the
+     number of lines we want to truncate to, so we don't need to do
+     anything.  It's the first line if we don't find a newline between
+     the current value of i and 0.  Otherwise, write from the start of
+     this line until the end of the buffer. */
+  for ( ; bp > buffer; bp--)
+    {
+      if (*bp == '\n' && HIST_TIMESTAMP_START(bp1) == 0)
+        {
+         bp++;
+         break;
+        }
+      bp1 = bp;
+    }
+
+  /* Write only if there are more lines in the file than we want to
+     truncate to. */
+  if (bp <= buffer)
+    {
+      rv = 0;
+      /* No-op if LINES == 0 at this point */
+      history_lines_written_to_file = orig_lines - lines;
+      goto truncate_exit;
+    }
+
+  bakname = history_backupfile (filename);
+  if (filename && bakname)
+    rename (filename, bakname);
+
+  if ((file = open (filename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0600)) != -1)
+    {
+      if (write (file, bp, chars_read - (bp - buffer)) < 0)
+       rv = errno;
+
+#if defined (__BEOS__)
+      /* BeOS ignores O_TRUNC. */
+      ftruncate (file, chars_read - (bp - buffer));
+#endif
+
+      if (close (file) < 0 && rv == 0)
+       rv = errno;
+    }
+  else
+    rv = errno;
+
+ truncate_exit:
+  FREE (buffer);
+
+  history_lines_written_to_file = orig_lines - lines;
+
+  if (rv != 0 && filename && bakname)
+    rename (bakname, filename);
+  else if (rv == 0 && bakname)
+    unlink (bakname);
+
+  /* Make sure the new filename is owned by the same user as the old.  If one
+     user is running this, it's a no-op.  If the shell is running after sudo
+     with a shared history file, we don't want to leave the history file
+     owned by root. */
+  if (rv == 0 && exists)
+    chown (filename, finfo.st_uid, finfo.st_gid);
+
+  xfree (filename);
+  FREE (bakname);
+
+  return rv;
+}
+
+/* Workhorse function for writing history.  Writes the last NELEMENT entries
+   from the history list to FILENAME.  OVERWRITE is non-zero if you
+   wish to replace FILENAME with the entries. */
+static int
+history_do_write (filename, nelements, overwrite)
+     const char *filename;
+     int nelements, overwrite;
+{
+  register int i;
+  char *output, *bakname;
+  int file, mode, rv, exists;
+  struct stat finfo;
+#ifdef HISTORY_USE_MMAP
+  size_t cursize;
+
+  history_lines_written_to_file = 0;
+
+  mode = overwrite ? O_RDWR|O_CREAT|O_TRUNC|O_BINARY : O_RDWR|O_APPEND|O_BINARY;
+#else
+  mode = overwrite ? O_WRONLY|O_CREAT|O_TRUNC|O_BINARY : O_WRONLY|O_APPEND|O_BINARY;
+#endif
+  output = history_filename (filename);
+  bakname = (overwrite && output) ? history_backupfile (output) : 0;
+  exists = output ? (stat (output, &finfo) == 0) : 0;
+
+  if (output && bakname)
+    rename (output, bakname);
+
+  file = output ? open (output, mode, 0600) : -1;
+  rv = 0;
+
+  if (file == -1)
+    {
+      rv = errno;
+      if (output && bakname)
+        rename (bakname, output);
+      FREE (output);
+      FREE (bakname);
+      return (rv);
+    }
+
+#ifdef HISTORY_USE_MMAP
+  cursize = overwrite ? 0 : lseek (file, 0, SEEK_END);
+#endif
+
+  if (nelements > history_length)
+    nelements = history_length;
+
+  /* Build a buffer of all the lines to write, and write them in one syscall.
+     Suggested by Peter Ho (peter@robosts.oxford.ac.uk). */
+  {
+    HIST_ENTRY **the_history;  /* local */
+    register int j;
+    int buffer_size;
+    char *buffer;
+
+    the_history = history_list ();
+    /* Calculate the total number of bytes to write. */
+    for (buffer_size = 0, i = history_length - nelements; i < history_length; i++)
+#if 0
+      buffer_size += 2 + HISTENT_BYTES (the_history[i]);
+#else
+      {
+       if (history_write_timestamps && the_history[i]->timestamp && the_history[i]->timestamp[0])
+         buffer_size += strlen (the_history[i]->timestamp) + 1;
+       buffer_size += strlen (the_history[i]->line) + 1;
+      }
+#endif
+
+    /* Allocate the buffer, and fill it. */
+#ifdef HISTORY_USE_MMAP
+    if (ftruncate (file, buffer_size+cursize) == -1)
+      goto mmap_error;
+    buffer = (char *)mmap (0, buffer_size, PROT_READ|PROT_WRITE, MAP_WFLAGS, file, cursize);
+    if ((void *)buffer == MAP_FAILED)
+      {
+mmap_error:
+       rv = errno;
+       close (file);
+       if (output && bakname)
+         rename (bakname, output);
+       FREE (output);
+       FREE (bakname);
+       return rv;
+      }
+#else    
+    buffer = (char *)malloc (buffer_size);
+    if (buffer == 0)
+      {
+       rv = errno;
+       close (file);
+       if (output && bakname)
+         rename (bakname, output);
+       FREE (output);
+       FREE (bakname);
+       return rv;
+      }
+#endif
+
+    for (j = 0, i = history_length - nelements; i < history_length; i++)
+      {
+       if (history_write_timestamps && the_history[i]->timestamp && the_history[i]->timestamp[0])
+         {
+           strcpy (buffer + j, the_history[i]->timestamp);
+           j += strlen (the_history[i]->timestamp);
+           buffer[j++] = '\n';
+         }
+       strcpy (buffer + j, the_history[i]->line);
+       j += strlen (the_history[i]->line);
+       buffer[j++] = '\n';
+      }
+
+#ifdef HISTORY_USE_MMAP
+    if (msync (buffer, buffer_size, MS_ASYNC) != 0 || munmap (buffer, buffer_size) != 0)
+      rv = errno;
+#else
+    if (write (file, buffer, buffer_size) < 0)
+      rv = errno;
+    xfree (buffer);
+#endif
+  }
+
+  history_lines_written_to_file = nelements;
+
+  if (close (file) < 0 && rv == 0)
+    rv = errno;
+
+  if (rv != 0 && output && bakname)
+    rename (bakname, output);
+  else if (rv == 0 && bakname)
+    unlink (bakname);
+
+  /* Make sure the new filename is owned by the same user as the old.  If one
+     user is running this, it's a no-op.  If the shell is running after sudo
+     with a shared history file, we don't want to leave the history file
+     owned by root. */
+  if (rv == 0 && exists)
+    chown (output, finfo.st_uid, finfo.st_gid);
+
+  FREE (output);
+  FREE (bakname);
+
+  return (rv);
+}
+
+/* Append NELEMENT entries to FILENAME.  The entries appended are from
+   the end of the list minus NELEMENTs up to the end of the list. */
+int
+append_history (nelements, filename)
+     int nelements;
+     const char *filename;
+{
+  return (history_do_write (filename, nelements, HISTORY_APPEND));
+}
+
+/* Overwrite FILENAME with the current history.  If FILENAME is NULL,
+   then write the history list to ~/.history.  Values returned
+   are as in read_history ().*/
+int
+write_history (filename)
+     const char *filename;
+{
+  return (history_do_write (filename, history_length, HISTORY_OVERWRITE));
+}
index 6d2e36b97a558a19a1496d817b4e00336974422f..b0f2191aeeefb24e0d9b23a3048ac7c3ce2b12f1 100644 (file)
@@ -533,9 +533,9 @@ readline_internal_charloop ()
   int c, code, lk;
 
   lastc = EOF;
-  eof_found = 0;
 
 #if !defined (READLINE_CALLBACKS)
+  eof_found = 0;
   while (rl_done == 0)
     {
 #endif
diff --git a/lib/readline/readline.c~ b/lib/readline/readline.c~
new file mode 100644 (file)
index 0000000..6d2e36b
--- /dev/null
@@ -0,0 +1,1426 @@
+/* readline.c -- a general facility for reading lines of input
+   with emacs style editing and completion. */
+
+/* Copyright (C) 1987-2013 Free Software Foundation, Inc.
+
+   This file is part of the GNU Readline Library (Readline), a library
+   for reading lines of text with interactive input and history editing.      
+
+   Readline is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   Readline 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 Readline.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#define READLINE_LIBRARY
+
+#if defined (HAVE_CONFIG_H)
+#  include <config.h>
+#endif
+
+#include <sys/types.h>
+#include "posixstat.h"
+#include <fcntl.h>
+#if defined (HAVE_SYS_FILE_H)
+#  include <sys/file.h>
+#endif /* HAVE_SYS_FILE_H */
+
+#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>
+#include "posixjmp.h"
+#include <errno.h>
+
+#if !defined (errno)
+extern int errno;
+#endif /* !errno */
+
+/* System-specific feature definitions and include files. */
+#include "rldefs.h"
+#include "rlmbutil.h"
+
+#if defined (__EMX__)
+#  define INCL_DOSPROCESS
+#  include <os2.h>
+#endif /* __EMX__ */
+
+/* Some standard library routines. */
+#include "readline.h"
+#include "history.h"
+
+#include "rlprivate.h"
+#include "rlshell.h"
+#include "xmalloc.h"
+
+#ifndef RL_LIBRARY_VERSION
+#  define RL_LIBRARY_VERSION "5.1"
+#endif
+
+#ifndef RL_READLINE_VERSION
+#  define RL_READLINE_VERSION  0x0501
+#endif
+
+extern void _rl_free_history_entry PARAMS((HIST_ENTRY *));
+
+#if defined (COLOR_SUPPORT)
+extern void _rl_parse_colors PARAMS((void));           /* XXX */
+#endif
+
+
+/* Forward declarations used in this file. */
+static char *readline_internal PARAMS((void));
+static void readline_initialize_everything PARAMS((void));
+
+static void bind_arrow_keys_internal PARAMS((Keymap));
+static void bind_arrow_keys PARAMS((void));
+
+static void bind_bracketed_paste_prefix PARAMS((void));
+
+static void readline_default_bindings PARAMS((void));
+static void reset_default_bindings PARAMS((void));
+
+static int _rl_subseq_result PARAMS((int, Keymap, int, int));
+static int _rl_subseq_getchar PARAMS((int));
+
+/* **************************************************************** */
+/*                                                                 */
+/*                     Line editing input utility                  */
+/*                                                                 */
+/* **************************************************************** */
+
+const char *rl_library_version = RL_LIBRARY_VERSION;
+
+int rl_readline_version = RL_READLINE_VERSION;
+
+/* True if this is `real' readline as opposed to some stub substitute. */
+int rl_gnu_readline_p = 1;
+
+/* A pointer to the keymap that is currently in use.
+   By default, it is the standard emacs keymap. */
+Keymap _rl_keymap = emacs_standard_keymap;
+
+/* The current style of editing. */
+int rl_editing_mode = emacs_mode;
+
+/* The current insert mode:  input (the default) or overwrite */
+int rl_insert_mode = RL_IM_DEFAULT;
+
+/* Non-zero if we called this function from _rl_dispatch().  It's present
+   so functions can find out whether they were called from a key binding
+   or directly from an application. */
+int rl_dispatching;
+
+/* Non-zero if the previous command was a kill command. */
+int _rl_last_command_was_kill = 0;
+
+/* The current value of the numeric argument specified by the user. */
+int rl_numeric_arg = 1;
+
+/* Non-zero if an argument was typed. */
+int rl_explicit_arg = 0;
+
+/* Temporary value used while generating the argument. */
+int rl_arg_sign = 1;
+
+/* Non-zero means we have been called at least once before. */
+static int rl_initialized;
+
+#if 0
+/* If non-zero, this program is running in an EMACS buffer. */
+static int running_in_emacs;
+#endif
+
+/* Flags word encapsulating the current readline state. */
+int rl_readline_state = RL_STATE_NONE;
+
+/* The current offset in the current input line. */
+int rl_point;
+
+/* Mark in the current input line. */
+int rl_mark;
+
+/* Length of the current input line. */
+int rl_end;
+
+/* Make this non-zero to return the current input_line. */
+int rl_done;
+
+/* The last function executed by readline. */
+rl_command_func_t *rl_last_func = (rl_command_func_t *)NULL;
+
+/* Top level environment for readline_internal (). */
+procenv_t _rl_top_level;
+
+/* The streams we interact with. */
+FILE *_rl_in_stream, *_rl_out_stream;
+
+/* The names of the streams that we do input and output to. */
+FILE *rl_instream = (FILE *)NULL;
+FILE *rl_outstream = (FILE *)NULL;
+
+/* Non-zero means echo characters as they are read.  Defaults to no echo;
+   set to 1 if there is a controlling terminal, we can get its attributes,
+   and the attributes include `echo'.  Look at rltty.c:prepare_terminal_settings
+   for the code that sets it. */
+int _rl_echoing_p = 0;
+
+/* Current prompt. */
+char *rl_prompt = (char *)NULL;
+int rl_visible_prompt_length = 0;
+
+/* Set to non-zero by calling application if it has already printed rl_prompt
+   and does not want readline to do it the first time. */
+int rl_already_prompted = 0;
+
+/* The number of characters read in order to type this complete command. */
+int rl_key_sequence_length = 0;
+
+/* If non-zero, then this is the address of a function to call just
+   before readline_internal_setup () prints the first prompt. */
+rl_hook_func_t *rl_startup_hook = (rl_hook_func_t *)NULL;
+
+/* If non-zero, this is the address of a function to call just before
+   readline_internal_setup () returns and readline_internal starts
+   reading input characters. */
+rl_hook_func_t *rl_pre_input_hook = (rl_hook_func_t *)NULL;
+
+/* What we use internally.  You should always refer to RL_LINE_BUFFER. */
+static char *the_line;
+
+/* The character that can generate an EOF.  Really read from
+   the terminal driver... just defaulted here. */
+int _rl_eof_char = CTRL ('D');
+
+/* Non-zero makes this the next keystroke to read. */
+int rl_pending_input = 0;
+
+/* Pointer to a useful terminal name. */
+const char *rl_terminal_name = (const char *)NULL;
+
+/* Non-zero means to always use horizontal scrolling in line display. */
+int _rl_horizontal_scroll_mode = 0;
+
+/* Non-zero means to display an asterisk at the starts of history lines
+   which have been modified. */
+int _rl_mark_modified_lines = 0;  
+
+/* The style of `bell' notification preferred.  This can be set to NO_BELL,
+   AUDIBLE_BELL, or VISIBLE_BELL. */
+int _rl_bell_preference = AUDIBLE_BELL;
+     
+/* String inserted into the line by rl_insert_comment (). */
+char *_rl_comment_begin;
+
+/* Keymap holding the function currently being executed. */
+Keymap rl_executing_keymap;
+
+/* Keymap we're currently using to dispatch. */
+Keymap _rl_dispatching_keymap;
+
+/* Non-zero means to erase entire line, including prompt, on empty input lines. */
+int rl_erase_empty_line = 0;
+
+/* Non-zero means to read only this many characters rather than up to a
+   character bound to accept-line. */
+int rl_num_chars_to_read;
+
+/* Line buffer and maintenance. */
+char *rl_line_buffer = (char *)NULL;
+int rl_line_buffer_len = 0;
+
+/* Key sequence `contexts' */
+_rl_keyseq_cxt *_rl_kscxt = 0;
+
+int rl_executing_key;
+char *rl_executing_keyseq = 0;
+int _rl_executing_keyseq_size = 0;
+
+/* Timeout (specified in milliseconds) when reading characters making up an
+   ambiguous multiple-key sequence */
+int _rl_keyseq_timeout = 500;
+
+#define RESIZE_KEYSEQ_BUFFER() \
+  do \
+    { \
+      if (rl_key_sequence_length + 2 >= _rl_executing_keyseq_size) \
+       { \
+         _rl_executing_keyseq_size += 16; \
+         rl_executing_keyseq = xrealloc (rl_executing_keyseq, _rl_executing_keyseq_size); \
+       } \
+    } \
+  while (0);
+        
+/* Forward declarations used by the display, termcap, and history code. */
+
+/* **************************************************************** */
+/*                                                                 */
+/*                     `Forward' declarations                      */
+/*                                                                 */
+/* **************************************************************** */
+
+/* Non-zero means do not parse any lines other than comments and
+   parser directives. */
+unsigned char _rl_parsing_conditionalized_out = 0;
+
+/* Non-zero means to convert characters with the meta bit set to
+   escape-prefixed characters so we can indirect through
+   emacs_meta_keymap or vi_escape_keymap. */
+int _rl_convert_meta_chars_to_ascii = 1;
+
+/* Non-zero means to output characters with the meta bit set directly
+   rather than as a meta-prefixed escape sequence. */
+int _rl_output_meta_chars = 0;
+
+/* Non-zero means to look at the termios special characters and bind
+   them to equivalent readline functions at startup. */
+int _rl_bind_stty_chars = 1;
+
+/* Non-zero means to go through the history list at every newline (or
+   whenever rl_done is set and readline returns) and revert each line to
+   its initial state. */
+int _rl_revert_all_at_newline = 0;
+
+/* Non-zero means to honor the termios ECHOCTL bit and echo control
+   characters corresponding to keyboard-generated signals. */
+int _rl_echo_control_chars = 1;
+
+/* Non-zero means to prefix the displayed prompt with a character indicating
+   the editing mode: @ for emacs, : for vi-command, + for vi-insert. */
+int _rl_show_mode_in_prompt = 0;
+
+/* Non-zero means to attempt to put the terminal in `bracketed paste mode',
+   where it will prefix pasted text with an escape sequence and send
+   another to mark the end of the paste. */
+int _rl_enable_bracketed_paste = 0;
+
+/* **************************************************************** */
+/*                                                                 */
+/*                     Top Level Functions                         */
+/*                                                                 */
+/* **************************************************************** */
+
+/* Non-zero means treat 0200 bit in terminal input as Meta bit. */
+int _rl_meta_flag = 0; /* Forward declaration */
+
+/* Set up the prompt and expand it.  Called from readline() and
+   rl_callback_handler_install (). */
+int
+rl_set_prompt (prompt)
+     const char *prompt;
+{
+  FREE (rl_prompt);
+  rl_prompt = prompt ? savestring (prompt) : (char *)NULL;
+  rl_display_prompt = rl_prompt ? rl_prompt : "";
+
+  rl_visible_prompt_length = rl_expand_prompt (rl_prompt);
+  return 0;
+}
+  
+/* Read a line of input.  Prompt with PROMPT.  An empty PROMPT means
+   none.  A return value of NULL means that EOF was encountered. */
+char *
+readline (prompt)
+     const char *prompt;
+{
+  char *value;
+#if 0
+  int in_callback;
+#endif
+
+  /* If we are at EOF return a NULL string. */
+  if (rl_pending_input == EOF)
+    {
+      rl_clear_pending_input ();
+      return ((char *)NULL);
+    }
+
+#if 0
+  /* If readline() is called after installing a callback handler, temporarily
+     turn off the callback state to avoid ensuing messiness.  Patch supplied
+     by the gdb folks.  XXX -- disabled.  This can be fooled and readline
+     left in a strange state by a poorly-timed longjmp. */
+  if (in_callback = RL_ISSTATE (RL_STATE_CALLBACK))
+    RL_UNSETSTATE (RL_STATE_CALLBACK);
+#endif
+
+  rl_set_prompt (prompt);
+
+  rl_initialize ();
+  if (rl_prep_term_function)
+    (*rl_prep_term_function) (_rl_meta_flag);
+
+#if defined (HANDLE_SIGNALS)
+  rl_set_signals ();
+#endif
+
+  value = readline_internal ();
+  if (rl_deprep_term_function)
+    (*rl_deprep_term_function) ();
+
+#if defined (HANDLE_SIGNALS)
+  rl_clear_signals ();
+#endif
+
+#if 0
+  if (in_callback)
+    RL_SETSTATE (RL_STATE_CALLBACK);
+#endif
+
+#if HAVE_DECL_AUDIT_TTY && defined (ENABLE_TTY_AUDIT_SUPPORT)
+  if (value)
+    _rl_audit_tty (value);
+#endif
+
+  return (value);
+}
+
+#if defined (READLINE_CALLBACKS)
+#  define STATIC_CALLBACK
+#else
+#  define STATIC_CALLBACK static
+#endif
+
+STATIC_CALLBACK void
+readline_internal_setup ()
+{
+  char *nprompt;
+
+  _rl_in_stream = rl_instream;
+  _rl_out_stream = rl_outstream;
+
+  /* Enable the meta key only for the duration of readline(), if this
+     terminal has one and the terminal has been initialized */
+  if (_rl_enable_meta & RL_ISSTATE (RL_STATE_TERMPREPPED))
+    _rl_enable_meta_key ();
+
+  if (rl_startup_hook)
+    (*rl_startup_hook) ();
+
+#if defined (VI_MODE)
+  if (rl_editing_mode == vi_mode)
+    rl_vi_insertion_mode (1, 'i');     /* don't want to reset last */
+#endif /* VI_MODE */
+
+  /* If we're not echoing, we still want to at least print a prompt, because
+     rl_redisplay will not do it for us.  If the calling application has a
+     custom redisplay function, though, let that function handle it. */
+  if (_rl_echoing_p == 0 && rl_redisplay_function == rl_redisplay)
+    {
+      if (rl_prompt && rl_already_prompted == 0)
+       {
+         nprompt = _rl_strip_prompt (rl_prompt);
+         fprintf (_rl_out_stream, "%s", nprompt);
+         fflush (_rl_out_stream);
+         xfree (nprompt);
+       }
+    }
+  else
+    {
+      if (rl_prompt && rl_already_prompted)
+       rl_on_new_line_with_prompt ();
+      else
+       rl_on_new_line ();
+      (*rl_redisplay_function) ();
+    }
+
+  if (rl_pre_input_hook)
+    (*rl_pre_input_hook) ();
+
+  RL_CHECK_SIGNALS ();
+}
+
+STATIC_CALLBACK char *
+readline_internal_teardown (eof)
+     int eof;
+{
+  char *temp;
+  HIST_ENTRY *entry;
+
+  RL_CHECK_SIGNALS ();
+
+  /* Restore the original of this history line, iff the line that we
+     are editing was originally in the history, AND the line has changed. */
+  entry = current_history ();
+
+  if (entry && rl_undo_list)
+    {
+      temp = savestring (the_line);
+      rl_revert_line (1, 0);
+      entry = replace_history_entry (where_history (), the_line, (histdata_t)NULL);
+      _rl_free_history_entry (entry);
+
+      strcpy (the_line, temp);
+      xfree (temp);
+    }
+
+  if (_rl_revert_all_at_newline)
+    _rl_revert_all_lines ();
+
+  /* At any rate, it is highly likely that this line has an undo list.  Get
+     rid of it now. */
+  if (rl_undo_list)
+    rl_free_undo_list ();
+
+  /* Disable the meta key, if this terminal has one and we were told to use it.
+     The check whether or not we sent the enable string is in
+     _rl_disable_meta_key(); the flag is set in _rl_enable_meta_key */
+  _rl_disable_meta_key ();
+
+  /* Restore normal cursor, if available. */
+  _rl_set_insert_mode (RL_IM_INSERT, 0);
+
+  return (eof ? (char *)NULL : savestring (the_line));
+}
+
+void
+_rl_internal_char_cleanup ()
+{
+#if defined (VI_MODE)
+  /* In vi mode, when you exit insert mode, the cursor moves back
+     over the previous character.  We explicitly check for that here. */
+  if (rl_editing_mode == vi_mode && _rl_keymap == vi_movement_keymap)
+    rl_vi_check ();
+#endif /* VI_MODE */
+
+  if (rl_num_chars_to_read && rl_end >= rl_num_chars_to_read)
+    {
+      (*rl_redisplay_function) ();
+      _rl_want_redisplay = 0;
+      rl_newline (1, '\n');
+    }
+
+  if (rl_done == 0)
+    {
+      (*rl_redisplay_function) ();
+      _rl_want_redisplay = 0;
+    }
+
+  /* If the application writer has told us to erase the entire line if
+     the only character typed was something bound to rl_newline, do so. */
+  if (rl_erase_empty_line && rl_done && rl_last_func == rl_newline &&
+      rl_point == 0 && rl_end == 0)
+    _rl_erase_entire_line ();
+}
+
+STATIC_CALLBACK int
+#if defined (READLINE_CALLBACKS)
+readline_internal_char ()
+#else
+readline_internal_charloop ()
+#endif
+{
+  static int lastc, eof_found;
+  int c, code, lk;
+
+  lastc = EOF;
+  eof_found = 0;
+
+#if !defined (READLINE_CALLBACKS)
+  while (rl_done == 0)
+    {
+#endif
+      lk = _rl_last_command_was_kill;
+
+#if defined (HAVE_POSIX_SIGSETJMP)
+      code = sigsetjmp (_rl_top_level, 0);
+#else
+      code = setjmp (_rl_top_level);
+#endif
+
+      if (code)
+       {
+         (*rl_redisplay_function) ();
+         _rl_want_redisplay = 0;
+         /* If we get here, we're not being called from something dispatched
+            from _rl_callback_read_char(), which sets up its own value of
+            _rl_top_level (saving and restoring the old, of course), so
+            we can just return here. */
+         if (RL_ISSTATE (RL_STATE_CALLBACK))
+           return (0);
+       }
+
+      if (rl_pending_input == 0)
+       {
+         /* Then initialize the argument and number of keys read. */
+         _rl_reset_argument ();
+         rl_key_sequence_length = 0;
+         rl_executing_keyseq[0] = 0;
+       }
+
+      RL_SETSTATE(RL_STATE_READCMD);
+      c = rl_read_key ();
+      RL_UNSETSTATE(RL_STATE_READCMD);
+
+      /* look at input.c:rl_getc() for the circumstances under which this will
+        be returned; punt immediately on read error without converting it to
+        a newline; assume that rl_read_key has already called the signal
+        handler. */
+      if (c == READERR)
+       {
+#if defined (READLINE_CALLBACKS)
+         RL_SETSTATE(RL_STATE_DONE);
+         return (rl_done = 1);
+#else
+         eof_found = 1;
+         break;
+#endif
+       }
+
+      /* EOF typed to a non-blank line is ^D the first time, EOF the second
+        time in a row.  This won't return any partial line read from the tty.
+        If we want to change this, to force any existing line to be returned
+        when read(2) reads EOF, for example, this is the place to change. */
+      if (c == EOF && rl_end)
+       {
+         if (RL_SIG_RECEIVED ())
+           {
+             RL_CHECK_SIGNALS ();
+             if (rl_signal_event_hook)
+               (*rl_signal_event_hook) ();             /* XXX */
+           }
+
+         /* XXX - reading two consecutive EOFs returns EOF */
+         if (RL_ISSTATE (RL_STATE_TERMPREPPED))
+           {
+             if (lastc == _rl_eof_char || lastc == EOF)
+               rl_end = 0;
+             else
+               c = _rl_eof_char;
+           }
+         else
+           c = NEWLINE;
+       }
+
+      /* The character _rl_eof_char typed to blank line, and not as the
+        previous character is interpreted as EOF.  This doesn't work when
+        READLINE_CALLBACKS is defined, so hitting a series of ^Ds will
+        erase all the chars on the line and then return EOF. */
+      if (((c == _rl_eof_char && lastc != c) || c == EOF) && rl_end == 0)
+       {
+#if defined (READLINE_CALLBACKS)
+         RL_SETSTATE(RL_STATE_DONE);
+         return (rl_done = 1);
+#else
+         eof_found = 1;
+         break;
+#endif
+       }
+
+      lastc = c;
+      _rl_dispatch ((unsigned char)c, _rl_keymap);
+      RL_CHECK_SIGNALS ();
+
+      /* If there was no change in _rl_last_command_was_kill, then no kill
+        has taken place.  Note that if input is pending we are reading
+        a prefix command, so nothing has changed yet. */
+      if (rl_pending_input == 0 && lk == _rl_last_command_was_kill)
+       _rl_last_command_was_kill = 0;
+
+      _rl_internal_char_cleanup ();
+
+#if defined (READLINE_CALLBACKS)
+      return 0;
+#else
+    }
+
+  return (eof_found);
+#endif
+}
+
+#if defined (READLINE_CALLBACKS)
+static int
+readline_internal_charloop ()
+{
+  int eof = 1;
+
+  while (rl_done == 0)
+    eof = readline_internal_char ();
+  return (eof);
+}
+#endif /* READLINE_CALLBACKS */
+
+/* Read a line of input from the global rl_instream, doing output on
+   the global rl_outstream.
+   If rl_prompt is non-null, then that is our prompt. */
+static char *
+readline_internal ()
+{
+  int eof;
+
+  readline_internal_setup ();
+  eof = readline_internal_charloop ();
+  return (readline_internal_teardown (eof));
+}
+
+void
+_rl_init_line_state ()
+{
+  rl_point = rl_end = rl_mark = 0;
+  the_line = rl_line_buffer;
+  the_line[0] = 0;
+}
+
+void
+_rl_set_the_line ()
+{
+  the_line = rl_line_buffer;
+}
+
+#if defined (READLINE_CALLBACKS)
+_rl_keyseq_cxt *
+_rl_keyseq_cxt_alloc ()
+{
+  _rl_keyseq_cxt *cxt;
+
+  cxt = (_rl_keyseq_cxt *)xmalloc (sizeof (_rl_keyseq_cxt));
+
+  cxt->flags = cxt->subseq_arg = cxt->subseq_retval = 0;
+
+  cxt->okey = 0;
+  cxt->ocxt = _rl_kscxt;
+  cxt->childval = 42;          /* sentinel value */
+
+  return cxt;
+}
+
+void
+_rl_keyseq_cxt_dispose (cxt)
+    _rl_keyseq_cxt *cxt;
+{
+  xfree (cxt);
+}
+
+void
+_rl_keyseq_chain_dispose ()
+{
+  _rl_keyseq_cxt *cxt;
+
+  while (_rl_kscxt)
+    {
+      cxt = _rl_kscxt;
+      _rl_kscxt = _rl_kscxt->ocxt;
+      _rl_keyseq_cxt_dispose (cxt);
+    }
+}
+#endif
+
+static int
+_rl_subseq_getchar (key)
+     int key;
+{
+  int k;
+
+  if (key == ESC)
+    RL_SETSTATE(RL_STATE_METANEXT);
+  RL_SETSTATE(RL_STATE_MOREINPUT);
+  k = rl_read_key ();
+  RL_UNSETSTATE(RL_STATE_MOREINPUT);
+  if (key == ESC)
+    RL_UNSETSTATE(RL_STATE_METANEXT);
+
+  return k;
+}
+
+#if defined (READLINE_CALLBACKS)
+int
+_rl_dispatch_callback (cxt)
+     _rl_keyseq_cxt *cxt;
+{
+  int nkey, r;
+
+  /* For now */
+  /* The first time this context is used, we want to read input and dispatch
+     on it.  When traversing the chain of contexts back `up', we want to use
+     the value from the next context down.  We're simulating recursion using
+     a chain of contexts. */
+  if ((cxt->flags & KSEQ_DISPATCHED) == 0)
+    {
+      nkey = _rl_subseq_getchar (cxt->okey);
+      if (nkey < 0)
+       {
+         _rl_abort_internal ();
+         return -1;
+       }
+      r = _rl_dispatch_subseq (nkey, cxt->dmap, cxt->subseq_arg);
+      cxt->flags |= KSEQ_DISPATCHED;
+    }
+  else
+    r = cxt->childval;
+
+  /* For now */
+  if (r != -3) /* don't do this if we indicate there will be other matches */
+    r = _rl_subseq_result (r, cxt->oldmap, cxt->okey, (cxt->flags & KSEQ_SUBSEQ));
+
+  RL_CHECK_SIGNALS ();
+  /* We only treat values < 0 specially to simulate recursion. */
+  if (r >= 0 || (r == -1 && (cxt->flags & KSEQ_SUBSEQ) == 0))  /* success! or failure! */
+    {
+      _rl_keyseq_chain_dispose ();
+      RL_UNSETSTATE (RL_STATE_MULTIKEY);
+      return r;
+    }
+
+  if (r != -3)                 /* magic value that says we added to the chain */
+    _rl_kscxt = cxt->ocxt;
+  if (_rl_kscxt)
+    _rl_kscxt->childval = r;
+  if (r != -3)
+    _rl_keyseq_cxt_dispose (cxt);
+
+  return r;
+}
+#endif /* READLINE_CALLBACKS */
+  
+/* Do the command associated with KEY in MAP.
+   If the associated command is really a keymap, then read
+   another key, and dispatch into that map. */
+int
+_rl_dispatch (key, map)
+     register int key;
+     Keymap map;
+{
+  _rl_dispatching_keymap = map;
+  return _rl_dispatch_subseq (key, map, 0);
+}
+
+int
+_rl_dispatch_subseq (key, map, got_subseq)
+     register int key;
+     Keymap map;
+     int got_subseq;
+{
+  int r, newkey;
+  char *macro;
+  rl_command_func_t *func;
+#if defined (READLINE_CALLBACKS)
+  _rl_keyseq_cxt *cxt;
+#endif
+
+  if (META_CHAR (key) && _rl_convert_meta_chars_to_ascii)
+    {
+      if (map[ESC].type == ISKMAP)
+       {
+         if (RL_ISSTATE (RL_STATE_MACRODEF))
+           _rl_add_macro_char (ESC);
+         RESIZE_KEYSEQ_BUFFER ();
+         rl_executing_keyseq[rl_key_sequence_length++] = ESC;
+         map = FUNCTION_TO_KEYMAP (map, ESC);
+         key = UNMETA (key);
+         return (_rl_dispatch (key, map));
+       }
+      else
+       rl_ding ();
+      return 0;
+    }
+
+  if (RL_ISSTATE (RL_STATE_MACRODEF))
+    _rl_add_macro_char (key);
+
+  r = 0;
+  switch (map[key].type)
+    {
+    case ISFUNC:
+      func = map[key].function;
+      if (func)
+       {
+         /* Special case rl_do_lowercase_version (). */
+         if (func == rl_do_lowercase_version)
+           /* Should we do anything special if key == ANYOTHERKEY? */
+           return (_rl_dispatch (_rl_to_lower (key), map));
+
+         rl_executing_keymap = map;
+         rl_executing_key = key;
+
+         RESIZE_KEYSEQ_BUFFER();
+         rl_executing_keyseq[rl_key_sequence_length++] = key;
+         rl_executing_keyseq[rl_key_sequence_length] = '\0';
+
+         rl_dispatching = 1;
+         RL_SETSTATE(RL_STATE_DISPATCHING);
+         r = (*func) (rl_numeric_arg * rl_arg_sign, key);
+         RL_UNSETSTATE(RL_STATE_DISPATCHING);
+         rl_dispatching = 0;
+
+         /* If we have input pending, then the last command was a prefix
+            command.  Don't change the state of rl_last_func.  Otherwise,
+            remember the last command executed in this variable. */
+         if (rl_pending_input == 0 && map[key].function != rl_digit_argument)
+           rl_last_func = map[key].function;
+
+         RL_CHECK_SIGNALS ();
+       }
+      else if (map[ANYOTHERKEY].function)
+       {
+         /* OK, there's no function bound in this map, but there is a
+            shadow function that was overridden when the current keymap
+            was created.  Return -2 to note  that. */
+         if (RL_ISSTATE (RL_STATE_MACROINPUT))
+           _rl_prev_macro_key ();
+         else
+           _rl_unget_char  (key);
+         return -2;
+       }
+      else if (got_subseq)
+       {
+         /* Return -1 to note that we're in a subsequence, but  we don't
+            have a matching key, nor was one overridden.  This means
+            we need to back up the recursion chain and find the last
+            subsequence that is bound to a function. */
+         if (RL_ISSTATE (RL_STATE_MACROINPUT))
+           _rl_prev_macro_key ();
+         else
+           _rl_unget_char (key);
+         return -1;
+       }
+      else
+       {
+#if defined (READLINE_CALLBACKS)
+         RL_UNSETSTATE (RL_STATE_MULTIKEY);
+         _rl_keyseq_chain_dispose ();
+#endif
+         _rl_abort_internal ();
+         return -1;
+       }
+      break;
+
+    case ISKMAP:
+      if (map[key].function != 0)
+       {
+#if defined (VI_MODE)
+         /* The only way this test will be true is if a subsequence has been
+            bound starting with ESC, generally the arrow keys.  What we do is
+            check whether there's input in the queue, which there generally
+            will be if an arrow key has been pressed, and, if there's not,
+            just dispatch to (what we assume is) rl_vi_movement_mode right
+            away.  This is essentially an input test with a zero timeout (by
+            default) or a timeout determined by the value of `keyseq-timeout' */
+         /* _rl_keyseq_timeout specified in milliseconds; _rl_input_queued
+            takes microseconds, so multiply by 1000 */
+         if (rl_editing_mode == vi_mode && key == ESC && map == vi_insertion_keymap
+             && _rl_input_queued ((_rl_keyseq_timeout > 0) ? _rl_keyseq_timeout*1000 : 0) == 0)
+           return (_rl_dispatch (ANYOTHERKEY, FUNCTION_TO_KEYMAP (map, key)));
+#endif
+
+         RESIZE_KEYSEQ_BUFFER ();
+         rl_executing_keyseq[rl_key_sequence_length++] = key;
+         _rl_dispatching_keymap = FUNCTION_TO_KEYMAP (map, key);
+
+         /* Allocate new context here.  Use linked contexts (linked through
+            cxt->ocxt) to simulate recursion */
+#if defined (READLINE_CALLBACKS)
+         if (RL_ISSTATE (RL_STATE_CALLBACK))
+           {
+             /* Return 0 only the first time, to indicate success to
+                _rl_callback_read_char.  The rest of the time, we're called
+                from _rl_dispatch_callback, so we return -3 to indicate
+                special handling is necessary. */
+             r = RL_ISSTATE (RL_STATE_MULTIKEY) ? -3 : 0;
+             cxt = _rl_keyseq_cxt_alloc ();
+
+             if (got_subseq)
+               cxt->flags |= KSEQ_SUBSEQ;
+             cxt->okey = key;
+             cxt->oldmap = map;
+             cxt->dmap = _rl_dispatching_keymap;
+             cxt->subseq_arg = got_subseq || cxt->dmap[ANYOTHERKEY].function;
+
+             RL_SETSTATE (RL_STATE_MULTIKEY);
+             _rl_kscxt = cxt;
+
+             return r;         /* don't indicate immediate success */
+           }
+#endif
+
+         /* Tentative inter-character timeout for potential multi-key
+            sequences?  If no input within timeout, abort sequence and
+            act as if we got non-matching input. */
+         /* _rl_keyseq_timeout specified in milliseconds; _rl_input_queued
+            takes microseconds, so multiply by 1000 */
+         if (_rl_keyseq_timeout > 0 &&
+               (RL_ISSTATE (RL_STATE_INPUTPENDING|RL_STATE_MACROINPUT) == 0) &&
+               _rl_pushed_input_available () == 0 &&
+               _rl_dispatching_keymap[ANYOTHERKEY].function &&
+               _rl_input_queued (_rl_keyseq_timeout*1000) == 0)
+           return (_rl_subseq_result (-2, map, key, got_subseq));
+
+         newkey = _rl_subseq_getchar (key);
+         if (newkey < 0)
+           {
+             _rl_abort_internal ();
+             return -1;
+           }
+
+         r = _rl_dispatch_subseq (newkey, _rl_dispatching_keymap, got_subseq || map[ANYOTHERKEY].function);
+         return _rl_subseq_result (r, map, key, got_subseq);
+       }
+      else
+       {
+         _rl_abort_internal ();        /* XXX */
+         return -1;
+       }
+      break;
+
+    case ISMACR:
+      if (map[key].function != 0)
+       {
+         rl_executing_keyseq[rl_key_sequence_length] = '\0';
+         macro = savestring ((char *)map[key].function);
+         _rl_with_macro_input (macro);
+         return 0;
+       }
+      break;
+    }
+#if defined (VI_MODE)
+  if (rl_editing_mode == vi_mode && _rl_keymap == vi_movement_keymap &&
+      key != ANYOTHERKEY &&
+      _rl_dispatching_keymap == vi_movement_keymap &&
+      _rl_vi_textmod_command (key))
+    _rl_vi_set_last (key, rl_numeric_arg, rl_arg_sign);
+#endif
+
+  return (r);
+}
+
+static int
+_rl_subseq_result (r, map, key, got_subseq)
+     int r;
+     Keymap map;
+     int key, got_subseq;
+{
+  Keymap m;
+  int type, nt;
+  rl_command_func_t *func, *nf;
+
+  if (r == -2)
+    /* We didn't match anything, and the keymap we're indexed into
+       shadowed a function previously bound to that prefix.  Call
+       the function.  The recursive call to _rl_dispatch_subseq has
+       already taken care of pushing any necessary input back onto
+       the input queue with _rl_unget_char. */
+    {
+      m = _rl_dispatching_keymap;
+      type = m[ANYOTHERKEY].type;
+      func = m[ANYOTHERKEY].function;
+      if (type == ISFUNC && func == rl_do_lowercase_version)
+       r = _rl_dispatch (_rl_to_lower (key), map);
+      else if (type == ISFUNC && func == rl_insert)
+       {
+         /* If the function that was shadowed was self-insert, we
+            somehow need a keymap with map[key].func == self-insert.
+            Let's use this one. */
+         nt = m[key].type;
+         nf = m[key].function;
+
+         m[key].type = type;
+         m[key].function = func;
+         r = _rl_dispatch (key, m);
+         m[key].type = nt;
+         m[key].function = nf;
+       }
+      else
+       r = _rl_dispatch (ANYOTHERKEY, m);
+    }
+  else if (r && map[ANYOTHERKEY].function)
+    {
+      /* We didn't match (r is probably -1), so return something to
+        tell the caller that it should try ANYOTHERKEY for an
+        overridden function. */
+      if (RL_ISSTATE (RL_STATE_MACROINPUT))
+       _rl_prev_macro_key ();
+      else
+       _rl_unget_char (key);
+      _rl_dispatching_keymap = map;
+      return -2;
+    }
+  else if (r && got_subseq)
+    {
+      /* OK, back up the chain. */
+      if (RL_ISSTATE (RL_STATE_MACROINPUT))
+       _rl_prev_macro_key ();
+      else
+       _rl_unget_char (key);
+      _rl_dispatching_keymap = map;
+      return -1;
+    }
+
+  return r;
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*                     Initializations                             */
+/*                                                                 */
+/* **************************************************************** */
+
+/* Initialize readline (and terminal if not already). */
+int
+rl_initialize ()
+{
+  /* If we have never been called before, initialize the
+     terminal and data structures. */
+  if (!rl_initialized)
+    {
+      RL_SETSTATE(RL_STATE_INITIALIZING);
+      readline_initialize_everything ();
+      RL_UNSETSTATE(RL_STATE_INITIALIZING);
+      rl_initialized++;
+      RL_SETSTATE(RL_STATE_INITIALIZED);
+    }
+
+  /* Initialize the current line information. */
+  _rl_init_line_state ();
+
+  /* We aren't done yet.  We haven't even gotten started yet! */
+  rl_done = 0;
+  RL_UNSETSTATE(RL_STATE_DONE);
+
+  /* Tell the history routines what is going on. */
+  _rl_start_using_history ();
+
+  /* Make the display buffer match the state of the line. */
+  rl_reset_line_state ();
+
+  /* No such function typed yet. */
+  rl_last_func = (rl_command_func_t *)NULL;
+
+  /* Parsing of key-bindings begins in an enabled state. */
+  _rl_parsing_conditionalized_out = 0;
+
+#if defined (VI_MODE)
+  if (rl_editing_mode == vi_mode)
+    _rl_vi_initialize_line ();
+#endif
+
+  /* Each line starts in insert mode (the default). */
+  _rl_set_insert_mode (RL_IM_DEFAULT, 1);
+
+  return 0;
+}
+
+#if 0
+#if defined (__EMX__)
+static void
+_emx_build_environ ()
+{
+  TIB *tibp;
+  PIB *pibp;
+  char *t, **tp;
+  int c;
+
+  DosGetInfoBlocks (&tibp, &pibp);
+  t = pibp->pib_pchenv;
+  for (c = 1; *t; c++)
+    t += strlen (t) + 1;
+  tp = environ = (char **)xmalloc ((c + 1) * sizeof (char *));
+  t = pibp->pib_pchenv;
+  while (*t)
+    {
+      *tp++ = t;
+      t += strlen (t) + 1;
+    }
+  *tp = 0;
+}
+#endif /* __EMX__ */
+#endif
+
+/* Initialize the entire state of the world. */
+static void
+readline_initialize_everything ()
+{
+#if 0
+#if defined (__EMX__)
+  if (environ == 0)
+    _emx_build_environ ();
+#endif
+#endif
+
+#if 0
+  /* Find out if we are running in Emacs -- UNUSED. */
+  running_in_emacs = sh_get_env_value ("EMACS") != (char *)0;
+#endif
+
+  /* Set up input and output if they are not already set up. */
+  if (!rl_instream)
+    rl_instream = stdin;
+
+  if (!rl_outstream)
+    rl_outstream = stdout;
+
+  /* Bind _rl_in_stream and _rl_out_stream immediately.  These values
+     may change, but they may also be used before readline_internal ()
+     is called. */
+  _rl_in_stream = rl_instream;
+  _rl_out_stream = rl_outstream;
+
+  /* Allocate data structures. */
+  if (rl_line_buffer == 0)
+    rl_line_buffer = (char *)xmalloc (rl_line_buffer_len = DEFAULT_BUFFER_SIZE);
+
+  /* Initialize the terminal interface. */
+  if (rl_terminal_name == 0)
+    rl_terminal_name = sh_get_env_value ("TERM");
+  _rl_init_terminal_io (rl_terminal_name);
+
+  /* Bind tty characters to readline functions. */
+  readline_default_bindings ();
+
+  /* Initialize the function names. */
+  rl_initialize_funmap ();
+
+  /* Decide whether we should automatically go into eight-bit mode. */
+  _rl_init_eightbit ();
+      
+  /* Read in the init file. */
+  rl_read_init_file ((char *)NULL);
+
+  /* XXX */
+  if (_rl_horizontal_scroll_mode && _rl_term_autowrap)
+    {
+      _rl_screenwidth--;
+      _rl_screenchars -= _rl_screenheight;
+    }
+
+  /* Override the effect of any `set keymap' assignments in the
+     inputrc file. */
+  rl_set_keymap_from_edit_mode ();
+
+  /* Try to bind a common arrow key prefix, if not already bound. */
+  bind_arrow_keys ();
+
+  /* Bind the bracketed paste prefix assuming that the user will enable
+     it on terminals that support it. */
+  bind_bracketed_paste_prefix ();
+
+  /* If the completion parser's default word break characters haven't
+     been set yet, then do so now. */
+  if (rl_completer_word_break_characters == (char *)NULL)
+    rl_completer_word_break_characters = (char *)rl_basic_word_break_characters;
+
+#if defined (COLOR_SUPPORT)
+  if (_rl_colored_stats || _rl_colored_completion_prefix)
+    _rl_parse_colors ();
+#endif
+
+  rl_executing_keyseq = malloc (_rl_executing_keyseq_size = 16);
+  if (rl_executing_keyseq)
+    rl_executing_keyseq[0] = '\0';
+}
+
+/* If this system allows us to look at the values of the regular
+   input editing characters, then bind them to their readline
+   equivalents, iff the characters are not bound to keymaps. */
+static void
+readline_default_bindings ()
+{
+  if (_rl_bind_stty_chars)
+    rl_tty_set_default_bindings (_rl_keymap);
+}
+
+/* Reset the default bindings for the terminal special characters we're
+   interested in back to rl_insert and read the new ones. */
+static void
+reset_default_bindings ()
+{
+  if (_rl_bind_stty_chars)
+    {
+      rl_tty_unset_default_bindings (_rl_keymap);
+      rl_tty_set_default_bindings (_rl_keymap);
+    }
+}
+
+/* Bind some common arrow key sequences in MAP. */
+static void
+bind_arrow_keys_internal (map)
+     Keymap map;
+{
+  Keymap xkeymap;
+
+  xkeymap = _rl_keymap;
+  _rl_keymap = map;
+
+#if defined (__MSDOS__)
+  rl_bind_keyseq_if_unbound ("\033[0A", rl_get_previous_history);
+  rl_bind_keyseq_if_unbound ("\033[0B", rl_backward_char);
+  rl_bind_keyseq_if_unbound ("\033[0C", rl_forward_char);
+  rl_bind_keyseq_if_unbound ("\033[0D", rl_get_next_history);
+#endif
+
+  rl_bind_keyseq_if_unbound ("\033[A", rl_get_previous_history);
+  rl_bind_keyseq_if_unbound ("\033[B", rl_get_next_history);
+  rl_bind_keyseq_if_unbound ("\033[C", rl_forward_char);
+  rl_bind_keyseq_if_unbound ("\033[D", rl_backward_char);
+  rl_bind_keyseq_if_unbound ("\033[H", rl_beg_of_line);
+  rl_bind_keyseq_if_unbound ("\033[F", rl_end_of_line);
+
+  rl_bind_keyseq_if_unbound ("\033OA", rl_get_previous_history);
+  rl_bind_keyseq_if_unbound ("\033OB", rl_get_next_history);
+  rl_bind_keyseq_if_unbound ("\033OC", rl_forward_char);
+  rl_bind_keyseq_if_unbound ("\033OD", rl_backward_char);
+  rl_bind_keyseq_if_unbound ("\033OH", rl_beg_of_line);
+  rl_bind_keyseq_if_unbound ("\033OF", rl_end_of_line);
+
+#if defined (__MINGW32__)
+  rl_bind_keyseq_if_unbound ("\340H", rl_get_previous_history);
+  rl_bind_keyseq_if_unbound ("\340P", rl_get_next_history);
+  rl_bind_keyseq_if_unbound ("\340M", rl_forward_char);
+  rl_bind_keyseq_if_unbound ("\340K", rl_backward_char);
+  rl_bind_keyseq_if_unbound ("\340G", rl_beg_of_line);
+  rl_bind_keyseq_if_unbound ("\340O", rl_end_of_line);
+  rl_bind_keyseq_if_unbound ("\340S", rl_delete);
+  rl_bind_keyseq_if_unbound ("\340R", rl_overwrite_mode);
+
+  /* These may or may not work because of the embedded NUL. */
+  rl_bind_keyseq_if_unbound ("\\000H", rl_get_previous_history);
+  rl_bind_keyseq_if_unbound ("\\000P", rl_get_next_history);
+  rl_bind_keyseq_if_unbound ("\\000M", rl_forward_char);
+  rl_bind_keyseq_if_unbound ("\\000K", rl_backward_char);
+  rl_bind_keyseq_if_unbound ("\\000G", rl_beg_of_line);
+  rl_bind_keyseq_if_unbound ("\\000O", rl_end_of_line);
+  rl_bind_keyseq_if_unbound ("\\000S", rl_delete);
+  rl_bind_keyseq_if_unbound ("\\000R", rl_overwrite_mode);
+#endif
+
+  _rl_keymap = xkeymap;
+}
+
+/* Try and bind the common arrow key prefixes after giving termcap and
+   the inputrc file a chance to bind them and create `real' keymaps
+   for the arrow key prefix. */
+static void
+bind_arrow_keys ()
+{
+  bind_arrow_keys_internal (emacs_standard_keymap);
+
+#if defined (VI_MODE)
+  bind_arrow_keys_internal (vi_movement_keymap);
+  /* Unbind vi_movement_keymap[ESC] to allow users to repeatedly hit ESC
+     in vi command mode while still allowing the arrow keys to work. */
+  if (vi_movement_keymap[ESC].type == ISKMAP)
+    rl_bind_keyseq_in_map ("\033", (rl_command_func_t *)NULL, vi_movement_keymap);
+  bind_arrow_keys_internal (vi_insertion_keymap);
+#endif
+}
+
+static void
+bind_bracketed_paste_prefix ()
+{
+  Keymap xkeymap;
+
+  xkeymap = _rl_keymap;
+
+  _rl_keymap = emacs_standard_keymap;
+  rl_bind_keyseq_if_unbound (BRACK_PASTE_PREF, rl_bracketed_paste_begin);
+  
+  _rl_keymap = vi_insertion_keymap;
+  rl_bind_keyseq_if_unbound (BRACK_PASTE_PREF, rl_bracketed_paste_begin);
+
+  _rl_keymap = xkeymap;
+}
+  
+/* **************************************************************** */
+/*                                                                 */
+/*             Saving and Restoring Readline's state               */
+/*                                                                 */
+/* **************************************************************** */
+
+int
+rl_save_state (sp)
+     struct readline_state *sp;
+{
+  if (sp == 0)
+    return -1;
+
+  sp->point = rl_point;
+  sp->end = rl_end;
+  sp->mark = rl_mark;
+  sp->buffer = rl_line_buffer;
+  sp->buflen = rl_line_buffer_len;
+  sp->ul = rl_undo_list;
+  sp->prompt = rl_prompt;
+
+  sp->rlstate = rl_readline_state;
+  sp->done = rl_done;
+  sp->kmap = _rl_keymap;
+
+  sp->lastfunc = rl_last_func;
+  sp->insmode = rl_insert_mode;
+  sp->edmode = rl_editing_mode;
+  sp->kseq = rl_executing_keyseq;
+  sp->kseqlen = rl_key_sequence_length;
+  sp->inf = rl_instream;
+  sp->outf = rl_outstream;
+  sp->pendingin = rl_pending_input;
+  sp->macro = rl_executing_macro;
+
+  sp->catchsigs = rl_catch_signals;
+  sp->catchsigwinch = rl_catch_sigwinch;
+
+  sp->entryfunc = rl_completion_entry_function;
+  sp->menuentryfunc = rl_menu_completion_entry_function;
+  sp->ignorefunc = rl_ignore_some_completions_function;
+  sp->attemptfunc = rl_attempted_completion_function;
+  sp->wordbreakchars = rl_completer_word_break_characters;
+
+  return (0);
+}
+
+int
+rl_restore_state (sp)
+     struct readline_state *sp;
+{
+  if (sp == 0)
+    return -1;
+
+  rl_point = sp->point;
+  rl_end = sp->end;
+  rl_mark = sp->mark;
+  the_line = rl_line_buffer = sp->buffer;
+  rl_line_buffer_len = sp->buflen;
+  rl_undo_list = sp->ul;
+  rl_prompt = sp->prompt;
+
+  rl_readline_state = sp->rlstate;
+  rl_done = sp->done;
+  _rl_keymap = sp->kmap;
+
+  rl_last_func = sp->lastfunc;
+  rl_insert_mode = sp->insmode;
+  rl_editing_mode = sp->edmode;
+  rl_executing_keyseq = sp->kseq;
+  rl_key_sequence_length = sp->kseqlen;
+  rl_instream = sp->inf;
+  rl_outstream = sp->outf;
+  rl_pending_input = sp->pendingin;
+  rl_executing_macro = sp->macro;
+
+  rl_catch_signals = sp->catchsigs;
+  rl_catch_sigwinch = sp->catchsigwinch;
+
+  rl_completion_entry_function = sp->entryfunc;
+  rl_menu_completion_entry_function = sp->menuentryfunc;
+  rl_ignore_some_completions_function = sp->ignorefunc;
+  rl_attempted_completion_function = sp->attemptfunc;
+  rl_completer_word_break_characters = sp->wordbreakchars;
+
+  return (0);
+}
index 06d85ab372fbe5d10260430a3ecbbd7afdff4413..0b0a32b90e2ac8328108fc070f0bd9ee13a45109 100644 (file)
@@ -132,6 +132,12 @@ extern int _rl_walphabetic PARAMS((wchar_t));
 #  define WCWIDTH(wc)  wcwidth(wc)
 #endif
 
+#if defined (WCWIDTH_BROKEN)
+#  define IS_COMBINING_CHAR(x) (WCWIDTH(x) == 0 && iswcntrl(x) == 0)
+#else
+#  define IS_COMBINING_CHAR(x) (WCWIDTH(x) == 0)
+#endif
+
 #else /* !HANDLE_MULTIBYTE */
 
 #undef MB_LEN_MAX
diff --git a/lib/readline/rlmbutil.h~ b/lib/readline/rlmbutil.h~
new file mode 100644 (file)
index 0000000..06d85ab
--- /dev/null
@@ -0,0 +1,163 @@
+/* rlmbutil.h -- utility functions for multibyte characters. */
+
+/* Copyright (C) 2001-2009 Free Software Foundation, Inc.
+
+   This file is part of the GNU Readline Library (Readline), a library
+   for reading lines of text with interactive input and history editing.      
+
+   Readline is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   Readline 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 Readline.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#if !defined (_RL_MBUTIL_H_)
+#define _RL_MBUTIL_H_
+
+#include "rlstdc.h"
+
+/************************************************/
+/* check multibyte capability for I18N code     */
+/************************************************/
+
+/* For platforms which support the ISO C amendement 1 functionality we
+   support user defined character classes.  */
+   /* Solaris 2.5 has a bug: <wchar.h> must be included before <wctype.h>.  */
+#if defined (HAVE_WCTYPE_H) && defined (HAVE_WCHAR_H) && defined (HAVE_LOCALE_H)
+#  include <wchar.h>
+#  include <wctype.h>
+#  if defined (HAVE_ISWCTYPE) && \
+      defined (HAVE_ISWLOWER) && \
+      defined (HAVE_ISWUPPER) && \
+      defined (HAVE_MBSRTOWCS) && \
+      defined (HAVE_MBRTOWC) && \
+      defined (HAVE_MBRLEN) && \
+      defined (HAVE_TOWLOWER) && \
+      defined (HAVE_TOWUPPER) && \
+      defined (HAVE_WCHAR_T) && \
+      defined (HAVE_WCWIDTH)
+     /* system is supposed to support XPG5 */
+#    define HANDLE_MULTIBYTE      1
+#  endif
+#endif
+
+/* If we don't want multibyte chars even on a system that supports them, let
+   the configuring user turn multibyte support off. */
+#if defined (NO_MULTIBYTE_SUPPORT)
+#  undef HANDLE_MULTIBYTE
+#endif
+
+/* Some systems, like BeOS, have multibyte encodings but lack mbstate_t.  */
+#if HANDLE_MULTIBYTE && !defined (HAVE_MBSTATE_T)
+#  define wcsrtombs(dest, src, len, ps) (wcsrtombs) (dest, src, len, 0)
+#  define mbsrtowcs(dest, src, len, ps) (mbsrtowcs) (dest, src, len, 0)
+#  define wcrtomb(s, wc, ps) (wcrtomb) (s, wc, 0)
+#  define mbrtowc(pwc, s, n, ps) (mbrtowc) (pwc, s, n, 0)
+#  define mbrlen(s, n, ps) (mbrlen) (s, n, 0)
+#  define mbstate_t int
+#endif
+
+/* Make sure MB_LEN_MAX is at least 16 on systems that claim to be able to
+   handle multibyte chars (some systems define MB_LEN_MAX as 1) */
+#ifdef HANDLE_MULTIBYTE
+#  include <limits.h>
+#  if defined(MB_LEN_MAX) && (MB_LEN_MAX < 16)
+#    undef MB_LEN_MAX
+#  endif
+#  if !defined (MB_LEN_MAX)
+#    define MB_LEN_MAX 16
+#  endif
+#endif
+
+/************************************************/
+/* end of multibyte capability checks for I18N  */
+/************************************************/
+
+/*
+ * Flags for _rl_find_prev_mbchar and _rl_find_next_mbchar:
+ *
+ * MB_FIND_ANY         find any multibyte character
+ * MB_FIND_NONZERO     find a non-zero-width multibyte character
+ */
+
+#define MB_FIND_ANY    0x00
+#define MB_FIND_NONZERO        0x01
+
+extern int _rl_find_prev_mbchar PARAMS((char *, int, int));
+extern int _rl_find_next_mbchar PARAMS((char *, int, int, int));
+
+#ifdef HANDLE_MULTIBYTE
+
+extern int _rl_compare_chars PARAMS((char *, int, mbstate_t *, char *, int, mbstate_t *));
+extern int _rl_get_char_len PARAMS((char *, mbstate_t *));
+extern int _rl_adjust_point PARAMS((char *, int, mbstate_t *));
+
+extern int _rl_read_mbchar PARAMS((char *, int));
+extern int _rl_read_mbstring PARAMS((int, char *, int));
+
+extern int _rl_is_mbchar_matched PARAMS((char *, int, int, char *, int));
+
+extern wchar_t _rl_char_value PARAMS((char *, int));
+extern int _rl_walphabetic PARAMS((wchar_t));
+
+#define _rl_to_wupper(wc)      (iswlower (wc) ? towupper (wc) : (wc))
+#define _rl_to_wlower(wc)      (iswupper (wc) ? towlower (wc) : (wc))
+
+#define MB_NEXTCHAR(b,s,c,f) \
+       ((MB_CUR_MAX > 1 && rl_byte_oriented == 0) \
+               ? _rl_find_next_mbchar ((b), (s), (c), (f)) \
+               : ((s) + (c)))
+#define MB_PREVCHAR(b,s,f) \
+       ((MB_CUR_MAX > 1 && rl_byte_oriented == 0) \
+               ? _rl_find_prev_mbchar ((b), (s), (f)) \
+               : ((s) - 1))
+
+#define MB_INVALIDCH(x)                ((x) == (size_t)-1 || (x) == (size_t)-2)
+#define MB_NULLWCH(x)          ((x) == 0)
+
+/* Unicode combining characters range from U+0300 to U+036F */
+#define UNICODE_COMBINING_CHAR(x) ((x) >= 768 && (x) <= 879)
+
+#if defined (WCWIDTH_BROKEN)
+#  define WCWIDTH(wc)  ((_rl_utf8locale && UNICODE_COMBINING_CHAR(wc)) ? 0 : wcwidth(wc))
+#else
+#  define WCWIDTH(wc)  wcwidth(wc)
+#endif
+
+#else /* !HANDLE_MULTIBYTE */
+
+#undef MB_LEN_MAX
+#undef MB_CUR_MAX
+
+#define MB_LEN_MAX     1
+#define MB_CUR_MAX     1
+
+#define _rl_find_prev_mbchar(b, i, f)          (((i) == 0) ? (i) : ((i) - 1))
+#define _rl_find_next_mbchar(b, i1, i2, f)     ((i1) + (i2))
+
+#define _rl_char_value(buf,ind)        ((buf)[(ind)])
+
+#define _rl_walphabetic(c)     (rl_alphabetic (c))
+
+#define _rl_to_wupper(c)       (_rl_to_upper (c))
+#define _rl_to_wlower(c)       (_rl_to_lower (c))
+
+#define MB_NEXTCHAR(b,s,c,f)   ((s) + (c))
+#define MB_PREVCHAR(b,s,f)     ((s) - 1)
+
+#define MB_INVALIDCH(x)                (0)
+#define MB_NULLWCH(x)          (0)
+
+#endif /* !HANDLE_MULTIBYTE */
+
+extern int rl_byte_oriented;
+
+#endif /* _RL_MBUTIL_H_ */
index 0370029c50f8315665b1cf0c10d5cd76ad6fb146..7f1f076dfcff4fee93e3465fe87747f518cd0125 100644 (file)
@@ -1103,7 +1103,7 @@ _rl_rubout_char (count, key)
       c = rl_line_buffer[--rl_point];
       rl_delete_text (rl_point, orig_point);
       /* The erase-at-end-of-line hack is of questionable merit now. */
-      if (rl_point == rl_end && ISPRINT (c) && _rl_last_c_pos)
+      if (rl_point == rl_end && ISPRINT ((unsigned char)c) && _rl_last_c_pos)
        {
          int l;
          l = rl_character_len (c, rl_point);
@@ -1337,7 +1337,7 @@ rl_change_case (count, op)
        }
       else
        nop = op;
-      if (MB_CUR_MAX == 1 || rl_byte_oriented || isascii (c))
+      if (MB_CUR_MAX == 1 || rl_byte_oriented || isascii ((unsigned char)c))
        {
          nc = (nop == UpCase) ? _rl_to_upper (c) : _rl_to_lower (c);
          rl_line_buffer[start] = nc;
diff --git a/lib/readline/text.c~ b/lib/readline/text.c~
new file mode 100644 (file)
index 0000000..bb496d5
--- /dev/null
@@ -0,0 +1,1705 @@
+/* text.c -- text handling commands for readline. */
+
+/* Copyright (C) 1987-2010 Free Software Foundation, Inc.
+
+   This file is part of the GNU Readline Library (Readline), a library
+   for reading lines of text with interactive input and history editing.      
+
+   Readline is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   Readline 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 Readline.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#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"
+
+#if defined (__EMX__)
+#  define INCL_DOSPROCESS
+#  include <os2.h>
+#endif /* __EMX__ */
+
+/* Some standard library routines. */
+#include "readline.h"
+#include "history.h"
+
+#include "rlprivate.h"
+#include "rlshell.h"
+#include "xmalloc.h"
+
+/* Forward declarations. */
+static int rl_change_case PARAMS((int, int));
+static int _rl_char_search PARAMS((int, int, int));
+
+#if defined (READLINE_CALLBACKS)
+static int _rl_insert_next_callback PARAMS((_rl_callback_generic_arg *));
+static int _rl_char_search_callback PARAMS((_rl_callback_generic_arg *));
+#endif
+
+/* The largest chunk of text that can be inserted in one call to
+   rl_insert_text.  Text blocks larger than this are divided. */
+#define TEXT_COUNT_MAX 1024
+
+/* **************************************************************** */
+/*                                                                 */
+/*                     Insert and Delete                           */
+/*                                                                 */
+/* **************************************************************** */
+
+/* Insert a string of text into the line at point.  This is the only
+   way that you should do insertion.  _rl_insert_char () calls this
+   function.  Returns the number of characters inserted. */
+int
+rl_insert_text (string)
+     const char *string;
+{
+  register int i, l;
+
+  l = (string && *string) ? strlen (string) : 0;
+  if (l == 0)
+    return 0;
+
+  if (rl_end + l >= rl_line_buffer_len)
+    rl_extend_line_buffer (rl_end + l);
+
+  for (i = rl_end; i >= rl_point; i--)
+    rl_line_buffer[i + l] = rl_line_buffer[i];
+  strncpy (rl_line_buffer + rl_point, string, l);
+
+  /* Remember how to undo this if we aren't undoing something. */
+  if (_rl_doing_an_undo == 0)
+    {
+      /* If possible and desirable, concatenate the undos. */
+      if ((l == 1) &&
+         rl_undo_list &&
+         (rl_undo_list->what == UNDO_INSERT) &&
+         (rl_undo_list->end == rl_point) &&
+         (rl_undo_list->end - rl_undo_list->start < 20))
+       rl_undo_list->end++;
+      else
+       rl_add_undo (UNDO_INSERT, rl_point, rl_point + l, (char *)NULL);
+    }
+  rl_point += l;
+  rl_end += l;
+  rl_line_buffer[rl_end] = '\0';
+  return l;
+}
+
+/* Delete the string between FROM and TO.  FROM is inclusive, TO is not.
+   Returns the number of characters deleted. */
+int
+rl_delete_text (from, to)
+     int from, to;
+{
+  register char *text;
+  register int diff, i;
+
+  /* Fix it if the caller is confused. */
+  if (from > to)
+    SWAP (from, to);
+
+  /* fix boundaries */
+  if (to > rl_end)
+    {
+      to = rl_end;
+      if (from > to)
+       from = to;
+    }
+  if (from < 0)
+    from = 0;
+
+  text = rl_copy_text (from, to);
+
+  /* Some versions of strncpy() can't handle overlapping arguments. */
+  diff = to - from;
+  for (i = from; i < rl_end - diff; i++)
+    rl_line_buffer[i] = rl_line_buffer[i + diff];
+
+  /* Remember how to undo this delete. */
+  if (_rl_doing_an_undo == 0)
+    rl_add_undo (UNDO_DELETE, from, to, text);
+  else
+    xfree (text);
+
+  rl_end -= diff;
+  rl_line_buffer[rl_end] = '\0';
+  return (diff);
+}
+
+/* Fix up point so that it is within the line boundaries after killing
+   text.  If FIX_MARK_TOO is non-zero, the mark is forced within line
+   boundaries also. */
+
+#define _RL_FIX_POINT(x) \
+       do { \
+       if (x > rl_end) \
+         x = rl_end; \
+       else if (x < 0) \
+         x = 0; \
+       } while (0)
+
+void
+_rl_fix_point (fix_mark_too)
+     int fix_mark_too;
+{
+  _RL_FIX_POINT (rl_point);
+  if (fix_mark_too)
+    _RL_FIX_POINT (rl_mark);
+}
+#undef _RL_FIX_POINT
+
+/* Replace the contents of the line buffer between START and END with
+   TEXT.  The operation is undoable.  To replace the entire line in an
+   undoable mode, use _rl_replace_text(text, 0, rl_end); */
+int
+_rl_replace_text (text, start, end)
+     const char *text;
+     int start, end;
+{
+  int n;
+
+  n = 0;
+  rl_begin_undo_group ();
+  if (start <= end)
+    rl_delete_text (start, end + 1);
+  rl_point = start;
+  if (*text)
+    n = rl_insert_text (text);
+  rl_end_undo_group ();
+
+  return n;
+}
+
+/* Replace the current line buffer contents with TEXT.  If CLEAR_UNDO is
+   non-zero, we free the current undo list. */
+void
+rl_replace_line (text, clear_undo)
+     const char *text;
+     int clear_undo;
+{
+  int len;
+
+  len = strlen (text);
+  if (len >= rl_line_buffer_len)
+    rl_extend_line_buffer (len);
+  strcpy (rl_line_buffer, text);
+  rl_end = len;
+
+  if (clear_undo)
+    rl_free_undo_list ();
+
+  _rl_fix_point (1);
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*                     Readline character functions                */
+/*                                                                 */
+/* **************************************************************** */
+
+/* This is not a gap editor, just a stupid line input routine.  No hair
+   is involved in writing any of the functions, and none should be. */
+
+/* Note that:
+
+   rl_end is the place in the string that we would place '\0';
+   i.e., it is always safe to place '\0' there.
+
+   rl_point is the place in the string where the cursor is.  Sometimes
+   this is the same as rl_end.
+
+   Any command that is called interactively receives two arguments.
+   The first is a count: the numeric arg passed to this command.
+   The second is the key which invoked this command.
+*/
+
+/* **************************************************************** */
+/*                                                                 */
+/*                     Movement Commands                           */
+/*                                                                 */
+/* **************************************************************** */
+
+/* Note that if you `optimize' the display for these functions, you cannot
+   use said functions in other functions which do not do optimizing display.
+   I.e., you will have to update the data base for rl_redisplay, and you
+   might as well let rl_redisplay do that job. */
+
+/* Move forward COUNT bytes. */
+int
+rl_forward_byte (count, key)
+     int count, key;
+{
+  if (count < 0)
+    return (rl_backward_byte (-count, key));
+
+  if (count > 0)
+    {
+      int end, lend;
+
+      end = rl_point + count;
+#if defined (VI_MODE)
+      lend = rl_end > 0 ? rl_end - (VI_COMMAND_MODE()) : rl_end;
+#else
+      lend = rl_end;
+#endif
+
+      if (end > lend)
+       {
+         rl_point = lend;
+         rl_ding ();
+       }
+      else
+       rl_point = end;
+    }
+
+  if (rl_end < 0)
+    rl_end = 0;
+
+  return 0;
+}
+
+int
+_rl_forward_char_internal (count)
+     int count;
+{
+  int point;
+
+#if defined (HANDLE_MULTIBYTE)
+  point = _rl_find_next_mbchar (rl_line_buffer, rl_point, count, MB_FIND_NONZERO);
+
+#if defined (VI_MODE)
+  if (point >= rl_end && VI_COMMAND_MODE())
+    point = _rl_find_prev_mbchar (rl_line_buffer, rl_end, MB_FIND_NONZERO);
+#endif
+
+    if (rl_end < 0)
+       rl_end = 0;
+#else
+  point = rl_point + count;
+  if (point > rl_end)
+    point = rl_end;
+#endif
+
+  return (point);
+}
+
+#if defined (HANDLE_MULTIBYTE)
+/* Move forward COUNT characters. */
+int
+rl_forward_char (count, key)
+     int count, key;
+{
+  int point;
+
+  if (MB_CUR_MAX == 1 || rl_byte_oriented)
+    return (rl_forward_byte (count, key));
+
+  if (count < 0)
+    return (rl_backward_char (-count, key));
+
+  if (count > 0)
+    {
+      if (rl_point == rl_end && EMACS_MODE())
+       {
+         rl_ding ();
+         return 0;
+       }
+
+      point = _rl_forward_char_internal (count);
+
+      if (rl_point == point)
+       rl_ding ();
+
+      rl_point = point;
+    }
+
+  return 0;
+}
+#else /* !HANDLE_MULTIBYTE */
+int
+rl_forward_char (count, key)
+     int count, key;
+{
+  return (rl_forward_byte (count, key));
+}
+#endif /* !HANDLE_MULTIBYTE */
+  
+/* Backwards compatibility. */
+int
+rl_forward (count, key)
+     int count, key;
+{
+  return (rl_forward_char (count, key));
+}
+
+/* Move backward COUNT bytes. */
+int
+rl_backward_byte (count, key)
+     int count, key;
+{
+  if (count < 0)
+    return (rl_forward_byte (-count, key));
+
+  if (count > 0)
+    {
+      if (rl_point < count)
+       {
+         rl_point = 0;
+         rl_ding ();
+       }
+      else
+       rl_point -= count;
+    }
+
+  if (rl_point < 0)
+    rl_point = 0;
+
+  return 0;
+}
+
+#if defined (HANDLE_MULTIBYTE)
+/* Move backward COUNT characters. */
+int
+rl_backward_char (count, key)
+     int count, key;
+{
+  int point;
+
+  if (MB_CUR_MAX == 1 || rl_byte_oriented)
+    return (rl_backward_byte (count, key));
+
+  if (count < 0)
+    return (rl_forward_char (-count, key));
+
+  if (count > 0)
+    {
+      point = rl_point;
+
+      while (count > 0 && point > 0)
+       {
+         point = _rl_find_prev_mbchar (rl_line_buffer, point, MB_FIND_NONZERO);
+         count--;
+       }
+      if (count > 0)
+       {
+         rl_point = 0;
+         rl_ding ();
+       }
+      else
+        rl_point = point;
+    }
+
+  return 0;
+}
+#else
+int
+rl_backward_char (count, key)
+     int count, key;
+{
+  return (rl_backward_byte (count, key));
+}
+#endif
+
+/* Backwards compatibility. */
+int
+rl_backward (count, key)
+     int count, key;
+{
+  return (rl_backward_char (count, key));
+}
+
+/* Move to the beginning of the line. */
+int
+rl_beg_of_line (count, key)
+     int count, key;
+{
+  rl_point = 0;
+  return 0;
+}
+
+/* Move to the end of the line. */
+int
+rl_end_of_line (count, key)
+     int count, key;
+{
+  rl_point = rl_end;
+  return 0;
+}
+
+/* Move forward a word.  We do what Emacs does.  Handles multibyte chars. */
+int
+rl_forward_word (count, key)
+     int count, key;
+{
+  int c;
+
+  if (count < 0)
+    return (rl_backward_word (-count, key));
+
+  while (count)
+    {
+      if (rl_point == rl_end)
+       return 0;
+
+      /* If we are not in a word, move forward until we are in one.
+        Then, move forward until we hit a non-alphabetic character. */
+      c = _rl_char_value (rl_line_buffer, rl_point);
+
+      if (_rl_walphabetic (c) == 0)
+       {
+         rl_point = MB_NEXTCHAR (rl_line_buffer, rl_point, 1, MB_FIND_NONZERO);
+         while (rl_point < rl_end)
+           {
+             c = _rl_char_value (rl_line_buffer, rl_point);
+             if (_rl_walphabetic (c))
+               break;
+             rl_point = MB_NEXTCHAR (rl_line_buffer, rl_point, 1, MB_FIND_NONZERO);
+           }
+       }
+
+      if (rl_point == rl_end)
+       return 0;
+
+      rl_point = MB_NEXTCHAR (rl_line_buffer, rl_point, 1, MB_FIND_NONZERO);
+      while (rl_point < rl_end)
+       {
+         c = _rl_char_value (rl_line_buffer, rl_point);
+         if (_rl_walphabetic (c) == 0)
+           break;
+         rl_point = MB_NEXTCHAR (rl_line_buffer, rl_point, 1, MB_FIND_NONZERO);
+       }
+
+      --count;
+    }
+
+  return 0;
+}
+
+/* Move backward a word.  We do what Emacs does.  Handles multibyte chars. */
+int
+rl_backward_word (count, key)
+     int count, key;
+{
+  int c, p;
+
+  if (count < 0)
+    return (rl_forward_word (-count, key));
+
+  while (count)
+    {
+      if (rl_point == 0)
+       return 0;
+
+      /* Like rl_forward_word (), except that we look at the characters
+        just before point. */
+
+      p = MB_PREVCHAR (rl_line_buffer, rl_point, MB_FIND_NONZERO);
+      c = _rl_char_value (rl_line_buffer, p);
+
+      if (_rl_walphabetic (c) == 0)
+       {
+         rl_point = p;
+         while (rl_point > 0)
+           {
+             p = MB_PREVCHAR (rl_line_buffer, rl_point, MB_FIND_NONZERO);
+             c = _rl_char_value (rl_line_buffer, p);
+             if (_rl_walphabetic (c))
+               break;
+             rl_point = p;
+           }
+       }
+
+      while (rl_point)
+       {
+         p = MB_PREVCHAR (rl_line_buffer, rl_point, MB_FIND_NONZERO);
+         c = _rl_char_value (rl_line_buffer, p);         
+         if (_rl_walphabetic (c) == 0)
+           break;
+         else
+           rl_point = p;
+       }
+
+      --count;
+    }
+
+  return 0;
+}
+
+/* Clear the current line.  Numeric argument to C-l does this. */
+int
+rl_refresh_line (ignore1, ignore2)
+     int ignore1, ignore2;
+{
+  int curr_line;
+
+  curr_line = _rl_current_display_line ();
+
+  _rl_move_vert (curr_line);
+  _rl_move_cursor_relative (0, rl_line_buffer);   /* XXX is this right */
+
+  _rl_clear_to_eol (0);                /* arg of 0 means to not use spaces */
+
+  rl_forced_update_display ();
+  rl_display_fixed = 1;
+
+  return 0;
+}
+
+/* C-l typed to a line without quoting clears the screen, and then reprints
+   the prompt and the current input line.  Given a numeric arg, redraw only
+   the current line. */
+int
+rl_clear_screen (count, key)
+     int count, key;
+{
+  if (rl_explicit_arg)
+    {
+      rl_refresh_line (count, key);
+      return 0;
+    }
+
+  _rl_clear_screen ();         /* calls termcap function to clear screen */
+  rl_forced_update_display ();
+  rl_display_fixed = 1;
+
+  return 0;
+}
+
+int
+rl_skip_csi_sequence (count, key)
+     int count, key;
+{
+  int ch;
+
+  RL_SETSTATE (RL_STATE_MOREINPUT);
+  do
+    ch = rl_read_key ();
+  while (ch >= 0x20 && ch < 0x40);
+  RL_UNSETSTATE (RL_STATE_MOREINPUT);
+
+  return 0;
+}
+
+int
+rl_arrow_keys (count, c)
+     int count, c;
+{
+  int ch;
+
+  RL_SETSTATE(RL_STATE_MOREINPUT);
+  ch = rl_read_key ();
+  RL_UNSETSTATE(RL_STATE_MOREINPUT);
+
+  switch (_rl_to_upper (ch))
+    {
+    case 'A':
+      rl_get_previous_history (count, ch);
+      break;
+
+    case 'B':
+      rl_get_next_history (count, ch);
+      break;
+
+    case 'C':
+      if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
+       rl_forward_char (count, ch);
+      else
+       rl_forward_byte (count, ch);
+      break;
+
+    case 'D':
+      if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
+       rl_backward_char (count, ch);
+      else
+       rl_backward_byte (count, ch);
+      break;
+
+    default:
+      rl_ding ();
+    }
+
+  return 0;
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*                     Text commands                               */
+/*                                                                 */
+/* **************************************************************** */
+
+#ifdef HANDLE_MULTIBYTE
+static char pending_bytes[MB_LEN_MAX];
+static int pending_bytes_length = 0;
+static mbstate_t ps = {0};
+#endif
+
+/* Insert the character C at the current location, moving point forward.
+   If C introduces a multibyte sequence, we read the whole sequence and
+   then insert the multibyte char into the line buffer. */
+int
+_rl_insert_char (count, c)
+     int count, c;
+{
+  register int i;
+  char *string;
+#ifdef HANDLE_MULTIBYTE
+  int string_size;
+  char incoming[MB_LEN_MAX + 1];
+  int incoming_length = 0;
+  mbstate_t ps_back;
+  static int stored_count = 0;
+#endif
+
+  if (count <= 0)
+    return 0;
+
+#if defined (HANDLE_MULTIBYTE)
+  if (MB_CUR_MAX == 1 || rl_byte_oriented)
+    {
+      incoming[0] = c;
+      incoming[1] = '\0';
+      incoming_length = 1;
+    }
+  else
+    {
+      wchar_t wc;
+      size_t ret;
+
+      if (stored_count <= 0)
+       stored_count = count;
+      else
+       count = stored_count;
+
+      ps_back = ps;
+      pending_bytes[pending_bytes_length++] = c;
+      ret = mbrtowc (&wc, pending_bytes, pending_bytes_length, &ps);
+
+      if (ret == (size_t)-2)
+       {
+         /* Bytes too short to compose character, try to wait for next byte.
+            Restore the state of the byte sequence, because in this case the
+            effect of mbstate is undefined. */
+         ps = ps_back;
+         return 1;
+       }
+      else if (ret == (size_t)-1)
+       {
+         /* Invalid byte sequence for the current locale.  Treat first byte
+            as a single character. */
+         incoming[0] = pending_bytes[0];
+         incoming[1] = '\0';
+         incoming_length = 1;
+         pending_bytes_length--;
+         memmove (pending_bytes, pending_bytes + 1, pending_bytes_length);
+         /* Clear the state of the byte sequence, because in this case the
+            effect of mbstate is undefined. */
+         memset (&ps, 0, sizeof (mbstate_t));
+       }
+      else if (ret == (size_t)0)
+       {
+         incoming[0] = '\0';
+         incoming_length = 0;
+         pending_bytes_length--;
+         /* Clear the state of the byte sequence, because in this case the
+            effect of mbstate is undefined. */
+         memset (&ps, 0, sizeof (mbstate_t));
+       }
+      else
+       {
+         /* We successfully read a single multibyte character. */
+         memcpy (incoming, pending_bytes, pending_bytes_length);
+         incoming[pending_bytes_length] = '\0';
+         incoming_length = pending_bytes_length;
+         pending_bytes_length = 0;
+       }
+    }
+#endif /* HANDLE_MULTIBYTE */
+         
+  /* If we can optimize, then do it.  But don't let people crash
+     readline because of extra large arguments. */
+  if (count > 1 && count <= TEXT_COUNT_MAX)
+    {
+#if defined (HANDLE_MULTIBYTE)
+      string_size = count * incoming_length;
+      string = (char *)xmalloc (1 + string_size);
+
+      i = 0;
+      while (i < string_size)
+       {
+         strncpy (string + i, incoming, incoming_length);
+         i += incoming_length;
+       }
+      incoming_length = 0;
+      stored_count = 0;
+#else /* !HANDLE_MULTIBYTE */
+      string = (char *)xmalloc (1 + count);
+
+      for (i = 0; i < count; i++)
+       string[i] = c;
+#endif /* !HANDLE_MULTIBYTE */
+
+      string[i] = '\0';
+      rl_insert_text (string);
+      xfree (string);
+
+      return 0;
+    }
+
+  if (count > TEXT_COUNT_MAX)
+    {
+      int decreaser;
+#if defined (HANDLE_MULTIBYTE)
+      string_size = incoming_length * TEXT_COUNT_MAX;
+      string = (char *)xmalloc (1 + string_size);
+
+      i = 0;
+      while (i < string_size)
+       {
+         strncpy (string + i, incoming, incoming_length);
+         i += incoming_length;
+       }
+
+      while (count)
+       {
+         decreaser = (count > TEXT_COUNT_MAX) ? TEXT_COUNT_MAX : count;
+         string[decreaser*incoming_length] = '\0';
+         rl_insert_text (string);
+         count -= decreaser;
+       }
+
+      xfree (string);
+      incoming_length = 0;
+      stored_count = 0;
+#else /* !HANDLE_MULTIBYTE */
+      char str[TEXT_COUNT_MAX+1];
+
+      for (i = 0; i < TEXT_COUNT_MAX; i++)
+       str[i] = c;
+
+      while (count)
+       {
+         decreaser = (count > TEXT_COUNT_MAX ? TEXT_COUNT_MAX : count);
+         str[decreaser] = '\0';
+         rl_insert_text (str);
+         count -= decreaser;
+       }
+#endif /* !HANDLE_MULTIBYTE */
+
+      return 0;
+    }
+
+  if (MB_CUR_MAX == 1 || rl_byte_oriented)
+    {
+      /* We are inserting a single character.
+        If there is pending input, then make a string of all of the
+        pending characters that are bound to rl_insert, and insert
+        them all.  Don't do this if we're current reading input from
+        a macro. */
+      if ((RL_ISSTATE (RL_STATE_MACROINPUT) == 0) && _rl_pushed_input_available ())
+       _rl_insert_typein (c);
+      else
+       {
+         /* Inserting a single character. */
+         char str[2];
+
+         str[1] = '\0';
+         str[0] = c;
+         rl_insert_text (str);
+       }
+    }
+#if defined (HANDLE_MULTIBYTE)
+  else
+    {
+      rl_insert_text (incoming);
+      stored_count = 0;
+    }
+#endif
+
+  return 0;
+}
+
+/* Overwrite the character at point (or next COUNT characters) with C.
+   If C introduces a multibyte character sequence, read the entire sequence
+   before starting the overwrite loop. */
+int
+_rl_overwrite_char (count, c)
+     int count, c;
+{
+  int i;
+#if defined (HANDLE_MULTIBYTE)
+  char mbkey[MB_LEN_MAX];
+  int k;
+
+  /* Read an entire multibyte character sequence to insert COUNT times. */
+  if (count > 0 && MB_CUR_MAX > 1 && rl_byte_oriented == 0)
+    k = _rl_read_mbstring (c, mbkey, MB_LEN_MAX);
+#endif
+
+  rl_begin_undo_group ();
+
+  for (i = 0; i < count; i++)
+    {
+#if defined (HANDLE_MULTIBYTE)
+      if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
+       rl_insert_text (mbkey);
+      else
+#endif
+       _rl_insert_char (1, c);
+
+      if (rl_point < rl_end)
+       rl_delete (1, c);
+    }
+
+  rl_end_undo_group ();
+
+  return 0;
+}
+
+int
+rl_insert (count, c)
+     int count, c;
+{
+  return (rl_insert_mode == RL_IM_INSERT ? _rl_insert_char (count, c)
+                                        : _rl_overwrite_char (count, c));
+}
+
+/* Insert the next typed character verbatim. */
+static int
+_rl_insert_next (count)
+     int count;
+{
+  int c;
+
+  RL_SETSTATE(RL_STATE_MOREINPUT);
+  c = rl_read_key ();
+  RL_UNSETSTATE(RL_STATE_MOREINPUT);
+
+  if (c < 0)
+    return 1;
+
+  if (RL_ISSTATE (RL_STATE_MACRODEF))
+    _rl_add_macro_char (c);
+
+#if defined (HANDLE_SIGNALS)
+  if (RL_ISSTATE (RL_STATE_CALLBACK) == 0)
+    _rl_restore_tty_signals ();
+#endif
+
+  return (_rl_insert_char (count, c));  
+}
+
+#if defined (READLINE_CALLBACKS)
+static int
+_rl_insert_next_callback (data)
+     _rl_callback_generic_arg *data;
+{
+  int count;
+
+  count = data->count;
+
+  /* Deregister function, let rl_callback_read_char deallocate data */
+  _rl_callback_func = 0;
+  _rl_want_redisplay = 1;
+  return _rl_insert_next (count);
+}
+#endif
+  
+int
+rl_quoted_insert (count, key)
+     int count, key;
+{
+  /* Let's see...should the callback interface futz with signal handling? */
+#if defined (HANDLE_SIGNALS)
+  if (RL_ISSTATE (RL_STATE_CALLBACK) == 0)
+    _rl_disable_tty_signals ();
+#endif
+
+#if defined (READLINE_CALLBACKS)
+  if (RL_ISSTATE (RL_STATE_CALLBACK))
+    {
+      _rl_callback_data = _rl_callback_data_alloc (count);
+      _rl_callback_func = _rl_insert_next_callback;
+      return (0);
+    }
+#endif
+      
+  return _rl_insert_next (count);
+}
+
+/* Insert a tab character. */
+int
+rl_tab_insert (count, key)
+     int count, key;
+{
+  return (_rl_insert_char (count, '\t'));
+}
+
+/* What to do when a NEWLINE is pressed.  We accept the whole line.
+   KEY is the key that invoked this command.  I guess it could have
+   meaning in the future. */
+int
+rl_newline (count, key)
+     int count, key;
+{
+  rl_done = 1;
+
+  if (_rl_history_preserve_point)
+    _rl_history_saved_point = (rl_point == rl_end) ? -1 : rl_point;
+
+  RL_SETSTATE(RL_STATE_DONE);
+
+#if defined (VI_MODE)
+  if (rl_editing_mode == vi_mode)
+    {
+      _rl_vi_done_inserting ();
+      if (_rl_vi_textmod_command (_rl_vi_last_command) == 0)   /* XXX */
+       _rl_vi_reset_last ();
+    }
+#endif /* VI_MODE */
+
+  /* If we've been asked to erase empty lines, suppress the final update,
+     since _rl_update_final calls rl_crlf(). */
+  if (rl_erase_empty_line && rl_point == 0 && rl_end == 0)
+    return 0;
+
+  if (_rl_echoing_p)
+    _rl_update_final ();
+  return 0;
+}
+
+/* What to do for some uppercase characters, like meta characters,
+   and some characters appearing in emacs_ctlx_keymap.  This function
+   is just a stub, you bind keys to it and the code in _rl_dispatch ()
+   is special cased. */
+int
+rl_do_lowercase_version (ignore1, ignore2)
+     int ignore1, ignore2;
+{
+  return 0;
+}
+
+/* This is different from what vi does, so the code's not shared.  Emacs
+   rubout in overwrite mode has one oddity:  it replaces a control
+   character that's displayed as two characters (^X) with two spaces. */
+int
+_rl_overwrite_rubout (count, key)
+     int count, key;
+{
+  int opoint;
+  int i, l;
+
+  if (rl_point == 0)
+    {
+      rl_ding ();
+      return 1;
+    }
+
+  opoint = rl_point;
+
+  /* L == number of spaces to insert */
+  for (i = l = 0; i < count; i++)
+    {
+      rl_backward_char (1, key);
+      l += rl_character_len (rl_line_buffer[rl_point], rl_point);      /* not exactly right */
+    }
+
+  rl_begin_undo_group ();
+
+  if (count > 1 || rl_explicit_arg)
+    rl_kill_text (opoint, rl_point);
+  else
+    rl_delete_text (opoint, rl_point);
+
+  /* Emacs puts point at the beginning of the sequence of spaces. */
+  if (rl_point < rl_end)
+    {
+      opoint = rl_point;
+      _rl_insert_char (l, ' ');
+      rl_point = opoint;
+    }
+
+  rl_end_undo_group ();
+
+  return 0;
+}
+  
+/* Rubout the character behind point. */
+int
+rl_rubout (count, key)
+     int count, key;
+{
+  if (count < 0)
+    return (rl_delete (-count, key));
+
+  if (!rl_point)
+    {
+      rl_ding ();
+      return 1;
+    }
+
+  if (rl_insert_mode == RL_IM_OVERWRITE)
+    return (_rl_overwrite_rubout (count, key));
+
+  return (_rl_rubout_char (count, key));
+}
+
+int
+_rl_rubout_char (count, key)
+     int count, key;
+{
+  int orig_point;
+  unsigned char c;
+
+  /* Duplicated code because this is called from other parts of the library. */
+  if (count < 0)
+    return (rl_delete (-count, key));
+
+  if (rl_point == 0)
+    {
+      rl_ding ();
+      return 1;
+    }
+
+  orig_point = rl_point;
+  if (count > 1 || rl_explicit_arg)
+    {
+      rl_backward_char (count, key);
+      rl_kill_text (orig_point, rl_point);
+    }
+  else if (MB_CUR_MAX == 1 || rl_byte_oriented)
+    {
+      c = rl_line_buffer[--rl_point];
+      rl_delete_text (rl_point, orig_point);
+      /* The erase-at-end-of-line hack is of questionable merit now. */
+      if (rl_point == rl_end && ISPRINT (c) && _rl_last_c_pos)
+       {
+         int l;
+         l = rl_character_len (c, rl_point);
+         _rl_erase_at_end_of_line (l);
+       }
+    }
+  else
+    {
+      rl_point = _rl_find_prev_mbchar (rl_line_buffer, rl_point, MB_FIND_NONZERO);
+      rl_delete_text (rl_point, orig_point);
+    }
+
+  return 0;
+}
+
+/* Delete the character under the cursor.  Given a numeric argument,
+   kill that many characters instead. */
+int
+rl_delete (count, key)
+     int count, key;
+{
+  int xpoint;
+
+  if (count < 0)
+    return (_rl_rubout_char (-count, key));
+
+  if (rl_point == rl_end)
+    {
+      rl_ding ();
+      return 1;
+    }
+
+  if (count > 1 || rl_explicit_arg)
+    {
+      xpoint = rl_point;
+      if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
+       rl_forward_char (count, key);
+      else
+       rl_forward_byte (count, key);
+
+      rl_kill_text (xpoint, rl_point);
+      rl_point = xpoint;
+    }
+  else
+    {
+      xpoint = MB_NEXTCHAR (rl_line_buffer, rl_point, 1, MB_FIND_NONZERO);
+      rl_delete_text (rl_point, xpoint);
+    }
+  return 0;
+}
+
+/* Delete the character under the cursor, unless the insertion
+   point is at the end of the line, in which case the character
+   behind the cursor is deleted.  COUNT is obeyed and may be used
+   to delete forward or backward that many characters. */      
+int
+rl_rubout_or_delete (count, key)
+     int count, key;
+{
+  if (rl_end != 0 && rl_point == rl_end)
+    return (_rl_rubout_char (count, key));
+  else
+    return (rl_delete (count, key));
+}  
+
+/* Delete all spaces and tabs around point. */
+int
+rl_delete_horizontal_space (count, ignore)
+     int count, ignore;
+{
+  int start;
+
+  while (rl_point && whitespace (rl_line_buffer[rl_point - 1]))
+    rl_point--;
+
+  start = rl_point;
+
+  while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point]))
+    rl_point++;
+
+  if (start != rl_point)
+    {
+      rl_delete_text (start, rl_point);
+      rl_point = start;
+    }
+
+  if (rl_point < 0)
+    rl_point = 0;
+
+  return 0;
+}
+
+/* Like the tcsh editing function delete-char-or-list.  The eof character
+   is caught before this is invoked, so this really does the same thing as
+   delete-char-or-list-or-eof, as long as it's bound to the eof character. */
+int
+rl_delete_or_show_completions (count, key)
+     int count, key;
+{
+  if (rl_end != 0 && rl_point == rl_end)
+    return (rl_possible_completions (count, key));
+  else
+    return (rl_delete (count, key));
+}
+
+#ifndef RL_COMMENT_BEGIN_DEFAULT
+#define RL_COMMENT_BEGIN_DEFAULT "#"
+#endif
+
+/* Turn the current line into a comment in shell history.
+   A K*rn shell style function. */
+int
+rl_insert_comment (count, key)
+     int count, key;
+{
+  char *rl_comment_text;
+  int rl_comment_len;
+
+  rl_beg_of_line (1, key);
+  rl_comment_text = _rl_comment_begin ? _rl_comment_begin : RL_COMMENT_BEGIN_DEFAULT;
+
+  if (rl_explicit_arg == 0)
+    rl_insert_text (rl_comment_text);
+  else
+    {
+      rl_comment_len = strlen (rl_comment_text);
+      if (STREQN (rl_comment_text, rl_line_buffer, rl_comment_len))
+       rl_delete_text (rl_point, rl_point + rl_comment_len);
+      else
+       rl_insert_text (rl_comment_text);
+    }
+
+  (*rl_redisplay_function) ();
+  rl_newline (1, '\n');
+
+  return (0);
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*                     Changing Case                               */
+/*                                                                 */
+/* **************************************************************** */
+
+/* The three kinds of things that we know how to do. */
+#define UpCase 1
+#define DownCase 2
+#define CapCase 3
+
+/* Uppercase the word at point. */
+int
+rl_upcase_word (count, key)
+     int count, key;
+{
+  return (rl_change_case (count, UpCase));
+}
+
+/* Lowercase the word at point. */
+int
+rl_downcase_word (count, key)
+     int count, key;
+{
+  return (rl_change_case (count, DownCase));
+}
+
+/* Upcase the first letter, downcase the rest. */
+int
+rl_capitalize_word (count, key)
+     int count, key;
+{
+ return (rl_change_case (count, CapCase));
+}
+
+/* The meaty function.
+   Change the case of COUNT words, performing OP on them.
+   OP is one of UpCase, DownCase, or CapCase.
+   If a negative argument is given, leave point where it started,
+   otherwise, leave it where it moves to. */
+static int
+rl_change_case (count, op)
+     int count, op;
+{
+  int start, next, end;
+  int inword, c, nc, nop;
+#if defined (HANDLE_MULTIBYTE)
+  wchar_t wc, nwc;
+  char mb[MB_LEN_MAX+1];
+  int mlen;
+  size_t m;
+  mbstate_t mps;
+#endif
+
+  start = rl_point;
+  rl_forward_word (count, 0);
+  end = rl_point;
+
+  if (op != UpCase && op != DownCase && op != CapCase)
+    {
+      rl_ding ();
+      return 1;
+    }
+
+  if (count < 0)
+    SWAP (start, end);
+
+#if defined (HANDLE_MULTIBYTE)
+  memset (&mps, 0, sizeof (mbstate_t));
+#endif
+
+  /* We are going to modify some text, so let's prepare to undo it. */
+  rl_modifying (start, end);
+
+  inword = 0;
+  while (start < end)
+    {
+      c = _rl_char_value (rl_line_buffer, start);
+      /*  This assumes that the upper and lower case versions are the same width. */
+      next = MB_NEXTCHAR (rl_line_buffer, start, 1, MB_FIND_NONZERO);
+
+      if (_rl_walphabetic (c) == 0)
+       {
+         inword = 0;
+         start = next;
+         continue;
+       }
+
+      if (op == CapCase)
+       {
+         nop = inword ? DownCase : UpCase;
+         inword = 1;
+       }
+      else
+       nop = op;
+      if (MB_CUR_MAX == 1 || rl_byte_oriented || isascii ((unsigned char)c))
+       {
+         nc = (nop == UpCase) ? _rl_to_upper (c) : _rl_to_lower (c);
+         rl_line_buffer[start] = nc;
+       }
+#if defined (HANDLE_MULTIBYTE)
+      else
+       {
+         m = mbrtowc (&wc, rl_line_buffer + start, end - start, &mps);
+         if (MB_INVALIDCH (m))
+           wc = (wchar_t)rl_line_buffer[start];
+         else if (MB_NULLWCH (m))
+           wc = L'\0';
+         nwc = (nop == UpCase) ? _rl_to_wupper (wc) : _rl_to_wlower (wc);
+         if  (nwc != wc)       /*  just skip unchanged characters */
+           {
+             mlen = wcrtomb (mb, nwc, &mps);
+             if (mlen > 0)
+               mb[mlen] = '\0';
+             /* Assume the same width */
+             strncpy (rl_line_buffer + start, mb, mlen);
+           }
+       }
+#endif
+
+      start = next;
+    }
+
+  rl_point = end;
+  return 0;
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*                     Transposition                               */
+/*                                                                 */
+/* **************************************************************** */
+
+/* Transpose the words at point.  If point is at the end of the line,
+   transpose the two words before point. */
+int
+rl_transpose_words (count, key)
+     int count, key;
+{
+  char *word1, *word2;
+  int w1_beg, w1_end, w2_beg, w2_end;
+  int orig_point = rl_point;
+
+  if (!count)
+    return 0;
+
+  /* Find the two words. */
+  rl_forward_word (count, key);
+  w2_end = rl_point;
+  rl_backward_word (1, key);
+  w2_beg = rl_point;
+  rl_backward_word (count, key);
+  w1_beg = rl_point;
+  rl_forward_word (1, key);
+  w1_end = rl_point;
+
+  /* Do some check to make sure that there really are two words. */
+  if ((w1_beg == w2_beg) || (w2_beg < w1_end))
+    {
+      rl_ding ();
+      rl_point = orig_point;
+      return 1;
+    }
+
+  /* Get the text of the words. */
+  word1 = rl_copy_text (w1_beg, w1_end);
+  word2 = rl_copy_text (w2_beg, w2_end);
+
+  /* We are about to do many insertions and deletions.  Remember them
+     as one operation. */
+  rl_begin_undo_group ();
+
+  /* Do the stuff at word2 first, so that we don't have to worry
+     about word1 moving. */
+  rl_point = w2_beg;
+  rl_delete_text (w2_beg, w2_end);
+  rl_insert_text (word1);
+
+  rl_point = w1_beg;
+  rl_delete_text (w1_beg, w1_end);
+  rl_insert_text (word2);
+
+  /* This is exactly correct since the text before this point has not
+     changed in length. */
+  rl_point = w2_end;
+
+  /* I think that does it. */
+  rl_end_undo_group ();
+  xfree (word1);
+  xfree (word2);
+
+  return 0;
+}
+
+/* Transpose the characters at point.  If point is at the end of the line,
+   then transpose the characters before point. */
+int
+rl_transpose_chars (count, key)
+     int count, key;
+{
+#if defined (HANDLE_MULTIBYTE)
+  char *dummy;
+  int i;
+#else
+  char dummy[2];
+#endif
+  int char_length, prev_point;
+
+  if (count == 0)
+    return 0;
+
+  if (!rl_point || rl_end < 2)
+    {
+      rl_ding ();
+      return 1;
+    }
+
+  rl_begin_undo_group ();
+
+  if (rl_point == rl_end)
+    {
+      rl_point = MB_PREVCHAR (rl_line_buffer, rl_point, MB_FIND_NONZERO);
+      count = 1;
+    }
+
+  prev_point = rl_point;
+  rl_point = MB_PREVCHAR (rl_line_buffer, rl_point, MB_FIND_NONZERO);
+
+#if defined (HANDLE_MULTIBYTE)
+  char_length = prev_point - rl_point;
+  dummy = (char *)xmalloc (char_length + 1);
+  for (i = 0; i < char_length; i++)
+    dummy[i] = rl_line_buffer[rl_point + i];
+  dummy[i] = '\0';
+#else
+  dummy[0] = rl_line_buffer[rl_point];
+  dummy[char_length = 1] = '\0';
+#endif
+
+  rl_delete_text (rl_point, rl_point + char_length);
+
+  rl_point = _rl_find_next_mbchar (rl_line_buffer, rl_point, count, MB_FIND_NONZERO);
+
+  _rl_fix_point (0);
+  rl_insert_text (dummy);
+  rl_end_undo_group ();
+
+#if defined (HANDLE_MULTIBYTE)
+  xfree (dummy);
+#endif
+
+  return 0;
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*                     Character Searching                         */
+/*                                                                 */
+/* **************************************************************** */
+
+int
+#if defined (HANDLE_MULTIBYTE)
+_rl_char_search_internal (count, dir, smbchar, len)
+     int count, dir;
+     char *smbchar;
+     int len;
+#else
+_rl_char_search_internal (count, dir, schar)
+     int count, dir, schar;
+#endif
+{
+  int pos, inc;
+#if defined (HANDLE_MULTIBYTE)
+  int prepos;
+#endif
+
+  if (dir == 0)
+    return 1;
+
+  pos = rl_point;
+  inc = (dir < 0) ? -1 : 1;
+  while (count)
+    {
+      if ((dir < 0 && pos <= 0) || (dir > 0 && pos >= rl_end))
+       {
+         rl_ding ();
+         return 1;
+       }
+
+#if defined (HANDLE_MULTIBYTE)
+      pos = (inc > 0) ? _rl_find_next_mbchar (rl_line_buffer, pos, 1, MB_FIND_ANY)
+                     : _rl_find_prev_mbchar (rl_line_buffer, pos, MB_FIND_ANY);
+#else
+      pos += inc;
+#endif
+      do
+       {
+#if defined (HANDLE_MULTIBYTE)
+         if (_rl_is_mbchar_matched (rl_line_buffer, pos, rl_end, smbchar, len))
+#else
+         if (rl_line_buffer[pos] == schar)
+#endif
+           {
+             count--;
+             if (dir < 0)
+               rl_point = (dir == BTO) ? _rl_find_next_mbchar (rl_line_buffer, pos, 1, MB_FIND_ANY)
+                                       : pos;
+             else
+               rl_point = (dir == FTO) ? _rl_find_prev_mbchar (rl_line_buffer, pos, MB_FIND_ANY)
+                                       : pos;
+             break;
+           }
+#if defined (HANDLE_MULTIBYTE)
+         prepos = pos;
+#endif
+       }
+#if defined (HANDLE_MULTIBYTE)
+      while ((dir < 0) ? (pos = _rl_find_prev_mbchar (rl_line_buffer, pos, MB_FIND_ANY)) != prepos
+                      : (pos = _rl_find_next_mbchar (rl_line_buffer, pos, 1, MB_FIND_ANY)) != prepos);
+#else
+      while ((dir < 0) ? pos-- : ++pos < rl_end);
+#endif
+    }
+  return (0);
+}
+
+/* Search COUNT times for a character read from the current input stream.
+   FDIR is the direction to search if COUNT is non-negative; otherwise
+   the search goes in BDIR.  So much is dependent on HANDLE_MULTIBYTE
+   that there are two separate versions of this function. */
+#if defined (HANDLE_MULTIBYTE)
+static int
+_rl_char_search (count, fdir, bdir)
+     int count, fdir, bdir;
+{
+  char mbchar[MB_LEN_MAX];
+  int mb_len;
+
+  mb_len = _rl_read_mbchar (mbchar, MB_LEN_MAX);
+
+  if (mb_len <= 0)
+    return 1;
+
+  if (count < 0)
+    return (_rl_char_search_internal (-count, bdir, mbchar, mb_len));
+  else
+    return (_rl_char_search_internal (count, fdir, mbchar, mb_len));
+}
+#else /* !HANDLE_MULTIBYTE */
+static int
+_rl_char_search (count, fdir, bdir)
+     int count, fdir, bdir;
+{
+  int c;
+
+  RL_SETSTATE(RL_STATE_MOREINPUT);
+  c = rl_read_key ();
+  RL_UNSETSTATE(RL_STATE_MOREINPUT);
+
+  if (c < 0)
+    return 1;
+
+  if (count < 0)
+    return (_rl_char_search_internal (-count, bdir, c));
+  else
+    return (_rl_char_search_internal (count, fdir, c));
+}
+#endif /* !HANDLE_MULTIBYTE */
+
+#if defined (READLINE_CALLBACKS)
+static int
+_rl_char_search_callback (data)
+     _rl_callback_generic_arg *data;
+{
+  _rl_callback_func = 0;
+  _rl_want_redisplay = 1;
+
+  return (_rl_char_search (data->count, data->i1, data->i2));
+}
+#endif
+
+int
+rl_char_search (count, key)
+     int count, key;
+{
+#if defined (READLINE_CALLBACKS)
+  if (RL_ISSTATE (RL_STATE_CALLBACK))
+    {
+      _rl_callback_data = _rl_callback_data_alloc (count);
+      _rl_callback_data->i1 = FFIND;
+      _rl_callback_data->i2 = BFIND;
+      _rl_callback_func = _rl_char_search_callback;
+      return (0);
+    }
+#endif
+  
+  return (_rl_char_search (count, FFIND, BFIND));
+}
+
+int
+rl_backward_char_search (count, key)
+     int count, key;
+{
+#if defined (READLINE_CALLBACKS)
+  if (RL_ISSTATE (RL_STATE_CALLBACK))
+    {
+      _rl_callback_data = _rl_callback_data_alloc (count);
+      _rl_callback_data->i1 = BFIND;
+      _rl_callback_data->i2 = FFIND;
+      _rl_callback_func = _rl_char_search_callback;
+      return (0);
+    }
+#endif
+
+  return (_rl_char_search (count, BFIND, FFIND));
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*                The Mark and the Region.                         */
+/*                                                                 */
+/* **************************************************************** */
+
+/* Set the mark at POSITION. */
+int
+_rl_set_mark_at_pos (position)
+     int position;
+{
+  if (position > rl_end)
+    return 1;
+
+  rl_mark = position;
+  return 0;
+}
+
+/* A bindable command to set the mark. */
+int
+rl_set_mark (count, key)
+     int count, key;
+{
+  return (_rl_set_mark_at_pos (rl_explicit_arg ? count : rl_point));
+}
+
+/* Exchange the position of mark and point. */
+int
+rl_exchange_point_and_mark (count, key)
+     int count, key;
+{
+  if (rl_mark > rl_end)
+    rl_mark = -1;
+
+  if (rl_mark == -1)
+    {
+      rl_ding ();
+      return 1;
+    }
+  else
+    SWAP (rl_point, rl_mark);
+
+  return 0;
+}
index bbcffa6fa11fcda38b1bc62b8867bd6ca1af5cde..48ce8514121446eec40fa4794bdc857fc27c8aca 100644 (file)
@@ -1,6 +1,6 @@
 /* casemod.c -- functions to change case of strings */
 
-/* Copyright (C) 2008,2009 Free Software Foundation, Inc.
+/* Copyright (C) 2008,2009,2015 Free Software Foundation, Inc.
 
    This file is part of GNU Bash, the Bourne Again SHell.
 
@@ -49,7 +49,7 @@
 #if !defined (HANDLE_MULTIBYTE)
 #  define cval(s, i)   ((s)[(i)])
 #  define iswalnum(c)  (isalnum(c))
-#  define TOGGLE(x)    (ISUPPER (x) ? tolower (x) : (TOUPPER (x)))
+#  define TOGGLE(x)    (ISUPPER (x) ? tolower ((unsigned char)x) : (TOUPPER (x)))
 #else
 #  define TOGGLE(x)    (iswupper (x) ? towlower (x) : (_to_wupper(x)))
 #endif
@@ -105,7 +105,7 @@ sh_modcase (string, pat, flags)
      char *pat;
      int flags;
 {
-  int start, next, end;
+  int start, next, end, retind;
   int inword, c, nc, nop, match, usewords;
   char *ret, *s;
   wchar_t wc;
@@ -131,8 +131,8 @@ sh_modcase (string, pat, flags)
   start = 0;
   end = strlen (string);
 
-  ret = (char *)xmalloc (end + 1);
-  strcpy (ret, string);
+  ret = (char *)xmalloc (2*end + 1);
+  retind = 0;
 
   /* See if we are supposed to split on alphanumerics and operate on each word */
   usewords = (flags & CASE_USEWORDS);
@@ -141,26 +141,23 @@ sh_modcase (string, pat, flags)
   inword = 0;
   while (start < end)
     {
-      wc = cval (ret, start);
+      wc = cval (string, start);
 
       if (iswalnum (wc) == 0)
-       {
-         inword = 0;
-#if 0
-         ADVANCE_CHAR (ret, end, start);
-         continue;
-#endif
-       }
+       inword = 0;
 
       if (pat)
        {
          next = start;
-         ADVANCE_CHAR (ret, end, next);
-         s = substring (ret, start, next);
+         ADVANCE_CHAR (string, end, next);
+         s = substring (string, start, next);
          match = strmatch (pat, s, FNM_EXTMATCH) != FNM_NOMATCH;
          free (s);
          if (match == 0)
             {
+              /* copy unmatched portion */
+              memcpy (ret + retind, string + start, next - start);
+              retind += next - start;
               start = next;
               inword = 1;
               continue;
@@ -210,27 +207,27 @@ sh_modcase (string, pat, flags)
       else
        nop = flags;
 
-      /* Need to check UCHAR_MAX since wc may have already been converted to a
-        wide character by cval() */
-      if (MB_CUR_MAX == 1 || (wc <= UCHAR_MAX && is_basic ((int)wc)))
+      /* Can't short-circuit, some locales have multibyte upper and lower
+        case equivalents of single-byte ascii characters (e.g., Turkish) */
+      if (MB_CUR_MAX == 1)
        {
 singlebyte:
          switch (nop)
-         {
-         default:
-         case CASE_NOOP:  nc = wc; break;
-         case CASE_UPPER:  nc = TOUPPER (wc); break;
-         case CASE_LOWER:  nc = TOLOWER (wc); break;
-         case CASE_TOGGLEALL:
-         case CASE_TOGGLE: nc = TOGGLE (wc); break;
-         }
-         ret[start] = nc;
+           {
+           default:
+           case CASE_NOOP:  nc = wc; break;
+           case CASE_UPPER:  nc = TOUPPER (wc); break;
+           case CASE_LOWER:  nc = TOLOWER (wc); break;
+           case CASE_TOGGLEALL:
+           case CASE_TOGGLE: nc = TOGGLE (wc); break;
+           }
+         ret[retind++] = nc;
        }
 #if defined (HANDLE_MULTIBYTE)
       else
        {
          m = mbrtowc (&wc, string + start, end - start, &state);
-         if (MB_INVALIDCH (m))
+         if (MB_INVALIDCH (m) || m == 1)
            {
              wc = (unsigned char)string[start];
              goto singlebyte;
@@ -238,28 +235,34 @@ singlebyte:
          else if (MB_NULLWCH (m))
            wc = L'\0';
          switch (nop)
-         {
-         default:
-         case CASE_NOOP:  nwc = wc; break;
-         case CASE_UPPER:  nwc = _to_wupper (wc); break;
-         case CASE_LOWER:  nwc = _to_wlower (wc); break;
-         case CASE_TOGGLEALL:
-         case CASE_TOGGLE: nwc = TOGGLE (wc); break;
-         }
-         if  (nwc != wc)       /*  just skip unchanged characters */
+           {
+           default:
+           case CASE_NOOP:  nwc = wc; break;
+           case CASE_UPPER:  nwc = _to_wupper (wc); break;
+           case CASE_LOWER:  nwc = _to_wlower (wc); break;
+           case CASE_TOGGLEALL:
+           case CASE_TOGGLE: nwc = TOGGLE (wc); break;
+           }
+
+         /* We don't have to convert `wide' characters that are in the
+            unsigned char range back to single-byte `multibyte' characters. */
+         if ((int)nwc <= UCHAR_MAX && is_basic ((int)nwc))
+           ret[retind++] = nwc;
+         else
            {
              mlen = wcrtomb (mb, nwc, &state);
              if (mlen > 0)
                mb[mlen] = '\0';
-             /* Assume the same width */
-             strncpy (ret + start, mb, mlen);
+             /* Don't assume the same width */
+             strncpy (ret + retind, mb, mlen);
+             retind += mlen;
            }
        }
 #endif
 
-      /*  This assumes that the upper and lower case versions are the same width. */
-      ADVANCE_CHAR (ret, end, start);
+      ADVANCE_CHAR (string, end, start);
     }
 
+  ret[retind] = '\0';
   return ret;
 }
diff --git a/lib/sh/casemod.c~ b/lib/sh/casemod.c~
new file mode 100644 (file)
index 0000000..1838641
--- /dev/null
@@ -0,0 +1,270 @@
+/* casemod.c -- functions to change case of strings */
+
+/* Copyright (C) 2008,2009,2015 Free Software Foundation, Inc.
+
+   This file is part of GNU Bash, the Bourne Again SHell.
+
+   Bash is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   Bash is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with Bash.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#if defined (HAVE_CONFIG_H)
+#  include <config.h>
+#endif
+
+#if defined (HAVE_UNISTD_H)
+#  include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+
+#include <stdc.h>
+
+#include <bashansi.h>
+#include <bashintl.h>
+#include <bashtypes.h>
+
+#include <stdio.h>
+#include <ctype.h>
+#include <xmalloc.h>
+
+#include <shmbchar.h>
+#include <shmbutil.h>
+#include <chartypes.h>
+#include <typemax.h>
+
+#include <glob/strmatch.h>
+
+#define _to_wupper(wc) (iswlower (wc) ? towupper (wc) : (wc))
+#define _to_wlower(wc) (iswupper (wc) ? towlower (wc) : (wc))
+
+#if !defined (HANDLE_MULTIBYTE)
+#  define cval(s, i)   ((s)[(i)])
+#  define iswalnum(c)  (isalnum(c))
+#  define TOGGLE(x)    (ISUPPER (x) ? tolower ((unsigned char)x) : (TOUPPER (x)))
+#else
+#  define TOGGLE(x)    (iswupper (x) ? towlower (x) : (_to_wupper(x)))
+#endif
+
+/* These must agree with the defines in externs.h */
+#define CASE_NOOP      0x0000
+#define CASE_LOWER     0x0001
+#define CASE_UPPER     0x0002
+#define CASE_CAPITALIZE        0x0004
+#define CASE_UNCAP     0x0008
+#define CASE_TOGGLE    0x0010
+#define CASE_TOGGLEALL 0x0020
+#define CASE_UPFIRST   0x0040
+#define CASE_LOWFIRST  0x0080
+
+#define CASE_USEWORDS  0x1000          /* modify behavior to act on words in passed string */
+
+extern char *substring __P((char *, int, int));
+
+#ifndef UCHAR_MAX
+#  define UCHAR_MAX    TYPE_MAXIMUM(unsigned char)
+#endif
+
+#if defined (HANDLE_MULTIBYTE)
+static wchar_t
+cval (s, i)
+     char *s;
+     int i;
+{
+  size_t tmp;
+  wchar_t wc;
+  int l;
+  mbstate_t mps;  
+
+  if (MB_CUR_MAX == 1 || is_basic (s[i]))
+    return ((wchar_t)s[i]);
+  l = strlen (s);
+  if (i >= (l - 1))
+    return ((wchar_t)s[i]);
+  memset (&mps, 0, sizeof (mbstate_t));
+  tmp = mbrtowc (&wc, s + i, l - i, &mps);
+  if (MB_INVALIDCH (tmp) || MB_NULLWCH (tmp))
+    return ((wchar_t)s[i]);
+  return wc;  
+}
+#endif
+
+/* Modify the case of characters in STRING matching PAT based on the value of
+   FLAGS.  If PAT is null, modify the case of each character */
+char *
+sh_modcase (string, pat, flags)
+     const char *string;
+     char *pat;
+     int flags;
+{
+  int start, next, end, retind;
+  int inword, c, nc, nop, match, usewords;
+  char *ret, *s;
+  wchar_t wc;
+#if defined (HANDLE_MULTIBYTE)
+  wchar_t nwc;
+  char mb[MB_LEN_MAX+1];
+  int mlen;
+  size_t m;
+  mbstate_t state;
+#endif
+
+  if (string == 0 || *string == 0)
+    {
+      ret = (char *)xmalloc (1);
+      ret[0] = '\0';
+      return ret;
+    }
+
+#if defined (HANDLE_MULTIBYTE)
+  memset (&state, 0, sizeof (mbstate_t));
+#endif
+
+  start = 0;
+  end = strlen (string);
+
+  ret = (char *)xmalloc (2*end + 1);
+  retind = 0;
+
+  /* See if we are supposed to split on alphanumerics and operate on each word */
+  usewords = (flags & CASE_USEWORDS);
+  flags &= ~CASE_USEWORDS;
+
+  inword = 0;
+  while (start < end)
+    {
+      wc = cval (string, start);
+
+      if (iswalnum (wc) == 0)
+       inword = 0;
+
+      if (pat)
+       {
+         next = start;
+         ADVANCE_CHAR (string, end, next);
+         s = substring (string, start, next);
+         match = strmatch (pat, s, FNM_EXTMATCH) != FNM_NOMATCH;
+         free (s);
+         if (match == 0)
+            {
+              /* copy unmatched portion */
+              memcpy (ret + retind, string + start, next - start);
+              retind += next - start;
+              start = next;
+              inword = 1;
+              continue;
+            }
+       }
+
+      /* XXX - for now, the toggling operators work on the individual
+        words in the string, breaking on alphanumerics.  Should I
+        leave the capitalization operators to do that also? */
+      if (flags == CASE_CAPITALIZE)
+       {
+         if (usewords)
+           nop = inword ? CASE_LOWER : CASE_UPPER;
+         else
+           nop = (start > 0) ? CASE_LOWER : CASE_UPPER;
+         inword = 1;
+       }
+      else if (flags == CASE_UNCAP)
+       {
+         if (usewords)
+           nop = inword ? CASE_UPPER : CASE_LOWER;
+         else
+           nop = (start > 0) ? CASE_UPPER : CASE_LOWER;
+         inword = 1;
+       }
+      else if (flags == CASE_UPFIRST)
+       {
+         if (usewords)
+           nop = inword ? CASE_NOOP : CASE_UPPER;
+         else
+           nop = (start > 0) ? CASE_NOOP : CASE_UPPER;
+         inword = 1;
+       }
+      else if (flags == CASE_LOWFIRST)
+       {
+         if (usewords)
+           nop = inword ? CASE_NOOP : CASE_LOWER;
+         else
+           nop = (start > 0) ? CASE_NOOP : CASE_LOWER;
+         inword = 1;
+       }
+      else if (flags == CASE_TOGGLE)
+       {
+         nop = inword ? CASE_NOOP : CASE_TOGGLE;
+         inword = 1;
+       }
+      else
+       nop = flags;
+
+      /* Can't short-circuit, some locales have multibyte upper and lower
+        case equivalents of single-byte ascii characters (e.g., Turkish) */
+      if (MB_CUR_MAX == 1)
+       {
+singlebyte:
+         switch (nop)
+           {
+           default:
+           case CASE_NOOP:  nc = wc; break;
+           case CASE_UPPER:  nc = TOUPPER (wc); break;
+           case CASE_LOWER:  nc = TOLOWER (wc); break;
+           case CASE_TOGGLEALL:
+           case CASE_TOGGLE: nc = TOGGLE (wc); break;
+           }
+         ret[retind++] = nc;
+       }
+#if defined (HANDLE_MULTIBYTE)
+      else
+       {
+         m = mbrtowc (&wc, string + start, end - start, &state);
+if (m == 1)
+  itrace("sh_modcase: mbrtowc returns 1 for single byte char");
+         if (MB_INVALIDCH (m) || m == 1)
+           {
+             wc = (unsigned char)string[start];
+             goto singlebyte;
+           }
+         else if (MB_NULLWCH (m))
+           wc = L'\0';
+         switch (nop)
+           {
+           default:
+           case CASE_NOOP:  nwc = wc; break;
+           case CASE_UPPER:  nwc = _to_wupper (wc); break;
+           case CASE_LOWER:  nwc = _to_wlower (wc); break;
+           case CASE_TOGGLEALL:
+           case CASE_TOGGLE: nwc = TOGGLE (wc); break;
+           }
+
+         /* We don't have to convert `wide' characters that are in the
+            unsigned char range back to single-byte `multibyte' characters. */
+         if ((int)nwc <= UCHAR_MAX && is_basic ((int)nwc))
+           ret[retind++] = nwc;
+         else
+           {
+             mlen = wcrtomb (mb, nwc, &state);
+             if (mlen > 0)
+               mb[mlen] = '\0';
+             /* Don't assume the same width */
+             strncpy (ret + retind, mb, mlen);
+             retind += mlen;
+           }
+       }
+#endif
+
+      ADVANCE_CHAR (string, end, start);
+    }
+
+  ret[retind] = '\0';
+  return ret;
+}
index e678365bd567d19251c8c547478ebb5890ea51ef..40d09935b2c9d0a4ec4b9a8c94ac00b2ac47d6b4 100644 (file)
@@ -33,6 +33,9 @@
 #include "syntax.h"
 #include <xmalloc.h>
 
+#include "shmbchar.h"
+#include "shmbutil.h"
+
 extern char *ansic_quote __P((char *, int, int *));
 extern int ansic_shouldquote __P((const char *));
 
@@ -231,13 +234,25 @@ sh_backslash_quote (string, table, flags)
      int flags;
 {
   int c;
-  char *result, *r, *s, *backslash_table;
+  size_t slen;
+  char *result, *r, *s, *backslash_table, *send;
+  DECLARE_MBSTATE;
 
-  result = (char *)xmalloc (2 * strlen (string) + 1);
+  slen = strlen (string);
+  send = string + slen;
+  result = (char *)xmalloc (2 * slen + 1);
 
   backslash_table = table ? table : (char *)bstab;
   for (r = result, s = string; s && (c = *s); s++)
     {
+#if defined (HANDLE_MULTIBYTE)
+      if (MB_CUR_MAX > 1 && is_basic (c) == 0)
+       {
+         COPY_CHAR_P (r, s, send);
+         s--;          /* compensate for auto-increment in loop above */
+         continue;
+       }
+#endif
       if (backslash_table[c] == 1)
        *r++ = '\\';
       else if (c == '#' && s == string)                        /* comment char */
index 8dabc7d2c863241e47fc7042f1ba1c6ec833221e..5b3e66b96ef762f4597eafd57ac6d933e0eeee53 100644 (file)
--- a/locale.c
+++ b/locale.c
@@ -535,7 +535,7 @@ locale_setblanks ()
 
   for (x = 0; x < sh_syntabsiz; x++)
     {
-      if (isblank (x))
+      if (isblank ((unsigned char)x))
        sh_syntaxtab[x] |= CSHBRK|CBLANK;
       else if (member (x, shell_break_chars))
        {
diff --git a/locale.c~ b/locale.c~
new file mode 100644 (file)
index 0000000..8dabc7d
--- /dev/null
+++ b/locale.c~
@@ -0,0 +1,563 @@
+/* locale.c - Miscellaneous internationalization functions. */
+
+/* Copyright (C) 1996-2009,2012 Free Software Foundation, Inc.
+
+   This file is part of GNU Bash, the Bourne Again SHell.
+
+   Bash is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   Bash is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with Bash.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "config.h"
+
+#include "bashtypes.h"
+
+#if defined (HAVE_UNISTD_H)
+#  include <unistd.h>
+#endif
+
+#if HAVE_LANGINFO_CODESET
+#  include <langinfo.h>
+#endif
+
+#include "bashintl.h"
+#include "bashansi.h"
+#include <stdio.h>
+#include "chartypes.h"
+#include <errno.h>
+
+#include "shell.h"
+#include "input.h"     /* For bash_input */
+
+#ifndef errno
+extern int errno;
+#endif
+
+int locale_utf8locale; /* unused for now */
+int locale_mb_cur_max; /* value of MB_CUR_MAX for current locale (LC_CTYPE) */
+
+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));
+static int locale_isutf8 __P((char *));
+
+/* 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);
+
+  locale_mb_cur_max = MB_CUR_MAX;
+}
+
+/* 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 ()
+{
+  char *val;
+
+#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 ();
+      locale_mb_cur_max = MB_CUR_MAX;
+      u32reset ();
+    }
+#  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 */
+
+#  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");
+  if (val && *val)
+    {
+      FREE (default_domain);
+      default_domain = savestring (val);
+      if (default_dir && *default_dir)
+       bindtextdomain (default_domain, default_dir);
+    }
+
+  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;
+  char *x;
+
+  x = "";
+  errno = 0;
+  if (var[0] == 'T' && var[10] == 0)           /* TEXTDOMAIN */
+    {
+      FREE (default_domain);
+      default_domain = value ? savestring (value) : (char *)NULL;
+      if (default_dir && *default_dir)
+       bindtextdomain (default_domain, default_dir);
+      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 ? ((x = setlocale (LC_ALL, lc_all)) != 0) : reset_locale_vars ();
+      if (x == 0)
+       {
+         if (errno == 0)
+           internal_warning(_("setlocale: LC_ALL: cannot change locale (%s)"), lc_all);
+         else
+           internal_warning(_("setlocale: LC_ALL: cannot change locale (%s): %s"), lc_all, strerror (errno));
+       }
+      locale_setblanks ();
+      locale_mb_cur_max = MB_CUR_MAX;
+      u32reset ();
+      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')
+       {
+         x = setlocale (LC_CTYPE, get_locale_var ("LC_CTYPE"));
+         locale_setblanks ();
+         locale_mb_cur_max = MB_CUR_MAX;
+         u32reset ();
+       }
+#  endif
+    }
+  else if (var[3] == 'C' && var[4] == 'O')     /* LC_COLLATE */
+    {
+#  if defined (LC_COLLATE)
+      if (lc_all == 0 || *lc_all == '\0')
+       x = setlocale (LC_COLLATE, get_locale_var ("LC_COLLATE"));
+#  endif /* LC_COLLATE */
+    }
+  else if (var[3] == 'M' && var[4] == 'E')     /* LC_MESSAGES */
+    {
+#  if defined (LC_MESSAGES)
+      if (lc_all == 0 || *lc_all == '\0')
+       x = setlocale (LC_MESSAGES, get_locale_var ("LC_MESSAGES"));
+#  endif /* LC_MESSAGES */
+    }
+  else if (var[3] == 'N' && var[4] == 'U')     /* LC_NUMERIC */
+    {
+#  if defined (LC_NUMERIC)
+      if (lc_all == 0 || *lc_all == '\0')
+       x = setlocale (LC_NUMERIC, get_locale_var ("LC_NUMERIC"));
+#  endif /* LC_NUMERIC */
+    }
+  else if (var[3] == 'T' && var[4] == 'I')     /* LC_TIME */
+    {
+#  if defined (LC_TIME)
+      if (lc_all == 0 || *lc_all == '\0')
+       x = setlocale (LC_TIME, get_locale_var ("LC_TIME"));
+#  endif /* LC_TIME */
+    }
+#endif /* HAVE_SETLOCALE */
+  
+  if (x == 0)
+    {
+      if (errno == 0)
+       internal_warning(_("setlocale: %s: cannot change locale (%s)"), var, get_locale_var (var));
+      else
+       internal_warning(_("setlocale: %s: cannot change locale (%s): %s"), var, get_locale_var (var), strerror (errno));
+    }
+
+  return (x != 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);
+}
+
+/* Set default values for LANG and LC_ALL.  Default values for all other
+   locale-related variables depend on these. */
+void
+set_default_lang ()
+{
+  char *v;
+
+  v = get_string_value ("LC_ALL");
+  set_locale_var ("LC_ALL", v);
+
+  v = get_string_value ("LANG");
+  set_lang ("LANG", v);
+}
+
+/* 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);   /* XXX - mem leak? */
+  if (locale == 0 || *locale == 0)
+    locale = lang;
+  if (locale == 0 || *locale == 0)
+#if 0
+    locale = default_locale;   /* system-dependent; not really portable.  should it be "C"? */
+#else
+    locale = "";
+#endif
+  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 ()
+{
+  char *t;
+#if defined (HAVE_SETLOCALE)
+  if (lang == 0 || *lang == '\0')
+    maybe_make_export_env ();          /* trust that this will change environment for setlocale */
+  if (setlocale (LC_ALL, lang ? lang : "") == 0)
+    return 0;
+
+#  if defined (LC_CTYPE)
+  t = setlocale (LC_CTYPE, get_locale_var ("LC_CTYPE"));
+#  endif
+#  if defined (LC_COLLATE)
+  t = setlocale (LC_COLLATE, get_locale_var ("LC_COLLATE"));
+#  endif
+#  if defined (LC_MESSAGES)
+  t = setlocale (LC_MESSAGES, get_locale_var ("LC_MESSAGES"));
+#  endif
+#  if defined (LC_NUMERIC)
+  t = setlocale (LC_NUMERIC, get_locale_var ("LC_NUMERIC"));
+#  endif
+#  if defined (LC_TIME)
+  t = setlocale (LC_TIME, get_locale_var ("LC_TIME"));
+#  endif
+
+  locale_setblanks ();  
+  locale_mb_cur_max = MB_CUR_MAX;
+  u32reset ();
+
+#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|CBLANK;
+      else if (member (x, shell_break_chars))
+       {
+         sh_syntaxtab[x] |= CSHBRK;
+         sh_syntaxtab[x] &= ~CBLANK;
+       }
+      else
+       sh_syntaxtab[x] &= ~(CSHBRK|CBLANK);
+    }
+}
+
+static int
+locale_isutf8 (lspec)
+     char *lspec;
+{
+  char *cp;
+
+#if HAVE_LANGINFO_CODESET
+  cp = nl_langinfo (CODESET);
+  return (STREQ (cp, "UTF-8") || STREQ (cp, "utf8"));
+#else
+  /* Take a shot */
+  return (strstr (lspec, "UTF-8") || strstr (lspec, "utf8"));
+#endif
+}
diff --git a/parse.y b/parse.y
index b0903b9f5cc1c5c2c97e7ac08b663493a6978eca..c6797fc7d88c07fa6098b586b5a9f68cfc0d3af1 100644 (file)
--- a/parse.y
+++ b/parse.y
@@ -3813,7 +3813,7 @@ eof_error:
       /* If we can read a reserved word, try to read one. */
       if (tflags & LEX_RESWDOK)
        {
-         if MBTEST(islower (ch))
+         if MBTEST(islower ((unsigned char)ch))
            {
              /* Add this character. */
              RESIZE_MALLOCED_BUFFER (ret, retind, 1, retsize, 64);
@@ -3841,7 +3841,7 @@ eof_error:
             RESWDOK flag, but reset the reserved word length counter so we
             can read another one. */
          else if MBTEST(((tflags & LEX_INCASE) == 0) &&
-                         (isblank(ch) || ch == '\n') &&
+                         (isblank((unsigned char)ch) || ch == '\n') &&
                          lex_rwlen == 2 &&
                          STREQN (ret + retind - 2, "do", 2))
            {
@@ -3866,7 +3866,7 @@ eof_error:
 #if 0
          /* If we find a space or tab but have read something and it's not
             `do', turn off the reserved-word-ok flag */
-         else if MBTEST(isblank (ch) && lex_rwlen > 0)
+         else if MBTEST(isblank ((unsigned char)ch) && lex_rwlen > 0)
            {
              tflags &= ~LEX_RESWDOK;
 /*itrace("parse_comsub:%d: found `%c', lex_reswordok -> 0", line_number, ch);*/
index d55c439848905fa84cc211764b3298988b2b09bd..d2df2c787f16c920a98b9fafd939d6b8439fd0be 100644 (file)
Binary files a/po/._fr.po and b/po/._fr.po differ
diff --git a/shell.c b/shell.c
index 2382f773e4084de41118181608890d083c21a396..c0107ffcf980ba14bd7bbed61a197fff36287152 100644 (file)
--- a/shell.c
+++ b/shell.c
@@ -1,6 +1,6 @@
 /* shell.c -- GNU's idea of the POSIX shell specification. */
 
-/* Copyright (C) 1987-2012 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2015 Free Software Foundation, Inc.
 
    This file is part of GNU Bash, the Bourne Again SHell.
 
@@ -415,7 +415,7 @@ main (argc, argv, env)
   mcheck (programming_error, (void (*) ())0);
 #endif /* USE_GNU_MALLOC_LIBRARY */
 
-  if (setjmp (subshell_top_level))
+  if (setjmp_sigs (subshell_top_level))
     {
       argc = subshell_argc;
       argv = subshell_argv;
@@ -598,7 +598,7 @@ main (argc, argv, env)
   /* Give this shell a place to longjmp to before executing the
      startup files.  This allows users to press C-c to abort the
      lengthy startup. */
-  code = setjmp (top_level);
+  code = setjmp_sigs (top_level);
   if (code)
     {
       if (code == EXITPROG || code == ERREXIT)
diff --git a/shell.c~ b/shell.c~
new file mode 100644 (file)
index 0000000..4883dcf
--- /dev/null
+++ b/shell.c~
@@ -0,0 +1,1925 @@
+/* shell.c -- GNU's idea of the POSIX shell specification. */
+
+/* Copyright (C) 1987-2012 Free Software Foundation, Inc.
+
+   This file is part of GNU Bash, the Bourne Again SHell.
+
+   Bash is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   Bash is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with Bash.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+  Birthdate:
+  Sunday, January 10th, 1988.
+  Initial author: Brian Fox
+*/
+#define INSTALL_DEBUG_MODE
+
+#include "config.h"
+
+#include "bashtypes.h"
+#if !defined (_MINIX) && defined (HAVE_SYS_FILE_H)
+#  include <sys/file.h>
+#endif
+#include "posixstat.h"
+#include "posixtime.h"
+#include "bashansi.h"
+#include <stdio.h>
+#include <signal.h>
+#include <errno.h>
+#include "filecntl.h"
+#include <pwd.h>
+
+#if defined (HAVE_UNISTD_H)
+#  include <unistd.h>
+#endif
+
+#include "bashintl.h"
+
+#define NEED_SH_SETLINEBUF_DECL                /* used in externs.h */
+
+#include "shell.h"
+#include "flags.h"
+#include "trap.h"
+#include "mailcheck.h"
+#include "builtins.h"
+#include "builtins/common.h"
+
+#if defined (JOB_CONTROL)
+#include "jobs.h"
+#endif /* JOB_CONTROL */
+
+#include "input.h"
+#include "execute_cmd.h"
+#include "findcmd.h"
+
+#if defined (USING_BASH_MALLOC) && defined (DEBUG) && !defined (DISABLE_MALLOC_WRAPPERS)
+#  include <malloc/shmalloc.h>
+#endif
+
+#if defined (HISTORY)
+#  include "bashhist.h"
+#  include <readline/history.h>
+#endif
+
+#if defined (READLINE)
+#  include <readline/readline.h>
+#  include "bashline.h"
+#endif
+
+#include <tilde/tilde.h>
+#include <glob/strmatch.h>
+
+#if defined (__OPENNT)
+#  include <opennt/opennt.h>
+#endif
+
+#if !defined (HAVE_GETPW_DECLS)
+extern struct passwd *getpwuid ();
+#endif /* !HAVE_GETPW_DECLS */
+
+#if !defined (errno)
+extern int errno;
+#endif
+
+#if defined (NO_MAIN_ENV_ARG)
+extern char **environ; /* used if no third argument to main() */
+#endif
+
+extern char *dist_version, *release_status;
+extern int patch_level, build_version;
+extern int shell_level;
+extern int subshell_environment;
+extern int running_in_background;
+extern int last_command_exit_value;
+extern int line_number;
+extern int expand_aliases;
+extern int array_needs_making;
+extern int gnu_error_format;
+extern char *primary_prompt, *secondary_prompt;
+extern char *this_command_name;
+
+/* Non-zero means that this shell has already been run; i.e. you should
+   call shell_reinitialize () if you need to start afresh. */
+int shell_initialized = 0;
+
+COMMAND *global_command = (COMMAND *)NULL;
+
+/* Information about the current user. */
+struct user_info current_user =
+{
+  (uid_t)-1, (uid_t)-1, (gid_t)-1, (gid_t)-1,
+  (char *)NULL, (char *)NULL, (char *)NULL
+};
+
+/* The current host's name. */
+char *current_host_name = (char *)NULL;
+
+/* Non-zero means that this shell is a login shell.
+   Specifically:
+   0 = not login shell.
+   1 = login shell from getty (or equivalent fake out)
+  -1 = login shell from "--login" (or -l) flag.
+  -2 = both from getty, and from flag.
+ */
+int login_shell = 0;
+
+/* Non-zero means that at this moment, the shell is interactive.  In
+   general, this means that the shell is at this moment reading input
+   from the keyboard. */
+int interactive = 0;
+
+/* Non-zero means that the shell was started as an interactive shell. */
+int interactive_shell = 0;
+
+/* Non-zero means to send a SIGHUP to all jobs when an interactive login
+   shell exits. */
+int hup_on_exit = 0;
+
+/* Non-zero means to list status of running and stopped jobs at shell exit */
+int check_jobs_at_exit = 0;
+
+/* Non-zero means to change to a directory name supplied as a command name */
+int autocd = 0;
+
+/* Tells what state the shell was in when it started:
+       0 = non-interactive shell script
+       1 = interactive
+       2 = -c command
+       3 = wordexp evaluation
+   This is a superset of the information provided by interactive_shell.
+*/
+int startup_state = 0;
+
+/* Special debugging helper. */
+int debugging_login_shell = 0;
+
+/* The environment that the shell passes to other commands. */
+char **shell_environment;
+
+/* Non-zero when we are executing a top-level command. */
+int executing = 0;
+
+/* The number of commands executed so far. */
+int current_command_number = 1;
+
+/* Non-zero is the recursion depth for commands. */
+int indirection_level = 0;
+
+/* The name of this shell, as taken from argv[0]. */
+char *shell_name = (char *)NULL;
+
+/* time in seconds when the shell was started */
+time_t shell_start_time;
+
+/* Are we running in an emacs shell window? */
+int running_under_emacs;
+
+/* Do we have /dev/fd? */
+#ifdef HAVE_DEV_FD
+int have_devfd = HAVE_DEV_FD;
+#else
+int have_devfd = 0;
+#endif
+
+/* The name of the .(shell)rc file. */
+static char *bashrc_file = DEFAULT_BASHRC;
+
+/* Non-zero means to act more like the Bourne shell on startup. */
+static int act_like_sh;
+
+/* Non-zero if this shell is being run by `su'. */
+static int su_shell;
+
+/* Non-zero if we have already expanded and sourced $ENV. */
+static int sourced_env;
+
+/* Is this shell running setuid? */
+static int running_setuid;
+
+/* Values for the long-winded argument names. */
+static int debugging;                  /* Do debugging things. */
+static int no_rc;                      /* Don't execute ~/.bashrc */
+static int no_profile;                 /* Don't execute .profile */
+static int do_version;                 /* Display interesting version info. */
+static int make_login_shell;           /* Make this shell be a `-bash' shell. */
+static int want_initial_help;          /* --help option */
+
+int debugging_mode = 0;                /* In debugging mode with --debugger */
+#if defined (READLINE)
+int no_line_editing = 0;       /* non-zero -> don't do fancy line editing. */
+#else
+int no_line_editing = 1;       /* can't have line editing without readline */
+#endif
+int dump_translatable_strings; /* Dump strings in $"...", don't execute. */
+int dump_po_strings;           /* Dump strings in $"..." in po format */
+int wordexp_only = 0;          /* Do word expansion only */
+int protected_mode = 0;                /* No command substitution with --wordexp */
+
+#if defined (STRICT_POSIX)
+int posixly_correct = 1;       /* Non-zero means posix.2 superset. */
+#else
+int posixly_correct = 0;       /* Non-zero means posix.2 superset. */
+#endif
+
+/* Some long-winded argument names.  These are obviously new. */
+#define Int 1
+#define Charp 2
+static const struct {
+  const char *name;
+  int type;
+  int *int_value;
+  char **char_value;
+} long_args[] = {
+  { "debug", Int, &debugging, (char **)0x0 },
+#if defined (DEBUGGER)
+  { "debugger", Int, &debugging_mode, (char **)0x0 },
+#endif
+  { "dump-po-strings", Int, &dump_po_strings, (char **)0x0 },
+  { "dump-strings", Int, &dump_translatable_strings, (char **)0x0 },
+  { "help", Int, &want_initial_help, (char **)0x0 },
+  { "init-file", Charp, (int *)0x0, &bashrc_file },
+  { "login", Int, &make_login_shell, (char **)0x0 },
+  { "noediting", Int, &no_line_editing, (char **)0x0 },
+  { "noprofile", Int, &no_profile, (char **)0x0 },
+  { "norc", Int, &no_rc, (char **)0x0 },
+  { "posix", Int, &posixly_correct, (char **)0x0 },
+#if defined (WORDEXP_OPTION)
+  { "protected", Int, &protected_mode, (char **)0x0 },
+#endif
+  { "rcfile", Charp, (int *)0x0, &bashrc_file },
+#if defined (RESTRICTED_SHELL)
+  { "restricted", Int, &restricted, (char **)0x0 },
+#endif
+  { "verbose", Int, &echo_input_at_read, (char **)0x0 },
+  { "version", Int, &do_version, (char **)0x0 },
+#if defined (WORDEXP_OPTION)
+  { "wordexp", Int, &wordexp_only, (char **)0x0 },
+#endif
+  { (char *)0x0, Int, (int *)0x0, (char **)0x0 }
+};
+
+/* These are extern so execute_simple_command can set them, and then
+   longjmp back to main to execute a shell script, instead of calling
+   main () again and resulting in indefinite, possibly fatal, stack
+   growth. */
+procenv_t subshell_top_level;
+int subshell_argc;
+char **subshell_argv;
+char **subshell_envp;
+
+char *exec_argv0;
+
+#if defined (BUFFERED_INPUT)
+/* The file descriptor from which the shell is reading input. */
+int default_buffered_input = -1;
+#endif
+
+/* The following two variables are not static so they can show up in $-. */
+int read_from_stdin;           /* -s flag supplied */
+int want_pending_command;      /* -c flag supplied */
+
+/* This variable is not static so it can be bound to $BASH_EXECUTION_STRING */
+char *command_execution_string;        /* argument to -c option */
+
+int malloc_trace_at_exit = 0;
+
+static int shell_reinitialized = 0;
+
+static FILE *default_input;
+
+static STRING_INT_ALIST *shopt_alist;
+static int shopt_ind = 0, shopt_len = 0;
+
+static int parse_long_options __P((char **, int, int));
+static int parse_shell_options __P((char **, int, int));
+static int bind_args __P((char **, int, int, int));
+
+static void start_debugger __P((void));
+
+static void add_shopt_to_alist __P((char *, int));
+static void run_shopt_alist __P((void));
+
+static void execute_env_file __P((char *));
+static void run_startup_files __P((void));
+static int open_shell_script __P((char *));
+static void set_bash_input __P((void));
+static int run_one_command __P((char *));
+#if defined (WORDEXP_OPTION)
+static int run_wordexp __P((char *));
+#endif
+
+static int uidget __P((void));
+
+static void init_interactive __P((void));
+static void init_noninteractive __P((void));
+static void init_interactive_script __P((void));
+
+static void set_shell_name __P((char *));
+static void shell_initialize __P((void));
+static void shell_reinitialize __P((void));
+
+static void show_shell_usage __P((FILE *, int));
+
+#ifdef __CYGWIN__
+static void
+_cygwin32_check_tmp ()
+{
+  struct stat sb;
+
+  if (stat ("/tmp", &sb) < 0)
+    internal_warning (_("could not find /tmp, please create!"));
+  else
+    {
+      if (S_ISDIR (sb.st_mode) == 0)
+       internal_warning (_("/tmp must be a valid directory name"));
+    }
+}
+#endif /* __CYGWIN__ */
+
+#if defined (NO_MAIN_ENV_ARG)
+/* systems without third argument to main() */
+int
+main (argc, argv)
+     int argc;
+     char **argv;
+#else /* !NO_MAIN_ENV_ARG */
+int
+main (argc, argv, env)
+     int argc;
+     char **argv, **env;
+#endif /* !NO_MAIN_ENV_ARG */
+{
+  register int i;
+  int code, old_errexit_flag;
+#if defined (RESTRICTED_SHELL)
+  int saverst;
+#endif
+  volatile int locally_skip_execution;
+  volatile int arg_index, top_level_arg_index;
+#ifdef __OPENNT
+  char **env;
+
+  env = environ;
+#endif /* __OPENNT */
+
+  USE_VAR(argc);
+  USE_VAR(argv);
+  USE_VAR(env);
+  USE_VAR(code);
+  USE_VAR(old_errexit_flag);
+#if defined (RESTRICTED_SHELL)
+  USE_VAR(saverst);
+#endif
+
+  /* Catch early SIGINTs. */
+  code = setjmp_nosigs (top_level);
+  if (code)
+    exit (2);
+
+  xtrace_init ();
+
+#if defined (USING_BASH_MALLOC) && defined (DEBUG) && !defined (DISABLE_MALLOC_WRAPPERS)
+#  if 1
+  malloc_set_register (1);
+#  endif
+#endif
+
+  check_dev_tty ();
+
+#ifdef __CYGWIN__
+  _cygwin32_check_tmp ();
+#endif /* __CYGWIN__ */
+
+  /* Wait forever if we are debugging a login shell. */
+  while (debugging_login_shell) sleep (3);
+
+  set_default_locale ();
+
+  running_setuid = uidget ();
+
+  if (getenv ("POSIXLY_CORRECT") || getenv ("POSIX_PEDANTIC"))
+    posixly_correct = 1;
+
+#if defined (USE_GNU_MALLOC_LIBRARY)
+  mcheck (programming_error, (void (*) ())0);
+#endif /* USE_GNU_MALLOC_LIBRARY */
+
+  if (setjmp_sigs (subshell_top_level))
+    {
+      argc = subshell_argc;
+      argv = subshell_argv;
+      env = subshell_envp;
+      sourced_env = 0;
+    }
+
+  shell_reinitialized = 0;
+
+  /* Initialize `local' variables for all `invocations' of main (). */
+  arg_index = 1;
+  if (arg_index > argc)
+    arg_index = argc;
+  command_execution_string = (char *)NULL;
+  want_pending_command = locally_skip_execution = read_from_stdin = 0;
+  default_input = stdin;
+#if defined (BUFFERED_INPUT)
+  default_buffered_input = -1;
+#endif
+
+  /* Fix for the `infinite process creation' bug when running shell scripts
+     from startup files on System V. */
+  login_shell = make_login_shell = 0;
+
+  /* If this shell has already been run, then reinitialize it to a
+     vanilla state. */
+  if (shell_initialized || shell_name)
+    {
+      /* Make sure that we do not infinitely recurse as a login shell. */
+      if (*shell_name == '-')
+       shell_name++;
+
+      shell_reinitialize ();
+      if (setjmp_nosigs (top_level))
+       exit (2);
+    }
+
+  shell_environment = env;
+  set_shell_name (argv[0]);
+  shell_start_time = NOW;      /* NOW now defined in general.h */
+
+  /* Parse argument flags from the input line. */
+
+  /* Find full word arguments first. */
+  arg_index = parse_long_options (argv, arg_index, argc);
+
+  if (want_initial_help)
+    {
+      show_shell_usage (stdout, 1);
+      exit (EXECUTION_SUCCESS);
+    }
+
+  if (do_version)
+    {
+      show_shell_version (1);
+      exit (EXECUTION_SUCCESS);
+    }
+
+  /* All done with full word options; do standard shell option parsing.*/
+  this_command_name = shell_name;      /* for error reporting */
+  arg_index = parse_shell_options (argv, arg_index, argc);
+
+  /* If user supplied the "--login" (or -l) flag, then set and invert
+     LOGIN_SHELL. */
+  if (make_login_shell)
+    {
+      login_shell++;
+      login_shell = -login_shell;
+    }
+
+  set_login_shell ("login_shell", login_shell != 0);
+
+  if (dump_po_strings)
+    dump_translatable_strings = 1;
+
+  if (dump_translatable_strings)
+    read_but_dont_execute = 1;
+
+  if (running_setuid && privileged_mode == 0)
+    disable_priv_mode ();
+
+  /* Need to get the argument to a -c option processed in the
+     above loop.  The next arg is a command to execute, and the
+     following args are $0...$n respectively. */
+  if (want_pending_command)
+    {
+      command_execution_string = argv[arg_index];
+      if (command_execution_string == 0)
+       {
+         report_error (_("%s: option requires an argument"), "-c");
+         exit (EX_BADUSAGE);
+       }
+      arg_index++;
+    }
+  this_command_name = (char *)NULL;
+
+  cmd_init();          /* initialize the command object caches */
+
+  /* First, let the outside world know about our interactive status.
+     A shell is interactive if the `-i' flag was given, or if all of
+     the following conditions are met:
+       no -c command
+       no arguments remaining or the -s flag given
+       standard input is a terminal
+       standard error is a terminal
+     Refer to Posix.2, the description of the `sh' utility. */
+
+  if (forced_interactive ||            /* -i flag */
+      (!command_execution_string &&    /* No -c command and ... */
+       wordexp_only == 0 &&            /* No --wordexp and ... */
+       ((arg_index == argc) ||         /*   no remaining args or... */
+       read_from_stdin) &&             /*   -s flag with args, and */
+       isatty (fileno (stdin)) &&      /* Input is a terminal and */
+       isatty (fileno (stderr))))      /* error output is a terminal. */
+    init_interactive ();
+  else
+    init_noninteractive ();
+
+  /*
+   * Some systems have the bad habit of starting login shells with lots of open
+   * file descriptors.  For instance, most systems that have picked up the
+   * pre-4.0 Sun YP code leave a file descriptor open each time you call one
+   * of the getpw* functions, and it's set to be open across execs.  That
+   * means one for login, one for xterm, one for shelltool, etc.  There are
+   * also systems that open persistent FDs to other agents or files as part
+   * of process startup; these need to be set to be close-on-exec.
+   */
+  if (login_shell && interactive_shell)
+    {
+      for (i = 3; i < 20; i++)
+       SET_CLOSE_ON_EXEC (i);
+    }
+
+  /* If we're in a strict Posix.2 mode, turn on interactive comments,
+     alias expansion in non-interactive shells, and other Posix.2 things. */
+  if (posixly_correct)
+    {
+      bind_variable ("POSIXLY_CORRECT", "y", 0);
+      sv_strict_posix ("POSIXLY_CORRECT");
+    }
+
+  /* Now we run the shopt_alist and process the options. */
+  if (shopt_alist)
+    run_shopt_alist ();
+
+  /* From here on in, the shell must be a normal functioning shell.
+     Variables from the environment are expected to be set, etc. */
+  shell_initialize ();
+
+  set_default_lang ();
+  set_default_locale_vars ();
+
+  /*
+   * M-x term -> TERM=eterm EMACS=22.1 (term:0.96)     (eterm)
+   * M-x shell -> TERM=dumb EMACS=t                    (no line editing)
+   * M-x terminal -> TERM=emacs-em7955 EMACS=          (line editing)
+   */
+  if (interactive_shell)
+    {
+      char *term, *emacs;
+
+      term = get_string_value ("TERM");
+      emacs = get_string_value ("EMACS");
+
+      /* Not sure any emacs terminal emulator sets TERM=emacs any more */
+      no_line_editing |= term && (STREQ (term, "emacs"));
+      no_line_editing |= emacs && emacs[0] == 't' && emacs[1] == '\0' && STREQ (term, "dumb");
+
+      /* running_under_emacs == 2 for `eterm' */
+      running_under_emacs = (emacs != 0) || (term && STREQN (term, "emacs", 5));
+      running_under_emacs += term && STREQN (term, "eterm", 5) && emacs && strstr (emacs, "term");
+
+      if (running_under_emacs)
+       gnu_error_format = 1;
+    }
+
+  top_level_arg_index = arg_index;
+  old_errexit_flag = exit_immediately_on_error;
+
+  /* Give this shell a place to longjmp to before executing the
+     startup files.  This allows users to press C-c to abort the
+     lengthy startup. */
+  code = setjmp_sigs (top_level);
+  if (code)
+    {
+      if (code == EXITPROG || code == ERREXIT)
+       exit_shell (last_command_exit_value);
+      else
+       {
+#if defined (JOB_CONTROL)
+         /* Reset job control, since run_startup_files turned it off. */
+         set_job_control (interactive_shell);
+#endif
+         /* Reset value of `set -e', since it's turned off before running
+            the startup files. */
+         exit_immediately_on_error += old_errexit_flag;
+         locally_skip_execution++;
+       }
+    }
+
+  arg_index = top_level_arg_index;
+
+  /* Execute the start-up scripts. */
+
+  if (interactive_shell == 0)
+    {
+      unbind_variable ("PS1");
+      unbind_variable ("PS2");
+      interactive = 0;
+#if 0
+      /* This has already been done by init_noninteractive */
+      expand_aliases = posixly_correct;
+#endif
+    }
+  else
+    {
+      change_flag ('i', FLAG_ON);
+      interactive = 1;
+    }
+
+#if defined (RESTRICTED_SHELL)
+  /* Set restricted_shell based on whether the basename of $0 indicates that
+     the shell should be restricted or if the `-r' option was supplied at
+     startup. */
+  restricted_shell = shell_is_restricted (shell_name);
+
+  /* If the `-r' option is supplied at invocation, make sure that the shell
+     is not in restricted mode when running the startup files. */
+  saverst = restricted;
+  restricted = 0;
+#endif
+
+  /* The startup files are run with `set -e' temporarily disabled. */
+  if (locally_skip_execution == 0 && running_setuid == 0)
+    {
+      old_errexit_flag = exit_immediately_on_error;
+      exit_immediately_on_error = 0;
+
+      run_startup_files ();
+      exit_immediately_on_error += old_errexit_flag;
+    }
+
+  /* If we are invoked as `sh', turn on Posix mode. */
+  if (act_like_sh)
+    {
+      bind_variable ("POSIXLY_CORRECT", "y", 0);
+      sv_strict_posix ("POSIXLY_CORRECT");
+    }
+
+#if defined (RESTRICTED_SHELL)
+  /* Turn on the restrictions after executing the startup files.  This
+     means that `bash -r' or `set -r' invoked from a startup file will
+     turn on the restrictions after the startup files are executed. */
+  restricted = saverst || restricted;
+  if (shell_reinitialized == 0)
+    maybe_make_restricted (shell_name);
+#endif /* RESTRICTED_SHELL */
+
+#if defined (WORDEXP_OPTION)
+  if (wordexp_only)
+    {
+      startup_state = 3;
+      last_command_exit_value = run_wordexp (argv[arg_index]);
+      exit_shell (last_command_exit_value);
+    }
+#endif
+
+  if (command_execution_string)
+    {
+      arg_index = bind_args (argv, arg_index, argc, 0);
+      startup_state = 2;
+
+      if (debugging_mode)
+       start_debugger ();
+
+#if defined (ONESHOT)
+      executing = 1;
+      run_one_command (command_execution_string);
+      exit_shell (last_command_exit_value);
+#else /* ONESHOT */
+      with_input_from_string (command_execution_string, "-c");
+      goto read_and_execute;
+#endif /* !ONESHOT */
+    }
+
+  /* Get possible input filename and set up default_buffered_input or
+     default_input as appropriate. */
+  if (arg_index != argc && read_from_stdin == 0)
+    {
+      open_shell_script (argv[arg_index]);
+      arg_index++;
+    }
+  else if (interactive == 0)
+    /* In this mode, bash is reading a script from stdin, which is a
+       pipe or redirected file. */
+#if defined (BUFFERED_INPUT)
+    default_buffered_input = fileno (stdin);   /* == 0 */
+#else
+    setbuf (default_input, (char *)NULL);
+#endif /* !BUFFERED_INPUT */
+
+  set_bash_input ();
+
+  /* Bind remaining args to $1 ... $n */
+  arg_index = bind_args (argv, arg_index, argc, 1);
+
+  if (debugging_mode && locally_skip_execution == 0 && running_setuid == 0 && (dollar_vars[1] || interactive_shell == 0))
+    start_debugger ();
+
+  /* Do the things that should be done only for interactive shells. */
+  if (interactive_shell)
+    {
+      /* Set up for checking for presence of mail. */
+      reset_mail_timer ();
+      init_mail_dates ();
+
+#if defined (HISTORY)
+      /* Initialize the interactive history stuff. */
+      bash_initialize_history ();
+      /* Don't load the history from the history file if we've already
+        saved some lines in this session (e.g., by putting `history -s xx'
+        into one of the startup files). */
+      if (shell_initialized == 0 && history_lines_this_session == 0)
+       load_history ();
+#endif /* HISTORY */
+
+      /* Initialize terminal state for interactive shells after the
+        .bash_profile and .bashrc are interpreted. */
+      get_tty_state ();
+    }
+
+#if !defined (ONESHOT)
+ read_and_execute:
+#endif /* !ONESHOT */
+
+  shell_initialized = 1;
+
+  /* Read commands until exit condition. */
+  reader_loop ();
+  exit_shell (last_command_exit_value);
+}
+
+static int
+parse_long_options (argv, arg_start, arg_end)
+     char **argv;
+     int arg_start, arg_end;
+{
+  int arg_index, longarg, i;
+  char *arg_string;
+
+  arg_index = arg_start;
+  while ((arg_index != arg_end) && (arg_string = argv[arg_index]) &&
+        (*arg_string == '-'))
+    {
+      longarg = 0;
+
+      /* Make --login equivalent to -login. */
+      if (arg_string[1] == '-' && arg_string[2])
+       {
+         longarg = 1;
+         arg_string++;
+       }
+
+      for (i = 0; long_args[i].name; i++)
+       {
+         if (STREQ (arg_string + 1, long_args[i].name))
+           {
+             if (long_args[i].type == Int)
+               *long_args[i].int_value = 1;
+             else if (argv[++arg_index] == 0)
+               {
+                 report_error (_("%s: option requires an argument"), long_args[i].name);
+                 exit (EX_BADUSAGE);
+               }
+             else
+               *long_args[i].char_value = argv[arg_index];
+
+             break;
+           }
+       }
+      if (long_args[i].name == 0)
+       {
+         if (longarg)
+           {
+             report_error (_("%s: invalid option"), argv[arg_index]);
+             show_shell_usage (stderr, 0);
+             exit (EX_BADUSAGE);
+           }
+         break;                /* No such argument.  Maybe flag arg. */
+       }
+
+      arg_index++;
+    }
+
+  return (arg_index);
+}
+
+static int
+parse_shell_options (argv, arg_start, arg_end)
+     char **argv;
+     int arg_start, arg_end;
+{
+  int arg_index;
+  int arg_character, on_or_off, next_arg, i;
+  char *o_option, *arg_string;
+
+  arg_index = arg_start;
+  while (arg_index != arg_end && (arg_string = argv[arg_index]) &&
+        (*arg_string == '-' || *arg_string == '+'))
+    {
+      /* There are flag arguments, so parse them. */
+      next_arg = arg_index + 1;
+
+      /* A single `-' signals the end of options.  From the 4.3 BSD sh.
+        An option `--' means the same thing; this is the standard
+        getopt(3) meaning. */
+      if (arg_string[0] == '-' &&
+          (arg_string[1] == '\0' ||
+            (arg_string[1] == '-' && arg_string[2] == '\0')))
+       return (next_arg);
+
+      i = 1;
+      on_or_off = arg_string[0];
+      while (arg_character = arg_string[i++])
+       {
+         switch (arg_character)
+           {
+           case 'c':
+             want_pending_command = 1;
+             break;
+
+           case 'l':
+             make_login_shell = 1;
+             break;
+
+           case 's':
+             read_from_stdin = 1;
+             break;
+
+           case 'o':
+             o_option = argv[next_arg];
+             if (o_option == 0)
+               {
+                 list_minus_o_opts (-1, (on_or_off == '-') ? 0 : 1);
+                 break;
+               }
+             if (set_minus_o_option (on_or_off, o_option) != EXECUTION_SUCCESS)
+               exit (EX_BADUSAGE);
+             next_arg++;
+             break;
+
+           case 'O':
+             /* Since some of these can be overridden by the normal
+                interactive/non-interactive shell initialization or
+                initializing posix mode, we save the options and process
+                them after initialization. */
+             o_option = argv[next_arg];
+             if (o_option == 0)
+               {
+                 shopt_listopt (o_option, (on_or_off == '-') ? 0 : 1);
+                 break;
+               }
+             add_shopt_to_alist (o_option, on_or_off);
+             next_arg++;
+             break;
+
+           case 'D':
+             dump_translatable_strings = 1;
+             break;
+
+           default:
+             if (change_flag (arg_character, on_or_off) == FLAG_ERROR)
+               {
+                 report_error (_("%c%c: invalid option"), on_or_off, arg_character);
+                 show_shell_usage (stderr, 0);
+                 exit (EX_BADUSAGE);
+               }
+           }
+       }
+      /* Can't do just a simple increment anymore -- what about
+        "bash -abouo emacs ignoreeof -hP"? */
+      arg_index = next_arg;
+    }
+
+  return (arg_index);
+}
+
+/* Exit the shell with status S. */
+void
+exit_shell (s)
+     int s;
+{
+  fflush (stdout);             /* XXX */
+  fflush (stderr);
+
+  /* Clean up the terminal if we are in a state where it's been modified. */
+#if defined (READLINE)
+  if (RL_ISSTATE (RL_STATE_TERMPREPPED) && rl_deprep_term_function)
+    (*rl_deprep_term_function) ();
+#endif
+  if (read_tty_modified ())
+    read_tty_cleanup ();
+
+  /* Do trap[0] if defined.  Allow it to override the exit status
+     passed to us. */
+  if (signal_is_trapped (0))
+    s = run_exit_trap ();
+
+#if defined (PROCESS_SUBSTITUTION)
+  unlink_fifo_list ();
+#endif /* PROCESS_SUBSTITUTION */
+
+#if defined (HISTORY)
+  if (remember_on_history)
+    maybe_save_shell_history ();
+#endif /* HISTORY */
+
+#if defined (COPROCESS_SUPPORT)
+  coproc_flush ();
+#endif
+
+#if defined (JOB_CONTROL)
+  /* If the user has run `shopt -s huponexit', hangup all jobs when we exit
+     an interactive login shell.  ksh does this unconditionally. */
+  if (interactive_shell && login_shell && hup_on_exit)
+    hangup_all_jobs ();
+
+  /* If this shell is interactive, or job control is active, terminate all
+     stopped jobs and restore the original terminal process group.  Don't do
+     this if we're in a subshell and calling exit_shell after, for example,
+     a failed word expansion.  We want to do this even if the shell is not
+     interactive because we set the terminal's process group when job control
+     is enabled regardless of the interactive status. */
+  if (subshell_environment == 0)
+    end_job_control ();
+#endif /* JOB_CONTROL */
+
+  /* Always return the exit status of the last command to our parent. */
+  sh_exit (s);
+}
+
+/* A wrapper for exit that (optionally) can do other things, like malloc
+   statistics tracing. */
+void
+sh_exit (s)
+     int s;
+{
+#if defined (MALLOC_DEBUG) && defined (USING_BASH_MALLOC)
+  if (malloc_trace_at_exit)
+    trace_malloc_stats (get_name_for_error (), (char *)NULL);
+#endif
+
+  exit (s);
+}
+
+/* Exit a subshell, which includes calling the exit trap.  We don't want to
+   do any more cleanup, since a subshell is created as an exact copy of its
+   parent. */
+void
+subshell_exit (s)
+     int s;
+{
+  fflush (stdout);
+  fflush (stderr);
+
+  /* Do trap[0] if defined.  Allow it to override the exit status
+     passed to us. */
+  if (signal_is_trapped (0))
+    s = run_exit_trap ();
+
+  sh_exit (s);
+}
+
+/* Source the bash startup files.  If POSIXLY_CORRECT is non-zero, we obey
+   the Posix.2 startup file rules:  $ENV is expanded, and if the file it
+   names exists, that file is sourced.  The Posix.2 rules are in effect
+   for interactive shells only. (section 4.56.5.3) */
+
+/* Execute ~/.bashrc for most shells.  Never execute it if
+   ACT_LIKE_SH is set, or if NO_RC is set.
+
+   If the executable file "/usr/gnu/src/bash/foo" contains:
+
+   #!/usr/gnu/bin/bash
+   echo hello
+
+   then:
+
+        COMMAND            EXECUTE BASHRC
+        --------------------------------
+        bash -c foo            NO
+        bash foo               NO
+        foo                    NO
+        rsh machine ls         YES (for rsh, which calls `bash -c')
+        rsh machine foo        YES (for shell started by rsh) NO (for foo!)
+        echo ls | bash         NO
+        login                  NO
+        bash                   YES
+*/
+
+static void
+execute_env_file (env_file)
+      char *env_file;
+{
+  char *fn;
+
+  if (env_file && *env_file)
+    {
+      fn = expand_string_unsplit_to_string (env_file, Q_DOUBLE_QUOTES);
+      if (fn && *fn)
+       maybe_execute_file (fn, 1);
+      FREE (fn);
+    }
+}
+
+static void
+run_startup_files ()
+{
+#if defined (JOB_CONTROL)
+  int old_job_control;
+#endif
+  int sourced_login, run_by_ssh;
+
+  /* get the rshd/sshd case out of the way first. */
+  if (interactive_shell == 0 && no_rc == 0 && login_shell == 0 &&
+      act_like_sh == 0 && command_execution_string)
+    {
+#ifdef SSH_SOURCE_BASHRC
+      run_by_ssh = (find_variable ("SSH_CLIENT") != (SHELL_VAR *)0) ||
+                  (find_variable ("SSH2_CLIENT") != (SHELL_VAR *)0);
+#else
+      run_by_ssh = 0;
+#endif
+
+      /* If we were run by sshd or we think we were run by rshd, execute
+        ~/.bashrc if we are a top-level shell. */
+      if ((run_by_ssh || isnetconn (fileno (stdin))) && shell_level < 2)
+       {
+#ifdef SYS_BASHRC
+#  if defined (__OPENNT)
+         maybe_execute_file (_prefixInstallPath(SYS_BASHRC, NULL, 0), 1);
+#  else
+         maybe_execute_file (SYS_BASHRC, 1);
+#  endif
+#endif
+         maybe_execute_file (bashrc_file, 1);
+         return;
+       }
+    }
+
+#if defined (JOB_CONTROL)
+  /* Startup files should be run without job control enabled. */
+  old_job_control = interactive_shell ? set_job_control (0) : 0;
+#endif
+
+  sourced_login = 0;
+
+  /* A shell begun with the --login (or -l) flag that is not in posix mode
+     runs the login shell startup files, no matter whether or not it is
+     interactive.  If NON_INTERACTIVE_LOGIN_SHELLS is defined, run the
+     startup files if argv[0][0] == '-' as well. */
+#if defined (NON_INTERACTIVE_LOGIN_SHELLS)
+  if (login_shell && posixly_correct == 0)
+#else
+  if (login_shell < 0 && posixly_correct == 0)
+#endif
+    {
+      /* We don't execute .bashrc for login shells. */
+      no_rc++;
+
+      /* Execute /etc/profile and one of the personal login shell
+        initialization files. */
+      if (no_profile == 0)
+       {
+         maybe_execute_file (SYS_PROFILE, 1);
+
+         if (act_like_sh)      /* sh */
+           maybe_execute_file ("~/.profile", 1);
+         else if ((maybe_execute_file ("~/.bash_profile", 1) == 0) &&
+                  (maybe_execute_file ("~/.bash_login", 1) == 0))      /* bash */
+           maybe_execute_file ("~/.profile", 1);
+       }
+
+      sourced_login = 1;
+    }
+
+  /* A non-interactive shell not named `sh' and not in posix mode reads and
+     executes commands from $BASH_ENV.  If `su' starts a shell with `-c cmd'
+     and `-su' as the name of the shell, we want to read the startup files.
+     No other non-interactive shells read any startup files. */
+  if (interactive_shell == 0 && !(su_shell && login_shell))
+    {
+      if (posixly_correct == 0 && act_like_sh == 0 && privileged_mode == 0 &&
+           sourced_env++ == 0)
+       execute_env_file (get_string_value ("BASH_ENV"));
+      return;
+    }
+
+  /* Interactive shell or `-su' shell. */
+  if (posixly_correct == 0)              /* bash, sh */
+    {
+      if (login_shell && sourced_login++ == 0)
+       {
+         /* We don't execute .bashrc for login shells. */
+         no_rc++;
+
+         /* Execute /etc/profile and one of the personal login shell
+            initialization files. */
+         if (no_profile == 0)
+           {
+             maybe_execute_file (SYS_PROFILE, 1);
+
+             if (act_like_sh)  /* sh */
+               maybe_execute_file ("~/.profile", 1);
+             else if ((maybe_execute_file ("~/.bash_profile", 1) == 0) &&
+                      (maybe_execute_file ("~/.bash_login", 1) == 0))  /* bash */
+               maybe_execute_file ("~/.profile", 1);
+           }
+       }
+
+      /* bash */
+      if (act_like_sh == 0 && no_rc == 0)
+       {
+#ifdef SYS_BASHRC
+#  if defined (__OPENNT)
+         maybe_execute_file (_prefixInstallPath(SYS_BASHRC, NULL, 0), 1);
+#  else
+         maybe_execute_file (SYS_BASHRC, 1);
+#  endif
+#endif
+         maybe_execute_file (bashrc_file, 1);
+       }
+      /* sh */
+      else if (act_like_sh && privileged_mode == 0 && sourced_env++ == 0)
+       execute_env_file (get_string_value ("ENV"));
+    }
+  else         /* bash --posix, sh --posix */
+    {
+      /* bash and sh */
+      if (interactive_shell && privileged_mode == 0 && sourced_env++ == 0)
+       execute_env_file (get_string_value ("ENV"));
+    }
+
+#if defined (JOB_CONTROL)
+  set_job_control (old_job_control);
+#endif
+}
+
+#if defined (RESTRICTED_SHELL)
+/* Return 1 if the shell should be a restricted one based on NAME or the
+   value of `restricted'.  Don't actually do anything, just return a
+   boolean value. */
+int
+shell_is_restricted (name)
+     char *name;
+{
+  char *temp;
+
+  if (restricted)
+    return 1;
+  temp = base_pathname (name);
+  if (*temp == '-')
+    temp++;
+  return (STREQ (temp, RESTRICTED_SHELL_NAME));
+}
+
+/* Perhaps make this shell a `restricted' one, based on NAME.  If the
+   basename of NAME is "rbash", then this shell is restricted.  The
+   name of the restricted shell is a configurable option, see config.h.
+   In a restricted shell, PATH, SHELL, ENV, and BASH_ENV are read-only
+   and non-unsettable.
+   Do this also if `restricted' is already set to 1; maybe the shell was
+   started with -r. */
+int
+maybe_make_restricted (name)
+     char *name;
+{
+  char *temp;
+
+  temp = base_pathname (name);
+  if (*temp == '-')
+    temp++;
+  if (restricted || (STREQ (temp, RESTRICTED_SHELL_NAME)))
+    {
+      set_var_read_only ("PATH");
+      set_var_read_only ("SHELL");
+      set_var_read_only ("ENV");
+      set_var_read_only ("BASH_ENV");
+      restricted = 1;
+    }
+  return (restricted);
+}
+#endif /* RESTRICTED_SHELL */
+
+/* Fetch the current set of uids and gids and return 1 if we're running
+   setuid or setgid. */
+static int
+uidget ()
+{
+  uid_t u;
+
+  u = getuid ();
+  if (current_user.uid != u)
+    {
+      FREE (current_user.user_name);
+      FREE (current_user.shell);
+      FREE (current_user.home_dir);
+      current_user.user_name = current_user.shell = current_user.home_dir = (char *)NULL;
+    }
+  current_user.uid = u;
+  current_user.gid = getgid ();
+  current_user.euid = geteuid ();
+  current_user.egid = getegid ();
+
+  /* See whether or not we are running setuid or setgid. */
+  return (current_user.uid != current_user.euid) ||
+          (current_user.gid != current_user.egid);
+}
+
+void
+disable_priv_mode ()
+{
+  int e;
+
+  if (setuid (current_user.uid) < 0)
+    {
+      e = errno;
+      sys_error (_("cannot set uid to %d: effective uid %d"), current_user.uid, current_user.euid);
+#if defined (EXIT_ON_SETUID_FAILURE)
+      if (e == EAGAIN)
+       exit (e);
+#endif
+    }
+  if (setgid (current_user.gid) < 0)
+    sys_error (_("cannot set gid to %d: effective gid %d"), current_user.gid, current_user.egid);
+
+  current_user.euid = current_user.uid;
+  current_user.egid = current_user.gid;
+}
+
+#if defined (WORDEXP_OPTION)
+static int
+run_wordexp (words)
+     char *words;
+{
+  int code, nw, nb;
+  WORD_LIST *wl, *tl, *result;
+
+  code = setjmp_nosigs (top_level);
+
+  if (code != NOT_JUMPED)
+    {
+      switch (code)
+       {
+         /* Some kind of throw to top_level has occurred. */
+       case FORCE_EOF:
+         return last_command_exit_value = 127;
+       case ERREXIT:
+       case EXITPROG:
+         return last_command_exit_value;
+       case DISCARD:
+         return last_command_exit_value = 1;
+       default:
+         command_error ("run_wordexp", CMDERR_BADJUMP, code, 0);
+       }
+    }
+
+  /* Run it through the parser to get a list of words and expand them */
+  if (words && *words)
+    {
+      with_input_from_string (words, "--wordexp");
+      if (parse_command () != 0)
+       return (126);
+      if (global_command == 0)
+       {
+         printf ("0\n0\n");
+         return (0);
+       }
+      if (global_command->type != cm_simple)
+       return (126);
+      wl = global_command->value.Simple->words;
+      if (protected_mode)
+       for (tl = wl; tl; tl = tl->next)
+         tl->word->flags |= W_NOCOMSUB|W_NOPROCSUB;
+      result = wl ? expand_words_no_vars (wl) : (WORD_LIST *)0;
+    }
+  else
+    result = (WORD_LIST *)0;
+
+  last_command_exit_value = 0;
+
+  if (result == 0)
+    {
+      printf ("0\n0\n");
+      return (0);
+    }
+
+  /* Count up the number of words and bytes, and print them.  Don't count
+     the trailing NUL byte. */
+  for (nw = nb = 0, wl = result; wl; wl = wl->next)
+    {
+      nw++;
+      nb += strlen (wl->word->word);
+    }
+  printf ("%u\n%u\n", nw, nb);
+  /* Print each word on a separate line.  This will have to be changed when
+     the interface to glibc is completed. */
+  for (wl = result; wl; wl = wl->next)
+    printf ("%s\n", wl->word->word);
+
+  return (0);
+}
+#endif
+
+#if defined (ONESHOT)
+/* Run one command, given as the argument to the -c option.  Tell
+   parse_and_execute not to fork for a simple command. */
+static int
+run_one_command (command)
+     char *command;
+{
+  int code;
+
+  code = setjmp_nosigs (top_level);
+
+  if (code != NOT_JUMPED)
+    {
+#if defined (PROCESS_SUBSTITUTION)
+      unlink_fifo_list ();
+#endif /* PROCESS_SUBSTITUTION */
+      switch (code)
+       {
+         /* Some kind of throw to top_level has occurred. */
+       case FORCE_EOF:
+         return last_command_exit_value = 127;
+       case ERREXIT:
+       case EXITPROG:
+         return last_command_exit_value;
+       case DISCARD:
+         return last_command_exit_value = 1;
+       default:
+         command_error ("run_one_command", CMDERR_BADJUMP, code, 0);
+       }
+    }
+   return (parse_and_execute (savestring (command), "-c", SEVAL_NOHIST));
+}
+#endif /* ONESHOT */
+
+static int
+bind_args (argv, arg_start, arg_end, start_index)
+     char **argv;
+     int arg_start, arg_end, start_index;
+{
+  register int i;
+  WORD_LIST *args;
+
+  for (i = arg_start, args = (WORD_LIST *)NULL; i < arg_end; i++)
+    args = make_word_list (make_word (argv[i]), args);
+  if (args)
+    {
+      args = REVERSE_LIST (args, WORD_LIST *);
+      if (start_index == 0)    /* bind to $0...$n for sh -c command */
+       {
+         /* Posix.2 4.56.3 says that the first argument after sh -c command
+            becomes $0, and the rest of the arguments become $1...$n */
+         shell_name = savestring (args->word->word);
+         FREE (dollar_vars[0]);
+         dollar_vars[0] = savestring (args->word->word);
+         remember_args (args->next, 1);
+         push_args (args->next);       /* BASH_ARGV and BASH_ARGC */
+       }
+      else                     /* bind to $1...$n for shell script */
+        {
+         remember_args (args, 1);
+         push_args (args);             /* BASH_ARGV and BASH_ARGC */
+        }
+
+      dispose_words (args);
+    }
+
+  return (i);
+}
+
+void
+unbind_args ()
+{
+  remember_args ((WORD_LIST *)NULL, 1);
+  pop_args ();                         /* Reset BASH_ARGV and BASH_ARGC */
+}
+
+static void
+start_debugger ()
+{
+#if defined (DEBUGGER) && defined (DEBUGGER_START_FILE)
+  int old_errexit;
+  int r;
+
+  old_errexit = exit_immediately_on_error;
+  exit_immediately_on_error = 0;
+
+  r = force_execute_file (DEBUGGER_START_FILE, 1);
+  if (r < 0)
+    {
+      internal_warning ("cannot start debugger; debugging mode disabled");
+      debugging_mode = function_trace_mode = 0;
+    }
+  else
+    function_trace_mode = 1;
+
+  exit_immediately_on_error += old_errexit;
+#endif
+}
+
+static int
+open_shell_script (script_name)
+     char *script_name;
+{
+  int fd, e, fd_is_tty;
+  char *filename, *path_filename, *t;
+  char sample[80];
+  int sample_len;
+  struct stat sb;
+#if defined (ARRAY_VARS)
+  SHELL_VAR *funcname_v, *bash_source_v, *bash_lineno_v;
+  ARRAY *funcname_a, *bash_source_a, *bash_lineno_a;
+#endif
+
+  filename = savestring (script_name);
+
+  fd = open (filename, O_RDONLY);
+  if ((fd < 0) && (errno == ENOENT) && (absolute_program (filename) == 0))
+    {
+      e = errno;
+      /* If it's not in the current directory, try looking through PATH
+        for it. */
+      path_filename = find_path_file (script_name);
+      if (path_filename)
+       {
+         free (filename);
+         filename = path_filename;
+         fd = open (filename, O_RDONLY);
+       }
+      else
+       errno = e;
+    }
+
+  if (fd < 0)
+    {
+      e = errno;
+      file_error (filename);
+      exit ((e == ENOENT) ? EX_NOTFOUND : EX_NOINPUT);
+    }
+
+  free (dollar_vars[0]);
+  dollar_vars[0] = exec_argv0 ? savestring (exec_argv0) : savestring (script_name);
+  if (exec_argv0)
+    {
+      free (exec_argv0);
+      exec_argv0 = (char *)NULL;
+    }
+
+#if defined (ARRAY_VARS)
+  GET_ARRAY_FROM_VAR ("FUNCNAME", funcname_v, funcname_a);
+  GET_ARRAY_FROM_VAR ("BASH_SOURCE", bash_source_v, bash_source_a);
+  GET_ARRAY_FROM_VAR ("BASH_LINENO", bash_lineno_v, bash_lineno_a);
+
+  array_push (bash_source_a, filename);
+  if (bash_lineno_a)
+    {
+      t = itos (executing_line_number ());
+      array_push (bash_lineno_a, t);
+      free (t);
+    }
+  array_push (funcname_a, "main");
+#endif
+
+#ifdef HAVE_DEV_FD
+  fd_is_tty = isatty (fd);
+#else
+  fd_is_tty = 0;
+#endif
+
+  /* Only do this with non-tty file descriptors we can seek on. */
+  if (fd_is_tty == 0 && (lseek (fd, 0L, 1) != -1))
+    {
+      /* Check to see if the `file' in `bash file' is a binary file
+        according to the same tests done by execute_simple_command (),
+        and report an error and exit if it is. */
+      sample_len = read (fd, sample, sizeof (sample));
+      if (sample_len < 0)
+       {
+         e = errno;
+         if ((fstat (fd, &sb) == 0) && S_ISDIR (sb.st_mode))
+           internal_error (_("%s: is a directory"), filename);
+         else
+           {
+             errno = e;
+             file_error (filename);
+           }
+         exit (EX_NOEXEC);
+       }
+      else if (sample_len > 0 && (check_binary_file (sample, sample_len)))
+       {
+         internal_error (_("%s: cannot execute binary file"), filename);
+         exit (EX_BINARY_FILE);
+       }
+      /* Now rewind the file back to the beginning. */
+      lseek (fd, 0L, 0);
+    }
+
+  /* Open the script.  But try to move the file descriptor to a randomly
+     large one, in the hopes that any descriptors used by the script will
+     not match with ours. */
+  fd = move_to_high_fd (fd, 1, -1);
+
+#if defined (BUFFERED_INPUT)
+  default_buffered_input = fd;
+  SET_CLOSE_ON_EXEC (default_buffered_input);
+#else /* !BUFFERED_INPUT */
+  default_input = fdopen (fd, "r");
+
+  if (default_input == 0)
+    {
+      file_error (filename);
+      exit (EX_NOTFOUND);
+    }
+
+  SET_CLOSE_ON_EXEC (fd);
+  if (fileno (default_input) != fd)
+    SET_CLOSE_ON_EXEC (fileno (default_input));
+#endif /* !BUFFERED_INPUT */
+
+  /* Just about the only way for this code to be executed is if something
+     like `bash -i /dev/stdin' is executed. */
+  if (interactive_shell && fd_is_tty)
+    {
+      dup2 (fd, 0);
+      close (fd);
+      fd = 0;
+#if defined (BUFFERED_INPUT)
+      default_buffered_input = 0;
+#else
+      fclose (default_input);
+      default_input = stdin;
+#endif
+    }
+  else if (forced_interactive && fd_is_tty == 0)
+    /* But if a script is called with something like `bash -i scriptname',
+       we need to do a non-interactive setup here, since we didn't do it
+       before. */
+    init_interactive_script ();
+
+  free (filename);
+  return (fd);
+}
+
+/* Initialize the input routines for the parser. */
+static void
+set_bash_input ()
+{
+  /* Make sure the fd from which we are reading input is not in
+     no-delay mode. */
+#if defined (BUFFERED_INPUT)
+  if (interactive == 0)
+    sh_unset_nodelay_mode (default_buffered_input);
+  else
+#endif /* !BUFFERED_INPUT */
+    sh_unset_nodelay_mode (fileno (stdin));
+
+  /* with_input_from_stdin really means `with_input_from_readline' */
+  if (interactive && no_line_editing == 0)
+    with_input_from_stdin ();
+#if defined (BUFFERED_INPUT)
+  else if (interactive == 0)
+    with_input_from_buffered_stream (default_buffered_input, dollar_vars[0]);
+#endif /* BUFFERED_INPUT */
+  else
+    with_input_from_stream (default_input, dollar_vars[0]);
+}
+
+/* Close the current shell script input source and forget about it.  This is
+   extern so execute_cmd.c:initialize_subshell() can call it.  If CHECK_ZERO
+   is non-zero, we close default_buffered_input even if it's the standard
+   input (fd 0). */
+void
+unset_bash_input (check_zero)
+     int check_zero;
+{
+#if defined (BUFFERED_INPUT)
+  if ((check_zero && default_buffered_input >= 0) ||
+      (check_zero == 0 && default_buffered_input > 0))
+    {
+      close_buffered_fd (default_buffered_input);
+      default_buffered_input = bash_input.location.buffered_fd = -1;
+      bash_input.type = st_none;               /* XXX */
+    }
+#else /* !BUFFERED_INPUT */
+  if (default_input)
+    {
+      fclose (default_input);
+      default_input = (FILE *)NULL;
+    }
+#endif /* !BUFFERED_INPUT */
+}
+      
+
+#if !defined (PROGRAM)
+#  define PROGRAM "bash"
+#endif
+
+static void
+set_shell_name (argv0)
+     char *argv0;
+{
+  /* Here's a hack.  If the name of this shell is "sh", then don't do
+     any startup files; just try to be more like /bin/sh. */
+  shell_name = argv0 ? base_pathname (argv0) : PROGRAM;
+
+  if (argv0 && *argv0 == '-')
+    {
+      if (*shell_name == '-')
+       shell_name++;
+      login_shell = 1;
+    }
+
+  if (shell_name[0] == 's' && shell_name[1] == 'h' && shell_name[2] == '\0')
+    act_like_sh++;
+  if (shell_name[0] == 's' && shell_name[1] == 'u' && shell_name[2] == '\0')
+    su_shell++;
+
+  shell_name = argv0 ? argv0 : PROGRAM;
+  FREE (dollar_vars[0]);
+  dollar_vars[0] = savestring (shell_name);
+
+  /* A program may start an interactive shell with
+         "execl ("/bin/bash", "-", NULL)".
+     If so, default the name of this shell to our name. */
+  if (!shell_name || !*shell_name || (shell_name[0] == '-' && !shell_name[1]))
+    shell_name = PROGRAM;
+}
+
+static void
+init_interactive ()
+{
+  expand_aliases = interactive_shell = startup_state = 1;
+  interactive = 1;
+}
+
+static void
+init_noninteractive ()
+{
+#if defined (HISTORY)
+  bash_history_reinit (0);
+#endif /* HISTORY */
+  interactive_shell = startup_state = interactive = 0;
+  expand_aliases = posixly_correct;    /* XXX - was 0 not posixly_correct */
+  no_line_editing = 1;
+#if defined (JOB_CONTROL)
+  /* Even if the shell is not interactive, enable job control if the -i or
+     -m option is supplied at startup. */
+  set_job_control (forced_interactive||jobs_m_flag);
+#endif /* JOB_CONTROL */
+}
+
+static void
+init_interactive_script ()
+{
+  init_noninteractive ();
+  expand_aliases = interactive_shell = startup_state = 1;
+}
+
+void
+get_current_user_info ()
+{
+  struct passwd *entry;
+
+  /* Don't fetch this more than once. */
+  if (current_user.user_name == 0)
+    {
+#if defined (__TANDEM)
+      entry = getpwnam (getlogin ());
+#else
+      entry = getpwuid (current_user.uid);
+#endif
+      if (entry)
+       {
+         current_user.user_name = savestring (entry->pw_name);
+         current_user.shell = (entry->pw_shell && entry->pw_shell[0])
+                               ? savestring (entry->pw_shell)
+                               : savestring ("/bin/sh");
+         current_user.home_dir = savestring (entry->pw_dir);
+       }
+      else
+       {
+         current_user.user_name = _("I have no name!");
+         current_user.user_name = savestring (current_user.user_name);
+         current_user.shell = savestring ("/bin/sh");
+         current_user.home_dir = savestring ("/");
+       }
+      endpwent ();
+    }
+}
+
+/* Do whatever is necessary to initialize the shell.
+   Put new initializations in here. */
+static void
+shell_initialize ()
+{
+  char hostname[256];
+  int should_be_restricted;
+
+  /* Line buffer output for stderr and stdout. */
+  if (shell_initialized == 0)
+    {
+      sh_setlinebuf (stderr);
+      sh_setlinebuf (stdout);
+    }
+
+  /* Sort the array of shell builtins so that the binary search in
+     find_shell_builtin () works correctly. */
+  initialize_shell_builtins ();
+
+  /* Initialize the trap signal handlers before installing our own
+     signal handlers.  traps.c:restore_original_signals () is responsible
+     for restoring the original default signal handlers.  That function
+     is called when we make a new child. */
+  initialize_traps ();
+  initialize_signals (0);
+
+  /* It's highly unlikely that this will change. */
+  if (current_host_name == 0)
+    {
+      /* Initialize current_host_name. */
+      if (gethostname (hostname, 255) < 0)
+       current_host_name = "??host??";
+      else
+       current_host_name = savestring (hostname);
+    }
+
+  /* Initialize the stuff in current_user that comes from the password
+     file.  We don't need to do this right away if the shell is not
+     interactive. */
+  if (interactive_shell)
+    get_current_user_info ();
+
+  /* Initialize our interface to the tilde expander. */
+  tilde_initialize ();
+
+#if defined (RESTRICTED_SHELL)
+  should_be_restricted = shell_is_restricted (shell_name);
+#endif
+
+  /* Initialize internal and environment variables.  Don't import shell
+     functions from the environment if we are running in privileged or
+     restricted mode or if the shell is running setuid. */
+#if defined (RESTRICTED_SHELL)
+  initialize_shell_variables (shell_environment, privileged_mode||restricted||should_be_restricted||running_setuid);
+#else
+  initialize_shell_variables (shell_environment, privileged_mode||running_setuid);
+#endif
+
+  /* Initialize the data structures for storing and running jobs. */
+  initialize_job_control (jobs_m_flag);
+
+  /* Initialize input streams to null. */
+  initialize_bash_input ();
+
+  initialize_flags ();
+
+  /* Initialize the shell options.  Don't import the shell options
+     from the environment variables $SHELLOPTS or $BASHOPTS if we are
+     running in privileged or restricted mode or if the shell is running
+     setuid. */
+#if defined (RESTRICTED_SHELL)
+  initialize_shell_options (privileged_mode||restricted||should_be_restricted||running_setuid);
+  initialize_bashopts (privileged_mode||restricted||should_be_restricted||running_setuid);
+#else
+  initialize_shell_options (privileged_mode||running_setuid);
+  initialize_bashopts (privileged_mode||running_setuid);
+#endif
+}
+
+/* Function called by main () when it appears that the shell has already
+   had some initialization performed.  This is supposed to reset the world
+   back to a pristine state, as if we had been exec'ed. */
+static void
+shell_reinitialize ()
+{
+  /* The default shell prompts. */
+  primary_prompt = PPROMPT;
+  secondary_prompt = SPROMPT;
+
+  /* Things that get 1. */
+  current_command_number = 1;
+
+  /* We have decided that the ~/.bashrc file should not be executed
+     for the invocation of each shell script.  If the variable $ENV
+     (or $BASH_ENV) is set, its value is used as the name of a file
+     to source. */
+  no_rc = no_profile = 1;
+
+  /* Things that get 0. */
+  login_shell = make_login_shell = interactive = executing = 0;
+  debugging = do_version = line_number = last_command_exit_value = 0;
+  forced_interactive = interactive_shell = 0;
+  subshell_environment = running_in_background = 0;
+  expand_aliases = 0;
+
+  /* XXX - should we set jobs_m_flag to 0 here? */
+
+#if defined (HISTORY)
+  bash_history_reinit (0);
+#endif /* HISTORY */
+
+#if defined (RESTRICTED_SHELL)
+  restricted = 0;
+#endif /* RESTRICTED_SHELL */
+
+  /* Ensure that the default startup file is used.  (Except that we don't
+     execute this file for reinitialized shells). */
+  bashrc_file = DEFAULT_BASHRC;
+
+  /* Delete all variables and functions.  They will be reinitialized when
+     the environment is parsed. */
+  delete_all_contexts (shell_variables);
+  delete_all_variables (shell_functions);
+
+  reinit_special_variables ();
+
+#if defined (READLINE)
+  bashline_reinitialize ();
+#endif
+
+  shell_reinitialized = 1;
+}
+
+static void
+show_shell_usage (fp, extra)
+     FILE *fp;
+     int extra;
+{
+  int i;
+  char *set_opts, *s, *t;
+
+  if (extra)
+    fprintf (fp, _("GNU bash, version %s-(%s)\n"), shell_version_string (), MACHTYPE);
+  fprintf (fp, _("Usage:\t%s [GNU long option] [option] ...\n\t%s [GNU long option] [option] script-file ...\n"),
+            shell_name, shell_name);
+  fputs (_("GNU long options:\n"), fp);
+  for (i = 0; long_args[i].name; i++)
+    fprintf (fp, "\t--%s\n", long_args[i].name);
+
+  fputs (_("Shell options:\n"), fp);
+  fputs (_("\t-ilrsD or -c command or -O shopt_option\t\t(invocation only)\n"), fp);
+
+  for (i = 0, set_opts = 0; shell_builtins[i].name; i++)
+    if (STREQ (shell_builtins[i].name, "set"))
+      set_opts = savestring (shell_builtins[i].short_doc);
+  if (set_opts)
+    {
+      s = strchr (set_opts, '[');
+      if (s == 0)
+       s = set_opts;
+      while (*++s == '-')
+       ;
+      t = strchr (s, ']');
+      if (t)
+       *t = '\0';
+      fprintf (fp, _("\t-%s or -o option\n"), s);
+      free (set_opts);
+    }
+
+  if (extra)
+    {
+      fprintf (fp, _("Type `%s -c \"help set\"' for more information about shell options.\n"), shell_name);
+      fprintf (fp, _("Type `%s -c help' for more information about shell builtin commands.\n"), shell_name);
+      fprintf (fp, _("Use the `bashbug' command to report bugs.\n"));
+    }
+}
+
+static void
+add_shopt_to_alist (opt, on_or_off)
+     char *opt;
+     int on_or_off;
+{
+  if (shopt_ind >= shopt_len)
+    {
+      shopt_len += 8;
+      shopt_alist = (STRING_INT_ALIST *)xrealloc (shopt_alist, shopt_len * sizeof (shopt_alist[0]));
+    }
+  shopt_alist[shopt_ind].word = opt;
+  shopt_alist[shopt_ind].token = on_or_off;
+  shopt_ind++;
+}
+
+static void
+run_shopt_alist ()
+{
+  register int i;
+
+  for (i = 0; i < shopt_ind; i++)
+    if (shopt_setopt (shopt_alist[i].word, (shopt_alist[i].token == '-')) != EXECUTION_SUCCESS)
+      exit (EX_BADUSAGE);
+  free (shopt_alist);
+  shopt_alist = 0;
+  shopt_ind = shopt_len = 0;
+}
diff --git a/subst.c b/subst.c
index ec5766be550d9b66330c10369d3413459f0a863b..e2b57aa29debc80134b13fa4562024442f4714d1 100644 (file)
--- a/subst.c
+++ b/subst.c
@@ -52,6 +52,9 @@
 #include "mailcheck.h"
 
 #include "shmbutil.h"
+#if defined (HAVE_MBSTR_H) && defined (HAVE_MBSCHR)
+#  include <mbstr.h>           /* mbschr */
+#endif
 #include "typemax.h"
 
 #include "builtins/getopt.h"
diff --git a/test.c b/test.c
index ab7bec79f84a73eff0eb0032101e2f978a14074f..1b3d1995a9cb3f400b85d5a17d60bf1a0f0b77f6 100644 (file)
--- a/test.c
+++ b/test.c
@@ -225,6 +225,7 @@ and ()
  *     '-'('G'|'L'|'O'|'S'|'N') filename
  *     '-t' [int]
  *     '-'('z'|'n') string
+ *     '-'('v'|'R') varname
  *     '-o' option
  *     string
  *     string ('!='|'='|'==') string
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 0f9633d198ca233e9effd616a813fdc46cc0b371..e0f64d53fda38b3169d61c33bf1b90ac87fe8f89 100644 (file)
@@ -593,7 +593,7 @@ c Sub = 0 2 4 8
 <'ab cd'> 
 <'4'> <'ab cd'> 
 <> 
-argv[1] = <host(2)[4.3]$ >
+argv[1] = <host(2)[4.4]$ >
 <      
 >
 <' \t\n'>
index 54b1167146644b80bbe36d1ff35d5c0982ccb34c..2f07ebb92abab975ee64e919df1c3ff604ae6c54 100644 (file)
@@ -5409,14 +5409,14 @@ sv_shcompat (name)
       return;
     }
   /* Handle decimal-like compatibility version specifications: 4.2 */
-  if (isdigit (val[0]) && val[1] == '.' && isdigit (val[2]) && val[3] == 0)
+  if (ISDIGIT (val[0]) && val[1] == '.' && ISDIGIT (val[2]) && val[3] == 0)
     {
       tens = val[0] - '0';
       ones = val[2] - '0';
       compatval = tens*10 + ones;
     }
   /* Handle integer-like compatibility version specifications: 42 */
-  else if (isdigit (val[0]) && isdigit (val[1]) && val[2] == 0)
+  else if (ISDIGIT (val[0]) && ISDIGIT (val[1]) && val[2] == 0)
     {
       tens = val[0] - '0';
       ones = val[1] - '0';
diff --git a/variables.c~ b/variables.c~
new file mode 100644 (file)
index 0000000..54b1167
--- /dev/null
@@ -0,0 +1,5453 @@
+/* variables.c -- Functions for hacking shell variables. */
+
+/* Copyright (C) 1987-2015 Free Software Foundation, Inc.
+
+   This file is part of GNU Bash, the Bourne Again SHell.
+
+   Bash is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   Bash is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with Bash.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "config.h"
+
+#include "bashtypes.h"
+#include "posixstat.h"
+#include "posixtime.h"
+
+#if defined (__QNX__)
+#  if defined (__QNXNTO__)
+#    include <sys/netmgr.h>
+#  else
+#    include <sys/vc.h>
+#  endif /* !__QNXNTO__ */
+#endif /* __QNX__ */
+
+#if defined (HAVE_UNISTD_H)
+#  include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include "chartypes.h"
+#if defined (HAVE_PWD_H)
+#  include <pwd.h>
+#endif
+#include "bashansi.h"
+#include "bashintl.h"
+
+#define NEED_XTRACE_SET_DECL
+
+#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 "alias.h"
+#include "jobs.h"
+
+#include "version.h"
+
+#include "builtins/getopt.h"
+#include "builtins/common.h"
+#include "builtins/builtext.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')
+
+#define BASHFUNC_PREFIX                "BASH_FUNC_"
+#define BASHFUNC_PREFLEN       10      /* == strlen(BASHFUNC_PREFIX */
+#define BASHFUNC_SUFFIX                "%%"
+#define BASHFUNC_SUFFLEN       2       /* == strlen(BASHFUNC_SUFFIX) */
+
+/* flags for find_variable_internal */
+
+#define FV_FORCETEMPENV                0x01
+#define FV_SKIPINVISIBLE       0x02
+
+extern char **environ;
+
+/* Variables used here and defined in other files. */
+extern int posixly_correct;
+extern int line_number, line_number_base;
+extern int subshell_environment, indirection_level, subshell_level;
+extern int build_version, patch_level;
+extern int expanding_redir;
+extern int last_command_exit_value;
+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;
+extern int assigning_in_environment;
+extern int executing_builtin;
+extern int funcnest_max;
+
+#if defined (READLINE)
+extern int no_line_editing;
+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;
+
+/* 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;
+
+/* 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;
+
+#if defined (READLINE)
+static int winsize_assignment;         /* currently assigning to LINES or COLUMNS */
+#endif
+
+static HASH_TABLE *last_table_searched;        /* hash_lookup sets this */
+
+/* Some forward declarations. */
+static void create_variable_tables __P((void));
+
+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, char *));
+#if defined (ARRAY_VARS)
+static SHELL_VAR *null_array_assign __P((SHELL_VAR *, char *, arrayind_t, char *));
+#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));
+static SHELL_VAR *init_dynamic_assoc_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, char *));
+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 void seedrand __P((void));                      /* seed generator randomly */
+static SHELL_VAR *assign_random __P((SHELL_VAR *, char *, arrayind_t, char *));
+static SHELL_VAR *get_random __P((SHELL_VAR *));
+
+static SHELL_VAR *assign_lineno __P((SHELL_VAR *, char *, arrayind_t, char *));
+static SHELL_VAR *get_lineno __P((SHELL_VAR *));
+
+static SHELL_VAR *assign_subshell __P((SHELL_VAR *, char *, arrayind_t, char *));
+static SHELL_VAR *get_subshell __P((SHELL_VAR *));
+
+static SHELL_VAR *get_bashpid __P((SHELL_VAR *));
+
+#if defined (HISTORY)
+static SHELL_VAR *get_histcmd __P((SHELL_VAR *));
+#endif
+
+#if defined (READLINE)
+static SHELL_VAR *get_comp_wordbreaks __P((SHELL_VAR *));
+static SHELL_VAR *assign_comp_wordbreaks __P((SHELL_VAR *, char *, arrayind_t, char *));
+#endif
+
+#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS)
+static SHELL_VAR *assign_dirstack __P((SHELL_VAR *, char *, arrayind_t, char *));
+static SHELL_VAR *get_dirstack __P((SHELL_VAR *));
+#endif
+
+#if defined (ARRAY_VARS)
+static SHELL_VAR *get_groupset __P((SHELL_VAR *));
+
+static SHELL_VAR *build_hashcmd __P((SHELL_VAR *));
+static SHELL_VAR *get_hashcmd __P((SHELL_VAR *));
+static SHELL_VAR *assign_hashcmd __P((SHELL_VAR *,  char *, arrayind_t, char *));
+#  if defined (ALIAS)
+static SHELL_VAR *build_aliasvar __P((SHELL_VAR *));
+static SHELL_VAR *get_aliasvar __P((SHELL_VAR *));
+static SHELL_VAR *assign_aliasvar __P((SHELL_VAR *,  char *, arrayind_t, char *));
+#  endif
+#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, int));
+
+static void dispose_variable_value __P((SHELL_VAR *));
+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 export_environment_candidate __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 *find_nameref_at_context __P((SHELL_VAR *, VAR_CONTEXT *));
+static SHELL_VAR *find_variable_nameref_context __P((SHELL_VAR *, VAR_CONTEXT *, VAR_CONTEXT **));
+static SHELL_VAR *find_variable_last_nameref_context __P((SHELL_VAR *, VAR_CONTEXT *, VAR_CONTEXT **));
+
+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 *, int));
+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 *));
+
+static void
+create_variable_tables ()
+{
+  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
+}
+
+/* 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, ro;
+  SHELL_VAR *temp_var;
+
+  create_variable_tables ();
+
+  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) */
+
+      temp_var = (SHELL_VAR *)NULL;
+
+#if defined (FUNCTION_IMPORT)
+      /* 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 (BASHFUNC_PREFIX, name, BASHFUNC_PREFLEN) &&
+          STREQ (BASHFUNC_SUFFIX, name + char_index - BASHFUNC_SUFFLEN) &&
+         STREQN ("() {", string, 4))
+       {
+         size_t namelen;
+         char *tname;          /* desired imported function name */
+
+         namelen = char_index - BASHFUNC_PREFLEN - BASHFUNC_SUFFLEN;
+
+         tname = name + BASHFUNC_PREFLEN;      /* start of func name */
+         tname[namelen] = '\0';                /* now tname == func name */
+
+         string_length = strlen (string);
+         temp_string = (char *)xmalloc (namelen + string_length + 2);
+
+         memcpy (temp_string, tname, namelen);
+         temp_string[namelen] = ' ';
+         memcpy (temp_string + namelen + 1, string, string_length + 1);
+
+         /* Don't import function names that are invalid identifiers from the
+            environment in posix mode, though we still allow them to be defined as
+            shell variables. */
+         if (absolute_program (tname) == 0 && (posixly_correct == 0 || legal_identifier (tname)))
+           parse_and_execute (temp_string, tname, SEVAL_NONINT|SEVAL_NOHIST|SEVAL_FUNCDEF|SEVAL_ONECMD);
+         else
+           free (temp_string);         /* parse_and_execute does this */
+
+         if (temp_var = find_function (tname))
+           {
+             VSETATTR (temp_var, (att_exported|att_imported));
+             array_needs_making = 1;
+           }
+         else
+           {
+             if (temp_var = bind_variable (name, string, 0))
+               {
+                 VSETATTR (temp_var, (att_exported | att_imported | att_invisible));
+                 array_needs_making = 1;
+               }
+             last_command_exit_value = 1;
+             report_error (_("error importing function definition for `%s'"), tname);
+           }
+
+         /* Restore original suffix */
+         tname[namelen] = BASHFUNC_SUFFIX[0];
+       }
+#endif /* FUNCTION_IMPORT */
+#if defined (ARRAY_VARS)
+#  if ARRAY_EXPORT
+      /* Array variables may not yet be exported. */
+      else if (*string == '(' && string[1] == '[' && string[strlen (string) - 1] == ')')
+       {
+         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 /* ARRAY_EXPORT */
+#endif
+      else
+       {
+         ro = 0;
+         if (posixly_correct && STREQ (name, "SHELLOPTS"))
+           {
+             temp_var = find_variable ("SHELLOPTS");
+             ro = temp_var && readonly_p (temp_var);
+             if (temp_var)
+               VUNSETATTR (temp_var, att_readonly);
+           }
+         temp_var = bind_variable (name, string, 0);
+         if (temp_var)
+           {
+             if (legal_identifier (name))
+               VSETATTR (temp_var, (att_exported | att_imported));
+             else
+               VSETATTR (temp_var, (att_exported | att_imported | att_invisible));
+             if (ro)
+               VSETATTR (temp_var, att_readonly);
+             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 = set_if_not ("_", 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];
+#  if defined (__QNXNTO__)
+    netmgr_ndtostr(ND2S_LOCAL_STR, ND_LOCAL_NODE, node_name, sizeof(node_name));
+#  else
+    qnx_nidtostr (getnid (), node_name, sizeof (node_name));
+#  endif
+    temp_var = bind_variable ("NODE", node_name, 0);
+    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", 0);
+  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)
+    {
+      temp_var = set_if_not ("MAILCHECK", posixly_correct ? "600" : "60");
+      VSETATTR (temp_var, att_integer);
+    }
+
+  /* Do some things with shell level. */
+  initialize_shell_level ();
+
+  set_ppid ();
+
+  /* Initialize the `getopts' stuff. */
+  temp_var = bind_variable ("OPTIND", "1", 0);
+  VSETATTR (temp_var, att_integer);
+  getopts_reset (0);
+  bind_variable ("OPTERR", "1", 0);
+  sh_opterr = 1;
+
+  if (login_shell == 1 && posixly_correct == 0)
+    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, 0);
+  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 (), 0);
+#if defined (ARRAY_VARS)
+  make_vers_array ();
+#endif
+
+  if (command_execution_string)
+    bind_variable ("BASH_EXECUTION_STRING", command_execution_string, 0);
+
+  /* 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);
+    }
+#endif /* HISTORY */
+
+  /* Seed the random number generator. */
+  seedrand ();
+
+  /* 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");
+      sv_histtimefmt ("HISTTIMEFORMAT");
+    }
+#endif /* HISTORY */
+
+#if defined (READLINE) && defined (STRICT_POSIX)
+  /* POSIXLY_CORRECT will only be 1 here if the shell was compiled
+     -DSTRICT_POSIX */
+  if (interactive_shell && posixly_correct && no_line_editing == 0)
+    rl_prefer_env_winsize = 1;
+#endif /* READLINE && STRICT_POSIX */
+
+     /*
+      * 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 ();
+
+  temp_var = find_variable ("BASH_XTRACEFD");
+  if (temp_var && imported_p (temp_var))
+    sv_xtracefd (temp_var->name);
+
+  /* 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 (), 0);
+#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, 0);
+    }
+#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, 0);
+  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, 0);
+      set_auto_export (temp_var);
+    }
+  else
+    {
+      temp_string = get_working_directory ("shell-init");
+      if (temp_string)
+       {
+         temp_var = bind_variable ("PWD", temp_string, 0);
+         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, 0);
+  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, 0);
+  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, 0);
+      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, 0);
+      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 = strchr (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;
+
+#if defined (READLINE)
+  /* If we are currently assigning to LINES or COLUMNS, don't do anything. */
+  if (winsize_assignment)
+    return;
+#endif
+
+  v = inttostr (lines, val, sizeof (val));
+  bind_variable ("LINES", v, 0);
+
+  v = inttostr (cols, val, sizeof (val));
+  bind_variable ("COLUMNS", v, 0);
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*                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);
+  else if (assoc_p (var))
+    print_assoc_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;
+{
+  char *x;
+
+  if (function_p (var) && var_isset (var))
+    {
+      x = named_function_string ((char *)NULL, function_cell(var), FUNC_MULTILINE|FUNC_EXTERNAL);
+      printf ("%s", x);
+    }
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*                     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), 0); \
+      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)
+
+#define INIT_DYNAMIC_ASSOC_VAR(var, gfunc, afunc) \
+  do \
+    { \
+      v = make_new_assoc_variable (var); \
+      v->dynamic_value = gfunc; \
+      v->assign_func = afunc; \
+    } \
+  while (0)
+
+static SHELL_VAR *
+null_assign (self, value, unused, key)
+     SHELL_VAR *self;
+     char *value;
+     arrayind_t unused;
+     char *key;
+{
+  return (self);
+}
+
+#if defined (ARRAY_VARS)
+static SHELL_VAR *
+null_array_assign (self, value, ind, key)
+     SHELL_VAR *self;
+     char *value;
+     arrayind_t ind;
+     char *key;
+{
+  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.  Initialize 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;
+}
+
+static SHELL_VAR *
+init_dynamic_assoc_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_ASSOC_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, key)
+     SHELL_VAR *self;
+     char *value;
+     arrayind_t unused;
+     char *key;
+{
+  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;
+static int seeded_subshell = 0;
+
+/* 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 ()
+{
+  /* From "Random number generators: good ones are hard to find",
+     Park and Miller, Communications of the ACM, vol. 31, no. 10,
+     October 1988, p. 1195. filtered through FreeBSD */
+  long h, l;
+
+  /* Can't seed with 0. */
+  if (rseed == 0)
+    rseed = 123459876;
+  h = rseed / 127773;
+  l = rseed % 127773;
+  rseed = 16807 * l - 2836 * h;
+#if 0
+  if (rseed < 0)
+    rseed += 0x7fffffff;
+#endif
+  return ((unsigned int)(rseed & 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 void
+seedrand ()
+{
+  struct timeval tv;
+
+  gettimeofday (&tv, NULL);
+  sbrand (tv.tv_sec ^ tv.tv_usec ^ getpid ());
+}
+
+static SHELL_VAR *
+assign_random (self, value, unused, key)
+     SHELL_VAR *self;
+     char *value;
+     arrayind_t unused;
+     char *key;
+{
+  sbrand (strtoul (value, (char **)NULL, 10));
+  if (subshell_environment)
+    seeded_subshell = getpid ();
+  return (self);
+}
+
+int
+get_random_number ()
+{
+  int rv, pid;
+
+  /* Reset for command and process substitution. */
+  pid = getpid ();
+  if (subshell_environment && seeded_subshell != pid)
+    {
+      seedrand ();
+      seeded_subshell = pid;
+    }
+
+  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, key)
+     SHELL_VAR *var;
+     char *value;
+     arrayind_t unused;
+     char *key;
+{
+  intmax_t new_value;
+
+  if (value == 0 || *value == '\0' || legal_number (value, &new_value) == 0)
+    new_value = 0;
+  line_number = line_number_base = 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, key)
+     SHELL_VAR *var;
+     char *value;
+     arrayind_t unused;
+     char *key;
+{
+  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_bashpid (var)
+     SHELL_VAR *var;
+{
+  int pid;
+  char *p;
+
+  pid = getpid ();
+  p = itos (pid);
+
+  FREE (value_cell (var));
+  VSETATTR (var, att_integer|att_readonly);
+  var_setvalue (var, p);
+  return (var);
+}
+
+static SHELL_VAR *
+get_bash_command (var)
+     SHELL_VAR *var;
+{
+  char *p;
+
+  if (the_printed_command_except_trap)
+    p = savestring (the_printed_command_except_trap);
+  else
+    {
+      p = (char *)xmalloc (1);
+      p[0] = '\0';
+    }
+  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;
+{
+  /* 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);
+
+  FREE (value_cell (var));
+  var_setvalue (var, savestring (rl_completer_word_break_characters));
+
+  return (var);
+}
+
+/* When this function returns, rl_completer_word_break_characters points to
+   malloced memory. */
+static SHELL_VAR *
+assign_comp_wordbreaks (self, value, unused, key)
+     SHELL_VAR *self;
+     char *value;
+     arrayind_t unused;
+     char *key;
+{
+  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, key)
+     SHELL_VAR *self;
+     char *value;
+     arrayind_t ind;
+     char *key;
+{
+  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 (0);
+  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);
+}
+
+static SHELL_VAR *
+build_hashcmd (self)
+     SHELL_VAR *self;
+{
+  HASH_TABLE *h;
+  int i;
+  char *k, *v;
+  BUCKET_CONTENTS *item;
+
+  h = assoc_cell (self);
+  if (h)
+    assoc_dispose (h);
+
+  if (hashed_filenames == 0 || HASH_ENTRIES (hashed_filenames) == 0)
+    {
+      var_setvalue (self, (char *)NULL);
+      return self;
+    }
+
+  h = assoc_create (hashed_filenames->nbuckets);
+  for (i = 0; i < hashed_filenames->nbuckets; i++)
+    {
+      for (item = hash_items (i, hashed_filenames); item; item = item->next)
+       {
+         k = savestring (item->key);
+         v = pathdata(item)->path;
+         assoc_insert (h, k, v);
+       }
+    }
+
+  var_setvalue (self, (char *)h);
+  return self;
+}
+
+static SHELL_VAR *
+get_hashcmd (self)
+     SHELL_VAR *self;
+{
+  build_hashcmd (self);
+  return (self);
+}
+
+static SHELL_VAR *
+assign_hashcmd (self, value, ind, key)
+     SHELL_VAR *self;
+     char *value;
+     arrayind_t ind;
+     char *key;
+{
+  phash_insert (key, value, 0, 0);
+  return (build_hashcmd (self));
+}
+
+#if defined (ALIAS)
+static SHELL_VAR *
+build_aliasvar (self)
+     SHELL_VAR *self;
+{
+  HASH_TABLE *h;
+  int i;
+  char *k, *v;
+  BUCKET_CONTENTS *item;
+
+  h = assoc_cell (self);
+  if (h)
+    assoc_dispose (h);
+
+  if (aliases == 0 || HASH_ENTRIES (aliases) == 0)
+    {
+      var_setvalue (self, (char *)NULL);
+      return self;
+    }
+
+  h = assoc_create (aliases->nbuckets);
+  for (i = 0; i < aliases->nbuckets; i++)
+    {
+      for (item = hash_items (i, aliases); item; item = item->next)
+       {
+         k = savestring (item->key);
+         v = ((alias_t *)(item->data))->value;
+         assoc_insert (h, k, v);
+       }
+    }
+
+  var_setvalue (self, (char *)h);
+  return self;
+}
+
+static SHELL_VAR *
+get_aliasvar (self)
+     SHELL_VAR *self;
+{
+  build_aliasvar (self);
+  return (self);
+}
+
+static SHELL_VAR *
+assign_aliasvar (self, value, ind, key)
+     SHELL_VAR *self;
+     char *value;
+     arrayind_t ind;
+     char *key;
+{
+  add_alias (key, value);
+  return (build_aliasvar (self));
+}
+#endif /* ALIAS */
+
+#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);
+  VSETATTR (v, att_integer);
+  INIT_DYNAMIC_VAR ("LINENO", (char *)NULL, get_lineno, assign_lineno);
+  VSETATTR (v, att_integer);
+
+  INIT_DYNAMIC_VAR ("BASHPID", (char *)NULL, get_bashpid, null_assign);
+  VSETATTR (v, att_integer|att_readonly);
+
+#if defined (HISTORY)
+  INIT_DYNAMIC_VAR ("HISTCMD", (char *)NULL, get_histcmd, (sh_var_assign_func_t *)NULL);
+  VSETATTR (v, att_integer);
+#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_noassign|att_nounset);
+  v = init_dynamic_array_var ("BASH_ARGV", get_self, null_array_assign, att_noassign|att_nounset);
+#  endif /* DEBUGGER */
+  v = init_dynamic_array_var ("BASH_SOURCE", get_self, null_array_assign, att_noassign|att_nounset);
+  v = init_dynamic_array_var ("BASH_LINENO", get_self, null_array_assign, att_noassign|att_nounset);
+
+  v = init_dynamic_assoc_var ("BASH_CMDS", get_hashcmd, assign_hashcmd, att_nofree);
+#  if defined (ALIAS)
+  v = init_dynamic_assoc_var ("BASH_ALIASES", get_aliasvar, assign_aliasvar, att_nofree);
+#  endif
+#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);
+  /* If we find the name in HASHED_VARS, set LAST_TABLE_SEARCHED to that
+     table. */
+  if (bucket)
+    last_table_searched = hashed_vars;
+  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, flags)
+     const char *name;
+     int flags;
+{
+  SHELL_VAR *var;
+  int search_tempenv, force_tempenv;
+  VAR_CONTEXT *vc;
+
+  var = (SHELL_VAR *)NULL;
+
+  force_tempenv = (flags & FV_FORCETEMPENV);
+
+  /* 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)
+    {
+      if ((flags & FV_SKIPINVISIBLE) == 0)
+       var = var_lookup (name, shell_variables);
+      else
+       {
+         /* essentially var_lookup expanded inline so we can check for
+            att_invisible */
+         for (vc = shell_variables; vc; vc = vc->down)
+           {
+             var = hash_lookup (name, vc->table);
+             if (var && invisible_p (var))
+               var = 0;
+             if (var)
+               break;
+           }
+       }
+    }
+
+  if (var == 0)
+    return ((SHELL_VAR *)NULL);
+
+  return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var);
+}
+
+/* Look up and resolve the chain of nameref variables starting at V all the
+   way to NULL or non-nameref. */
+SHELL_VAR *
+find_variable_nameref (v)
+     SHELL_VAR *v;
+{
+  int level, flags;
+  char *newname;
+  SHELL_VAR *orig, *oldv;
+
+  level = 0;
+  orig = v;
+  while (v && nameref_p (v))
+    {
+      level++;
+      if (level > NAMEREF_MAX)
+       return ((SHELL_VAR *)0);        /* error message here? */
+      newname = nameref_cell (v);
+      if (newname == 0 || *newname == '\0')
+       return ((SHELL_VAR *)0);
+      oldv = v;
+      flags = 0;
+      if (expanding_redir == 0 && (assigning_in_environment || executing_builtin))
+       flags |= FV_FORCETEMPENV;
+      v = find_variable_internal (newname, flags);
+      if (v == orig || v == oldv)
+       {
+         internal_warning (_("%s: circular name reference"), orig->name);
+         return ((SHELL_VAR *)0);
+       }
+    }
+  return v;
+}
+
+/* Resolve the chain of nameref variables for NAME.  XXX - could change later */
+SHELL_VAR *
+find_variable_last_nameref (name)
+     const char *name;
+{
+  SHELL_VAR *v, *nv;
+  char *newname;
+  int level, flags;
+
+  nv = v = find_variable_noref (name);
+  level = 0;
+  while (v && nameref_p (v))
+    {
+      level++;
+      if (level > NAMEREF_MAX)
+        return ((SHELL_VAR *)0);       /* error message here? */
+      newname = nameref_cell (v);
+      if (newname == 0 || *newname == '\0')
+       return ((SHELL_VAR *)0);
+      nv = v;
+      flags = 0;
+      if (expanding_redir == 0 && (assigning_in_environment || executing_builtin))
+       flags |= FV_FORCETEMPENV;
+      v = find_variable_internal (newname, flags);
+    }
+  return nv;
+}
+
+/* Resolve the chain of nameref variables for NAME.  XXX - could change later */
+SHELL_VAR *
+find_global_variable_last_nameref (name)
+     const char *name;
+{
+  SHELL_VAR *v, *nv;
+  char *newname;
+  int level;
+
+  nv = v = find_global_variable_noref (name);
+  level = 0;
+  while (v && nameref_p (v))
+    {
+      level++;
+      if (level > NAMEREF_MAX)
+        return ((SHELL_VAR *)0);       /* error message here? */
+      newname = nameref_cell (v);
+      if (newname == 0 || *newname == '\0')
+       return ((SHELL_VAR *)0);
+      nv = v;
+      v = find_global_variable_noref (newname);
+    }
+  return nv;
+}
+
+static SHELL_VAR *
+find_nameref_at_context (v, vc)
+     SHELL_VAR *v;
+     VAR_CONTEXT *vc;
+{
+  SHELL_VAR *nv, *nv2;
+  VAR_CONTEXT *nvc;
+  char *newname;
+  int level;
+
+  nv = v;
+  level = 1;
+  while (nv && nameref_p (nv))
+    {
+      level++;
+      if (level > NAMEREF_MAX)
+        return ((SHELL_VAR *)NULL);
+      newname = nameref_cell (nv);
+      if (newname == 0 || *newname == '\0')
+        return ((SHELL_VAR *)NULL);      
+      nv2 = hash_lookup (newname, vc->table);
+      if (nv2 == 0)
+        break;
+      nv = nv2;
+    }
+  return nv;
+}
+
+/* Do nameref resolution from the VC, which is the local context for some
+   function or builtin, `up' the chain to the global variables context.  If
+   NVCP is not NULL, return the variable context where we finally ended the
+   nameref resolution (so the bind_variable_internal can use the correct
+   variable context and hash table). */
+static SHELL_VAR *
+find_variable_nameref_context (v, vc, nvcp)
+     SHELL_VAR *v;
+     VAR_CONTEXT *vc;
+     VAR_CONTEXT **nvcp;
+{
+  SHELL_VAR *nv, *nv2;
+  VAR_CONTEXT *nvc;
+
+  /* Look starting at the current context all the way `up' */
+  for (nv = v, nvc = vc; nvc; nvc = nvc->down)
+    {
+      nv2 = find_nameref_at_context (nv, nvc);
+      if (nv2 == 0)
+        continue;
+      nv = nv2;
+      if (*nvcp)
+        *nvcp = nvc;
+      if (nameref_p (nv) == 0)
+        break;
+    }
+  return (nameref_p (nv) ? (SHELL_VAR *)NULL : nv);
+}
+
+/* Do nameref resolution from the VC, which is the local context for some
+   function or builtin, `up' the chain to the global variables context.  If
+   NVCP is not NULL, return the variable context where we finally ended the
+   nameref resolution (so the bind_variable_internal can use the correct
+   variable context and hash table). */
+static SHELL_VAR *
+find_variable_last_nameref_context (v, vc, nvcp)
+     SHELL_VAR *v;
+     VAR_CONTEXT *vc;
+     VAR_CONTEXT **nvcp;
+{
+  SHELL_VAR *nv, *nv2;
+  VAR_CONTEXT *nvc;
+
+  /* Look starting at the current context all the way `up' */
+  for (nv = v, nvc = vc; nvc; nvc = nvc->down)
+    {
+      nv2 = find_nameref_at_context (nv, nvc);
+      if (nv2 == 0)
+       continue;
+      nv = nv2;
+      if (*nvcp)
+        *nvcp = nvc;
+    }
+  return (nameref_p (nv) ? nv : (SHELL_VAR *)NULL);
+}
+
+/* Find a variable, forcing a search of the temporary environment first */
+SHELL_VAR *
+find_variable_tempenv (name)
+     const char *name;
+{
+  SHELL_VAR *var;
+
+  var = find_variable_internal (name, FV_FORCETEMPENV);
+  if (var && nameref_p (var))
+    var = find_variable_nameref (var);
+  return (var);
+}
+
+/* Find a variable, not forcing a search of the temporary environment first */
+SHELL_VAR *
+find_variable_notempenv (name)
+     const char *name;
+{
+  SHELL_VAR *var;
+
+  var = find_variable_internal (name, 0);
+  if (var && nameref_p (var))
+    var = find_variable_nameref (var);
+  return (var);
+}
+
+SHELL_VAR *
+find_global_variable (name)
+     const char *name;
+{
+  SHELL_VAR *var;
+
+  var = var_lookup (name, global_variables);
+  if (var && nameref_p (var))
+    var = find_variable_nameref (var);
+
+  if (var == 0)
+    return ((SHELL_VAR *)NULL);
+
+  return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var);
+}
+
+SHELL_VAR *
+find_global_variable_noref (name)
+     const char *name;
+{
+  SHELL_VAR *var;
+
+  var = var_lookup (name, global_variables);
+
+  if (var == 0)
+    return ((SHELL_VAR *)NULL);
+
+  return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var);
+}
+
+SHELL_VAR *
+find_shell_variable (name)
+     const char *name;
+{
+  SHELL_VAR *var;
+
+  var = var_lookup (name, shell_variables);
+  if (var && nameref_p (var))
+    var = find_variable_nameref (var);
+
+  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;
+{
+  SHELL_VAR *v;
+  int flags;
+
+  last_table_searched = 0;
+  flags = 0;
+  if (expanding_redir == 0 && (assigning_in_environment || executing_builtin))
+    flags |= FV_FORCETEMPENV;
+  v = find_variable_internal (name, flags);
+  if (v && nameref_p (v))
+    v = find_variable_nameref (v);
+  return v;
+}
+
+/* Find the first instance of NAME in the variable context chain; return first
+   one found without att_invisible set; return 0 if no non-invisible instances
+   found. */
+SHELL_VAR *
+find_variable_no_invisible (name)
+     const char *name;
+{
+  SHELL_VAR *v;
+  int flags;
+
+  last_table_searched = 0;
+  flags = FV_SKIPINVISIBLE;
+  if (expanding_redir == 0 && (assigning_in_environment || executing_builtin))
+    flags |= FV_FORCETEMPENV;
+  v = find_variable_internal (name, flags);
+  if (v && nameref_p (v))
+    v = find_variable_nameref (v);
+  return v;
+}
+
+/* Find the first instance of NAME in the variable context chain; return first
+   one found even if att_invisible set. */
+SHELL_VAR *
+find_variable_for_assignment (name)
+     const char *name;
+{
+  SHELL_VAR *v;
+  int flags;
+
+  last_table_searched = 0;
+  flags = 0;
+  if (expanding_redir == 0 && (assigning_in_environment || executing_builtin))
+    flags |= FV_FORCETEMPENV;
+  v = find_variable_internal (name, flags);
+  if (v && nameref_p (v))
+    v = find_variable_nameref (v);
+  return v;
+}
+
+SHELL_VAR *
+find_variable_noref (name)
+     const char *name;
+{
+  SHELL_VAR *v;
+  int flags;
+
+  flags = 0;
+  if (expanding_redir == 0 && (assigning_in_environment || executing_builtin))
+    flags |= FV_FORCETEMPENV;
+  v = find_variable_internal (name, flags);
+  return v;
+}
+
+/* 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;
+{
+#if defined (DEBUGGER)
+  return ((FUNCTION_DEF *)hash_lookup (name, shell_function_defs));
+#else
+  return ((FUNCTION_DEF *)0);
+#endif
+}
+
+/* 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));
+  else if (assoc_p (var))
+    return (assoc_reference (assoc_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;
+
+  if (shell_variables == 0)
+    create_variable_tables ();
+
+  v = find_variable (name);
+  if (v == 0)
+    v = bind_variable_internal (name, value, global_variables->table, HASH_NOSRCH, 0);
+  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 we're making a local variable in a shell function, the temporary env
+     has already been merged into the function's variable context stack.  We
+     can assume that a temporary var in the same context appears in the same
+     VAR_CONTEXT and can safely be returned without creating a new variable
+     (which results in duplicate names in the same VAR_CONTEXT->table */
+  /* We can't just test tmpvar_p because variables in the temporary env given
+     to a shell function appear in the function's local variable VAR_CONTEXT
+     but retain their tempvar attribute.  We want temporary variables that are
+     found in temporary_env, hence the test for last_table_searched, which is
+     set in hash_lookup and only (so far) checked here. */
+  if (was_tmpvar && old_var->context == variable_context && last_table_searched != temporary_env)
+    {
+      VUNSETATTR (old_var, att_invisible);     /* XXX */
+      return (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);
+      else if (noassign_p (old_var))
+       builtin_error (_("%s: variable may not be assigned value"), name);
+#if 0
+      /* Let noassign variables through with a warning */
+      if (readonly_p (old_var))
+#endif
+       return ((SHELL_VAR *)NULL);
+    }
+
+  if (old_var == 0)
+    new_var = make_new_variable (name, vc->table);
+  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'. XXX - see above for temporary env
+        variables with the same context level as variable_context */
+      /* XXX - we should only do this if the variable is not an array. */
+      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);
+
+  if (was_tmpvar == 0 && no_invisible_vars == 0)
+    VSETATTR (new_var, att_invisible); /* XXX */
+  return (new_var);
+}
+
+/* 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 responsibility 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)
+    create_variable_tables ();
+
+  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;
+}
+
+SHELL_VAR *
+make_local_array_variable (name, assoc_ok)
+     char *name;
+     int assoc_ok;
+{
+  SHELL_VAR *var;
+  ARRAY *array;
+
+  var = make_local_variable (name);
+  if (var == 0 || array_p (var) || (assoc_ok && assoc_p (var)))
+    return var;
+
+  array = array_create ();
+
+  dispose_variable_value (var);
+  var_setarray (var, array);
+  VSETATTR (var, att_array);
+  return var;
+}
+
+SHELL_VAR *
+make_new_assoc_variable (name)
+     char *name;
+{
+  SHELL_VAR *entry;
+  HASH_TABLE *hash;
+
+  entry = make_new_variable (name, global_variables->table);
+  hash = assoc_create (0);
+
+  var_setassoc (entry, hash);
+  VSETATTR (entry, att_assoc);
+  return entry;
+}
+
+SHELL_VAR *
+make_local_assoc_variable (name)
+     char *name;
+{
+  SHELL_VAR *var;
+  HASH_TABLE *hash;
+
+  var = make_local_variable (name);
+  if (var == 0 || assoc_p (var))
+    return var;
+
+  dispose_variable_value (var);
+  hash = assoc_create (0);
+
+  var_setassoc (var, hash);
+  VSETATTR (var, att_assoc);
+  return var;
+}
+#endif
+
+char *
+make_variable_value (var, value, flags)
+     SHELL_VAR *var;
+     char *value;
+     int flags;
+{
+  char *retval, *oval;
+  intmax_t lval, rval;
+  int expok, olen, op;
+
+  /* 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))
+    {
+      if (flags & ASS_APPEND)
+       {
+         oval = value_cell (var);
+         lval = evalexp (oval, &expok);        /* ksh93 seems to do this */
+         if (expok == 0)
+           {
+             top_level_cleanup ();
+             jump_to_top_level (DISCARD);
+           }
+       }
+      rval = evalexp (value, &expok);
+      if (expok == 0)
+       {
+         top_level_cleanup ();
+         jump_to_top_level (DISCARD);
+       }
+      /* This can be fooled if the variable's value changes while evaluating
+        `rval'.  We can change it if we move the evaluation of lval to here. */
+      if (flags & ASS_APPEND)
+       rval += lval;
+      retval = itos (rval);
+    }
+#if defined (CASEMOD_ATTRS)
+  else if (capcase_p (var) || uppercase_p (var) || lowercase_p (var))
+    {
+      if (flags & ASS_APPEND)
+       {
+         oval = get_variable_value (var);
+         if (oval == 0)        /* paranoia */
+           oval = "";
+         olen = STRLEN (oval);
+         retval = (char *)xmalloc (olen + (value ? STRLEN (value) : 0) + 1);
+         strcpy (retval, oval);
+         if (value)
+           strcpy (retval+olen, value);
+       }
+      else if (*value)
+       retval = savestring (value);
+      else
+       {
+         retval = (char *)xmalloc (1);
+         retval[0] = '\0';
+       }
+      op = capcase_p (var) ? CASE_CAPITALIZE
+                        : (uppercase_p (var) ? CASE_UPPER : CASE_LOWER);
+      oval = sh_modcase (retval, (char *)0, op);
+      free (retval);
+      retval = oval;
+    }
+#endif /* CASEMOD_ATTRS */
+  else if (value)
+    {
+      if (flags & ASS_APPEND)
+       {
+         oval = get_variable_value (var);
+         if (oval == 0)        /* paranoia */
+           oval = "";
+         olen = STRLEN (oval);
+         retval = (char *)xmalloc (olen + (value ? STRLEN (value) : 0) + 1);
+         strcpy (retval, oval);
+         if (value)
+           strcpy (retval+olen, value);
+       }
+      else 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, aflags)
+     const char *name;
+     char *value;
+     HASH_TABLE *table;
+     int hflags, aflags;
+{
+  char *newval;
+  SHELL_VAR *entry;
+
+  entry = (hflags & HASH_NOSRCH) ? (SHELL_VAR *)NULL : hash_lookup (name, table);
+  /* Follow the nameref chain here if this is the global variables table */
+  if (entry && nameref_p (entry) && (invisible_p (entry) == 0) && table == global_variables->table)
+    {
+      entry = find_global_variable (entry->name);
+      /* Let's see if we have a nameref referencing a variable that hasn't yet
+        been created. */
+      if (entry == 0)
+       entry = find_variable_last_nameref (name);      /* XXX */
+      if (entry == 0)                                  /* just in case */
+        return (entry);
+    }
+
+  /* The first clause handles `declare -n ref; ref=x;' */
+  if (entry && invisible_p (entry) && nameref_p (entry))
+    goto assign_value;
+  else if (entry && nameref_p (entry))
+    {
+      newval = nameref_cell (entry);
+#if defined (ARRAY_VARS)
+      /* declare -n foo=x[2] */
+      if (valid_array_reference (newval))
+        /* XXX - should it be aflags? */
+       entry = assign_array_element (newval, make_variable_value (entry, value, 0), aflags);
+      else
+#endif
+      {
+      entry = make_new_variable (newval, table);
+      var_setvalue (entry, make_variable_value (entry, value, 0));
+      }
+    }
+  else if (entry == 0)
+    {
+      entry = make_new_variable (name, table);
+      var_setvalue (entry, make_variable_value (entry, value, 0)); /* XXX */
+    }
+  else if (entry->assign_func) /* array vars have assign functions now */
+    {
+      INVALIDATE_EXPORTSTR (entry);
+      newval = (aflags & ASS_APPEND) ? make_variable_value (entry, value, aflags) : value;
+      if (assoc_p (entry))
+       entry = (*(entry->assign_func)) (entry, newval, -1, savestring ("0"));
+      else if (array_p (entry))
+       entry = (*(entry->assign_func)) (entry, newval, 0, 0);
+      else
+       entry = (*(entry->assign_func)) (entry, newval, -1, 0);
+      if (newval != value)
+       free (newval);
+      return (entry);
+    }
+  else
+    {
+assign_value:
+      if ((readonly_p (entry) && (aflags & ASS_FORCE) == 0) || noassign_p (entry))
+       {
+         if (readonly_p (entry))
+           err_readonly (name);
+         return (entry);
+       }
+
+      /* Variables which are bound are visible. */
+      VUNSETATTR (entry, att_invisible);
+
+#if defined (ARRAY_VARS)
+      if (assoc_p (entry) || array_p (entry))
+        newval = make_array_variable_value (entry, 0, "0", value, aflags);
+      else
+#endif
+
+      newval = make_variable_value (entry, value, aflags);     /* XXX */
+
+      /* 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 (assoc_p (entry))
+       {
+         assoc_insert (assoc_cell (entry), savestring ("0"), newval);
+         free (newval);
+       }
+      else 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, flags)
+     const char *name;
+     char *value;
+     int flags;
+{
+  SHELL_VAR *v, *nv;
+  VAR_CONTEXT *vc, *nvc;
+  int level;
+
+  if (shell_variables == 0)
+    create_variable_tables ();
+
+  /* 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 && value)          /* XXX - can value be null here? */
+    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);
+         nvc = vc;
+         if (v && nameref_p (v))
+           {
+             nv = find_variable_nameref_context (v, vc, &nvc);
+             if (nv == 0)
+               {
+                 nv = find_variable_last_nameref_context (v, vc, &nvc);
+                 if (nv && nameref_p (nv))
+                   {
+                     /* If this nameref variable doesn't have a value yet,
+                        set the value.  Otherwise, assign using the value as
+                        normal. */
+                     if (nameref_cell (nv) == 0)
+                       return (bind_variable_internal (nv->name, value, nvc->table, 0, flags));
+#if defined (ARRAY_VARS)
+                     else if (valid_array_reference (nameref_cell (nv)))
+                       return (assign_array_element (nameref_cell (nv), value, flags));
+                     else
+#endif
+                     return (bind_variable_internal (nameref_cell (nv), value, nvc->table, 0, flags));
+                   }
+                 else
+                   v = nv;
+               }
+             else
+               v = nv;
+           }
+         if (v)
+           return (bind_variable_internal (v->name, value, nvc->table, 0, flags));
+       }
+    }
+  /* bind_variable_internal will handle nameref resolution in this case */
+  return (bind_variable_internal (name, value, global_variables->table, 0, flags));
+}
+
+SHELL_VAR *
+bind_global_variable (name, value, flags)
+     const char *name;
+     char *value;
+     int flags;
+{
+  SHELL_VAR *v, *nv;
+  VAR_CONTEXT *vc, *nvc;
+  int level;
+
+  if (shell_variables == 0)
+    create_variable_tables ();
+
+  /* bind_variable_internal will handle nameref resolution in this case */
+  return (bind_variable_internal (name, value, global_variables->table, 0, flags));
+}
+
+/* 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, aflags)
+     SHELL_VAR *var;
+     char *value;
+     int aflags;
+{
+  char *t;
+  int invis;
+
+  invis = invisible_p (var);
+  VUNSETATTR (var, att_invisible);
+
+  if (var->assign_func)
+    {
+      /* If we're appending, we need the old value, so use
+        make_variable_value */
+      t = (aflags & ASS_APPEND) ? make_variable_value (var, value, aflags) : value;
+      (*(var->assign_func)) (var, t, -1, 0);
+      if (t != value && t)
+       free (t);      
+    }
+  else
+    {
+      t = make_variable_value (var, value, aflags);
+#if defined (ARRAY_VARS)
+      if ((aflags & ASS_NAMEREF) && (t == 0 || *t == 0 || (legal_identifier (t) == 0 && valid_array_reference (t) == 0)))
+#else
+      if ((aflags & ASS_NAMEREF) && (t == 0 || *t == 0 || legal_identifier (t) == 0))
+#endif
+       {
+         free (t);
+         if (invis)
+           VSETATTR (var, att_invisible);      /* XXX */
+         return ((SHELL_VAR *)NULL);
+       }
+      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;
+  int isint, isarr, implicitarray;
+
+  isint = isarr = implicitarray = 0;
+#if defined (ARRAY_VARS)
+  if (valid_array_reference (lhs))
+    {
+      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 (array_p (v) && isarr == 0)
+       implicitarray = 1;
+#endif
+    }
+
+#if defined (ARRAY_VARS)
+  if (isarr)
+    v = assign_array_element (lhs, rhs, 0);
+  else if (implicitarray)
+    v = bind_array_variable (lhs, 0, rhs, 0);
+  else
+#endif
+    v = bind_variable (lhs, rhs, 0);
+
+  if (v && isint)
+    VSETATTR (v, att_integer);
+
+  VUNSETATTR (v, att_invisible);
+
+  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);
+}
+
+#if defined (DEBUGGER)
+/* 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;
+    }
+}
+#endif /* DEBUGGER */
+
+/* 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 (word, flags)
+     WORD_DESC *word;
+     int flags;
+{
+  int offset, aflags;
+  char *name, *temp, *value;
+  SHELL_VAR *var;
+  const char *string;
+
+  string = word->word;
+
+  aflags = 0;
+  offset = assignment (string, 0);
+  name = savestring (string);
+  value = (char *)NULL;
+
+  if (name[offset] == '=')
+    {
+      name[offset] = 0;
+
+      /* don't ignore the `+' when assigning temporary environment */
+      if (name[offset - 1] == '+')
+       {
+         name[offset - 1] = '\0';
+         aflags |= ASS_APPEND;
+       }
+
+      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;
+      value = expand_assignment_string_to_string (temp, 0);
+
+      if (var && (aflags & ASS_APPEND))
+       {
+         temp = make_variable_value (var, value, aflags);
+         FREE (value);
+         value = 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, 0);
+
+  array_needs_making = 1;
+
+  if (flags)
+    stupidly_hack_special_variables (name);
+
+  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, array_copy (array_cell (var)));
+      else if (assoc_p (var))
+       var_setassoc (copy, assoc_copy (assoc_cell (var)));
+#endif
+      else if (nameref_cell (var))     /* XXX - nameref */
+       var_setref (copy, savestring (nameref_cell (var)));
+      else if (value_cell (var))       /* XXX - nameref */
+       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. */
+static void
+dispose_variable_value (var)
+     SHELL_VAR *var;
+{
+  if (function_p (var))
+    dispose_command (function_cell (var));
+#if defined (ARRAY_VARS)
+  else if (array_p (var))
+    array_dispose (array_cell (var));
+  else if (assoc_p (var))
+    assoc_dispose (assoc_cell (var));
+#endif
+  else if (nameref_p (var))
+    FREE (nameref_cell (var));
+  else
+    FREE (value_cell (var));
+}
+
+void
+dispose_variable (var)
+     SHELL_VAR *var;
+{
+  if (var == 0)
+    return;
+
+  if (nofree_p (var) == 0)
+    dispose_variable_value (var);
+
+  FREE_EXPORTSTR (var);
+
+  free (var->name);
+
+  if (exported_p (var))
+    array_needs_making = 1;
+
+  free (var);
+}
+
+/* Unset the shell variable referenced by NAME.  Unsetting a nameref variable
+   unsets the variable it resolves to but leaves the nameref alone. */
+int
+unbind_variable (name)
+     const char *name;
+{
+  SHELL_VAR *v, *nv;
+  int r;
+
+  v = var_lookup (name, shell_variables);
+  nv = (v && nameref_p (v)) ? find_variable_nameref (v) : (SHELL_VAR *)NULL;
+
+  r = nv ? makunbound (nv->name, shell_variables) : makunbound (name, shell_variables);
+  return r;
+}
+
+/* Unbind NAME, where NAME is assumed to be a nameref variable */
+int
+unbind_nameref (name)
+     const char *name;
+{
+  SHELL_VAR *v;
+
+  v = var_lookup (name, shell_variables);
+  if (v && nameref_p (v))
+    return makunbound (name, shell_variables);
+  return 0;
+}
+
+/* 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;  
+}
+
+#if defined (DEBUGGER)
+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;  
+}
+#endif /* DEBUGGER */
+
+int
+delete_var (name, vc)
+     const char *name;
+     VAR_CONTEXT *vc;
+{
+  BUCKET_CONTENTS *elt;
+  SHELL_VAR *old_var;
+  VAR_CONTEXT *v;
+
+  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;
+  free (elt->key);
+  free (elt);
+
+  dispose_variable (old_var);
+  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)
+    {
+      if (nofree_p (old_var))
+       var_setvalue (old_var, (char *)NULL);
+#if defined (ARRAY_VARS)
+      else if (array_p (old_var))
+       array_dispose (array_cell (old_var));
+      else if (assoc_p (old_var))
+       assoc_dispose (assoc_cell (old_var));
+#endif
+      else if (nameref_p (old_var))
+       FREE (nameref_cell (old_var));
+      else
+       FREE (value_cell (old_var));
+      /* 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);
+      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, "", 0); \
+         if (!no_invisible_vars && entry) 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));
+}
+
+/* Candidate variables for the export environment are either valid variables
+   with the export attribute or invalid variables inherited from the initial
+   environment and simply passed through. */
+static int
+export_environment_candidate (var)
+     SHELL_VAR *var;
+{
+  return (exported_p (var) && (invisible_p (var) == 0 || imported_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);
+}
+
+char **tempvar_list;
+int tvlist_ind;
+
+/* 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, ASS_FORCE);
+
+  /* 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;
+
+  if (find_special_var (var->name) >= 0)
+    tempvar_list[tvlist_ind++] = savestring (var->name);
+
+  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
+    {
+      if (find_special_var (var->name) >= 0)
+       tempvar_list[tvlist_ind++] = savestring (var->name);
+      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.  PUSHF stores names of variables
+   that require special handling (e.g., IFS) on tempvar_list, so this
+   function can call stupidly_hack_special_variables on all the
+   variables in the list when the temporary hash table is destroyed. */
+static void
+dispose_temporary_env (pushf)
+     sh_free_func_t *pushf;
+{
+  int i;
+
+  tempvar_list = strvec_create (HASH_ENTRIES (temporary_env) + 1);
+  tempvar_list[tvlist_ind = 0] = 0;
+    
+  hash_flush (temporary_env, pushf);
+  hash_dispose (temporary_env);
+  temporary_env = (HASH_TABLE *)NULL;
+
+  tempvar_list[tvlist_ind] = 0;
+
+  array_needs_making = 1;
+
+#if 0
+  sv_ifs ("IFS");              /* XXX here for now -- check setifs in assign_in_env */  
+#endif
+  for (i = 0; i < tvlist_ind; i++)
+    stupidly_hack_special_variables (tempvar_list[i]);
+
+  strvec_dispose (tempvar_list);
+  tempvar_list = 0;
+  tvlist_ind = 0;
+}
+
+void
+dispose_used_env_vars ()
+{
+  if (temporary_env)
+    {
+      dispose_temporary_env (propagate_temp_var);
+      maybe_make_export_env ();
+    }
+}
+
+/* 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);
+}
+
+void
+flush_temporary_env ()
+{
+  if (temporary_env)
+    {
+      hash_flush (temporary_env, free_variable_hash_data);
+      hash_dispose (temporary_env);
+      temporary_env = (HASH_TABLE *)NULL;
+    }
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*          Creating and manipulating the environment              */
+/*                                                                 */
+/* **************************************************************** */
+
+static inline char *
+mk_env_string (name, value, isfunc)
+     const char *name, *value;
+     int isfunc;
+{
+  size_t name_len, value_len;
+  char *p, *q;
+
+  name_len = strlen (name);
+  value_len = STRLEN (value);
+
+  /* If we are exporting a shell function, construct the encoded function
+     name. */
+  if (isfunc && value)
+    {
+      p = (char *)xmalloc (BASHFUNC_PREFLEN + name_len + BASHFUNC_SUFFLEN + value_len + 2);
+      q = p;
+      memcpy (q, BASHFUNC_PREFIX, BASHFUNC_PREFLEN);
+      q += BASHFUNC_PREFLEN;
+      memcpy (q, name, name_len);
+      q += name_len;
+      memcpy (q, BASHFUNC_SUFFIX, BASHFUNC_SUFFLEN);
+      q += BASHFUNC_SUFFLEN;
+    }
+  else
+    {
+      p = (char *)xmalloc (2 + name_len + value_len);
+      memcpy (p, name, name_len);
+      q = p + name_len;
+    }
+
+  q[0] = '=';
+  if (value && *value)
+    memcpy (q + 1, value, value_len + 1);
+  else
+    q[1] = '\0';
+
+  return (p);
+}
+
+#ifdef DEBUG
+/* Debugging */
+static int
+valid_exportstr (v)
+     SHELL_VAR *v;
+{
+  char *s;
+
+  s = v->exportstr;
+  if (s == 0)
+    {
+      internal_error (_("%s has null exportstr"), v->name);
+      return (0);
+    }
+  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 ARRAY_EXPORT
+       value = array_to_assignment_string (array_cell (var));
+#  else
+       continue;       /* XXX array vars cannot yet be exported */
+#  endif /* ARRAY_EXPORT */
+      else if (assoc_p (var))
+#  if 0
+       value = assoc_to_assignment_string (assoc_cell (var));
+#  else
+       continue;       /* XXX associative 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, function_p (var));
+
+         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) || assoc_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;
+
+#if 0
+  vars = map_over (visible_and_exported, vcxt);
+#else
+  vars = map_over (export_environment_candidate, vcxt);
+#endif
+
+  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); \
+       environ = export_env; \
+      } \
+    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;
+}
+
+int
+chkexport (name)
+     char *name;
+{
+  SHELL_VAR *v;
+
+  v = find_variable (name);
+  if (v && exported_p (v))
+    {
+      array_needs_making = 1;
+      maybe_make_export_env ();
+      return 1;
+    }
+  return 0;
+}
+
+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);
+         environ = export_env;
+       }
+      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);
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*                   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 (local_p (var) && STREQ (var->name, "-"))
+    set_current_options (value_cell (var));
+  else if (tempvar_p (var) && (posixly_correct || (var->attributes & att_propagate)))
+    {
+      /* Make sure we have a hash table to store the variable in while it is
+        being propagated down to the global variables table.  Create one if
+        we have to */
+      if ((vc_isfuncenv (shell_variables) || vc_istempenv (shell_variables)) && shell_variables->table == 0)
+       shell_variables->table = hash_create (0);
+      /* XXX - should we set v->context here? */
+      v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0, 0);
+      if (shell_variables == global_variables)
+       var->attributes &= ~(att_tempvar|att_propagate);
+      else
+       shell_variables->flags |= VC_HASTMPVAR;
+      v->attributes |= var->attributes;
+    }
+  else
+    stupidly_hack_special_variables (var->name);       /* XXX */
+
+  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. */
+  /* XXX - This isn't exactly right, because all tempenv variables have the
+    export attribute set. */
+#if 0
+  if (exported_p (var) || (var->attributes & att_propagate))
+#else
+  if (tempvar_p (var) && exported_p (var) && (var->attributes & att_propagate))
+#endif
+    {
+      var->attributes &= ~att_tempvar;         /* XXX */
+      v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0, 0);
+      if (shell_variables == global_variables)
+       var->attributes &= ~att_propagate;
+      v->attributes |= var->attributes;
+    }
+  else
+    stupidly_hack_special_variables (var->name);       /* XXX */
+
+  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 - should always be followed by remember_args () */
+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 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[] = {
+  { "BASH_COMPAT", sv_shcompat },
+  { "BASH_XTRACEFD", sv_xtracefd },
+
+#if defined (JOB_CONTROL)
+  { "CHILD_MAX", sv_childmax },
+#endif
+
+#if defined (READLINE)
+#  if defined (STRICT_POSIX)
+  { "COLUMNS", sv_winsize },
+#  endif
+  { "COMP_WORDBREAKS", sv_comp_wordbreaks },
+#endif
+
+  { "FUNCNEST", sv_funcnest },
+
+  { "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 (__CYGWIN__)
+  { "HOME", sv_home },
+#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 },
+  { "LC_TIME", sv_locale },
+
+#if defined (READLINE) && defined (STRICT_POSIX)
+  { "LINES", sv_winsize },
+#endif
+
+  { "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)
+  { "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);
+}
+
+/* Special variables that need hooks to be run when they are unset as part
+   of shell reinitialization should have their sv_ functions run here. */
+void
+reinit_special_variables ()
+{
+#if defined (READLINE)
+  sv_comp_wordbreaks ("COMP_WORDBREAKS");
+#endif
+  sv_globignore ("GLOBIGNORE");
+  sv_opterr ("OPTERR");
+}
+
+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 ();
+    }
+}
+
+void
+sv_funcnest (name)
+     char *name;
+{
+  SHELL_VAR *v;
+  intmax_t num;
+
+  v = find_variable (name);
+  if (v == 0)
+    funcnest_max = 0;
+  else if (legal_number (value_cell (v), &num) == 0)
+    funcnest_max = 0;
+  else
+    funcnest_max = num;
+}
+
+/* What to do when GLOBIGNORE changes. */
+void
+sv_globignore (name)
+     char *name;
+{
+  if (privileged_mode == 0)
+    setup_glob_ignore (name);
+}
+
+#if defined (READLINE)
+void
+sv_comp_wordbreaks (name)
+     char *name;
+{
+  SHELL_VAR *sv;
+
+  sv = find_variable (name);
+  if (sv == 0)
+    reset_completer_word_break_chars ();
+}
+
+/* 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;
+}
+
+#if defined (STRICT_POSIX)
+/* In strict posix mode, we allow assignments to LINES and COLUMNS (and values
+   found in the initial environment) to override the terminal size reported by
+   the kernel. */
+void
+sv_winsize (name)
+     char *name;
+{
+  SHELL_VAR *v;
+  intmax_t xd;
+  int d;
+
+  if (posixly_correct == 0 || interactive_shell == 0 || no_line_editing)
+    return;
+
+  v = find_variable (name);
+  if (v == 0 || var_isnull (v))
+    rl_reset_screen_size ();
+  else
+    {
+      if (legal_number (value_cell (v), &xd) == 0)
+       return;
+      winsize_assignment = 1;
+      d = xd;                  /* truncate */
+      if (name[0] == 'L')      /* LINES */
+       rl_set_screen_size (d, -1);
+      else                     /* COLUMNS */
+       rl_set_screen_size (-1, d);
+      winsize_assignment = 0;
+    }
+}
+#endif /* STRICT_POSIX */
+#endif /* READLINE */
+
+/* Update the value of HOME in the export environment so tilde expansion will
+   work on cygwin. */
+#if defined (__CYGWIN__)
+sv_home (name)
+     char *name;
+{
+  array_needs_making = 1;
+  maybe_make_export_env ();
+}
+#endif
+
+#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;
+  int hmax;
+
+  temp = get_string_value (name);
+
+  if (temp && *temp)
+    {
+      if (legal_number (temp, &num))
+       {
+         hmax = num;
+         if (hmax < 0 && name[4] == 'S')
+           unstifle_history ();        /* unstifle history if HISTSIZE < 0 */
+         else if (name[4] == 'S')
+           {
+             stifle_history (hmax);
+             hmax = where_history ();
+             if (history_lines_this_session > hmax)
+               history_lines_this_session = hmax;
+           }
+         else if (hmax >= 0)   /* truncate HISTFILE if HISTFILESIZE >= 0 */
+           {
+             history_truncate_file (get_string_value ("HISTFILE"), hmax);
+             /* If we just shrank the history file to fewer lines than we've
+                already read, make sure we adjust our idea of how many lines
+                we have read from the file. */
+             if (hmax < history_lines_in_file)
+               history_lines_in_file = hmax;
+           }
+       }
+    }
+  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;
+
+  if (v = find_variable (name))
+    {
+      if (history_comment_char == 0)
+       history_comment_char = '#';
+    }
+  history_write_timestamps = (v != 0);
+}
+#endif /* HISTORY */
+
+#if defined (HAVE_TZSET)
+void
+sv_tz (name)
+     char *name;
+{
+  if (chkexport (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;
+{
+  SHELL_VAR *var;
+  char *tt;
+  int s;
+
+  var = find_variable ("OPTIND");
+  tt = var ? get_variable_value (var) : (char *)NULL;
+
+  /* Assume that if var->context < variable_context and variable_context > 0
+     then we are restoring the variables's previous state while returning
+     from a function. */
+  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;
+  int r;
+
+  v = get_string_value (name);
+  if (name[0] == 'L' && name[1] == 'A')        /* LANG */
+    r = set_lang (name, v);
+  else
+    r = set_locale_var (name, v);              /* LC_*, TEXTDOMAIN* */
+
+#if 1
+  if (r == 0 && posixly_correct)
+    last_command_exit_value = 1;
+#endif
+}
+
+#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);
+       }
+    }
+}
+
+ARRAY *
+save_pipestatus_array ()
+{
+  SHELL_VAR *v;
+  ARRAY *a, *a2;
+
+  v = find_variable ("PIPESTATUS");
+  if (v == 0 || array_p (v) == 0 || array_cell (v) == 0)
+    return ((ARRAY *)NULL);
+    
+  a = array_cell (v);
+  a2 = array_copy (array_cell (v));
+
+  return a2;
+}
+
+void
+restore_pipestatus_array (a)
+     ARRAY *a;
+{
+  SHELL_VAR *v;
+  ARRAY *a2;
+
+  v = find_variable ("PIPESTATUS");
+  /* XXX - should we still assign even if existing value is NULL? */
+  if (v == 0 || array_p (v) == 0 || array_cell (v) == 0)
+    return;
+
+  a2 = array_cell (v);
+  var_setarray (v, a); 
+
+  array_dispose (a2);
+}
+#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
+}
+
+void
+sv_xtracefd (name)
+     char *name;
+{
+  SHELL_VAR *v;
+  char *t, *e;
+  int fd;
+  FILE *fp;
+
+  v = find_variable (name);
+  if (v == 0)
+    {
+      xtrace_reset ();
+      return;
+    }
+
+  t = value_cell (v);
+  if (t == 0 || *t == 0)
+    xtrace_reset ();
+  else
+    {
+      fd = (int)strtol (t, &e, 10);
+      if (e != t && *e == '\0' && sh_validfd (fd))
+       {
+         fp = fdopen (fd, "w");
+         if (fp == 0)
+           internal_error (_("%s: %s: cannot open as FILE"), name, value_cell (v));
+         else
+           xtrace_set (fd, fp);
+       }
+      else
+       internal_error (_("%s: %s: invalid value for trace file descriptor"), name, value_cell (v));
+    }
+}
+
+#define MIN_COMPAT_LEVEL 31
+
+void
+sv_shcompat (name)
+     char *name;
+{
+  SHELL_VAR *v;
+  char *val;
+  int tens, ones, compatval;
+
+  v = find_variable (name);
+  if (v == 0)
+    {
+      shell_compatibility_level = DEFAULT_COMPAT_LEVEL;
+      set_compatibility_opts ();
+      return;
+    }
+  val = value_cell (v);
+  if (val == 0 || *val == '\0')
+    {
+      shell_compatibility_level = DEFAULT_COMPAT_LEVEL;
+      set_compatibility_opts ();
+      return;
+    }
+  /* Handle decimal-like compatibility version specifications: 4.2 */
+  if (isdigit (val[0]) && val[1] == '.' && isdigit (val[2]) && val[3] == 0)
+    {
+      tens = val[0] - '0';
+      ones = val[2] - '0';
+      compatval = tens*10 + ones;
+    }
+  /* Handle integer-like compatibility version specifications: 42 */
+  else if (isdigit (val[0]) && isdigit (val[1]) && val[2] == 0)
+    {
+      tens = val[0] - '0';
+      ones = val[1] - '0';
+      compatval = tens*10 + ones;
+    }
+  else
+    {
+compat_error:
+      internal_error (_("%s: %s: compatibility value out of range"), name, val);
+      shell_compatibility_level = DEFAULT_COMPAT_LEVEL;
+      set_compatibility_opts ();
+      return;
+    }
+
+  if (compatval < MIN_COMPAT_LEVEL || compatval > DEFAULT_COMPAT_LEVEL)
+    goto compat_error;
+
+  shell_compatibility_level = compatval;
+  set_compatibility_opts ();
+}
+
+#if defined (JOB_CONTROL)
+void
+sv_childmax (name)
+     char *name;
+{
+  char *tt;
+  int s;
+
+  tt = get_string_value (name);
+  s = (tt && *tt) ? atoi (tt) : 0;
+  set_maxchild (s);
+}
+#endif