commit bash-20040301 snapshot bash-3.0-beta
authorChet Ramey <chet.ramey@case.edu>
Sat, 3 Dec 2011 17:55:02 +0000 (12:55 -0500)
committerChet Ramey <chet.ramey@case.edu>
Sat, 3 Dec 2011 17:55:02 +0000 (12:55 -0500)
20 files changed:
CWRU/CWRU.chlog
CWRU/CWRU.chlog~
INSTALL
NEWS
doc/Makefile.in
doc/bash.pdf
doc/bashref.pdf
expr.c
lib/readline/bind.c
lib/readline/bind.c~ [new file with mode: 0644]
lib/readline/histfile.c
lib/readline/histfile.c~ [new file with mode: 0644]
lib/readline/input.c
lib/readline/input.c~ [new file with mode: 0644]
lib/readline/parens.c
lib/readline/parens.c~ [new file with mode: 0644]
lib/sh/strftime.c
lib/sh/strftime.c~ [new file with mode: 0644]
subst.c
subst.c~

index 08bdada..417be31 100644 (file)
@@ -9202,3 +9202,27 @@ bashline.c
        - fix conditional binding of emacs-mode M-~ -- there is a default
          binding for it (rl_tilde_expand), so a straight call to
          rl_bind_key_if_unbound_in_map doesn't do the right thing
+
+                                  2/27
+                                  ----
+[bash-3.0-beta1 released]
+
+                                  2/29
+                                  ----
+subst.c
+       - fixed expansion so referencing $a, when a is an array variable
+         without an element assigned to index 0, exits the shell when
+         `-u' is enabled
+
+expr.c
+       - make the exponentiation operator (**) associative, so things like
+         2**3**4  work right (change `if' to `while')
+
+                                   3/2
+                                   ---
+lib/sh/strftime.c
+       - SCO Unix 3.2, like Solaris, requires that the system's `timezone'
+         variable be declared as long
+
+lib/readline/{bind,histfile,input,parens}.c
+       - changes for Tandem (including `floss.h' (?))
index fac3512..a03188d 100644 (file)
@@ -9144,9 +9144,82 @@ lib/glob/strmatch.c
          strmatch()
 
 subst.c
-       - old match_pattern is now internal_match_pattern
-       - match_pattern now either calls internal_match_pattern or converts
-         mbstrings to wide chars and calls internal_match_wpattern
-       - internal_match_pattern reverted to old non-multibyte code
+       - old match_pattern is now match_upattern
+       - match_pattern now either calls match_upattern or converts
+         mbstrings to wide chars and calls match_wpattern
+       - match_upattern reverted to old non-multibyte code
        - new function: match_pattern_wchar, wide character version of
          match_pattern_char
+
+                                   2/1
+                                   ---
+subst.c
+       - old remove_pattern is now remove_upattern
+       - remove_upattern reverted to old non-multibyte code (pre-Waugh patch)
+       - new multibyte version of remove_pattern: remove_wpattern
+       - remove_pattern now calls either remove_upattern or converts a
+         multibyte string to a wide character string and calls
+         remove_wpattern
+       - new function, wcsdup, wide-character version of strdup(3)
+
+                                   2/4
+                                   ---
+print_cmd.c
+       - temporarily translate a >&filename redirection from
+         r_duplicating_output_word to r_err_and_out (as the expansion code
+         in redir.c does) so it prints without a leading `1' (file
+         descriptor)
+
+                                   2/5
+                                   ---
+aclocal.m4
+       - add a check for wcsdup to BASH_CHECK_MULTIBYTE
+
+config.h.in
+       - add HAVE_WCSDUP define
+
+                                   2/9
+                                   ---
+builtins/shift.def
+       - fix a call to sh_erange that possibly dereferences a NULL pointer
+
+                                  2/12
+                                  ----
+general.c
+       - start at a general set of file property checking functions:
+         file_isdir(), file_iswdir() (is writable directory)
+
+general.h
+       - extern declarations for new functions
+
+lib/sh/tmpfile.c
+       - use file_iswdir() to make sure the temporary directory used for
+         here documents and other temp files is writable in get_sys_tmpdir()
+
+                                  2/17
+                                  ----
+bashline.c
+       - fix conditional binding of emacs-mode M-~ -- there is a default
+         binding for it (rl_tilde_expand), so a straight call to
+         rl_bind_key_if_unbound_in_map doesn't do the right thing
+
+                                  2/27
+                                  ----
+[bash-3.0-beta1 released]
+
+                                  2/29
+                                  ----
+subst.c
+       - fixed expansion so referencing $a, when a is an array variable
+         without an element assigned to index 0, exits the shell when
+         `-u' is enabled
+
+expr.c
+       - make the exponentiation operator (**) associative, so things like
+         2**3**4  work right (change `if' to `while')
+
+                                   3/2
+                                   ---
+lib/sh/strftime.c
+       - SCO Unix 3.2, like Solaris, requires that the system's `timezone'
+         variable be declared as long
diff --git a/INSTALL b/INSTALL
index 51997bc..fc66407 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -283,7 +283,12 @@ does not provide the necessary support.
      builtins and functions to be timed.
 
 `--enable-cond-command'
-     Include support for the `[[' conditional command (*note
+     Include support for the `[[' conditional command.  (*note
+     Conditional Constructs::).
+
+`--enable-cond-regexp'
+     Include support for matching POSIX regular expressions using the
+     `=~' binary operator in the `[[' conditional command.  (*note
      Conditional Constructs::).
 
 `--enable-directory-stack'
diff --git a/NEWS b/NEWS
index 37fff1a..1011173 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -128,6 +128,15 @@ ll. The error message printed when bash cannot open a shell script supplied
     as argument 1 now includes the name of the shell, to better identify
     the error as coming from bash.
 
+mm. A bug that caused here documents to not work if the directory the shell
+    used for the temporary files was not writable has been fixed.
+
+nn. The parameter pattern removal and substitution expansions are now much
+    faster and more efficient when using multibyte characters.
+
+oo. Fixed a bug in the `shift' builtin that could cause core dumps when
+    reporting an out-of-range argument.
+
 2.  New Features in Readline
 
 a.  History expansion has a new `a' modifier equivalent to the `g' modifier
index 23a6a8d..fc779eb 100644 (file)
@@ -286,4 +286,4 @@ rbash:      bashref.texi
        cmp -s RBASH ../RBASH || mv RBASH ../RBASH
        $(RM) RBASH
 
-xdist: inst posix rbash
+xdist: pdf inst posix rbash
index 088c9d7..520b109 100644 (file)
Binary files a/doc/bash.pdf and b/doc/bash.pdf differ
index b3b8011..fd70b7c 100644 (file)
Binary files a/doc/bashref.pdf and b/doc/bashref.pdf differ
diff --git a/expr.c b/expr.c
index 5b6069b..147cf55 100644 (file)
--- a/expr.c
+++ b/expr.c
@@ -42,6 +42,7 @@
        "||"
        "expr ? expr : expr"
        "=", "*=", "/=", "%=", "+=", "-=", "<<=", ">>=", "&=", "^=", "|="
+       ,                       [comma]
 
  (Note that most of these operators have special meaning to bash, and an
  entire expression should be quoted, e.g. "a=$a+1" or "a=a+1" to ensure
@@ -765,7 +766,7 @@ exppower ()
   register intmax_t val1, val2, c;
 
   val1 = exp1 ();
-  if (curtok == POWER)
+  while (curtok == POWER)
     {
       readtok ();
       val2 = exp1 ();
index aa0bb22..15d0e4e 100644 (file)
    is generally kept in a file called COPYING or LICENSE.  If you do not
    have a copy of the license, write to the Free Software Foundation,
    59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+
 #define READLINE_LIBRARY
 
+#if defined (__TANDEM)
+#  include <floss.h>
+#endif
+
 #if defined (HAVE_CONFIG_H)
 #  include <config.h>
 #endif
diff --git a/lib/readline/bind.c~ b/lib/readline/bind.c~
new file mode 100644 (file)
index 0000000..3f243c1
--- /dev/null
@@ -0,0 +1,2223 @@
+/* bind.c -- key binding and startup file support for the readline library. */
+
+/* Copyright (C) 1987, 1989, 1992 Free Software Foundation, Inc.
+
+   This file is part of the GNU Readline Library, a library for
+   reading lines of text with interactive input and history editing.
+
+   The GNU Readline Library is free software; you can redistribute it
+   and/or modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2, or
+   (at your option) any later version.
+
+   The GNU Readline Library is distributed in the hope that it will be
+   useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+   of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   The GNU General Public License is often shipped with GNU software, and
+   is generally kept in a file called COPYING or LICENSE.  If you do not
+   have a copy of the license, write to the Free Software Foundation,
+   59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+
+#if defined (__TANDEM)
+#  include <floss.h>
+#endif
+
+#define READLINE_LIBRARY
+
+#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 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 substring_member_of_array PARAMS((char *, const char **));
+
+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))
+    {
+      free (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 || !*keyseq)
+    {
+      if (type == ISMACR)
+       free (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))
+    {
+      free (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)
+       return -1;
+
+      if (_rl_convert_meta_chars_to_ascii && META_CHAR (ic))
+       {
+         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)
+           free ((char *)map[ic].function);
+         else if (map[ic].type == ISKMAP)
+           {
+             map = FUNCTION_TO_KEYMAP (map, ic);
+             ic = ANYOTHERKEY;
+           }
+
+         map[ic].function = KEYMAP_TO_FUNCTION (data);
+         map[ic].type = type;
+       }
+
+      rl_binding_keymap = map;
+    }
+  free (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++;
+                 array[l++] = ESC;     /* ESC is meta-prefix */
+               }
+             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);
+}
+
+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 (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;
+}
+
+static char *
+_rl_untranslate_macro_value (seq)
+     char *seq;
+{
+  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 (CTRL_CHAR (c) && c != ESC)
+       {
+         *r++ = '\\';
+         *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)
+    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)
+           {
+             if (type)
+               *type = map[ESC].type;
+
+             return (map[ESC].function);
+           }
+         else
+           {
+             map = FUNCTION_TO_KEYMAP (map, ESC);
+             ic = UNMETA (ic);
+           }
+       }
+
+      if (map[ic].type == ISKMAP)
+       {
+         /* If this is the last key in the key sequence, return the
+            map. */
+         if (!keyseq[i + 1])
+           {
+             if (type)
+               *type = ISKMAP;
+
+             return (map[ic].function);
+           }
+         else
+           map = FUNCTION_TO_KEYMAP (map, ic);
+       }
+      else
+       {
+         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)
+    {
+      free (buffer);
+      return ((char *)NULL);
+    }
+
+  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
+   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 = DEFAULT_INPUTRC;
+    }
+
+  if (*filename == 0)
+    filename = DEFAULT_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);
+  free (openname);
+
+  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++;
+    }
+
+  free (buffer);
+  currently_reading_init_file = 0;
+  return (0);
+}
+
+static void
+_rl_init_file_error (msg)
+     const char *msg;
+{
+  if (currently_reading_init_file)
+    fprintf (stderr, "readline: %s: line %d: %s\n", current_readline_init_file,
+                    current_readline_init_lineno, msg);
+  else
+    fprintf (stderr, "readline: %s\n", msg);
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*                     Parser Directives                           */
+/*                                                                 */
+/* **************************************************************** */
+
+typedef int _rl_parser_func_t PARAMS((char *));
+
+/* Things that mean `Control'. */
+const char *_rl_possible_control_prefixes[] = {
+  "Control-", "C-", "CTRL-", (const char *)NULL
+};
+
+const char *_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);
+      free (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 struct {
+  const char *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);
+}
+
+/* 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 || !*string || *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 == '"')
+    {
+      int passc = 0;
+
+      for (i = 1; c = string[i]; i++)
+       {
+         if (passc)
+           {
+             passc = 0;
+             continue;
+           }
+
+         if (c == '\\')
+           {
+             passc++;
+             continue;
+           }
+
+         if (c == '"')
+           break;
+       }
+      /* 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;
+        }
+    }
+
+  /* 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 = string + i;
+      char *value;
+
+      /* 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)) value++;
+      if (*value)
+       *value++ = '\0';
+      while (*value && whitespace (*value)) value++;
+
+      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 == '"')
+    {
+      int delimiter = string[i++], passc;
+
+      for (passc = 0; c = string[i]; i++)
+       {
+         if (passc)
+           {
+             passc = 0;
+             continue;
+           }
+
+         if (c == '\\')
+           {
+             passc = 1;
+             continue;
+           }
+
+         if (c == delimiter)
+           break;
+       }
+      if (c)
+       i++;
+    }
+
+  /* Advance to the end of the string.  */
+  for (; string[i] && !whitespace (string[i]); 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));
+
+      free (seq);
+      return 0;
+    }
+
+  /* Get the actual character we want to deal with. */
+  kname = strrchr (string, '-');
+  if (!kname)
+    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 struct {
+  const char *name;
+  int *value;
+  int flags;
+} boolean_varlist [] = {
+  { "blink-matching-paren",    &rl_blink_matching_paren,       V_SPECIAL },
+  { "byte-oriented",           &rl_byte_oriented,              0 },
+  { "completion-ignore-case",  &_rl_completion_case_fold,      0 },
+  { "convert-meta",            &_rl_convert_meta_chars_to_ascii, 0 },
+  { "disable-completion",      &rl_inhibit_completion,         0 },
+  { "enable-keypad",           &_rl_enable_keypad,             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 },
+  { "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 },
+  { "show-all-if-ambiguous",   &_rl_complete_show_all,         0 },
+  { "show-all-if-unmodified",  &_rl_complete_show_unmodified,  0 },
+#if defined (VISIBLE_STATS)
+  { "visible-stats",           &rl_visible_stats,              0 },
+#endif /* VISIBLE_STATS */
+  { (char *)NULL, (int *)NULL }
+};
+
+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;
+    }
+}
+
+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_compquery PARAMS((const char *));
+static int sv_editmode PARAMS((const char *));
+static int sv_isrchterm PARAMS((const char *));
+static int sv_keymap PARAMS((const char *));
+
+static struct {
+  const char *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-query-items", V_INT,   sv_compquery },
+  { "editing-mode",    V_STRING,       sv_editmode },
+  { "isearch-terminators", V_STRING,   sv_isrchterm },
+  { "keymap",          V_STRING,       sv_keymap },
+  { (char *)NULL,      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-insenstive), or "1".  Any other
+   values result in 0 (false). */
+static int
+bool_to_int (value)
+     char *value;
+{
+  return (value == 0 || *value == '\0' ||
+               (_rl_stricmp (value, "on") == 0) ||
+               (value[0] == '1' && value[1] == '\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_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_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_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';
+
+  free (v);
+  return 0;
+}
+      
+/* Return the character which matches NAME.
+   For example, `Space' returns ' '. */
+
+typedef struct {
+  const char *name;
+  int value;
+} assoc_list;
+
+static 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 struct {
+  const char *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]);
+
+  free (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 0
+                 sprintf (keyname, "\\e");
+#else
+               /* XXX - experimental */
+                 sprintf (keyname, "\\M-");
+#endif
+               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]);
+               free (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;
+             }
+
+           free (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);
+                 free (invokers[j]);
+               }
+
+             free (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++)
+               free (invokers[j]);
+
+             free (invokers);
+           }
+       }
+    }
+}
+
+/* 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);
+
+         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 : "");
+         free (keyname);
+         free (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);
+                 free (keyname);
+                 keyname = out;
+               }
+           }
+
+         _rl_macro_dumper_internal (print_readably, FUNCTION_TO_KEYMAP (map, key), keyname);
+         free (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);
+}
+
+void
+rl_variable_dumper (print_readably)
+     int print_readably;
+{
+  int i;
+  const char *kname;
+
+  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");
+    }
+
+  /* bell-style */
+  switch (_rl_bell_preference)
+    {
+    case NO_BELL:
+      kname = "none"; break;
+    case VISIBLE_BELL:
+      kname = "visible"; break;
+    case AUDIBLE_BELL:
+    default:
+      kname = "audible"; break;
+    }
+  if (print_readably)
+    fprintf (rl_outstream, "set bell-style %s\n", kname);
+  else
+    fprintf (rl_outstream, "bell-style is set to `%s'\n", kname);
+
+  /* comment-begin */
+  if (print_readably)
+    fprintf (rl_outstream, "set comment-begin %s\n", _rl_comment_begin ? _rl_comment_begin : RL_COMMENT_BEGIN_DEFAULT);
+  else
+    fprintf (rl_outstream, "comment-begin is set to `%s'\n", _rl_comment_begin ? _rl_comment_begin : RL_COMMENT_BEGIN_DEFAULT);
+
+  /* completion-query-items */
+  if (print_readably)
+    fprintf (rl_outstream, "set completion-query-items %d\n", rl_completion_query_items);
+  else
+    fprintf (rl_outstream, "completion-query-items is set to `%d'\n", rl_completion_query_items);
+
+  /* editing-mode */
+  if (print_readably)
+    fprintf (rl_outstream, "set editing-mode %s\n", (rl_editing_mode == emacs_mode) ? "emacs" : "vi");
+  else
+    fprintf (rl_outstream, "editing-mode is set to `%s'\n", (rl_editing_mode == emacs_mode) ? "emacs" : "vi");
+
+  /* isearch-terminators */
+  if (_rl_isearch_terminators)
+    {
+      char *disp;
+
+      disp = _rl_untranslate_macro_value (_rl_isearch_terminators);
+
+      if (print_readably)
+       fprintf (rl_outstream, "set isearch-terminators \"%s\"\n", disp);
+      else
+       fprintf (rl_outstream, "isearch-terminators is set to \"%s\"\n", disp);
+
+      free (disp);
+    }
+
+  /* keymap */
+  kname = rl_get_keymap_name (_rl_keymap);
+  if (kname == 0)
+    kname = rl_get_keymap_name_from_edit_mode ();
+  if (print_readably)
+    fprintf (rl_outstream, "set keymap %s\n", kname ? kname : "none");
+  else
+    fprintf (rl_outstream, "keymap is set to `%s'\n", kname ? kname : "none");
+}
+
+/* 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)
+     char *string;
+     const char **array;
+{
+  while (*array)
+    {
+      if (_rl_strindex (string, *array))
+       return (1);
+      array++;
+    }
+  return (0);
+}
index b0b1a1b..717bbee 100644 (file)
 /* 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
diff --git a/lib/readline/histfile.c~ b/lib/readline/histfile.c~
new file mode 100644 (file)
index 0000000..977a301
--- /dev/null
@@ -0,0 +1,542 @@
+/* histfile.c - functions to manipulate the history file. */
+
+/* Copyright (C) 1989-2003 Free Software Foundation, Inc.
+
+   This file contains the GNU History Library (the Library), a set of
+   routines for managing the text of previously typed lines.
+
+   The Library is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   The Library is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   The GNU General Public License is often shipped with GNU software, and
+   is generally kept in a file called COPYING or LICENSE.  If you do not
+   have a copy of the license, write to the Free Software Foundation,
+   59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+
+/* 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. */
+
+#if defined (__TANDEM)
+#  include <floss.h>
+#endif
+
+#define READLINE_LIBRARY
+
+#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
+
+#if defined (__EMX__) || defined (__CYGWIN__)
+#  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;
+
+/* 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)
+
+/* 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 (home == 0)
+    {
+      home = ".";
+      home_len = 1;
+    }
+  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);
+}
+
+/* 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
+
+  buffer = last_ts = (char *)NULL;
+  input = history_filename (filename);
+  file = open (input, O_RDONLY|O_BINARY, 0666);
+
+  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')
+      {
+       *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;
+      }
+
+  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, *bp, *bp1;          /* bp1 == bp+1 */
+  int file, chars_read, rv;
+  struct stat finfo;
+  size_t file_size;
+
+  buffer = (char *)NULL;
+  filename = history_filename (fname);
+  file = open (filename, O_RDONLY|O_BINARY, 0666);
+  rv = 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;
+    }
+
+  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)
+    {
+      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;
+    }
+
+  /* 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 && ((file = open (filename, O_WRONLY|O_TRUNC|O_BINARY, 0600)) != -1))
+    {
+      write (file, bp, chars_read - (bp - buffer));
+
+#if defined (__BEOS__)
+      /* BeOS ignores O_TRUNC. */
+      ftruncate (file, chars_read - (bp - buffer));
+#endif
+
+      close (file);
+    }
+
+ truncate_exit:
+
+  FREE (buffer);
+
+  free (filename);
+  return rv;
+}
+
+/* Workhorse function for writing history.  Writes 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;
+  int file, mode, rv;
+#ifdef HISTORY_USE_MMAP
+  size_t cursize;
+
+  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);
+  rv = 0;
+
+  if ((file = open (output, mode, 0600)) == -1)
+    {
+      FREE (output);
+      return (errno);
+    }
+
+#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;
+       FREE (output);
+       close (file);
+       return rv;
+      }
+#else    
+    buffer = (char *)malloc (buffer_size);
+    if (buffer == 0)
+      {
+       rv = errno;
+       FREE (output);
+       close (file);
+       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, 0) != 0 || munmap (buffer, buffer_size) != 0)
+      rv = errno;
+#else
+    if (write (file, buffer, buffer_size) < 0)
+      rv = errno;
+    free (buffer);
+#endif
+  }
+
+  close (file);
+
+  FREE (output);
+
+  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 6d2bb2f..443a7d0 100644 (file)
    59 Temple Place, Suite 330, Boston, MA 02111 USA. */
 #define READLINE_LIBRARY
 
