diff options
-rw-r--r-- | CWRU/CWRU.chlog | 24 | ||||
-rw-r--r-- | CWRU/CWRU.chlog~ | 81 | ||||
-rw-r--r-- | INSTALL | 7 | ||||
-rw-r--r-- | NEWS | 9 | ||||
-rw-r--r-- | doc/Makefile.in | 2 | ||||
-rw-r--r-- | doc/bash.pdf | bin | 563152 -> 307165 bytes | |||
-rw-r--r-- | doc/bashref.pdf | bin | 523869 -> 529292 bytes | |||
-rw-r--r-- | expr.c | 3 | ||||
-rw-r--r-- | lib/readline/bind.c | 5 | ||||
-rw-r--r-- | lib/readline/bind.c~ | 2223 | ||||
-rw-r--r-- | lib/readline/histfile.c | 5 | ||||
-rw-r--r-- | lib/readline/histfile.c~ | 542 | ||||
-rw-r--r-- | lib/readline/input.c | 4 | ||||
-rw-r--r-- | lib/readline/input.c~ | 551 | ||||
-rw-r--r-- | lib/readline/parens.c | 4 | ||||
-rw-r--r-- | lib/readline/parens.c~ | 179 | ||||
-rw-r--r-- | lib/sh/strftime.c | 2 | ||||
-rw-r--r-- | lib/sh/strftime.c~ | 859 | ||||
-rw-r--r-- | subst.c | 10 | ||||
-rw-r--r-- | subst.c~ | 459 |
20 files changed, 4832 insertions, 137 deletions
diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index 08bdada3..417be316 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -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' (?)) diff --git a/CWRU/CWRU.chlog~ b/CWRU/CWRU.chlog~ index fac3512b..a03188d6 100644 --- a/CWRU/CWRU.chlog~ +++ b/CWRU/CWRU.chlog~ @@ -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 @@ -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' @@ -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 diff --git a/doc/Makefile.in b/doc/Makefile.in index 23a6a8d7..fc779eb3 100644 --- a/doc/Makefile.in +++ b/doc/Makefile.in @@ -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 diff --git a/doc/bash.pdf b/doc/bash.pdf Binary files differindex 088c9d73..520b1099 100644 --- a/doc/bash.pdf +++ b/doc/bash.pdf diff --git a/doc/bashref.pdf b/doc/bashref.pdf Binary files differindex b3b80112..fd70b7c2 100644 --- a/doc/bashref.pdf +++ b/doc/bashref.pdf @@ -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 (); diff --git a/lib/readline/bind.c b/lib/readline/bind.c index aa0bb228..15d0e4e3 100644 --- a/lib/readline/bind.c +++ b/lib/readline/bind.c @@ -19,8 +19,13 @@ 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 index 00000000..3f243c1c --- /dev/null +++ b/lib/readline/bind.c~ @@ -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, ¯o_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); +} diff --git a/lib/readline/histfile.c b/lib/readline/histfile.c index b0b1a1b2..717bbee6 100644 --- a/lib/readline/histfile.c +++ b/lib/readline/histfile.c @@ -23,8 +23,13 @@ /* 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 index 00000000..977a3017 --- /dev/null +++ b/lib/readline/histfile.c~ @@ -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)); +} diff --git a/lib/readline/input.c b/lib/readline/input.c index 6d2bb2f1..443a7d09 100644 --- a/lib/readline/input.c +++ b/lib/readline/input.c @@ -21,6 +21,10 @@ 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 index 00000000..6d2bb2f1 --- /dev/null +++ b/lib/readline/input.c~ @@ -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 */ diff --git a/lib/readline/parens.c b/lib/readline/parens.c index 54ef1f36..737f7675 100644 --- a/lib/readline/parens.c +++ b/lib/readline/parens.c @@ -21,6 +21,10 @@ 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 index 00000000..54ef1f36 --- /dev/null +++ b/lib/readline/parens.c~ @@ -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); +} diff --git a/lib/sh/strftime.c b/lib/sh/strftime.c index 253d1df4..4cb542f5 100644 --- a/lib/sh/strftime.c +++ b/lib/sh/strftime.c @@ -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 index 00000000..253d1df4 --- /dev/null +++ b/lib/sh/strftime.c~ @@ -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 */ @@ -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 @@ -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 |