+#if defined (__TANDEM)
+#  include <floss.h>
+#endif
+
 #if defined (HAVE_CONFIG_H)
 #  include <config.h>
 #endif
diff --git a/lib/readline/input.c~ b/lib/readline/input.c~
new file mode 100644 (file)
index 0000000..6d2bb2f
--- /dev/null
@@ -0,0 +1,551 @@
+/* input.c -- character input functions for readline. */
+
+/* Copyright (C) 1994 Free Software Foundation, Inc.
+
+   This file is part of the GNU Readline Library, a library for
+   reading lines of text with interactive input and history editing.
+
+   The GNU Readline Library is free software; you can redistribute it
+   and/or modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2, or
+   (at your option) any later version.
+
+   The GNU Readline Library is distributed in the hope that it will be
+   useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+   of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   The GNU General Public License is often shipped with GNU software, and
+   is generally kept in a file called COPYING or LICENSE.  If you do not
+   have a copy of the license, write to the Free Software Foundation,
+   59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+#define READLINE_LIBRARY
+
+#if defined (HAVE_CONFIG_H)
+#  include <config.h>
+#endif
+
+#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 */
+
+#if defined (HAVE_SELECT)
+#  if !defined (HAVE_SYS_SELECT_H) || !defined (M_UNIX)
+#    include <sys/time.h>
+#  endif
+#endif /* HAVE_SELECT */
+#if defined (HAVE_SYS_SELECT_H)
+#  include <sys/select.h>
+#endif
+
+#if defined (FIONREAD_IN_SYS_IOCTL)
+#  include <sys/ioctl.h>
+#endif
+
+#include <stdio.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"
+
+/* Some standard library routines. */
+#include "readline.h"
+
+#include "rlprivate.h"
+#include "rlshell.h"
+#include "xmalloc.h"
+
+/* What kind of non-blocking I/O do we have? */
+#if !defined (O_NDELAY) && defined (O_NONBLOCK)
+#  define O_NDELAY O_NONBLOCK  /* Posix style */
+#endif
+
+/* Non-null means it is a pointer to a function to run while waiting for
+   character input. */
+rl_hook_func_t *rl_event_hook = (rl_hook_func_t *)NULL;
+
+rl_getc_func_t *rl_getc_function = rl_getc;
+
+static int _keyboard_input_timeout = 100000;           /* 0.1 seconds; it's in usec */
+
+static int ibuffer_space PARAMS((void));
+static int rl_get_char PARAMS((int *));
+static int rl_gather_tyi PARAMS((void));
+
+/* **************************************************************** */
+/*                                                                 */
+/*                     Character Input Buffering                   */
+/*                                                                 */
+/* **************************************************************** */
+
+static int pop_index, push_index;
+static unsigned char ibuffer[512];
+static int ibuffer_len = sizeof (ibuffer) - 1;
+
+#define any_typein (push_index != pop_index)
+
+int
+_rl_any_typein ()
+{
+  return any_typein;
+}
+
+/* Return the amount of space available in the buffer for stuffing
+   characters. */
+static int
+ibuffer_space ()
+{
+  if (pop_index > push_index)
+    return (pop_index - push_index - 1);
+  else
+    return (ibuffer_len - (push_index - pop_index));
+}
+
+/* Get a key from the buffer of characters to be read.
+   Return the key in KEY.
+   Result is KEY if there was a key, or 0 if there wasn't. */
+static int
+rl_get_char (key)
+     int *key;
+{
+  if (push_index == pop_index)
+    return (0);
+
+  *key = ibuffer[pop_index++];
+
+  if (pop_index >= ibuffer_len)
+    pop_index = 0;
+
+  return (1);
+}
+
+/* Stuff KEY into the *front* of the input buffer.
+   Returns non-zero if successful, zero if there is
+   no space left in the buffer. */
+int
+_rl_unget_char (key)
+     int key;
+{
+  if (ibuffer_space ())
+    {
+      pop_index--;
+      if (pop_index < 0)
+       pop_index = ibuffer_len - 1;
+      ibuffer[pop_index] = key;
+      return (1);
+    }
+  return (0);
+}
+
+int
+_rl_pushed_input_available ()
+{
+  return (push_index != pop_index);
+}
+
+/* If a character is available to be read, then read it and stuff it into
+   IBUFFER.  Otherwise, just return.  Returns number of characters read
+   (0 if none available) and -1 on error (EIO). */
+static int
+rl_gather_tyi ()
+{
+  int tty;
+  register int tem, result;
+  int chars_avail, k;
+  char input;
+#if defined(HAVE_SELECT)
+  fd_set readfds, exceptfds;
+  struct timeval timeout;
+#endif
+
+  tty = fileno (rl_instream);
+
+#if defined (HAVE_SELECT)
+  FD_ZERO (&readfds);
+  FD_ZERO (&exceptfds);
+  FD_SET (tty, &readfds);
+  FD_SET (tty, &exceptfds);
+  timeout.tv_sec = 0;
+  timeout.tv_usec = _keyboard_input_timeout;
+  result = select (tty + 1, &readfds, (fd_set *)NULL, &exceptfds, &timeout);
+  if (result <= 0)
+    return 0;  /* Nothing to read. */
+#endif
+
+  result = -1;
+#if defined (FIONREAD)
+  errno = 0;
+  result = ioctl (tty, FIONREAD, &chars_avail);
+  if (result == -1 && errno == EIO)
+    return -1;
+#endif
+
+#if defined (O_NDELAY)
+  if (result == -1)
+    {
+      tem = fcntl (tty, F_GETFL, 0);
+
+      fcntl (tty, F_SETFL, (tem | O_NDELAY));
+      chars_avail = read (tty, &input, 1);
+
+      fcntl (tty, F_SETFL, tem);
+      if (chars_avail == -1 && errno == EAGAIN)
+       return 0;
+    }
+#endif /* O_NDELAY */
+
+  /* If there's nothing available, don't waste time trying to read
+     something. */
+  if (chars_avail <= 0)
+    return 0;
+
+  tem = ibuffer_space ();
+
+  if (chars_avail > tem)
+    chars_avail = tem;
+
+  /* One cannot read all of the available input.  I can only read a single
+     character at a time, or else programs which require input can be
+     thwarted.  If the buffer is larger than one character, I lose.
+     Damn! */
+  if (tem < ibuffer_len)
+    chars_avail = 0;
+
+  if (result != -1)
+    {
+      while (chars_avail--)
+       {
+         k = (*rl_getc_function) (rl_instream);
+         rl_stuff_char (k);
+         if (k == NEWLINE || k == RETURN)
+           break;
+       }
+    }
+  else
+    {
+      if (chars_avail)
+       rl_stuff_char (input);
+    }
+
+  return 1;
+}
+
+int
+rl_set_keyboard_input_timeout (u)
+     int u;
+{
+  int o;
+
+  o = _keyboard_input_timeout;
+  if (u > 0)
+    _keyboard_input_timeout = u;
+  return (o);
+}
+
+/* Is there input available to be read on the readline input file
+   descriptor?  Only works if the system has select(2) or FIONREAD.
+   Uses the value of _keyboard_input_timeout as the timeout; if another
+   readline function wants to specify a timeout and not leave it up to
+   the user, it should use _rl_input_queued(timeout_value_in_microseconds)
+   instead. */
+int
+_rl_input_available ()
+{
+#if defined(HAVE_SELECT)
+  fd_set readfds, exceptfds;
+  struct timeval timeout;
+#endif
+#if !defined (HAVE_SELECT) && defined(FIONREAD)
+  int chars_avail;
+#endif
+  int tty;
+
+  tty = fileno (rl_instream);
+
+#if defined (HAVE_SELECT)
+  FD_ZERO (&readfds);
+  FD_ZERO (&exceptfds);
+  FD_SET (tty, &readfds);
+  FD_SET (tty, &exceptfds);
+  timeout.tv_sec = 0;
+  timeout.tv_usec = _keyboard_input_timeout;
+  return (select (tty + 1, &readfds, (fd_set *)NULL, &exceptfds, &timeout) > 0);
+#else
+
+#if defined (FIONREAD)
+  if (ioctl (tty, FIONREAD, &chars_avail) == 0)
+    return (chars_avail);
+#endif
+
+#endif
+
+  return 0;
+}
+
+int
+_rl_input_queued (t)
+     int t;
+{
+  int old_timeout, r;
+
+  old_timeout = rl_set_keyboard_input_timeout (t);
+  r = _rl_input_available ();
+  rl_set_keyboard_input_timeout (old_timeout);
+  return r;
+}
+
+void
+_rl_insert_typein (c)
+     int c;     
+{      
+  int key, t, i;
+  char *string;
+
+  i = key = 0;
+  string = (char *)xmalloc (ibuffer_len + 1);
+  string[i++] = (char) c;
+
+  while ((t = rl_get_char (&key)) &&
+        _rl_keymap[key].type == ISFUNC &&
+        _rl_keymap[key].function == rl_insert)
+    string[i++] = key;
+
+  if (t)
+    _rl_unget_char (key);
+
+  string[i] = '\0';
+  rl_insert_text (string);
+  free (string);
+}
+
+/* Add KEY to the buffer of characters to be read.  Returns 1 if the
+   character was stuffed correctly; 0 otherwise. */
+int
+rl_stuff_char (key)
+     int key;
+{
+  if (ibuffer_space () == 0)
+    return 0;
+
+  if (key == EOF)
+    {
+      key = NEWLINE;
+      rl_pending_input = EOF;
+      RL_SETSTATE (RL_STATE_INPUTPENDING);
+    }
+  ibuffer[push_index++] = key;
+  if (push_index >= ibuffer_len)
+    push_index = 0;
+
+  return 1;
+}
+
+/* Make C be the next command to be executed. */
+int
+rl_execute_next (c)
+     int c;
+{
+  rl_pending_input = c;
+  RL_SETSTATE (RL_STATE_INPUTPENDING);
+  return 0;
+}
+
+/* Clear any pending input pushed with rl_execute_next() */
+int
+rl_clear_pending_input ()
+{
+  rl_pending_input = 0;
+  RL_UNSETSTATE (RL_STATE_INPUTPENDING);
+  return 0;
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*                          Character Input                        */
+/*                                                                 */
+/* **************************************************************** */
+
+/* Read a key, including pending input. */
+int
+rl_read_key ()
+{
+  int c;
+
+  rl_key_sequence_length++;
+
+  if (rl_pending_input)
+    {
+      c = rl_pending_input;
+      rl_clear_pending_input ();
+    }
+  else
+    {
+      /* If input is coming from a macro, then use that. */
+      if (c = _rl_next_macro_key ())
+       return (c);
+
+      /* If the user has an event function, then call it periodically. */
+      if (rl_event_hook)
+       {
+         while (rl_event_hook && rl_get_char (&c) == 0)
+           {
+             (*rl_event_hook) ();
+             if (rl_done)              /* XXX - experimental */
+               return ('\n');
+             if (rl_gather_tyi () < 0) /* XXX - EIO */
+               {
+                 rl_done = 1;
+                 return ('\n');
+               }
+           }
+       }
+      else
+       {
+         if (rl_get_char (&c) == 0)
+           c = (*rl_getc_function) (rl_instream);
+       }
+    }
+
+  return (c);
+}
+
+int
+rl_getc (stream)
+     FILE *stream;
+{
+  int result;
+  unsigned char c;
+
+  while (1)
+    {
+      result = read (fileno (stream), &c, sizeof (unsigned char));
+
+      if (result == sizeof (unsigned char))
+       return (c);
+
+      /* If zero characters are returned, then the file that we are
+        reading from is empty!  Return EOF in that case. */
+      if (result == 0)
+       return (EOF);
+
+#if defined (__BEOS__)
+      if (errno == EINTR)
+       continue;
+#endif
+
+#if defined (EWOULDBLOCK)
+#  define X_EWOULDBLOCK EWOULDBLOCK
+#else
+#  define X_EWOULDBLOCK -99
+#endif
+
+#if defined (EAGAIN)
+#  define X_EAGAIN EAGAIN
+#else
+#  define X_EAGAIN -99
+#endif
+
+      if (errno == X_EWOULDBLOCK || errno == X_EAGAIN)
+       {
+         if (sh_unset_nodelay_mode (fileno (stream)) < 0)
+           return (EOF);
+         continue;
+       }
+
+#undef X_EWOULDBLOCK
+#undef X_EAGAIN
+
+      /* If the error that we received was SIGINT, then try again,
+        this is simply an interrupted system call to read ().
+        Otherwise, some error ocurred, also signifying EOF. */
+      if (errno != EINTR)
+       return (EOF);
+    }
+}
+
+#if defined (HANDLE_MULTIBYTE)
+/* read multibyte char */
+int
+_rl_read_mbchar (mbchar, size)
+     char *mbchar;
+     int size;
+{
+  int mb_len = 0;
+  size_t mbchar_bytes_length;
+  wchar_t wc;
+  mbstate_t ps, ps_back;
+
+  memset(&ps, 0, sizeof (mbstate_t));
+  memset(&ps_back, 0, sizeof (mbstate_t));
+  
+  while (mb_len < size)
+    {
+      RL_SETSTATE(RL_STATE_MOREINPUT);
+      mbchar[mb_len++] = rl_read_key ();
+      RL_UNSETSTATE(RL_STATE_MOREINPUT);
+
+      mbchar_bytes_length = mbrtowc (&wc, mbchar, mb_len, &ps);
+      if (mbchar_bytes_length == (size_t)(-1))
+       break;          /* invalid byte sequence for the current locale */
+      else if (mbchar_bytes_length == (size_t)(-2))
+       {
+         /* shorted bytes */
+         ps = ps_back;
+         continue;
+       } 
+      else if (mbchar_bytes_length > (size_t)(0))
+       break;
+    }
+
+  return mb_len;
+}
+
+/* Read a multibyte-character string whose first character is FIRST into
+   the buffer MB of length MBLEN.  Returns the last character read, which
+   may be FIRST.  Used by the search functions, among others.  Very similar
+   to _rl_read_mbchar. */
+int
+_rl_read_mbstring (first, mb, mblen)
+     int first;
+     char *mb;
+     int mblen;
+{
+  int i, c;
+  mbstate_t ps;
+
+  c = first;
+  memset (mb, 0, mblen);
+  for (i = 0; i < mblen; i++)
+    {
+      mb[i] = (char)c;
+      memset (&ps, 0, sizeof (mbstate_t));
+      if (_rl_get_char_len (mb, &ps) == -2)
+       {
+         /* Read more for multibyte character */
+         RL_SETSTATE (RL_STATE_MOREINPUT);
+         c = rl_read_key ();
+         RL_UNSETSTATE (RL_STATE_MOREINPUT);
+       }
+      else
+       break;
+    }
+  return c;
+}
+#endif /* HANDLE_MULTIBYTE */
index 54ef1f3..737f767 100644 (file)
    59 Temple Place, Suite 330, Boston, MA 02111 USA. */
 #define READLINE_LIBRARY
 
+#if defined (__TANDEM)
+#  include <floss.h>
+#endif
+
 #include "rlconf.h"
 
 #if defined (HAVE_CONFIG_H)
diff --git a/lib/readline/parens.c~ b/lib/readline/parens.c~
new file mode 100644 (file)
index 0000000..54ef1f3
--- /dev/null
@@ -0,0 +1,179 @@
+/* parens.c -- Implementation of matching parentheses feature. */
+
+/* Copyright (C) 1987, 1989, 1992 Free Software Foundation, Inc.
+
+   This file is part of the GNU Readline Library, a library for
+   reading lines of text with interactive input and history editing.
+
+   The GNU Readline Library is free software; you can redistribute it
+   and/or modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2, or
+   (at your option) any later version.
+
+   The GNU Readline Library is distributed in the hope that it will be
+   useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+   of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   The GNU General Public License is often shipped with GNU software, and
+   is generally kept in a file called COPYING or LICENSE.  If you do not
+   have a copy of the license, write to the Free Software Foundation,
+   59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+#define READLINE_LIBRARY
+
+#include "rlconf.h"
+
+#if defined (HAVE_CONFIG_H)
+#  include <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#if defined (HAVE_UNISTD_H)
+#  include <unistd.h>
+#endif
+
+#if defined (FD_SET) && !defined (HAVE_SELECT)
+#  define HAVE_SELECT
+#endif
+
+#if defined (HAVE_SELECT)
+#  include <sys/time.h>
+#endif /* HAVE_SELECT */
+#if defined (HAVE_SYS_SELECT_H)
+#  include <sys/select.h>
+#endif
+
+#if defined (HAVE_STRING_H)
+#  include <string.h>
+#else /* !HAVE_STRING_H */
+#  include <strings.h>
+#endif /* !HAVE_STRING_H */
+
+#if !defined (strchr) && !defined (__STDC__)
+extern char *strchr (), *strrchr ();
+#endif /* !strchr && !__STDC__ */
+
+#include "readline.h"
+#include "rlprivate.h"
+
+static int find_matching_open PARAMS((char *, int, int));
+
+/* Non-zero means try to blink the matching open parenthesis when the
+   close parenthesis is inserted. */
+#if defined (HAVE_SELECT)
+int rl_blink_matching_paren = 1;
+#else /* !HAVE_SELECT */
+int rl_blink_matching_paren = 0;
+#endif /* !HAVE_SELECT */
+
+static int _paren_blink_usec = 500000;
+
+/* Change emacs_standard_keymap to have bindings for paren matching when
+   ON_OR_OFF is 1, change them back to self_insert when ON_OR_OFF == 0. */
+void
+_rl_enable_paren_matching (on_or_off)
+     int on_or_off;
+{
+  if (on_or_off)
+    {  /* ([{ */
+      rl_bind_key_in_map (')', rl_insert_close, emacs_standard_keymap);
+      rl_bind_key_in_map (']', rl_insert_close, emacs_standard_keymap);
+      rl_bind_key_in_map ('}', rl_insert_close, emacs_standard_keymap);
+    }
+  else
+    {  /* ([{ */
+      rl_bind_key_in_map (')', rl_insert, emacs_standard_keymap);
+      rl_bind_key_in_map (']', rl_insert, emacs_standard_keymap);
+      rl_bind_key_in_map ('}', rl_insert, emacs_standard_keymap);
+    }
+}
+
+int
+rl_set_paren_blink_timeout (u)
+     int u;
+{
+  int o;
+
+  o = _paren_blink_usec;
+  if (u > 0)
+    _paren_blink_usec = u;
+  return (o);
+}
+
+int
+rl_insert_close (count, invoking_key)
+     int count, invoking_key;
+{
+  if (rl_explicit_arg || !rl_blink_matching_paren)
+    _rl_insert_char (count, invoking_key);
+  else
+    {
+#if defined (HAVE_SELECT)
+      int orig_point, match_point, ready;
+      struct timeval timer;
+      fd_set readfds;
+
+      _rl_insert_char (1, invoking_key);
+      (*rl_redisplay_function) ();
+      match_point =
+       find_matching_open (rl_line_buffer, rl_point - 2, invoking_key);
+
+      /* Emacs might message or ring the bell here, but I don't. */
+      if (match_point < 0)
+       return -1;
+
+      FD_ZERO (&readfds);
+      FD_SET (fileno (rl_instream), &readfds);
+      timer.tv_sec = 0;
+      timer.tv_usec = _paren_blink_usec;
+
+      orig_point = rl_point;
+      rl_point = match_point;
+      (*rl_redisplay_function) ();
+      ready = select (1, &readfds, (fd_set *)NULL, (fd_set *)NULL, &timer);
+      rl_point = orig_point;
+#else /* !HAVE_SELECT */
+      _rl_insert_char (count, invoking_key);
+#endif /* !HAVE_SELECT */
+    }
+  return 0;
+}
+
+static int
+find_matching_open (string, from, closer)
+     char *string;
+     int from, closer;
+{
+  register int i;
+  int opener, level, delimiter;
+
+  switch (closer)
+    {
+    case ']': opener = '['; break;
+    case '}': opener = '{'; break;
+    case ')': opener = '('; break;
+    default:
+      return (-1);
+    }
+
+  level = 1;                   /* The closer passed in counts as 1. */
+  delimiter = 0;               /* Delimited state unknown. */
+
+  for (i = from; i > -1; i--)
+    {
+      if (delimiter && (string[i] == delimiter))
+       delimiter = 0;
+      else if (rl_basic_quote_characters && strchr (rl_basic_quote_characters, string[i]))
+       delimiter = string[i];
+      else if (!delimiter && (string[i] == closer))
+       level++;
+      else if (!delimiter && (string[i] == opener))
+       level--;
+
+      if (!level)
+       break;
+    }
+  return (i);
+}
index 253d1df..4cb542f 100644 (file)
@@ -95,7 +95,7 @@ static int iso8601wknum(const struct tm *timeptr);
 #if !defined(OS2) && !defined(MSDOS) && defined(HAVE_TZNAME)
 extern char *tzname[2];
 extern int daylight;
-#if defined(SOLARIS) || defined(mips)
+#if defined(SOLARIS) || defined(mips) || defined (M_UNIX)
 extern long int timezone, altzone;
 #else
 extern int timezone, altzone;
diff --git a/lib/sh/strftime.c~ b/lib/sh/strftime.c~
new file mode 100644 (file)
index 0000000..253d1df
--- /dev/null
@@ -0,0 +1,859 @@
+/*
+ * Modified slightly by Chet Ramey for inclusion in Bash
+ */
+
+/*
+ * strftime.c
+ *
+ * Public-domain implementation of ISO C library routine.
+ *
+ * If you can't do prototypes, get GCC.
+ *
+ * The C99 standard now specifies just about all of the formats
+ * that were additional in the earlier versions of this file.
+ *
+ * For extensions from SunOS, add SUNOS_EXT.
+ * For extensions from HP/UX, add HPUX_EXT.
+ * For VMS dates, add VMS_EXT.
+ * For complete POSIX semantics, add POSIX_SEMANTICS.
+ *
+ * The code for %c, %x, and %X follows the C99 specification for
+ * the "C" locale.
+ *
+ * This version ignores LOCALE information.
+ * It also doesn't worry about multi-byte characters.
+ * So there.
+ *
+ * This file is also shipped with GAWK (GNU Awk), gawk specific bits of
+ * code are included if GAWK is defined.
+ *
+ * Arnold Robbins
+ * January, February, March, 1991
+ * Updated March, April 1992
+ * Updated April, 1993
+ * Updated February, 1994
+ * Updated May, 1994
+ * Updated January, 1995
+ * Updated September, 1995
+ * Updated January, 1996
+ * Updated July, 1997
+ * Updated October, 1999
+ * Updated September, 2000
+ *
+ * Fixes from ado@elsie.nci.nih.gov,
+ * February 1991, May 1992
+ * Fixes from Tor Lillqvist tml@tik.vtt.fi,
+ * May 1993
+ * Further fixes from ado@elsie.nci.nih.gov,
+ * February 1994
+ * %z code from chip@chinacat.unicom.com,
+ * Applied September 1995
+ * %V code fixed (again) and %G, %g added,
+ * January 1996
+ * %v code fixed, better configuration,
+ * July 1997
+ * Moved to C99 specification.
+ * September 2000
+ */
+#include <config.h>
+
+#ifndef GAWK
+#include <stdio.h>
+#include <ctype.h>
+#include <time.h>
+#endif
+#if defined(TM_IN_SYS_TIME)
+#include <sys/types.h>
+#include <sys/time.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+/* defaults: season to taste */
+#define SUNOS_EXT      1       /* stuff in SunOS strftime routine */
+#define VMS_EXT                1       /* include %v for VMS date format */
+#define HPUX_EXT       1       /* non-conflicting stuff in HP-UX date */
+#ifndef GAWK
+#define POSIX_SEMANTICS        1       /* call tzset() if TZ changes */
+#endif
+
+#undef strchr  /* avoid AIX weirdness */
+
+extern void tzset(void);
+static int weeknumber(const struct tm *timeptr, int firstweekday);
+static int iso8601wknum(const struct tm *timeptr);
+
+#ifdef __GNUC__
+#define inline __inline__
+#else
+#define inline /**/
+#endif
+
+#define range(low, item, hi)   max(low, min(item, hi))
+
+#if !defined(OS2) && !defined(MSDOS) && defined(HAVE_TZNAME)
+extern char *tzname[2];
+extern int daylight;
+#if defined(SOLARIS) || defined(mips)
+extern long int timezone, altzone;
+#else
+extern int timezone, altzone;
+#endif
+#endif
+
+#undef min     /* just in case */
+
+/* min --- return minimum of two numbers */
+
+static inline int
+min(int a, int b)
+{
+       return (a < b ? a : b);
+}
+
+#undef max     /* also, just in case */
+
+/* max --- return maximum of two numbers */
+
+static inline int
+max(int a, int b)
+{
+       return (a > b ? a : b);
+}
+
+/* strftime --- produce formatted time */
+
+size_t
+strftime(char *s, size_t maxsize, const char *format, const struct tm *timeptr)
+{
+       char *endp = s + maxsize;
+       char *start = s;
+       auto char tbuf[100];
+       long off;
+       int i, w, y;
+       static short first = 1;
+#ifdef POSIX_SEMANTICS
+       static char *savetz = NULL;
+       static int savetzlen = 0;
+       char *tz;
+#endif /* POSIX_SEMANTICS */
+#ifndef HAVE_TM_ZONE
+#ifndef HAVE_TM_NAME
+#ifndef HAVE_TZNAME
+       extern char *timezone();
+       struct timeval tv;
+       struct timezone zone;
+#endif /* HAVE_TZNAME */
+#endif /* HAVE_TM_NAME */
+#endif /* HAVE_TM_ZONE */
+
+       /* various tables, useful in North America */
+       static const char *days_a[] = {
+               "Sun", "Mon", "Tue", "Wed",
+               "Thu", "Fri", "Sat",
+       };
+       static const char *days_l[] = {
+               "Sunday", "Monday", "Tuesday", "Wednesday",
+               "Thursday", "Friday", "Saturday",
+       };
+       static const char *months_a[] = {
+               "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+               "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+       };
+       static const char *months_l[] = {
+               "January", "February", "March", "April",
+               "May", "June", "July", "August", "September",
+               "October", "November", "December",
+       };
+       static const char *ampm[] = { "AM", "PM", };
+
+       if (s == NULL || format == NULL || timeptr == NULL || maxsize == 0)
+               return 0;
+
+       /* quick check if we even need to bother */
+       if (strchr(format, '%') == NULL && strlen(format) + 1 >= maxsize)
+               return 0;
+
+#ifndef POSIX_SEMANTICS
+       if (first) {
+               tzset();
+               first = 0;
+       }
+#else  /* POSIX_SEMANTICS */
+#if defined (SHELL)
+       tz = get_string_value ("TZ");
+#else
+       tz = getenv("TZ");
+#endif
+       if (first) {
+               if (tz != NULL) {
+                       int tzlen = strlen(tz);
+
+                       savetz = (char *) malloc(tzlen + 1);
+                       if (savetz != NULL) {
+                               savetzlen = tzlen + 1;
+                               strcpy(savetz, tz);
+                       }
+               }
+               tzset();
+               first = 0;
+       }
+       /* if we have a saved TZ, and it is different, recapture and reset */
+       if (tz && savetz && (tz[0] != savetz[0] || strcmp(tz, savetz) != 0)) {
+               i = strlen(tz) + 1;
+               if (i > savetzlen) {
+                       savetz = (char *) realloc(savetz, i);
+                       if (savetz) {
+                               savetzlen = i;
+                               strcpy(savetz, tz);
+                       }
+               } else
+                       strcpy(savetz, tz);
+               tzset();
+       }
+#endif /* POSIX_SEMANTICS */
+
+       for (; *format && s < endp - 1; format++) {
+               tbuf[0] = '\0';
+               if (*format != '%') {
+                       *s++ = *format;
+                       continue;
+               }
+       again:
+               switch (*++format) {
+               case '\0':
+                       *s++ = '%';
+                       goto out;
+
+               case '%':
+                       *s++ = '%';
+                       continue;
+
+               case 'a':       /* abbreviated weekday name */
+                       if (timeptr->tm_wday < 0 || timeptr->tm_wday > 6)
+                               strcpy(tbuf, "?");
+                       else
+                               strcpy(tbuf, days_a[timeptr->tm_wday]);
+                       break;
+
+               case 'A':       /* full weekday name */
+                       if (timeptr->tm_wday < 0 || timeptr->tm_wday > 6)
+                               strcpy(tbuf, "?");
+                       else
+                               strcpy(tbuf, days_l[timeptr->tm_wday]);
+                       break;
+
+               case 'b':       /* abbreviated month name */
+               short_month:
+                       if (timeptr->tm_mon < 0 || timeptr->tm_mon > 11)
+                               strcpy(tbuf, "?");
+                       else
+                               strcpy(tbuf, months_a[timeptr->tm_mon]);
+                       break;
+
+               case 'B':       /* full month name */
+                       if (timeptr->tm_mon < 0 || timeptr->tm_mon > 11)
+                               strcpy(tbuf, "?");
+                       else
+                               strcpy(tbuf, months_l[timeptr->tm_mon]);
+                       break;
+
+               case 'c':       /* appropriate date and time representation */
+                       /*
+                        * This used to be:
+                        *
+                        * strftime(tbuf, sizeof tbuf, "%a %b %e %H:%M:%S %Y", timeptr);
+                        *
+                        * Now, per the ISO 1999 C standard, it this:
+                        */
+                       strftime(tbuf, sizeof tbuf, "%A %B %d %T %Y", timeptr);
+                       break;
+
+               case 'C':
+               century:
+                       sprintf(tbuf, "%02d", (timeptr->tm_year + 1900) / 100);
+                       break;
+
+               case 'd':       /* day of the month, 01 - 31 */
+                       i = range(1, timeptr->tm_mday, 31);
+                       sprintf(tbuf, "%02d", i);
+                       break;
+
+               case 'D':       /* date as %m/%d/%y */
+                       strftime(tbuf, sizeof tbuf, "%m/%d/%y", timeptr);
+                       break;
+
+               case 'e':       /* day of month, blank padded */
+                       sprintf(tbuf, "%2d", range(1, timeptr->tm_mday, 31));
+                       break;
+
+               case 'E':
+                       /* POSIX (now C99) locale extensions, ignored for now */
+                       goto again;
+
+               case 'F':       /* ISO 8601 date representation */
+                       strftime(tbuf, sizeof tbuf, "%Y-%m-%d", timeptr);
+                       break;
+
+               case 'g':
+               case 'G':
+                       /*
+                        * Year of ISO week.
+                        *
+                        * If it's December but the ISO week number is one,
+                        * that week is in next year.
+                        * If it's January but the ISO week number is 52 or
+                        * 53, that week is in last year.
+                        * Otherwise, it's this year.
+                        */
+                       w = iso8601wknum(timeptr);
+                       if (timeptr->tm_mon == 11 && w == 1)
+                               y = 1900 + timeptr->tm_year + 1;
+                       else if (timeptr->tm_mon == 0 && w >= 52)
+                               y = 1900 + timeptr->tm_year - 1;
+                       else
+                               y = 1900 + timeptr->tm_year;
+
+                       if (*format == 'G')
+                               sprintf(tbuf, "%d", y);
+                       else
+                               sprintf(tbuf, "%02d", y % 100);
+                       break;
+
+               case 'h':       /* abbreviated month name */
+                       goto short_month;
+
+               case 'H':       /* hour, 24-hour clock, 00 - 23 */
+                       i = range(0, timeptr->tm_hour, 23);
+                       sprintf(tbuf, "%02d", i);
+                       break;
+
+               case 'I':       /* hour, 12-hour clock, 01 - 12 */
+                       i = range(0, timeptr->tm_hour, 23);
+                       if (i == 0)
+                               i = 12;
+                       else if (i > 12)
+                               i -= 12;
+                       sprintf(tbuf, "%02d", i);
+                       break;
+
+               case 'j':       /* day of the year, 001 - 366 */
+                       sprintf(tbuf, "%03d", timeptr->tm_yday + 1);
+                       break;
+
+               case 'm':       /* month, 01 - 12 */
+                       i = range(0, timeptr->tm_mon, 11);
+                       sprintf(tbuf, "%02d", i + 1);
+                       break;
+
+               case 'M':       /* minute, 00 - 59 */
+                       i = range(0, timeptr->tm_min, 59);
+                       sprintf(tbuf, "%02d", i);
+                       break;
+
+               case 'n':       /* same as \n */
+                       tbuf[0] = '\n';
+                       tbuf[1] = '\0';
+                       break;
+
+               case 'O':
+                       /* POSIX (now C99) locale extensions, ignored for now */
+                       goto again;
+
+               case 'p':       /* am or pm based on 12-hour clock */
+                       i = range(0, timeptr->tm_hour, 23);
+                       if (i < 12)
+                               strcpy(tbuf, ampm[0]);
+                       else
+                               strcpy(tbuf, ampm[1]);
+                       break;
+
+               case 'r':       /* time as %I:%M:%S %p */
+                       strftime(tbuf, sizeof tbuf, "%I:%M:%S %p", timeptr);
+                       break;
+
+               case 'R':       /* time as %H:%M */
+                       strftime(tbuf, sizeof tbuf, "%H:%M", timeptr);
+                       break;
+
+#if defined(HAVE_MKTIME) || defined(GAWK)
+               case 's':       /* time as seconds since the Epoch */
+               {
+                       struct tm non_const_timeptr;
+
+                       non_const_timeptr = *timeptr;
+                       sprintf(tbuf, "%ld", mktime(& non_const_timeptr));
+                       break;
+               }
+#endif /* defined(HAVE_MKTIME) || defined(GAWK) */
+
+               case 'S':       /* second, 00 - 60 */
+                       i = range(0, timeptr->tm_sec, 60);
+                       sprintf(tbuf, "%02d", i);
+                       break;
+
+               case 't':       /* same as \t */
+                       tbuf[0] = '\t';
+                       tbuf[1] = '\0';
+                       break;
+
+               case 'T':       /* time as %H:%M:%S */
+               the_time:
+                       strftime(tbuf, sizeof tbuf, "%H:%M:%S", timeptr);
+                       break;
+
+               case 'u':
+               /* ISO 8601: Weekday as a decimal number [1 (Monday) - 7] */
+                       sprintf(tbuf, "%d", timeptr->tm_wday == 0 ? 7 :
+                                       timeptr->tm_wday);
+                       break;
+
+               case 'U':       /* week of year, Sunday is first day of week */
+                       sprintf(tbuf, "%02d", weeknumber(timeptr, 0));
+                       break;
+
+               case 'V':       /* week of year according ISO 8601 */
+                       sprintf(tbuf, "%02d", iso8601wknum(timeptr));
+                       break;
+
+               case 'w':       /* weekday, Sunday == 0, 0 - 6 */
+                       i = range(0, timeptr->tm_wday, 6);
+                       sprintf(tbuf, "%d", i);
+                       break;
+
+               case 'W':       /* week of year, Monday is first day of week */
+                       sprintf(tbuf, "%02d", weeknumber(timeptr, 1));
+                       break;
+
+               case 'x':       /* appropriate date representation */
+                       strftime(tbuf, sizeof tbuf, "%A %B %d %Y", timeptr);
+                       break;
+
+               case 'X':       /* appropriate time representation */
+                       goto the_time;
+                       break;
+
+               case 'y':       /* year without a century, 00 - 99 */
+               year:
+                       i = timeptr->tm_year % 100;
+                       sprintf(tbuf, "%02d", i);
+                       break;
+
+               case 'Y':       /* year with century */
+               fullyear:
+                       sprintf(tbuf, "%d", 1900 + timeptr->tm_year);
+                       break;
+
+               /*
+                * From: Chip Rosenthal <chip@chinacat.unicom.com>
+                * Date: Sun, 19 Mar 1995 00:33:29 -0600 (CST)
+                * 
+                * Warning: the %z [code] is implemented by inspecting the
+                * timezone name conditional compile settings, and
+                * inferring a method to get timezone offsets. I've tried
+                * this code on a couple of machines, but I don't doubt
+                * there is some system out there that won't like it.
+                * Maybe the easiest thing to do would be to bracket this
+                * with an #ifdef that can turn it off. The %z feature
+                * would be an admittedly obscure one that most folks can
+                * live without, but it would be a great help to those of
+                * us that muck around with various message processors.
+                */
+               case 'z':       /* time zone offset east of GMT e.g. -0600 */
+#ifdef HAVE_TM_NAME
+                       /*
+                        * Systems with tm_name probably have tm_tzadj as
+                        * secs west of GMT.  Convert to mins east of GMT.
+                        */
+                       off = -timeptr->tm_tzadj / 60;
+#else /* !HAVE_TM_NAME */
+#ifdef HAVE_TM_ZONE
+                       /*
+                        * Systems with tm_zone probably have tm_gmtoff as
+                        * secs east of GMT.  Convert to mins east of GMT.
+                        */
+                       off = timeptr->tm_gmtoff / 60;
+#else /* !HAVE_TM_ZONE */
+#if HAVE_TZNAME
+                       /*
+                        * Systems with tzname[] probably have timezone as
+                        * secs west of GMT.  Convert to mins east of GMT.
+                        */
+                       off = -(daylight ? timezone : altzone) / 60;
+#else /* !HAVE_TZNAME */
+                       off = -zone.tz_minuteswest;
+#endif /* !HAVE_TZNAME */
+#endif /* !HAVE_TM_ZONE */
+#endif /* !HAVE_TM_NAME */
+                       if (off < 0) {
+                               tbuf[0] = '-';
+                               off = -off;
+                       } else {
+                               tbuf[0] = '+';
+                       }
+                       sprintf(tbuf+1, "%02d%02d", off/60, off%60);
+                       break;
+
+               case 'Z':       /* time zone name or abbrevation */
+#ifdef HAVE_TZNAME
+                       i = (daylight && timeptr->tm_isdst > 0); /* 0 or 1 */
+                       strcpy(tbuf, tzname[i]);
+#else
+#ifdef HAVE_TM_ZONE
+                       strcpy(tbuf, timeptr->tm_zone);
+#else
+#ifdef HAVE_TM_NAME
+                       strcpy(tbuf, timeptr->tm_name);
+#else
+                       gettimeofday(& tv, & zone);
+                       strcpy(tbuf, timezone(zone.tz_minuteswest,
+                                               timeptr->tm_isdst > 0));
+#endif /* HAVE_TM_NAME */
+#endif /* HAVE_TM_ZONE */
+#endif /* HAVE_TZNAME */
+                       break;
+
+#ifdef SUNOS_EXT
+               case 'k':       /* hour, 24-hour clock, blank pad */
+                       sprintf(tbuf, "%2d", range(0, timeptr->tm_hour, 23));
+                       break;
+
+               case 'l':       /* hour, 12-hour clock, 1 - 12, blank pad */
+                       i = range(0, timeptr->tm_hour, 23);
+                       if (i == 0)
+                               i = 12;
+                       else if (i > 12)
+                               i -= 12;
+                       sprintf(tbuf, "%2d", i);
+                       break;
+#endif
+
+#ifdef HPUX_EXT
+               case 'N':       /* Emperor/Era name */
+                       /* this is essentially the same as the century */
+                       goto century;   /* %C */
+
+               case 'o':       /* Emperor/Era year */
+                       goto year;      /* %y */
+#endif /* HPUX_EXT */
+
+
+#ifdef VMS_EXT
+               case 'v':       /* date as dd-bbb-YYYY */
+                       sprintf(tbuf, "%2d-%3.3s-%4d",
+                               range(1, timeptr->tm_mday, 31),
+                               months_a[range(0, timeptr->tm_mon, 11)],
+                               timeptr->tm_year + 1900);
+                       for (i = 3; i < 6; i++)
+                               if (islower(tbuf[i]))
+                                       tbuf[i] = toupper(tbuf[i]);
+                       break;
+#endif
+
+               default:
+                       tbuf[0] = '%';
+                       tbuf[1] = *format;
+                       tbuf[2] = '\0';
+                       break;
+               }
+               i = strlen(tbuf);
+               if (i) {
+                       if (s + i < endp - 1) {
+                               strcpy(s, tbuf);
+                               s += i;
+                       } else
+                               return 0;
+               }
+       }
+out:
+       if (s < endp && *format == '\0') {
+               *s = '\0';
+               return (s - start);
+       } else
+               return 0;
+}
+
+/* isleap --- is a year a leap year? */
+
+static int
+isleap(int year)
+{
+       return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
+}
+
+
+/* iso8601wknum --- compute week number according to ISO 8601 */
+
+static int
+iso8601wknum(const struct tm *timeptr)
+{
+       /*
+        * From 1003.2:
+        *      If the week (Monday to Sunday) containing January 1
+        *      has four or more days in the new year, then it is week 1;
+        *      otherwise it is the highest numbered week of the previous
+        *      year (52 or 53), and the next week is week 1.
+        *
+        * ADR: This means if Jan 1 was Monday through Thursday,
+        *      it was week 1, otherwise week 52 or 53.
+        *
+        * XPG4 erroneously included POSIX.2 rationale text in the
+        * main body of the standard. Thus it requires week 53.
+        */
+
+       int weeknum, jan1day, diff;
+
+       /* get week number, Monday as first day of the week */
+       weeknum = weeknumber(timeptr, 1);
+
+       /*
+        * With thanks and tip of the hatlo to tml@tik.vtt.fi
+        *
+        * What day of the week does January 1 fall on?
+        * We know that
+        *      (timeptr->tm_yday - jan1.tm_yday) MOD 7 ==
+        *              (timeptr->tm_wday - jan1.tm_wday) MOD 7
+        * and that
+        *      jan1.tm_yday == 0
+        * and that
+        *      timeptr->tm_wday MOD 7 == timeptr->tm_wday
+        * from which it follows that. . .
+        */
+       jan1day = timeptr->tm_wday - (timeptr->tm_yday % 7);
+       if (jan1day < 0)
+               jan1day += 7;
+
+       /*
+        * If Jan 1 was a Monday through Thursday, it was in
+        * week 1.  Otherwise it was last year's highest week, which is
+        * this year's week 0.
+        *
+        * What does that mean?
+        * If Jan 1 was Monday, the week number is exactly right, it can
+        *      never be 0.
+        * If it was Tuesday through Thursday, the weeknumber is one
+        *      less than it should be, so we add one.
+        * Otherwise, Friday, Saturday or Sunday, the week number is
+        * OK, but if it is 0, it needs to be 52 or 53.
+        */
+       switch (jan1day) {
+       case 1:         /* Monday */
+               break;
+       case 2:         /* Tuesday */
+       case 3:         /* Wednesday */
+       case 4:         /* Thursday */
+               weeknum++;
+               break;
+       case 5:         /* Friday */
+       case 6:         /* Saturday */
+       case 0:         /* Sunday */
+               if (weeknum == 0) {
+#ifdef USE_BROKEN_XPG4
+                       /* XPG4 (as of March 1994) says 53 unconditionally */
+                       weeknum = 53;
+#else
+                       /* get week number of last week of last year */
+                       struct tm dec31ly;      /* 12/31 last year */
+                       dec31ly = *timeptr;
+                       dec31ly.tm_year--;
+                       dec31ly.tm_mon = 11;
+                       dec31ly.tm_mday = 31;
+                       dec31ly.tm_wday = (jan1day == 0) ? 6 : jan1day - 1;
+                       dec31ly.tm_yday = 364 + isleap(dec31ly.tm_year + 1900);
+                       weeknum = iso8601wknum(& dec31ly);
+#endif
+               }
+               break;
+       }
+
+       if (timeptr->tm_mon == 11) {
+               /*
+                * The last week of the year
+                * can be in week 1 of next year.
+                * Sigh.
+                *
+                * This can only happen if
+                *      M   T  W
+                *      29  30 31
+                *      30  31
+                *      31
+                */
+               int wday, mday;
+
+               wday = timeptr->tm_wday;
+               mday = timeptr->tm_mday;
+               if (   (wday == 1 && (mday >= 29 && mday <= 31))
+                   || (wday == 2 && (mday == 30 || mday == 31))
+                   || (wday == 3 &&  mday == 31))
+                       weeknum = 1;
+       }
+
+       return weeknum;
+}
+
+/* weeknumber --- figure how many weeks into the year */
+
+/* With thanks and tip of the hatlo to ado@elsie.nci.nih.gov */
+
+static int
+weeknumber(const struct tm *timeptr, int firstweekday)
+{
+       int wday = timeptr->tm_wday;
+       int ret;
+
+       if (firstweekday == 1) {
+               if (wday == 0)  /* sunday */
+                       wday = 6;
+               else
+                       wday--;
+       }
+       ret = ((timeptr->tm_yday + 7 - wday) / 7);
+       if (ret < 0)
+               ret = 0;
+       return ret;
+}
+
+#if 0
+/* ADR --- I'm loathe to mess with ado's code ... */
+
+Date:         Wed, 24 Apr 91 20:54:08 MDT
+From: Michal Jaegermann <audfax!emory!vm.ucs.UAlberta.CA!NTOMCZAK>
+To: arnold@audiofax.com
+
+Hi Arnold,
+in a process of fixing of strftime() in libraries on Atari ST I grabbed
+some pieces of code from your own strftime.  When doing that it came
+to mind that your weeknumber() function compiles a little bit nicer
+in the following form:
+/*
+ * firstweekday is 0 if starting in Sunday, non-zero if in Monday
+ */
+{
+    return (timeptr->tm_yday - timeptr->tm_wday +
+           (firstweekday ? (timeptr->tm_wday ? 8 : 1) : 7)) / 7;
+}
+How nicer it depends on a compiler, of course, but always a tiny bit.
+
+   Cheers,
+   Michal
+   ntomczak@vm.ucs.ualberta.ca
+#endif
+
+#ifdef TEST_STRFTIME
+
+/*
+ * NAME:
+ *     tst
+ *
+ * SYNOPSIS:
+ *     tst
+ *
+ * DESCRIPTION:
+ *     "tst" is a test driver for the function "strftime".
+ *
+ * OPTIONS:
+ *     None.
+ *
+ * AUTHOR:
+ *     Karl Vogel
+ *     Control Data Systems, Inc.
+ *     vogelke@c-17igp.wpafb.af.mil
+ *
+ * BUGS:
+ *     None noticed yet.
+ *
+ * COMPILE:
+ *     cc -o tst -DTEST_STRFTIME strftime.c
+ */
+
+/* ADR: I reformatted this to my liking, and deleted some unneeded code. */
+
+#ifndef NULL
+#include       <stdio.h>
+#endif
+#include       <sys/time.h>
+#include       <string.h>
+
+#define                MAXTIME         132
+
+/*
+ * Array of time formats.
+ */
+
+static char *array[] =
+{
+       "(%%A)      full weekday name, var length (Sunday..Saturday)  %A",
+       "(%%B)       full month name, var length (January..December)  %B",
+       "(%%C)                                               Century  %C",
+       "(%%D)                                       date (%%m/%%d/%%y)  %D",
+       "(%%E)                           Locale extensions (ignored)  %E",
+       "(%%F)       full month name, var length (January..December)  %F",
+       "(%%H)                          hour (24-hour clock, 00..23)  %H",
+       "(%%I)                          hour (12-hour clock, 01..12)  %I",
+       "(%%M)                                       minute (00..59)  %M",
+       "(%%N)                                      Emporer/Era Name  %N",
+       "(%%O)                           Locale extensions (ignored)  %O",
+       "(%%R)                                 time, 24-hour (%%H:%%M)  %R",
+       "(%%S)                                       second (00..60)  %S",
+       "(%%T)                              time, 24-hour (%%H:%%M:%%S)  %T",
+       "(%%U)    week of year, Sunday as first day of week (00..53)  %U",
+       "(%%V)                    week of year according to ISO 8601  %V",
+       "(%%W)    week of year, Monday as first day of week (00..53)  %W",
+       "(%%X)     appropriate locale time representation (%H:%M:%S)  %X",
+       "(%%Y)                           year with century (1970...)  %Y",
+       "(%%Z) timezone (EDT), or blank if timezone not determinable  %Z",
+       "(%%a)          locale's abbreviated weekday name (Sun..Sat)  %a",
+       "(%%b)            locale's abbreviated month name (Jan..Dec)  %b",
+       "(%%c)           full date (Sat Nov  4 12:02:33 1989)%n%t%t%t  %c",
+       "(%%d)                             day of the month (01..31)  %d",
+       "(%%e)               day of the month, blank-padded ( 1..31)  %e",
+       "(%%h)                                should be same as (%%b)  %h",
+       "(%%j)                            day of the year (001..366)  %j",
+       "(%%k)               hour, 24-hour clock, blank pad ( 0..23)  %k",
+       "(%%l)               hour, 12-hour clock, blank pad ( 0..12)  %l",
+       "(%%m)                                        month (01..12)  %m",
+       "(%%o)                                      Emporer/Era Year  %o",
+       "(%%p)              locale's AM or PM based on 12-hour clock  %p",
+       "(%%r)                   time, 12-hour (same as %%I:%%M:%%S %%p)  %r",
+       "(%%u) ISO 8601: Weekday as decimal number [1 (Monday) - 7]   %u",
+       "(%%v)                                VMS date (dd-bbb-YYYY)  %v",
+       "(%%w)                       day of week (0..6, Sunday == 0)  %w",
+       "(%%x)                appropriate locale date representation  %x",
+       "(%%y)                      last two digits of year (00..99)  %y",
+       "(%%z)      timezone offset east of GMT as HHMM (e.g. -0500)  %z",
+       (char *) NULL
+};
+
+/* main routine. */
+
+int
+main(argc, argv)
+int argc;
+char **argv;
+{
+       long time();
+
+       char *next;
+       char string[MAXTIME];
+
+       int k;
+       int length;
+
+       struct tm *tm;
+
+       long clock;
+
+       /* Call the function. */
+
+       clock = time((long *) 0);
+       tm = localtime(&clock);
+
+       for (k = 0; next = array[k]; k++) {
+               length = strftime(string, MAXTIME, next, tm);
+               printf("%s\n", string);
+       }
+
+       exit(0);
+}
+#endif /* TEST_STRFTIME */
diff --git a/subst.c b/subst.c
index 68c6f45..1983948 100644 (file)
--- a/subst.c
+++ b/subst.c
@@ -5827,10 +5827,17 @@ param_expand (string, sindex, quoted, expanded_something,
         string might need it (consider "\"$@\""), but we need some
         way to signal that the final split on the first character
         of $IFS should be done, even though QUOTED is 1. */
+#if 0
+if (list && list->next)
+  {
+#endif
       if (quoted_dollar_at_p && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
        *quoted_dollar_at_p = 1;
       if (contains_dollar_at)
        *contains_dollar_at = 1;
+#if 0
+  }
+#endif
 
       /* We want to separate the positional parameters with the first
         character of $IFS in case $IFS is something other than a space.
@@ -5974,6 +5981,8 @@ comsub:
              temp = array_reference (array_cell (var), 0);
              if (temp)
                temp = quote_escapes (temp);
+             else if (unbound_vars_is_error)
+               goto unbound_variable;
            }
          else
 #endif
@@ -5985,6 +5994,7 @@ comsub:
 
       temp = (char *)NULL;
 
+unbound_variable:
       if (unbound_vars_is_error)
        err_unboundvar (temp1);
       else
index f59a7d4..479df1a 100644 (file)
--- a/subst.c~
+++ b/subst.c~
@@ -212,8 +212,21 @@ static char *pos_params __P((char *, int, int, int));
 
 static unsigned char *mb_getcharlens __P((char *, int));
 
+static char *remove_upattern __P((char *, char *, int));
+#if defined (HANDLE_MULTIBYTE)
+#  if !defined (HAVE_WCSDUP)
+static wchar_t *wcsdup __P((wchar_t *));
+#  endif
+static wchar_t *remove_wpattern __P((wchar_t *, size_t, wchar_t *, int));
+#endif
 static char *remove_pattern __P((char *, char *, int));
+
 static int match_pattern_char __P((char *, char *));
+static int match_upattern __P((char *, char *, int, char **, char **));
+#if defined (HANDLE_MULTIBYTE)
+static int match_pattern_wchar __P((wchar_t *, wchar_t *));
+static int match_wpattern __P((wchar_t *, char **, size_t, wchar_t *, int, char **, char **));
+#endif
 static int match_pattern __P((char *, char *, int, char **, char **));
 static int getpatspec __P((int, char *));
 static char *getpattern __P((char *, int, int));
@@ -1171,9 +1184,9 @@ de_backslash (string)
       prev_i = i;
       ADVANCE_CHAR (string, slen, i);
       if (j < prev_i)
-        do string[j++] = string[prev_i++]; while (prev_i < i);
+       do string[j++] = string[prev_i++]; while (prev_i < i);
       else
-        j = i;
+       j = i;
     }
   string[j] = '\0';
 
@@ -1565,6 +1578,10 @@ string_list_internal (list, sep)
   if (list == 0)
     return ((char *)NULL);
 
+  /* Short-circuit quickly if we don't need to separate anything. */
+  if (list->next == 0)
+    return (savestring (list->word->word));
+
   /* This is nearly always called with either sep[0] == 0 or sep[1] == 0. */
   sep_len = STRLEN (sep);
   result_size = 0;
@@ -2795,17 +2812,17 @@ remove_quoted_nulls (string)
   while (i < slen)
     {
       if (string[i] == CTLESC)
-        {
+       {
          /* Old code had j++, but we cannot assume that i == j at this
             point -- what if a CTLNUL has already been removed from the
             string?  We don't want to drop the CTLESC or recopy characters
             that we've already copied down. */
          i++; string[j++] = CTLESC;
-          if (i == slen)
-            break;
-        }
+         if (i == slen)
+           break;
+       }
       else if (string[i] == CTLNUL)
-        i++;
+       i++;
 
       prev_i = i;
       ADVANCE_CHAR (string, slen, i);
@@ -2840,13 +2857,14 @@ word_list_remove_quoted_nulls (list)
 /* **************************************************************** */
 
 #if defined (HANDLE_MULTIBYTE)
+#if 0 /* Currently unused */
 static unsigned char *
 mb_getcharlens (string, len)
      char *string;
      int len;
 {
-  int i, offset;
-  unsigned char last, *ret;
+  int i, offset, last;
+  unsigned char *ret;
   char *p;
   DECLARE_MBSTATE;
 
@@ -2863,6 +2881,7 @@ mb_getcharlens (string, len)
   return ret;
 }
 #endif
+#endif
 
 /* Remove the portion of PARAM matched by PATTERN according to OP, where OP
    can have one of 4 values:
@@ -2878,61 +2897,35 @@ mb_getcharlens (string, len)
 #define RP_SHORT_RIGHT 4
 
 static char *
-remove_pattern (param, pattern, op)
+remove_upattern (param, pattern, op)
      char *param, *pattern;
      int op;
 {
   register int len;
   register char *end;
   register char *p, *ret, c;
-  int offset;
-  unsigned char *mblen;
-  DECLARE_MBSTATE;
-
-  if (param == NULL)
-    return (param);
-  if (*param == '\0' || pattern == NULL || *pattern == '\0')   /* minor optimization */
-    return (savestring (param));
 
   len = STRLEN (param);
   end = param + len;
 
-  mblen = (unsigned char *)0;
-#if defined (HANDLE_MULTIBYTE)
-  if (MB_CUR_MAX > 1 && (op == RP_LONG_LEFT || op == RP_SHORT_RIGHT))
-    mblen = mb_getcharlens (param, len);
-#endif
-
   switch (op)
     {
       case RP_LONG_LEFT:       /* remove longest match at start */
-        p = end;
-        while (p >= param)
+       for (p = end; p >= param; p--)
          {
            c = *p; *p = '\0';
            if (strmatch (pattern, param, FNMATCH_EXTFLAG) != FNM_NOMATCH)
              {
                *p = c;
-               FREE (mblen);
                return (savestring (p));
              }
            *p = c;
 
-           if (MB_CUR_MAX > 1)
-             {
-               while (p >= param)
-                 if (mblen[--p - param])
-                   break;
-             }
-           else
-             p--;
          }
        break;
 
       case RP_SHORT_LEFT:      /* remove shortest match at start */
-        p = param;
-        offset = 0;
-        while (p <= end)
+       for (p = param; p <= end; p++)
          {
            c = *p; *p = '\0';
            if (strmatch (pattern, param, FNMATCH_EXTFLAG) != FNM_NOMATCH)
@@ -2941,21 +2934,11 @@ remove_pattern (param, pattern, op)
                return (savestring (p));
              }
            *p = c;
-
-           if (MB_CUR_MAX > 1)
-             {
-               ADVANCE_CHAR (param, len, offset);
-               p = param + offset;
-             }
-           else
-             p++;
          }
        break;
 
       case RP_LONG_RIGHT:      /* remove longest match at end */
-        p = param;
-        offset = 0;
-        while (p <= end)
+       for (p = param; p <= end; p++)
          {
            if (strmatch (pattern, p, FNMATCH_EXTFLAG) != FNM_NOMATCH)
              {
@@ -2964,44 +2947,158 @@ remove_pattern (param, pattern, op)
                *p = c;
                return (ret);
              }
-
-           if (MB_CUR_MAX > 1)
-             {
-               ADVANCE_CHAR (param, len, offset);
-               p = param + offset;
-             }
-           else
-             p++;
          }
        break;
 
       case RP_SHORT_RIGHT:     /* remove shortest match at end */
-        p = end;
-        while (p >= param)
+       for (p = end; p >= param; p--)
          {
            if (strmatch (pattern, p, FNMATCH_EXTFLAG) != FNM_NOMATCH)
              {
                c = *p; *p = '\0';
                ret = savestring (param);
                *p = c;
-               FREE (mblen);
                return (ret);
              }
+         }
+       break;
+    }
+
+  return (savestring (param)); /* no match, return original string */
+}
+
+#if defined (HANDLE_MULTIBYTE)
 
-           if (MB_CUR_MAX > 1)
+#if !defined (HAVE_WCSDUP)
+static wchar_t *
+wcsdup (ws)
+     wchar_t *ws;
+{
+  wchar_t *ret;
+  size_t len;
+
+  len = wcslen (ws);
+  ret = xmalloc ((len + 1) * sizeof (wchar_t));
+  if (ret == 0)
+    return ret;
+  return (wcscpy (ret, ws));
+}
+#endif /* !HAVE_WCSDUP */
+
+static wchar_t *
+remove_wpattern (wparam, wstrlen, wpattern, op)
+     wchar_t *wparam;
+     size_t wstrlen;
+     wchar_t *wpattern;
+     int op;
+{
+  wchar_t wc;
+  int n, n1;
+  wchar_t *ret;
+
+  switch (op)
+    {
+      case RP_LONG_LEFT:       /* remove longest match at start */
+        for (n = wstrlen; n >= 0; n--)
+         {
+           wc = wparam[n]; wparam[n] = L'\0';
+           if (wcsmatch (wpattern, wparam, FNMATCH_EXTFLAG) != FNM_NOMATCH)
              {
-               while (p >= param)
-                 if (mblen[--p - param])
-                   break;
+               wparam[n] = wc;
+               return (wcsdup (wparam + n));
+             }
+           wparam[n] = wc;
+         }
+       break;
+
+      case RP_SHORT_LEFT:      /* remove shortest match at start */
+       for (n = 0; n <= wstrlen; n++)
+         {
+           wc = wparam[n]; wparam[n] = L'\0';
+           if (wcsmatch (wpattern, wparam, FNMATCH_EXTFLAG) != FNM_NOMATCH)
+             {
+               wparam[n] = wc;
+               return (wcsdup (wparam + n));
+             }
+           wparam[n] = wc;
+         }
+       break;
+
+      case RP_LONG_RIGHT:      /* remove longest match at end */
+        for (n = 0; n <= wstrlen; n++)
+         {
+           if (wcsmatch (wpattern, wparam + n, FNMATCH_EXTFLAG) != FNM_NOMATCH)
+             {
+               wc = wparam[n]; wparam[n] = L'\0';
+               ret = wcsdup (wparam);
+               wparam[n] = wc;
+               return (ret);
+             }
+         }
+       break;
+
+      case RP_SHORT_RIGHT:     /* remove shortest match at end */
+       for (n = wstrlen; n >= 0; n--)
+         {
+           if (wcsmatch (wpattern, wparam + n, FNMATCH_EXTFLAG) != FNM_NOMATCH)
+             {
+               wc = wparam[n]; wparam[n] = L'\0';
+               ret = wcsdup (wparam);
+               wparam[n] = wc;
+               return (ret);
              }
-           else
-             p--;
          }
        break;
     }
 
-  FREE (mblen);
-  return (savestring (param)); /* no match, return original string */
+  return (wcsdup (wparam));    /* no match, return original string */
+}
+#endif /* HANDLE_MULTIBYTE */
+
+static char *
+remove_pattern (param, pattern, op)
+     char *param, *pattern;
+     int op;
+{
+  if (param == NULL)
+    return (param);
+  if (*param == '\0' || pattern == NULL || *pattern == '\0')   /* minor optimization */
+    return (savestring (param));
+
+#if defined (HANDLE_MULTIBYTE)
+  if (MB_CUR_MAX > 1)
+    {
+      wchar_t *ret, *oret;
+      size_t n;
+      wchar_t *wparam, *wpattern;
+      mbstate_t ps;
+      char *xret;
+
+      n = xdupmbstowcs (&wpattern, NULL, pattern);
+      if (n == (size_t)-1)
+       return (remove_upattern (param, pattern, op));
+      n = xdupmbstowcs (&wparam, NULL, param);
+      if (n == (size_t)-1)
+       {
+         free (wpattern);
+         return (remove_upattern (param, pattern, op));
+       }
+      oret = ret = remove_wpattern (wparam, n, wpattern, op);
+
+      free (wparam);
+      free (wpattern);
+
+      n = strlen (param);
+      xret = xmalloc (n + 1);
+      memset (&ps, '\0', sizeof (mbstate_t));
+      n = wcsrtombs (xret, (const wchar_t **)&ret, n, &ps);
+      xret[n] = '\0';          /* just to make sure */
+      free (oret);
+      return xret;      
+    }
+  else
+#endif
+    return (remove_upattern (param, pattern, op));
 }
 
 /* Return 1 of the first character of STRING could match the first
@@ -3041,7 +3138,7 @@ match_pattern_char (pat, string)
    MATCH_BEG and MATCH_END anchor the match at the beginning and end
    of the string, respectively.  The longest match is returned. */
 static int
-match_pattern (string, pat, mtype, sp, ep)
+match_upattern (string, pat, mtype, sp, ep)
      char *string, *pat;
      int mtype;
      char **sp, **ep;
@@ -3049,33 +3146,18 @@ match_pattern (string, pat, mtype, sp, ep)
   int c, len;
   register char *p, *p1;
   char *end;
-  int offset;
-  unsigned char *mblen;
-  DECLARE_MBSTATE;
-
-  if (string == 0 || *string == 0 || pat == 0 || *pat == 0)
-    return (0);
 
   len = STRLEN (string);
   end = string + len;
 
-  mblen = (unsigned char *)0;
-#if defined (HANDLE_MULTIBYTE)
-  if (MB_CUR_MAX > 1 && (mtype == MATCH_ANY || mtype == MATCH_BEG))
-    mblen = mb_getcharlens (string, len);
-#endif
-
   switch (mtype)
     {
     case MATCH_ANY:
-      p = string;
-      offset = 0;
-      while (p <= end)
+      for (p = string; p <= end; p++)
        {
          if (match_pattern_char (pat, p))
            {
-             p1 = end;
-             while (p1 >= p)
+             for (p1 = end; p1 >= p; p1--)
                {
                  c = *p1; *p1 = '\0';
                  if (strmatch (pat, p, FNMATCH_EXTFLAG) == 0)
@@ -3083,40 +3165,20 @@ match_pattern (string, pat, mtype, sp, ep)
                      *p1 = c;
                      *sp = p;
                      *ep = p1;
-                     FREE (mblen);
                      return 1;
                    }
                  *p1 = c;
-
-                 if (MB_CUR_MAX > 1)
-                   {
-                     while (p1 >= p)
-                       if (mblen[--p1 - string])
-                         break;
-                   }
-                 else
-                   p1--;
                }
            }
-
-         if (MB_CUR_MAX > 1)
-           {
-             ADVANCE_CHAR (string, len, offset);
-             p = string + offset;
-           }
-         else
-           p++;
        }
 
-      FREE (mblen);
       return (0);
 
     case MATCH_BEG:
       if (match_pattern_char (pat, string) == 0)
        return (0);
 
-      p = end;
-      while (p >= string)
+      for (p = end; p >= string; p--)
        {
          c = *p; *p = '\0';
          if (strmatch (pat, string, FNMATCH_EXTFLAG) == 0)
@@ -3124,28 +3186,15 @@ match_pattern (string, pat, mtype, sp, ep)
              *p = c;
              *sp = string;
              *ep = p;
-             FREE (mblen);
              return 1;
            }
          *p = c;
-
-         if (MB_CUR_MAX > 1)
-           {
-             while (p >= string)
-               if (mblen[--p - string])
-                 break;
-           }
-         else
-           p--;
        }
 
-      FREE (mblen);
       return (0);
 
     case MATCH_END:
-      p = string;
-      offset = 0;
-      while (p <= end)
+      for (p = string; p <= end; p++)
        {
          if (strmatch (pat, p, FNMATCH_EXTFLAG) == 0)
            {
@@ -3154,20 +3203,166 @@ match_pattern (string, pat, mtype, sp, ep)
              return 1;
            }
 
-         if (MB_CUR_MAX > 1)
+       }
+
+      return (0);
+    }
+
+  return (0);
+}
+
+#if defined (HANDLE_MULTIBYTE)
+/* Return 1 of the first character of WSTRING could match the first
+   character of pattern WPAT.  Wide character version. */
+static int
+match_pattern_wchar (wpat, wstring)
+     wchar_t *wpat, *wstring;
+{
+  wchar_t wc;
+
+  if (*wstring == 0)
+    return (0);
+
+  switch (wc = *wpat++)
+    {
+    default:
+      return (*wstring == wc);
+    case L'\\':
+      return (*wstring == *wpat);
+    case L'?':
+      return (*wpat == LPAREN ? 1 : (*wstring != L'\0'));
+    case L'*':
+      return (1);
+    case L'+':
+    case L'!':
+    case L'@':
+      return (*wpat == LPAREN ? 1 : (*wstring == wc));
+    case L'[':
+      return (*wstring != L'\0');
+    }
+}
+
+/* Match WPAT anywhere in WSTRING and return the match boundaries.
+   This returns 1 in case of a successful match, 0 otherwise.  Wide
+   character version. */
+static int
+match_wpattern (wstring, indices, wstrlen, wpat, mtype, sp, ep)
+     wchar_t *wstring;
+     char **indices;
+     size_t wstrlen;
+     wchar_t *wpat;
+     int mtype;
+     char **sp, **ep;
+{
+  wchar_t wc;
+  int len;
+#if 0
+  size_t n, n1;        /* Apple's gcc seems to miscompile this badly */
+#else
+  int n, n1;
+#endif
+
+  switch (mtype)
+    {
+    case MATCH_ANY:
+      for (n = 0; n <= wstrlen; n++)
+       {
+         if (match_pattern_wchar (wpat, wstring + n))
            {
-             ADVANCE_CHAR (string, len, offset);
-             p = string + offset;
+             for (n1 = wstrlen; n1 >= n; n1--)
+               {
+                 wc = wstring[n1]; wstring[n1] = L'\0';
+                 if (wcsmatch (wpat, wstring + n, FNMATCH_EXTFLAG) == 0)
+                   {
+                     wstring[n1] = wc;
+                     *sp = indices[n];
+                     *ep = indices[n1];
+                     return 1;
+                   }
+                 wstring[n1] = wc;
+               }
            }
-         else
-           p++;
        }
+
+      return (0);
+
+    case MATCH_BEG:
+      if (match_pattern_wchar (wpat, wstring) == 0)
+       return (0);
+
+      for (n = wstrlen; n >= 0; n--)
+       {
+         wc = wstring[n]; wstring[n] = L'\0';
+         if (wcsmatch (wpat, wstring, FNMATCH_EXTFLAG) == 0)
+           {
+             wstring[n] = wc;
+             *sp = indices[0];
+             *ep = indices[n];
+             return 1;
+           }
+         wstring[n] = wc;
+       }
+
+      return (0);
+
+    case MATCH_END:
+      for (n = 0; n <= wstrlen; n++)
+       {
+         if (wcsmatch (wpat, wstring + n, FNMATCH_EXTFLAG) == 0)
+           {
+             *sp = indices[n];
+             *ep = indices[wstrlen];
+             return 1;
+           }
+       }
+
       return (0);
     }
 
-  FREE (mblen);
   return (0);
 }
+#endif /* HANDLE_MULTIBYTE */
+
+static int
+match_pattern (string, pat, mtype, sp, ep)
+     char *string, *pat;
+     int mtype;
+     char **sp, **ep;
+{
+#if defined (HANDLE_MULTIBYTE)
+  int ret;
+  size_t n;
+  wchar_t *wstring, *wpat;
+  char **indices;
+#endif
+
+  if (string == 0 || *string == 0 || pat == 0 || *pat == 0)
+    return (0);
+
+#if defined (HANDLE_MULTIBYTE)
+  if (MB_CUR_MAX > 1)
+    {
+      n = xdupmbstowcs (&wpat, NULL, pat);
+      if (n == (size_t)-1)
+       return (match_upattern (string, pat, mtype, sp, ep));
+      n = xdupmbstowcs (&wstring, &indices, string);
+      if (n == (size_t)-1)
+       {
+         free (wpat);
+         return (match_upattern (string, pat, mtype, sp, ep));
+       }
+      ret = match_wpattern (wstring, indices, n, wpat, mtype, sp, ep);
+
+      free (wpat);
+      free (wstring);
+      free (indices);
+
+      return (ret);
+    }
+  else
+#endif
+    return (match_upattern (string, pat, mtype, sp, ep));
+}
 
 static int
 getpatspec (c, value)
@@ -4850,8 +5045,8 @@ parameter_brace_substring (varname, value, substr, quoted)
 #if defined (ARRAY_VARS)
     case VT_ARRAYVAR:
       /* We want E2 to be the number of elements desired (arrays can be sparse,
-         so verify_substring_values just returns the numbers specified and we
-         rely on array_subrange to understand how to deal with them). */
+        so verify_substring_values just returns the numbers specified and we
+        rely on array_subrange to understand how to deal with them). */
       tt = array_subrange (array_cell (v), e1, e2, starsub, quoted);
       if ((quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) == 0)
        {
@@ -5632,10 +5827,13 @@ param_expand (string, sindex, quoted, expanded_something,
         string might need it (consider "\"$@\""), but we need some
         way to signal that the final split on the first character
         of $IFS should be done, even though QUOTED is 1. */
+if (list && list->next)
+  {
       if (quoted_dollar_at_p && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
        *quoted_dollar_at_p = 1;
       if (contains_dollar_at)
        *contains_dollar_at = 1;
+  }
 
       /* We want to separate the positional parameters with the first
         character of $IFS in case $IFS is something other than a space.
@@ -5779,6 +5977,8 @@ comsub:
              temp = array_reference (array_cell (var), 0);
              if (temp)
                temp = quote_escapes (temp);
+             else if (unbound_vars_is_error)
+               goto unbound_variable;
            }
          else
 #endif
@@ -5790,6 +5990,7 @@ comsub:
 
       temp = (char *)NULL;
 
+unbound_variable:
       if (unbound_vars_is_error)
        err_unboundvar (temp1);
       else