diff options
| author | Chet Ramey <chet.ramey@case.edu> | 2014-11-18 15:49:36 -0500 |
|---|---|---|
| committer | Chet Ramey <chet.ramey@case.edu> | 2014-11-18 15:49:36 -0500 |
| commit | db02a1759b1422248a5e5b103131a6c04bf183d6 (patch) | |
| tree | f2c814ed1d122723c484e2a33842e7fe85fb15cb /lib/readline | |
| parent | 03d922b128707999ae5bf75af1a35f07175f4386 (diff) | |
| download | bash-db02a1759b1422248a5e5b103131a6c04bf183d6.tar.gz | |
commit bash-20141114 snapshot
Diffstat (limited to 'lib/readline')
| -rw-r--r-- | lib/readline/bind.c | 105 | ||||
| -rw-r--r-- | lib/readline/bind.c~ | 2579 | ||||
| -rw-r--r-- | lib/readline/colors.c | 19 | ||||
| -rw-r--r-- | lib/readline/colors.c~ | 271 | ||||
| -rw-r--r-- | lib/readline/colors.h | 1 | ||||
| -rw-r--r-- | lib/readline/colors.h~ | 122 | ||||
| -rw-r--r-- | lib/readline/complete.c | 13 | ||||
| -rw-r--r-- | lib/readline/complete.c~ | 2891 | ||||
| -rw-r--r-- | lib/readline/display.c | 154 | ||||
| -rw-r--r-- | lib/readline/display.c~ | 2884 | ||||
| -rw-r--r-- | lib/readline/doc/rluser.texi | 36 | ||||
| -rw-r--r-- | lib/readline/doc/rluser.texi~ | 2280 | ||||
| -rw-r--r-- | lib/readline/doc/version.texi | 6 | ||||
| -rw-r--r-- | lib/readline/doc/version.texi~ | 10 | ||||
| -rw-r--r-- | lib/readline/history.c | 7 | ||||
| -rw-r--r-- | lib/readline/history.c~ | 532 | ||||
| -rw-r--r-- | lib/readline/readline.h | 1 | ||||
| -rw-r--r-- | lib/readline/readline.h~ | 934 | ||||
| -rw-r--r-- | lib/readline/rlconf.h | 10 | ||||
| -rw-r--r-- | lib/readline/rlconf.h~ | 69 | ||||
| -rw-r--r-- | lib/readline/rlprivate.h | 7 | ||||
| -rw-r--r-- | lib/readline/rlprivate.h~ | 547 |
22 files changed, 13422 insertions, 56 deletions
diff --git a/lib/readline/bind.c b/lib/readline/bind.c index d5bcdfba..98de97c0 100644 --- a/lib/readline/bind.c +++ b/lib/readline/bind.c @@ -1570,10 +1570,13 @@ static int sv_dispprefix PARAMS((const char *)); static int sv_compquery PARAMS((const char *)); static int sv_compwidth PARAMS((const char *)); static int sv_editmode PARAMS((const char *)); +static int sv_emacs_modestr PARAMS((const char *)); static int sv_histsize PARAMS((const char *)); static int sv_isrchterm PARAMS((const char *)); static int sv_keymap PARAMS((const char *)); static int sv_seqtimeout PARAMS((const char *)); +static int sv_viins_modestr PARAMS((const char *)); +static int sv_vicmd_modestr PARAMS((const char *)); static const struct { const char * const name; @@ -1586,10 +1589,13 @@ static const struct { { "completion-prefix-display-length", V_INT, sv_dispprefix }, { "completion-query-items", V_INT, sv_compquery }, { "editing-mode", V_STRING, sv_editmode }, + { "emacs-mode-string", V_STRING, sv_emacs_modestr }, { "history-size", V_INT, sv_histsize }, { "isearch-terminators", V_STRING, sv_isrchterm }, { "keymap", V_STRING, sv_keymap }, { "keyseq-timeout", V_INT, sv_seqtimeout }, + { "vi-cmd-mode-string", V_STRING, sv_vicmd_modestr }, + { "vi-ins-mode-string", V_STRING, sv_viins_modestr }, { (char *)NULL, 0, (_rl_sv_func_t *)0 } }; @@ -1606,7 +1612,7 @@ find_string_var (name) } /* 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 + the value is null or empty, `on' (case-insensitive), or "1". Any other values result in 0 (false). */ static int bool_to_int (value) @@ -1847,7 +1853,96 @@ sv_isrchterm (value) xfree (v); return 0; } - + +extern char *_rl_emacs_mode_str; + +static int +sv_emacs_modestr (value) + const char *value; +{ + if (value && *value) + { + FREE (_rl_emacs_mode_str); + _rl_emacs_mode_str = (char *)xmalloc (2 * strlen (value) + 1); + rl_translate_keyseq (value, _rl_emacs_mode_str, &_rl_emacs_modestr_len); + _rl_emacs_mode_str[_rl_emacs_modestr_len] = '\0'; + return 0; + } + else if (value) + { + FREE (_rl_emacs_mode_str); + _rl_emacs_mode_str = (char *)xmalloc (1); + _rl_emacs_mode_str[_rl_emacs_modestr_len = 0] = '\0'; + return 0; + } + else if (value == 0) + { + FREE (_rl_emacs_mode_str); + _rl_emacs_mode_str = 0; /* prompt_modestr does the right thing */ + _rl_emacs_modestr_len = 0; + return 0; + } + return 1; +} + +static int +sv_viins_modestr (value) + const char *value; +{ + if (value && *value) + { + FREE (_rl_vi_ins_mode_str); + _rl_vi_ins_mode_str = (char *)xmalloc (2 * strlen (value) + 1); + rl_translate_keyseq (value, _rl_vi_ins_mode_str, &_rl_vi_ins_modestr_len); + _rl_vi_ins_mode_str[_rl_vi_ins_modestr_len] = '\0'; + return 0; + } + else if (value) + { + FREE (_rl_vi_ins_mode_str); + _rl_vi_ins_mode_str = (char *)xmalloc (1); + _rl_vi_ins_mode_str[_rl_vi_ins_modestr_len = 0] = '\0'; + return 0; + } + else if (value == 0) + { + FREE (_rl_vi_ins_mode_str); + _rl_vi_ins_mode_str = 0; /* prompt_modestr does the right thing */ + _rl_vi_ins_modestr_len = 0; + return 0; + } + return 1; +} + +static int +sv_vicmd_modestr (value) + const char *value; +{ + if (value && *value) + { + FREE (_rl_vi_cmd_mode_str); + _rl_vi_cmd_mode_str = (char *)xmalloc (2 * strlen (value) + 1); + rl_translate_keyseq (value, _rl_vi_cmd_mode_str, &_rl_vi_cmd_modestr_len); + _rl_vi_cmd_mode_str[_rl_vi_cmd_modestr_len] = '\0'; + return 0; + } + else if (value) + { + FREE (_rl_vi_cmd_mode_str); + _rl_vi_cmd_mode_str = (char *)xmalloc (1); + _rl_vi_cmd_mode_str[_rl_vi_cmd_modestr_len = 0] = '\0'; + return 0; + } + else if (value == 0) + { + FREE (_rl_vi_cmd_mode_str); + _rl_vi_cmd_mode_str = 0; /* prompt_modestr does the right thing */ + _rl_vi_cmd_modestr_len = 0; + return 0; + } + return 1; +} + /* Return the character which matches NAME. For example, `Space' returns ' '. */ @@ -2421,6 +2516,12 @@ _rl_get_string_variable_value (name) sprintf (numbuf, "%d", _rl_keyseq_timeout); return (numbuf); } + else if (_rl_stricmp (name, "emacs-mode-string") == 0) + return (_rl_emacs_mode_str ? _rl_emacs_mode_str : RL_EMACS_MODESTR_DEFAULT); + else if (_rl_stricmp (name, "vi-cmd-mode-string") == 0) + return (_rl_emacs_mode_str ? _rl_emacs_mode_str : RL_VI_CMD_MODESTR_DEFAULT); + else if (_rl_stricmp (name, "vi-ins-mode-string") == 0) + return (_rl_emacs_mode_str ? _rl_emacs_mode_str : RL_VI_INS_MODESTR_DEFAULT); else return (0); } diff --git a/lib/readline/bind.c~ b/lib/readline/bind.c~ new file mode 100644 index 00000000..c2d36f99 --- /dev/null +++ b/lib/readline/bind.c~ @@ -0,0 +1,2579 @@ +/* bind.c -- key binding and startup file support for the readline library. */ + +/* Copyright (C) 1987-2012 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Readline is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define READLINE_LIBRARY + +#if defined (__TANDEM) +# include <floss.h> +#endif + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#include <stdio.h> +#include <sys/types.h> +#include <fcntl.h> +#if defined (HAVE_SYS_FILE_H) +# include <sys/file.h> +#endif /* HAVE_SYS_FILE_H */ + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif /* HAVE_UNISTD_H */ + +#if defined (HAVE_STDLIB_H) +# include <stdlib.h> +#else +# include "ansi_stdlib.h" +#endif /* HAVE_STDLIB_H */ + +#include <errno.h> + +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +#include "posixstat.h" + +/* System-specific feature definitions and include files. */ +#include "rldefs.h" + +/* Some standard library routines. */ +#include "readline.h" +#include "history.h" + +#include "rlprivate.h" +#include "rlshell.h" +#include "xmalloc.h" + +#if !defined (strchr) && !defined (__STDC__) +extern char *strchr (), *strrchr (); +#endif /* !strchr && !__STDC__ */ + +/* Variables exported by this file. */ +Keymap rl_binding_keymap; + +static int _rl_skip_to_delim PARAMS((char *, int, int)); + +static char *_rl_read_file PARAMS((char *, size_t *)); +static void _rl_init_file_error PARAMS((const char *)); +static int _rl_read_init_file PARAMS((const char *, int)); +static int glean_key_from_name PARAMS((char *)); + +static int find_boolean_var PARAMS((const char *)); +static int find_string_var PARAMS((const char *)); + +static char *_rl_get_string_variable_value PARAMS((const char *)); +static int substring_member_of_array PARAMS((const char *, const char * const *)); + +static int currently_reading_init_file; + +/* used only in this file */ +static int _rl_prefer_visible_bell = 1; + +/* **************************************************************** */ +/* */ +/* Binding keys */ +/* */ +/* **************************************************************** */ + +/* rl_add_defun (char *name, rl_command_func_t *function, int key) + Add NAME to the list of named functions. Make FUNCTION be the function + that gets called. If KEY is not -1, then bind it. */ +int +rl_add_defun (name, function, key) + const char *name; + rl_command_func_t *function; + int key; +{ + if (key != -1) + rl_bind_key (key, function); + rl_add_funmap_entry (name, function); + return 0; +} + +/* Bind KEY to FUNCTION. Returns non-zero if KEY is out of range. */ +int +rl_bind_key (key, function) + int key; + rl_command_func_t *function; +{ + if (key < 0) + return (key); + + if (META_CHAR (key) && _rl_convert_meta_chars_to_ascii) + { + if (_rl_keymap[ESC].type == ISKMAP) + { + Keymap escmap; + + escmap = FUNCTION_TO_KEYMAP (_rl_keymap, ESC); + key = UNMETA (key); + escmap[key].type = ISFUNC; + escmap[key].function = function; + return (0); + } + return (key); + } + + _rl_keymap[key].type = ISFUNC; + _rl_keymap[key].function = function; + rl_binding_keymap = _rl_keymap; + return (0); +} + +/* Bind KEY to FUNCTION in MAP. Returns non-zero in case of invalid + KEY. */ +int +rl_bind_key_in_map (key, function, map) + int key; + rl_command_func_t *function; + Keymap map; +{ + int result; + Keymap oldmap; + + oldmap = _rl_keymap; + _rl_keymap = map; + result = rl_bind_key (key, function); + _rl_keymap = oldmap; + return (result); +} + +/* Bind key sequence KEYSEQ to DEFAULT_FUNC if KEYSEQ is unbound. Right + now, this is always used to attempt to bind the arrow keys, hence the + check for rl_vi_movement_mode. */ +int +rl_bind_key_if_unbound_in_map (key, default_func, kmap) + int key; + rl_command_func_t *default_func; + Keymap kmap; +{ + char keyseq[2]; + + keyseq[0] = (unsigned char)key; + keyseq[1] = '\0'; + return (rl_bind_keyseq_if_unbound_in_map (keyseq, default_func, kmap)); +} + +int +rl_bind_key_if_unbound (key, default_func) + int key; + rl_command_func_t *default_func; +{ + char keyseq[2]; + + keyseq[0] = (unsigned char)key; + keyseq[1] = '\0'; + return (rl_bind_keyseq_if_unbound_in_map (keyseq, default_func, _rl_keymap)); +} + +/* Make KEY do nothing in the currently selected keymap. + Returns non-zero in case of error. */ +int +rl_unbind_key (key) + int key; +{ + return (rl_bind_key (key, (rl_command_func_t *)NULL)); +} + +/* Make KEY do nothing in MAP. + Returns non-zero in case of error. */ +int +rl_unbind_key_in_map (key, map) + int key; + Keymap map; +{ + return (rl_bind_key_in_map (key, (rl_command_func_t *)NULL, map)); +} + +/* Unbind all keys bound to FUNCTION in MAP. */ +int +rl_unbind_function_in_map (func, map) + rl_command_func_t *func; + Keymap map; +{ + register int i, rval; + + for (i = rval = 0; i < KEYMAP_SIZE; i++) + { + if (map[i].type == ISFUNC && map[i].function == func) + { + map[i].function = (rl_command_func_t *)NULL; + rval = 1; + } + } + return rval; +} + +int +rl_unbind_command_in_map (command, map) + const char *command; + Keymap map; +{ + rl_command_func_t *func; + + func = rl_named_function (command); + if (func == 0) + return 0; + return (rl_unbind_function_in_map (func, map)); +} + +/* Bind the key sequence represented by the string KEYSEQ to + FUNCTION, starting in the current keymap. This makes new + keymaps as necessary. */ +int +rl_bind_keyseq (keyseq, function) + const char *keyseq; + rl_command_func_t *function; +{ + return (rl_generic_bind (ISFUNC, keyseq, (char *)function, _rl_keymap)); +} + +/* Bind the key sequence represented by the string KEYSEQ to + FUNCTION. This makes new keymaps as necessary. The initial + place to do bindings is in MAP. */ +int +rl_bind_keyseq_in_map (keyseq, function, map) + const char *keyseq; + rl_command_func_t *function; + Keymap map; +{ + return (rl_generic_bind (ISFUNC, keyseq, (char *)function, map)); +} + +/* Backwards compatibility; equivalent to rl_bind_keyseq_in_map() */ +int +rl_set_key (keyseq, function, map) + const char *keyseq; + rl_command_func_t *function; + Keymap map; +{ + return (rl_generic_bind (ISFUNC, keyseq, (char *)function, map)); +} + +/* Bind key sequence KEYSEQ to DEFAULT_FUNC if KEYSEQ is unbound. Right + now, this is always used to attempt to bind the arrow keys, hence the + check for rl_vi_movement_mode. */ +int +rl_bind_keyseq_if_unbound_in_map (keyseq, default_func, kmap) + const char *keyseq; + rl_command_func_t *default_func; + Keymap kmap; +{ + rl_command_func_t *func; + + if (keyseq) + { + func = rl_function_of_keyseq (keyseq, kmap, (int *)NULL); +#if defined (VI_MODE) + if (!func || func == rl_do_lowercase_version || func == rl_vi_movement_mode) +#else + if (!func || func == rl_do_lowercase_version) +#endif + return (rl_bind_keyseq_in_map (keyseq, default_func, kmap)); + else + return 1; + } + return 0; +} + +int +rl_bind_keyseq_if_unbound (keyseq, default_func) + const char *keyseq; + rl_command_func_t *default_func; +{ + return (rl_bind_keyseq_if_unbound_in_map (keyseq, default_func, _rl_keymap)); +} + +/* Bind the key sequence represented by the string KEYSEQ to + the string of characters MACRO. This makes new keymaps as + necessary. The initial place to do bindings is in MAP. */ +int +rl_macro_bind (keyseq, macro, map) + const char *keyseq, *macro; + Keymap map; +{ + char *macro_keys; + int macro_keys_len; + + macro_keys = (char *)xmalloc ((2 * strlen (macro)) + 1); + + if (rl_translate_keyseq (macro, macro_keys, ¯o_keys_len)) + { + xfree (macro_keys); + return -1; + } + rl_generic_bind (ISMACR, keyseq, macro_keys, map); + return 0; +} + +/* Bind the key sequence represented by the string KEYSEQ to + the arbitrary pointer DATA. TYPE says what kind of data is + pointed to by DATA, right now this can be a function (ISFUNC), + a macro (ISMACR), or a keymap (ISKMAP). This makes new keymaps + as necessary. The initial place to do bindings is in MAP. */ +int +rl_generic_bind (type, keyseq, data, map) + int type; + const char *keyseq; + char *data; + Keymap map; +{ + char *keys; + int keys_len; + register int i; + KEYMAP_ENTRY k; + + k.function = 0; + + /* If no keys to bind to, exit right away. */ + if (keyseq == 0 || *keyseq == 0) + { + if (type == ISMACR) + xfree (data); + return -1; + } + + keys = (char *)xmalloc (1 + (2 * strlen (keyseq))); + + /* Translate the ASCII representation of KEYSEQ into an array of + characters. Stuff the characters into KEYS, and the length of + KEYS into KEYS_LEN. */ + if (rl_translate_keyseq (keyseq, keys, &keys_len)) + { + xfree (keys); + return -1; + } + + /* Bind keys, making new keymaps as necessary. */ + for (i = 0; i < keys_len; i++) + { + unsigned char uc = keys[i]; + int ic; + + ic = uc; + if (ic < 0 || ic >= KEYMAP_SIZE) + { + xfree (keys); + return -1; + } + + if (META_CHAR (ic) && _rl_convert_meta_chars_to_ascii) + { + ic = UNMETA (ic); + if (map[ESC].type == ISKMAP) + map = FUNCTION_TO_KEYMAP (map, ESC); + } + + if ((i + 1) < keys_len) + { + if (map[ic].type != ISKMAP) + { + /* We allow subsequences of keys. If a keymap is being + created that will `shadow' an existing function or macro + key binding, we save that keybinding into the ANYOTHERKEY + index in the new map. The dispatch code will look there + to find the function to execute if the subsequence is not + matched. ANYOTHERKEY was chosen to be greater than + UCHAR_MAX. */ + k = map[ic]; + + map[ic].type = ISKMAP; + map[ic].function = KEYMAP_TO_FUNCTION (rl_make_bare_keymap()); + } + map = FUNCTION_TO_KEYMAP (map, ic); + /* The dispatch code will return this function if no matching + key sequence is found in the keymap. This (with a little + help from the dispatch code in readline.c) allows `a' to be + mapped to something, `abc' to be mapped to something else, + and the function bound to `a' to be executed when the user + types `abx', leaving `bx' in the input queue. */ + if (k.function && ((k.type == ISFUNC && k.function != rl_do_lowercase_version) || k.type == ISMACR)) + { + map[ANYOTHERKEY] = k; + k.function = 0; + } + } + else + { + if (map[ic].type == ISMACR) + xfree ((char *)map[ic].function); + else if (map[ic].type == ISKMAP) + { + map = FUNCTION_TO_KEYMAP (map, ic); + ic = ANYOTHERKEY; + /* If we're trying to override a keymap with a null function + (e.g., trying to unbind it), we can't use a null pointer + here because that's indistinguishable from having not been + overridden. We use a special bindable function that does + nothing. */ + if (type == ISFUNC && data == 0) + data = (char *)_rl_null_function; + } + + map[ic].function = KEYMAP_TO_FUNCTION (data); + map[ic].type = type; + } + + rl_binding_keymap = map; + } + xfree (keys); + return 0; +} + +/* Translate the ASCII representation of SEQ, stuffing the values into ARRAY, + an array of characters. LEN gets the final length of ARRAY. Return + non-zero if there was an error parsing SEQ. */ +int +rl_translate_keyseq (seq, array, len) + const char *seq; + char *array; + int *len; +{ + register int i, c, l, temp; + + for (i = l = 0; c = seq[i]; i++) + { + if (c == '\\') + { + c = seq[++i]; + + if (c == 0) + break; + + /* Handle \C- and \M- prefixes. */ + if ((c == 'C' || c == 'M') && seq[i + 1] == '-') + { + /* Handle special case of backwards define. */ + if (strncmp (&seq[i], "C-\\M-", 5) == 0) + { + array[l++] = ESC; /* ESC is meta-prefix */ + i += 5; + array[l++] = CTRL (_rl_to_upper (seq[i])); + if (seq[i] == '\0') + i--; + } + else if (c == 'M') + { + i++; /* seq[i] == '-' */ + /* XXX - obey convert-meta setting */ + if (_rl_convert_meta_chars_to_ascii && _rl_keymap[ESC].type == ISKMAP) + array[l++] = ESC; /* ESC is meta-prefix */ + else if (seq[i+1] == '\\' && seq[i+2] == 'C' && seq[i+3] == '-') + { + i += 4; + temp = (seq[i] == '?') ? RUBOUT : CTRL (_rl_to_upper (seq[i])); + array[l++] = META (temp); + } + else + { + /* This doesn't yet handle things like \M-\a, which may + or may not have any reasonable meaning. You're + probably better off using straight octal or hex. */ + i++; + array[l++] = META (seq[i]); + } + } + else if (c == 'C') + { + i += 2; + /* Special hack for C-?... */ + array[l++] = (seq[i] == '?') ? RUBOUT : CTRL (_rl_to_upper (seq[i])); + } + continue; + } + + /* Translate other backslash-escaped characters. These are the + same escape sequences that bash's `echo' and `printf' builtins + handle, with the addition of \d -> RUBOUT. A backslash + preceding a character that is not special is stripped. */ + switch (c) + { + case 'a': + array[l++] = '\007'; + break; + case 'b': + array[l++] = '\b'; + break; + case 'd': + array[l++] = RUBOUT; /* readline-specific */ + break; + case 'e': + array[l++] = ESC; + break; + case 'f': + array[l++] = '\f'; + break; + case 'n': + array[l++] = NEWLINE; + break; + case 'r': + array[l++] = RETURN; + break; + case 't': + array[l++] = TAB; + break; + case 'v': + array[l++] = 0x0B; + break; + case '\\': + array[l++] = '\\'; + break; + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + i++; + for (temp = 2, c -= '0'; ISOCTAL (seq[i]) && temp--; i++) + c = (c * 8) + OCTVALUE (seq[i]); + i--; /* auto-increment in for loop */ + array[l++] = c & largest_char; + break; + case 'x': + i++; + for (temp = 2, c = 0; ISXDIGIT ((unsigned char)seq[i]) && temp--; i++) + c = (c * 16) + HEXVALUE (seq[i]); + if (temp == 2) + c = 'x'; + i--; /* auto-increment in for loop */ + array[l++] = c & largest_char; + break; + default: /* backslashes before non-special chars just add the char */ + array[l++] = c; + break; /* the backslash is stripped */ + } + continue; + } + + array[l++] = c; + } + + *len = l; + array[l] = '\0'; + return (0); +} + +static int +_rl_isescape (c) + int c; +{ + switch (c) + { + case '\007': + case '\b': + case '\f': + case '\n': + case '\r': + case TAB: + case 0x0b: return (1); + default: return (0); + } +} + +static int +_rl_escchar (c) + int c; +{ + switch (c) + { + case '\007': return ('a'); + case '\b': return ('b'); + case '\f': return ('f'); + case '\n': return ('n'); + case '\r': return ('r'); + case TAB: return ('t'); + case 0x0b: return ('v'); + default: return (c); + } +} + +char * +rl_untranslate_keyseq (seq) + int seq; +{ + static char kseq[16]; + int i, c; + + i = 0; + c = seq; + if (META_CHAR (c)) + { + kseq[i++] = '\\'; + kseq[i++] = 'M'; + kseq[i++] = '-'; + c = UNMETA (c); + } + else if (c == ESC) + { + kseq[i++] = '\\'; + c = 'e'; + } + else if (CTRL_CHAR (c)) + { + kseq[i++] = '\\'; + kseq[i++] = 'C'; + kseq[i++] = '-'; + c = _rl_to_lower (UNCTRL (c)); + } + else if (c == RUBOUT) + { + kseq[i++] = '\\'; + kseq[i++] = 'C'; + kseq[i++] = '-'; + c = '?'; + } + + if (c == ESC) + { + kseq[i++] = '\\'; + c = 'e'; + } + else if (c == '\\' || c == '"') + { + kseq[i++] = '\\'; + } + + kseq[i++] = (unsigned char) c; + kseq[i] = '\0'; + return kseq; +} + +char * +_rl_untranslate_macro_value (seq, use_escapes) + char *seq; + int use_escapes; +{ + char *ret, *r, *s; + int c; + + r = ret = (char *)xmalloc (7 * strlen (seq) + 1); + for (s = seq; *s; s++) + { + c = *s; + if (META_CHAR (c)) + { + *r++ = '\\'; + *r++ = 'M'; + *r++ = '-'; + c = UNMETA (c); + } + else if (c == ESC) + { + *r++ = '\\'; + c = 'e'; + } + else if (CTRL_CHAR (c)) + { + *r++ = '\\'; + if (use_escapes && _rl_isescape (c)) + c = _rl_escchar (c); + else + { + *r++ = 'C'; + *r++ = '-'; + c = _rl_to_lower (UNCTRL (c)); + } + } + else if (c == RUBOUT) + { + *r++ = '\\'; + *r++ = 'C'; + *r++ = '-'; + c = '?'; + } + + if (c == ESC) + { + *r++ = '\\'; + c = 'e'; + } + else if (c == '\\' || c == '"') + *r++ = '\\'; + + *r++ = (unsigned char)c; + } + *r = '\0'; + return ret; +} + +/* Return a pointer to the function that STRING represents. + If STRING doesn't have a matching function, then a NULL pointer + is returned. */ +rl_command_func_t * +rl_named_function (string) + const char *string; +{ + register int i; + + rl_initialize_funmap (); + + for (i = 0; funmap[i]; i++) + if (_rl_stricmp (funmap[i]->name, string) == 0) + return (funmap[i]->function); + return ((rl_command_func_t *)NULL); +} + +/* Return the function (or macro) definition which would be invoked via + KEYSEQ if executed in MAP. If MAP is NULL, then the current keymap is + used. TYPE, if non-NULL, is a pointer to an int which will receive the + type of the object pointed to. One of ISFUNC (function), ISKMAP (keymap), + or ISMACR (macro). */ +rl_command_func_t * +rl_function_of_keyseq (keyseq, map, type) + const char *keyseq; + Keymap map; + int *type; +{ + register int i; + + if (map == 0) + map = _rl_keymap; + + for (i = 0; keyseq && keyseq[i]; i++) + { + unsigned char ic = keyseq[i]; + + if (META_CHAR (ic) && _rl_convert_meta_chars_to_ascii) + { + if (map[ESC].type == ISKMAP) + { + map = FUNCTION_TO_KEYMAP (map, ESC); + ic = UNMETA (ic); + } + /* XXX - should we just return NULL here, since this obviously + doesn't match? */ + else + { + if (type) + *type = map[ESC].type; + + return (map[ESC].function); + } + } + + if (map[ic].type == ISKMAP) + { + /* If this is the last key in the key sequence, return the + map. */ + if (keyseq[i + 1] == '\0') + { + if (type) + *type = ISKMAP; + + return (map[ic].function); + } + else + map = FUNCTION_TO_KEYMAP (map, ic); + } + /* If we're not at the end of the key sequence, and the current key + is bound to something other than a keymap, then the entire key + sequence is not bound. */ + else if (map[ic].type != ISKMAP && keyseq[i+1]) + return ((rl_command_func_t *)NULL); + else /* map[ic].type != ISKMAP && keyseq[i+1] == 0 */ + { + if (type) + *type = map[ic].type; + + return (map[ic].function); + } + } + return ((rl_command_func_t *) NULL); +} + +/* The last key bindings file read. */ +static char *last_readline_init_file = (char *)NULL; + +/* The file we're currently reading key bindings from. */ +static const char *current_readline_init_file; +static int current_readline_init_include_level; +static int current_readline_init_lineno; + +/* Read FILENAME into a locally-allocated buffer and return the buffer. + The size of the buffer is returned in *SIZEP. Returns NULL if any + errors were encountered. */ +static char * +_rl_read_file (filename, sizep) + char *filename; + size_t *sizep; +{ + struct stat finfo; + size_t file_size; + char *buffer; + int i, file; + + if ((stat (filename, &finfo) < 0) || (file = open (filename, O_RDONLY, 0666)) < 0) + return ((char *)NULL); + + file_size = (size_t)finfo.st_size; + + /* check for overflow on very large files */ + if (file_size != finfo.st_size || file_size + 1 < file_size) + { + if (file >= 0) + close (file); +#if defined (EFBIG) + errno = EFBIG; +#endif + return ((char *)NULL); + } + + /* Read the file into BUFFER. */ + buffer = (char *)xmalloc (file_size + 1); + i = read (file, buffer, file_size); + close (file); + + if (i < 0) + { + xfree (buffer); + return ((char *)NULL); + } + + RL_CHECK_SIGNALS (); + + buffer[i] = '\0'; + if (sizep) + *sizep = i; + + return (buffer); +} + +/* Re-read the current keybindings file. */ +int +rl_re_read_init_file (count, ignore) + int count, ignore; +{ + int r; + r = rl_read_init_file ((const char *)NULL); + rl_set_keymap_from_edit_mode (); + return r; +} + +/* Do key bindings from a file. If FILENAME is NULL it defaults + to the first non-null filename from this list: + 1. the filename used for the previous call + 2. the value of the shell variable `INPUTRC' + 3. ~/.inputrc + 4. /etc/inputrc + If the file existed and could be opened and read, 0 is returned, + otherwise errno is returned. */ +int +rl_read_init_file (filename) + const char *filename; +{ + /* Default the filename. */ + if (filename == 0) + filename = last_readline_init_file; + if (filename == 0) + filename = sh_get_env_value ("INPUTRC"); + if (filename == 0 || *filename == 0) + { + filename = DEFAULT_INPUTRC; + /* Try to read DEFAULT_INPUTRC; fall back to SYS_INPUTRC on failure */ + if (_rl_read_init_file (filename, 0) == 0) + return 0; + filename = SYS_INPUTRC; + } + +#if defined (__MSDOS__) + if (_rl_read_init_file (filename, 0) == 0) + return 0; + filename = "~/_inputrc"; +#endif + return (_rl_read_init_file (filename, 0)); +} + +static int +_rl_read_init_file (filename, include_level) + const char *filename; + int include_level; +{ + register int i; + char *buffer, *openname, *line, *end; + size_t file_size; + + current_readline_init_file = filename; + current_readline_init_include_level = include_level; + + openname = tilde_expand (filename); + buffer = _rl_read_file (openname, &file_size); + xfree (openname); + + RL_CHECK_SIGNALS (); + if (buffer == 0) + return (errno); + + if (include_level == 0 && filename != last_readline_init_file) + { + FREE (last_readline_init_file); + last_readline_init_file = savestring (filename); + } + + currently_reading_init_file = 1; + + /* Loop over the lines in the file. Lines that start with `#' are + comments; all other lines are commands for readline initialization. */ + current_readline_init_lineno = 1; + line = buffer; + end = buffer + file_size; + while (line < end) + { + /* Find the end of this line. */ + for (i = 0; line + i != end && line[i] != '\n'; i++); + +#if defined (__CYGWIN__) + /* ``Be liberal in what you accept.'' */ + if (line[i] == '\n' && line[i-1] == '\r') + line[i - 1] = '\0'; +#endif + + /* Mark end of line. */ + line[i] = '\0'; + + /* Skip leading whitespace. */ + while (*line && whitespace (*line)) + { + line++; + i--; + } + + /* If the line is not a comment, then parse it. */ + if (*line && *line != '#') + rl_parse_and_bind (line); + + /* Move to the next line. */ + line += i + 1; + current_readline_init_lineno++; + } + + xfree (buffer); + currently_reading_init_file = 0; + return (0); +} + +static void +_rl_init_file_error (msg) + const char *msg; +{ + if (currently_reading_init_file) + _rl_errmsg ("%s: line %d: %s\n", current_readline_init_file, + current_readline_init_lineno, msg); + else + _rl_errmsg ("%s", msg); +} + +/* **************************************************************** */ +/* */ +/* Parser Directives */ +/* */ +/* **************************************************************** */ + +typedef int _rl_parser_func_t PARAMS((char *)); + +/* Things that mean `Control'. */ +const char * const _rl_possible_control_prefixes[] = { + "Control-", "C-", "CTRL-", (const char *)NULL +}; + +const char * const _rl_possible_meta_prefixes[] = { + "Meta", "M-", (const char *)NULL +}; + +/* Conditionals. */ + +/* Calling programs set this to have their argv[0]. */ +const char *rl_readline_name = "other"; + +/* Stack of previous values of parsing_conditionalized_out. */ +static unsigned char *if_stack = (unsigned char *)NULL; +static int if_stack_depth; +static int if_stack_size; + +/* Push _rl_parsing_conditionalized_out, and set parser state based + on ARGS. */ +static int +parser_if (args) + char *args; +{ + register int i; + + /* Push parser state. */ + if (if_stack_depth + 1 >= if_stack_size) + { + if (!if_stack) + if_stack = (unsigned char *)xmalloc (if_stack_size = 20); + else + if_stack = (unsigned char *)xrealloc (if_stack, if_stack_size += 20); + } + if_stack[if_stack_depth++] = _rl_parsing_conditionalized_out; + + /* If parsing is turned off, then nothing can turn it back on except + for finding the matching endif. In that case, return right now. */ + if (_rl_parsing_conditionalized_out) + return 0; + + /* Isolate first argument. */ + for (i = 0; args[i] && !whitespace (args[i]); i++); + + if (args[i]) + args[i++] = '\0'; + + /* Handle "$if term=foo" and "$if mode=emacs" constructs. If this + isn't term=foo, or mode=emacs, then check to see if the first + word in ARGS is the same as the value stored in rl_readline_name. */ + if (rl_terminal_name && _rl_strnicmp (args, "term=", 5) == 0) + { + char *tem, *tname; + + /* Terminals like "aaa-60" are equivalent to "aaa". */ + tname = savestring (rl_terminal_name); + tem = strchr (tname, '-'); + if (tem) + *tem = '\0'; + + /* Test the `long' and `short' forms of the terminal name so that + if someone has a `sun-cmd' and does not want to have bindings + that will be executed if the terminal is a `sun', they can put + `$if term=sun-cmd' into their .inputrc. */ + _rl_parsing_conditionalized_out = _rl_stricmp (args + 5, tname) && + _rl_stricmp (args + 5, rl_terminal_name); + xfree (tname); + } +#if defined (VI_MODE) + else if (_rl_strnicmp (args, "mode=", 5) == 0) + { + int mode; + + if (_rl_stricmp (args + 5, "emacs") == 0) + mode = emacs_mode; + else if (_rl_stricmp (args + 5, "vi") == 0) + mode = vi_mode; + else + mode = no_mode; + + _rl_parsing_conditionalized_out = mode != rl_editing_mode; + } +#endif /* VI_MODE */ + /* Check to see if the first word in ARGS is the same as the + value stored in rl_readline_name. */ + else if (_rl_stricmp (args, rl_readline_name) == 0) + _rl_parsing_conditionalized_out = 0; + else + _rl_parsing_conditionalized_out = 1; + return 0; +} + +/* Invert the current parser state if there is anything on the stack. */ +static int +parser_else (args) + char *args; +{ + register int i; + + if (if_stack_depth == 0) + { + _rl_init_file_error ("$else found without matching $if"); + return 0; + } + +#if 0 + /* Check the previous (n - 1) levels of the stack to make sure that + we haven't previously turned off parsing. */ + for (i = 0; i < if_stack_depth - 1; i++) +#else + /* Check the previous (n) levels of the stack to make sure that + we haven't previously turned off parsing. */ + for (i = 0; i < if_stack_depth; i++) +#endif + if (if_stack[i] == 1) + return 0; + + /* Invert the state of parsing if at top level. */ + _rl_parsing_conditionalized_out = !_rl_parsing_conditionalized_out; + return 0; +} + +/* Terminate a conditional, popping the value of + _rl_parsing_conditionalized_out from the stack. */ +static int +parser_endif (args) + char *args; +{ + if (if_stack_depth) + _rl_parsing_conditionalized_out = if_stack[--if_stack_depth]; + else + _rl_init_file_error ("$endif without matching $if"); + return 0; +} + +static int +parser_include (args) + char *args; +{ + const char *old_init_file; + char *e; + int old_line_number, old_include_level, r; + + if (_rl_parsing_conditionalized_out) + return (0); + + old_init_file = current_readline_init_file; + old_line_number = current_readline_init_lineno; + old_include_level = current_readline_init_include_level; + + e = strchr (args, '\n'); + if (e) + *e = '\0'; + r = _rl_read_init_file ((const char *)args, old_include_level + 1); + + current_readline_init_file = old_init_file; + current_readline_init_lineno = old_line_number; + current_readline_init_include_level = old_include_level; + + return r; +} + +/* Associate textual names with actual functions. */ +static const struct { + const char * const name; + _rl_parser_func_t *function; +} parser_directives [] = { + { "if", parser_if }, + { "endif", parser_endif }, + { "else", parser_else }, + { "include", parser_include }, + { (char *)0x0, (_rl_parser_func_t *)0x0 } +}; + +/* Handle a parser directive. STATEMENT is the line of the directive + without any leading `$'. */ +static int +handle_parser_directive (statement) + char *statement; +{ + register int i; + char *directive, *args; + + /* Isolate the actual directive. */ + + /* Skip whitespace. */ + for (i = 0; whitespace (statement[i]); i++); + + directive = &statement[i]; + + for (; statement[i] && !whitespace (statement[i]); i++); + + if (statement[i]) + statement[i++] = '\0'; + + for (; statement[i] && whitespace (statement[i]); i++); + + args = &statement[i]; + + /* Lookup the command, and act on it. */ + for (i = 0; parser_directives[i].name; i++) + if (_rl_stricmp (directive, parser_directives[i].name) == 0) + { + (*parser_directives[i].function) (args); + return (0); + } + + /* display an error message about the unknown parser directive */ + _rl_init_file_error ("unknown parser directive"); + return (1); +} + +/* Start at STRING[START] and look for DELIM. Return I where STRING[I] == + DELIM or STRING[I] == 0. DELIM is usually a double quote. */ +static int +_rl_skip_to_delim (string, start, delim) + char *string; + int start, delim; +{ + int i, c, passc; + + for (i = start,passc = 0; c = string[i]; i++) + { + if (passc) + { + passc = 0; + if (c == 0) + break; + continue; + } + + if (c == '\\') + { + passc = 1; + continue; + } + + if (c == delim) + break; + } + + return i; +} + +/* Read the binding command from STRING and perform it. + A key binding command looks like: Keyname: function-name\0, + a variable binding command looks like: set variable value. + A new-style keybinding looks like "\C-x\C-x": exchange-point-and-mark. */ +int +rl_parse_and_bind (string) + char *string; +{ + char *funname, *kname; + register int c, i; + int key, equivalency; + + while (string && whitespace (*string)) + string++; + + if (string == 0 || *string == 0 || *string == '#') + return 0; + + /* If this is a parser directive, act on it. */ + if (*string == '$') + { + handle_parser_directive (&string[1]); + return 0; + } + + /* If we aren't supposed to be parsing right now, then we're done. */ + if (_rl_parsing_conditionalized_out) + return 0; + + i = 0; + /* If this keyname is a complex key expression surrounded by quotes, + advance to after the matching close quote. This code allows the + backslash to quote characters in the key expression. */ + if (*string == '"') + { + i = _rl_skip_to_delim (string, 1, '"'); + + /* If we didn't find a closing quote, abort the line. */ + if (string[i] == '\0') + { + _rl_init_file_error ("no closing `\"' in key binding"); + return 1; + } + else + i++; /* skip past closing double quote */ + } + + /* Advance to the colon (:) or whitespace which separates the two objects. */ + for (; (c = string[i]) && c != ':' && c != ' ' && c != '\t'; i++ ); + + equivalency = (c == ':' && string[i + 1] == '='); + + /* Mark the end of the command (or keyname). */ + if (string[i]) + string[i++] = '\0'; + + /* If doing assignment, skip the '=' sign as well. */ + if (equivalency) + string[i++] = '\0'; + + /* If this is a command to set a variable, then do that. */ + if (_rl_stricmp (string, "set") == 0) + { + char *var, *value, *e; + int s; + + var = string + i; + /* Make VAR point to start of variable name. */ + while (*var && whitespace (*var)) var++; + + /* Make VALUE point to start of value string. */ + value = var; + while (*value && whitespace (*value) == 0) value++; + if (*value) + *value++ = '\0'; + while (*value && whitespace (*value)) value++; + + /* Strip trailing whitespace from values of boolean variables. */ + if (find_boolean_var (var) >= 0) + { + /* remove trailing whitespace */ +remove_trailing: + e = value + strlen (value) - 1; + while (e >= value && whitespace (*e)) + e--; + e++; /* skip back to whitespace or EOS */ + + if (*e && e >= value) + *e = '\0'; + } + else if ((i = find_string_var (var)) >= 0) + { + /* Allow quoted strings in variable values */ + if (*value == '"') + { + i = _rl_skip_to_delim (value, 1, *value); + value[i] = '\0'; + value++; /* skip past the quote */ + } + else + goto remove_trailing; + } + + rl_variable_bind (var, value); + return 0; + } + + /* Skip any whitespace between keyname and funname. */ + for (; string[i] && whitespace (string[i]); i++); + funname = &string[i]; + + /* Now isolate funname. + For straight function names just look for whitespace, since + that will signify the end of the string. But this could be a + macro definition. In that case, the string is quoted, so skip + to the matching delimiter. We allow the backslash to quote the + delimiter characters in the macro body. */ + /* This code exists to allow whitespace in macro expansions, which + would otherwise be gobbled up by the next `for' loop.*/ + /* XXX - it may be desirable to allow backslash quoting only if " is + the quoted string delimiter, like the shell. */ + if (*funname == '\'' || *funname == '"') + { + i = _rl_skip_to_delim (string, i+1, *funname); + if (string[i]) + i++; + } + + /* Advance to the end of the string. */ + for (; string[i] && whitespace (string[i]) == 0; i++); + + /* No extra whitespace at the end of the string. */ + string[i] = '\0'; + + /* Handle equivalency bindings here. Make the left-hand side be exactly + whatever the right-hand evaluates to, including keymaps. */ + if (equivalency) + { + return 0; + } + + /* If this is a new-style key-binding, then do the binding with + rl_bind_keyseq (). Otherwise, let the older code deal with it. */ + if (*string == '"') + { + char *seq; + register int j, k, passc; + + seq = (char *)xmalloc (1 + strlen (string)); + for (j = 1, k = passc = 0; string[j]; j++) + { + /* Allow backslash to quote characters, but leave them in place. + This allows a string to end with a backslash quoting another + backslash, or with a backslash quoting a double quote. The + backslashes are left in place for rl_translate_keyseq (). */ + if (passc || (string[j] == '\\')) + { + seq[k++] = string[j]; + passc = !passc; + continue; + } + + if (string[j] == '"') + break; + + seq[k++] = string[j]; + } + seq[k] = '\0'; + + /* Binding macro? */ + if (*funname == '\'' || *funname == '"') + { + j = strlen (funname); + + /* Remove the delimiting quotes from each end of FUNNAME. */ + if (j && funname[j - 1] == *funname) + funname[j - 1] = '\0'; + + rl_macro_bind (seq, &funname[1], _rl_keymap); + } + else + rl_bind_keyseq (seq, rl_named_function (funname)); + + xfree (seq); + return 0; + } + + /* Get the actual character we want to deal with. */ + kname = strrchr (string, '-'); + if (kname == 0) + kname = string; + else + kname++; + + key = glean_key_from_name (kname); + + /* Add in control and meta bits. */ + if (substring_member_of_array (string, _rl_possible_control_prefixes)) + key = CTRL (_rl_to_upper (key)); + + if (substring_member_of_array (string, _rl_possible_meta_prefixes)) + key = META (key); + + /* Temporary. Handle old-style keyname with macro-binding. */ + if (*funname == '\'' || *funname == '"') + { + char useq[2]; + int fl = strlen (funname); + + useq[0] = key; useq[1] = '\0'; + if (fl && funname[fl - 1] == *funname) + funname[fl - 1] = '\0'; + + rl_macro_bind (useq, &funname[1], _rl_keymap); + } +#if defined (PREFIX_META_HACK) + /* Ugly, but working hack to keep prefix-meta around. */ + else if (_rl_stricmp (funname, "prefix-meta") == 0) + { + char seq[2]; + + seq[0] = key; + seq[1] = '\0'; + rl_generic_bind (ISKMAP, seq, (char *)emacs_meta_keymap, _rl_keymap); + } +#endif /* PREFIX_META_HACK */ + else + rl_bind_key (key, rl_named_function (funname)); + return 0; +} + +/* Simple structure for boolean readline variables (i.e., those that can + have one of two values; either "On" or 1 for truth, or "Off" or 0 for + false. */ + +#define V_SPECIAL 0x1 + +static const struct { + const char * const name; + int *value; + int flags; +} boolean_varlist [] = { + { "bind-tty-special-chars", &_rl_bind_stty_chars, 0 }, + { "blink-matching-paren", &rl_blink_matching_paren, V_SPECIAL }, + { "byte-oriented", &rl_byte_oriented, 0 }, +#if defined (COLOR_SUPPORT) + { "colored-stats", &_rl_colored_stats, 0 }, +#endif + { "completion-ignore-case", &_rl_completion_case_fold, 0 }, + { "completion-map-case", &_rl_completion_case_map, 0 }, + { "convert-meta", &_rl_convert_meta_chars_to_ascii, 0 }, + { "disable-completion", &rl_inhibit_completion, 0 }, + { "echo-control-characters", &_rl_echo_control_chars, 0 }, + { "enable-bracketed-paste", &_rl_enable_bracketed_paste, 0 }, + { "enable-keypad", &_rl_enable_keypad, 0 }, + { "enable-meta-key", &_rl_enable_meta, 0 }, + { "expand-tilde", &rl_complete_with_tilde_expansion, 0 }, + { "history-preserve-point", &_rl_history_preserve_point, 0 }, + { "horizontal-scroll-mode", &_rl_horizontal_scroll_mode, 0 }, + { "input-meta", &_rl_meta_flag, 0 }, + { "mark-directories", &_rl_complete_mark_directories, 0 }, + { "mark-modified-lines", &_rl_mark_modified_lines, 0 }, + { "mark-symlinked-directories", &_rl_complete_mark_symlink_dirs, 0 }, + { "match-hidden-files", &_rl_match_hidden_files, 0 }, + { "menu-complete-display-prefix", &_rl_menu_complete_prefix_first, 0 }, + { "meta-flag", &_rl_meta_flag, 0 }, + { "output-meta", &_rl_output_meta_chars, 0 }, + { "page-completions", &_rl_page_completions, 0 }, + { "prefer-visible-bell", &_rl_prefer_visible_bell, V_SPECIAL }, + { "print-completions-horizontally", &_rl_print_completions_horizontally, 0 }, + { "revert-all-at-newline", &_rl_revert_all_at_newline, 0 }, + { "show-all-if-ambiguous", &_rl_complete_show_all, 0 }, + { "show-all-if-unmodified", &_rl_complete_show_unmodified, 0 }, + { "show-mode-in-prompt", &_rl_show_mode_in_prompt, 0 }, + { "skip-completed-text", &_rl_skip_completed_text, 0 }, +#if defined (VISIBLE_STATS) + { "visible-stats", &rl_visible_stats, 0 }, +#endif /* VISIBLE_STATS */ + { (char *)NULL, (int *)NULL, 0 } +}; + +static int +find_boolean_var (name) + const char *name; +{ + register int i; + + for (i = 0; boolean_varlist[i].name; i++) + if (_rl_stricmp (name, boolean_varlist[i].name) == 0) + return i; + return -1; +} + +/* Hooks for handling special boolean variables, where a + function needs to be called or another variable needs + to be changed when they're changed. */ +static void +hack_special_boolean_var (i) + int i; +{ + const char *name; + + name = boolean_varlist[i].name; + + if (_rl_stricmp (name, "blink-matching-paren") == 0) + _rl_enable_paren_matching (rl_blink_matching_paren); + else if (_rl_stricmp (name, "prefer-visible-bell") == 0) + { + if (_rl_prefer_visible_bell) + _rl_bell_preference = VISIBLE_BELL; + else + _rl_bell_preference = AUDIBLE_BELL; + } + else if (_rl_stricmp (name, "show-mode-in-prompt") == 0) + _rl_reset_prompt (); +} + +typedef int _rl_sv_func_t PARAMS((const char *)); + +/* These *must* correspond to the array indices for the appropriate + string variable. (Though they're not used right now.) */ +#define V_BELLSTYLE 0 +#define V_COMBEGIN 1 +#define V_EDITMODE 2 +#define V_ISRCHTERM 3 +#define V_KEYMAP 4 + +#define V_STRING 1 +#define V_INT 2 + +/* Forward declarations */ +static int sv_bell_style PARAMS((const char *)); +static int sv_combegin PARAMS((const char *)); +static int sv_dispprefix PARAMS((const char *)); +static int sv_compquery PARAMS((const char *)); +static int sv_compwidth PARAMS((const char *)); +static int sv_editmode PARAMS((const char *)); +static int sv_emacs_modestr PARAMS((const char *)); +static int sv_histsize PARAMS((const char *)); +static int sv_isrchterm PARAMS((const char *)); +static int sv_keymap PARAMS((const char *)); +static int sv_seqtimeout PARAMS((const char *)); +static int sv_viins_modestr PARAMS((const char *)); +static int sv_vicmd_modestr PARAMS((const char *)); + +static const struct { + const char * const name; + int flags; + _rl_sv_func_t *set_func; +} string_varlist[] = { + { "bell-style", V_STRING, sv_bell_style }, + { "comment-begin", V_STRING, sv_combegin }, + { "completion-display-width", V_INT, sv_compwidth }, + { "completion-prefix-display-length", V_INT, sv_dispprefix }, + { "completion-query-items", V_INT, sv_compquery }, + { "editing-mode", V_STRING, sv_editmode }, + { "emacs-mode-string", V_STRING, sv_emacs_modestr }, + { "history-size", V_INT, sv_histsize }, + { "isearch-terminators", V_STRING, sv_isrchterm }, + { "keymap", V_STRING, sv_keymap }, + { "keyseq-timeout", V_INT, sv_seqtimeout }, + { "vi-cmd-mode-string", V_STRING, sv_vicmd_modestr }, + { "vi-ins-mode-string", V_STRING, sv_viins_modestr }, + { (char *)NULL, 0, (_rl_sv_func_t *)0 } +}; + +static int +find_string_var (name) + const char *name; +{ + register int i; + + for (i = 0; string_varlist[i].name; i++) + if (_rl_stricmp (name, string_varlist[i].name) == 0) + return i; + return -1; +} + +/* A boolean value that can appear in a `set variable' command is true if + the value is null or empty, `on' (case-insensitive), or "1". Any other + values result in 0 (false). */ +static int +bool_to_int (value) + const char *value; +{ + return (value == 0 || *value == '\0' || + (_rl_stricmp (value, "on") == 0) || + (value[0] == '1' && value[1] == '\0')); +} + +char * +rl_variable_value (name) + const char *name; +{ + register int i; + + /* Check for simple variables first. */ + i = find_boolean_var (name); + if (i >= 0) + return (*boolean_varlist[i].value ? "on" : "off"); + + i = find_string_var (name); + if (i >= 0) + return (_rl_get_string_variable_value (string_varlist[i].name)); + + /* Unknown variable names return NULL. */ + return 0; +} + +int +rl_variable_bind (name, value) + const char *name, *value; +{ + register int i; + int v; + + /* Check for simple variables first. */ + i = find_boolean_var (name); + if (i >= 0) + { + *boolean_varlist[i].value = bool_to_int (value); + if (boolean_varlist[i].flags & V_SPECIAL) + hack_special_boolean_var (i); + return 0; + } + + i = find_string_var (name); + + /* For the time being, unknown variable names or string names without a + handler function are simply ignored. */ + if (i < 0 || string_varlist[i].set_func == 0) + return 0; + + v = (*string_varlist[i].set_func) (value); + return v; +} + +static int +sv_editmode (value) + const char *value; +{ + if (_rl_strnicmp (value, "vi", 2) == 0) + { +#if defined (VI_MODE) + _rl_keymap = vi_insertion_keymap; + rl_editing_mode = vi_mode; +#endif /* VI_MODE */ + return 0; + } + else if (_rl_strnicmp (value, "emacs", 5) == 0) + { + _rl_keymap = emacs_standard_keymap; + rl_editing_mode = emacs_mode; + return 0; + } + return 1; +} + +static int +sv_combegin (value) + const char *value; +{ + if (value && *value) + { + FREE (_rl_comment_begin); + _rl_comment_begin = savestring (value); + return 0; + } + return 1; +} + +static int +sv_dispprefix (value) + const char *value; +{ + int nval = 0; + + if (value && *value) + { + nval = atoi (value); + if (nval < 0) + nval = 0; + } + _rl_completion_prefix_display_length = nval; + return 0; +} + +static int +sv_compquery (value) + const char *value; +{ + int nval = 100; + + if (value && *value) + { + nval = atoi (value); + if (nval < 0) + nval = 0; + } + rl_completion_query_items = nval; + return 0; +} + +static int +sv_compwidth (value) + const char *value; +{ + int nval = -1; + + if (value && *value) + nval = atoi (value); + + _rl_completion_columns = nval; + return 0; +} + +static int +sv_histsize (value) + const char *value; +{ + int nval; + + nval = 500; + if (value && *value) + { + nval = atoi (value); + if (nval < 0) + { + unstifle_history (); + return 0; + } + } + stifle_history (nval); + return 0; +} + +static int +sv_keymap (value) + const char *value; +{ + Keymap kmap; + + kmap = rl_get_keymap_by_name (value); + if (kmap) + { + rl_set_keymap (kmap); + return 0; + } + return 1; +} + +static int +sv_seqtimeout (value) + const char *value; +{ + int nval; + + nval = 0; + if (value && *value) + { + nval = atoi (value); + if (nval < 0) + nval = 0; + } + _rl_keyseq_timeout = nval; + return 0; +} + +static int +sv_bell_style (value) + const char *value; +{ + if (value == 0 || *value == '\0') + _rl_bell_preference = AUDIBLE_BELL; + else if (_rl_stricmp (value, "none") == 0 || _rl_stricmp (value, "off") == 0) + _rl_bell_preference = NO_BELL; + else if (_rl_stricmp (value, "audible") == 0 || _rl_stricmp (value, "on") == 0) + _rl_bell_preference = AUDIBLE_BELL; + else if (_rl_stricmp (value, "visible") == 0) + _rl_bell_preference = VISIBLE_BELL; + else + return 1; + return 0; +} + +static int +sv_isrchterm (value) + const char *value; +{ + int beg, end, delim; + char *v; + + if (value == 0) + return 1; + + /* Isolate the value and translate it into a character string. */ + v = savestring (value); + FREE (_rl_isearch_terminators); + if (v[0] == '"' || v[0] == '\'') + { + delim = v[0]; + for (beg = end = 1; v[end] && v[end] != delim; end++) + ; + } + else + { + for (beg = end = 0; whitespace (v[end]) == 0; end++) + ; + } + + v[end] = '\0'; + + /* The value starts at v + beg. Translate it into a character string. */ + _rl_isearch_terminators = (char *)xmalloc (2 * strlen (v) + 1); + rl_translate_keyseq (v + beg, _rl_isearch_terminators, &end); + _rl_isearch_terminators[end] = '\0'; + + xfree (v); + return 0; +} + +extern char *_rl_emacs_mode_str; + +static int +sv_emacs_modestr (value) + const char *value; +{ + if (value && *value) + { + FREE (_rl_emacs_mode_str); + _rl_emacs_mode_str = (char *)xmalloc (2 * strlen (value) + 1); + rl_translate_keyseq (value, _rl_emacs_mode_str, &_rl_emacs_modestr_len); + _rl_emacs_mode_str[_rl_emacs_modestr_len] = '\0'; + return 0; + } + else if (value) + { + FREE (_rl_emacs_mode_str); + _rl_emacs_mode_str = (char *)xmalloc (1); + _rl_emacs_mode_str[_rl_emacs_modestr_len = 0] = '\0'; + return 0; + } + else if (value == 0) + { + FREE (_rl_emacs_mode_str); + _rl_emacs_mode_str = 0; /* prompt_modestr does the right thing */ + _rl_emacs_modestr_len = 0; + return 0; + } + return 1; +} + +static int +sv_viins_modestr (value) + const char *value; +{ + if (value && *value) + { + FREE (_rl_vi_ins_mode_str); + _rl_vi_ins_mode_str = (char *)xmalloc (2 * strlen (value) + 1); + rl_translate_keyseq (value, _rl_vi_ins_mode_str, &_rl_vi_ins_modestr_len); + _rl_vi_ins_mode_str[_rl_vi_ins_modestr_len] = '\0'; + return 0; + } + else if (value) + { + FREE (_rl_vi_ins_mode_str); + _rl_vi_ins_mode_str = (char *)xmalloc (1); + _rl_vi_ins_mode_str[_rl_vi_ins_modestr_len = 0] = '\0'; + return 0; + } + else if (value == 0) + { + FREE (_rl_vi_ins_mode_str); + _rl_vi_ins_mode_str = 0; /* prompt_modestr does the right thing */ + _rl_vi_ins_modestr_len = 0; + return 0; + } + return 1; +} + +static int +sv_vicmd_modestr (value) + const char *value; +{ + if (value && *value) + { + FREE (_rl_vi_cmd_mode_str); + _rl_vi_cmd_mode_str = (char *)xmalloc (2 * strlen (value) + 1); + rl_translate_keyseq (value, _rl_vi_cmd_mode_str, &_rl_vi_cmd_modestr_len); + _rl_vi_cmd_mode_str[_rl_vi_cmd_modestr_len] = '\0'; + return 0; + } + else if (value) + { + FREE (_rl_vi_cmd_mode_str); + _rl_vi_cmd_mode_str = (char *)xmalloc (1); + _rl_vi_cmd_mode_str[_rl_vi_cmd_modestr_len = 0] = '\0'; + return 0; + } + else if (value == 0) + { + FREE (_rl_vi_cmd_mode_str); + _rl_vi_cmd_mode_str = 0; /* prompt_modestr does the right thing */ + _rl_vi_cmd_modestr_len = 0; + return 0; + } + return 1; +} + +/* Return the character which matches NAME. + For example, `Space' returns ' '. */ + +typedef struct { + const char * const name; + int value; +} assoc_list; + +static const assoc_list name_key_alist[] = { + { "DEL", 0x7f }, + { "ESC", '\033' }, + { "Escape", '\033' }, + { "LFD", '\n' }, + { "Newline", '\n' }, + { "RET", '\r' }, + { "Return", '\r' }, + { "Rubout", 0x7f }, + { "SPC", ' ' }, + { "Space", ' ' }, + { "Tab", 0x09 }, + { (char *)0x0, 0 } +}; + +static int +glean_key_from_name (name) + char *name; +{ + register int i; + + for (i = 0; name_key_alist[i].name; i++) + if (_rl_stricmp (name, name_key_alist[i].name) == 0) + return (name_key_alist[i].value); + + return (*(unsigned char *)name); /* XXX was return (*name) */ +} + +/* Auxiliary functions to manage keymaps. */ +static const struct { + const char * const name; + Keymap map; +} keymap_names[] = { + { "emacs", emacs_standard_keymap }, + { "emacs-standard", emacs_standard_keymap }, + { "emacs-meta", emacs_meta_keymap }, + { "emacs-ctlx", emacs_ctlx_keymap }, +#if defined (VI_MODE) + { "vi", vi_movement_keymap }, + { "vi-move", vi_movement_keymap }, + { "vi-command", vi_movement_keymap }, + { "vi-insert", vi_insertion_keymap }, +#endif /* VI_MODE */ + { (char *)0x0, (Keymap)0x0 } +}; + +Keymap +rl_get_keymap_by_name (name) + const char *name; +{ + register int i; + + for (i = 0; keymap_names[i].name; i++) + if (_rl_stricmp (name, keymap_names[i].name) == 0) + return (keymap_names[i].map); + return ((Keymap) NULL); +} + +char * +rl_get_keymap_name (map) + Keymap map; +{ + register int i; + for (i = 0; keymap_names[i].name; i++) + if (map == keymap_names[i].map) + return ((char *)keymap_names[i].name); + return ((char *)NULL); +} + +void +rl_set_keymap (map) + Keymap map; +{ + if (map) + _rl_keymap = map; +} + +Keymap +rl_get_keymap () +{ + return (_rl_keymap); +} + +void +rl_set_keymap_from_edit_mode () +{ + if (rl_editing_mode == emacs_mode) + _rl_keymap = emacs_standard_keymap; +#if defined (VI_MODE) + else if (rl_editing_mode == vi_mode) + _rl_keymap = vi_insertion_keymap; +#endif /* VI_MODE */ +} + +char * +rl_get_keymap_name_from_edit_mode () +{ + if (rl_editing_mode == emacs_mode) + return "emacs"; +#if defined (VI_MODE) + else if (rl_editing_mode == vi_mode) + return "vi"; +#endif /* VI_MODE */ + else + return "none"; +} + +/* **************************************************************** */ +/* */ +/* Key Binding and Function Information */ +/* */ +/* **************************************************************** */ + +/* Each of the following functions produces information about the + state of keybindings and functions known to Readline. The info + is always printed to rl_outstream, and in such a way that it can + be read back in (i.e., passed to rl_parse_and_bind ()). */ + +/* Print the names of functions known to Readline. */ +void +rl_list_funmap_names () +{ + register int i; + const char **funmap_names; + + funmap_names = rl_funmap_names (); + + if (!funmap_names) + return; + + for (i = 0; funmap_names[i]; i++) + fprintf (rl_outstream, "%s\n", funmap_names[i]); + + xfree (funmap_names); +} + +static char * +_rl_get_keyname (key) + int key; +{ + char *keyname; + int i, c; + + keyname = (char *)xmalloc (8); + + c = key; + /* Since this is going to be used to write out keysequence-function + pairs for possible inclusion in an inputrc file, we don't want to + do any special meta processing on KEY. */ + +#if 1 + /* XXX - Experimental */ + /* We might want to do this, but the old version of the code did not. */ + + /* If this is an escape character, we don't want to do any more processing. + Just add the special ESC key sequence and return. */ + if (c == ESC) + { + keyname[0] = '\\'; + keyname[1] = 'e'; + keyname[2] = '\0'; + return keyname; + } +#endif + + /* RUBOUT is translated directly into \C-? */ + if (key == RUBOUT) + { + keyname[0] = '\\'; + keyname[1] = 'C'; + keyname[2] = '-'; + keyname[3] = '?'; + keyname[4] = '\0'; + return keyname; + } + + i = 0; + /* Now add special prefixes needed for control characters. This can + potentially change C. */ + if (CTRL_CHAR (c)) + { + keyname[i++] = '\\'; + keyname[i++] = 'C'; + keyname[i++] = '-'; + c = _rl_to_lower (UNCTRL (c)); + } + + /* XXX experimental code. Turn the characters that are not ASCII or + ISO Latin 1 (128 - 159) into octal escape sequences (\200 - \237). + This changes C. */ + if (c >= 128 && c <= 159) + { + keyname[i++] = '\\'; + keyname[i++] = '2'; + c -= 128; + keyname[i++] = (c / 8) + '0'; + c = (c % 8) + '0'; + } + + /* Now, if the character needs to be quoted with a backslash, do that. */ + if (c == '\\' || c == '"') + keyname[i++] = '\\'; + + /* Now add the key, terminate the string, and return it. */ + keyname[i++] = (char) c; + keyname[i] = '\0'; + + return keyname; +} + +/* Return a NULL terminated array of strings which represent the key + sequences that are used to invoke FUNCTION in MAP. */ +char ** +rl_invoking_keyseqs_in_map (function, map) + rl_command_func_t *function; + Keymap map; +{ + register int key; + char **result; + int result_index, result_size; + + result = (char **)NULL; + result_index = result_size = 0; + + for (key = 0; key < KEYMAP_SIZE; key++) + { + switch (map[key].type) + { + case ISMACR: + /* Macros match, if, and only if, the pointers are identical. + Thus, they are treated exactly like functions in here. */ + case ISFUNC: + /* If the function in the keymap is the one we are looking for, + then add the current KEY to the list of invoking keys. */ + if (map[key].function == function) + { + char *keyname; + + keyname = _rl_get_keyname (key); + + if (result_index + 2 > result_size) + { + result_size += 10; + result = (char **)xrealloc (result, result_size * sizeof (char *)); + } + + result[result_index++] = keyname; + result[result_index] = (char *)NULL; + } + break; + + case ISKMAP: + { + char **seqs; + register int i; + + /* Find the list of keyseqs in this map which have FUNCTION as + their target. Add the key sequences found to RESULT. */ + if (map[key].function) + seqs = + rl_invoking_keyseqs_in_map (function, FUNCTION_TO_KEYMAP (map, key)); + else + break; + + if (seqs == 0) + break; + + for (i = 0; seqs[i]; i++) + { + char *keyname = (char *)xmalloc (6 + strlen (seqs[i])); + + if (key == ESC) + { + /* If ESC is the meta prefix and we're converting chars + with the eighth bit set to ESC-prefixed sequences, then + we can use \M-. Otherwise we need to use the sequence + for ESC. */ + if (_rl_convert_meta_chars_to_ascii && map[ESC].type == ISKMAP) + sprintf (keyname, "\\M-"); + else + sprintf (keyname, "\\e"); + } + else if (CTRL_CHAR (key)) + sprintf (keyname, "\\C-%c", _rl_to_lower (UNCTRL (key))); + else if (key == RUBOUT) + sprintf (keyname, "\\C-?"); + else if (key == '\\' || key == '"') + { + keyname[0] = '\\'; + keyname[1] = (char) key; + keyname[2] = '\0'; + } + else + { + keyname[0] = (char) key; + keyname[1] = '\0'; + } + + strcat (keyname, seqs[i]); + xfree (seqs[i]); + + if (result_index + 2 > result_size) + { + result_size += 10; + result = (char **)xrealloc (result, result_size * sizeof (char *)); + } + + result[result_index++] = keyname; + result[result_index] = (char *)NULL; + } + + xfree (seqs); + } + break; + } + } + return (result); +} + +/* Return a NULL terminated array of strings which represent the key + sequences that can be used to invoke FUNCTION using the current keymap. */ +char ** +rl_invoking_keyseqs (function) + rl_command_func_t *function; +{ + return (rl_invoking_keyseqs_in_map (function, _rl_keymap)); +} + +/* Print all of the functions and their bindings to rl_outstream. If + PRINT_READABLY is non-zero, then print the output in such a way + that it can be read back in. */ +void +rl_function_dumper (print_readably) + int print_readably; +{ + register int i; + const char **names; + const char *name; + + names = rl_funmap_names (); + + fprintf (rl_outstream, "\n"); + + for (i = 0; name = names[i]; i++) + { + rl_command_func_t *function; + char **invokers; + + function = rl_named_function (name); + invokers = rl_invoking_keyseqs_in_map (function, _rl_keymap); + + if (print_readably) + { + if (!invokers) + fprintf (rl_outstream, "# %s (not bound)\n", name); + else + { + register int j; + + for (j = 0; invokers[j]; j++) + { + fprintf (rl_outstream, "\"%s\": %s\n", + invokers[j], name); + xfree (invokers[j]); + } + + xfree (invokers); + } + } + else + { + if (!invokers) + fprintf (rl_outstream, "%s is not bound to any keys\n", + name); + else + { + register int j; + + fprintf (rl_outstream, "%s can be found on ", name); + + for (j = 0; invokers[j] && j < 5; j++) + { + fprintf (rl_outstream, "\"%s\"%s", invokers[j], + invokers[j + 1] ? ", " : ".\n"); + } + + if (j == 5 && invokers[j]) + fprintf (rl_outstream, "...\n"); + + for (j = 0; invokers[j]; j++) + xfree (invokers[j]); + + xfree (invokers); + } + } + } + + xfree (names); +} + +/* Print all of the current functions and their bindings to + rl_outstream. If an explicit argument is given, then print + the output in such a way that it can be read back in. */ +int +rl_dump_functions (count, key) + int count, key; +{ + if (rl_dispatching) + fprintf (rl_outstream, "\r\n"); + rl_function_dumper (rl_explicit_arg); + rl_on_new_line (); + return (0); +} + +static void +_rl_macro_dumper_internal (print_readably, map, prefix) + int print_readably; + Keymap map; + char *prefix; +{ + register int key; + char *keyname, *out; + int prefix_len; + + for (key = 0; key < KEYMAP_SIZE; key++) + { + switch (map[key].type) + { + case ISMACR: + keyname = _rl_get_keyname (key); + out = _rl_untranslate_macro_value ((char *)map[key].function, 0); + + if (print_readably) + fprintf (rl_outstream, "\"%s%s\": \"%s\"\n", prefix ? prefix : "", + keyname, + out ? out : ""); + else + fprintf (rl_outstream, "%s%s outputs %s\n", prefix ? prefix : "", + keyname, + out ? out : ""); + xfree (keyname); + xfree (out); + break; + case ISFUNC: + break; + case ISKMAP: + prefix_len = prefix ? strlen (prefix) : 0; + if (key == ESC) + { + keyname = (char *)xmalloc (3 + prefix_len); + if (prefix) + strcpy (keyname, prefix); + keyname[prefix_len] = '\\'; + keyname[prefix_len + 1] = 'e'; + keyname[prefix_len + 2] = '\0'; + } + else + { + keyname = _rl_get_keyname (key); + if (prefix) + { + out = (char *)xmalloc (strlen (keyname) + prefix_len + 1); + strcpy (out, prefix); + strcpy (out + prefix_len, keyname); + xfree (keyname); + keyname = out; + } + } + + _rl_macro_dumper_internal (print_readably, FUNCTION_TO_KEYMAP (map, key), keyname); + xfree (keyname); + break; + } + } +} + +void +rl_macro_dumper (print_readably) + int print_readably; +{ + _rl_macro_dumper_internal (print_readably, _rl_keymap, (char *)NULL); +} + +int +rl_dump_macros (count, key) + int count, key; +{ + if (rl_dispatching) + fprintf (rl_outstream, "\r\n"); + rl_macro_dumper (rl_explicit_arg); + rl_on_new_line (); + return (0); +} + +static char * +_rl_get_string_variable_value (name) + const char *name; +{ + static char numbuf[32]; + char *ret; + + if (_rl_stricmp (name, "bell-style") == 0) + { + switch (_rl_bell_preference) + { + case NO_BELL: + return "none"; + case VISIBLE_BELL: + return "visible"; + case AUDIBLE_BELL: + default: + return "audible"; + } + } + else if (_rl_stricmp (name, "comment-begin") == 0) + return (_rl_comment_begin ? _rl_comment_begin : RL_COMMENT_BEGIN_DEFAULT); + else if (_rl_stricmp (name, "completion-display-width") == 0) + { + sprintf (numbuf, "%d", _rl_completion_columns); + return (numbuf); + } + else if (_rl_stricmp (name, "completion-prefix-display-length") == 0) + { + sprintf (numbuf, "%d", _rl_completion_prefix_display_length); + return (numbuf); + } + else if (_rl_stricmp (name, "completion-query-items") == 0) + { + sprintf (numbuf, "%d", rl_completion_query_items); + return (numbuf); + } + else if (_rl_stricmp (name, "editing-mode") == 0) + return (rl_get_keymap_name_from_edit_mode ()); + else if (_rl_stricmp (name, "history-size") == 0) + { + sprintf (numbuf, "%d", history_is_stifled() ? history_max_entries : 0); + return (numbuf); + } + else if (_rl_stricmp (name, "isearch-terminators") == 0) + { + if (_rl_isearch_terminators == 0) + return 0; + ret = _rl_untranslate_macro_value (_rl_isearch_terminators, 0); + if (ret) + { + strncpy (numbuf, ret, sizeof (numbuf) - 1); + xfree (ret); + numbuf[sizeof(numbuf) - 1] = '\0'; + } + else + numbuf[0] = '\0'; + return numbuf; + } + else if (_rl_stricmp (name, "keymap") == 0) + { + ret = rl_get_keymap_name (_rl_keymap); + if (ret == 0) + ret = rl_get_keymap_name_from_edit_mode (); + return (ret ? ret : "none"); + } + else if (_rl_stricmp (name, "keyseq-timeout") == 0) + { + sprintf (numbuf, "%d", _rl_keyseq_timeout); + return (numbuf); + } + else + return (0); +} + +void +rl_variable_dumper (print_readably) + int print_readably; +{ + int i; + char *v; + + for (i = 0; boolean_varlist[i].name; i++) + { + if (print_readably) + fprintf (rl_outstream, "set %s %s\n", boolean_varlist[i].name, + *boolean_varlist[i].value ? "on" : "off"); + else + fprintf (rl_outstream, "%s is set to `%s'\n", boolean_varlist[i].name, + *boolean_varlist[i].value ? "on" : "off"); + } + + for (i = 0; string_varlist[i].name; i++) + { + v = _rl_get_string_variable_value (string_varlist[i].name); + if (v == 0) /* _rl_isearch_terminators can be NULL */ + continue; + if (print_readably) + fprintf (rl_outstream, "set %s %s\n", string_varlist[i].name, v); + else + fprintf (rl_outstream, "%s is set to `%s'\n", string_varlist[i].name, v); + } +} + +/* Print all of the current variables and their values to + rl_outstream. If an explicit argument is given, then print + the output in such a way that it can be read back in. */ +int +rl_dump_variables (count, key) + int count, key; +{ + if (rl_dispatching) + fprintf (rl_outstream, "\r\n"); + rl_variable_dumper (rl_explicit_arg); + rl_on_new_line (); + return (0); +} + +/* Return non-zero if any members of ARRAY are a substring in STRING. */ +static int +substring_member_of_array (string, array) + const char *string; + const char * const *array; +{ + while (*array) + { + if (_rl_strindex (string, *array)) + return (1); + array++; + } + return (0); +} diff --git a/lib/readline/colors.c b/lib/readline/colors.c index 89d9035f..fad97b79 100644 --- a/lib/readline/colors.c +++ b/lib/readline/colors.c @@ -98,6 +98,25 @@ _rl_set_normal_color (void) } } +bool +_rl_print_filename_color (void) +{ + struct bin_str *s; + + s = &_rl_color_indicator[C_FILE]; + if (s->string != NULL) + { + if (is_colored (C_NORM)) + restore_default_color (); + _rl_put_indicator (&_rl_color_indicator[C_LEFT]); + _rl_put_indicator (s); + _rl_put_indicator (&_rl_color_indicator[C_RIGHT]); + return 0; + } + else + return 1; +} + /* Returns whether any color sequence was printed. */ bool _rl_print_color_indicator (char *f) diff --git a/lib/readline/colors.c~ b/lib/readline/colors.c~ new file mode 100644 index 00000000..7b9a4e90 --- /dev/null +++ b/lib/readline/colors.c~ @@ -0,0 +1,271 @@ +/* `dir', `vdir' and `ls' directory listing programs for GNU. + + Modified by Chet Ramey for Readline. + + Copyright (C) 1985, 1988, 1990-1991, 1995-2010, 2012 Free Software Foundation, + Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* Written by Richard Stallman and David MacKenzie. */ + +/* Color support by Peter Anvin <Peter.Anvin@linux.org> and Dennis + Flaherty <dennisf@denix.elk.miles.com> based on original patches by + Greg Lee <lee@uhunix.uhcc.hawaii.edu>. */ + +#define READLINE_LIBRARY + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#include "rlconf.h" + +#include <stdio.h> + +#include "posixstat.h" // stat related macros (S_ISREG, ...) +#include <fcntl.h> // S_ISUID + +// strlen() +#if defined (HAVE_STRING_H) +# include <string.h> +#else /* !HAVE_STRING_H */ +# include <strings.h> +#endif /* !HAVE_STRING_H */ + +// abort() +#if defined (HAVE_STDLIB_H) +# include <stdlib.h> +#else +# include "ansi_stdlib.h" +#endif /* HAVE_STDLIB_H */ + +#include "readline.h" +#include "rldefs.h" + +#ifdef COLOR_SUPPORT + +#include "xmalloc.h" +#include "colors.h" + +static bool is_colored (enum indicator_no type); +static void restore_default_color (void); + +COLOR_EXT_TYPE *_rl_color_ext_list = 0; + +/* Output a color indicator (which may contain nulls). */ +void +_rl_put_indicator (const struct bin_str *ind) { + fwrite (ind->string, ind->len, 1, rl_outstream); +} + +static bool +is_colored (enum indicator_no colored_filetype) +{ + size_t len = _rl_color_indicator[colored_filetype].len; + char const *s = _rl_color_indicator[colored_filetype].string; + return ! (len == 0 + || (len == 1 && strncmp (s, "0", 1) == 0) + || (len == 2 && strncmp (s, "00", 2) == 0)); +} + +static void +restore_default_color (void) +{ + _rl_put_indicator (&_rl_color_indicator[C_LEFT]); + _rl_put_indicator (&_rl_color_indicator[C_RIGHT]); +} + +void +_rl_set_normal_color (void) +{ + if (is_colored (C_NORM)) + { + _rl_put_indicator (&_rl_color_indicator[C_LEFT]); + _rl_put_indicator (&_rl_color_indicator[C_NORM]); + _rl_put_indicator (&_rl_color_indicator[C_RIGHT]); + } +} + +bool +_rl_print_filename_color (void) +{ + struct bin_str *s; + + s = &_rl_color_indicator[C_FILE]; + if (s->string != NULL) + { + /* Need to reset so not dealing with attribute combinations */ + if (is_colored (C_NORM)) + restore_default_color (); + _rl_put_indicator (&_rl_color_indicator[C_LEFT]); + _rl_put_indicator (s); + _rl_put_indicator (&_rl_color_indicator[C_RIGHT]); + return 0; + } + else + return 1; +} + +/* Returns whether any color sequence was printed. */ +bool +_rl_print_color_indicator (char *f) +{ + enum indicator_no colored_filetype; + COLOR_EXT_TYPE *ext; /* Color extension */ + size_t len; /* Length of name */ + + const char* name; + char *filename; + struct stat astat; + mode_t mode; + int linkok; + + int stat_ok; + + name = f; + + /* This should already have undergone tilde expansion */ + filename = 0; + if (rl_filename_stat_hook) + { + filename = savestring (f); + (*rl_filename_stat_hook) (&filename); + name = filename; + } + +#if defined (HAVE_LSTAT) + stat_ok = lstat(name, &astat); +#else + stat_ok = stat(name, &astat); +#endif + if( stat_ok == 0 ) { + mode = astat.st_mode; + linkok = 1; //f->linkok; + } + else + linkok = -1; + + /* Is this a nonexistent file? If so, linkok == -1. */ + + if (linkok == -1 && _rl_color_indicator[C_MISSING].string != NULL) + colored_filetype = C_MISSING; + else if(stat_ok != 0) + { + static enum indicator_no filetype_indicator[] = FILETYPE_INDICATORS; + colored_filetype = filetype_indicator[normal]; //f->filetype]; + } + else + { + if (S_ISREG (mode)) + { + colored_filetype = C_FILE; + + if ((mode & S_ISUID) != 0 && is_colored (C_SETUID)) + colored_filetype = C_SETUID; + else if ((mode & S_ISGID) != 0 && is_colored (C_SETGID)) + colored_filetype = C_SETGID; + else if (is_colored (C_CAP) && 0) //f->has_capability) + colored_filetype = C_CAP; + else if ((mode & S_IXUGO) != 0 && is_colored (C_EXEC)) + colored_filetype = C_EXEC; + else if ((1 < astat.st_nlink) && is_colored (C_MULTIHARDLINK)) + colored_filetype = C_MULTIHARDLINK; + } + else if (S_ISDIR (mode)) + { + colored_filetype = C_DIR; + +#if defined (S_ISVTX) + if ((mode & S_ISVTX) && (mode & S_IWOTH) + && is_colored (C_STICKY_OTHER_WRITABLE)) + colored_filetype = C_STICKY_OTHER_WRITABLE; + else +#endif + if ((mode & S_IWOTH) != 0 && is_colored (C_OTHER_WRITABLE)) + colored_filetype = C_OTHER_WRITABLE; +#if defined (S_ISVTX) + else if ((mode & S_ISVTX) != 0 && is_colored (C_STICKY)) + colored_filetype = C_STICKY; +#endif + } + else if (S_ISLNK (mode)) + colored_filetype = ((linkok == 0 + && (!strncmp (_rl_color_indicator[C_LINK].string, "target", 6) + || _rl_color_indicator[C_ORPHAN].string)) + ? C_ORPHAN : C_LINK); + else if (S_ISFIFO (mode)) + colored_filetype = C_FIFO; + else if (S_ISSOCK (mode)) + colored_filetype = C_SOCK; + else if (S_ISBLK (mode)) + colored_filetype = C_BLK; + else if (S_ISCHR (mode)) + colored_filetype = C_CHR; + else + { + /* Classify a file of some other type as C_ORPHAN. */ + colored_filetype = C_ORPHAN; + } + } + + /* Check the file's suffix only if still classified as C_FILE. */ + ext = NULL; + if (colored_filetype == C_FILE) + { + /* Test if NAME has a recognized suffix. */ + len = strlen (name); + name += len; /* Pointer to final \0. */ + for (ext = _rl_color_ext_list; ext != NULL; ext = ext->next) + { + if (ext->ext.len <= len + && strncmp (name - ext->ext.len, ext->ext.string, + ext->ext.len) == 0) + break; + } + } + + free (filename); /* NULL or savestring return value */ + + { + const struct bin_str *const s + = ext ? &(ext->seq) : &_rl_color_indicator[colored_filetype]; + if (s->string != NULL) + { + /* Need to reset so not dealing with attribute combinations */ + if (is_colored (C_NORM)) + restore_default_color (); + _rl_put_indicator (&_rl_color_indicator[C_LEFT]); + _rl_put_indicator (s); + _rl_put_indicator (&_rl_color_indicator[C_RIGHT]); + return 0; + } + else + return 1; + } +} + +void +_rl_prep_non_filename_text (void) +{ + if (_rl_color_indicator[C_END].string != NULL) + _rl_put_indicator (&_rl_color_indicator[C_END]); + else + { + _rl_put_indicator (&_rl_color_indicator[C_LEFT]); + _rl_put_indicator (&_rl_color_indicator[C_RESET]); + _rl_put_indicator (&_rl_color_indicator[C_RIGHT]); + } +} +#endif /* COLOR_SUPPORT */ diff --git a/lib/readline/colors.h b/lib/readline/colors.h index fc926e53..3e0371cc 100644 --- a/lib/readline/colors.h +++ b/lib/readline/colors.h @@ -116,6 +116,7 @@ enum filetype extern void _rl_put_indicator (const struct bin_str *ind); extern void _rl_set_normal_color (void); +extern bool _rl_print_filename_color (void); extern bool _rl_print_color_indicator (char *f); extern void _rl_prep_non_filename_text (void); diff --git a/lib/readline/colors.h~ b/lib/readline/colors.h~ new file mode 100644 index 00000000..fc926e53 --- /dev/null +++ b/lib/readline/colors.h~ @@ -0,0 +1,122 @@ +/* `dir', `vdir' and `ls' directory listing programs for GNU. + + Modified by Chet Ramey for Readline. + + Copyright (C) 1985, 1988, 1990-1991, 1995-2010, 2012 Free Software Foundation, + Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* Written by Richard Stallman and David MacKenzie. */ + +/* Color support by Peter Anvin <Peter.Anvin@linux.org> and Dennis + Flaherty <dennisf@denix.elk.miles.com> based on original patches by + Greg Lee <lee@uhunix.uhcc.hawaii.edu>. */ + +#ifndef _COLORS_H_ +#define _COLORS_H_ + +#include <stdio.h> // size_t + +#if defined(__TANDEM) && defined(HAVE_STDBOOL_H) && (__STDC_VERSION__ < 199901L) +typedef int _Bool; +#endif + +#if defined (HAVE_STDBOOL_H) +# include <stdbool.h> // bool +#else +typedef int _rl_bool_t; + +#ifdef bool +# undef bool +#endif +#define bool _rl_bool_t + +#ifndef true +# define true 1 +# define false 0 +#endif + +#endif /* !HAVE_STDBOOL_H */ + +/* Null is a valid character in a color indicator (think about Epson + printers, for example) so we have to use a length/buffer string + type. */ +struct bin_str + { + size_t len; + const char *string; + }; + +/* file type indicators (dir, sock, fifo, ...) + Default value is initialized in parse-colors.c. + It is then modified from the values of $LS_COLORS. */ +extern struct bin_str _rl_color_indicator[]; + +/* The LS_COLORS variable is in a termcap-like format. */ +typedef struct _color_ext_type + { + struct bin_str ext; /* The extension we're looking for */ + struct bin_str seq; /* The sequence to output when we do */ + struct _color_ext_type *next; /* Next in list */ + } COLOR_EXT_TYPE; + +/* file extensions indicators (.txt, .log, .jpg, ...) + Values are taken from $LS_COLORS in rl_parse_colors(). */ +extern COLOR_EXT_TYPE *_rl_color_ext_list; + +#define FILETYPE_INDICATORS \ + { \ + C_ORPHAN, C_FIFO, C_CHR, C_DIR, C_BLK, C_FILE, \ + C_LINK, C_SOCK, C_FILE, C_DIR \ + } + +/* Whether we used any colors in the output so far. If so, we will + need to restore the default color later. If not, we will need to + call prep_non_filename_text before using color for the first time. */ + +enum indicator_no + { + C_LEFT, C_RIGHT, C_END, C_RESET, C_NORM, C_FILE, C_DIR, C_LINK, + C_FIFO, C_SOCK, + C_BLK, C_CHR, C_MISSING, C_ORPHAN, C_EXEC, C_DOOR, C_SETUID, C_SETGID, + C_STICKY, C_OTHER_WRITABLE, C_STICKY_OTHER_WRITABLE, C_CAP, C_MULTIHARDLINK, + C_CLR_TO_EOL + }; + + +#if !S_IXUGO +# define S_IXUGO (S_IXUSR | S_IXGRP | S_IXOTH) +#endif + +enum filetype + { + unknown, + fifo, + chardev, + directory, + blockdev, + normal, + symbolic_link, + sock, + whiteout, + arg_directory + }; + +extern void _rl_put_indicator (const struct bin_str *ind); +extern void _rl_set_normal_color (void); +extern bool _rl_print_color_indicator (char *f); +extern void _rl_prep_non_filename_text (void); + +#endif /* !_COLORS_H_ */ diff --git a/lib/readline/complete.c b/lib/readline/complete.c index 8d768d2d..ebd11e60 100644 --- a/lib/readline/complete.c +++ b/lib/readline/complete.c @@ -662,6 +662,19 @@ colored_stat_end () _rl_prep_non_filename_text (); _rl_put_indicator (&_rl_color_indicator[C_CLR_TO_EOL]); } + +static int +colored_prefix_start () +{ + _rl_set_normal_color (); + return (_rl_print_filename_color ()); +} + +static void +colored_prefix_end () +{ + colored_stat_end (); /* for now */ +} #endif /* Return the portion of PATHNAME that should be output when listing diff --git a/lib/readline/complete.c~ b/lib/readline/complete.c~ new file mode 100644 index 00000000..8d768d2d --- /dev/null +++ b/lib/readline/complete.c~ @@ -0,0 +1,2891 @@ +/* complete.c -- filename completion for readline. */ + +/* Copyright (C) 1987-2012 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Readline is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define READLINE_LIBRARY + +#if defined (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 + +#include <signal.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 <stdio.h> + +#include <errno.h> +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +#if defined (HAVE_PWD_H) +#include <pwd.h> +#endif + +#include "posixdir.h" +#include "posixstat.h" + +/* System-specific feature definitions and include files. */ +#include "rldefs.h" +#include "rlmbutil.h" + +/* Some standard library routines. */ +#include "readline.h" +#include "xmalloc.h" +#include "rlprivate.h" + +#if defined (COLOR_SUPPORT) +# include "colors.h" +#endif + +#ifdef __STDC__ +typedef int QSFUNC (const void *, const void *); +#else +typedef int QSFUNC (); +#endif + +#ifdef HAVE_LSTAT +# define LSTAT lstat +#else +# define LSTAT stat +#endif + +/* Unix version of a hidden file. Could be different on other systems. */ +#define HIDDEN_FILE(fname) ((fname)[0] == '.') + +/* Most systems don't declare getpwent in <pwd.h> if _POSIX_SOURCE is + defined. */ +#if defined (HAVE_GETPWENT) && (!defined (HAVE_GETPW_DECLS) || defined (_POSIX_SOURCE)) +extern struct passwd *getpwent PARAMS((void)); +#endif /* HAVE_GETPWENT && (!HAVE_GETPW_DECLS || _POSIX_SOURCE) */ + +/* If non-zero, then this is the address of a function to call when + completing a word would normally display the list of possible matches. + This function is called instead of actually doing the display. + It takes three arguments: (char **matches, int num_matches, int max_length) + where MATCHES is the array of strings that matched, NUM_MATCHES is the + number of strings in that array, and MAX_LENGTH is the length of the + longest string in that array. */ +rl_compdisp_func_t *rl_completion_display_matches_hook = (rl_compdisp_func_t *)NULL; + +#if defined (VISIBLE_STATS) || defined (COLOR_SUPPORT) +# if !defined (X_OK) +# define X_OK 1 +# endif +#endif + +#if defined (VISIBLE_STATS) +static int stat_char PARAMS((char *)); +#endif + +#if defined (COLOR_SUPPORT) +static int colored_stat_start PARAMS((char *)); +static void colored_stat_end PARAMS((void)); +#endif + +static int path_isdir PARAMS((const char *)); + +static char *rl_quote_filename PARAMS((char *, int, char *)); + +static void _rl_complete_sigcleanup PARAMS((int, void *)); + +static void set_completion_defaults PARAMS((int)); +static int get_y_or_n PARAMS((int)); +static int _rl_internal_pager PARAMS((int)); +static char *printable_part PARAMS((char *)); +static int fnwidth PARAMS((const char *)); +static int fnprint PARAMS((const char *, int)); +static int print_filename PARAMS((char *, char *, int)); + +static char **gen_completion_matches PARAMS((char *, int, int, rl_compentry_func_t *, int, int)); + +static char **remove_duplicate_matches PARAMS((char **)); +static void insert_match PARAMS((char *, int, int, char *)); +static int append_to_match PARAMS((char *, int, int, int)); +static void insert_all_matches PARAMS((char **, int, char *)); +static int complete_fncmp PARAMS((const char *, int, const char *, int)); +static void display_matches PARAMS((char **)); +static int compute_lcd_of_matches PARAMS((char **, int, const char *)); +static int postprocess_matches PARAMS((char ***, int)); +static int complete_get_screenwidth PARAMS((void)); + +static char *make_quoted_replacement PARAMS((char *, int, char *)); + +/* **************************************************************** */ +/* */ +/* Completion matching, from readline's point of view. */ +/* */ +/* **************************************************************** */ + +/* Variables known only to the readline library. */ + +/* If non-zero, non-unique completions always show the list of matches. */ +int _rl_complete_show_all = 0; + +/* If non-zero, non-unique completions show the list of matches, unless it + is not possible to do partial completion and modify the line. */ +int _rl_complete_show_unmodified = 0; + +/* If non-zero, completed directory names have a slash appended. */ +int _rl_complete_mark_directories = 1; + +/* If non-zero, the symlinked directory completion behavior introduced in + readline-4.2a is disabled, and symlinks that point to directories have + a slash appended (subject to the value of _rl_complete_mark_directories). + This is user-settable via the mark-symlinked-directories variable. */ +int _rl_complete_mark_symlink_dirs = 0; + +/* If non-zero, completions are printed horizontally in alphabetical order, + like `ls -x'. */ +int _rl_print_completions_horizontally; + +/* Non-zero means that case is not significant in filename completion. */ +#if defined (__MSDOS__) && !defined (__DJGPP__) +int _rl_completion_case_fold = 1; +#else +int _rl_completion_case_fold = 0; +#endif + +/* Non-zero means that `-' and `_' are equivalent when comparing filenames + for completion. */ +int _rl_completion_case_map = 0; + +/* If zero, don't match hidden files (filenames beginning with a `.' on + Unix) when doing filename completion. */ +int _rl_match_hidden_files = 1; + +/* Length in characters of a common prefix replaced with an ellipsis (`...') + when displaying completion matches. Matches whose printable portion has + more than this number of displaying characters in common will have the common + display prefix replaced with an ellipsis. */ +int _rl_completion_prefix_display_length = 0; + +/* The readline-private number of screen columns to use when displaying + matches. If < 0 or > _rl_screenwidth, it is ignored. */ +int _rl_completion_columns = -1; + +/* Global variables available to applications using readline. */ + +#if defined (VISIBLE_STATS) +/* Non-zero means add an additional character to each filename displayed + during listing completion iff rl_filename_completion_desired which helps + to indicate the type of file being listed. */ +int rl_visible_stats = 0; +#endif /* VISIBLE_STATS */ + +#if defined (COLOR_SUPPORT) +/* Non-zero means to use colors to indicate file type when listing possible + completions. The colors used are taken from $LS_COLORS, if set. */ +int _rl_colored_stats = 0; +#endif + +/* If non-zero, when completing in the middle of a word, don't insert + characters from the match that match characters following point in + the word. This means, for instance, completing when the cursor is + after the `e' in `Makefile' won't result in `Makefilefile'. */ +int _rl_skip_completed_text = 0; + +/* If non-zero, menu completion displays the common prefix first in the + cycle of possible completions instead of the last. */ +int _rl_menu_complete_prefix_first = 0; + +/* If non-zero, then this is the address of a function to call when + completing on a directory name. The function is called with + the address of a string (the current directory name) as an arg. */ +rl_icppfunc_t *rl_directory_completion_hook = (rl_icppfunc_t *)NULL; + +rl_icppfunc_t *rl_directory_rewrite_hook = (rl_icppfunc_t *)NULL; + +rl_icppfunc_t *rl_filename_stat_hook = (rl_icppfunc_t *)NULL; + +/* If non-zero, this is the address of a function to call when reading + directory entries from the filesystem for completion and comparing + them to the partial word to be completed. The function should + either return its first argument (if no conversion takes place) or + newly-allocated memory. This can, for instance, convert filenames + between character sets for comparison against what's typed at the + keyboard. The returned value is what is added to the list of + matches. The second argument is the length of the filename to be + converted. */ +rl_dequote_func_t *rl_filename_rewrite_hook = (rl_dequote_func_t *)NULL; + +/* Non-zero means readline completion functions perform tilde expansion. */ +int rl_complete_with_tilde_expansion = 0; + +/* Pointer to the generator function for completion_matches (). + NULL means to use rl_filename_completion_function (), the default filename + completer. */ +rl_compentry_func_t *rl_completion_entry_function = (rl_compentry_func_t *)NULL; + +/* Pointer to generator function for rl_menu_complete (). NULL means to use + *rl_completion_entry_function (see above). */ +rl_compentry_func_t *rl_menu_completion_entry_function = (rl_compentry_func_t *)NULL; + +/* Pointer to alternative function to create matches. + Function is called with TEXT, START, and END. + START and END are indices in RL_LINE_BUFFER saying what the boundaries + of TEXT are. + If this function exists and returns NULL then call the value of + rl_completion_entry_function to try to match, otherwise use the + array of strings returned. */ +rl_completion_func_t *rl_attempted_completion_function = (rl_completion_func_t *)NULL; + +/* Non-zero means to suppress normal filename completion after the + user-specified completion function has been called. */ +int rl_attempted_completion_over = 0; + +/* Set to a character indicating the type of completion being performed + by rl_complete_internal, available for use by application completion + functions. */ +int rl_completion_type = 0; + +/* Up to this many items will be displayed in response to a + possible-completions call. After that, we ask the user if + she is sure she wants to see them all. A negative value means + don't ask. */ +int rl_completion_query_items = 100; + +int _rl_page_completions = 1; + +/* The basic list of characters that signal a break between words for the + completer routine. The contents of this variable is what breaks words + in the shell, i.e. " \t\n\"\\'`@$><=" */ +const char *rl_basic_word_break_characters = " \t\n\"\\'`@$><=;|&{("; /* }) */ + +/* List of basic quoting characters. */ +const char *rl_basic_quote_characters = "\"'"; + +/* The list of characters that signal a break between words for + rl_complete_internal. The default list is the contents of + rl_basic_word_break_characters. */ +/*const*/ char *rl_completer_word_break_characters = (/*const*/ char *)NULL; + +/* Hook function to allow an application to set the completion word + break characters before readline breaks up the line. Allows + position-dependent word break characters. */ +rl_cpvfunc_t *rl_completion_word_break_hook = (rl_cpvfunc_t *)NULL; + +/* List of characters which can be used to quote a substring of the line. + Completion occurs on the entire substring, and within the substring + rl_completer_word_break_characters are treated as any other character, + unless they also appear within this list. */ +const char *rl_completer_quote_characters = (const char *)NULL; + +/* List of characters that should be quoted in filenames by the completer. */ +const char *rl_filename_quote_characters = (const char *)NULL; + +/* List of characters that are word break characters, but should be left + in TEXT when it is passed to the completion function. The shell uses + this to help determine what kind of completing to do. */ +const char *rl_special_prefixes = (const char *)NULL; + +/* If non-zero, then disallow duplicates in the matches. */ +int rl_ignore_completion_duplicates = 1; + +/* Non-zero means that the results of the matches are to be treated + as filenames. This is ALWAYS zero on entry, and can only be changed + within a completion entry finder function. */ +int rl_filename_completion_desired = 0; + +/* Non-zero means that the results of the matches are to be quoted using + double quotes (or an application-specific quoting mechanism) if the + filename contains any characters in rl_filename_quote_chars. This is + ALWAYS non-zero on entry, and can only be changed within a completion + entry finder function. */ +int rl_filename_quoting_desired = 1; + +/* This function, if defined, is called by the completer when real + filename completion is done, after all the matching names have been + generated. It is passed a (char**) known as matches in the code below. + It consists of a NULL-terminated array of pointers to potential + matching strings. The 1st element (matches[0]) is the maximal + substring that is common to all matches. This function can re-arrange + the list of matches as required, but all elements of the array must be + free()'d if they are deleted. The main intent of this function is + to implement FIGNORE a la SunOS csh. */ +rl_compignore_func_t *rl_ignore_some_completions_function = (rl_compignore_func_t *)NULL; + +/* Set to a function to quote a filename in an application-specific fashion. + Called with the text to quote, the type of match found (single or multiple) + and a pointer to the quoting character to be used, which the function can + reset if desired. */ +rl_quote_func_t *rl_filename_quoting_function = rl_quote_filename; + +/* Function to call to remove quoting characters from a filename. Called + before completion is attempted, so the embedded quotes do not interfere + with matching names in the file system. Readline doesn't do anything + with this; it's set only by applications. */ +rl_dequote_func_t *rl_filename_dequoting_function = (rl_dequote_func_t *)NULL; + +/* Function to call to decide whether or not a word break character is + quoted. If a character is quoted, it does not break words for the + completer. */ +rl_linebuf_func_t *rl_char_is_quoted_p = (rl_linebuf_func_t *)NULL; + +/* If non-zero, the completion functions don't append anything except a + possible closing quote. This is set to 0 by rl_complete_internal and + may be changed by an application-specific completion function. */ +int rl_completion_suppress_append = 0; + +/* Character appended to completed words when at the end of the line. The + default is a space. */ +int rl_completion_append_character = ' '; + +/* If non-zero, the completion functions don't append any closing quote. + This is set to 0 by rl_complete_internal and may be changed by an + application-specific completion function. */ +int rl_completion_suppress_quote = 0; + +/* Set to any quote character readline thinks it finds before any application + completion function is called. */ +int rl_completion_quote_character; + +/* Set to a non-zero value if readline found quoting anywhere in the word to + be completed; set before any application completion function is called. */ +int rl_completion_found_quote; + +/* If non-zero, a slash will be appended to completed filenames that are + symbolic links to directory names, subject to the value of the + mark-directories variable (which is user-settable). This exists so + that application completion functions can override the user's preference + (set via the mark-symlinked-directories variable) if appropriate. + It's set to the value of _rl_complete_mark_symlink_dirs in + rl_complete_internal before any application-specific completion + function is called, so without that function doing anything, the user's + preferences are honored. */ +int rl_completion_mark_symlink_dirs; + +/* If non-zero, inhibit completion (temporarily). */ +int rl_inhibit_completion; + +/* Set to the last key used to invoke one of the completion functions */ +int rl_completion_invoking_key; + +/* If non-zero, sort the completion matches. On by default. */ +int rl_sort_completion_matches = 1; + +/* Variables local to this file. */ + +/* Local variable states what happened during the last completion attempt. */ +static int completion_changed_buffer; + +/* The result of the query to the user about displaying completion matches */ +static int completion_y_or_n; + +/*************************************/ +/* */ +/* Bindable completion functions */ +/* */ +/*************************************/ + +/* Complete the word at or before point. You have supplied the function + that does the initial simple matching selection algorithm (see + rl_completion_matches ()). The default is to do filename completion. */ +int +rl_complete (ignore, invoking_key) + int ignore, invoking_key; +{ + rl_completion_invoking_key = invoking_key; + + if (rl_inhibit_completion) + return (_rl_insert_char (ignore, invoking_key)); + else if (rl_last_func == rl_complete && !completion_changed_buffer) + return (rl_complete_internal ('?')); + else if (_rl_complete_show_all) + return (rl_complete_internal ('!')); + else if (_rl_complete_show_unmodified) + return (rl_complete_internal ('@')); + else + return (rl_complete_internal (TAB)); +} + +/* List the possible completions. See description of rl_complete (). */ +int +rl_possible_completions (ignore, invoking_key) + int ignore, invoking_key; +{ + rl_completion_invoking_key = invoking_key; + return (rl_complete_internal ('?')); +} + +int +rl_insert_completions (ignore, invoking_key) + int ignore, invoking_key; +{ + rl_completion_invoking_key = invoking_key; + return (rl_complete_internal ('*')); +} + +/* Return the correct value to pass to rl_complete_internal performing + the same tests as rl_complete. This allows consecutive calls to an + application's completion function to list possible completions and for + an application-specific completion function to honor the + show-all-if-ambiguous readline variable. */ +int +rl_completion_mode (cfunc) + rl_command_func_t *cfunc; +{ + if (rl_last_func == cfunc && !completion_changed_buffer) + return '?'; + else if (_rl_complete_show_all) + return '!'; + else if (_rl_complete_show_unmodified) + return '@'; + else + return TAB; +} + +/************************************/ +/* */ +/* Completion utility functions */ +/* */ +/************************************/ + +/* Reset readline state on a signal or other event. */ +void +_rl_reset_completion_state () +{ + rl_completion_found_quote = 0; + rl_completion_quote_character = 0; +} + +static void +_rl_complete_sigcleanup (sig, ptr) + int sig; + void *ptr; +{ + if (sig == SIGINT) /* XXX - for now */ + _rl_free_match_list ((char **)ptr); +} + +/* Set default values for readline word completion. These are the variables + that application completion functions can change or inspect. */ +static void +set_completion_defaults (what_to_do) + int what_to_do; +{ + /* Only the completion entry function can change these. */ + rl_filename_completion_desired = 0; + rl_filename_quoting_desired = 1; + rl_completion_type = what_to_do; + rl_completion_suppress_append = rl_completion_suppress_quote = 0; + rl_completion_append_character = ' '; + + /* The completion entry function may optionally change this. */ + rl_completion_mark_symlink_dirs = _rl_complete_mark_symlink_dirs; +} + +/* The user must press "y" or "n". Non-zero return means "y" pressed. */ +static int +get_y_or_n (for_pager) + int for_pager; +{ + int c; + + /* For now, disable pager in callback mode, until we later convert to state + driven functions. Have to wait until next major version to add new + state definition, since it will change value of RL_STATE_DONE. */ +#if defined (READLINE_CALLBACKS) + if (RL_ISSTATE (RL_STATE_CALLBACK)) + return 1; +#endif + + for (;;) + { + RL_SETSTATE(RL_STATE_MOREINPUT); + c = rl_read_key (); + RL_UNSETSTATE(RL_STATE_MOREINPUT); + + if (c == 'y' || c == 'Y' || c == ' ') + return (1); + if (c == 'n' || c == 'N' || c == RUBOUT) + return (0); + if (c == ABORT_CHAR || c < 0) + _rl_abort_internal (); + if (for_pager && (c == NEWLINE || c == RETURN)) + return (2); + if (for_pager && (c == 'q' || c == 'Q')) + return (0); + rl_ding (); + } +} + +static int +_rl_internal_pager (lines) + int lines; +{ + int i; + + fprintf (rl_outstream, "--More--"); + fflush (rl_outstream); + i = get_y_or_n (1); + _rl_erase_entire_line (); + if (i == 0) + return -1; + else if (i == 2) + return (lines - 1); + else + return 0; +} + +static int +path_isdir (filename) + const char *filename; +{ + struct stat finfo; + + return (stat (filename, &finfo) == 0 && S_ISDIR (finfo.st_mode)); +} + +#if defined (VISIBLE_STATS) +/* Return the character which best describes FILENAME. + `@' for symbolic links + `/' for directories + `*' for executables + `=' for sockets + `|' for FIFOs + `%' for character special devices + `#' for block special devices */ +static int +stat_char (filename) + char *filename; +{ + struct stat finfo; + int character, r; + char *f; + const char *fn; + + /* Short-circuit a //server on cygwin, since that will always behave as + a directory. */ +#if __CYGWIN__ + if (filename[0] == '/' && filename[1] == '/' && strchr (filename+2, '/') == 0) + return '/'; +#endif + + f = 0; + if (rl_filename_stat_hook) + { + f = savestring (filename); + (*rl_filename_stat_hook) (&f); + fn = f; + } + else + fn = filename; + +#if defined (HAVE_LSTAT) && defined (S_ISLNK) + r = lstat (fn, &finfo); +#else + r = stat (fn, &finfo); +#endif + + if (r == -1) + return (0); + + character = 0; + if (S_ISDIR (finfo.st_mode)) + character = '/'; +#if defined (S_ISCHR) + else if (S_ISCHR (finfo.st_mode)) + character = '%'; +#endif /* S_ISCHR */ +#if defined (S_ISBLK) + else if (S_ISBLK (finfo.st_mode)) + character = '#'; +#endif /* S_ISBLK */ +#if defined (S_ISLNK) + else if (S_ISLNK (finfo.st_mode)) + character = '@'; +#endif /* S_ISLNK */ +#if defined (S_ISSOCK) + else if (S_ISSOCK (finfo.st_mode)) + character = '='; +#endif /* S_ISSOCK */ +#if defined (S_ISFIFO) + else if (S_ISFIFO (finfo.st_mode)) + character = '|'; +#endif + else if (S_ISREG (finfo.st_mode)) + { + if (access (filename, X_OK) == 0) + character = '*'; + } + + xfree (f); + return (character); +} +#endif /* VISIBLE_STATS */ + +#if defined (COLOR_SUPPORT) +static int +colored_stat_start (filename) + char *filename; +{ + _rl_set_normal_color (); + return (_rl_print_color_indicator (filename)); +} + +static void +colored_stat_end () +{ + _rl_prep_non_filename_text (); + _rl_put_indicator (&_rl_color_indicator[C_CLR_TO_EOL]); +} +#endif + +/* Return the portion of PATHNAME that should be output when listing + possible completions. If we are hacking filename completion, we + are only interested in the basename, the portion following the + final slash. Otherwise, we return what we were passed. Since + printing empty strings is not very informative, if we're doing + filename completion, and the basename is the empty string, we look + for the previous slash and return the portion following that. If + there's no previous slash, we just return what we were passed. */ +static char * +printable_part (pathname) + char *pathname; +{ + char *temp, *x; + + if (rl_filename_completion_desired == 0) /* don't need to do anything */ + return (pathname); + + temp = strrchr (pathname, '/'); +#if defined (__MSDOS__) + if (temp == 0 && ISALPHA ((unsigned char)pathname[0]) && pathname[1] == ':') + temp = pathname + 1; +#endif + + if (temp == 0 || *temp == '\0') + return (pathname); + /* If the basename is NULL, we might have a pathname like '/usr/src/'. + Look for a previous slash and, if one is found, return the portion + following that slash. If there's no previous slash, just return the + pathname we were passed. */ + else if (temp[1] == '\0') + { + for (x = temp - 1; x > pathname; x--) + if (*x == '/') + break; + return ((*x == '/') ? x + 1 : pathname); + } + else + return ++temp; +} + +/* Compute width of STRING when displayed on screen by print_filename */ +static int +fnwidth (string) + const char *string; +{ + int width, pos; +#if defined (HANDLE_MULTIBYTE) + mbstate_t ps; + int left, w; + size_t clen; + wchar_t wc; + + left = strlen (string) + 1; + memset (&ps, 0, sizeof (mbstate_t)); +#endif + + width = pos = 0; + while (string[pos]) + { + if (CTRL_CHAR (string[pos]) || string[pos] == RUBOUT) + { + width += 2; + pos++; + } + else + { +#if defined (HANDLE_MULTIBYTE) + clen = mbrtowc (&wc, string + pos, left - pos, &ps); + if (MB_INVALIDCH (clen)) + { + width++; + pos++; + memset (&ps, 0, sizeof (mbstate_t)); + } + else if (MB_NULLWCH (clen)) + break; + else + { + pos += clen; + w = WCWIDTH (wc); + width += (w >= 0) ? w : 1; + } +#else + width++; + pos++; +#endif + } + } + + return width; +} + +#define ELLIPSIS_LEN 3 + +static int +fnprint (to_print, prefix_bytes) + const char *to_print; + int prefix_bytes; +{ + int printed_len, w; + const char *s; +#if defined (HANDLE_MULTIBYTE) + mbstate_t ps; + const char *end; + size_t tlen; + int width; + wchar_t wc; + + end = to_print + strlen (to_print) + 1; + memset (&ps, 0, sizeof (mbstate_t)); +#endif + + printed_len = 0; + + /* Don't print only the ellipsis if the common prefix is one of the + possible completions */ + if (to_print[prefix_bytes] == '\0') + prefix_bytes = 0; + + if (prefix_bytes) + { + char ellipsis; + + ellipsis = (to_print[prefix_bytes] == '.') ? '_' : '.'; + for (w = 0; w < ELLIPSIS_LEN; w++) + putc (ellipsis, rl_outstream); + printed_len = ELLIPSIS_LEN; + } + + s = to_print + prefix_bytes; + while (*s) + { + if (CTRL_CHAR (*s)) + { + putc ('^', rl_outstream); + putc (UNCTRL (*s), rl_outstream); + printed_len += 2; + s++; +#if defined (HANDLE_MULTIBYTE) + memset (&ps, 0, sizeof (mbstate_t)); +#endif + } + else if (*s == RUBOUT) + { + putc ('^', rl_outstream); + putc ('?', rl_outstream); + printed_len += 2; + s++; +#if defined (HANDLE_MULTIBYTE) + memset (&ps, 0, sizeof (mbstate_t)); +#endif + } + else + { +#if defined (HANDLE_MULTIBYTE) + tlen = mbrtowc (&wc, s, end - s, &ps); + if (MB_INVALIDCH (tlen)) + { + tlen = 1; + width = 1; + memset (&ps, 0, sizeof (mbstate_t)); + } + else if (MB_NULLWCH (tlen)) + break; + else + { + w = WCWIDTH (wc); + width = (w >= 0) ? w : 1; + } + fwrite (s, 1, tlen, rl_outstream); + s += tlen; + printed_len += width; +#else + putc (*s, rl_outstream); + s++; + printed_len++; +#endif + } + } + + return printed_len; +} + +/* Output TO_PRINT to rl_outstream. If VISIBLE_STATS is defined and we + are using it, check for and output a single character for `special' + filenames. Return the number of characters we output. */ + +static int +print_filename (to_print, full_pathname, prefix_bytes) + char *to_print, *full_pathname; + int prefix_bytes; +{ + int printed_len, extension_char, slen, tlen; + char *s, c, *new_full_pathname, *dn; + + extension_char = 0; +#if defined (COLOR_SUPPORT) + /* Defer printing if we want to prefix with a color indicator */ + if (_rl_colored_stats == 0 || rl_filename_completion_desired == 0) +#endif + printed_len = fnprint (to_print, prefix_bytes); + + if (rl_filename_completion_desired && ( +#if defined (VISIBLE_STATS) + rl_visible_stats || +#endif +#if defined (COLOR_SUPPORT) + _rl_colored_stats || +#endif + _rl_complete_mark_directories)) + { + /* If to_print != full_pathname, to_print is the basename of the + path passed. In this case, we try to expand the directory + name before checking for the stat character. */ + if (to_print != full_pathname) + { + /* Terminate the directory name. */ + c = to_print[-1]; + to_print[-1] = '\0'; + + /* If setting the last slash in full_pathname to a NUL results in + full_pathname being the empty string, we are trying to complete + files in the root directory. If we pass a null string to the + bash directory completion hook, for example, it will expand it + to the current directory. We just want the `/'. */ + if (full_pathname == 0 || *full_pathname == 0) + dn = "/"; + else if (full_pathname[0] != '/') + dn = full_pathname; + else if (full_pathname[1] == 0) + dn = "//"; /* restore trailing slash to `//' */ + else if (full_pathname[1] == '/' && full_pathname[2] == 0) + dn = "/"; /* don't turn /// into // */ + else + dn = full_pathname; + s = tilde_expand (dn); + if (rl_directory_completion_hook) + (*rl_directory_completion_hook) (&s); + + slen = strlen (s); + tlen = strlen (to_print); + new_full_pathname = (char *)xmalloc (slen + tlen + 2); + strcpy (new_full_pathname, s); + if (s[slen - 1] == '/') + slen--; + else + new_full_pathname[slen] = '/'; + new_full_pathname[slen] = '/'; + strcpy (new_full_pathname + slen + 1, to_print); + +#if defined (VISIBLE_STATS) + if (rl_visible_stats) + extension_char = stat_char (new_full_pathname); + else +#endif + if (_rl_complete_mark_directories) + { + dn = 0; + if (rl_directory_completion_hook == 0 && rl_filename_stat_hook) + { + dn = savestring (new_full_pathname); + (*rl_filename_stat_hook) (&dn); + xfree (new_full_pathname); + new_full_pathname = dn; + } + if (path_isdir (new_full_pathname)) + extension_char = '/'; + } + +#if defined (COLOR_SUPPORT) + if (_rl_colored_stats) + { + colored_stat_start (new_full_pathname); + printed_len = fnprint (to_print, prefix_bytes); + colored_stat_end (); + } +#endif + + xfree (new_full_pathname); + to_print[-1] = c; + } + else + { + s = tilde_expand (full_pathname); +#if defined (VISIBLE_STATS) + if (rl_visible_stats) + extension_char = stat_char (s); + else +#endif + if (_rl_complete_mark_directories && path_isdir (s)) + extension_char = '/'; + +#if defined (COLOR_SUPPORT) + if (_rl_colored_stats) + { + colored_stat_start (s); + printed_len = fnprint (to_print, prefix_bytes); + colored_stat_end (); + } +#endif + + } + + xfree (s); + if (extension_char) + { + putc (extension_char, rl_outstream); + printed_len++; + } + } + + return printed_len; +} + +static char * +rl_quote_filename (s, rtype, qcp) + char *s; + int rtype; + char *qcp; +{ + char *r; + + r = (char *)xmalloc (strlen (s) + 2); + *r = *rl_completer_quote_characters; + strcpy (r + 1, s); + if (qcp) + *qcp = *rl_completer_quote_characters; + return r; +} + +/* Find the bounds of the current word for completion purposes, and leave + rl_point set to the end of the word. This function skips quoted + substrings (characters between matched pairs of characters in + rl_completer_quote_characters). First we try to find an unclosed + quoted substring on which to do matching. If one is not found, we use + the word break characters to find the boundaries of the current word. + We call an application-specific function to decide whether or not a + particular word break character is quoted; if that function returns a + non-zero result, the character does not break a word. This function + returns the opening quote character if we found an unclosed quoted + substring, '\0' otherwise. FP, if non-null, is set to a value saying + which (shell-like) quote characters we found (single quote, double + quote, or backslash) anywhere in the string. DP, if non-null, is set to + the value of the delimiter character that caused a word break. */ + +char +_rl_find_completion_word (fp, dp) + int *fp, *dp; +{ + int scan, end, found_quote, delimiter, pass_next, isbrk; + char quote_char, *brkchars; + + end = rl_point; + found_quote = delimiter = 0; + quote_char = '\0'; + + brkchars = 0; + if (rl_completion_word_break_hook) + brkchars = (*rl_completion_word_break_hook) (); + if (brkchars == 0) + brkchars = rl_completer_word_break_characters; + + if (rl_completer_quote_characters) + { + /* We have a list of characters which can be used in pairs to + quote substrings for the completer. Try to find the start + of an unclosed quoted substring. */ + /* FOUND_QUOTE is set so we know what kind of quotes we found. */ + for (scan = pass_next = 0; scan < end; scan = MB_NEXTCHAR (rl_line_buffer, scan, 1, MB_FIND_ANY)) + { + if (pass_next) + { + pass_next = 0; + continue; + } + + /* Shell-like semantics for single quotes -- don't allow backslash + to quote anything in single quotes, especially not the closing + quote. If you don't like this, take out the check on the value + of quote_char. */ + if (quote_char != '\'' && rl_line_buffer[scan] == '\\') + { + pass_next = 1; + found_quote |= RL_QF_BACKSLASH; + continue; + } + + if (quote_char != '\0') + { + /* Ignore everything until the matching close quote char. */ + if (rl_line_buffer[scan] == quote_char) + { + /* Found matching close. Abandon this substring. */ + quote_char = '\0'; + rl_point = end; + } + } + else if (strchr (rl_completer_quote_characters, rl_line_buffer[scan])) + { + /* Found start of a quoted substring. */ + quote_char = rl_line_buffer[scan]; + rl_point = scan + 1; + /* Shell-like quoting conventions. */ + if (quote_char == '\'') + found_quote |= RL_QF_SINGLE_QUOTE; + else if (quote_char == '"') + found_quote |= RL_QF_DOUBLE_QUOTE; + else + found_quote |= RL_QF_OTHER_QUOTE; + } + } + } + + if (rl_point == end && quote_char == '\0') + { + /* We didn't find an unclosed quoted substring upon which to do + completion, so use the word break characters to find the + substring on which to complete. */ + while (rl_point = MB_PREVCHAR (rl_line_buffer, rl_point, MB_FIND_ANY)) + { + scan = rl_line_buffer[rl_point]; + + if (strchr (brkchars, scan) == 0) + continue; + + /* Call the application-specific function to tell us whether + this word break character is quoted and should be skipped. */ + if (rl_char_is_quoted_p && found_quote && + (*rl_char_is_quoted_p) (rl_line_buffer, rl_point)) + continue; + + /* Convoluted code, but it avoids an n^2 algorithm with calls + to char_is_quoted. */ + break; + } + } + + /* If we are at an unquoted word break, then advance past it. */ + scan = rl_line_buffer[rl_point]; + + /* If there is an application-specific function to say whether or not + a character is quoted and we found a quote character, let that + function decide whether or not a character is a word break, even + if it is found in rl_completer_word_break_characters. Don't bother + if we're at the end of the line, though. */ + if (scan) + { + if (rl_char_is_quoted_p) + isbrk = (found_quote == 0 || + (*rl_char_is_quoted_p) (rl_line_buffer, rl_point) == 0) && + strchr (brkchars, scan) != 0; + else + isbrk = strchr (brkchars, scan) != 0; + + if (isbrk) + { + /* If the character that caused the word break was a quoting + character, then remember it as the delimiter. */ + if (rl_basic_quote_characters && + strchr (rl_basic_quote_characters, scan) && + (end - rl_point) > 1) + delimiter = scan; + + /* If the character isn't needed to determine something special + about what kind of completion to perform, then advance past it. */ + if (rl_special_prefixes == 0 || strchr (rl_special_prefixes, scan) == 0) + rl_point++; + } + } + + if (fp) + *fp = found_quote; + if (dp) + *dp = delimiter; + + return (quote_char); +} + +static char ** +gen_completion_matches (text, start, end, our_func, found_quote, quote_char) + char *text; + int start, end; + rl_compentry_func_t *our_func; + int found_quote, quote_char; +{ + char **matches; + + rl_completion_found_quote = found_quote; + rl_completion_quote_character = quote_char; + + /* If the user wants to TRY to complete, but then wants to give + up and use the default completion function, they set the + variable rl_attempted_completion_function. */ + if (rl_attempted_completion_function) + { + matches = (*rl_attempted_completion_function) (text, start, end); + if (RL_SIG_RECEIVED()) + { + _rl_free_match_list (matches); + matches = 0; + RL_CHECK_SIGNALS (); + } + + if (matches || rl_attempted_completion_over) + { + rl_attempted_completion_over = 0; + return (matches); + } + } + + /* XXX -- filename dequoting moved into rl_filename_completion_function */ + + /* rl_completion_matches will check for signals as well to avoid a long + delay while reading a directory. */ + matches = rl_completion_matches (text, our_func); + if (RL_SIG_RECEIVED()) + { + _rl_free_match_list (matches); + matches = 0; + RL_CHECK_SIGNALS (); + } + return matches; +} + +/* Filter out duplicates in MATCHES. This frees up the strings in + MATCHES. */ +static char ** +remove_duplicate_matches (matches) + char **matches; +{ + char *lowest_common; + int i, j, newlen; + char dead_slot; + char **temp_array; + + /* Sort the items. */ + for (i = 0; matches[i]; i++) + ; + + /* Sort the array without matches[0], since we need it to + stay in place no matter what. */ + if (i && rl_sort_completion_matches) + qsort (matches+1, i-1, sizeof (char *), (QSFUNC *)_rl_qsort_string_compare); + + /* Remember the lowest common denominator for it may be unique. */ + lowest_common = savestring (matches[0]); + + for (i = newlen = 0; matches[i + 1]; i++) + { + if (strcmp (matches[i], matches[i + 1]) == 0) + { + xfree (matches[i]); + matches[i] = (char *)&dead_slot; + } + else + newlen++; + } + + /* We have marked all the dead slots with (char *)&dead_slot. + Copy all the non-dead entries into a new array. */ + temp_array = (char **)xmalloc ((3 + newlen) * sizeof (char *)); + for (i = j = 1; matches[i]; i++) + { + if (matches[i] != (char *)&dead_slot) + temp_array[j++] = matches[i]; + } + temp_array[j] = (char *)NULL; + + if (matches[0] != (char *)&dead_slot) + xfree (matches[0]); + + /* Place the lowest common denominator back in [0]. */ + temp_array[0] = lowest_common; + + /* If there is one string left, and it is identical to the + lowest common denominator, then the LCD is the string to + insert. */ + if (j == 2 && strcmp (temp_array[0], temp_array[1]) == 0) + { + xfree (temp_array[1]); + temp_array[1] = (char *)NULL; + } + return (temp_array); +} + +/* Find the common prefix of the list of matches, and put it into + matches[0]. */ +static int +compute_lcd_of_matches (match_list, matches, text) + char **match_list; + int matches; + const char *text; +{ + register int i, c1, c2, si; + int low; /* Count of max-matched characters. */ + int lx; + char *dtext; /* dequoted TEXT, if needed */ +#if defined (HANDLE_MULTIBYTE) + int v; + size_t v1, v2; + mbstate_t ps1, ps2; + wchar_t wc1, wc2; +#endif + + /* If only one match, just use that. Otherwise, compare each + member of the list with the next, finding out where they + stop matching. */ + if (matches == 1) + { + match_list[0] = match_list[1]; + match_list[1] = (char *)NULL; + return 1; + } + + for (i = 1, low = 100000; i < matches; i++) + { +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + { + memset (&ps1, 0, sizeof (mbstate_t)); + memset (&ps2, 0, sizeof (mbstate_t)); + } +#endif + if (_rl_completion_case_fold) + { + for (si = 0; + (c1 = _rl_to_lower(match_list[i][si])) && + (c2 = _rl_to_lower(match_list[i + 1][si])); + si++) +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + { + v1 = mbrtowc(&wc1, match_list[i]+si, strlen (match_list[i]+si), &ps1); + v2 = mbrtowc (&wc2, match_list[i+1]+si, strlen (match_list[i+1]+si), &ps2); + if (MB_INVALIDCH (v1) || MB_INVALIDCH (v2)) + { + if (c1 != c2) /* do byte comparison */ + break; + continue; + } + wc1 = towlower (wc1); + wc2 = towlower (wc2); + if (wc1 != wc2) + break; + else if (v1 > 1) + si += v1 - 1; + } + else +#endif + if (c1 != c2) + break; + } + else + { + for (si = 0; + (c1 = match_list[i][si]) && + (c2 = match_list[i + 1][si]); + si++) +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + { + mbstate_t ps_back; + ps_back = ps1; + if (!_rl_compare_chars (match_list[i], si, &ps1, match_list[i+1], si, &ps2)) + break; + else if ((v = _rl_get_char_len (&match_list[i][si], &ps_back)) > 1) + si += v - 1; + } + else +#endif + if (c1 != c2) + break; + } + + if (low > si) + low = si; + } + + /* If there were multiple matches, but none matched up to even the + first character, and the user typed something, use that as the + value of matches[0]. */ + if (low == 0 && text && *text) + { + match_list[0] = (char *)xmalloc (strlen (text) + 1); + strcpy (match_list[0], text); + } + else + { + match_list[0] = (char *)xmalloc (low + 1); + + /* XXX - this might need changes in the presence of multibyte chars */ + + /* If we are ignoring case, try to preserve the case of the string + the user typed in the face of multiple matches differing in case. */ + if (_rl_completion_case_fold) + { + /* We're making an assumption here: + IF we're completing filenames AND + the application has defined a filename dequoting function AND + we found a quote character AND + the application has requested filename quoting + THEN + we assume that TEXT was dequoted before checking against + the file system and needs to be dequoted here before we + check against the list of matches + FI */ + dtext = (char *)NULL; + if (rl_filename_completion_desired && + rl_filename_dequoting_function && + rl_completion_found_quote && + rl_filename_quoting_desired) + { + dtext = (*rl_filename_dequoting_function) ((char *)text, rl_completion_quote_character); + text = dtext; + } + + /* sort the list to get consistent answers. */ + qsort (match_list+1, matches, sizeof(char *), (QSFUNC *)_rl_qsort_string_compare); + + si = strlen (text); + lx = (si <= low) ? si : low; /* check shorter of text and matches */ + /* Try to preserve the case of what the user typed in the presence of + multiple matches: check each match for something that matches + what the user typed taking case into account; use it up to common + length of matches if one is found. If not, just use first match. */ + for (i = 1; i <= matches; i++) + if (strncmp (match_list[i], text, lx) == 0) + { + strncpy (match_list[0], match_list[i], low); + break; + } + /* no casematch, use first entry */ + if (i > matches) + strncpy (match_list[0], match_list[1], low); + + FREE (dtext); + } + else + strncpy (match_list[0], match_list[1], low); + + match_list[0][low] = '\0'; + } + + return matches; +} + +static int +postprocess_matches (matchesp, matching_filenames) + char ***matchesp; + int matching_filenames; +{ + char *t, **matches, **temp_matches; + int nmatch, i; + + matches = *matchesp; + + if (matches == 0) + return 0; + + /* It seems to me that in all the cases we handle we would like + to ignore duplicate possibilities. Scan for the text to + insert being identical to the other completions. */ + if (rl_ignore_completion_duplicates) + { + temp_matches = remove_duplicate_matches (matches); + xfree (matches); + matches = temp_matches; + } + + /* If we are matching filenames, then here is our chance to + do clever processing by re-examining the list. Call the + ignore function with the array as a parameter. It can + munge the array, deleting matches as it desires. */ + if (rl_ignore_some_completions_function && matching_filenames) + { + for (nmatch = 1; matches[nmatch]; nmatch++) + ; + (void)(*rl_ignore_some_completions_function) (matches); + if (matches == 0 || matches[0] == 0) + { + FREE (matches); + *matchesp = (char **)0; + return 0; + } + else + { + /* If we removed some matches, recompute the common prefix. */ + for (i = 1; matches[i]; i++) + ; + if (i > 1 && i < nmatch) + { + t = matches[0]; + compute_lcd_of_matches (matches, i - 1, t); + FREE (t); + } + } + } + + *matchesp = matches; + return (1); +} + +static int +complete_get_screenwidth () +{ + int cols; + char *envcols; + + cols = _rl_completion_columns; + if (cols >= 0 && cols <= _rl_screenwidth) + return cols; + envcols = getenv ("COLUMNS"); + if (envcols && *envcols) + cols = atoi (envcols); + if (cols >= 0 && cols <= _rl_screenwidth) + return cols; + return _rl_screenwidth; +} + +/* A convenience function for displaying a list of strings in + columnar format on readline's output stream. MATCHES is the list + of strings, in argv format, LEN is the number of strings in MATCHES, + and MAX is the length of the longest string in MATCHES. */ +void +rl_display_match_list (matches, len, max) + char **matches; + int len, max; +{ + int count, limit, printed_len, lines, cols; + int i, j, k, l, common_length, sind; + char *temp, *t; + + /* Find the length of the prefix common to all items: length as displayed + characters (common_length) and as a byte index into the matches (sind) */ + common_length = sind = 0; + if (_rl_completion_prefix_display_length > 0) + { + t = printable_part (matches[0]); + temp = strrchr (t, '/'); + common_length = temp ? fnwidth (temp) : fnwidth (t); + sind = temp ? strlen (temp) : strlen (t); + + if (common_length > _rl_completion_prefix_display_length && common_length > ELLIPSIS_LEN) + max -= common_length - ELLIPSIS_LEN; + else + common_length = sind = 0; + } + + /* How many items of MAX length can we fit in the screen window? */ + cols = complete_get_screenwidth (); + max += 2; + limit = cols / max; + if (limit != 1 && (limit * max == cols)) + limit--; + + /* If cols == 0, limit will end up -1 */ + if (cols < _rl_screenwidth && limit < 0) + limit = 1; + + /* Avoid a possible floating exception. If max > cols, + limit will be 0 and a divide-by-zero fault will result. */ + if (limit == 0) + limit = 1; + + /* How many iterations of the printing loop? */ + count = (len + (limit - 1)) / limit; + + /* Watch out for special case. If LEN is less than LIMIT, then + just do the inner printing loop. + 0 < len <= limit implies count = 1. */ + + /* Sort the items if they are not already sorted. */ + if (rl_ignore_completion_duplicates == 0 && rl_sort_completion_matches) + qsort (matches + 1, len, sizeof (char *), (QSFUNC *)_rl_qsort_string_compare); + + rl_crlf (); + + lines = 0; + if (_rl_print_completions_horizontally == 0) + { + /* Print the sorted items, up-and-down alphabetically, like ls. */ + for (i = 1; i <= count; i++) + { + for (j = 0, l = i; j < limit; j++) + { + if (l > len || matches[l] == 0) + break; + else + { + temp = printable_part (matches[l]); + printed_len = print_filename (temp, matches[l], sind); + + if (j + 1 < limit) + for (k = 0; k < max - printed_len; k++) + putc (' ', rl_outstream); + } + l += count; + } + rl_crlf (); + lines++; + if (_rl_page_completions && lines >= (_rl_screenheight - 1) && i < count) + { + lines = _rl_internal_pager (lines); + if (lines < 0) + return; + } + } + } + else + { + /* Print the sorted items, across alphabetically, like ls -x. */ + for (i = 1; matches[i]; i++) + { + temp = printable_part (matches[i]); + printed_len = print_filename (temp, matches[i], sind); + /* Have we reached the end of this line? */ + if (matches[i+1]) + { + if (limit == 1 || (i && (limit > 1) && (i % limit) == 0)) + { + rl_crlf (); + lines++; + if (_rl_page_completions && lines >= _rl_screenheight - 1) + { + lines = _rl_internal_pager (lines); + if (lines < 0) + return; + } + } + else + for (k = 0; k < max - printed_len; k++) + putc (' ', rl_outstream); + } + } + rl_crlf (); + } +} + +/* Display MATCHES, a list of matching filenames in argv format. This + handles the simple case -- a single match -- first. If there is more + than one match, we compute the number of strings in the list and the + length of the longest string, which will be needed by the display + function. If the application wants to handle displaying the list of + matches itself, it sets RL_COMPLETION_DISPLAY_MATCHES_HOOK to the + address of a function, and we just call it. If we're handling the + display ourselves, we just call rl_display_match_list. We also check + that the list of matches doesn't exceed the user-settable threshold, + and ask the user if he wants to see the list if there are more matches + than RL_COMPLETION_QUERY_ITEMS. */ +static void +display_matches (matches) + char **matches; +{ + int len, max, i; + char *temp; + + /* Move to the last visible line of a possibly-multiple-line command. */ + _rl_move_vert (_rl_vis_botlin); + + /* Handle simple case first. What if there is only one answer? */ + if (matches[1] == 0) + { + temp = printable_part (matches[0]); + rl_crlf (); + print_filename (temp, matches[0], 0); + rl_crlf (); + + rl_forced_update_display (); + rl_display_fixed = 1; + + return; + } + + /* There is more than one answer. Find out how many there are, + and find the maximum printed length of a single entry. */ + for (max = 0, i = 1; matches[i]; i++) + { + temp = printable_part (matches[i]); + len = fnwidth (temp); + + if (len > max) + max = len; + } + + len = i - 1; + + /* If the caller has defined a display hook, then call that now. */ + if (rl_completion_display_matches_hook) + { + (*rl_completion_display_matches_hook) (matches, len, max); + return; + } + + /* If there are many items, then ask the user if she really wants to + see them all. */ + if (rl_completion_query_items > 0 && len >= rl_completion_query_items) + { + rl_crlf (); + fprintf (rl_outstream, "Display all %d possibilities? (y or n)", len); + fflush (rl_outstream); + if ((completion_y_or_n = get_y_or_n (0)) == 0) + { + rl_crlf (); + + rl_forced_update_display (); + rl_display_fixed = 1; + + return; + } + } + + rl_display_match_list (matches, len, max); + + rl_forced_update_display (); + rl_display_fixed = 1; +} + +static char * +make_quoted_replacement (match, mtype, qc) + char *match; + int mtype; + char *qc; /* Pointer to quoting character, if any */ +{ + int should_quote, do_replace; + char *replacement; + + /* If we are doing completion on quoted substrings, and any matches + contain any of the completer_word_break_characters, then auto- + matically prepend the substring with a quote character (just pick + the first one from the list of such) if it does not already begin + with a quote string. FIXME: Need to remove any such automatically + inserted quote character when it no longer is necessary, such as + if we change the string we are completing on and the new set of + matches don't require a quoted substring. */ + replacement = match; + + should_quote = match && rl_completer_quote_characters && + rl_filename_completion_desired && + rl_filename_quoting_desired; + + if (should_quote) + should_quote = should_quote && (!qc || !*qc || + (rl_completer_quote_characters && strchr (rl_completer_quote_characters, *qc))); + + if (should_quote) + { + /* If there is a single match, see if we need to quote it. + This also checks whether the common prefix of several + matches needs to be quoted. */ + should_quote = rl_filename_quote_characters + ? (_rl_strpbrk (match, rl_filename_quote_characters) != 0) + : 0; + + do_replace = should_quote ? mtype : NO_MATCH; + /* Quote the replacement, since we found an embedded + word break character in a potential match. */ + if (do_replace != NO_MATCH && rl_filename_quoting_function) + replacement = (*rl_filename_quoting_function) (match, do_replace, qc); + } + return (replacement); +} + +static void +insert_match (match, start, mtype, qc) + char *match; + int start, mtype; + char *qc; +{ + char *replacement, *r; + char oqc; + int end, rlen; + + oqc = qc ? *qc : '\0'; + replacement = make_quoted_replacement (match, mtype, qc); + + /* Now insert the match. */ + if (replacement) + { + rlen = strlen (replacement); + /* Don't double an opening quote character. */ + if (qc && *qc && start && rl_line_buffer[start - 1] == *qc && + replacement[0] == *qc) + start--; + /* If make_quoted_replacement changed the quoting character, remove + the opening quote and insert the (fully-quoted) replacement. */ + else if (qc && (*qc != oqc) && start && rl_line_buffer[start - 1] == oqc && + replacement[0] != oqc) + start--; + end = rl_point - 1; + /* Don't double a closing quote character */ + if (qc && *qc && end && rl_line_buffer[rl_point] == *qc && replacement[rlen - 1] == *qc) + end++; + if (_rl_skip_completed_text) + { + r = replacement; + while (start < rl_end && *r && rl_line_buffer[start] == *r) + { + start++; + r++; + } + if (start <= end || *r) + _rl_replace_text (r, start, end); + rl_point = start + strlen (r); + } + else + _rl_replace_text (replacement, start, end); + if (replacement != match) + xfree (replacement); + } +} + +/* Append any necessary closing quote and a separator character to the + just-inserted match. If the user has specified that directories + should be marked by a trailing `/', append one of those instead. The + default trailing character is a space. Returns the number of characters + appended. If NONTRIVIAL_MATCH is set, we test for a symlink (if the OS + has them) and don't add a suffix for a symlink to a directory. A + nontrivial match is one that actually adds to the word being completed. + The variable rl_completion_mark_symlink_dirs controls this behavior + (it's initially set to the what the user has chosen, indicated by the + value of _rl_complete_mark_symlink_dirs, but may be modified by an + application's completion function). */ +static int +append_to_match (text, delimiter, quote_char, nontrivial_match) + char *text; + int delimiter, quote_char, nontrivial_match; +{ + char temp_string[4], *filename, *fn; + int temp_string_index, s; + struct stat finfo; + + temp_string_index = 0; + if (quote_char && rl_point && rl_completion_suppress_quote == 0 && + rl_line_buffer[rl_point - 1] != quote_char) + temp_string[temp_string_index++] = quote_char; + + if (delimiter) + temp_string[temp_string_index++] = delimiter; + else if (rl_completion_suppress_append == 0 && rl_completion_append_character) + temp_string[temp_string_index++] = rl_completion_append_character; + + temp_string[temp_string_index++] = '\0'; + + if (rl_filename_completion_desired) + { + filename = tilde_expand (text); + if (rl_filename_stat_hook) + { + fn = savestring (filename); + (*rl_filename_stat_hook) (&fn); + xfree (filename); + filename = fn; + } + s = (nontrivial_match && rl_completion_mark_symlink_dirs == 0) + ? LSTAT (filename, &finfo) + : stat (filename, &finfo); + if (s == 0 && S_ISDIR (finfo.st_mode)) + { + if (_rl_complete_mark_directories /* && rl_completion_suppress_append == 0 */) + { + /* This is clumsy. Avoid putting in a double slash if point + is at the end of the line and the previous character is a + slash. */ + if (rl_point && rl_line_buffer[rl_point] == '\0' && rl_line_buffer[rl_point - 1] == '/') + ; + else if (rl_line_buffer[rl_point] != '/') + rl_insert_text ("/"); + } + } +#ifdef S_ISLNK + /* Don't add anything if the filename is a symlink and resolves to a + directory. */ + else if (s == 0 && S_ISLNK (finfo.st_mode) && path_isdir (filename)) + ; +#endif + else + { + if (rl_point == rl_end && temp_string_index) + rl_insert_text (temp_string); + } + xfree (filename); + } + else + { + if (rl_point == rl_end && temp_string_index) + rl_insert_text (temp_string); + } + + return (temp_string_index); +} + +static void +insert_all_matches (matches, point, qc) + char **matches; + int point; + char *qc; +{ + int i; + char *rp; + + rl_begin_undo_group (); + /* remove any opening quote character; make_quoted_replacement will add + it back. */ + if (qc && *qc && point && rl_line_buffer[point - 1] == *qc) + point--; + rl_delete_text (point, rl_point); + rl_point = point; + + if (matches[1]) + { + for (i = 1; matches[i]; i++) + { + rp = make_quoted_replacement (matches[i], SINGLE_MATCH, qc); + rl_insert_text (rp); + rl_insert_text (" "); + if (rp != matches[i]) + xfree (rp); + } + } + else + { + rp = make_quoted_replacement (matches[0], SINGLE_MATCH, qc); + rl_insert_text (rp); + rl_insert_text (" "); + if (rp != matches[0]) + xfree (rp); + } + rl_end_undo_group (); +} + +void +_rl_free_match_list (matches) + char **matches; +{ + register int i; + + if (matches == 0) + return; + + for (i = 0; matches[i]; i++) + xfree (matches[i]); + xfree (matches); +} + +/* Complete the word at or before point. + WHAT_TO_DO says what to do with the completion. + `?' means list the possible completions. + TAB means do standard completion. + `*' means insert all of the possible completions. + `!' means to do standard completion, and list all possible completions if + there is more than one. + `@' means to do standard completion, and list all possible completions if + there is more than one and partial completion is not possible. */ +int +rl_complete_internal (what_to_do) + int what_to_do; +{ + char **matches; + rl_compentry_func_t *our_func; + int start, end, delimiter, found_quote, i, nontrivial_lcd; + char *text, *saved_line_buffer; + char quote_char; +#if 1 + int tlen, mlen; +#endif + + RL_SETSTATE(RL_STATE_COMPLETING); + + set_completion_defaults (what_to_do); + + saved_line_buffer = rl_line_buffer ? savestring (rl_line_buffer) : (char *)NULL; + our_func = rl_completion_entry_function + ? rl_completion_entry_function + : rl_filename_completion_function; + /* We now look backwards for the start of a filename/variable word. */ + end = rl_point; + found_quote = delimiter = 0; + quote_char = '\0'; + + if (rl_point) + /* This (possibly) changes rl_point. If it returns a non-zero char, + we know we have an open quote. */ + quote_char = _rl_find_completion_word (&found_quote, &delimiter); + + start = rl_point; + rl_point = end; + + text = rl_copy_text (start, end); + matches = gen_completion_matches (text, start, end, our_func, found_quote, quote_char); + /* nontrivial_lcd is set if the common prefix adds something to the word + being completed. */ + nontrivial_lcd = matches && strcmp (text, matches[0]) != 0; + if (what_to_do == '!' || what_to_do == '@') + tlen = strlen (text); + xfree (text); + + if (matches == 0) + { + rl_ding (); + FREE (saved_line_buffer); + completion_changed_buffer = 0; + RL_UNSETSTATE(RL_STATE_COMPLETING); + _rl_reset_completion_state (); + return (0); + } + + /* If we are matching filenames, the attempted completion function will + have set rl_filename_completion_desired to a non-zero value. The basic + rl_filename_completion_function does this. */ + i = rl_filename_completion_desired; + + if (postprocess_matches (&matches, i) == 0) + { + rl_ding (); + FREE (saved_line_buffer); + completion_changed_buffer = 0; + RL_UNSETSTATE(RL_STATE_COMPLETING); + _rl_reset_completion_state (); + return (0); + } + + switch (what_to_do) + { + case TAB: + case '!': + case '@': + /* Insert the first match with proper quoting. */ + if (what_to_do == TAB) + { + if (*matches[0]) + insert_match (matches[0], start, matches[1] ? MULT_MATCH : SINGLE_MATCH, "e_char); + } + else if (*matches[0] && matches[1] == 0) + /* should we perform the check only if there are multiple matches? */ + insert_match (matches[0], start, matches[1] ? MULT_MATCH : SINGLE_MATCH, "e_char); + else if (*matches[0]) /* what_to_do != TAB && multiple matches */ + { + mlen = *matches[0] ? strlen (matches[0]) : 0; + if (mlen >= tlen) + insert_match (matches[0], start, matches[1] ? MULT_MATCH : SINGLE_MATCH, "e_char); + } + + /* If there are more matches, ring the bell to indicate. + If we are in vi mode, Posix.2 says to not ring the bell. + If the `show-all-if-ambiguous' variable is set, display + all the matches immediately. Otherwise, if this was the + only match, and we are hacking files, check the file to + see if it was a directory. If so, and the `mark-directories' + variable is set, add a '/' to the name. If not, and we + are at the end of the line, then add a space. */ + if (matches[1]) + { + if (what_to_do == '!') + { + display_matches (matches); + break; + } + else if (what_to_do == '@') + { + if (nontrivial_lcd == 0) + display_matches (matches); + break; + } + else if (rl_editing_mode != vi_mode) + rl_ding (); /* There are other matches remaining. */ + } + else + append_to_match (matches[0], delimiter, quote_char, nontrivial_lcd); + + break; + + case '*': + insert_all_matches (matches, start, "e_char); + break; + + case '?': + if (rl_completion_display_matches_hook == 0) + { + _rl_sigcleanup = _rl_complete_sigcleanup; + _rl_sigcleanarg = matches; + } + display_matches (matches); + _rl_sigcleanup = 0; + _rl_sigcleanarg = 0; + break; + + default: + _rl_ttymsg ("bad value %d for what_to_do in rl_complete", what_to_do); + rl_ding (); + FREE (saved_line_buffer); + RL_UNSETSTATE(RL_STATE_COMPLETING); + _rl_free_match_list (matches); + _rl_reset_completion_state (); + return 1; + } + + _rl_free_match_list (matches); + + /* Check to see if the line has changed through all of this manipulation. */ + if (saved_line_buffer) + { + completion_changed_buffer = strcmp (rl_line_buffer, saved_line_buffer) != 0; + xfree (saved_line_buffer); + } + + RL_UNSETSTATE(RL_STATE_COMPLETING); + _rl_reset_completion_state (); + return 0; +} + +/***************************************************************/ +/* */ +/* Application-callable completion match generator functions */ +/* */ +/***************************************************************/ + +/* Return an array of (char *) which is a list of completions for TEXT. + If there are no completions, return a NULL pointer. + The first entry in the returned array is the substitution for TEXT. + The remaining entries are the possible completions. + The array is terminated with a NULL pointer. + + ENTRY_FUNCTION is a function of two args, and returns a (char *). + The first argument is TEXT. + The second is a state argument; it should be zero on the first call, and + non-zero on subsequent calls. It returns a NULL pointer to the caller + when there are no more matches. + */ +char ** +rl_completion_matches (text, entry_function) + const char *text; + rl_compentry_func_t *entry_function; +{ + register int i; + + /* Number of slots in match_list. */ + int match_list_size; + + /* The list of matches. */ + char **match_list; + + /* Number of matches actually found. */ + int matches; + + /* Temporary string binder. */ + char *string; + + matches = 0; + match_list_size = 10; + match_list = (char **)xmalloc ((match_list_size + 1) * sizeof (char *)); + match_list[1] = (char *)NULL; + + while (string = (*entry_function) (text, matches)) + { + if (RL_SIG_RECEIVED ()) + { + /* Start at 1 because we don't set matches[0] in this function. + Only free the list members if we're building match list from + rl_filename_completion_function, since we know that doesn't + free the strings it returns. */ + if (entry_function == rl_filename_completion_function) + { + for (i = 1; match_list[i]; i++) + xfree (match_list[i]); + } + xfree (match_list); + match_list = 0; + match_list_size = 0; + matches = 0; + RL_CHECK_SIGNALS (); + } + + if (matches + 1 >= match_list_size) + match_list = (char **)xrealloc + (match_list, ((match_list_size += 10) + 1) * sizeof (char *)); + + if (match_list == 0) + return (match_list); + + match_list[++matches] = string; + match_list[matches + 1] = (char *)NULL; + } + + /* If there were any matches, then look through them finding out the + lowest common denominator. That then becomes match_list[0]. */ + if (matches) + compute_lcd_of_matches (match_list, matches, text); + else /* There were no matches. */ + { + xfree (match_list); + match_list = (char **)NULL; + } + return (match_list); +} + +/* A completion function for usernames. + TEXT contains a partial username preceded by a random + character (usually `~'). */ +char * +rl_username_completion_function (text, state) + const char *text; + int state; +{ +#if defined (__WIN32__) || defined (__OPENNT) + return (char *)NULL; +#else /* !__WIN32__ && !__OPENNT) */ + static char *username = (char *)NULL; + static struct passwd *entry; + static int namelen, first_char, first_char_loc; + char *value; + + if (state == 0) + { + FREE (username); + + first_char = *text; + first_char_loc = first_char == '~'; + + username = savestring (&text[first_char_loc]); + namelen = strlen (username); +#if defined (HAVE_GETPWENT) + setpwent (); +#endif + } + +#if defined (HAVE_GETPWENT) + while (entry = getpwent ()) + { + /* Null usernames should result in all users as possible completions. */ + if (namelen == 0 || (STREQN (username, entry->pw_name, namelen))) + break; + } +#endif + + if (entry == 0) + { +#if defined (HAVE_GETPWENT) + endpwent (); +#endif + return ((char *)NULL); + } + else + { + value = (char *)xmalloc (2 + strlen (entry->pw_name)); + + *value = *text; + + strcpy (value + first_char_loc, entry->pw_name); + + if (first_char == '~') + rl_filename_completion_desired = 1; + + return (value); + } +#endif /* !__WIN32__ && !__OPENNT */ +} + +/* Return non-zero if CONVFN matches FILENAME up to the length of FILENAME + (FILENAME_LEN). If _rl_completion_case_fold is set, compare without + regard to the alphabetic case of characters. If + _rl_completion_case_map is set, make `-' and `_' equivalent. CONVFN is + the possibly-converted directory entry; FILENAME is what the user typed. */ +static int +complete_fncmp (convfn, convlen, filename, filename_len) + const char *convfn; + int convlen; + const char *filename; + int filename_len; +{ + register char *s1, *s2; + int d, len; +#if defined (HANDLE_MULTIBYTE) + size_t v1, v2; + mbstate_t ps1, ps2; + wchar_t wc1, wc2; +#endif + +#if defined (HANDLE_MULTIBYTE) + memset (&ps1, 0, sizeof (mbstate_t)); + memset (&ps2, 0, sizeof (mbstate_t)); +#endif + + if (filename_len == 0) + return 1; + if (convlen < filename_len) + return 0; + + len = filename_len; + s1 = (char *)convfn; + s2 = (char *)filename; + + /* Otherwise, if these match up to the length of filename, then + it is a match. */ + if (_rl_completion_case_fold && _rl_completion_case_map) + { + /* Case-insensitive comparison treating _ and - as equivalent */ +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + { + do + { + v1 = mbrtowc (&wc1, s1, convlen, &ps1); + v2 = mbrtowc (&wc2, s2, filename_len, &ps2); + if (v1 == 0 && v2 == 0) + return 1; + else if (MB_INVALIDCH (v1) || MB_INVALIDCH (v2)) + { + if (*s1 != *s2) /* do byte comparison */ + return 0; + else if ((*s1 == '-' || *s1 == '_') && (*s2 == '-' || *s2 == '_')) + return 0; + s1++; s2++; len--; + continue; + } + wc1 = towlower (wc1); + wc2 = towlower (wc2); + s1 += v1; + s2 += v1; + len -= v1; + if ((wc1 == L'-' || wc1 == L'_') && (wc2 == L'-' || wc2 == L'_')) + continue; + if (wc1 != wc2) + return 0; + } + while (len != 0); + } + else +#endif + { + do + { + d = _rl_to_lower (*s1) - _rl_to_lower (*s2); + /* *s1 == [-_] && *s2 == [-_] */ + if ((*s1 == '-' || *s1 == '_') && (*s2 == '-' || *s2 == '_')) + d = 0; + if (d != 0) + return 0; + s1++; s2++; /* already checked convlen >= filename_len */ + } + while (--len != 0); + } + + return 1; + } + else if (_rl_completion_case_fold) + { +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + { + do + { + v1 = mbrtowc (&wc1, s1, convlen, &ps1); + v2 = mbrtowc (&wc2, s2, filename_len, &ps2); + if (v1 == 0 && v2 == 0) + return 1; + else if (MB_INVALIDCH (v1) || MB_INVALIDCH (v2)) + { + if (*s1 != *s2) /* do byte comparison */ + return 0; + s1++; s2++; len--; + continue; + } + wc1 = towlower (wc1); + wc2 = towlower (wc2); + if (wc1 != wc2) + return 0; + s1 += v1; + s2 += v1; + len -= v1; + } + while (len != 0); + return 1; + } + else +#endif + if ((_rl_to_lower (convfn[0]) == _rl_to_lower (filename[0])) && + (convlen >= filename_len) && + (_rl_strnicmp (filename, convfn, filename_len) == 0)) + return 1; + } + else + { + if ((convfn[0] == filename[0]) && + (convlen >= filename_len) && + (strncmp (filename, convfn, filename_len) == 0)) + return 1; + } + return 0; +} + +/* Okay, now we write the entry_function for filename completion. In the + general case. Note that completion in the shell is a little different + because of all the pathnames that must be followed when looking up the + completion for a command. */ +char * +rl_filename_completion_function (text, state) + const char *text; + int state; +{ + static DIR *directory = (DIR *)NULL; + static char *filename = (char *)NULL; + static char *dirname = (char *)NULL; + static char *users_dirname = (char *)NULL; + static int filename_len; + char *temp, *dentry, *convfn; + int dirlen, dentlen, convlen; + struct dirent *entry; + + /* If we don't have any state, then do some initialization. */ + if (state == 0) + { + /* If we were interrupted before closing the directory or reading + all of its contents, close it. */ + if (directory) + { + closedir (directory); + directory = (DIR *)NULL; + } + FREE (dirname); + FREE (filename); + FREE (users_dirname); + + filename = savestring (text); + if (*text == 0) + text = "."; + dirname = savestring (text); + + temp = strrchr (dirname, '/'); + +#if defined (__MSDOS__) + /* special hack for //X/... */ + if (dirname[0] == '/' && dirname[1] == '/' && ISALPHA ((unsigned char)dirname[2]) && dirname[3] == '/') + temp = strrchr (dirname + 3, '/'); +#endif + + if (temp) + { + strcpy (filename, ++temp); + *temp = '\0'; + } +#if defined (__MSDOS__) + /* searches from current directory on the drive */ + else if (ISALPHA ((unsigned char)dirname[0]) && dirname[1] == ':') + { + strcpy (filename, dirname + 2); + dirname[2] = '\0'; + } +#endif + else + { + dirname[0] = '.'; + dirname[1] = '\0'; + } + + /* We aren't done yet. We also support the "~user" syntax. */ + + /* Save the version of the directory that the user typed, dequoting + it if necessary. */ + if (rl_completion_found_quote && rl_filename_dequoting_function) + users_dirname = (*rl_filename_dequoting_function) (dirname, rl_completion_quote_character); + else + users_dirname = savestring (dirname); + + if (*dirname == '~') + { + temp = tilde_expand (dirname); + xfree (dirname); + dirname = temp; + } + + /* We have saved the possibly-dequoted version of the directory name + the user typed. Now transform the directory name we're going to + pass to opendir(2). The directory rewrite hook modifies only the + directory name; the directory completion hook modifies both the + directory name passed to opendir(2) and the version the user + typed. Both the directory completion and rewrite hooks should perform + any necessary dequoting. The hook functions return 1 if they modify + the directory name argument. If either hook returns 0, it should + not modify the directory name pointer passed as an argument. */ + if (rl_directory_rewrite_hook) + (*rl_directory_rewrite_hook) (&dirname); + else if (rl_directory_completion_hook && (*rl_directory_completion_hook) (&dirname)) + { + xfree (users_dirname); + users_dirname = savestring (dirname); + } + else if (rl_completion_found_quote && rl_filename_dequoting_function) + { + /* delete single and double quotes */ + xfree (dirname); + dirname = savestring (users_dirname); + } + directory = opendir (dirname); + + /* Now dequote a non-null filename. FILENAME will not be NULL, but may + be empty. */ + if (*filename && rl_completion_found_quote && rl_filename_dequoting_function) + { + /* delete single and double quotes */ + temp = (*rl_filename_dequoting_function) (filename, rl_completion_quote_character); + xfree (filename); + filename = temp; + } + filename_len = strlen (filename); + + rl_filename_completion_desired = 1; + } + + /* At this point we should entertain the possibility of hacking wildcarded + filenames, like /usr/man/man<WILD>/te<TAB>. If the directory name + contains globbing characters, then build an array of directories, and + then map over that list while completing. */ + /* *** UNIMPLEMENTED *** */ + + /* Now that we have some state, we can read the directory. */ + + entry = (struct dirent *)NULL; + while (directory && (entry = readdir (directory))) + { + convfn = dentry = entry->d_name; + convlen = dentlen = D_NAMLEN (entry); + + if (rl_filename_rewrite_hook) + { + convfn = (*rl_filename_rewrite_hook) (dentry, dentlen); + convlen = (convfn == dentry) ? dentlen : strlen (convfn); + } + + /* Special case for no filename. If the user has disabled the + `match-hidden-files' variable, skip filenames beginning with `.'. + All other entries except "." and ".." match. */ + if (filename_len == 0) + { + if (_rl_match_hidden_files == 0 && HIDDEN_FILE (convfn)) + continue; + + if (convfn[0] != '.' || + (convfn[1] && (convfn[1] != '.' || convfn[2]))) + break; + } + else + { + if (complete_fncmp (convfn, convlen, filename, filename_len)) + break; + } + } + + if (entry == 0) + { + if (directory) + { + closedir (directory); + directory = (DIR *)NULL; + } + if (dirname) + { + xfree (dirname); + dirname = (char *)NULL; + } + if (filename) + { + xfree (filename); + filename = (char *)NULL; + } + if (users_dirname) + { + xfree (users_dirname); + users_dirname = (char *)NULL; + } + + return (char *)NULL; + } + else + { + /* dirname && (strcmp (dirname, ".") != 0) */ + if (dirname && (dirname[0] != '.' || dirname[1])) + { + if (rl_complete_with_tilde_expansion && *users_dirname == '~') + { + dirlen = strlen (dirname); + temp = (char *)xmalloc (2 + dirlen + D_NAMLEN (entry)); + strcpy (temp, dirname); + /* Canonicalization cuts off any final slash present. We + may need to add it back. */ + if (dirname[dirlen - 1] != '/') + { + temp[dirlen++] = '/'; + temp[dirlen] = '\0'; + } + } + else + { + dirlen = strlen (users_dirname); + temp = (char *)xmalloc (2 + dirlen + D_NAMLEN (entry)); + strcpy (temp, users_dirname); + /* Make sure that temp has a trailing slash here. */ + if (users_dirname[dirlen - 1] != '/') + temp[dirlen++] = '/'; + } + + strcpy (temp + dirlen, convfn); + } + else + temp = savestring (convfn); + + if (convfn != dentry) + xfree (convfn); + + return (temp); + } +} + +/* An initial implementation of a menu completion function a la tcsh. The + first time (if the last readline command was not rl_old_menu_complete), we + generate the list of matches. This code is very similar to the code in + rl_complete_internal -- there should be a way to combine the two. Then, + for each item in the list of matches, we insert the match in an undoable + fashion, with the appropriate character appended (this happens on the + second and subsequent consecutive calls to rl_old_menu_complete). When we + hit the end of the match list, we restore the original unmatched text, + ring the bell, and reset the counter to zero. */ +int +rl_old_menu_complete (count, invoking_key) + int count, invoking_key; +{ + rl_compentry_func_t *our_func; + int matching_filenames, found_quote; + + static char *orig_text; + static char **matches = (char **)0; + static int match_list_index = 0; + static int match_list_size = 0; + static int orig_start, orig_end; + static char quote_char; + static int delimiter; + + /* The first time through, we generate the list of matches and set things + up to insert them. */ + if (rl_last_func != rl_old_menu_complete) + { + /* Clean up from previous call, if any. */ + FREE (orig_text); + if (matches) + _rl_free_match_list (matches); + + match_list_index = match_list_size = 0; + matches = (char **)NULL; + + rl_completion_invoking_key = invoking_key; + + RL_SETSTATE(RL_STATE_COMPLETING); + + /* Only the completion entry function can change these. */ + set_completion_defaults ('%'); + + our_func = rl_menu_completion_entry_function; + if (our_func == 0) + our_func = rl_completion_entry_function + ? rl_completion_entry_function + : rl_filename_completion_function; + + /* We now look backwards for the start of a filename/variable word. */ + orig_end = rl_point; + found_quote = delimiter = 0; + quote_char = '\0'; + + if (rl_point) + /* This (possibly) changes rl_point. If it returns a non-zero char, + we know we have an open quote. */ + quote_char = _rl_find_completion_word (&found_quote, &delimiter); + + orig_start = rl_point; + rl_point = orig_end; + + orig_text = rl_copy_text (orig_start, orig_end); + matches = gen_completion_matches (orig_text, orig_start, orig_end, + our_func, found_quote, quote_char); + + /* If we are matching filenames, the attempted completion function will + have set rl_filename_completion_desired to a non-zero value. The basic + rl_filename_completion_function does this. */ + matching_filenames = rl_filename_completion_desired; + + if (matches == 0 || postprocess_matches (&matches, matching_filenames) == 0) + { + rl_ding (); + FREE (matches); + matches = (char **)0; + FREE (orig_text); + orig_text = (char *)0; + completion_changed_buffer = 0; + RL_UNSETSTATE(RL_STATE_COMPLETING); + return (0); + } + + RL_UNSETSTATE(RL_STATE_COMPLETING); + + for (match_list_size = 0; matches[match_list_size]; match_list_size++) + ; + /* matches[0] is lcd if match_list_size > 1, but the circular buffer + code below should take care of it. */ + + if (match_list_size > 1 && _rl_complete_show_all) + display_matches (matches); + } + + /* Now we have the list of matches. Replace the text between + rl_line_buffer[orig_start] and rl_line_buffer[rl_point] with + matches[match_list_index], and add any necessary closing char. */ + + if (matches == 0 || match_list_size == 0) + { + rl_ding (); + FREE (matches); + matches = (char **)0; + completion_changed_buffer = 0; + return (0); + } + + match_list_index += count; + if (match_list_index < 0) + { + while (match_list_index < 0) + match_list_index += match_list_size; + } + else + match_list_index %= match_list_size; + + if (match_list_index == 0 && match_list_size > 1) + { + rl_ding (); + insert_match (orig_text, orig_start, MULT_MATCH, "e_char); + } + else + { + insert_match (matches[match_list_index], orig_start, SINGLE_MATCH, "e_char); + append_to_match (matches[match_list_index], delimiter, quote_char, + strcmp (orig_text, matches[match_list_index])); + } + + completion_changed_buffer = 1; + return (0); +} + +int +rl_menu_complete (count, ignore) + int count, ignore; +{ + rl_compentry_func_t *our_func; + int matching_filenames, found_quote; + + static char *orig_text; + static char **matches = (char **)0; + static int match_list_index = 0; + static int match_list_size = 0; + static int nontrivial_lcd = 0; + static int full_completion = 0; /* set to 1 if menu completion should reinitialize on next call */ + static int orig_start, orig_end; + static char quote_char; + static int delimiter, cstate; + + /* The first time through, we generate the list of matches and set things + up to insert them. */ + if ((rl_last_func != rl_menu_complete && rl_last_func != rl_backward_menu_complete) || full_completion) + { + /* Clean up from previous call, if any. */ + FREE (orig_text); + if (matches) + _rl_free_match_list (matches); + + match_list_index = match_list_size = 0; + matches = (char **)NULL; + + full_completion = 0; + + RL_SETSTATE(RL_STATE_COMPLETING); + + /* Only the completion entry function can change these. */ + set_completion_defaults ('%'); + + our_func = rl_menu_completion_entry_function; + if (our_func == 0) + our_func = rl_completion_entry_function + ? rl_completion_entry_function + : rl_filename_completion_function; + + /* We now look backwards for the start of a filename/variable word. */ + orig_end = rl_point; + found_quote = delimiter = 0; + quote_char = '\0'; + + if (rl_point) + /* This (possibly) changes rl_point. If it returns a non-zero char, + we know we have an open quote. */ + quote_char = _rl_find_completion_word (&found_quote, &delimiter); + + orig_start = rl_point; + rl_point = orig_end; + + orig_text = rl_copy_text (orig_start, orig_end); + matches = gen_completion_matches (orig_text, orig_start, orig_end, + our_func, found_quote, quote_char); + + nontrivial_lcd = matches && strcmp (orig_text, matches[0]) != 0; + + /* If we are matching filenames, the attempted completion function will + have set rl_filename_completion_desired to a non-zero value. The basic + rl_filename_completion_function does this. */ + matching_filenames = rl_filename_completion_desired; + + if (matches == 0 || postprocess_matches (&matches, matching_filenames) == 0) + { + rl_ding (); + FREE (matches); + matches = (char **)0; + FREE (orig_text); + orig_text = (char *)0; + completion_changed_buffer = 0; + RL_UNSETSTATE(RL_STATE_COMPLETING); + return (0); + } + + RL_UNSETSTATE(RL_STATE_COMPLETING); + + for (match_list_size = 0; matches[match_list_size]; match_list_size++) + ; + + if (match_list_size == 0) + { + rl_ding (); + FREE (matches); + matches = (char **)0; + match_list_index = 0; + completion_changed_buffer = 0; + return (0); + } + + /* matches[0] is lcd if match_list_size > 1, but the circular buffer + code below should take care of it. */ + if (*matches[0]) + { + insert_match (matches[0], orig_start, matches[1] ? MULT_MATCH : SINGLE_MATCH, "e_char); + orig_end = orig_start + strlen (matches[0]); + completion_changed_buffer = STREQ (orig_text, matches[0]) == 0; + } + + if (match_list_size > 1 && _rl_complete_show_all) + { + display_matches (matches); + /* If there are so many matches that the user has to be asked + whether or not he wants to see the matches, menu completion + is unwieldy. */ + if (rl_completion_query_items > 0 && match_list_size >= rl_completion_query_items) + { + rl_ding (); + FREE (matches); + matches = (char **)0; + full_completion = 1; + return (0); + } + else if (_rl_menu_complete_prefix_first) + { + rl_ding (); + return (0); + } + } + else if (match_list_size <= 1) + { + append_to_match (matches[0], delimiter, quote_char, nontrivial_lcd); + full_completion = 1; + return (0); + } + else if (_rl_menu_complete_prefix_first && match_list_size > 1) + { + rl_ding (); + return (0); + } + } + + /* Now we have the list of matches. Replace the text between + rl_line_buffer[orig_start] and rl_line_buffer[rl_point] with + matches[match_list_index], and add any necessary closing char. */ + + if (matches == 0 || match_list_size == 0) + { + rl_ding (); + FREE (matches); + matches = (char **)0; + completion_changed_buffer = 0; + return (0); + } + + match_list_index += count; + if (match_list_index < 0) + { + while (match_list_index < 0) + match_list_index += match_list_size; + } + else + match_list_index %= match_list_size; + + if (match_list_index == 0 && match_list_size > 1) + { + rl_ding (); + insert_match (matches[0], orig_start, MULT_MATCH, "e_char); + } + else + { + insert_match (matches[match_list_index], orig_start, SINGLE_MATCH, "e_char); + append_to_match (matches[match_list_index], delimiter, quote_char, + strcmp (orig_text, matches[match_list_index])); + } + + completion_changed_buffer = 1; + return (0); +} + +int +rl_backward_menu_complete (count, key) + int count, key; +{ + /* Positive arguments to backward-menu-complete translate into negative + arguments for menu-complete, and vice versa. */ + return (rl_menu_complete (-count, key)); +} diff --git a/lib/readline/display.c b/lib/readline/display.c index 6e30ef9f..020873fa 100644 --- a/lib/readline/display.c +++ b/lib/readline/display.c @@ -69,6 +69,12 @@ static void delete_chars PARAMS((int)); static void insert_some_chars PARAMS((char *, int, int)); static void open_some_spaces PARAMS((int)); static void cr PARAMS((void)); +static void redraw_prompt PARAMS((char *)); + +/* Values for FLAGS */ +#define PMT_MULTILINE 0x01 + +static char *expand_prompt PARAMS((char *, int, int *, int *, int *, int *)); /* State of visible and invisible lines. */ struct line_state @@ -158,6 +164,16 @@ int _rl_want_redisplay = 0; This is usually pointing to rl_prompt. */ char *rl_display_prompt = (char *)NULL; +/* Variables used to include the editing mode in the prompt. */ +char *_rl_emacs_mode_str; +int _rl_emacs_modestr_len; + +char *_rl_vi_ins_mode_str; +int _rl_vi_ins_modestr_len; + +char *_rl_vi_cmd_mode_str; +int _rl_vi_cmd_modestr_len; + /* Pseudo-global variables declared here. */ /* The visible cursor position. If you print some text, adjust this. */ @@ -239,16 +255,30 @@ static int saved_local_length; static int saved_invis_chars_first_line; static int saved_physical_chars; -/* Return a character indicating the editing mode, for use in the prompt. */ -static int -prompt_modechar () +/* Return a string indicating the editing mode, for use in the prompt. */ + +static char * +prompt_modestr (lenp) + int *lenp; { if (rl_editing_mode == emacs_mode) - return '@'; + { + if (lenp) + *lenp = _rl_emacs_mode_str ? _rl_emacs_modestr_len : RL_EMACS_MODESTR_DEFLEN; + return _rl_emacs_mode_str ? _rl_emacs_mode_str : RL_EMACS_MODESTR_DEFAULT; + } else if (_rl_keymap == vi_insertion_keymap) - return '+'; /* vi insert mode */ + { + if (lenp) + *lenp = _rl_vi_ins_mode_str ? _rl_vi_ins_modestr_len : RL_VI_INS_MODESTR_DEFLEN; + return _rl_vi_ins_mode_str ? _rl_vi_ins_mode_str : RL_VI_INS_MODESTR_DEFAULT; /* vi insert mode */ + } else - return ':'; /* vi command mode */ + { + if (lenp) + *lenp = _rl_vi_cmd_mode_str ? _rl_vi_cmd_modestr_len : RL_VI_CMD_MODESTR_DEFLEN; + return _rl_vi_cmd_mode_str ? _rl_vi_cmd_mode_str : RL_VI_CMD_MODESTR_DEFAULT; /* vi command mode */ + } } /* Expand the prompt string S and return the number of visible @@ -266,26 +296,37 @@ prompt_modechar () the returned string; all characters except those between \001 and \002 are assumed to be `visible'. */ +/* Possible values for FLAGS: + PMT_MULTILINE caller indicates that this is part of a multiline prompt +*/ + static char * -expand_prompt (pmt, lp, lip, niflp, vlp) +expand_prompt (pmt, flags, lp, lip, niflp, vlp) char *pmt; + int flags; int *lp, *lip, *niflp, *vlp; { - char *r, *ret, *p, *igstart; + char *r, *ret, *p, *igstart, *nprompt, *ms; int l, rl, last, ignoring, ninvis, invfl, invflset, ind, pind, physchars; + int mlen; + + /* We only expand the mode string for the last line of a multiline prompt + (a prompt with embedded newlines). */ + ms = (((pmt == rl_prompt) ^ (flags & PMT_MULTILINE)) && _rl_show_mode_in_prompt) ? prompt_modestr (&mlen) : 0; + if (ms) + { + l = strlen (pmt); + nprompt = (char *)xmalloc (l + mlen + 1); + memcpy (nprompt, ms, mlen); + strcpy (nprompt + mlen, pmt); + } + else + nprompt = pmt; /* Short-circuit if we can. */ - if ((MB_CUR_MAX <= 1 || rl_byte_oriented) && strchr (pmt, RL_PROMPT_START_IGNORE) == 0) - { - if (pmt == rl_prompt && _rl_show_mode_in_prompt) - { - l = strlen (pmt); - r = (char *)xmalloc (l + 2); - r[0] = prompt_modechar (); - strcpy (r + 1, pmt); - } - else - r = savestring (pmt); + if ((MB_CUR_MAX <= 1 || rl_byte_oriented) && strchr (nprompt, RL_PROMPT_START_IGNORE) == 0) + { + r = (nprompt == pmt) ? savestring (pmt) : nprompt; if (lp) *lp = strlen (r); @@ -298,21 +339,15 @@ expand_prompt (pmt, lp, lip, niflp, vlp) return r; } - l = strlen (pmt); - r = ret = (char *)xmalloc (l + 2); - - rl = physchars = 0; /* move up here so mode show can set them */ - if (pmt == rl_prompt && _rl_show_mode_in_prompt) - { - *r++ = prompt_modechar (); - rl = physchars = 1; - } + l = strlen (nprompt); /* XXX */ + r = ret = (char *)xmalloc (l + 1); + rl = physchars = 0; /* mode string now part of nprompt */ invfl = 0; /* invisible chars in first line of prompt */ invflset = 0; /* we only want to set invfl once */ - igstart = 0; - for (ignoring = last = ninvis = 0, p = pmt; p && *p; p++) + + for (ignoring = last = ninvis = 0, p = nprompt; p && *p; p++) { /* This code strips the invisible character string markers RL_PROMPT_START_IGNORE and RL_PROMPT_END_IGNORE */ @@ -334,8 +369,8 @@ expand_prompt (pmt, lp, lip, niflp, vlp) #if defined (HANDLE_MULTIBYTE) if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) { - pind = p - pmt; - ind = _rl_find_next_mbchar (pmt, pind, 1, MB_FIND_NONZERO); + pind = p - nprompt; + ind = _rl_find_next_mbchar (nprompt, pind, 1, MB_FIND_NONZERO); l = ind - pind; while (l--) *r++ = *p++; @@ -347,7 +382,7 @@ expand_prompt (pmt, lp, lip, niflp, vlp) not be the same as the number of physical characters on the screen in the presence of multibyte characters */ rl += ind - pind; - physchars += _rl_col_width (pmt, pind, ind, 0); + physchars += _rl_col_width (nprompt, pind, ind, 0); } else ninvis += ind - pind; @@ -386,6 +421,10 @@ expand_prompt (pmt, lp, lip, niflp, vlp) *niflp = invfl; if (vlp) *vlp = physchars; + + if (nprompt != pmt) + free (nprompt); + return ret; } @@ -397,7 +436,7 @@ _rl_strip_prompt (pmt) { char *ret; - ret = expand_prompt (pmt, (int *)NULL, (int *)NULL, (int *)NULL, (int *)NULL); + ret = expand_prompt (pmt, 0, (int *)NULL, (int *)NULL, (int *)NULL, (int *)NULL); return ret; } @@ -444,13 +483,13 @@ rl_expand_prompt (prompt) return (0); p = strrchr (prompt, '\n'); - if (!p) + if (p == 0) { /* The prompt is only one logical line, though it might wrap. */ - local_prompt = expand_prompt (prompt, &prompt_visible_length, - &prompt_last_invisible, - &prompt_invis_chars_first_line, - &prompt_physical_chars); + local_prompt = expand_prompt (prompt, 0, &prompt_visible_length, + &prompt_last_invisible, + &prompt_invis_chars_first_line, + &prompt_physical_chars); local_prompt_prefix = (char *)0; local_prompt_len = local_prompt ? strlen (local_prompt) : 0; return (prompt_visible_length); @@ -459,14 +498,16 @@ rl_expand_prompt (prompt) { /* The prompt spans multiple lines. */ t = ++p; - local_prompt = expand_prompt (p, &prompt_visible_length, + local_prompt = expand_prompt (p, PMT_MULTILINE, + &prompt_visible_length, &prompt_last_invisible, &prompt_invis_chars_first_line, &prompt_physical_chars); c = *t; *t = '\0'; /* The portion of the prompt string up to and including the final newline is now null-terminated. */ - local_prompt_prefix = expand_prompt (prompt, &prompt_prefix_length, + local_prompt_prefix = expand_prompt (prompt, PMT_MULTILINE, + &prompt_prefix_length, (int *)NULL, (int *)NULL, (int *)NULL); @@ -1972,6 +2013,18 @@ rl_forced_update_display () return 0; } +/* Redraw only the last line of a multi-line prompt. */ +void +rl_redraw_prompt_last_line () +{ + char *t; + t = strrchr (rl_display_prompt, '\n'); + if (t) + redraw_prompt (++t); + else + rl_forced_update_display (); +} + /* Move the cursor from _rl_last_c_pos to NEW, which are buffer indices. (Well, when we don't have multibyte characters, _rl_last_c_pos is a buffer index.) @@ -2279,10 +2332,10 @@ rl_message (va_alist) local_prompt = (char *)NULL; } rl_display_prompt = msg_buf; - local_prompt = expand_prompt (msg_buf, &prompt_visible_length, - &prompt_last_invisible, - &prompt_invis_chars_first_line, - &prompt_physical_chars); + local_prompt = expand_prompt (msg_buf, 0, &prompt_visible_length, + &prompt_last_invisible, + &prompt_invis_chars_first_line, + &prompt_physical_chars); local_prompt_prefix = (char *)NULL; local_prompt_len = local_prompt ? strlen (local_prompt) : 0; (*rl_redisplay_function) (); @@ -2312,10 +2365,10 @@ rl_message (format, arg1, arg2) FREE (local_prompt_prefix); local_prompt = (char *)NULL; } - local_prompt = expand_prompt (msg_buf, &prompt_visible_length, - &prompt_last_invisible, - &prompt_invis_chars_first_line, - &prompt_physical_chars); + local_prompt = expand_prompt (msg_buf, 0, &prompt_visible_length, + &prompt_last_invisible, + &prompt_invis_chars_first_line, + &prompt_physical_chars); local_prompt_prefix = (char *)NULL; local_prompt_len = local_prompt ? strlen (local_prompt) : 0; (*rl_redisplay_function) (); @@ -2618,7 +2671,8 @@ redraw_prompt (t) rl_save_prompt (); rl_display_prompt = t; - local_prompt = expand_prompt (t, &prompt_visible_length, + local_prompt = expand_prompt (t, PMT_MULTILINE, + &prompt_visible_length, &prompt_last_invisible, &prompt_invis_chars_first_line, &prompt_physical_chars); diff --git a/lib/readline/display.c~ b/lib/readline/display.c~ new file mode 100644 index 00000000..41be8678 --- /dev/null +++ b/lib/readline/display.c~ @@ -0,0 +1,2884 @@ +/* display.c -- readline redisplay facility. */ + +/* Copyright (C) 1987-2013 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Readline is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define READLINE_LIBRARY + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#include <sys/types.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif /* HAVE_UNISTD_H */ + +#include "posixstat.h" + +#if defined (HAVE_STDLIB_H) +# include <stdlib.h> +#else +# include "ansi_stdlib.h" +#endif /* HAVE_STDLIB_H */ + +#include <stdio.h> + +#ifdef __MSDOS__ +# include <pc.h> +#endif + +/* System-specific feature definitions and include files. */ +#include "rldefs.h" +#include "rlmbutil.h" + +/* Termcap library stuff. */ +#include "tcap.h" + +/* Some standard library routines. */ +#include "readline.h" +#include "history.h" + +#include "rlprivate.h" +#include "xmalloc.h" + +#if !defined (strchr) && !defined (__STDC__) +extern char *strchr (), *strrchr (); +#endif /* !strchr && !__STDC__ */ + +static void update_line PARAMS((char *, char *, int, int, int, int)); +static void space_to_eol PARAMS((int)); +static void delete_chars PARAMS((int)); +static void insert_some_chars PARAMS((char *, int, int)); +static void open_some_spaces PARAMS((int)); +static void cr PARAMS((void)); +static void redraw_prompt PARAMS((char *)); + +/* Values for FLAGS */ +#define PMT_MULTILINE 0x01 + +static char *expand_prompt PARAMS((char *, int, int *, int *, int *, int *)); + +/* State of visible and invisible lines. */ +struct line_state + { + char *line; + int *lbreaks; + int lbsize; +#if defined (HANDLE_MULTIBYTE) + int *wrapped_line; + int wbsize; +#endif + }; + +/* The line display buffers. One is the line currently displayed on + the screen. The other is the line about to be displayed. */ +static struct line_state line_state_array[2]; +static struct line_state *line_state_visible = &line_state_array[0]; +static struct line_state *line_state_invisible = &line_state_array[1]; +static int line_structures_initialized = 0; + +/* Backwards-compatible names. */ +#define inv_lbreaks (line_state_invisible->lbreaks) +#define inv_lbsize (line_state_invisible->lbsize) +#define vis_lbreaks (line_state_visible->lbreaks) +#define vis_lbsize (line_state_visible->lbsize) + +#define visible_line (line_state_visible->line) +#define invisible_line (line_state_invisible->line) + +#if defined (HANDLE_MULTIBYTE) +static int _rl_col_width PARAMS((const char *, int, int, int)); +#else +# define _rl_col_width(l, s, e, f) (((e) <= (s)) ? 0 : (e) - (s)) +#endif + +/* Heuristic used to decide whether it is faster to move from CUR to NEW + by backing up or outputting a carriage return and moving forward. CUR + and NEW are either both buffer positions or absolute screen positions. */ +#define CR_FASTER(new, cur) (((new) + 1) < ((cur) - (new))) + +/* _rl_last_c_pos is an absolute cursor position in multibyte locales and a + buffer index in others. This macro is used when deciding whether the + current cursor position is in the middle of a prompt string containing + invisible characters. XXX - might need to take `modmark' into account. */ +#define PROMPT_ENDING_INDEX \ + ((MB_CUR_MAX > 1 && rl_byte_oriented == 0) ? prompt_physical_chars : prompt_last_invisible+1) + + +/* **************************************************************** */ +/* */ +/* Display stuff */ +/* */ +/* **************************************************************** */ + +/* This is the stuff that is hard for me. I never seem to write good + display routines in C. Let's see how I do this time. */ + +/* (PWP) Well... Good for a simple line updater, but totally ignores + the problems of input lines longer than the screen width. + + update_line and the code that calls it makes a multiple line, + automatically wrapping line update. Careful attention needs + to be paid to the vertical position variables. */ + +/* Keep two buffers; one which reflects the current contents of the + screen, and the other to draw what we think the new contents should + be. Then compare the buffers, and make whatever changes to the + screen itself that we should. Finally, make the buffer that we + just drew into be the one which reflects the current contents of the + screen, and place the cursor where it belongs. + + Commands that want to can fix the display themselves, and then let + this function know that the display has been fixed by setting the + RL_DISPLAY_FIXED variable. This is good for efficiency. */ + +/* Application-specific redisplay function. */ +rl_voidfunc_t *rl_redisplay_function = rl_redisplay; + +/* Global variables declared here. */ +/* What YOU turn on when you have handled all redisplay yourself. */ +int rl_display_fixed = 0; + +int _rl_suppress_redisplay = 0; +int _rl_want_redisplay = 0; + +/* The stuff that gets printed out before the actual text of the line. + This is usually pointing to rl_prompt. */ +char *rl_display_prompt = (char *)NULL; + +/* Variables used to include the editing mode in the prompt. */ +char *_rl_emacs_mode_str; +int _rl_emacs_modestr_len; + +char *_rl_vi_ins_mode_str; +int _rl_vi_ins_modestr_len; + +char *_rl_vi_cmd_mode_str; +int _rl_vi_cmd_modestr_len; + +/* Pseudo-global variables declared here. */ + +/* The visible cursor position. If you print some text, adjust this. */ +/* NOTE: _rl_last_c_pos is used as a buffer index when not in a locale + supporting multibyte characters, and an absolute cursor position when + in such a locale. This is an artifact of the donated multibyte support. + Care must be taken when modifying its value. */ +int _rl_last_c_pos = 0; +int _rl_last_v_pos = 0; + +static int cpos_adjusted; +static int cpos_buffer_position; +static int displaying_prompt_first_line; +static int prompt_multibyte_chars; + +/* Number of lines currently on screen minus 1. */ +int _rl_vis_botlin = 0; + +/* Variables used only in this file. */ +/* The last left edge of text that was displayed. This is used when + doing horizontal scrolling. It shifts in thirds of a screenwidth. */ +static int last_lmargin; + +/* A buffer for `modeline' messages. */ +static char *msg_buf = 0; +static int msg_bufsiz = 0; + +/* Non-zero forces the redisplay even if we thought it was unnecessary. */ +static int forced_display; + +/* Default and initial buffer size. Can grow. */ +static int line_size = 1024; + +/* Variables to keep track of the expanded prompt string, which may + include invisible characters. */ + +static char *local_prompt, *local_prompt_prefix; +static int local_prompt_len; +static int prompt_visible_length, prompt_prefix_length; + +/* The number of invisible characters in the line currently being + displayed on the screen. */ +static int visible_wrap_offset; + +/* The number of invisible characters in the prompt string. Static so it + can be shared between rl_redisplay and update_line */ +static int wrap_offset; + +/* The index of the last invisible character in the prompt string. */ +static int prompt_last_invisible; + +/* The length (buffer offset) of the first line of the last (possibly + multi-line) buffer displayed on the screen. */ +static int visible_first_line_len; + +/* Number of invisible characters on the first physical line of the prompt. + Only valid when the number of physical characters in the prompt exceeds + (or is equal to) _rl_screenwidth. */ +static int prompt_invis_chars_first_line; + +static int prompt_last_screen_line; + +static int prompt_physical_chars; + +/* set to a non-zero value by rl_redisplay if we are marking modified history + lines and the current line is so marked. */ +static int modmark; + +/* Variables to save and restore prompt and display information. */ + +/* These are getting numerous enough that it's time to create a struct. */ + +static char *saved_local_prompt; +static char *saved_local_prefix; +static int saved_last_invisible; +static int saved_visible_length; +static int saved_prefix_length; +static int saved_local_length; +static int saved_invis_chars_first_line; +static int saved_physical_chars; + +/* Return a string indicating the editing mode, for use in the prompt. */ + +static char * +prompt_modestr (lenp) + int *lenp; +{ + if (rl_editing_mode == emacs_mode) + { + if (lenp) + *lenp = _rl_emacs_mode_str ? _rl_emacs_modestr_len : RL_EMACS_MODESTR_DEFLEN; + return _rl_emacs_mode_str ? _rl_emacs_mode_str : RL_EMACS_MODESTR_DEFAULT; + } + else if (_rl_keymap == vi_insertion_keymap) + { + if (lenp) + *lenp = _rl_vi_ins_mode_str ? _rl_vi_ins_modestr_len : RL_VI_INS_MODESTR_DEFLEN; + return _rl_vi_ins_mode_str ? _rl_vi_ins_mode_str : RL_VI_INS_MODESTR_DEFAULT; /* vi insert mode */ + } + else + { + if (lenp) + *lenp = _rl_vi_cmd_mode_str ? _rl_vi_cmd_modestr_len : RL_VI_CMD_MODESTR_DEFLEN; + return _rl_vi_cmd_mode_str ? _rl_vi_cmd_mode_str : RL_VI_CMD_MODESTR_DEFAULT; /* vi command mode */ + } +} + +/* Expand the prompt string S and return the number of visible + characters in *LP, if LP is not null. This is currently more-or-less + a placeholder for expansion. LIP, if non-null is a place to store the + index of the last invisible character in the returned string. NIFLP, + if non-zero, is a place to store the number of invisible characters in + the first prompt line. The previous are used as byte counts -- indexes + into a character buffer. */ + +/* Current implementation: + \001 (^A) start non-visible characters + \002 (^B) end non-visible characters + all characters except \001 and \002 (following a \001) are copied to + the returned string; all characters except those between \001 and + \002 are assumed to be `visible'. */ + +/* Possible values for FLAGS: + PMT_MULTILINE caller indicates that this is part of a multiline prompt +*/ + +static char * +expand_prompt (pmt, flags, lp, lip, niflp, vlp) + char *pmt; + int flags; + int *lp, *lip, *niflp, *vlp; +{ + char *r, *ret, *p, *igstart, *nprompt, *ms; + int l, rl, last, ignoring, ninvis, invfl, invflset, ind, pind, physchars; + int mlen; + + /* We only expand the mode string for the last line of a multiline prompt + (a prompt with embedded newlines). */ + ms = (((pmt == rl_prompt) ^ (flags & PMT_MULTILINE)) && _rl_show_mode_in_prompt) ? prompt_modestr (&mlen) : 0; + if (ms) + { + l = strlen (pmt); + nprompt = (char *)xmalloc (l + mlen + 1); + memcpy (nprompt, ms, mlen); + strcpy (nprompt + mlen, pmt); + } + else + nprompt = pmt; + + /* Short-circuit if we can. */ + if ((MB_CUR_MAX <= 1 || rl_byte_oriented) && strchr (nprompt, RL_PROMPT_START_IGNORE) == 0) + { + r = (nprompt == pmt) ? savestring (pmt) : nprompt; + + if (lp) + *lp = strlen (r); + if (lip) + *lip = 0; + if (niflp) + *niflp = 0; + if (vlp) + *vlp = lp ? *lp : strlen (r); + return r; + } + + l = strlen (nprompt); /* XXX */ + r = ret = (char *)xmalloc (l + 1); + + rl = physchars = 0; /* mode string now part of nprompt */ + invfl = 0; /* invisible chars in first line of prompt */ + invflset = 0; /* we only want to set invfl once */ + igstart = 0; + + for (ignoring = last = ninvis = 0, p = nprompt; p && *p; p++) + { + /* This code strips the invisible character string markers + RL_PROMPT_START_IGNORE and RL_PROMPT_END_IGNORE */ + if (ignoring == 0 && *p == RL_PROMPT_START_IGNORE) /* XXX - check ignoring? */ + { + ignoring = 1; + igstart = p; + continue; + } + else if (ignoring && *p == RL_PROMPT_END_IGNORE) + { + ignoring = 0; + if (p != (igstart + 1)) + last = r - ret - 1; + continue; + } + else + { +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + { + pind = p - nprompt; + ind = _rl_find_next_mbchar (nprompt, pind, 1, MB_FIND_NONZERO); + l = ind - pind; + while (l--) + *r++ = *p++; + if (!ignoring) + { + /* rl ends up being assigned to prompt_visible_length, + which is the number of characters in the buffer that + contribute to characters on the screen, which might + not be the same as the number of physical characters + on the screen in the presence of multibyte characters */ + rl += ind - pind; + physchars += _rl_col_width (nprompt, pind, ind, 0); + } + else + ninvis += ind - pind; + p--; /* compensate for later increment */ + } + else +#endif + { + *r++ = *p; + if (!ignoring) + { + rl++; /* visible length byte counter */ + physchars++; + } + else + ninvis++; /* invisible chars byte counter */ + } + + if (invflset == 0 && rl >= _rl_screenwidth) + { + invfl = ninvis; + invflset = 1; + } + } + } + + if (rl < _rl_screenwidth) + invfl = ninvis; + + *r = '\0'; + if (lp) + *lp = rl; + if (lip) + *lip = last; + if (niflp) + *niflp = invfl; + if (vlp) + *vlp = physchars; + + if (nprompt != pmt) + free (nprompt); + + return ret; +} + +/* Just strip out RL_PROMPT_START_IGNORE and RL_PROMPT_END_IGNORE from + PMT and return the rest of PMT. */ +char * +_rl_strip_prompt (pmt) + char *pmt; +{ + char *ret; + + ret = expand_prompt (pmt, 0, (int *)NULL, (int *)NULL, (int *)NULL, (int *)NULL); + return ret; +} + +void +_rl_reset_prompt () +{ + rl_visible_prompt_length = rl_expand_prompt (rl_prompt); +} + +/* + * Expand the prompt string into the various display components, if + * necessary. + * + * local_prompt = expanded last line of string in rl_display_prompt + * (portion after the final newline) + * local_prompt_prefix = portion before last newline of rl_display_prompt, + * expanded via expand_prompt + * prompt_visible_length = number of visible characters in local_prompt + * prompt_prefix_length = number of visible characters in local_prompt_prefix + * + * This function is called once per call to readline(). It may also be + * called arbitrarily to expand the primary prompt. + * + * The return value is the number of visible characters on the last line + * of the (possibly multi-line) prompt. + */ +int +rl_expand_prompt (prompt) + char *prompt; +{ + char *p, *t; + int c; + + /* Clear out any saved values. */ + FREE (local_prompt); + FREE (local_prompt_prefix); + + local_prompt = local_prompt_prefix = (char *)0; + local_prompt_len = 0; + prompt_last_invisible = prompt_invis_chars_first_line = 0; + prompt_visible_length = prompt_physical_chars = 0; + + if (prompt == 0 || *prompt == 0) + return (0); + + p = strrchr (prompt, '\n'); + if (p == 0) + { + /* The prompt is only one logical line, though it might wrap. */ + local_prompt = expand_prompt (prompt, 0, &prompt_visible_length, + &prompt_last_invisible, + &prompt_invis_chars_first_line, + &prompt_physical_chars); + local_prompt_prefix = (char *)0; + local_prompt_len = local_prompt ? strlen (local_prompt) : 0; + return (prompt_visible_length); + } + else + { + /* The prompt spans multiple lines. */ + t = ++p; + local_prompt = expand_prompt (p, PMT_MULTILINE, + &prompt_visible_length, + &prompt_last_invisible, + &prompt_invis_chars_first_line, + &prompt_physical_chars); + c = *t; *t = '\0'; + /* The portion of the prompt string up to and including the + final newline is now null-terminated. */ + local_prompt_prefix = expand_prompt (prompt, PMT_MULTILINE, + &prompt_prefix_length, + (int *)NULL, + (int *)NULL, + (int *)NULL); + *t = c; + local_prompt_len = local_prompt ? strlen (local_prompt) : 0; + return (prompt_prefix_length); + } +} + +/* Initialize the VISIBLE_LINE and INVISIBLE_LINE arrays, and their associated + arrays of line break markers. MINSIZE is the minimum size of VISIBLE_LINE + and INVISIBLE_LINE; if it is greater than LINE_SIZE, LINE_SIZE is + increased. If the lines have already been allocated, this ensures that + they can hold at least MINSIZE characters. */ +static void +init_line_structures (minsize) + int minsize; +{ + register int n; + + if (invisible_line == 0) /* initialize it */ + { + if (line_size < minsize) + line_size = minsize; + visible_line = (char *)xmalloc (line_size); + invisible_line = (char *)xmalloc (line_size); + } + else if (line_size < minsize) /* ensure it can hold MINSIZE chars */ + { + line_size *= 2; + if (line_size < minsize) + line_size = minsize; + visible_line = (char *)xrealloc (visible_line, line_size); + invisible_line = (char *)xrealloc (invisible_line, line_size); + } + + for (n = minsize; n < line_size; n++) + { + visible_line[n] = 0; + invisible_line[n] = 1; + } + + if (vis_lbreaks == 0) + { + /* should be enough. */ + inv_lbsize = vis_lbsize = 256; + +#if defined (HANDLE_MULTIBYTE) + line_state_visible->wbsize = vis_lbsize; + line_state_visible->wrapped_line = (int *)xmalloc (line_state_visible->wbsize * sizeof (int)); + + line_state_invisible->wbsize = inv_lbsize; + line_state_invisible->wrapped_line = (int *)xmalloc (line_state_invisible->wbsize * sizeof (int)); +#endif + + inv_lbreaks = (int *)xmalloc (inv_lbsize * sizeof (int)); + vis_lbreaks = (int *)xmalloc (vis_lbsize * sizeof (int)); + inv_lbreaks[0] = vis_lbreaks[0] = 0; + } + + line_structures_initialized = 1; +} + +/* Basic redisplay algorithm. */ +void +rl_redisplay () +{ + register int in, out, c, linenum, cursor_linenum; + register char *line; + int inv_botlin, lb_botlin, lb_linenum, o_cpos; + int newlines, lpos, temp, n0, num, prompt_lines_estimate; + char *prompt_this_line; +#if defined (HANDLE_MULTIBYTE) + wchar_t wc; + size_t wc_bytes; + int wc_width; + mbstate_t ps; + int _rl_wrapped_multicolumn = 0; +#endif + + if (_rl_echoing_p == 0) + return; + + /* Block keyboard interrupts because this function manipulates global + data structures. */ + _rl_block_sigint (); + RL_SETSTATE (RL_STATE_REDISPLAYING); + + if (!rl_display_prompt) + rl_display_prompt = ""; + + if (line_structures_initialized == 0) + { + init_line_structures (0); + rl_on_new_line (); + } + + /* Draw the line into the buffer. */ + cpos_buffer_position = -1; + + prompt_multibyte_chars = prompt_visible_length - prompt_physical_chars; + + line = invisible_line; + out = inv_botlin = 0; + + /* Mark the line as modified or not. We only do this for history + lines. */ + modmark = 0; + if (_rl_mark_modified_lines && current_history () && rl_undo_list) + { + line[out++] = '*'; + line[out] = '\0'; + modmark = 1; + } + + /* If someone thought that the redisplay was handled, but the currently + visible line has a different modification state than the one about + to become visible, then correct the caller's misconception. */ + if (visible_line[0] != invisible_line[0]) + rl_display_fixed = 0; + + /* If the prompt to be displayed is the `primary' readline prompt (the + one passed to readline()), use the values we have already expanded. + If not, use what's already in rl_display_prompt. WRAP_OFFSET is the + number of non-visible characters in the prompt string. */ + if (rl_display_prompt == rl_prompt || local_prompt) + { + if (local_prompt_prefix && forced_display) + _rl_output_some_chars (local_prompt_prefix, strlen (local_prompt_prefix)); + + if (local_prompt_len > 0) + { + temp = local_prompt_len + out + 2; + if (temp >= line_size) + { + line_size = (temp + 1024) - (temp % 1024); + visible_line = (char *)xrealloc (visible_line, line_size); + line = invisible_line = (char *)xrealloc (invisible_line, line_size); + } + strncpy (line + out, local_prompt, local_prompt_len); + out += local_prompt_len; + } + line[out] = '\0'; + wrap_offset = local_prompt_len - prompt_visible_length; + } + else + { + int pmtlen; + prompt_this_line = strrchr (rl_display_prompt, '\n'); + if (!prompt_this_line) + prompt_this_line = rl_display_prompt; + else + { + prompt_this_line++; + pmtlen = prompt_this_line - rl_display_prompt; /* temp var */ + if (forced_display) + { + _rl_output_some_chars (rl_display_prompt, pmtlen); + /* Make sure we are at column zero even after a newline, + regardless of the state of terminal output processing. */ + if (pmtlen < 2 || prompt_this_line[-2] != '\r') + cr (); + } + } + + prompt_physical_chars = pmtlen = strlen (prompt_this_line); + temp = pmtlen + out + 2; + if (temp >= line_size) + { + line_size = (temp + 1024) - (temp % 1024); + visible_line = (char *)xrealloc (visible_line, line_size); + line = invisible_line = (char *)xrealloc (invisible_line, line_size); + } + strncpy (line + out, prompt_this_line, pmtlen); + out += pmtlen; + line[out] = '\0'; + wrap_offset = prompt_invis_chars_first_line = 0; + } + +#define CHECK_INV_LBREAKS() \ + do { \ + if (newlines >= (inv_lbsize - 2)) \ + { \ + inv_lbsize *= 2; \ + inv_lbreaks = (int *)xrealloc (inv_lbreaks, inv_lbsize * sizeof (int)); \ + } \ + } while (0) + +#if defined (HANDLE_MULTIBYTE) +#define CHECK_LPOS() \ + do { \ + lpos++; \ + if (lpos >= _rl_screenwidth) \ + { \ + if (newlines >= (inv_lbsize - 2)) \ + { \ + inv_lbsize *= 2; \ + inv_lbreaks = (int *)xrealloc (inv_lbreaks, inv_lbsize * sizeof (int)); \ + } \ + inv_lbreaks[++newlines] = out; \ + if (newlines >= (line_state_invisible->wbsize - 1)) \ + { \ + line_state_invisible->wbsize *= 2; \ + line_state_invisible->wrapped_line = (int *)xrealloc (line_state_invisible->wrapped_line, line_state_invisible->wbsize * sizeof(int)); \ + } \ + line_state_invisible->wrapped_line[newlines] = _rl_wrapped_multicolumn; \ + lpos = 0; \ + } \ + } while (0) +#else +#define CHECK_LPOS() \ + do { \ + lpos++; \ + if (lpos >= _rl_screenwidth) \ + { \ + if (newlines >= (inv_lbsize - 2)) \ + { \ + inv_lbsize *= 2; \ + inv_lbreaks = (int *)xrealloc (inv_lbreaks, inv_lbsize * sizeof (int)); \ + } \ + inv_lbreaks[++newlines] = out; \ + lpos = 0; \ + } \ + } while (0) +#endif + + /* inv_lbreaks[i] is where line i starts in the buffer. */ + inv_lbreaks[newlines = 0] = 0; + lpos = prompt_physical_chars + modmark; + +#if defined (HANDLE_MULTIBYTE) + memset (line_state_invisible->wrapped_line, 0, line_state_invisible->wbsize * sizeof (int)); + num = 0; +#endif + + /* prompt_invis_chars_first_line is the number of invisible characters in + the first physical line of the prompt. + wrap_offset - prompt_invis_chars_first_line is the number of invis + chars on the second (or, more generally, last) line. */ + + /* This is zero-based, used to set the newlines */ + prompt_lines_estimate = lpos / _rl_screenwidth; + + /* what if lpos is already >= _rl_screenwidth before we start drawing the + contents of the command line? */ + while (lpos >= _rl_screenwidth) + { + int z; + /* fix from Darin Johnson <darin@acuson.com> for prompt string with + invisible characters that is longer than the screen width. The + prompt_invis_chars_first_line variable could be made into an array + saying how many invisible characters there are per line, but that's + probably too much work for the benefit gained. How many people have + prompts that exceed two physical lines? + Additional logic fix from Edward Catmur <ed@catmur.co.uk> */ +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0 && prompt_multibyte_chars > 0) + { + n0 = num; + temp = local_prompt_len; + while (num < temp) + { + z = _rl_col_width (local_prompt, n0, num, 1); + if (z > _rl_screenwidth) + { + num = _rl_find_prev_mbchar (local_prompt, num, MB_FIND_ANY); + break; + } + else if (z == _rl_screenwidth) + break; + num++; + } + temp = num; + } + else +#endif /* !HANDLE_MULTIBYTE */ + temp = ((newlines + 1) * _rl_screenwidth); + + /* Now account for invisible characters in the current line. */ + /* XXX - this assumes that the invisible characters may be split, but only + between the first and the last lines. */ + temp += (newlines == 0) ? prompt_invis_chars_first_line + : ((newlines == prompt_lines_estimate) ? wrap_offset : prompt_invis_chars_first_line); + + inv_lbreaks[++newlines] = temp; +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0 && prompt_multibyte_chars > 0) + lpos -= _rl_col_width (local_prompt, n0, num, 1); + else +#endif + lpos -= _rl_screenwidth; + } + + prompt_last_screen_line = newlines; + + /* Draw the rest of the line (after the prompt) into invisible_line, keeping + track of where the cursor is (cpos_buffer_position), the number of the line containing + the cursor (lb_linenum), the last line number (lb_botlin and inv_botlin). + It maintains an array of line breaks for display (inv_lbreaks). + This handles expanding tabs for display and displaying meta characters. */ + lb_linenum = 0; +#if defined (HANDLE_MULTIBYTE) + in = 0; + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + { + memset (&ps, 0, sizeof (mbstate_t)); + /* XXX - what if wc_bytes ends up <= 0? check for MB_INVALIDCH */ + wc_bytes = mbrtowc (&wc, rl_line_buffer, rl_end, &ps); + } + else + wc_bytes = 1; + while (in < rl_end) +#else + for (in = 0; in < rl_end; in++) +#endif + { + c = (unsigned char)rl_line_buffer[in]; + +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + { + if (MB_INVALIDCH (wc_bytes)) + { + /* Byte sequence is invalid or shortened. Assume that the + first byte represents a character. */ + wc_bytes = 1; + /* Assume that a character occupies a single column. */ + wc_width = 1; + memset (&ps, 0, sizeof (mbstate_t)); + } + else if (MB_NULLWCH (wc_bytes)) + break; /* Found '\0' */ + else + { + temp = WCWIDTH (wc); + wc_width = (temp >= 0) ? temp : 1; + } + } +#endif + + if (out + 8 >= line_size) /* XXX - 8 for \t */ + { + line_size *= 2; + visible_line = (char *)xrealloc (visible_line, line_size); + invisible_line = (char *)xrealloc (invisible_line, line_size); + line = invisible_line; + } + + if (in == rl_point) + { + cpos_buffer_position = out; + lb_linenum = newlines; + } + +#if defined (HANDLE_MULTIBYTE) + if (META_CHAR (c) && _rl_output_meta_chars == 0) /* XXX - clean up */ +#else + if (META_CHAR (c)) +#endif + { + if (_rl_output_meta_chars == 0) + { + sprintf (line + out, "\\%o", c); + + if (lpos + 4 >= _rl_screenwidth) + { + temp = _rl_screenwidth - lpos; + CHECK_INV_LBREAKS (); + inv_lbreaks[++newlines] = out + temp; + lpos = 4 - temp; + } + else + lpos += 4; + + out += 4; + } + else + { + line[out++] = c; + CHECK_LPOS(); + } + } +#if defined (DISPLAY_TABS) + else if (c == '\t') + { + register int newout; + +#if 0 + newout = (out | (int)7) + 1; +#else + newout = out + 8 - lpos % 8; +#endif + temp = newout - out; + if (lpos + temp >= _rl_screenwidth) + { + register int temp2; + temp2 = _rl_screenwidth - lpos; + CHECK_INV_LBREAKS (); + inv_lbreaks[++newlines] = out + temp2; + lpos = temp - temp2; + while (out < newout) + line[out++] = ' '; + } + else + { + while (out < newout) + line[out++] = ' '; + lpos += temp; + } + } +#endif + else if (c == '\n' && _rl_horizontal_scroll_mode == 0 && _rl_term_up && *_rl_term_up) + { + line[out++] = '\0'; /* XXX - sentinel */ + CHECK_INV_LBREAKS (); + inv_lbreaks[++newlines] = out; + lpos = 0; + } + else if (CTRL_CHAR (c) || c == RUBOUT) + { + line[out++] = '^'; + CHECK_LPOS(); + line[out++] = CTRL_CHAR (c) ? UNCTRL (c) : '?'; + CHECK_LPOS(); + } + else + { +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + { + register int i; + + _rl_wrapped_multicolumn = 0; + + if (_rl_screenwidth < lpos + wc_width) + for (i = lpos; i < _rl_screenwidth; i++) + { + /* The space will be removed in update_line() */ + line[out++] = ' '; + _rl_wrapped_multicolumn++; + CHECK_LPOS(); + } + if (in == rl_point) + { + cpos_buffer_position = out; + lb_linenum = newlines; + } + for (i = in; i < in+wc_bytes; i++) + line[out++] = rl_line_buffer[i]; + for (i = 0; i < wc_width; i++) + CHECK_LPOS(); + } + else + { + line[out++] = c; + CHECK_LPOS(); + } +#else + line[out++] = c; + CHECK_LPOS(); +#endif + } + +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + { + in += wc_bytes; + /* XXX - what if wc_bytes ends up <= 0? check for MB_INVALIDCH */ + wc_bytes = mbrtowc (&wc, rl_line_buffer + in, rl_end - in, &ps); + } + else + in++; +#endif + + } + line[out] = '\0'; + if (cpos_buffer_position < 0) + { + cpos_buffer_position = out; + lb_linenum = newlines; + } + + inv_botlin = lb_botlin = newlines; + CHECK_INV_LBREAKS (); + inv_lbreaks[newlines+1] = out; + cursor_linenum = lb_linenum; + + /* CPOS_BUFFER_POSITION == position in buffer where cursor should be placed. + CURSOR_LINENUM == line number where the cursor should be placed. */ + + /* PWP: now is when things get a bit hairy. The visible and invisible + line buffers are really multiple lines, which would wrap every + (screenwidth - 1) characters. Go through each in turn, finding + the changed region and updating it. The line order is top to bottom. */ + + /* If we can move the cursor up and down, then use multiple lines, + otherwise, let long lines display in a single terminal line, and + horizontally scroll it. */ + displaying_prompt_first_line = 1; + if (_rl_horizontal_scroll_mode == 0 && _rl_term_up && *_rl_term_up) + { + int nleft, pos, changed_screen_line, tx; + + if (!rl_display_fixed || forced_display) + { + forced_display = 0; + + /* If we have more than a screenful of material to display, then + only display a screenful. We should display the last screen, + not the first. */ + if (out >= _rl_screenchars) + { + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + out = _rl_find_prev_mbchar (line, _rl_screenchars, MB_FIND_ANY); + else + out = _rl_screenchars - 1; + } + + /* The first line is at character position 0 in the buffer. The + second and subsequent lines start at inv_lbreaks[N], offset by + OFFSET (which has already been calculated above). */ + +#define INVIS_FIRST() (prompt_physical_chars > _rl_screenwidth ? prompt_invis_chars_first_line : wrap_offset) +#define WRAP_OFFSET(line, offset) ((line == 0) \ + ? (offset ? INVIS_FIRST() : 0) \ + : ((line == prompt_last_screen_line) ? wrap_offset-prompt_invis_chars_first_line : 0)) +#define W_OFFSET(line, offset) ((line) == 0 ? offset : 0) +#define VIS_LLEN(l) ((l) > _rl_vis_botlin ? 0 : (vis_lbreaks[l+1] - vis_lbreaks[l])) +#define INV_LLEN(l) (inv_lbreaks[l+1] - inv_lbreaks[l]) +#define VIS_CHARS(line) (visible_line + vis_lbreaks[line]) +#define VIS_LINE(line) ((line) > _rl_vis_botlin) ? "" : VIS_CHARS(line) +#define INV_LINE(line) (invisible_line + inv_lbreaks[line]) + +#define OLD_CPOS_IN_PROMPT() (cpos_adjusted == 0 && \ + _rl_last_c_pos != o_cpos && \ + _rl_last_c_pos > wrap_offset && \ + o_cpos < prompt_last_invisible) + + /* For each line in the buffer, do the updating display. */ + for (linenum = 0; linenum <= inv_botlin; linenum++) + { + /* This can lead us astray if we execute a program that changes + the locale from a non-multibyte to a multibyte one. */ + o_cpos = _rl_last_c_pos; + cpos_adjusted = 0; + update_line (VIS_LINE(linenum), INV_LINE(linenum), linenum, + VIS_LLEN(linenum), INV_LLEN(linenum), inv_botlin); + + /* update_line potentially changes _rl_last_c_pos, but doesn't + take invisible characters into account, since _rl_last_c_pos + is an absolute cursor position in a multibyte locale. See + if compensating here is the right thing, or if we have to + change update_line itself. There are several cases in which + update_line adjusts _rl_last_c_pos itself (so it can pass + _rl_move_cursor_relative accurate values); it communicates + this back by setting cpos_adjusted. If we assume that + _rl_last_c_pos is correct (an absolute cursor position) each + time update_line is called, then we can assume in our + calculations that o_cpos does not need to be adjusted by + wrap_offset. */ + if (linenum == 0 && (MB_CUR_MAX > 1 && rl_byte_oriented == 0) && OLD_CPOS_IN_PROMPT()) + _rl_last_c_pos -= prompt_invis_chars_first_line; /* XXX - was wrap_offset */ + else if (linenum == prompt_last_screen_line && prompt_physical_chars > _rl_screenwidth && + (MB_CUR_MAX > 1 && rl_byte_oriented == 0) && + cpos_adjusted == 0 && + _rl_last_c_pos != o_cpos && + _rl_last_c_pos > (prompt_last_invisible - _rl_screenwidth - prompt_invis_chars_first_line)) + _rl_last_c_pos -= (wrap_offset-prompt_invis_chars_first_line); + + /* If this is the line with the prompt, we might need to + compensate for invisible characters in the new line. Do + this only if there is not more than one new line (which + implies that we completely overwrite the old visible line) + and the new line is shorter than the old. Make sure we are + at the end of the new line before clearing. */ + if (linenum == 0 && + inv_botlin == 0 && _rl_last_c_pos == out && + (wrap_offset > visible_wrap_offset) && + (_rl_last_c_pos < visible_first_line_len)) + { + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + nleft = _rl_screenwidth - _rl_last_c_pos; + else + nleft = _rl_screenwidth + wrap_offset - _rl_last_c_pos; + if (nleft) + _rl_clear_to_eol (nleft); + } +#if 0 + /* This segment is intended to handle the case where the prompt + has invisible characters on the second line and the new line + to be displayed needs to clear the rest of the old characters + out (e.g., when printing the i-search prompt). In general, + the case of the new line being shorter than the old. + Incomplete */ + else if (linenum == prompt_last_screen_line && + prompt_physical_chars > _rl_screenwidth && + wrap_offset != prompt_invis_chars_first_line && + _rl_last_c_pos == out && +#endif + + + /* Since the new first line is now visible, save its length. */ + if (linenum == 0) + visible_first_line_len = (inv_botlin > 0) ? inv_lbreaks[1] : out - wrap_offset; + } + + /* We may have deleted some lines. If so, clear the left over + blank ones at the bottom out. */ + if (_rl_vis_botlin > inv_botlin) + { + char *tt; + for (; linenum <= _rl_vis_botlin; linenum++) + { + tt = VIS_CHARS (linenum); + _rl_move_vert (linenum); + _rl_move_cursor_relative (0, tt); + _rl_clear_to_eol + ((linenum == _rl_vis_botlin) ? strlen (tt) : _rl_screenwidth); + } + } + _rl_vis_botlin = inv_botlin; + + /* CHANGED_SCREEN_LINE is set to 1 if we have moved to a + different screen line during this redisplay. */ + changed_screen_line = _rl_last_v_pos != cursor_linenum; + if (changed_screen_line) + { + _rl_move_vert (cursor_linenum); + /* If we moved up to the line with the prompt using _rl_term_up, + the physical cursor position on the screen stays the same, + but the buffer position needs to be adjusted to account + for invisible characters. */ + if ((MB_CUR_MAX == 1 || rl_byte_oriented) && cursor_linenum == 0 && wrap_offset) + _rl_last_c_pos += wrap_offset; + } + + /* We have to reprint the prompt if it contains invisible + characters, since it's not generally OK to just reprint + the characters from the current cursor position. But we + only need to reprint it if the cursor is before the last + invisible character in the prompt string. */ + nleft = prompt_visible_length + wrap_offset; + if (cursor_linenum == 0 && wrap_offset > 0 && _rl_last_c_pos > 0 && +#if 0 + _rl_last_c_pos <= PROMPT_ENDING_INDEX && local_prompt) +#else + _rl_last_c_pos < PROMPT_ENDING_INDEX && local_prompt) +#endif + { +#if defined (__MSDOS__) + putc ('\r', rl_outstream); +#else + if (_rl_term_cr) + tputs (_rl_term_cr, 1, _rl_output_character_function); +#endif + if (modmark) + _rl_output_some_chars ("*", 1); + + _rl_output_some_chars (local_prompt, nleft); + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + _rl_last_c_pos = _rl_col_width (local_prompt, 0, nleft, 1) - wrap_offset + modmark; + else + _rl_last_c_pos = nleft + modmark; + } + + /* Where on that line? And where does that line start + in the buffer? */ + pos = inv_lbreaks[cursor_linenum]; + /* nleft == number of characters in the line buffer between the + start of the line and the desired cursor position. */ + nleft = cpos_buffer_position - pos; + + /* NLEFT is now a number of characters in a buffer. When in a + multibyte locale, however, _rl_last_c_pos is an absolute cursor + position that doesn't take invisible characters in the prompt + into account. We use a fudge factor to compensate. */ + + /* Since _rl_backspace() doesn't know about invisible characters in the + prompt, and there's no good way to tell it, we compensate for + those characters here and call _rl_backspace() directly. */ + if (wrap_offset && cursor_linenum == 0 && nleft < _rl_last_c_pos) + { + /* TX == new physical cursor position in multibyte locale. */ + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + tx = _rl_col_width (&visible_line[pos], 0, nleft, 1) - visible_wrap_offset; + else + tx = nleft; + if (tx >= 0 && _rl_last_c_pos > tx) + { + _rl_backspace (_rl_last_c_pos - tx); /* XXX */ + _rl_last_c_pos = tx; + } + } + + /* We need to note that in a multibyte locale we are dealing with + _rl_last_c_pos as an absolute cursor position, but moving to a + point specified by a buffer position (NLEFT) that doesn't take + invisible characters into account. */ + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + _rl_move_cursor_relative (nleft, &invisible_line[pos]); + else if (nleft != _rl_last_c_pos) + _rl_move_cursor_relative (nleft, &invisible_line[pos]); + } + } + else /* Do horizontal scrolling. */ + { +#define M_OFFSET(margin, offset) ((margin) == 0 ? offset : 0) + int lmargin, ndisp, nleft, phys_c_pos, t; + + /* Always at top line. */ + _rl_last_v_pos = 0; + + /* Compute where in the buffer the displayed line should start. This + will be LMARGIN. */ + + /* The number of characters that will be displayed before the cursor. */ + ndisp = cpos_buffer_position - wrap_offset; + nleft = prompt_visible_length + wrap_offset; + /* Where the new cursor position will be on the screen. This can be + longer than SCREENWIDTH; if it is, lmargin will be adjusted. */ + phys_c_pos = cpos_buffer_position - (last_lmargin ? last_lmargin : wrap_offset); + t = _rl_screenwidth / 3; + + /* If the number of characters had already exceeded the screenwidth, + last_lmargin will be > 0. */ + + /* If the number of characters to be displayed is more than the screen + width, compute the starting offset so that the cursor is about + two-thirds of the way across the screen. */ + if (phys_c_pos > _rl_screenwidth - 2) + { + lmargin = cpos_buffer_position - (2 * t); + if (lmargin < 0) + lmargin = 0; + /* If the left margin would be in the middle of a prompt with + invisible characters, don't display the prompt at all. */ + if (wrap_offset && lmargin > 0 && lmargin < nleft) + lmargin = nleft; + } + else if (ndisp < _rl_screenwidth - 2) /* XXX - was -1 */ + lmargin = 0; + else if (phys_c_pos < 1) + { + /* If we are moving back towards the beginning of the line and + the last margin is no longer correct, compute a new one. */ + lmargin = ((cpos_buffer_position - 1) / t) * t; /* XXX */ + if (wrap_offset && lmargin > 0 && lmargin < nleft) + lmargin = nleft; + } + else + lmargin = last_lmargin; + + displaying_prompt_first_line = lmargin < nleft; + + /* If the first character on the screen isn't the first character + in the display line, indicate this with a special character. */ + if (lmargin > 0) + line[lmargin] = '<'; + + /* If SCREENWIDTH characters starting at LMARGIN do not encompass + the whole line, indicate that with a special character at the + right edge of the screen. If LMARGIN is 0, we need to take the + wrap offset into account. */ + t = lmargin + M_OFFSET (lmargin, wrap_offset) + _rl_screenwidth; + if (t < out) + line[t - 1] = '>'; + + if (rl_display_fixed == 0 || forced_display || lmargin != last_lmargin) + { + forced_display = 0; + o_cpos = _rl_last_c_pos; + cpos_adjusted = 0; + update_line (&visible_line[last_lmargin], + &invisible_line[lmargin], + 0, + _rl_screenwidth + visible_wrap_offset, + _rl_screenwidth + (lmargin ? 0 : wrap_offset), + 0); + + if ((MB_CUR_MAX > 1 && rl_byte_oriented == 0) && + displaying_prompt_first_line && OLD_CPOS_IN_PROMPT()) + _rl_last_c_pos -= prompt_invis_chars_first_line; /* XXX - was wrap_offset */ + + /* If the visible new line is shorter than the old, but the number + of invisible characters is greater, and we are at the end of + the new line, we need to clear to eol. */ + t = _rl_last_c_pos - M_OFFSET (lmargin, wrap_offset); + if ((M_OFFSET (lmargin, wrap_offset) > visible_wrap_offset) && + (_rl_last_c_pos == out) && displaying_prompt_first_line && + t < visible_first_line_len) + { + nleft = _rl_screenwidth - t; + _rl_clear_to_eol (nleft); + } + visible_first_line_len = out - lmargin - M_OFFSET (lmargin, wrap_offset); + if (visible_first_line_len > _rl_screenwidth) + visible_first_line_len = _rl_screenwidth; + + _rl_move_cursor_relative (cpos_buffer_position - lmargin, &invisible_line[lmargin]); + last_lmargin = lmargin; + } + } + fflush (rl_outstream); + + /* Swap visible and non-visible lines. */ + { + struct line_state *vtemp = line_state_visible; + + line_state_visible = line_state_invisible; + line_state_invisible = vtemp; + + rl_display_fixed = 0; + /* If we are displaying on a single line, and last_lmargin is > 0, we + are not displaying any invisible characters, so set visible_wrap_offset + to 0. */ + if (_rl_horizontal_scroll_mode && last_lmargin) + visible_wrap_offset = 0; + else + visible_wrap_offset = wrap_offset; + } + + RL_UNSETSTATE (RL_STATE_REDISPLAYING); + _rl_release_sigint (); +} + +/* PWP: update_line() is based on finding the middle difference of each + line on the screen; vis: + + /old first difference + /beginning of line | /old last same /old EOL + v v v v +old: eddie> Oh, my little gruntle-buggy is to me, as lurgid as +new: eddie> Oh, my little buggy says to me, as lurgid as + ^ ^ ^ ^ + \beginning of line | \new last same \new end of line + \new first difference + + All are character pointers for the sake of speed. Special cases for + no differences, as well as for end of line additions must be handled. + + Could be made even smarter, but this works well enough */ +static void +update_line (old, new, current_line, omax, nmax, inv_botlin) + register char *old, *new; + int current_line, omax, nmax, inv_botlin; +{ + register char *ofd, *ols, *oe, *nfd, *nls, *ne; + int temp, lendiff, wsatend, od, nd, twidth, o_cpos; + int current_invis_chars; + int col_lendiff, col_temp; + int bytes_to_insert; +#if defined (HANDLE_MULTIBYTE) + mbstate_t ps_new, ps_old; + int new_offset, old_offset; +#endif + + /* If we're at the right edge of a terminal that supports xn, we're + ready to wrap around, so do so. This fixes problems with knowing + the exact cursor position and cut-and-paste with certain terminal + emulators. In this calculation, TEMP is the physical screen + position of the cursor. */ + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + temp = _rl_last_c_pos; + else + temp = _rl_last_c_pos - WRAP_OFFSET (_rl_last_v_pos, visible_wrap_offset); + if (temp == _rl_screenwidth && _rl_term_autowrap && !_rl_horizontal_scroll_mode + && _rl_last_v_pos == current_line - 1) + { +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + { + wchar_t wc; + mbstate_t ps; + int tempwidth, bytes; + size_t ret; + + /* This fixes only double-column characters, but if the wrapped + character consumes more than three columns, spaces will be + inserted in the string buffer. */ + if (current_line < line_state_visible->wbsize && line_state_visible->wrapped_line[current_line] > 0) + _rl_clear_to_eol (line_state_visible->wrapped_line[current_line]); + + memset (&ps, 0, sizeof (mbstate_t)); + ret = mbrtowc (&wc, new, MB_CUR_MAX, &ps); + if (MB_INVALIDCH (ret)) + { + tempwidth = 1; + ret = 1; + } + else if (MB_NULLWCH (ret)) + tempwidth = 0; + else + tempwidth = WCWIDTH (wc); + + if (tempwidth > 0) + { + int count, i; + bytes = ret; + for (count = 0; count < bytes; count++) + putc (new[count], rl_outstream); + _rl_last_c_pos = tempwidth; + _rl_last_v_pos++; + memset (&ps, 0, sizeof (mbstate_t)); + ret = mbrtowc (&wc, old, MB_CUR_MAX, &ps); + if (ret != 0 && bytes != 0) + { + if (MB_INVALIDCH (ret)) + ret = 1; + memmove (old+bytes, old+ret, strlen (old+ret)); + memcpy (old, new, bytes); + /* Fix up indices if we copy data from one line to another */ + omax += bytes - ret; + for (i = current_line+1; i <= inv_botlin+1; i++) + vis_lbreaks[i] += bytes - ret; + } + } + else + { + putc (' ', rl_outstream); + _rl_last_c_pos = 1; + _rl_last_v_pos++; + if (old[0] && new[0]) + old[0] = new[0]; + } + } + else +#endif + { + if (new[0]) + putc (new[0], rl_outstream); + else + putc (' ', rl_outstream); + _rl_last_c_pos = 1; + _rl_last_v_pos++; + if (old[0] && new[0]) + old[0] = new[0]; + } + } + + + /* Find first difference. */ +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + { + /* See if the old line is a subset of the new line, so that the + only change is adding characters. */ + temp = (omax < nmax) ? omax : nmax; + if (memcmp (old, new, temp) == 0) /* adding at the end */ + { + new_offset = old_offset = temp; + ofd = old + temp; + nfd = new + temp; + } + else + { + memset (&ps_new, 0, sizeof(mbstate_t)); + memset (&ps_old, 0, sizeof(mbstate_t)); + + if (omax == nmax && STREQN (new, old, omax)) + { + old_offset = omax; + new_offset = nmax; + ofd = old + omax; + nfd = new + nmax; + } + else + { + new_offset = old_offset = 0; + for (ofd = old, nfd = new; + (ofd - old < omax) && *ofd && + _rl_compare_chars(old, old_offset, &ps_old, new, new_offset, &ps_new); ) + { + old_offset = _rl_find_next_mbchar (old, old_offset, 1, MB_FIND_ANY); + new_offset = _rl_find_next_mbchar (new, new_offset, 1, MB_FIND_ANY); + + ofd = old + old_offset; + nfd = new + new_offset; + } + } + } + } + else +#endif + for (ofd = old, nfd = new; + (ofd - old < omax) && *ofd && (*ofd == *nfd); + ofd++, nfd++) + ; + + /* Move to the end of the screen line. ND and OD are used to keep track + of the distance between ne and new and oe and old, respectively, to + move a subtraction out of each loop. */ + for (od = ofd - old, oe = ofd; od < omax && *oe; oe++, od++); + for (nd = nfd - new, ne = nfd; nd < nmax && *ne; ne++, nd++); + + /* If no difference, continue to next line. */ + if (ofd == oe && nfd == ne) + return; + +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0 && _rl_utf8locale) + { + wchar_t wc; + mbstate_t ps = { 0 }; + int t; + + /* If the first character in the difference is a zero-width character, + assume it's a combining character and back one up so the two base + characters no longer compare equivalently. */ + t = mbrtowc (&wc, ofd, MB_CUR_MAX, &ps); + if (t > 0 && UNICODE_COMBINING_CHAR (wc) && WCWIDTH (wc) == 0) + { + old_offset = _rl_find_prev_mbchar (old, ofd - old, MB_FIND_ANY); + new_offset = _rl_find_prev_mbchar (new, nfd - new, MB_FIND_ANY); + ofd = old + old_offset; /* equal by definition */ + nfd = new + new_offset; + } + } +#endif + + wsatend = 1; /* flag for trailing whitespace */ + +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + { + ols = old + _rl_find_prev_mbchar (old, oe - old, MB_FIND_ANY); + nls = new + _rl_find_prev_mbchar (new, ne - new, MB_FIND_ANY); + + while ((ols > ofd) && (nls > nfd)) + { + memset (&ps_old, 0, sizeof (mbstate_t)); + memset (&ps_new, 0, sizeof (mbstate_t)); + +#if 0 + /* On advice from jir@yamato.ibm.com */ + _rl_adjust_point (old, ols - old, &ps_old); + _rl_adjust_point (new, nls - new, &ps_new); +#endif + + if (_rl_compare_chars (old, ols - old, &ps_old, new, nls - new, &ps_new) == 0) + break; + + if (*ols == ' ') + wsatend = 0; + + ols = old + _rl_find_prev_mbchar (old, ols - old, MB_FIND_ANY); + nls = new + _rl_find_prev_mbchar (new, nls - new, MB_FIND_ANY); + } + } + else + { +#endif /* HANDLE_MULTIBYTE */ + ols = oe - 1; /* find last same */ + nls = ne - 1; + while ((ols > ofd) && (nls > nfd) && (*ols == *nls)) + { + if (*ols != ' ') + wsatend = 0; + ols--; + nls--; + } +#if defined (HANDLE_MULTIBYTE) + } +#endif + + if (wsatend) + { + ols = oe; + nls = ne; + } +#if defined (HANDLE_MULTIBYTE) + /* This may not work for stateful encoding, but who cares? To handle + stateful encoding properly, we have to scan each string from the + beginning and compare. */ + else if (_rl_compare_chars (ols, 0, NULL, nls, 0, NULL) == 0) +#else + else if (*ols != *nls) +#endif + { + if (*ols) /* don't step past the NUL */ + { + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + ols = old + _rl_find_next_mbchar (old, ols - old, 1, MB_FIND_ANY); + else + ols++; + } + if (*nls) + { + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + nls = new + _rl_find_next_mbchar (new, nls - new, 1, MB_FIND_ANY); + else + nls++; + } + } + + /* count of invisible characters in the current invisible line. */ + current_invis_chars = W_OFFSET (current_line, wrap_offset); + if (_rl_last_v_pos != current_line) + { + _rl_move_vert (current_line); + if ((MB_CUR_MAX == 1 || rl_byte_oriented) && current_line == 0 && visible_wrap_offset) + _rl_last_c_pos += visible_wrap_offset; + } + + /* If this is the first line and there are invisible characters in the + prompt string, and the prompt string has not changed, and the current + cursor position is before the last invisible character in the prompt, + and the index of the character to move to is past the end of the prompt + string, then redraw the entire prompt string. We can only do this + reliably if the terminal supports a `cr' capability. + + This is not an efficiency hack -- there is a problem with redrawing + portions of the prompt string if they contain terminal escape + sequences (like drawing the `unbold' sequence without a corresponding + `bold') that manifests itself on certain terminals. */ + + lendiff = local_prompt_len; + od = ofd - old; /* index of first difference in visible line */ + if (current_line == 0 && !_rl_horizontal_scroll_mode && + _rl_term_cr && lendiff > prompt_visible_length && _rl_last_c_pos > 0 && + od >= lendiff && _rl_last_c_pos < PROMPT_ENDING_INDEX) + { +#if defined (__MSDOS__) + putc ('\r', rl_outstream); +#else + tputs (_rl_term_cr, 1, _rl_output_character_function); +#endif + if (modmark) + _rl_output_some_chars ("*", 1); + _rl_output_some_chars (local_prompt, lendiff); + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + { + /* We take wrap_offset into account here so we can pass correct + information to _rl_move_cursor_relative. */ + _rl_last_c_pos = _rl_col_width (local_prompt, 0, lendiff, 1) - wrap_offset + modmark; + cpos_adjusted = 1; + } + else + _rl_last_c_pos = lendiff + modmark; + } + + o_cpos = _rl_last_c_pos; + + /* When this function returns, _rl_last_c_pos is correct, and an absolute + cursor position in multibyte mode, but a buffer index when not in a + multibyte locale. */ + _rl_move_cursor_relative (od, old); + +#if defined (HANDLE_MULTIBYTE) + /* We need to indicate that the cursor position is correct in the presence of + invisible characters in the prompt string. Let's see if setting this when + we make sure we're at the end of the drawn prompt string works. */ + if (current_line == 0 && MB_CUR_MAX > 1 && rl_byte_oriented == 0 && + (_rl_last_c_pos > 0 || o_cpos > 0) && + _rl_last_c_pos == prompt_physical_chars) + cpos_adjusted = 1; +#endif + + /* if (len (new) > len (old)) + lendiff == difference in buffer (bytes) + col_lendiff == difference on screen (columns) + When not using multibyte characters, these are equal */ + lendiff = (nls - nfd) - (ols - ofd); + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + col_lendiff = _rl_col_width (new, nfd - new, nls - new, 1) - _rl_col_width (old, ofd - old, ols - old, 1); + else + col_lendiff = lendiff; + + /* If we are changing the number of invisible characters in a line, and + the spot of first difference is before the end of the invisible chars, + lendiff needs to be adjusted. */ + if (current_line == 0 && /* !_rl_horizontal_scroll_mode && */ + current_invis_chars != visible_wrap_offset) + { + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + { + lendiff += visible_wrap_offset - current_invis_chars; + col_lendiff += visible_wrap_offset - current_invis_chars; + } + else + { + lendiff += visible_wrap_offset - current_invis_chars; + col_lendiff = lendiff; + } + } + + /* We use temp as a count of the number of bytes from the first difference + to the end of the new line. col_temp is the corresponding number of + screen columns. A `dumb' update moves to the spot of first difference + and writes TEMP bytes. */ + /* Insert (diff (len (old), len (new)) ch. */ + temp = ne - nfd; + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + col_temp = _rl_col_width (new, nfd - new, ne - new, 1); + else + col_temp = temp; + + /* how many bytes from the new line buffer to write to the display */ + bytes_to_insert = nls - nfd; + + /* col_lendiff > 0 if we are adding characters to the line */ + if (col_lendiff > 0) /* XXX - was lendiff */ + { + /* Non-zero if we're increasing the number of lines. */ + int gl = current_line >= _rl_vis_botlin && inv_botlin > _rl_vis_botlin; + /* If col_lendiff is > 0, implying that the new string takes up more + screen real estate than the old, but lendiff is < 0, meaning that it + takes fewer bytes, we need to just output the characters starting + from the first difference. These will overwrite what is on the + display, so there's no reason to do a smart update. This can really + only happen in a multibyte environment. */ + if (lendiff < 0) + { + _rl_output_some_chars (nfd, temp); + _rl_last_c_pos += col_temp; /* XXX - was _rl_col_width (nfd, 0, temp, 1); */ + /* If nfd begins before any invisible characters in the prompt, + adjust _rl_last_c_pos to account for wrap_offset and set + cpos_adjusted to let the caller know. */ + if (current_line == 0 && displaying_prompt_first_line && wrap_offset && ((nfd - new) <= prompt_last_invisible)) + { + _rl_last_c_pos -= wrap_offset; + cpos_adjusted = 1; + } + return; + } + /* Sometimes it is cheaper to print the characters rather than + use the terminal's capabilities. If we're growing the number + of lines, make sure we actually cause the new line to wrap + around on auto-wrapping terminals. */ + else if (_rl_terminal_can_insert && ((2 * col_temp) >= col_lendiff || _rl_term_IC) && (!_rl_term_autowrap || !gl)) + { + /* If lendiff > prompt_visible_length and _rl_last_c_pos == 0 and + _rl_horizontal_scroll_mode == 1, inserting the characters with + _rl_term_IC or _rl_term_ic will screw up the screen because of the + invisible characters. We need to just draw them. */ + /* The same thing happens if we're trying to draw before the last + invisible character in the prompt string or we're increasing the + number of invisible characters in the line and we're not drawing + the entire prompt string. */ + if (*ols && ((_rl_horizontal_scroll_mode && + _rl_last_c_pos == 0 && + lendiff > prompt_visible_length && + current_invis_chars > 0) == 0) && + (((MB_CUR_MAX > 1 && rl_byte_oriented == 0) && + current_line == 0 && wrap_offset && + ((nfd - new) <= prompt_last_invisible) && + (col_lendiff < prompt_visible_length)) == 0) && + (visible_wrap_offset >= current_invis_chars)) + { + open_some_spaces (col_lendiff); + _rl_output_some_chars (nfd, bytes_to_insert); + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + _rl_last_c_pos += _rl_col_width (nfd, 0, bytes_to_insert, 1); + else + _rl_last_c_pos += bytes_to_insert; + } + else if ((MB_CUR_MAX == 1 || rl_byte_oriented != 0) && *ols == 0 && lendiff > 0) + { + /* At the end of a line the characters do not have to + be "inserted". They can just be placed on the screen. */ + _rl_output_some_chars (nfd, temp); + _rl_last_c_pos += col_temp; + return; + } + else /* just write from first difference to end of new line */ + { + _rl_output_some_chars (nfd, temp); + _rl_last_c_pos += col_temp; + /* If nfd begins before the last invisible character in the + prompt, adjust _rl_last_c_pos to account for wrap_offset + and set cpos_adjusted to let the caller know. */ + if ((MB_CUR_MAX > 1 && rl_byte_oriented == 0) && current_line == 0 && displaying_prompt_first_line && wrap_offset && ((nfd - new) <= prompt_last_invisible)) + { + _rl_last_c_pos -= wrap_offset; + cpos_adjusted = 1; + } + return; + } + + if (bytes_to_insert > lendiff) + { + /* If nfd begins before the last invisible character in the + prompt, adjust _rl_last_c_pos to account for wrap_offset + and set cpos_adjusted to let the caller know. */ + if ((MB_CUR_MAX > 1 && rl_byte_oriented == 0) && current_line == 0 && displaying_prompt_first_line && wrap_offset && ((nfd - new) <= prompt_last_invisible)) + { + _rl_last_c_pos -= wrap_offset; + cpos_adjusted = 1; + } + } + } + else + { + /* cannot insert chars, write to EOL */ + _rl_output_some_chars (nfd, temp); + _rl_last_c_pos += col_temp; + /* If we're in a multibyte locale and were before the last invisible + char in the current line (which implies we just output some invisible + characters) we need to adjust _rl_last_c_pos, since it represents + a physical character position. */ + if ((MB_CUR_MAX > 1 && rl_byte_oriented == 0) && + current_line == prompt_last_screen_line && wrap_offset && + displaying_prompt_first_line && + wrap_offset != prompt_invis_chars_first_line && + ((nfd-new) < (prompt_last_invisible-(current_line*_rl_screenwidth)))) + { + _rl_last_c_pos -= wrap_offset - prompt_invis_chars_first_line; + cpos_adjusted = 1; + } + } + } + else /* Delete characters from line. */ + { + /* If possible and inexpensive to use terminal deletion, then do so. */ + if (_rl_term_dc && (2 * col_temp) >= -col_lendiff) + { + /* If all we're doing is erasing the invisible characters in the + prompt string, don't bother. It screws up the assumptions + about what's on the screen. */ + if (_rl_horizontal_scroll_mode && _rl_last_c_pos == 0 && + displaying_prompt_first_line && + -lendiff == visible_wrap_offset) + col_lendiff = 0; + + /* If we have moved lmargin and we're shrinking the line, we've + already moved the cursor to the first character of the new line, + so deleting -col_lendiff characters will mess up the cursor + position calculation */ + if (_rl_horizontal_scroll_mode && displaying_prompt_first_line == 0 && + col_lendiff && _rl_last_c_pos < -col_lendiff) + col_lendiff = 0; + + if (col_lendiff) + delete_chars (-col_lendiff); /* delete (diff) characters */ + + /* Copy (new) chars to screen from first diff to last match, + overwriting what is there. */ + if (bytes_to_insert > 0) + { + /* If nfd begins at the prompt, or before the invisible + characters in the prompt, we need to adjust _rl_last_c_pos + in a multibyte locale to account for the wrap offset and + set cpos_adjusted accordingly. */ + _rl_output_some_chars (nfd, bytes_to_insert); + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + { + _rl_last_c_pos += _rl_col_width (nfd, 0, bytes_to_insert, 1); + if (current_line == 0 && wrap_offset && + displaying_prompt_first_line && + _rl_last_c_pos > wrap_offset && + ((nfd - new) <= prompt_last_invisible)) + { + _rl_last_c_pos -= wrap_offset; + cpos_adjusted = 1; + } + } + else + _rl_last_c_pos += bytes_to_insert; + + /* XXX - we only want to do this if we are at the end of the line + so we move there with _rl_move_cursor_relative */ + if (_rl_horizontal_scroll_mode && ((oe-old) > (ne-new))) + { + _rl_move_cursor_relative (ne-new, new); + goto clear_rest_of_line; + } + } + } + /* Otherwise, print over the existing material. */ + else + { + if (temp > 0) + { + /* If nfd begins at the prompt, or before the invisible + characters in the prompt, we need to adjust _rl_last_c_pos + in a multibyte locale to account for the wrap offset and + set cpos_adjusted accordingly. */ + _rl_output_some_chars (nfd, temp); + _rl_last_c_pos += col_temp; /* XXX */ + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + { + if (current_line == 0 && wrap_offset && + displaying_prompt_first_line && + _rl_last_c_pos > wrap_offset && + ((nfd - new) <= prompt_last_invisible)) + { + _rl_last_c_pos -= wrap_offset; + cpos_adjusted = 1; + } + } + } +clear_rest_of_line: + lendiff = (oe - old) - (ne - new); + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + col_lendiff = _rl_col_width (old, 0, oe - old, 1) - _rl_col_width (new, 0, ne - new, 1); + else + col_lendiff = lendiff; + + /* If we've already printed over the entire width of the screen, + including the old material, then col_lendiff doesn't matter and + space_to_eol will insert too many spaces. XXX - maybe we should + adjust col_lendiff based on the difference between _rl_last_c_pos + and _rl_screenwidth */ + if (col_lendiff && ((MB_CUR_MAX == 1 || rl_byte_oriented) || (_rl_last_c_pos < _rl_screenwidth))) + { + if (_rl_term_autowrap && current_line < inv_botlin) + space_to_eol (col_lendiff); + else + _rl_clear_to_eol (col_lendiff); + } + } + } +} + +/* Tell the update routines that we have moved onto a new (empty) line. */ +int +rl_on_new_line () +{ + if (visible_line) + visible_line[0] = '\0'; + + _rl_last_c_pos = _rl_last_v_pos = 0; + _rl_vis_botlin = last_lmargin = 0; + if (vis_lbreaks) + vis_lbreaks[0] = vis_lbreaks[1] = 0; + visible_wrap_offset = 0; + return 0; +} + +/* Tell the update routines that we have moved onto a new line with the + prompt already displayed. Code originally from the version of readline + distributed with CLISP. rl_expand_prompt must have already been called + (explicitly or implicitly). This still doesn't work exactly right. */ +int +rl_on_new_line_with_prompt () +{ + int prompt_size, i, l, real_screenwidth, newlines; + char *prompt_last_line, *lprompt; + + /* Initialize visible_line and invisible_line to ensure that they can hold + the already-displayed prompt. */ + prompt_size = strlen (rl_prompt) + 1; + init_line_structures (prompt_size); + + /* Make sure the line structures hold the already-displayed prompt for + redisplay. */ + lprompt = local_prompt ? local_prompt : rl_prompt; + strcpy (visible_line, lprompt); + strcpy (invisible_line, lprompt); + + /* If the prompt contains newlines, take the last tail. */ + prompt_last_line = strrchr (rl_prompt, '\n'); + if (!prompt_last_line) + prompt_last_line = rl_prompt; + + l = strlen (prompt_last_line); + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + _rl_last_c_pos = _rl_col_width (prompt_last_line, 0, l, 1); /* XXX */ + else + _rl_last_c_pos = l; + + /* Dissect prompt_last_line into screen lines. Note that here we have + to use the real screenwidth. Readline's notion of screenwidth might be + one less, see terminal.c. */ + real_screenwidth = _rl_screenwidth + (_rl_term_autowrap ? 0 : 1); + _rl_last_v_pos = l / real_screenwidth; + /* If the prompt length is a multiple of real_screenwidth, we don't know + whether the cursor is at the end of the last line, or already at the + beginning of the next line. Output a newline just to be safe. */ + if (l > 0 && (l % real_screenwidth) == 0) + _rl_output_some_chars ("\n", 1); + last_lmargin = 0; + + newlines = 0; i = 0; + while (i <= l) + { + _rl_vis_botlin = newlines; + vis_lbreaks[newlines++] = i; + i += real_screenwidth; + } + vis_lbreaks[newlines] = l; + visible_wrap_offset = 0; + + rl_display_prompt = rl_prompt; /* XXX - make sure it's set */ + + return 0; +} + +/* Actually update the display, period. */ +int +rl_forced_update_display () +{ + register char *temp; + + if (visible_line) + { + temp = visible_line; + while (*temp) + *temp++ = '\0'; + } + rl_on_new_line (); + forced_display++; + (*rl_redisplay_function) (); + return 0; +} + +/* Redraw only the last line of a multi-line prompt. */ +int +rl_redraw_prompt_last_line () +{ + char *t; + t = strrchr (rl_display_prompt, '\n'); + if (t) + redraw_prompt (++t); + else + rl_forced_update_display (); +} + +/* Move the cursor from _rl_last_c_pos to NEW, which are buffer indices. + (Well, when we don't have multibyte characters, _rl_last_c_pos is a + buffer index.) + DATA is the contents of the screen line of interest; i.e., where + the movement is being done. */ +void +_rl_move_cursor_relative (new, data) + int new; + const char *data; +{ + register int i; + int woff; /* number of invisible chars on current line */ + int cpos, dpos; /* current and desired cursor positions */ + int adjust; + + woff = WRAP_OFFSET (_rl_last_v_pos, wrap_offset); + cpos = _rl_last_c_pos; + + if (cpos == 0 && cpos == new) + return; + +#if defined (HANDLE_MULTIBYTE) + /* If we have multibyte characters, NEW is indexed by the buffer point in + a multibyte string, but _rl_last_c_pos is the display position. In + this case, NEW's display position is not obvious and must be + calculated. We need to account for invisible characters in this line, + as long as we are past them and they are counted by _rl_col_width. */ + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + { + adjust = 1; + /* Try to short-circuit common cases and eliminate a bunch of multibyte + character function calls. */ + /* 1. prompt string */ + if (new == local_prompt_len && memcmp (data, local_prompt, new) == 0) + { + dpos = prompt_physical_chars; + cpos_adjusted = 1; + adjust = 0; + } + /* 2. prompt_string + line contents */ + else if (new > local_prompt_len && local_prompt && memcmp (data, local_prompt, local_prompt_len) == 0) + { + dpos = prompt_physical_chars + _rl_col_width (data, local_prompt_len, new, 1); + cpos_adjusted = 1; + adjust = 0; + } + else + dpos = _rl_col_width (data, 0, new, 1); + + if (displaying_prompt_first_line == 0) + adjust = 0; + + /* Use NEW when comparing against the last invisible character in the + prompt string, since they're both buffer indices and DPOS is a + desired display position. */ + if (adjust && ((new > prompt_last_invisible) || /* XXX - don't use woff here */ + (prompt_physical_chars >= _rl_screenwidth && + _rl_last_v_pos == prompt_last_screen_line && + wrap_offset >= woff && dpos >= woff && + new > (prompt_last_invisible-(_rl_screenwidth*_rl_last_v_pos)-wrap_offset)))) + /* XXX last comparison might need to be >= */ + { + dpos -= woff; + /* Since this will be assigned to _rl_last_c_pos at the end (more + precisely, _rl_last_c_pos == dpos when this function returns), + let the caller know. */ + cpos_adjusted = 1; + } + } + else +#endif + dpos = new; + + /* If we don't have to do anything, then return. */ + if (cpos == dpos) + return; + + /* It may be faster to output a CR, and then move forwards instead + of moving backwards. */ + /* i == current physical cursor position. */ +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + i = _rl_last_c_pos; + else +#endif + i = _rl_last_c_pos - woff; + if (dpos == 0 || CR_FASTER (dpos, _rl_last_c_pos) || + (_rl_term_autowrap && i == _rl_screenwidth)) + { +#if defined (__MSDOS__) + putc ('\r', rl_outstream); +#else + tputs (_rl_term_cr, 1, _rl_output_character_function); +#endif /* !__MSDOS__ */ + cpos = _rl_last_c_pos = 0; + } + + if (cpos < dpos) + { + /* Move the cursor forward. We do it by printing the command + to move the cursor forward if there is one, else print that + portion of the output buffer again. Which is cheaper? */ + + /* The above comment is left here for posterity. It is faster + to print one character (non-control) than to print a control + sequence telling the terminal to move forward one character. + That kind of control is for people who don't know what the + data is underneath the cursor. */ + + /* However, we need a handle on where the current display position is + in the buffer for the immediately preceding comment to be true. + In multibyte locales, we don't currently have that info available. + Without it, we don't know where the data we have to display begins + in the buffer and we have to go back to the beginning of the screen + line. In this case, we can use the terminal sequence to move forward + if it's available. */ + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + { + if (_rl_term_forward_char) + { + for (i = cpos; i < dpos; i++) + tputs (_rl_term_forward_char, 1, _rl_output_character_function); + } + else + { + tputs (_rl_term_cr, 1, _rl_output_character_function); + for (i = 0; i < new; i++) + putc (data[i], rl_outstream); + } + } + else + for (i = cpos; i < new; i++) + putc (data[i], rl_outstream); + } + +#if defined (HANDLE_MULTIBYTE) + /* NEW points to the buffer point, but _rl_last_c_pos is the display point. + The byte length of the string is probably bigger than the column width + of the string, which means that if NEW == _rl_last_c_pos, then NEW's + display point is less than _rl_last_c_pos. */ +#endif + else if (cpos > dpos) + _rl_backspace (cpos - dpos); + + _rl_last_c_pos = dpos; +} + +/* PWP: move the cursor up or down. */ +void +_rl_move_vert (to) + int to; +{ + register int delta, i; + + if (_rl_last_v_pos == to || to > _rl_screenheight) + return; + + if ((delta = to - _rl_last_v_pos) > 0) + { + for (i = 0; i < delta; i++) + putc ('\n', rl_outstream); +#if defined (__MSDOS__) + putc ('\r', rl_outstream); +#else + tputs (_rl_term_cr, 1, _rl_output_character_function); +#endif + _rl_last_c_pos = 0; + } + else + { /* delta < 0 */ +#ifdef __DJGPP__ + int row, col; + + fflush (rl_outstream); + ScreenGetCursor (&row, &col); + ScreenSetCursor (row + delta, col); + i = -delta; +#else + if (_rl_term_up && *_rl_term_up) + for (i = 0; i < -delta; i++) + tputs (_rl_term_up, 1, _rl_output_character_function); +#endif /* !__DJGPP__ */ + } + + _rl_last_v_pos = to; /* Now TO is here */ +} + +/* Physically print C on rl_outstream. This is for functions which know + how to optimize the display. Return the number of characters output. */ +int +rl_show_char (c) + int c; +{ + int n = 1; + if (META_CHAR (c) && (_rl_output_meta_chars == 0)) + { + fprintf (rl_outstream, "M-"); + n += 2; + c = UNMETA (c); + } + +#if defined (DISPLAY_TABS) + if ((CTRL_CHAR (c) && c != '\t') || c == RUBOUT) +#else + if (CTRL_CHAR (c) || c == RUBOUT) +#endif /* !DISPLAY_TABS */ + { + fprintf (rl_outstream, "C-"); + n += 2; + c = CTRL_CHAR (c) ? UNCTRL (c) : '?'; + } + + putc (c, rl_outstream); + fflush (rl_outstream); + return n; +} + +int +rl_character_len (c, pos) + register int c, pos; +{ + unsigned char uc; + + uc = (unsigned char)c; + + if (META_CHAR (uc)) + return ((_rl_output_meta_chars == 0) ? 4 : 1); + + if (uc == '\t') + { +#if defined (DISPLAY_TABS) + return (((pos | 7) + 1) - pos); +#else + return (2); +#endif /* !DISPLAY_TABS */ + } + + if (CTRL_CHAR (c) || c == RUBOUT) + return (2); + + return ((ISPRINT (uc)) ? 1 : 2); +} +/* How to print things in the "echo-area". The prompt is treated as a + mini-modeline. */ +static int msg_saved_prompt = 0; + +#if defined (USE_VARARGS) +int +#if defined (PREFER_STDARG) +rl_message (const char *format, ...) +#else +rl_message (va_alist) + va_dcl +#endif +{ + va_list args; +#if defined (PREFER_VARARGS) + char *format; +#endif +#if defined (HAVE_VSNPRINTF) + int bneed; +#endif + +#if defined (PREFER_STDARG) + va_start (args, format); +#else + va_start (args); + format = va_arg (args, char *); +#endif + + if (msg_buf == 0) + msg_buf = xmalloc (msg_bufsiz = 128); + +#if defined (HAVE_VSNPRINTF) + bneed = vsnprintf (msg_buf, msg_bufsiz, format, args); + if (bneed >= msg_bufsiz - 1) + { + msg_bufsiz = bneed + 1; + msg_buf = xrealloc (msg_buf, msg_bufsiz); + va_end (args); + +#if defined (PREFER_STDARG) + va_start (args, format); +#else + va_start (args); + format = va_arg (args, char *); +#endif + vsnprintf (msg_buf, msg_bufsiz - 1, format, args); + } +#else + vsprintf (msg_buf, format, args); + msg_buf[msg_bufsiz - 1] = '\0'; /* overflow? */ +#endif + va_end (args); + + if (saved_local_prompt == 0) + { + rl_save_prompt (); + msg_saved_prompt = 1; + } + else if (local_prompt != saved_local_prompt) + { + FREE (local_prompt); + FREE (local_prompt_prefix); + local_prompt = (char *)NULL; + } + rl_display_prompt = msg_buf; + local_prompt = expand_prompt (msg_buf, 0, &prompt_visible_length, + &prompt_last_invisible, + &prompt_invis_chars_first_line, + &prompt_physical_chars); + local_prompt_prefix = (char *)NULL; + local_prompt_len = local_prompt ? strlen (local_prompt) : 0; + (*rl_redisplay_function) (); + + return 0; +} +#else /* !USE_VARARGS */ +int +rl_message (format, arg1, arg2) + char *format; +{ + if (msg_buf == 0) + msg_buf = xmalloc (msg_bufsiz = 128); + + sprintf (msg_buf, format, arg1, arg2); + msg_buf[msg_bufsiz - 1] = '\0'; /* overflow? */ + + rl_display_prompt = msg_buf; + if (saved_local_prompt == 0) + { + rl_save_prompt (); + msg_saved_prompt = 1; + } + else if (local_prompt != saved_local_prompt) + { + FREE (local_prompt); + FREE (local_prompt_prefix); + local_prompt = (char *)NULL; + } + local_prompt = expand_prompt (msg_buf, 0, &prompt_visible_length, + &prompt_last_invisible, + &prompt_invis_chars_first_line, + &prompt_physical_chars); + local_prompt_prefix = (char *)NULL; + local_prompt_len = local_prompt ? strlen (local_prompt) : 0; + (*rl_redisplay_function) (); + + return 0; +} +#endif /* !USE_VARARGS */ + +/* How to clear things from the "echo-area". */ +int +rl_clear_message () +{ + rl_display_prompt = rl_prompt; + if (msg_saved_prompt) + { + rl_restore_prompt (); + msg_saved_prompt = 0; + } + (*rl_redisplay_function) (); + return 0; +} + +int +rl_reset_line_state () +{ + rl_on_new_line (); + + rl_display_prompt = rl_prompt ? rl_prompt : ""; + forced_display = 1; + return 0; +} + +void +rl_save_prompt () +{ + saved_local_prompt = local_prompt; + saved_local_prefix = local_prompt_prefix; + saved_prefix_length = prompt_prefix_length; + saved_local_length = local_prompt_len; + saved_last_invisible = prompt_last_invisible; + saved_visible_length = prompt_visible_length; + saved_invis_chars_first_line = prompt_invis_chars_first_line; + saved_physical_chars = prompt_physical_chars; + + local_prompt = local_prompt_prefix = (char *)0; + local_prompt_len = 0; + prompt_last_invisible = prompt_visible_length = prompt_prefix_length = 0; + prompt_invis_chars_first_line = prompt_physical_chars = 0; +} + +void +rl_restore_prompt () +{ + FREE (local_prompt); + FREE (local_prompt_prefix); + + local_prompt = saved_local_prompt; + local_prompt_prefix = saved_local_prefix; + local_prompt_len = saved_local_length; + prompt_prefix_length = saved_prefix_length; + prompt_last_invisible = saved_last_invisible; + prompt_visible_length = saved_visible_length; + prompt_invis_chars_first_line = saved_invis_chars_first_line; + prompt_physical_chars = saved_physical_chars; + + /* can test saved_local_prompt to see if prompt info has been saved. */ + saved_local_prompt = saved_local_prefix = (char *)0; + saved_local_length = 0; + saved_last_invisible = saved_visible_length = saved_prefix_length = 0; + saved_invis_chars_first_line = saved_physical_chars = 0; +} + +char * +_rl_make_prompt_for_search (pchar) + int pchar; +{ + int len; + char *pmt, *p; + + rl_save_prompt (); + + /* We've saved the prompt, and can do anything with the various prompt + strings we need before they're restored. We want the unexpanded + portion of the prompt string after any final newline. */ + p = rl_prompt ? strrchr (rl_prompt, '\n') : 0; + if (p == 0) + { + len = (rl_prompt && *rl_prompt) ? strlen (rl_prompt) : 0; + pmt = (char *)xmalloc (len + 2); + if (len) + strcpy (pmt, rl_prompt); + pmt[len] = pchar; + pmt[len+1] = '\0'; + } + else + { + p++; + len = strlen (p); + pmt = (char *)xmalloc (len + 2); + if (len) + strcpy (pmt, p); + pmt[len] = pchar; + pmt[len+1] = '\0'; + } + + /* will be overwritten by expand_prompt, called from rl_message */ + prompt_physical_chars = saved_physical_chars + 1; + return pmt; +} + +/* Quick redisplay hack when erasing characters at the end of the line. */ +void +_rl_erase_at_end_of_line (l) + int l; +{ + register int i; + + _rl_backspace (l); + for (i = 0; i < l; i++) + putc (' ', rl_outstream); + _rl_backspace (l); + for (i = 0; i < l; i++) + visible_line[--_rl_last_c_pos] = '\0'; + rl_display_fixed++; +} + +/* Clear to the end of the line. COUNT is the minimum + number of character spaces to clear, */ +void +_rl_clear_to_eol (count) + int count; +{ +#ifndef __MSDOS__ + if (_rl_term_clreol) + tputs (_rl_term_clreol, 1, _rl_output_character_function); + else +#endif + if (count) + space_to_eol (count); +} + +/* Clear to the end of the line using spaces. COUNT is the minimum + number of character spaces to clear, */ +static void +space_to_eol (count) + int count; +{ + register int i; + + for (i = 0; i < count; i++) + putc (' ', rl_outstream); + + _rl_last_c_pos += count; +} + +void +_rl_clear_screen () +{ +#ifndef __DJGPP__ + if (_rl_term_clrpag) + tputs (_rl_term_clrpag, 1, _rl_output_character_function); + else + rl_crlf (); +#else + ScreenClear (); + ScreenSetCursor (0, 0); +#endif /* __DJGPP__ */ +} + +/* Insert COUNT characters from STRING to the output stream at column COL. */ +static void +insert_some_chars (string, count, col) + char *string; + int count, col; +{ + open_some_spaces (col); + _rl_output_some_chars (string, count); +} + +/* Insert COL spaces, keeping the cursor at the same position. We follow the + ncurses documentation and use either im/ei with explicit spaces, or IC/ic + by itself. We assume there will either be ei or we don't need to use it. */ +static void +open_some_spaces (col) + int col; +{ +#if !defined (__MSDOS__) && !defined (__MINGW32__) + char *buffer; + register int i; + + /* If IC is defined, then we do not have to "enter" insert mode. */ + if (_rl_term_IC) + { + buffer = tgoto (_rl_term_IC, 0, col); + tputs (buffer, 1, _rl_output_character_function); + } + else if (_rl_term_im && *_rl_term_im) + { + tputs (_rl_term_im, 1, _rl_output_character_function); + /* just output the desired number of spaces */ + for (i = col; i--; ) + _rl_output_character_function (' '); + /* If there is a string to turn off insert mode, use it now. */ + if (_rl_term_ei && *_rl_term_ei) + tputs (_rl_term_ei, 1, _rl_output_character_function); + /* and move back the right number of spaces */ + _rl_backspace (col); + } + else if (_rl_term_ic && *_rl_term_ic) + { + /* If there is a special command for inserting characters, then + use that first to open up the space. */ + for (i = col; i--; ) + tputs (_rl_term_ic, 1, _rl_output_character_function); + } +#endif /* !__MSDOS__ && !__MINGW32__ */ +} + +/* Delete COUNT characters from the display line. */ +static void +delete_chars (count) + int count; +{ + if (count > _rl_screenwidth) /* XXX */ + return; + +#if !defined (__MSDOS__) && !defined (__MINGW32__) + if (_rl_term_DC && *_rl_term_DC) + { + char *buffer; + buffer = tgoto (_rl_term_DC, count, count); + tputs (buffer, count, _rl_output_character_function); + } + else + { + if (_rl_term_dc && *_rl_term_dc) + while (count--) + tputs (_rl_term_dc, 1, _rl_output_character_function); + } +#endif /* !__MSDOS__ && !__MINGW32__ */ +} + +void +_rl_update_final () +{ + int full_lines; + + full_lines = 0; + /* If the cursor is the only thing on an otherwise-blank last line, + compensate so we don't print an extra CRLF. */ + if (_rl_vis_botlin && _rl_last_c_pos == 0 && + visible_line[vis_lbreaks[_rl_vis_botlin]] == 0) + { + _rl_vis_botlin--; + full_lines = 1; + } + _rl_move_vert (_rl_vis_botlin); + /* If we've wrapped lines, remove the final xterm line-wrap flag. */ + if (full_lines && _rl_term_autowrap && (VIS_LLEN(_rl_vis_botlin) == _rl_screenwidth)) + { + char *last_line; + + last_line = &visible_line[vis_lbreaks[_rl_vis_botlin]]; + cpos_buffer_position = -1; /* don't know where we are in buffer */ + _rl_move_cursor_relative (_rl_screenwidth - 1, last_line); /* XXX */ + _rl_clear_to_eol (0); + putc (last_line[_rl_screenwidth - 1], rl_outstream); + } + _rl_vis_botlin = 0; + rl_crlf (); + fflush (rl_outstream); + rl_display_fixed++; +} + +/* Move to the start of the current line. */ +static void +cr () +{ + if (_rl_term_cr) + { +#if defined (__MSDOS__) + putc ('\r', rl_outstream); +#else + tputs (_rl_term_cr, 1, _rl_output_character_function); +#endif + _rl_last_c_pos = 0; + } +} + +/* Redraw the last line of a multi-line prompt that may possibly contain + terminal escape sequences. Called with the cursor at column 0 of the + line to draw the prompt on. */ +static void +redraw_prompt (t) + char *t; +{ + char *oldp; + + oldp = rl_display_prompt; + rl_save_prompt (); + + rl_display_prompt = t; + local_prompt = expand_prompt (t, PMT_MULTILINE, + &prompt_visible_length, + &prompt_last_invisible, + &prompt_invis_chars_first_line, + &prompt_physical_chars); + local_prompt_prefix = (char *)NULL; + local_prompt_len = local_prompt ? strlen (local_prompt) : 0; + + rl_forced_update_display (); + + rl_display_prompt = oldp; + rl_restore_prompt(); +} + +/* Redisplay the current line after a SIGWINCH is received. */ +void +_rl_redisplay_after_sigwinch () +{ + char *t; + + /* Clear the last line (assuming that the screen size change will result in + either more or fewer characters on that line only) and put the cursor at + column 0. Make sure the right thing happens if we have wrapped to a new + screen line. */ + if (_rl_term_cr) + { + _rl_move_vert (_rl_vis_botlin); + +#if defined (__MSDOS__) + putc ('\r', rl_outstream); +#else + tputs (_rl_term_cr, 1, _rl_output_character_function); +#endif + _rl_last_c_pos = 0; +#if defined (__MSDOS__) + space_to_eol (_rl_screenwidth); + putc ('\r', rl_outstream); +#else + if (_rl_term_clreol) + tputs (_rl_term_clreol, 1, _rl_output_character_function); + else + { + space_to_eol (_rl_screenwidth); + tputs (_rl_term_cr, 1, _rl_output_character_function); + } +#endif + if (_rl_last_v_pos > 0) + _rl_move_vert (0); + } + else + rl_crlf (); + + /* Redraw only the last line of a multi-line prompt. */ + t = strrchr (rl_display_prompt, '\n'); + if (t) + redraw_prompt (++t); + else + rl_forced_update_display (); +} + +void +_rl_clean_up_for_exit () +{ + if (_rl_echoing_p) + { + if (_rl_vis_botlin > 0) /* minor optimization plus bug fix */ + _rl_move_vert (_rl_vis_botlin); + _rl_vis_botlin = 0; + fflush (rl_outstream); + rl_restart_output (1, 0); + } +} + +void +_rl_erase_entire_line () +{ + cr (); + _rl_clear_to_eol (0); + cr (); + fflush (rl_outstream); +} + +void +_rl_ttyflush () +{ + fflush (rl_outstream); +} + +/* return the `current display line' of the cursor -- the number of lines to + move up to get to the first screen line of the current readline line. */ +int +_rl_current_display_line () +{ + int ret, nleft; + + /* Find out whether or not there might be invisible characters in the + editing buffer. */ + if (rl_display_prompt == rl_prompt) + nleft = _rl_last_c_pos - _rl_screenwidth - rl_visible_prompt_length; + else + nleft = _rl_last_c_pos - _rl_screenwidth; + + if (nleft > 0) + ret = 1 + nleft / _rl_screenwidth; + else + ret = 0; + + return ret; +} + +#if defined (HANDLE_MULTIBYTE) +/* Calculate the number of screen columns occupied by STR from START to END. + In the case of multibyte characters with stateful encoding, we have to + scan from the beginning of the string to take the state into account. */ +static int +_rl_col_width (str, start, end, flags) + const char *str; + int start, end, flags; +{ + wchar_t wc; + mbstate_t ps; + int tmp, point, width, max; + + if (end <= start) + return 0; + if (MB_CUR_MAX == 1 || rl_byte_oriented) + /* this can happen in some cases where it's inconvenient to check */ + return (end - start); + + memset (&ps, 0, sizeof (mbstate_t)); + + point = 0; + max = end; + + /* Try to short-circuit common cases. The adjustment to remove wrap_offset + is done by the caller. */ + /* 1. prompt string */ + if (flags && start == 0 && end == local_prompt_len && memcmp (str, local_prompt, local_prompt_len) == 0) + return (prompt_physical_chars + wrap_offset); + /* 2. prompt string + line contents */ + else if (flags && start == 0 && local_prompt_len > 0 && end > local_prompt_len && local_prompt && memcmp (str, local_prompt, local_prompt_len) == 0) + { + tmp = prompt_physical_chars + wrap_offset; + /* XXX - try to call ourselves recursively with non-prompt portion */ + tmp += _rl_col_width (str, local_prompt_len, end, flags); + return (tmp); + } + + while (point < start) + { + tmp = mbrlen (str + point, max, &ps); + if (MB_INVALIDCH ((size_t)tmp)) + { + /* In this case, the bytes are invalid or too short to compose a + multibyte character, so we assume that the first byte represents + a single character. */ + point++; + max--; + + /* Clear the state of the byte sequence, because in this case the + effect of mbstate is undefined. */ + memset (&ps, 0, sizeof (mbstate_t)); + } + else if (MB_NULLWCH (tmp)) + break; /* Found '\0' */ + else + { + point += tmp; + max -= tmp; + } + } + + /* If START is not a byte that starts a character, then POINT will be + greater than START. In this case, assume that (POINT - START) gives + a byte count that is the number of columns of difference. */ + width = point - start; + + while (point < end) + { + tmp = mbrtowc (&wc, str + point, max, &ps); + if (MB_INVALIDCH ((size_t)tmp)) + { + /* In this case, the bytes are invalid or too short to compose a + multibyte character, so we assume that the first byte represents + a single character. */ + point++; + max--; + + /* and assume that the byte occupies a single column. */ + width++; + + /* Clear the state of the byte sequence, because in this case the + effect of mbstate is undefined. */ + memset (&ps, 0, sizeof (mbstate_t)); + } + else if (MB_NULLWCH (tmp)) + break; /* Found '\0' */ + else + { + point += tmp; + max -= tmp; + tmp = WCWIDTH(wc); + width += (tmp >= 0) ? tmp : 1; + } + } + + width += point - end; + + return width; +} +#endif /* HANDLE_MULTIBYTE */ diff --git a/lib/readline/doc/rluser.texi b/lib/readline/doc/rluser.texi index 1ac05922..b0da7791 100644 --- a/lib/readline/doc/rluser.texi +++ b/lib/readline/doc/rluser.texi @@ -506,7 +506,19 @@ key bindings is used. By default, Readline starts up in Emacs editing mode, where the keystrokes are most similar to Emacs. This variable can be set to either @samp{emacs} or @samp{vi}. +@item emacs-mode-string +@vindex emacs-mode-string +This string is displayed immediately before the last line of the primary +prompt when emacs editing mode is active. The value is expanded like a +key binding, so the standard set of meta- and control prefixes and +backslash escape sequences is available (@pxref{Key Bindings}). +Use the @samp{\1} and @samp{\2} escapes to begin and end sequences of +non-printing characters, which can be used to embed a terminal control +sequence into the mode string. +The default is @samp{@@}. + @item echo-control-characters +@vindex echo-control-characters When set to @samp{on}, on operating systems that indicate they support it, readline echoes a character corresponding to a signal generated from the keyboard. The default is @samp{on}. @@ -705,6 +717,30 @@ rather than @samp{Makefilefile}, assuming there is a single possible completion. The default value is @samp{off}. +@item vi-cmd-mode-string +@vindex vi-cmd-mode-string +This string is displayed immediately before the last line of the primary +prompt when vi editing mode is active and in command mode. +The value is expanded like a +key binding, so the standard set of meta- and control prefixes and +backslash escape sequences is available (@pxref{Key Bindings}). +Use the @samp{\1} and @samp{\2} escapes to begin and end sequences of +non-printing characters, which can be used to embed a terminal control +sequence into the mode string. +The default is @samp{(cmd)}. + +@item vi-ins-mode-string +@vindex vi-ins-mode-string +This string is displayed immediately before the last line of the primary +prompt when vi editing mode is active and in insertion mode. +The value is expanded like a +key binding, so the standard set of meta- and control prefixes and +backslash escape sequences is available (@pxref{Key Bindings}). +Use the @samp{\1} and @samp{\2} escapes to begin and end sequences of +non-printing characters, which can be used to embed a terminal control +sequence into the mode string. +The default is @samp{(ins)}. + @item visible-stats @vindex visible-stats If set to @samp{on}, a character denoting a file's type diff --git a/lib/readline/doc/rluser.texi~ b/lib/readline/doc/rluser.texi~ new file mode 100644 index 00000000..30ef01b8 --- /dev/null +++ b/lib/readline/doc/rluser.texi~ @@ -0,0 +1,2280 @@ +@comment %**start of header (This is for running Texinfo on a region.) +@setfilename rluser.info +@comment %**end of header (This is for running Texinfo on a region.) + +@ignore +This file documents the end user interface to the GNU command line +editing features. It is to be an appendix to manuals for programs which +use these features. There is a document entitled "readline.texinfo" +which contains both end-user and programmer documentation for the +GNU Readline Library. + +Copyright (C) 1988--2014 Free Software Foundation, Inc. + +Authored by Brian Fox and Chet Ramey. + +Permission is granted to process this file through Tex and print the +results, provided the printed document carries copying permission notice +identical to this one except for the removal of this paragraph (this +paragraph not being relevant to the printed manual). + +Permission is granted to make and distribute verbatim copies of this manual +provided the copyright notice and this permission notice are preserved on +all copies. + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided also that the +GNU Copyright statement is available to the distributee, and provided that +the entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions. +@end ignore + +@comment If you are including this manual as an appendix, then set the +@comment variable readline-appendix. + +@ifclear BashFeatures +@defcodeindex bt +@end ifclear + +@node Command Line Editing +@chapter Command Line Editing + +This chapter describes the basic features of the @sc{gnu} +command line editing interface. +@ifset BashFeatures +Command line editing is provided by the Readline library, which is +used by several different programs, including Bash. +Command line editing is enabled by default when using an interactive shell, +unless the @option{--noediting} option is supplied at shell invocation. +Line editing is also used when using the @option{-e} option to the +@code{read} builtin command (@pxref{Bash Builtins}). +By default, the line editing commands are similar to those of Emacs. +A vi-style line editing interface is also available. +Line editing can be enabled at any time using the @option{-o emacs} or +@option{-o vi} options to the @code{set} builtin command +(@pxref{The Set Builtin}), or disabled using the @option{+o emacs} or +@option{+o vi} options to @code{set}. +@end ifset + +@menu +* Introduction and Notation:: Notation used in this text. +* Readline Interaction:: The minimum set of commands for editing a line. +* Readline Init File:: Customizing Readline from a user's view. +* Bindable Readline Commands:: A description of most of the Readline commands + available for binding +* Readline vi Mode:: A short description of how to make Readline + behave like the vi editor. +@ifset BashFeatures +* Programmable Completion:: How to specify the possible completions for + a specific command. +* Programmable Completion Builtins:: Builtin commands to specify how to + complete arguments for a particular command. +* A Programmable Completion Example:: An example shell function for + generating possible completions. +@end ifset +@end menu + +@node Introduction and Notation +@section Introduction to Line Editing + +The following paragraphs describe the notation used to represent +keystrokes. + +The text @kbd{C-k} is read as `Control-K' and describes the character +produced when the @key{k} key is pressed while the Control key +is depressed. + +The text @kbd{M-k} is read as `Meta-K' and describes the character +produced when the Meta key (if you have one) is depressed, and the @key{k} +key is pressed. +The Meta key is labeled @key{ALT} on many keyboards. +On keyboards with two keys labeled @key{ALT} (usually to either side of +the space bar), the @key{ALT} on the left side is generally set to +work as a Meta key. +The @key{ALT} key on the right may also be configured to work as a +Meta key or may be configured as some other modifier, such as a +Compose key for typing accented characters. + +If you do not have a Meta or @key{ALT} key, or another key working as +a Meta key, the identical keystroke can be generated by typing @key{ESC} +@emph{first}, and then typing @key{k}. +Either process is known as @dfn{metafying} the @key{k} key. + +The text @kbd{M-C-k} is read as `Meta-Control-k' and describes the +character produced by @dfn{metafying} @kbd{C-k}. + +In addition, several keys have their own names. Specifically, +@key{DEL}, @key{ESC}, @key{LFD}, @key{SPC}, @key{RET}, and @key{TAB} all +stand for themselves when seen in this text, or in an init file +(@pxref{Readline Init File}). +If your keyboard lacks a @key{LFD} key, typing @key{C-j} will +produce the desired character. +The @key{RET} key may be labeled @key{Return} or @key{Enter} on +some keyboards. + +@node Readline Interaction +@section Readline Interaction +@cindex interaction, readline + +Often during an interactive session you type in a long line of text, +only to notice that the first word on the line is misspelled. The +Readline library gives you a set of commands for manipulating the text +as you type it in, allowing you to just fix your typo, and not forcing +you to retype the majority of the line. Using these editing commands, +you move the cursor to the place that needs correction, and delete or +insert the text of the corrections. Then, when you are satisfied with +the line, you simply press @key{RET}. You do not have to be at the +end of the line to press @key{RET}; the entire line is accepted +regardless of the location of the cursor within the line. + +@menu +* Readline Bare Essentials:: The least you need to know about Readline. +* Readline Movement Commands:: Moving about the input line. +* Readline Killing Commands:: How to delete text, and how to get it back! +* Readline Arguments:: Giving numeric arguments to commands. +* Searching:: Searching through previous lines. +@end menu + +@node Readline Bare Essentials +@subsection Readline Bare Essentials +@cindex notation, readline +@cindex command editing +@cindex editing command lines + +In order to enter characters into the line, simply type them. The typed +character appears where the cursor was, and then the cursor moves one +space to the right. If you mistype a character, you can use your +erase character to back up and delete the mistyped character. + +Sometimes you may mistype a character, and +not notice the error until you have typed several other characters. In +that case, you can type @kbd{C-b} to move the cursor to the left, and then +correct your mistake. Afterwards, you can move the cursor to the right +with @kbd{C-f}. + +When you add text in the middle of a line, you will notice that characters +to the right of the cursor are `pushed over' to make room for the text +that you have inserted. Likewise, when you delete text behind the cursor, +characters to the right of the cursor are `pulled back' to fill in the +blank space created by the removal of the text. A list of the bare +essentials for editing the text of an input line follows. + +@table @asis +@item @kbd{C-b} +Move back one character. +@item @kbd{C-f} +Move forward one character. +@item @key{DEL} or @key{Backspace} +Delete the character to the left of the cursor. +@item @kbd{C-d} +Delete the character underneath the cursor. +@item @w{Printing characters} +Insert the character into the line at the cursor. +@item @kbd{C-_} or @kbd{C-x C-u} +Undo the last editing command. You can undo all the way back to an +empty line. +@end table + +@noindent +(Depending on your configuration, the @key{Backspace} key be set to +delete the character to the left of the cursor and the @key{DEL} key set +to delete the character underneath the cursor, like @kbd{C-d}, rather +than the character to the left of the cursor.) + +@node Readline Movement Commands +@subsection Readline Movement Commands + + +The above table describes the most basic keystrokes that you need +in order to do editing of the input line. For your convenience, many +other commands have been added in addition to @kbd{C-b}, @kbd{C-f}, +@kbd{C-d}, and @key{DEL}. Here are some commands for moving more rapidly +about the line. + +@table @kbd +@item C-a +Move to the start of the line. +@item C-e +Move to the end of the line. +@item M-f +Move forward a word, where a word is composed of letters and digits. +@item M-b +Move backward a word. +@item C-l +Clear the screen, reprinting the current line at the top. +@end table + +Notice how @kbd{C-f} moves forward a character, while @kbd{M-f} moves +forward a word. It is a loose convention that control keystrokes +operate on characters while meta keystrokes operate on words. + +@node Readline Killing Commands +@subsection Readline Killing Commands + +@cindex killing text +@cindex yanking text + +@dfn{Killing} text means to delete the text from the line, but to save +it away for later use, usually by @dfn{yanking} (re-inserting) +it back into the line. +(`Cut' and `paste' are more recent jargon for `kill' and `yank'.) + +If the description for a command says that it `kills' text, then you can +be sure that you can get the text back in a different (or the same) +place later. + +When you use a kill command, the text is saved in a @dfn{kill-ring}. +Any number of consecutive kills save all of the killed text together, so +that when you yank it back, you get it all. The kill +ring is not line specific; the text that you killed on a previously +typed line is available to be yanked back later, when you are typing +another line. +@cindex kill ring + +Here is the list of commands for killing text. + +@table @kbd +@item C-k +Kill the text from the current cursor position to the end of the line. + +@item M-d +Kill from the cursor to the end of the current word, or, if between +words, to the end of the next word. +Word boundaries are the same as those used by @kbd{M-f}. + +@item M-@key{DEL} +Kill from the cursor the start of the current word, or, if between +words, to the start of the previous word. +Word boundaries are the same as those used by @kbd{M-b}. + +@item C-w +Kill from the cursor to the previous whitespace. This is different than +@kbd{M-@key{DEL}} because the word boundaries differ. + +@end table + +Here is how to @dfn{yank} the text back into the line. Yanking +means to copy the most-recently-killed text from the kill buffer. + +@table @kbd +@item C-y +Yank the most recently killed text back into the buffer at the cursor. + +@item M-y +Rotate the kill-ring, and yank the new top. You can only do this if +the prior command is @kbd{C-y} or @kbd{M-y}. +@end table + +@node Readline Arguments +@subsection Readline Arguments + +You can pass numeric arguments to Readline commands. Sometimes the +argument acts as a repeat count, other times it is the @i{sign} of the +argument that is significant. If you pass a negative argument to a +command which normally acts in a forward direction, that command will +act in a backward direction. For example, to kill text back to the +start of the line, you might type @samp{M-- C-k}. + +The general way to pass numeric arguments to a command is to type meta +digits before the command. If the first `digit' typed is a minus +sign (@samp{-}), then the sign of the argument will be negative. Once +you have typed one meta digit to get the argument started, you can type +the remainder of the digits, and then the command. For example, to give +the @kbd{C-d} command an argument of 10, you could type @samp{M-1 0 C-d}, +which will delete the next ten characters on the input line. + +@node Searching +@subsection Searching for Commands in the History + +Readline provides commands for searching through the command history +@ifset BashFeatures +(@pxref{Bash History Facilities}) +@end ifset +for lines containing a specified string. +There are two search modes: @dfn{incremental} and @dfn{non-incremental}. + +Incremental searches begin before the user has finished typing the +search string. +As each character of the search string is typed, Readline displays +the next entry from the history matching the string typed so far. +An incremental search requires only as many characters as needed to +find the desired history entry. +To search backward in the history for a particular string, type +@kbd{C-r}. Typing @kbd{C-s} searches forward through the history. +The characters present in the value of the @code{isearch-terminators} variable +are used to terminate an incremental search. +If that variable has not been assigned a value, the @key{ESC} and +@kbd{C-J} characters will terminate an incremental search. +@kbd{C-g} will abort an incremental search and restore the original line. +When the search is terminated, the history entry containing the +search string becomes the current line. + +To find other matching entries in the history list, type @kbd{C-r} or +@kbd{C-s} as appropriate. +This will search backward or forward in the history for the next +entry matching the search string typed so far. +Any other key sequence bound to a Readline command will terminate +the search and execute that command. +For instance, a @key{RET} will terminate the search and accept +the line, thereby executing the command from the history list. +A movement command will terminate the search, make the last line found +the current line, and begin editing. + +Readline remembers the last incremental search string. If two +@kbd{C-r}s are typed without any intervening characters defining a new +search string, any remembered search string is used. + +Non-incremental searches read the entire search string before starting +to search for matching history lines. The search string may be +typed by the user or be part of the contents of the current line. + +@node Readline Init File +@section Readline Init File +@cindex initialization file, readline + +Although the Readline library comes with a set of Emacs-like +keybindings installed by default, it is possible to use a different set +of keybindings. +Any user can customize programs that use Readline by putting +commands in an @dfn{inputrc} file, conventionally in his home directory. +The name of this +@ifset BashFeatures +file is taken from the value of the shell variable @env{INPUTRC}. If +@end ifset +@ifclear BashFeatures +file is taken from the value of the environment variable @env{INPUTRC}. If +@end ifclear +that variable is unset, the default is @file{~/.inputrc}. If that +file does not exist or cannot be read, the ultimate default is +@file{/etc/inputrc}. + +When a program which uses the Readline library starts up, the +init file is read, and the key bindings are set. + +In addition, the @code{C-x C-r} command re-reads this init file, thus +incorporating any changes that you might have made to it. + +@menu +* Readline Init File Syntax:: Syntax for the commands in the inputrc file. + +* Conditional Init Constructs:: Conditional key bindings in the inputrc file. + +* Sample Init File:: An example inputrc file. +@end menu + +@node Readline Init File Syntax +@subsection Readline Init File Syntax + +There are only a few basic constructs allowed in the +Readline init file. Blank lines are ignored. +Lines beginning with a @samp{#} are comments. +Lines beginning with a @samp{$} indicate conditional +constructs (@pxref{Conditional Init Constructs}). Other lines +denote variable settings and key bindings. + +@table @asis +@item Variable Settings +You can modify the run-time behavior of Readline by +altering the values of variables in Readline +using the @code{set} command within the init file. +The syntax is simple: + +@example +set @var{variable} @var{value} +@end example + +@noindent +Here, for example, is how to +change from the default Emacs-like key binding to use +@code{vi} line editing commands: + +@example +set editing-mode vi +@end example + +Variable names and values, where appropriate, are recognized without regard +to case. Unrecognized variable names are ignored. + +Boolean variables (those that can be set to on or off) are set to on if +the value is null or empty, @var{on} (case-insensitive), or 1. Any other +value results in the variable being set to off. + +@ifset BashFeatures +The @w{@code{bind -V}} command lists the current Readline variable names +and values. @xref{Bash Builtins}. +@end ifset + +A great deal of run-time behavior is changeable with the following +variables. + +@cindex variables, readline +@table @code + +@item bell-style +@vindex bell-style +Controls what happens when Readline wants to ring the terminal bell. +If set to @samp{none}, Readline never rings the bell. If set to +@samp{visible}, Readline uses a visible bell if one is available. +If set to @samp{audible} (the default), Readline attempts to ring +the terminal's bell. + +@item bind-tty-special-chars +@vindex bind-tty-special-chars +If set to @samp{on} (the default), Readline attempts to bind the control +characters treated specially by the kernel's terminal driver to their +Readline equivalents. + +@item blink-matching-paren +@vindex blink-matching-paren +If set to @samp{on}, Readline attempts to briefly move the cursor to an +opening parenthesis when a closing parenthsis is inserted. The default +is @samp{off}. + +@item colored-stats +@vindex colored-stats +If set to @samp{on}, Readline displays possible completions using different +colors to indicate their file type. +The color definitions are taken from the value of the @env{LS_COLORS} +environment variable. +The default is @samp{off}. + +@item comment-begin +@vindex comment-begin +The string to insert at the beginning of the line when the +@code{insert-comment} command is executed. The default value +is @code{"#"}. + +@item completion-display-width +@vindex completion-display-width +The number of screen columns used to display possible matches +when performing completion. +The value is ignored if it is less than 0 or greater than the terminal +screen width. +A value of 0 will cause matches to be displayed one per line. +The default value is -1. + +@item completion-ignore-case +@vindex completion-ignore-case +If set to @samp{on}, Readline performs filename matching and completion +in a case-insensitive fashion. +The default value is @samp{off}. + +@item completion-map-case +@vindex completion-map-case +If set to @samp{on}, and @var{completion-ignore-case} is enabled, Readline +treats hyphens (@samp{-}) and underscores (@samp{_}) as equivalent when +performing case-insensitive filename matching and completion. + +@item completion-prefix-display-length +@vindex completion-prefix-display-length +The length in characters of the common prefix of a list of possible +completions that is displayed without modification. When set to a +value greater than zero, common prefixes longer than this value are +replaced with an ellipsis when displaying possible completions. + +@item completion-query-items +@vindex completion-query-items +The number of possible completions that determines when the user is +asked whether the list of possibilities should be displayed. +If the number of possible completions is greater than this value, +Readline will ask the user whether or not he wishes to view +them; otherwise, they are simply listed. +This variable must be set to an integer value greater than or equal to 0. +A negative value means Readline should never ask. +The default limit is @code{100}. + +@item convert-meta +@vindex convert-meta +If set to @samp{on}, Readline will convert characters with the +eighth bit set to an @sc{ascii} key sequence by stripping the eighth +bit and prefixing an @key{ESC} character, converting them to a +meta-prefixed key sequence. The default value is @samp{on}. + +@item disable-completion +@vindex disable-completion +If set to @samp{On}, Readline will inhibit word completion. +Completion characters will be inserted into the line as if they had +been mapped to @code{self-insert}. The default is @samp{off}. + +@item editing-mode +@vindex editing-mode +The @code{editing-mode} variable controls which default set of +key bindings is used. By default, Readline starts up in Emacs editing +mode, where the keystrokes are most similar to Emacs. This variable can be +set to either @samp{emacs} or @samp{vi}. + +@item emacs-mode-string +@vindex emacs-mode-string +This string is displayed immediately before the last line of the primary +prompt when emacs editing mode is active. The value is expanded like a +key binding, so the standard set of meta- and control prefixes and +backslash escape sequences is available (@pxref{Key Bindings}). The +default is @samp{@@}. + +@item echo-control-characters +@vindex echo-control-characters +When set to @samp{on}, on operating systems that indicate they support it, +readline echoes a character corresponding to a signal generated from the +keyboard. The default is @samp{on}. + +@item enable-bracketed-paste +@vindex enable-bracketed-paste +When set to @samp{On}, Readline will configure the terminal in a way +that will enable it to insert each paste into the editing buffer as a +single string of characters, instead of treating each character as if +it had been read from the keyboard. This can prevent pasted characters +from being interpreted as editing commands. The default is @samp{off}. + +@item enable-keypad +@vindex enable-keypad +When set to @samp{on}, Readline will try to enable the application +keypad when it is called. Some systems need this to enable the +arrow keys. The default is @samp{off}. + +@item enable-meta-key +When set to @samp{on}, Readline will try to enable any meta modifier +key the terminal claims to support when it is called. On many terminals, +the meta key is used to send eight-bit characters. +The default is @samp{on}. + +@item expand-tilde +@vindex expand-tilde +If set to @samp{on}, tilde expansion is performed when Readline +attempts word completion. The default is @samp{off}. + +@item history-preserve-point +@vindex history-preserve-point +If set to @samp{on}, the history code attempts to place the point (the +current cursor position) at the +same location on each history line retrieved with @code{previous-history} +or @code{next-history}. The default is @samp{off}. + +@item history-size +@vindex history-size +Set the maximum number of history entries saved in the history list. +If set to zero, any existing history entries are deleted and no new entries +are saved. +If set to a value less than zero, the number of history entries is not +limited. +By default, the number of history entries is not limited. + +@item horizontal-scroll-mode +@vindex horizontal-scroll-mode +This variable can be set to either @samp{on} or @samp{off}. Setting it +to @samp{on} means that the text of the lines being edited will scroll +horizontally on a single screen line when they are longer than the width +of the screen, instead of wrapping onto a new screen line. By default, +this variable is set to @samp{off}. + +@item input-meta +@vindex input-meta +@vindex meta-flag +If set to @samp{on}, Readline will enable eight-bit input (it +will not clear the eighth bit in the characters it reads), +regardless of what the terminal claims it can support. The +default value is @samp{off}. The name @code{meta-flag} is a +synonym for this variable. + +@item isearch-terminators +@vindex isearch-terminators +The string of characters that should terminate an incremental search without +subsequently executing the character as a command (@pxref{Searching}). +If this variable has not been given a value, the characters @key{ESC} and +@kbd{C-J} will terminate an incremental search. + +@item keymap +@vindex keymap +Sets Readline's idea of the current keymap for key binding commands. +Acceptable @code{keymap} names are +@code{emacs}, +@code{emacs-standard}, +@code{emacs-meta}, +@code{emacs-ctlx}, +@code{vi}, +@code{vi-move}, +@code{vi-command}, and +@code{vi-insert}. +@code{vi} is equivalent to @code{vi-command}; @code{emacs} is +equivalent to @code{emacs-standard}. The default value is @code{emacs}. +The value of the @code{editing-mode} variable also affects the +default keymap. + +@item keyseq-timeout +Specifies the duration Readline will wait for a character when reading an +ambiguous key sequence (one that can form a complete key sequence using +the input read so far, or can take additional input to complete a longer +key sequence). +If no input is received within the timeout, Readline will use the shorter +but complete key sequence. +Readline uses this value to determine whether or not input is +available on the current input source (@code{rl_instream} by default). +The value is specified in milliseconds, so a value of 1000 means that +Readline will wait one second for additional input. +If this variable is set to a value less than or equal to zero, or to a +non-numeric value, Readline will wait until another key is pressed to +decide which key sequence to complete. +The default value is @code{500}. + +@item mark-directories +If set to @samp{on}, completed directory names have a slash +appended. The default is @samp{on}. + +@item mark-modified-lines +@vindex mark-modified-lines +This variable, when set to @samp{on}, causes Readline to display an +asterisk (@samp{*}) at the start of history lines which have been modified. +This variable is @samp{off} by default. + +@item mark-symlinked-directories +@vindex mark-symlinked-directories +If set to @samp{on}, completed names which are symbolic links +to directories have a slash appended (subject to the value of +@code{mark-directories}). +The default is @samp{off}. + +@item match-hidden-files +@vindex match-hidden-files +This variable, when set to @samp{on}, causes Readline to match files whose +names begin with a @samp{.} (hidden files) when performing filename +completion. +If set to @samp{off}, the leading @samp{.} must be +supplied by the user in the filename to be completed. +This variable is @samp{on} by default. + +@item menu-complete-display-prefix +@vindex menu-complete-display-prefix +If set to @samp{on}, menu completion displays the common prefix of the +list of possible completions (which may be empty) before cycling through +the list. The default is @samp{off}. + +@item output-meta +@vindex output-meta +If set to @samp{on}, Readline will display characters with the +eighth bit set directly rather than as a meta-prefixed escape +sequence. The default is @samp{off}. + +@item page-completions +@vindex page-completions +If set to @samp{on}, Readline uses an internal @code{more}-like pager +to display a screenful of possible completions at a time. +This variable is @samp{on} by default. + +@item print-completions-horizontally +If set to @samp{on}, Readline will display completions with matches +sorted horizontally in alphabetical order, rather than down the screen. +The default is @samp{off}. + +@item revert-all-at-newline +@vindex revert-all-at-newline +If set to @samp{on}, Readline will undo all changes to history lines +before returning when @code{accept-line} is executed. By default, +history lines may be modified and retain individual undo lists across +calls to @code{readline}. The default is @samp{off}. + +@item show-all-if-ambiguous +@vindex show-all-if-ambiguous +This alters the default behavior of the completion functions. If +set to @samp{on}, +words which have more than one possible completion cause the +matches to be listed immediately instead of ringing the bell. +The default value is @samp{off}. + +@item show-all-if-unmodified +@vindex show-all-if-unmodified +This alters the default behavior of the completion functions in +a fashion similar to @var{show-all-if-ambiguous}. +If set to @samp{on}, +words which have more than one possible completion without any +possible partial completion (the possible completions don't share +a common prefix) cause the matches to be listed immediately instead +of ringing the bell. +The default value is @samp{off}. + +@item show-mode-in-prompt +@vindex show-mode-in-prompt +If set to @samp{on}, add a character to the beginning of the prompt +indicating the editing mode: emacs (@samp{@@}), vi command (@samp{:}), +or vi insertion (@samp{+}). +The default value is @samp{off}. + +@item skip-completed-text +@vindex skip-completed-text +If set to @samp{on}, this alters the default completion behavior when +inserting a single match into the line. It's only active when +performing completion in the middle of a word. If enabled, readline +does not insert characters from the completion that match characters +after point in the word being completed, so portions of the word +following the cursor are not duplicated. +For instance, if this is enabled, attempting completion when the cursor +is after the @samp{e} in @samp{Makefile} will result in @samp{Makefile} +rather than @samp{Makefilefile}, assuming there is a single possible +completion. +The default value is @samp{off}. + +@item vi-cmd-mode-string +@vindex vi-cmd-mode-string +This string is displayed immediately before the last line of the primary +prompt when vi editing mode is active and in command mode. +The value is expanded like a +key binding, so the standard set of meta- and control prefixes and +backslash escape sequences is available (@pxref{Key Bindings}). The +default is @samp{(cmd)}. + +@item vi-ins-mode-string +@vindex vi-ins-mode-string +This string is displayed immediately before the last line of the primary +prompt when vi editing mode is active and in insertion mode. +The value is expanded like a +key binding, so the standard set of meta- and control prefixes and +backslash escape sequences is available (@pxref{Key Bindings}). The +default is @samp{(ins)}. + +@item visible-stats +@vindex visible-stats +If set to @samp{on}, a character denoting a file's type +is appended to the filename when listing possible +completions. The default is @samp{off}. + +@end table + +@item Key Bindings +The syntax for controlling key bindings in the init file is +simple. First you need to find the name of the command that you +want to change. The following sections contain tables of the command +name, the default keybinding, if any, and a short description of what +the command does. + +Once you know the name of the command, simply place on a line +in the init file the name of the key +you wish to bind the command to, a colon, and then the name of the +command. +There can be no space between the key name and the colon -- that will be +interpreted as part of the key name. +The name of the key can be expressed in different ways, depending on +what you find most comfortable. + +In addition to command names, readline allows keys to be bound +to a string that is inserted when the key is pressed (a @var{macro}). + +@ifset BashFeatures +The @w{@code{bind -p}} command displays Readline function names and +bindings in a format that can put directly into an initialization file. +@xref{Bash Builtins}. +@end ifset + +@table @asis +@item @w{@var{keyname}: @var{function-name} or @var{macro}} +@var{keyname} is the name of a key spelled out in English. For example: +@example +Control-u: universal-argument +Meta-Rubout: backward-kill-word +Control-o: "> output" +@end example + +In the above example, @kbd{C-u} is bound to the function +@code{universal-argument}, +@kbd{M-DEL} is bound to the function @code{backward-kill-word}, and +@kbd{C-o} is bound to run the macro +expressed on the right hand side (that is, to insert the text +@samp{> output} into the line). + +A number of symbolic character names are recognized while +processing this key binding syntax: +@var{DEL}, +@var{ESC}, +@var{ESCAPE}, +@var{LFD}, +@var{NEWLINE}, +@var{RET}, +@var{RETURN}, +@var{RUBOUT}, +@var{SPACE}, +@var{SPC}, +and +@var{TAB}. + +@item @w{"@var{keyseq}": @var{function-name} or @var{macro}} +@var{keyseq} differs from @var{keyname} above in that strings +denoting an entire key sequence can be specified, by placing +the key sequence in double quotes. Some @sc{gnu} Emacs style key +escapes can be used, as in the following example, but the +special character names are not recognized. + +@example +"\C-u": universal-argument +"\C-x\C-r": re-read-init-file +"\e[11~": "Function Key 1" +@end example + +In the above example, @kbd{C-u} is again bound to the function +@code{universal-argument} (just as it was in the first example), +@samp{@kbd{C-x} @kbd{C-r}} is bound to the function @code{re-read-init-file}, +and @samp{@key{ESC} @key{[} @key{1} @key{1} @key{~}} is bound to insert +the text @samp{Function Key 1}. + +@end table + +The following @sc{gnu} Emacs style escape sequences are available when +specifying key sequences: + +@table @code +@item @kbd{\C-} +control prefix +@item @kbd{\M-} +meta prefix +@item @kbd{\e} +an escape character +@item @kbd{\\} +backslash +@item @kbd{\"} +@key{"}, a double quotation mark +@item @kbd{\'} +@key{'}, a single quote or apostrophe +@end table + +In addition to the @sc{gnu} Emacs style escape sequences, a second +set of backslash escapes is available: + +@table @code +@item \a +alert (bell) +@item \b +backspace +@item \d +delete +@item \f +form feed +@item \n +newline +@item \r +carriage return +@item \t +horizontal tab +@item \v +vertical tab +@item \@var{nnn} +the eight-bit character whose value is the octal value @var{nnn} +(one to three digits) +@item \x@var{HH} +the eight-bit character whose value is the hexadecimal value @var{HH} +(one or two hex digits) +@end table + +When entering the text of a macro, single or double quotes must +be used to indicate a macro definition. +Unquoted text is assumed to be a function name. +In the macro body, the backslash escapes described above are expanded. +Backslash will quote any other character in the macro text, +including @samp{"} and @samp{'}. +For example, the following binding will make @samp{@kbd{C-x} \} +insert a single @samp{\} into the line: +@example +"\C-x\\": "\\" +@end example + +@end table + +@node Conditional Init Constructs +@subsection Conditional Init Constructs + +Readline implements a facility similar in spirit to the conditional +compilation features of the C preprocessor which allows key +bindings and variable settings to be performed as the result +of tests. There are four parser directives used. + +@table @code +@item $if +The @code{$if} construct allows bindings to be made based on the +editing mode, the terminal being used, or the application using +Readline. The text of the test extends to the end of the line; +no characters are required to isolate it. + +@table @code +@item mode +The @code{mode=} form of the @code{$if} directive is used to test +whether Readline is in @code{emacs} or @code{vi} mode. +This may be used in conjunction +with the @samp{set keymap} command, for instance, to set bindings in +the @code{emacs-standard} and @code{emacs-ctlx} keymaps only if +Readline is starting out in @code{emacs} mode. + +@item term +The @code{term=} form may be used to include terminal-specific +key bindings, perhaps to bind the key sequences output by the +terminal's function keys. The word on the right side of the +@samp{=} is tested against both the full name of the terminal and +the portion of the terminal name before the first @samp{-}. This +allows @code{sun} to match both @code{sun} and @code{sun-cmd}, +for instance. + +@item application +The @var{application} construct is used to include +application-specific settings. Each program using the Readline +library sets the @var{application name}, and you can test for +a particular value. +This could be used to bind key sequences to functions useful for +a specific program. For instance, the following command adds a +key sequence that quotes the current or previous word in Bash: +@example +$if Bash +# Quote the current or previous word +"\C-xq": "\eb\"\ef\"" +$endif +@end example +@end table + +@item $endif +This command, as seen in the previous example, terminates an +@code{$if} command. + +@item $else +Commands in this branch of the @code{$if} directive are executed if +the test fails. + +@item $include +This directive takes a single filename as an argument and reads commands +and bindings from that file. +For example, the following directive reads from @file{/etc/inputrc}: +@example +$include /etc/inputrc +@end example +@end table + +@node Sample Init File +@subsection Sample Init File + +Here is an example of an @var{inputrc} file. This illustrates key +binding, variable assignment, and conditional syntax. + +@example +@page +# This file controls the behaviour of line input editing for +# programs that use the GNU Readline library. Existing +# programs include FTP, Bash, and GDB. +# +# You can re-read the inputrc file with C-x C-r. +# Lines beginning with '#' are comments. +# +# First, include any system-wide bindings and variable +# assignments from /etc/Inputrc +$include /etc/Inputrc + +# +# Set various bindings for emacs mode. + +set editing-mode emacs + +$if mode=emacs + +Meta-Control-h: backward-kill-word Text after the function name is ignored + +# +# Arrow keys in keypad mode +# +#"\M-OD": backward-char +#"\M-OC": forward-char +#"\M-OA": previous-history +#"\M-OB": next-history +# +# Arrow keys in ANSI mode +# +"\M-[D": backward-char +"\M-[C": forward-char +"\M-[A": previous-history +"\M-[B": next-history +# +# Arrow keys in 8 bit keypad mode +# +#"\M-\C-OD": backward-char +#"\M-\C-OC": forward-char +#"\M-\C-OA": previous-history +#"\M-\C-OB": next-history +# +# Arrow keys in 8 bit ANSI mode +# +#"\M-\C-[D": backward-char +#"\M-\C-[C": forward-char +#"\M-\C-[A": previous-history +#"\M-\C-[B": next-history + +C-q: quoted-insert + +$endif + +# An old-style binding. This happens to be the default. +TAB: complete + +# Macros that are convenient for shell interaction +$if Bash +# edit the path +"\C-xp": "PATH=$@{PATH@}\e\C-e\C-a\ef\C-f" +# prepare to type a quoted word -- +# insert open and close double quotes +# and move to just after the open quote +"\C-x\"": "\"\"\C-b" +# insert a backslash (testing backslash escapes +# in sequences and macros) +"\C-x\\": "\\" +# Quote the current or previous word +"\C-xq": "\eb\"\ef\"" +# Add a binding to refresh the line, which is unbound +"\C-xr": redraw-current-line +# Edit variable on current line. +"\M-\C-v": "\C-a\C-k$\C-y\M-\C-e\C-a\C-y=" +$endif + +# use a visible bell if one is available +set bell-style visible + +# don't strip characters to 7 bits when reading +set input-meta on + +# allow iso-latin1 characters to be inserted rather +# than converted to prefix-meta sequences +set convert-meta off + +# display characters with the eighth bit set directly +# rather than as meta-prefixed characters +set output-meta on + +# if there are more than 150 possible completions for +# a word, ask the user if he wants to see all of them +set completion-query-items 150 + +# For FTP +$if Ftp +"\C-xg": "get \M-?" +"\C-xt": "put \M-?" +"\M-.": yank-last-arg +$endif +@end example + +@node Bindable Readline Commands +@section Bindable Readline Commands + +@menu +* Commands For Moving:: Moving about the line. +* Commands For History:: Getting at previous lines. +* Commands For Text:: Commands for changing text. +* Commands For Killing:: Commands for killing and yanking. +* Numeric Arguments:: Specifying numeric arguments, repeat counts. +* Commands For Completion:: Getting Readline to do the typing for you. +* Keyboard Macros:: Saving and re-executing typed characters +* Miscellaneous Commands:: Other miscellaneous commands. +@end menu + +This section describes Readline commands that may be bound to key +sequences. +@ifset BashFeatures +You can list your key bindings by executing +@w{@code{bind -P}} or, for a more terse format, suitable for an +@var{inputrc} file, @w{@code{bind -p}}. (@xref{Bash Builtins}.) +@end ifset +Command names without an accompanying key sequence are unbound by default. + +In the following descriptions, @dfn{point} refers to the current cursor +position, and @dfn{mark} refers to a cursor position saved by the +@code{set-mark} command. +The text between the point and mark is referred to as the @dfn{region}. + +@node Commands For Moving +@subsection Commands For Moving +@ftable @code +@item beginning-of-line (C-a) +Move to the start of the current line. + +@item end-of-line (C-e) +Move to the end of the line. + +@item forward-char (C-f) +Move forward a character. + +@item backward-char (C-b) +Move back a character. + +@item forward-word (M-f) +Move forward to the end of the next word. +Words are composed of letters and digits. + +@item backward-word (M-b) +Move back to the start of the current or previous word. +Words are composed of letters and digits. + +@ifset BashFeatures +@item shell-forward-word () +Move forward to the end of the next word. +Words are delimited by non-quoted shell metacharacters. + +@item shell-backward-word () +Move back to the start of the current or previous word. +Words are delimited by non-quoted shell metacharacters. +@end ifset + +@item clear-screen (C-l) +Clear the screen and redraw the current line, +leaving the current line at the top of the screen. + +@item redraw-current-line () +Refresh the current line. By default, this is unbound. + +@end ftable + +@node Commands For History +@subsection Commands For Manipulating The History + +@ftable @code +@item accept-line (Newline or Return) +@ifset BashFeatures +Accept the line regardless of where the cursor is. +If this line is +non-empty, add it to the history list according to the setting of +the @env{HISTCONTROL} and @env{HISTIGNORE} variables. +If this line is a modified history line, then restore the history line +to its original state. +@end ifset +@ifclear BashFeatures +Accept the line regardless of where the cursor is. +If this line is +non-empty, it may be added to the history list for future recall with +@code{add_history()}. +If this line is a modified history line, the history line is restored +to its original state. +@end ifclear + +@item previous-history (C-p) +Move `back' through the history list, fetching the previous command. + +@item next-history (C-n) +Move `forward' through the history list, fetching the next command. + +@item beginning-of-history (M-<) +Move to the first line in the history. + +@item end-of-history (M->) +Move to the end of the input history, i.e., the line currently +being entered. + +@item reverse-search-history (C-r) +Search backward starting at the current line and moving `up' through +the history as necessary. This is an incremental search. + +@item forward-search-history (C-s) +Search forward starting at the current line and moving `down' through +the history as necessary. This is an incremental search. + +@item non-incremental-reverse-search-history (M-p) +Search backward starting at the current line and moving `up' +through the history as necessary using a non-incremental search +for a string supplied by the user. +The search string may match anywhere in a history line. + +@item non-incremental-forward-search-history (M-n) +Search forward starting at the current line and moving `down' +through the history as necessary using a non-incremental search +for a string supplied by the user. +The search string may match anywhere in a history line. + +@item history-search-forward () +Search forward through the history for the string of characters +between the start of the current line and the point. +The search string must match at the beginning of a history line. +This is a non-incremental search. +By default, this command is unbound. + +@item history-search-backward () +Search backward through the history for the string of characters +between the start of the current line and the point. +The search string must match at the beginning of a history line. +This is a non-incremental search. +By default, this command is unbound. + +@item history-substr-search-forward () +Search forward through the history for the string of characters +between the start of the current line and the point. +The search string may match anywhere in a history line. +This is a non-incremental search. +By default, this command is unbound. + +@item history-substr-search-backward () +Search backward through the history for the string of characters +between the start of the current line and the point. +The search string may match anywhere in a history line. +This is a non-incremental search. +By default, this command is unbound. + +@item yank-nth-arg (M-C-y) +Insert the first argument to the previous command (usually +the second word on the previous line) at point. +With an argument @var{n}, +insert the @var{n}th word from the previous command (the words +in the previous command begin with word 0). A negative argument +inserts the @var{n}th word from the end of the previous command. +Once the argument @var{n} is computed, the argument is extracted +as if the @samp{!@var{n}} history expansion had been specified. + +@item yank-last-arg (M-. or M-_) +Insert last argument to the previous command (the last word of the +previous history entry). +With a numeric argument, behave exactly like @code{yank-nth-arg}. +Successive calls to @code{yank-last-arg} move back through the history +list, inserting the last word (or the word specified by the argument to +the first call) of each line in turn. +Any numeric argument supplied to these successive calls determines +the direction to move through the history. A negative argument switches +the direction through the history (back or forward). +The history expansion facilities are used to extract the last argument, +as if the @samp{!$} history expansion had been specified. + +@end ftable + +@node Commands For Text +@subsection Commands For Changing Text + +@ftable @code + +@item @i{end-of-file} (usually C-d) +The character indicating end-of-file as set, for example, by +@code{stty}. If this character is read when there are no characters +on the line, and point is at the beginning of the line, Readline +interprets it as the end of input and returns @sc{eof}. + +@item delete-char (C-d) +Delete the character at point. If this function is bound to the +same character as the tty @sc{eof} character, as @kbd{C-d} +commonly is, see above for the effects. + +@item backward-delete-char (Rubout) +Delete the character behind the cursor. A numeric argument means +to kill the characters instead of deleting them. + +@item forward-backward-delete-char () +Delete the character under the cursor, unless the cursor is at the +end of the line, in which case the character behind the cursor is +deleted. By default, this is not bound to a key. + +@item quoted-insert (C-q or C-v) +Add the next character typed to the line verbatim. This is +how to insert key sequences like @kbd{C-q}, for example. + +@ifclear BashFeatures +@item tab-insert (M-@key{TAB}) +Insert a tab character. +@end ifclear + +@item self-insert (a, b, A, 1, !, @dots{}) +Insert yourself. + +@item bracketed-paste-begin () +This function is intended to be bound to the "bracketed paste" escape +sequence sent by some terminals, and such a binding is assigned by default. +It allows Readline to insert the pasted text as a single unit without treating +each character as if it had been read from the keyboard. The characters +are inserted as if each one was bound to @code{self-insert}) instead of +executing any editing commands. + +@item transpose-chars (C-t) +Drag the character before the cursor forward over +the character at the cursor, moving the +cursor forward as well. If the insertion point +is at the end of the line, then this +transposes the last two characters of the line. +Negative arguments have no effect. + +@item transpose-words (M-t) +Drag the word before point past the word after point, +moving point past that word as well. +If the insertion point is at the end of the line, this transposes +the last two words on the line. + +@item upcase-word (M-u) +Uppercase the current (or following) word. With a negative argument, +uppercase the previous word, but do not move the cursor. + +@item downcase-word (M-l) +Lowercase the current (or following) word. With a negative argument, +lowercase the previous word, but do not move the cursor. + +@item capitalize-word (M-c) +Capitalize the current (or following) word. With a negative argument, +capitalize the previous word, but do not move the cursor. + +@item overwrite-mode () +Toggle overwrite mode. With an explicit positive numeric argument, +switches to overwrite mode. With an explicit non-positive numeric +argument, switches to insert mode. This command affects only +@code{emacs} mode; @code{vi} mode does overwrite differently. +Each call to @code{readline()} starts in insert mode. + +In overwrite mode, characters bound to @code{self-insert} replace +the text at point rather than pushing the text to the right. +Characters bound to @code{backward-delete-char} replace the character +before point with a space. + +By default, this command is unbound. + +@end ftable + +@node Commands For Killing +@subsection Killing And Yanking + +@ftable @code + +@item kill-line (C-k) +Kill the text from point to the end of the line. + +@item backward-kill-line (C-x Rubout) +Kill backward from the cursor to the beginning of the current line. + +@item unix-line-discard (C-u) +Kill backward from the cursor to the beginning of the current line. + +@item kill-whole-line () +Kill all characters on the current line, no matter where point is. +By default, this is unbound. + +@item kill-word (M-d) +Kill from point to the end of the current word, or if between +words, to the end of the next word. +Word boundaries are the same as @code{forward-word}. + +@item backward-kill-word (M-@key{DEL}) +Kill the word behind point. +Word boundaries are the same as @code{backward-word}. + +@ifset BashFeatures +@item shell-kill-word () +Kill from point to the end of the current word, or if between +words, to the end of the next word. +Word boundaries are the same as @code{shell-forward-word}. + +@item shell-backward-kill-word () +Kill the word behind point. +Word boundaries are the same as @code{shell-backward-word}. +@end ifset + +@item unix-word-rubout (C-w) +Kill the word behind point, using white space as a word boundary. +The killed text is saved on the kill-ring. + +@item unix-filename-rubout () +Kill the word behind point, using white space and the slash character +as the word boundaries. +The killed text is saved on the kill-ring. + +@item delete-horizontal-space () +Delete all spaces and tabs around point. By default, this is unbound. + +@item kill-region () +Kill the text in the current region. +By default, this command is unbound. + +@item copy-region-as-kill () +Copy the text in the region to the kill buffer, so it can be yanked +right away. By default, this command is unbound. + +@item copy-backward-word () +Copy the word before point to the kill buffer. +The word boundaries are the same as @code{backward-word}. +By default, this command is unbound. + +@item copy-forward-word () +Copy the word following point to the kill buffer. +The word boundaries are the same as @code{forward-word}. +By default, this command is unbound. + +@item yank (C-y) +Yank the top of the kill ring into the buffer at point. + +@item yank-pop (M-y) +Rotate the kill-ring, and yank the new top. You can only do this if +the prior command is @code{yank} or @code{yank-pop}. +@end ftable + +@node Numeric Arguments +@subsection Specifying Numeric Arguments +@ftable @code + +@item digit-argument (@kbd{M-0}, @kbd{M-1}, @dots{} @kbd{M--}) +Add this digit to the argument already accumulating, or start a new +argument. @kbd{M--} starts a negative argument. + +@item universal-argument () +This is another way to specify an argument. +If this command is followed by one or more digits, optionally with a +leading minus sign, those digits define the argument. +If the command is followed by digits, executing @code{universal-argument} +again ends the numeric argument, but is otherwise ignored. +As a special case, if this command is immediately followed by a +character that is neither a digit or minus sign, the argument count +for the next command is multiplied by four. +The argument count is initially one, so executing this function the +first time makes the argument count four, a second time makes the +argument count sixteen, and so on. +By default, this is not bound to a key. +@end ftable + +@node Commands For Completion +@subsection Letting Readline Type For You + +@ftable @code +@item complete (@key{TAB}) +Attempt to perform completion on the text before point. +The actual completion performed is application-specific. +@ifset BashFeatures +Bash attempts completion treating the text as a variable (if the +text begins with @samp{$}), username (if the text begins with +@samp{~}), hostname (if the text begins with @samp{@@}), or +command (including aliases and functions) in turn. If none +of these produces a match, filename completion is attempted. +@end ifset +@ifclear BashFeatures +The default is filename completion. +@end ifclear + +@item possible-completions (M-?) +List the possible completions of the text before point. +When displaying completions, Readline sets the number of columns used +for display to the value of @code{completion-display-width}, the value of +the environment variable @env{COLUMNS}, or the screen width, in that order. + +@item insert-completions (M-*) +Insert all completions of the text before point that would have +been generated by @code{possible-completions}. + +@item menu-complete () +Similar to @code{complete}, but replaces the word to be completed +with a single match from the list of possible completions. +Repeated execution of @code{menu-complete} steps through the list +of possible completions, inserting each match in turn. +At the end of the list of completions, the bell is rung +(subject to the setting of @code{bell-style}) +and the original text is restored. +An argument of @var{n} moves @var{n} positions forward in the list +of matches; a negative argument may be used to move backward +through the list. +This command is intended to be bound to @key{TAB}, but is unbound +by default. + +@item menu-complete-backward () +Identical to @code{menu-complete}, but moves backward through the list +of possible completions, as if @code{menu-complete} had been given a +negative argument. + +@item delete-char-or-list () +Deletes the character under the cursor if not at the beginning or +end of the line (like @code{delete-char}). +If at the end of the line, behaves identically to +@code{possible-completions}. +This command is unbound by default. + +@ifset BashFeatures +@item complete-filename (M-/) +Attempt filename completion on the text before point. + +@item possible-filename-completions (C-x /) +List the possible completions of the text before point, +treating it as a filename. + +@item complete-username (M-~) +Attempt completion on the text before point, treating +it as a username. + +@item possible-username-completions (C-x ~) +List the possible completions of the text before point, +treating it as a username. + +@item complete-variable (M-$) +Attempt completion on the text before point, treating +it as a shell variable. + +@item possible-variable-completions (C-x $) +List the possible completions of the text before point, +treating it as a shell variable. + +@item complete-hostname (M-@@) +Attempt completion on the text before point, treating +it as a hostname. + +@item possible-hostname-completions (C-x @@) +List the possible completions of the text before point, +treating it as a hostname. + +@item complete-command (M-!) +Attempt completion on the text before point, treating +it as a command name. Command completion attempts to +match the text against aliases, reserved words, shell +functions, shell builtins, and finally executable filenames, +in that order. + +@item possible-command-completions (C-x !) +List the possible completions of the text before point, +treating it as a command name. + +@item dynamic-complete-history (M-@key{TAB}) +Attempt completion on the text before point, comparing +the text against lines from the history list for possible +completion matches. + +@item dabbrev-expand () +Attempt menu completion on the text before point, comparing +the text against lines from the history list for possible +completion matches. + +@item complete-into-braces (M-@{) +Perform filename completion and insert the list of possible completions +enclosed within braces so the list is available to the shell +(@pxref{Brace Expansion}). + +@end ifset +@end ftable + +@node Keyboard Macros +@subsection Keyboard Macros +@ftable @code + +@item start-kbd-macro (C-x () +Begin saving the characters typed into the current keyboard macro. + +@item end-kbd-macro (C-x )) +Stop saving the characters typed into the current keyboard macro +and save the definition. + +@item call-last-kbd-macro (C-x e) +Re-execute the last keyboard macro defined, by making the characters +in the macro appear as if typed at the keyboard. + +@item print-last-kbd-macro () +Print the last keboard macro defined in a format suitable for the +@var{inputrc} file. + +@end ftable + +@node Miscellaneous Commands +@subsection Some Miscellaneous Commands +@ftable @code + +@item re-read-init-file (C-x C-r) +Read in the contents of the @var{inputrc} file, and incorporate +any bindings or variable assignments found there. + +@item abort (C-g) +Abort the current editing command and +ring the terminal's bell (subject to the setting of +@code{bell-style}). + +@item do-uppercase-version (M-a, M-b, M-@var{x}, @dots{}) +If the metafied character @var{x} is lowercase, run the command +that is bound to the corresponding uppercase character. + +@item prefix-meta (@key{ESC}) +Metafy the next character typed. This is for keyboards +without a meta key. Typing @samp{@key{ESC} f} is equivalent to typing +@kbd{M-f}. + +@item undo (C-_ or C-x C-u) +Incremental undo, separately remembered for each line. + +@item revert-line (M-r) +Undo all changes made to this line. This is like executing the @code{undo} +command enough times to get back to the beginning. + +@ifset BashFeatures +@item tilde-expand (M-&) +@end ifset +@ifclear BashFeatures +@item tilde-expand (M-~) +@end ifclear +Perform tilde expansion on the current word. + +@item set-mark (C-@@) +Set the mark to the point. If a +numeric argument is supplied, the mark is set to that position. + +@item exchange-point-and-mark (C-x C-x) +Swap the point with the mark. The current cursor position is set to +the saved position, and the old cursor position is saved as the mark. + +@item character-search (C-]) +A character is read and point is moved to the next occurrence of that +character. A negative count searches for previous occurrences. + +@item character-search-backward (M-C-]) +A character is read and point is moved to the previous occurrence +of that character. A negative count searches for subsequent +occurrences. + +@item skip-csi-sequence () +Read enough characters to consume a multi-key sequence such as those +defined for keys like Home and End. Such sequences begin with a +Control Sequence Indicator (CSI), usually ESC-[. If this sequence is +bound to "\e[", keys producing such sequences will have no effect +unless explicitly bound to a readline command, instead of inserting +stray characters into the editing buffer. This is unbound by default, +but usually bound to ESC-[. + +@item insert-comment (M-#) +Without a numeric argument, the value of the @code{comment-begin} +variable is inserted at the beginning of the current line. +If a numeric argument is supplied, this command acts as a toggle: if +the characters at the beginning of the line do not match the value +of @code{comment-begin}, the value is inserted, otherwise +the characters in @code{comment-begin} are deleted from the beginning of +the line. +In either case, the line is accepted as if a newline had been typed. +@ifset BashFeatures +The default value of @code{comment-begin} causes this command +to make the current line a shell comment. +If a numeric argument causes the comment character to be removed, the line +will be executed by the shell. +@end ifset + +@item dump-functions () +Print all of the functions and their key bindings to the +Readline output stream. If a numeric argument is supplied, +the output is formatted in such a way that it can be made part +of an @var{inputrc} file. This command is unbound by default. + +@item dump-variables () +Print all of the settable variables and their values to the +Readline output stream. If a numeric argument is supplied, +the output is formatted in such a way that it can be made part +of an @var{inputrc} file. This command is unbound by default. + +@item dump-macros () +Print all of the Readline key sequences bound to macros and the +strings they output. If a numeric argument is supplied, +the output is formatted in such a way that it can be made part +of an @var{inputrc} file. This command is unbound by default. + +@ifset BashFeatures +@item glob-complete-word (M-g) +The word before point is treated as a pattern for pathname expansion, +with an asterisk implicitly appended. This pattern is used to +generate a list of matching file names for possible completions. + +@item glob-expand-word (C-x *) +The word before point is treated as a pattern for pathname expansion, +and the list of matching file names is inserted, replacing the word. +If a numeric argument is supplied, a @samp{*} is appended before +pathname expansion. + +@item glob-list-expansions (C-x g) +The list of expansions that would have been generated by +@code{glob-expand-word} is displayed, and the line is redrawn. +If a numeric argument is supplied, a @samp{*} is appended before +pathname expansion. + +@item display-shell-version (C-x C-v) +Display version information about the current instance of Bash. + +@item shell-expand-line (M-C-e) +Expand the line as the shell does. +This performs alias and history expansion as well as all of the shell +word expansions (@pxref{Shell Expansions}). + +@item history-expand-line (M-^) +Perform history expansion on the current line. + +@item magic-space () +Perform history expansion on the current line and insert a space +(@pxref{History Interaction}). + +@item alias-expand-line () +Perform alias expansion on the current line (@pxref{Aliases}). + +@item history-and-alias-expand-line () +Perform history and alias expansion on the current line. + +@item insert-last-argument (M-. or M-_) +A synonym for @code{yank-last-arg}. + +@item operate-and-get-next (C-o) +Accept the current line for execution and fetch the next line +relative to the current line from the history for editing. Any +argument is ignored. + +@item edit-and-execute-command (C-xC-e) +Invoke an editor on the current command line, and execute the result as shell +commands. +Bash attempts to invoke +@code{$VISUAL}, @code{$EDITOR}, and @code{emacs} +as the editor, in that order. + +@end ifset + +@ifclear BashFeatures +@item emacs-editing-mode (C-e) +When in @code{vi} command mode, this causes a switch to @code{emacs} +editing mode. + +@item vi-editing-mode (M-C-j) +When in @code{emacs} editing mode, this causes a switch to @code{vi} +editing mode. + +@end ifclear + +@end ftable + +@node Readline vi Mode +@section Readline vi Mode + +While the Readline library does not have a full set of @code{vi} +editing functions, it does contain enough to allow simple editing +of the line. The Readline @code{vi} mode behaves as specified in +the @sc{posix} standard. + +@ifset BashFeatures +In order to switch interactively between @code{emacs} and @code{vi} +editing modes, use the @samp{set -o emacs} and @samp{set -o vi} +commands (@pxref{The Set Builtin}). +@end ifset +@ifclear BashFeatures +In order to switch interactively between @code{emacs} and @code{vi} +editing modes, use the command @kbd{M-C-j} (bound to emacs-editing-mode +when in @code{vi} mode and to vi-editing-mode in @code{emacs} mode). +@end ifclear +The Readline default is @code{emacs} mode. + +When you enter a line in @code{vi} mode, you are already placed in +`insertion' mode, as if you had typed an @samp{i}. Pressing @key{ESC} +switches you into `command' mode, where you can edit the text of the +line with the standard @code{vi} movement keys, move to previous +history lines with @samp{k} and subsequent lines with @samp{j}, and +so forth. + +@ifset BashFeatures +@node Programmable Completion +@section Programmable Completion +@cindex programmable completion + +When word completion is attempted for an argument to a command for +which a completion specification (a @var{compspec}) has been defined +using the @code{complete} builtin (@pxref{Programmable Completion Builtins}), +the programmable completion facilities are invoked. + +First, the command name is identified. +If a compspec has been defined for that command, the +compspec is used to generate the list of possible completions for the word. +If the command word is the empty string (completion attempted at the +beginning of an empty line), any compspec defined with +the @option{-E} option to @code{complete} is used. +If the command word is a full pathname, a compspec for the full +pathname is searched for first. +If no compspec is found for the full pathname, an attempt is made to +find a compspec for the portion following the final slash. +If those searches do not result in a compspec, any compspec defined with +the @option{-D} option to @code{complete} is used as the default. + +Once a compspec has been found, it is used to generate the list of +matching words. +If a compspec is not found, the default Bash completion +described above (@pxref{Commands For Completion}) is performed. + +First, the actions specified by the compspec are used. +Only matches which are prefixed by the word being completed are +returned. +When the @option{-f} or @option{-d} option is used for filename or +directory name completion, the shell variable @env{FIGNORE} is +used to filter the matches. +@xref{Bash Variables}, for a description of @env{FIGNORE}. + +Any completions specified by a filename expansion pattern to the +@option{-G} option are generated next. +The words generated by the pattern need not match the word being completed. +The @env{GLOBIGNORE} shell variable is not used to filter the matches, +but the @env{FIGNORE} shell variable is used. + +Next, the string specified as the argument to the @option{-W} option +is considered. +The string is first split using the characters in the @env{IFS} +special variable as delimiters. +Shell quoting is honored. +Each word is then expanded using +brace expansion, tilde expansion, parameter and variable expansion, +command substitution, and arithmetic expansion, +as described above (@pxref{Shell Expansions}). +The results are split using the rules described above +(@pxref{Word Splitting}). +The results of the expansion are prefix-matched against the word being +completed, and the matching words become the possible completions. + +After these matches have been generated, any shell function or command +specified with the @option{-F} and @option{-C} options is invoked. +When the command or function is invoked, the @env{COMP_LINE}, +@env{COMP_POINT}, @env{COMP_KEY}, and @env{COMP_TYPE} variables are +assigned values as described above (@pxref{Bash Variables}). +If a shell function is being invoked, the @env{COMP_WORDS} and +@env{COMP_CWORD} variables are also set. +When the function or command is invoked, the first argument ($1) is the +name of the command whose arguments are being completed, the +second argument ($2) is the word being completed, and the third argument +($3) is the word preceding the word being completed on the current command +line. +No filtering of the generated completions against the word being completed +is performed; the function or command has complete freedom in generating +the matches. + +Any function specified with @option{-F} is invoked first. +The function may use any of the shell facilities, including the +@code{compgen} and @code{compopt} builtins described below +(@pxref{Programmable Completion Builtins}), to generate the matches. +It must put the possible completions in the @env{COMPREPLY} array +variable, one per array element. + +Next, any command specified with the @option{-C} option is invoked +in an environment equivalent to command substitution. +It should print a list of completions, one per line, to +the standard output. +Backslash may be used to escape a newline, if necessary. + +After all of the possible completions are generated, any filter +specified with the @option{-X} option is applied to the list. +The filter is a pattern as used for pathname expansion; a @samp{&} +in the pattern is replaced with the text of the word being completed. +A literal @samp{&} may be escaped with a backslash; the backslash +is removed before attempting a match. +Any completion that matches the pattern will be removed from the list. +A leading @samp{!} negates the pattern; in this case any completion +not matching the pattern will be removed. + +Finally, any prefix and suffix specified with the @option{-P} and @option{-S} +options are added to each member of the completion list, and the result is +returned to the Readline completion code as the list of possible +completions. + +If the previously-applied actions do not generate any matches, and the +@option{-o dirnames} option was supplied to @code{complete} when the +compspec was defined, directory name completion is attempted. + +If the @option{-o plusdirs} option was supplied to @code{complete} when +the compspec was defined, directory name completion is attempted and any +matches are added to the results of the other actions. + +By default, if a compspec is found, whatever it generates is returned to +the completion code as the full set of possible completions. +The default Bash completions are not attempted, and the Readline default +of filename completion is disabled. +If the @option{-o bashdefault} option was supplied to @code{complete} when +the compspec was defined, the default Bash completions are attempted +if the compspec generates no matches. +If the @option{-o default} option was supplied to @code{complete} when the +compspec was defined, Readline's default completion will be performed +if the compspec (and, if attempted, the default Bash completions) +generate no matches. + +When a compspec indicates that directory name completion is desired, +the programmable completion functions force Readline to append a slash +to completed names which are symbolic links to directories, subject to +the value of the @var{mark-directories} Readline variable, regardless +of the setting of the @var{mark-symlinked-directories} Readline variable. + +There is some support for dynamically modifying completions. This is +most useful when used in combination with a default completion specified +with @option{-D}. It's possible for shell functions executed as completion +handlers to indicate that completion should be retried by returning an +exit status of 124. If a shell function returns 124, and changes +the compspec associated with the command on which completion is being +attempted (supplied as the first argument when the function is executed), +programmable completion restarts from the beginning, with an +attempt to find a new compspec for that command. This allows a set of +completions to be built dynamically as completion is attempted, rather than +being loaded all at once. + +For instance, assuming that there is a library of compspecs, each kept in a +file corresponding to the name of the command, the following default +completion function would load completions dynamically: + +@example +_completion_loader() +@{ + . "/etc/bash_completion.d/$1.sh" >/dev/null 2>&1 && return 124 +@} +complete -D -F _completion_loader -o bashdefault -o default +@end example + +@node Programmable Completion Builtins +@section Programmable Completion Builtins +@cindex completion builtins + +Three builtin commands are available to manipulate the programmable completion +facilities: one to specify how the arguments to a particular command are to +be completed, and two to modify the completion as it is happening. + +@table @code +@item compgen +@btindex compgen +@example +@code{compgen [@var{option}] [@var{word}]} +@end example + +Generate possible completion matches for @var{word} according to +the @var{option}s, which may be any option accepted by the +@code{complete} +builtin with the exception of @option{-p} and @option{-r}, and write +the matches to the standard output. +When using the @option{-F} or @option{-C} options, the various shell variables +set by the programmable completion facilities, while available, will not +have useful values. + +The matches will be generated in the same way as if the programmable +completion code had generated them directly from a completion specification +with the same flags. +If @var{word} is specified, only those completions matching @var{word} +will be displayed. + +The return value is true unless an invalid option is supplied, or no +matches were generated. + +@item complete +@btindex complete +@example +@code{complete [-abcdefgjksuv] [-o @var{comp-option}] [-DE] [-A @var{action}] [-G @var{globpat}] [-W @var{wordlist}] +[-F @var{function}] [-C @var{command}] [-X @var{filterpat}] +[-P @var{prefix}] [-S @var{suffix}] @var{name} [@var{name} @dots{}]} +@code{complete -pr [-DE] [@var{name} @dots{}]} +@end example + +Specify how arguments to each @var{name} should be completed. +If the @option{-p} option is supplied, or if no options are supplied, existing +completion specifications are printed in a way that allows them to be +reused as input. +The @option{-r} option removes a completion specification for +each @var{name}, or, if no @var{name}s are supplied, all +completion specifications. +The @option{-D} option indicates that the remaining options and actions should +apply to the ``default'' command completion; that is, completion attempted +on a command for which no completion has previously been defined. +The @option{-E} option indicates that the remaining options and actions should +apply to ``empty'' command completion; that is, completion attempted on a +blank line. + +The process of applying these completion specifications when word completion +is attempted is described above (@pxref{Programmable Completion}). The +@option{-D} option takes precedence over @option{-E}. + +Other options, if specified, have the following meanings. +The arguments to the @option{-G}, @option{-W}, and @option{-X} options +(and, if necessary, the @option{-P} and @option{-S} options) +should be quoted to protect them from expansion before the +@code{complete} builtin is invoked. + + +@table @code +@item -o @var{comp-option} +The @var{comp-option} controls several aspects of the compspec's behavior +beyond the simple generation of completions. +@var{comp-option} may be one of: + +@table @code + +@item bashdefault +Perform the rest of the default Bash completions if the compspec +generates no matches. + +@item default +Use Readline's default filename completion if the compspec generates +no matches. + +@item dirnames +Perform directory name completion if the compspec generates no matches. + +@item filenames +Tell Readline that the compspec generates filenames, so it can perform any +filename-specific processing (like adding a slash to directory names +quoting special characters, or suppressing trailing spaces). +This option is intended to be used with shell functions specified +with @option{-F}. + +@item noquote +Tell Readline not to quote the completed words if they are filenames +(quoting filenames is the default). + +@item nospace +Tell Readline not to append a space (the default) to words completed at +the end of the line. + +@item plusdirs +After any matches defined by the compspec are generated, +directory name completion is attempted and any +matches are added to the results of the other actions. + +@end table + +@item -A @var{action} +The @var{action} may be one of the following to generate a list of possible +completions: + +@table @code +@item alias +Alias names. May also be specified as @option{-a}. + +@item arrayvar +Array variable names. + +@item binding +Readline key binding names (@pxref{Bindable Readline Commands}). + +@item builtin +Names of shell builtin commands. May also be specified as @option{-b}. + +@item command +Command names. May also be specified as @option{-c}. + +@item directory +Directory names. May also be specified as @option{-d}. + +@item disabled +Names of disabled shell builtins. + +@item enabled +Names of enabled shell builtins. + +@item export +Names of exported shell variables. May also be specified as @option{-e}. + +@item file +File names. May also be specified as @option{-f}. + +@item function +Names of shell functions. + +@item group +Group names. May also be specified as @option{-g}. + +@item helptopic +Help topics as accepted by the @code{help} builtin (@pxref{Bash Builtins}). + +@item hostname +Hostnames, as taken from the file specified by the +@env{HOSTFILE} shell variable (@pxref{Bash Variables}). + +@item job +Job names, if job control is active. May also be specified as @option{-j}. + +@item keyword +Shell reserved words. May also be specified as @option{-k}. + +@item running +Names of running jobs, if job control is active. + +@item service +Service names. May also be specified as @option{-s}. + +@item setopt +Valid arguments for the @option{-o} option to the @code{set} builtin +(@pxref{The Set Builtin}). + +@item shopt +Shell option names as accepted by the @code{shopt} builtin +(@pxref{Bash Builtins}). + +@item signal +Signal names. + +@item stopped +Names of stopped jobs, if job control is active. + +@item user +User names. May also be specified as @option{-u}. + +@item variable +Names of all shell variables. May also be specified as @option{-v}. +@end table + +@item -C @var{command} +@var{command} is executed in a subshell environment, and its output is +used as the possible completions. + +@item -F @var{function} +The shell function @var{function} is executed in the current shell +environment. +When it is executed, $1 is the name of the command whose arguments are +being completed, $2 is the word being completed, and $3 is the word +preceding the word being completed, as described above +(@pxref{Programmable Completion}). +When it finishes, the possible completions are retrieved from the value +of the @env{COMPREPLY} array variable. + +@item -G @var{globpat} +The filename expansion pattern @var{globpat} is expanded to generate +the possible completions. + +@item -P @var{prefix} +@var{prefix} is added at the beginning of each possible completion +after all other options have been applied. + +@item -S @var{suffix} +@var{suffix} is appended to each possible completion +after all other options have been applied. + +@item -W @var{wordlist} +The @var{wordlist} is split using the characters in the +@env{IFS} special variable as delimiters, and each resultant word +is expanded. +The possible completions are the members of the resultant list which +match the word being completed. + +@item -X @var{filterpat} +@var{filterpat} is a pattern as used for filename expansion. +It is applied to the list of possible completions generated by the +preceding options and arguments, and each completion matching +@var{filterpat} is removed from the list. +A leading @samp{!} in @var{filterpat} negates the pattern; in this +case, any completion not matching @var{filterpat} is removed. +@end table + +The return value is true unless an invalid option is supplied, an option +other than @option{-p} or @option{-r} is supplied without a @var{name} +argument, an attempt is made to remove a completion specification for +a @var{name} for which no specification exists, or +an error occurs adding a completion specification. + +@item compopt +@btindex compopt +@example +@code{compopt} [-o @var{option}] [-DE] [+o @var{option}] [@var{name}] +@end example +Modify completion options for each @var{name} according to the +@var{option}s, or for the currently-executing completion if no @var{name}s +are supplied. +If no @var{option}s are given, display the completion options for each +@var{name} or the current completion. +The possible values of @var{option} are those valid for the @code{complete} +builtin described above. +The @option{-D} option indicates that the remaining options should +apply to the ``default'' command completion; that is, completion attempted +on a command for which no completion has previously been defined. +The @option{-E} option indicates that the remaining options should +apply to ``empty'' command completion; that is, completion attempted on a +blank line. + +The @option{-D} option takes precedence over @option{-E}. + +The return value is true unless an invalid option is supplied, an attempt +is made to modify the options for a @var{name} for which no completion +specification exists, or an output error occurs. + +@end table + +@node A Programmable Completion Example +@section A Programmable Completion Example + +The most common way to obtain additional completion functionality beyond +the default actions @code{complete} and @code{compgen} provide is to use +a shell function and bind it to a particular command using @code{complete -F}. + +The following function provides completions for the @code{cd} builtin. +It is a reasonably good example of what shell functions must do when +used for completion. This function uses the word passsed as @code{$2} +to determine the directory name to complete. You can also use the +@code{COMP_WORDS} array variable; the current word is indexed by the +@code{COMP_CWORD} variable. + +The function relies on the @code{complete} and @code{compgen} builtins +to do much of the work, adding only the things that the Bash @code{cd} +does beyond accepting basic directory names: +tilde expansion (@pxref{Tilde Expansion}), +searching directories in @var{$CDPATH}, which is described above +(@pxref{Bourne Shell Builtins}), +and basic support for the @code{cdable_vars} shell option +(@pxref{The Shopt Builtin}). +@code{_comp_cd} modifies the value of @var{IFS} so that it contains only +a newline to accommodate file names containing spaces and tabs -- +@code{compgen} prints the possible completions it generates one per line. + +Possible completions go into the @var{COMPREPLY} array variable, one +completion per array element. The programmable completion system retrieves +the completions from there when the function returns. + +@example +# A completion function for the cd builtin +# based on the cd completion function from the bash_completion package +_comp_cd() +@{ + local IFS=$' \t\n' # normalize IFS + local cur _skipdot _cdpath + local i j k + + # Tilde expansion, with side effect of expanding tilde to full pathname + case "$2" in + \~*) eval cur="$2" ;; + *) cur=$2 ;; + esac + + # no cdpath or absolute pathname -- straight directory completion + if [[ -z "$@{CDPATH:-@}" ]] || [[ "$cur" == @@(./*|../*|/*) ]]; then + # compgen prints paths one per line; could also use while loop + IFS=$'\n' + COMPREPLY=( $(compgen -d -- "$cur") ) + IFS=$' \t\n' + # CDPATH+directories in the current directory if not in CDPATH + else + IFS=$'\n' + _skipdot=false + # preprocess CDPATH to convert null directory names to . + _cdpath=$@{CDPATH/#:/.:@} + _cdpath=$@{_cdpath//::/:.:@} + _cdpath=$@{_cdpath/%:/:.@} + for i in $@{_cdpath//:/$'\n'@}; do + if [[ $i -ef . ]]; then _skipdot=true; fi + k="$@{#COMPREPLY[@@]@}" + for j in $( compgen -d -- "$i/$cur" ); do + COMPREPLY[k++]=$@{j#$i/@} # cut off directory + done + done + $_skipdot || COMPREPLY+=( $(compgen -d -- "$cur") ) + IFS=$' \t\n' + fi + + # variable names if appropriate shell option set and no completions + if shopt -q cdable_vars && [[ $@{#COMPREPLY[@@]@} -eq 0 ]]; then + COMPREPLY=( $(compgen -v -- "$cur") ) + fi + + return 0 +@} +@end example + +We install the completion function using the @option{-F} option to +@code{complete}: + +@example +# Tell readline to quote appropriate and append slashes to directories; +# use the bash default completion for other arguments +complete -o filenames -o nospace -o bashdefault -F _comp_cd cd +@end example + +@noindent +Since we'd like Bash and Readline to take care of some +of the other details for us, we use several other options to tell Bash +and Readline what to do. The @option{-o filenames} option tells Readline +that the possible completions should be treated as filenames, and quoted +appropriately. That option will also cause Readline to append a slash to +filenames it can determine are directories (which is why we might want to +extend @code{_comp_cd} to append a slash if we're using directories found +via @var{CDPATH}: Readline can't tell those completions are directories). +The @option{-o nospace} option tells Readline to not append a space +character to the directory name, in case we want to append to it. +The @option{-o bashdefault} option brings in the rest of the "Bash default" +completions -- possible completion that Bash adds to the default Readline +set. These include things like command name completion, variable completion +for words beginning with @samp{@{}, completions containing pathname +expansion patterns (@pxref{Filename Expansion}), and so on. + +Once installed using @code{complete}, @code{_comp_cd} will be called every +time we attempt word completion for a @code{cd} command. + +Many more examples -- an extensive collection of completions for most of +the common GNU, Unix, and Linux commands -- are available as part of the +bash_completion project. This is installed by default on many GNU/Linux +distributions. Originally written by Ian Macdonald, the project now lives +at @url{http://bash-completion.alioth.debian.org/}. There are ports for +other systems such as Solaris and Mac OS X. + +An older version of the bash_completion package is distributed with bash +in the @file{examples/complete} subdirectory. + +@end ifset diff --git a/lib/readline/doc/version.texi b/lib/readline/doc/version.texi index 1035805c..e584097e 100644 --- a/lib/readline/doc/version.texi +++ b/lib/readline/doc/version.texi @@ -4,7 +4,7 @@ Copyright (C) 1988-2014 Free Software Foundation, Inc. @set EDITION 6.3 @set VERSION 6.3 -@set UPDATED 1 July 2014 -@set UPDATED-MONTH Julyy 2014 +@set UPDATED 15 November 2014 +@set UPDATED-MONTH November 2014 -@set LASTCHANGE Tue Jul 1 16:37:33 PDT 2014 +@set LASTCHANGE Sat Nov 15 19:56:51 EST 2014 diff --git a/lib/readline/doc/version.texi~ b/lib/readline/doc/version.texi~ new file mode 100644 index 00000000..1035805c --- /dev/null +++ b/lib/readline/doc/version.texi~ @@ -0,0 +1,10 @@ +@ignore +Copyright (C) 1988-2014 Free Software Foundation, Inc. +@end ignore + +@set EDITION 6.3 +@set VERSION 6.3 +@set UPDATED 1 July 2014 +@set UPDATED-MONTH Julyy 2014 + +@set LASTCHANGE Tue Jul 1 16:37:33 PDT 2014 diff --git a/lib/readline/history.c b/lib/readline/history.c index ece68fee..bc93c4b2 100644 --- a/lib/readline/history.c +++ b/lib/readline/history.c @@ -282,9 +282,14 @@ add_history (string) if (the_history[0]) (void) free_history_entry (the_history[0]); - /* Copy the rest of the entries, moving down one slot. */ + /* Copy the rest of the entries, moving down one slot. Copy includes + trailing NULL. */ +#if 0 for (i = 0; i < history_length; i++) the_history[i] = the_history[i + 1]; +#else + memmove (the_history, the_history + 1, history_length * sizeof (HIST_ENTRY)); +#endif history_base++; } diff --git a/lib/readline/history.c~ b/lib/readline/history.c~ new file mode 100644 index 00000000..3b3f0f5d --- /dev/null +++ b/lib/readline/history.c~ @@ -0,0 +1,532 @@ +/* history.c -- standalone history library */ + +/* Copyright (C) 1989-2011 Free Software Foundation, Inc. + + This file contains the GNU History Library (History), a set of + routines for managing the text of previously typed lines. + + History is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + History is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with History. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* The goal is to make the implementation transparent, so that you + don't have to know what data types are used, just what functions + you can call. I think I have done that. */ +#define READLINE_LIBRARY + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#include <stdio.h> + +#if defined (HAVE_STDLIB_H) +# include <stdlib.h> +#else +# include "ansi_stdlib.h" +#endif /* HAVE_STDLIB_H */ + +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include <sys/types.h> +# endif +# include <unistd.h> +#endif + +#include "history.h" +#include "histlib.h" + +#include "xmalloc.h" + +/* How big to make the_history when we first allocate it. */ +#define DEFAULT_HISTORY_INITIAL_SIZE 502 + +/* The number of slots to increase the_history by. */ +#define DEFAULT_HISTORY_GROW_SIZE 50 + +static char *hist_inittime PARAMS((void)); + +/* **************************************************************** */ +/* */ +/* History Functions */ +/* */ +/* **************************************************************** */ + +/* An array of HIST_ENTRY. This is where we store the history. */ +static HIST_ENTRY **the_history = (HIST_ENTRY **)NULL; + +/* Non-zero means that we have enforced a limit on the amount of + history that we save. */ +static int history_stifled; + +/* The current number of slots allocated to the input_history. */ +static int history_size; + +/* If HISTORY_STIFLED is non-zero, then this is the maximum number of + entries to remember. */ +int history_max_entries; +int max_input_history; /* backwards compatibility */ + +/* The current location of the interactive history pointer. Just makes + life easier for outside callers. */ +int history_offset; + +/* The number of strings currently stored in the history list. */ +int history_length; + +/* The logical `base' of the history array. It defaults to 1. */ +int history_base = 1; + +/* Return the current HISTORY_STATE of the history. */ +HISTORY_STATE * +history_get_history_state () +{ + HISTORY_STATE *state; + + state = (HISTORY_STATE *)xmalloc (sizeof (HISTORY_STATE)); + state->entries = the_history; + state->offset = history_offset; + state->length = history_length; + state->size = history_size; + state->flags = 0; + if (history_stifled) + state->flags |= HS_STIFLED; + + return (state); +} + +/* Set the state of the current history array to STATE. */ +void +history_set_history_state (state) + HISTORY_STATE *state; +{ + the_history = state->entries; + history_offset = state->offset; + history_length = state->length; + history_size = state->size; + if (state->flags & HS_STIFLED) + history_stifled = 1; +} + +/* Begin a session in which the history functions might be used. This + initializes interactive variables. */ +void +using_history () +{ + history_offset = history_length; +} + +/* Return the number of bytes that the primary history entries are using. + This just adds up the lengths of the_history->lines and the associated + timestamps. */ +int +history_total_bytes () +{ + register int i, result; + + for (i = result = 0; the_history && the_history[i]; i++) + result += HISTENT_BYTES (the_history[i]); + + return (result); +} + +/* Returns the magic number which says what history element we are + looking at now. In this implementation, it returns history_offset. */ +int +where_history () +{ + return (history_offset); +} + +/* Make the current history item be the one at POS, an absolute index. + Returns zero if POS is out of range, else non-zero. */ +int +history_set_pos (pos) + int pos; +{ + if (pos > history_length || pos < 0 || !the_history) + return (0); + history_offset = pos; + return (1); +} + +/* Return the current history array. The caller has to be careful, since this + is the actual array of data, and could be bashed or made corrupt easily. + The array is terminated with a NULL pointer. */ +HIST_ENTRY ** +history_list () +{ + return (the_history); +} + +/* Return the history entry at the current position, as determined by + history_offset. If there is no entry there, return a NULL pointer. */ +HIST_ENTRY * +current_history () +{ + return ((history_offset == history_length) || the_history == 0) + ? (HIST_ENTRY *)NULL + : the_history[history_offset]; +} + +/* Back up history_offset to the previous history entry, and return + a pointer to that entry. If there is no previous entry then return + a NULL pointer. */ +HIST_ENTRY * +previous_history () +{ + return history_offset ? the_history[--history_offset] : (HIST_ENTRY *)NULL; +} + +/* Move history_offset forward to the next history entry, and return + a pointer to that entry. If there is no next entry then return a + NULL pointer. */ +HIST_ENTRY * +next_history () +{ + return (history_offset == history_length) ? (HIST_ENTRY *)NULL : the_history[++history_offset]; +} + +/* Return the history entry which is logically at OFFSET in the history array. + OFFSET is relative to history_base. */ +HIST_ENTRY * +history_get (offset) + int offset; +{ + int local_index; + + local_index = offset - history_base; + return (local_index >= history_length || local_index < 0 || the_history == 0) + ? (HIST_ENTRY *)NULL + : the_history[local_index]; +} + +HIST_ENTRY * +alloc_history_entry (string, ts) + char *string; + char *ts; +{ + HIST_ENTRY *temp; + + temp = (HIST_ENTRY *)xmalloc (sizeof (HIST_ENTRY)); + + temp->line = string ? savestring (string) : string; + temp->data = (char *)NULL; + temp->timestamp = ts; + + return temp; +} + +time_t +history_get_time (hist) + HIST_ENTRY *hist; +{ + char *ts; + time_t t; + + if (hist == 0 || hist->timestamp == 0) + return 0; + ts = hist->timestamp; + if (ts[0] != history_comment_char) + return 0; + t = (time_t) strtol (ts + 1, (char **)NULL, 10); /* XXX - should use strtol() here */ + return t; +} + +static char * +hist_inittime () +{ + time_t t; + char ts[64], *ret; + + t = (time_t) time ((time_t *)0); +#if defined (HAVE_VSNPRINTF) /* assume snprintf if vsnprintf exists */ + snprintf (ts, sizeof (ts) - 1, "X%lu", (unsigned long) t); +#else + sprintf (ts, "X%lu", (unsigned long) t); +#endif + ret = savestring (ts); + ret[0] = history_comment_char; + + return ret; +} + +/* Place STRING at the end of the history list. The data field + is set to NULL. */ +void +add_history (string) + const char *string; +{ + HIST_ENTRY *temp; + + if (history_stifled && (history_length == history_max_entries)) + { + register int i; + + /* If the history is stifled, and history_length is zero, + and it equals history_max_entries, we don't save items. */ + if (history_length == 0) + return; + + /* If there is something in the slot, then remove it. */ + if (the_history[0]) + (void) free_history_entry (the_history[0]); + + /* Copy the rest of the entries, moving down one slot. Copy includes + trailing NULL. */ +#if 0 + for (i = 0; i < history_length; i++) + the_history[i] = the_history[i + 1]; +#else + /* the_history + 1 because C89 compilers and newer automatically + add sizeof (the_history[0]) */ + memmove (the_history, the_history + 1, history_length * sizeof (HIST_ENTRY)); +#endif + + history_base++; + } + else + { + if (history_size == 0) + { + if (history_stifled && history_max_entries > 0) + history_size = history_max_entries + 2; + else + history_size = DEFAULT_HISTORY_INITIAL_SIZE; + the_history = (HIST_ENTRY **)xmalloc (history_size * sizeof (HIST_ENTRY *)); + history_length = 1; + } + else + { + if (history_length == (history_size - 1)) + { + history_size += DEFAULT_HISTORY_GROW_SIZE; + the_history = (HIST_ENTRY **) + xrealloc (the_history, history_size * sizeof (HIST_ENTRY *)); + } + history_length++; + } + } + + temp = alloc_history_entry (string, hist_inittime ()); + + the_history[history_length] = (HIST_ENTRY *)NULL; + the_history[history_length - 1] = temp; +} + +/* Change the time stamp of the most recent history entry to STRING. */ +void +add_history_time (string) + const char *string; +{ + HIST_ENTRY *hs; + + if (string == 0 || history_length < 1) + return; + hs = the_history[history_length - 1]; + FREE (hs->timestamp); + hs->timestamp = savestring (string); +} + +/* Free HIST and return the data so the calling application can free it + if necessary and desired. */ +histdata_t +free_history_entry (hist) + HIST_ENTRY *hist; +{ + histdata_t x; + + if (hist == 0) + return ((histdata_t) 0); + FREE (hist->line); + FREE (hist->timestamp); + x = hist->data; + xfree (hist); + return (x); +} + +HIST_ENTRY * +copy_history_entry (hist) + HIST_ENTRY *hist; +{ + HIST_ENTRY *ret; + char *ts; + + if (hist == 0) + return hist; + + ret = alloc_history_entry (hist->line, (char *)NULL); + + ts = hist->timestamp ? savestring (hist->timestamp) : hist->timestamp; + ret->timestamp = ts; + + ret->data = hist->data; + + return ret; +} + +/* Make the history entry at WHICH have LINE and DATA. This returns + the old entry so you can dispose of the data. In the case of an + invalid WHICH, a NULL pointer is returned. */ +HIST_ENTRY * +replace_history_entry (which, line, data) + int which; + const char *line; + histdata_t data; +{ + HIST_ENTRY *temp, *old_value; + + if (which < 0 || which >= history_length) + return ((HIST_ENTRY *)NULL); + + temp = (HIST_ENTRY *)xmalloc (sizeof (HIST_ENTRY)); + old_value = the_history[which]; + + temp->line = savestring (line); + temp->data = data; + temp->timestamp = savestring (old_value->timestamp); + the_history[which] = temp; + + return (old_value); +} + +/* Replace the DATA in the specified history entries, replacing OLD with + NEW. WHICH says which one(s) to replace: WHICH == -1 means to replace + all of the history entries where entry->data == OLD; WHICH == -2 means + to replace the `newest' history entry where entry->data == OLD; and + WHICH >= 0 means to replace that particular history entry's data, as + long as it matches OLD. */ +void +replace_history_data (which, old, new) + int which; + histdata_t *old, *new; +{ + HIST_ENTRY *entry; + register int i, last; + + if (which < -2 || which >= history_length || history_length == 0 || the_history == 0) + return; + + if (which >= 0) + { + entry = the_history[which]; + if (entry && entry->data == old) + entry->data = new; + return; + } + + last = -1; + for (i = 0; i < history_length; i++) + { + entry = the_history[i]; + if (entry == 0) + continue; + if (entry->data == old) + { + last = i; + if (which == -1) + entry->data = new; + } + } + if (which == -2 && last >= 0) + { + entry = the_history[last]; + entry->data = new; /* XXX - we don't check entry->old */ + } +} + +/* Remove history element WHICH from the history. The removed + element is returned to you so you can free the line, data, + and containing structure. */ +HIST_ENTRY * +remove_history (which) + int which; +{ + HIST_ENTRY *return_value; + register int i; + + if (which < 0 || which >= history_length || history_length == 0 || the_history == 0) + return ((HIST_ENTRY *)NULL); + + return_value = the_history[which]; + + for (i = which; i < history_length; i++) + the_history[i] = the_history[i + 1]; + + history_length--; + + return (return_value); +} + +/* Stifle the history list, remembering only MAX number of lines. */ +void +stifle_history (max) + int max; +{ + register int i, j; + + if (max < 0) + max = 0; + + if (history_length > max) + { + /* This loses because we cannot free the data. */ + for (i = 0, j = history_length - max; i < j; i++) + free_history_entry (the_history[i]); + + history_base = i; + for (j = 0, i = history_length - max; j < max; i++, j++) + the_history[j] = the_history[i]; + the_history[j] = (HIST_ENTRY *)NULL; + history_length = j; + } + + history_stifled = 1; + max_input_history = history_max_entries = max; +} + +/* Stop stifling the history. This returns the previous maximum + number of history entries. The value is positive if the history + was stifled, negative if it wasn't. */ +int +unstifle_history () +{ + if (history_stifled) + { + history_stifled = 0; + return (history_max_entries); + } + else + return (-history_max_entries); +} + +int +history_is_stifled () +{ + return (history_stifled); +} + +void +clear_history () +{ + register int i; + + /* This loses because we cannot free the data. */ + for (i = 0; i < history_length; i++) + { + free_history_entry (the_history[i]); + the_history[i] = (HIST_ENTRY *)NULL; + } + + history_offset = history_length = 0; +} diff --git a/lib/readline/readline.h b/lib/readline/readline.h index f2acf353..9565d9d5 100644 --- a/lib/readline/readline.h +++ b/lib/readline/readline.h @@ -390,6 +390,7 @@ extern int rl_show_char PARAMS((int)); /* Undocumented in texinfo manual. */ extern int rl_character_len PARAMS((int, int)); +extern void rl_redraw_prompt_last_line PARAMS((void)); /* Save and restore internal prompt redisplay information. */ extern void rl_save_prompt PARAMS((void)); diff --git a/lib/readline/readline.h~ b/lib/readline/readline.h~ new file mode 100644 index 00000000..3d6e6575 --- /dev/null +++ b/lib/readline/readline.h~ @@ -0,0 +1,934 @@ +/* Readline.h -- the names of functions callable from within readline. */ + +/* Copyright (C) 1987-2013 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Readline is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#if !defined (_READLINE_H_) +#define _READLINE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined (READLINE_LIBRARY) +# include "rlstdc.h" +# include "rltypedefs.h" +# include "keymaps.h" +# include "tilde.h" +#else +# include <readline/rlstdc.h> +# include <readline/rltypedefs.h> +# include <readline/keymaps.h> +# include <readline/tilde.h> +#endif + +/* Hex-encoded Readline version number. */ +#define RL_READLINE_VERSION 0x0603 /* Readline 6.3 */ +#define RL_VERSION_MAJOR 6 +#define RL_VERSION_MINOR 3 + +/* Readline data structures. */ + +/* Maintaining the state of undo. We remember individual deletes and inserts + on a chain of things to do. */ + +/* The actions that undo knows how to undo. Notice that UNDO_DELETE means + to insert some text, and UNDO_INSERT means to delete some text. I.e., + the code tells undo what to undo, not how to undo it. */ +enum undo_code { UNDO_DELETE, UNDO_INSERT, UNDO_BEGIN, UNDO_END }; + +/* What an element of THE_UNDO_LIST looks like. */ +typedef struct undo_list { + struct undo_list *next; + int start, end; /* Where the change took place. */ + char *text; /* The text to insert, if undoing a delete. */ + enum undo_code what; /* Delete, Insert, Begin, End. */ +} UNDO_LIST; + +/* The current undo list for RL_LINE_BUFFER. */ +extern UNDO_LIST *rl_undo_list; + +/* The data structure for mapping textual names to code addresses. */ +typedef struct _funmap { + const char *name; + rl_command_func_t *function; +} FUNMAP; + +extern FUNMAP **funmap; + +/* **************************************************************** */ +/* */ +/* Functions available to bind to key sequences */ +/* */ +/* **************************************************************** */ + +/* Bindable commands for numeric arguments. */ +extern int rl_digit_argument PARAMS((int, int)); +extern int rl_universal_argument PARAMS((int, int)); + +/* Bindable commands for moving the cursor. */ +extern int rl_forward_byte PARAMS((int, int)); +extern int rl_forward_char PARAMS((int, int)); +extern int rl_forward PARAMS((int, int)); +extern int rl_backward_byte PARAMS((int, int)); +extern int rl_backward_char PARAMS((int, int)); +extern int rl_backward PARAMS((int, int)); +extern int rl_beg_of_line PARAMS((int, int)); +extern int rl_end_of_line PARAMS((int, int)); +extern int rl_forward_word PARAMS((int, int)); +extern int rl_backward_word PARAMS((int, int)); +extern int rl_refresh_line PARAMS((int, int)); +extern int rl_clear_screen PARAMS((int, int)); +extern int rl_skip_csi_sequence PARAMS((int, int)); +extern int rl_arrow_keys PARAMS((int, int)); + +/* Bindable commands for inserting and deleting text. */ +extern int rl_insert PARAMS((int, int)); +extern int rl_quoted_insert PARAMS((int, int)); +extern int rl_tab_insert PARAMS((int, int)); +extern int rl_newline PARAMS((int, int)); +extern int rl_do_lowercase_version PARAMS((int, int)); +extern int rl_rubout PARAMS((int, int)); +extern int rl_delete PARAMS((int, int)); +extern int rl_rubout_or_delete PARAMS((int, int)); +extern int rl_delete_horizontal_space PARAMS((int, int)); +extern int rl_delete_or_show_completions PARAMS((int, int)); +extern int rl_insert_comment PARAMS((int, int)); + +/* Bindable commands for changing case. */ +extern int rl_upcase_word PARAMS((int, int)); +extern int rl_downcase_word PARAMS((int, int)); +extern int rl_capitalize_word PARAMS((int, int)); + +/* Bindable commands for transposing characters and words. */ +extern int rl_transpose_words PARAMS((int, int)); +extern int rl_transpose_chars PARAMS((int, int)); + +/* Bindable commands for searching within a line. */ +extern int rl_char_search PARAMS((int, int)); +extern int rl_backward_char_search PARAMS((int, int)); + +/* Bindable commands for readline's interface to the command history. */ +extern int rl_beginning_of_history PARAMS((int, int)); +extern int rl_end_of_history PARAMS((int, int)); +extern int rl_get_next_history PARAMS((int, int)); +extern int rl_get_previous_history PARAMS((int, int)); + +/* Bindable commands for managing the mark and region. */ +extern int rl_set_mark PARAMS((int, int)); +extern int rl_exchange_point_and_mark PARAMS((int, int)); + +/* Bindable commands to set the editing mode (emacs or vi). */ +extern int rl_vi_editing_mode PARAMS((int, int)); +extern int rl_emacs_editing_mode PARAMS((int, int)); + +/* Bindable commands to change the insert mode (insert or overwrite) */ +extern int rl_overwrite_mode PARAMS((int, int)); + +/* Bindable commands for managing key bindings. */ +extern int rl_re_read_init_file PARAMS((int, int)); +extern int rl_dump_functions PARAMS((int, int)); +extern int rl_dump_macros PARAMS((int, int)); +extern int rl_dump_variables PARAMS((int, int)); + +/* Bindable commands for word completion. */ +extern int rl_complete PARAMS((int, int)); +extern int rl_possible_completions PARAMS((int, int)); +extern int rl_insert_completions PARAMS((int, int)); +extern int rl_old_menu_complete PARAMS((int, int)); +extern int rl_menu_complete PARAMS((int, int)); +extern int rl_backward_menu_complete PARAMS((int, int)); + +/* Bindable commands for killing and yanking text, and managing the kill ring. */ +extern int rl_kill_word PARAMS((int, int)); +extern int rl_backward_kill_word PARAMS((int, int)); +extern int rl_kill_line PARAMS((int, int)); +extern int rl_backward_kill_line PARAMS((int, int)); +extern int rl_kill_full_line PARAMS((int, int)); +extern int rl_unix_word_rubout PARAMS((int, int)); +extern int rl_unix_filename_rubout PARAMS((int, int)); +extern int rl_unix_line_discard PARAMS((int, int)); +extern int rl_copy_region_to_kill PARAMS((int, int)); +extern int rl_kill_region PARAMS((int, int)); +extern int rl_copy_forward_word PARAMS((int, int)); +extern int rl_copy_backward_word PARAMS((int, int)); +extern int rl_yank PARAMS((int, int)); +extern int rl_yank_pop PARAMS((int, int)); +extern int rl_yank_nth_arg PARAMS((int, int)); +extern int rl_yank_last_arg PARAMS((int, int)); +extern int rl_bracketed_paste_begin PARAMS((int, int)); +/* Not available unless __CYGWIN__ is defined. */ +#ifdef __CYGWIN__ +extern int rl_paste_from_clipboard PARAMS((int, int)); +#endif + +/* Bindable commands for incremental searching. */ +extern int rl_reverse_search_history PARAMS((int, int)); +extern int rl_forward_search_history PARAMS((int, int)); + +/* Bindable keyboard macro commands. */ +extern int rl_start_kbd_macro PARAMS((int, int)); +extern int rl_end_kbd_macro PARAMS((int, int)); +extern int rl_call_last_kbd_macro PARAMS((int, int)); +extern int rl_print_last_kbd_macro PARAMS((int, int)); + +/* Bindable undo commands. */ +extern int rl_revert_line PARAMS((int, int)); +extern int rl_undo_command PARAMS((int, int)); + +/* Bindable tilde expansion commands. */ +extern int rl_tilde_expand PARAMS((int, int)); + +/* Bindable terminal control commands. */ +extern int rl_restart_output PARAMS((int, int)); +extern int rl_stop_output PARAMS((int, int)); + +/* Miscellaneous bindable commands. */ +extern int rl_abort PARAMS((int, int)); +extern int rl_tty_status PARAMS((int, int)); + +/* Bindable commands for incremental and non-incremental history searching. */ +extern int rl_history_search_forward PARAMS((int, int)); +extern int rl_history_search_backward PARAMS((int, int)); +extern int rl_history_substr_search_forward PARAMS((int, int)); +extern int rl_history_substr_search_backward PARAMS((int, int)); +extern int rl_noninc_forward_search PARAMS((int, int)); +extern int rl_noninc_reverse_search PARAMS((int, int)); +extern int rl_noninc_forward_search_again PARAMS((int, int)); +extern int rl_noninc_reverse_search_again PARAMS((int, int)); + +/* Bindable command used when inserting a matching close character. */ +extern int rl_insert_close PARAMS((int, int)); + +/* Not available unless READLINE_CALLBACKS is defined. */ +extern void rl_callback_handler_install PARAMS((const char *, rl_vcpfunc_t *)); +extern void rl_callback_read_char PARAMS((void)); +extern void rl_callback_handler_remove PARAMS((void)); + +/* Things for vi mode. Not available unless readline is compiled -DVI_MODE. */ +/* VI-mode bindable commands. */ +extern int rl_vi_redo PARAMS((int, int)); +extern int rl_vi_undo PARAMS((int, int)); +extern int rl_vi_yank_arg PARAMS((int, int)); +extern int rl_vi_fetch_history PARAMS((int, int)); +extern int rl_vi_search_again PARAMS((int, int)); +extern int rl_vi_search PARAMS((int, int)); +extern int rl_vi_complete PARAMS((int, int)); +extern int rl_vi_tilde_expand PARAMS((int, int)); +extern int rl_vi_prev_word PARAMS((int, int)); +extern int rl_vi_next_word PARAMS((int, int)); +extern int rl_vi_end_word PARAMS((int, int)); +extern int rl_vi_insert_beg PARAMS((int, int)); +extern int rl_vi_append_mode PARAMS((int, int)); +extern int rl_vi_append_eol PARAMS((int, int)); +extern int rl_vi_eof_maybe PARAMS((int, int)); +extern int rl_vi_insertion_mode PARAMS((int, int)); +extern int rl_vi_insert_mode PARAMS((int, int)); +extern int rl_vi_movement_mode PARAMS((int, int)); +extern int rl_vi_arg_digit PARAMS((int, int)); +extern int rl_vi_change_case PARAMS((int, int)); +extern int rl_vi_put PARAMS((int, int)); +extern int rl_vi_column PARAMS((int, int)); +extern int rl_vi_delete_to PARAMS((int, int)); +extern int rl_vi_change_to PARAMS((int, int)); +extern int rl_vi_yank_to PARAMS((int, int)); +extern int rl_vi_rubout PARAMS((int, int)); +extern int rl_vi_delete PARAMS((int, int)); +extern int rl_vi_back_to_indent PARAMS((int, int)); +extern int rl_vi_first_print PARAMS((int, int)); +extern int rl_vi_char_search PARAMS((int, int)); +extern int rl_vi_match PARAMS((int, int)); +extern int rl_vi_change_char PARAMS((int, int)); +extern int rl_vi_subst PARAMS((int, int)); +extern int rl_vi_overstrike PARAMS((int, int)); +extern int rl_vi_overstrike_delete PARAMS((int, int)); +extern int rl_vi_replace PARAMS((int, int)); +extern int rl_vi_set_mark PARAMS((int, int)); +extern int rl_vi_goto_mark PARAMS((int, int)); + +/* VI-mode utility functions. */ +extern int rl_vi_check PARAMS((void)); +extern int rl_vi_domove PARAMS((int, int *)); +extern int rl_vi_bracktype PARAMS((int)); + +extern void rl_vi_start_inserting PARAMS((int, int, int)); + +/* VI-mode pseudo-bindable commands, used as utility functions. */ +extern int rl_vi_fWord PARAMS((int, int)); +extern int rl_vi_bWord PARAMS((int, int)); +extern int rl_vi_eWord PARAMS((int, int)); +extern int rl_vi_fword PARAMS((int, int)); +extern int rl_vi_bword PARAMS((int, int)); +extern int rl_vi_eword PARAMS((int, int)); + +/* **************************************************************** */ +/* */ +/* Well Published Functions */ +/* */ +/* **************************************************************** */ + +/* Readline functions. */ +/* Read a line of input. Prompt with PROMPT. A NULL PROMPT means none. */ +extern char *readline PARAMS((const char *)); + +extern int rl_set_prompt PARAMS((const char *)); +extern int rl_expand_prompt PARAMS((char *)); + +extern int rl_initialize PARAMS((void)); + +/* Undocumented; unused by readline */ +extern int rl_discard_argument PARAMS((void)); + +/* Utility functions to bind keys to readline commands. */ +extern int rl_add_defun PARAMS((const char *, rl_command_func_t *, int)); +extern int rl_bind_key PARAMS((int, rl_command_func_t *)); +extern int rl_bind_key_in_map PARAMS((int, rl_command_func_t *, Keymap)); +extern int rl_unbind_key PARAMS((int)); +extern int rl_unbind_key_in_map PARAMS((int, Keymap)); +extern int rl_bind_key_if_unbound PARAMS((int, rl_command_func_t *)); +extern int rl_bind_key_if_unbound_in_map PARAMS((int, rl_command_func_t *, Keymap)); +extern int rl_unbind_function_in_map PARAMS((rl_command_func_t *, Keymap)); +extern int rl_unbind_command_in_map PARAMS((const char *, Keymap)); +extern int rl_bind_keyseq PARAMS((const char *, rl_command_func_t *)); +extern int rl_bind_keyseq_in_map PARAMS((const char *, rl_command_func_t *, Keymap)); +extern int rl_bind_keyseq_if_unbound PARAMS((const char *, rl_command_func_t *)); +extern int rl_bind_keyseq_if_unbound_in_map PARAMS((const char *, rl_command_func_t *, Keymap)); +extern int rl_generic_bind PARAMS((int, const char *, char *, Keymap)); + +extern char *rl_variable_value PARAMS((const char *)); +extern int rl_variable_bind PARAMS((const char *, const char *)); + +/* Backwards compatibility, use rl_bind_keyseq_in_map instead. */ +extern int rl_set_key PARAMS((const char *, rl_command_func_t *, Keymap)); + +/* Backwards compatibility, use rl_generic_bind instead. */ +extern int rl_macro_bind PARAMS((const char *, const char *, Keymap)); + +/* Undocumented in the texinfo manual; not really useful to programs. */ +extern int rl_translate_keyseq PARAMS((const char *, char *, int *)); +extern char *rl_untranslate_keyseq PARAMS((int)); + +extern rl_command_func_t *rl_named_function PARAMS((const char *)); +extern rl_command_func_t *rl_function_of_keyseq PARAMS((const char *, Keymap, int *)); + +extern void rl_list_funmap_names PARAMS((void)); +extern char **rl_invoking_keyseqs_in_map PARAMS((rl_command_func_t *, Keymap)); +extern char **rl_invoking_keyseqs PARAMS((rl_command_func_t *)); + +extern void rl_function_dumper PARAMS((int)); +extern void rl_macro_dumper PARAMS((int)); +extern void rl_variable_dumper PARAMS((int)); + +extern int rl_read_init_file PARAMS((const char *)); +extern int rl_parse_and_bind PARAMS((char *)); + +/* Functions for manipulating keymaps. */ +extern Keymap rl_make_bare_keymap PARAMS((void)); +extern Keymap rl_copy_keymap PARAMS((Keymap)); +extern Keymap rl_make_keymap PARAMS((void)); +extern void rl_discard_keymap PARAMS((Keymap)); +extern void rl_free_keymap PARAMS((Keymap)); + +extern Keymap rl_get_keymap_by_name PARAMS((const char *)); +extern char *rl_get_keymap_name PARAMS((Keymap)); +extern void rl_set_keymap PARAMS((Keymap)); +extern Keymap rl_get_keymap PARAMS((void)); +/* Undocumented; used internally only. */ +extern void rl_set_keymap_from_edit_mode PARAMS((void)); +extern char *rl_get_keymap_name_from_edit_mode PARAMS((void)); + +/* Functions for manipulating the funmap, which maps command names to functions. */ +extern int rl_add_funmap_entry PARAMS((const char *, rl_command_func_t *)); +extern const char **rl_funmap_names PARAMS((void)); +/* Undocumented, only used internally -- there is only one funmap, and this + function may be called only once. */ +extern void rl_initialize_funmap PARAMS((void)); + +/* Utility functions for managing keyboard macros. */ +extern void rl_push_macro_input PARAMS((char *)); + +/* Functions for undoing, from undo.c */ +extern void rl_add_undo PARAMS((enum undo_code, int, int, char *)); +extern void rl_free_undo_list PARAMS((void)); +extern int rl_do_undo PARAMS((void)); +extern int rl_begin_undo_group PARAMS((void)); +extern int rl_end_undo_group PARAMS((void)); +extern int rl_modifying PARAMS((int, int)); + +/* Functions for redisplay. */ +extern void rl_redisplay PARAMS((void)); +extern int rl_on_new_line PARAMS((void)); +extern int rl_on_new_line_with_prompt PARAMS((void)); +extern int rl_forced_update_display PARAMS((void)); +extern int rl_clear_message PARAMS((void)); +extern int rl_reset_line_state PARAMS((void)); +extern int rl_crlf PARAMS((void)); + +#if defined (USE_VARARGS) && defined (PREFER_STDARG) +extern int rl_message (const char *, ...) __attribute__((__format__ (printf, 1, 2))); +#else +extern int rl_message (); +#endif + +extern int rl_show_char PARAMS((int)); + +/* Undocumented in texinfo manual. */ +extern int rl_character_len PARAMS((int, int)); +extern int rl_redraw_prompt_last_line PARAMS((void)); + +/* Save and restore internal prompt redisplay information. */ +extern void rl_save_prompt PARAMS((void)); +extern void rl_restore_prompt PARAMS((void)); + +/* Modifying text. */ +extern void rl_replace_line PARAMS((const char *, int)); +extern int rl_insert_text PARAMS((const char *)); +extern int rl_delete_text PARAMS((int, int)); +extern int rl_kill_text PARAMS((int, int)); +extern char *rl_copy_text PARAMS((int, int)); + +/* Terminal and tty mode management. */ +extern void rl_prep_terminal PARAMS((int)); +extern void rl_deprep_terminal PARAMS((void)); +extern void rl_tty_set_default_bindings PARAMS((Keymap)); +extern void rl_tty_unset_default_bindings PARAMS((Keymap)); + +extern int rl_reset_terminal PARAMS((const char *)); +extern void rl_resize_terminal PARAMS((void)); +extern void rl_set_screen_size PARAMS((int, int)); +extern void rl_get_screen_size PARAMS((int *, int *)); +extern void rl_reset_screen_size PARAMS((void)); + +extern char *rl_get_termcap PARAMS((const char *)); + +/* Functions for character input. */ +extern int rl_stuff_char PARAMS((int)); +extern int rl_execute_next PARAMS((int)); +extern int rl_clear_pending_input PARAMS((void)); +extern int rl_read_key PARAMS((void)); +extern int rl_getc PARAMS((FILE *)); +extern int rl_set_keyboard_input_timeout PARAMS((int)); + +/* `Public' utility functions . */ +extern void rl_extend_line_buffer PARAMS((int)); +extern int rl_ding PARAMS((void)); +extern int rl_alphabetic PARAMS((int)); +extern void rl_free PARAMS((void *)); + +/* Readline signal handling, from signals.c */ +extern int rl_set_signals PARAMS((void)); +extern int rl_clear_signals PARAMS((void)); +extern void rl_cleanup_after_signal PARAMS((void)); +extern void rl_reset_after_signal PARAMS((void)); +extern void rl_free_line_state PARAMS((void)); + +extern void rl_echo_signal_char PARAMS((int)); + +extern int rl_set_paren_blink_timeout PARAMS((int)); + +/* History management functions. */ + +extern void rl_clear_history PARAMS((void)); + +/* Undocumented. */ +extern int rl_maybe_save_line PARAMS((void)); +extern int rl_maybe_unsave_line PARAMS((void)); +extern int rl_maybe_replace_line PARAMS((void)); + +/* Completion functions. */ +extern int rl_complete_internal PARAMS((int)); +extern void rl_display_match_list PARAMS((char **, int, int)); + +extern char **rl_completion_matches PARAMS((const char *, rl_compentry_func_t *)); +extern char *rl_username_completion_function PARAMS((const char *, int)); +extern char *rl_filename_completion_function PARAMS((const char *, int)); + +extern int rl_completion_mode PARAMS((rl_command_func_t *)); + +#if 0 +/* Backwards compatibility (compat.c). These will go away sometime. */ +extern void free_undo_list PARAMS((void)); +extern int maybe_save_line PARAMS((void)); +extern int maybe_unsave_line PARAMS((void)); +extern int maybe_replace_line PARAMS((void)); + +extern int ding PARAMS((void)); +extern int alphabetic PARAMS((int)); +extern int crlf PARAMS((void)); + +extern char **completion_matches PARAMS((char *, rl_compentry_func_t *)); +extern char *username_completion_function PARAMS((const char *, int)); +extern char *filename_completion_function PARAMS((const char *, int)); +#endif + +/* **************************************************************** */ +/* */ +/* Well Published Variables */ +/* */ +/* **************************************************************** */ + +/* The version of this incarnation of the readline library. */ +extern const char *rl_library_version; /* e.g., "4.2" */ +extern int rl_readline_version; /* e.g., 0x0402 */ + +/* True if this is real GNU readline. */ +extern int rl_gnu_readline_p; + +/* Flags word encapsulating the current readline state. */ +extern int rl_readline_state; + +/* Says which editing mode readline is currently using. 1 means emacs mode; + 0 means vi mode. */ +extern int rl_editing_mode; + +/* Insert or overwrite mode for emacs mode. 1 means insert mode; 0 means + overwrite mode. Reset to insert mode on each input line. */ +extern int rl_insert_mode; + +/* The name of the calling program. You should initialize this to + whatever was in argv[0]. It is used when parsing conditionals. */ +extern const char *rl_readline_name; + +/* The prompt readline uses. This is set from the argument to + readline (), and should not be assigned to directly. */ +extern char *rl_prompt; + +/* The prompt string that is actually displayed by rl_redisplay. Public so + applications can more easily supply their own redisplay functions. */ +extern char *rl_display_prompt; + +/* The line buffer that is in use. */ +extern char *rl_line_buffer; + +/* The location of point, and end. */ +extern int rl_point; +extern int rl_end; + +/* The mark, or saved cursor position. */ +extern int rl_mark; + +/* Flag to indicate that readline has finished with the current input + line and should return it. */ +extern int rl_done; + +/* If set to a character value, that will be the next keystroke read. */ +extern int rl_pending_input; + +/* Non-zero if we called this function from _rl_dispatch(). It's present + so functions can find out whether they were called from a key binding + or directly from an application. */ +extern int rl_dispatching; + +/* Non-zero if the user typed a numeric argument before executing the + current function. */ +extern int rl_explicit_arg; + +/* The current value of the numeric argument specified by the user. */ +extern int rl_numeric_arg; + +/* The address of the last command function Readline executed. */ +extern rl_command_func_t *rl_last_func; + +/* The name of the terminal to use. */ +extern const char *rl_terminal_name; + +/* The input and output streams. */ +extern FILE *rl_instream; +extern FILE *rl_outstream; + +/* If non-zero, Readline gives values of LINES and COLUMNS from the environment + greater precedence than values fetched from the kernel when computing the + screen dimensions. */ +extern int rl_prefer_env_winsize; + +/* If non-zero, then this is the address of a function to call just + before readline_internal () prints the first prompt. */ +extern rl_hook_func_t *rl_startup_hook; + +/* If non-zero, this is the address of a function to call just before + readline_internal_setup () returns and readline_internal starts + reading input characters. */ +extern rl_hook_func_t *rl_pre_input_hook; + +/* The address of a function to call periodically while Readline is + awaiting character input, or NULL, for no event handling. */ +extern rl_hook_func_t *rl_event_hook; + +/* The address of a function to call if a read is interrupted by a signal. */ +extern rl_hook_func_t *rl_signal_event_hook; + +/* The address of a function to call if Readline needs to know whether or not + there is data available from the current input source. */ +extern rl_hook_func_t *rl_input_available_hook; + +/* The address of the function to call to fetch a character from the current + Readline input stream */ +extern rl_getc_func_t *rl_getc_function; + +extern rl_voidfunc_t *rl_redisplay_function; + +extern rl_vintfunc_t *rl_prep_term_function; +extern rl_voidfunc_t *rl_deprep_term_function; + +/* Dispatch variables. */ +extern Keymap rl_executing_keymap; +extern Keymap rl_binding_keymap; + +extern int rl_executing_key; +extern char *rl_executing_keyseq; +extern int rl_key_sequence_length; + +/* Display variables. */ +/* If non-zero, readline will erase the entire line, including any prompt, + if the only thing typed on an otherwise-blank line is something bound to + rl_newline. */ +extern int rl_erase_empty_line; + +/* If non-zero, the application has already printed the prompt (rl_prompt) + before calling readline, so readline should not output it the first time + redisplay is done. */ +extern int rl_already_prompted; + +/* A non-zero value means to read only this many characters rather than + up to a character bound to accept-line. */ +extern int rl_num_chars_to_read; + +/* The text of a currently-executing keyboard macro. */ +extern char *rl_executing_macro; + +/* Variables to control readline signal handling. */ +/* If non-zero, readline will install its own signal handlers for + SIGINT, SIGTERM, SIGQUIT, SIGALRM, SIGTSTP, SIGTTIN, and SIGTTOU. */ +extern int rl_catch_signals; + +/* If non-zero, readline will install a signal handler for SIGWINCH + that also attempts to call any calling application's SIGWINCH signal + handler. Note that the terminal is not cleaned up before the + application's signal handler is called; use rl_cleanup_after_signal() + to do that. */ +extern int rl_catch_sigwinch; + +/* If non-zero, the readline SIGWINCH handler will modify LINES and + COLUMNS in the environment. */ +extern int rl_change_environment; + +/* Completion variables. */ +/* Pointer to the generator function for completion_matches (). + NULL means to use rl_filename_completion_function (), the default + filename completer. */ +extern rl_compentry_func_t *rl_completion_entry_function; + +/* Optional generator for menu completion. Default is + rl_completion_entry_function (rl_filename_completion_function). */ + extern rl_compentry_func_t *rl_menu_completion_entry_function; + +/* If rl_ignore_some_completions_function is non-NULL it is the address + of a function to call after all of the possible matches have been + generated, but before the actual completion is done to the input line. + The function is called with one argument; a NULL terminated array + of (char *). If your function removes any of the elements, they + must be free()'ed. */ +extern rl_compignore_func_t *rl_ignore_some_completions_function; + +/* Pointer to alternative function to create matches. + Function is called with TEXT, START, and END. + START and END are indices in RL_LINE_BUFFER saying what the boundaries + of TEXT are. + If this function exists and returns NULL then call the value of + rl_completion_entry_function to try to match, otherwise use the + array of strings returned. */ +extern rl_completion_func_t *rl_attempted_completion_function; + +/* The basic list of characters that signal a break between words for the + completer routine. The initial contents of this variable is what + breaks words in the shell, i.e. "n\"\\'`@$>". */ +extern const char *rl_basic_word_break_characters; + +/* The list of characters that signal a break between words for + rl_complete_internal. The default list is the contents of + rl_basic_word_break_characters. */ +extern /*const*/ char *rl_completer_word_break_characters; + +/* Hook function to allow an application to set the completion word + break characters before readline breaks up the line. Allows + position-dependent word break characters. */ +extern rl_cpvfunc_t *rl_completion_word_break_hook; + +/* List of characters which can be used to quote a substring of the line. + Completion occurs on the entire substring, and within the substring + rl_completer_word_break_characters are treated as any other character, + unless they also appear within this list. */ +extern const char *rl_completer_quote_characters; + +/* List of quote characters which cause a word break. */ +extern const char *rl_basic_quote_characters; + +/* List of characters that need to be quoted in filenames by the completer. */ +extern const char *rl_filename_quote_characters; + +/* List of characters that are word break characters, but should be left + in TEXT when it is passed to the completion function. The shell uses + this to help determine what kind of completing to do. */ +extern const char *rl_special_prefixes; + +/* If non-zero, then this is the address of a function to call when + completing on a directory name. The function is called with + the address of a string (the current directory name) as an arg. It + changes what is displayed when the possible completions are printed + or inserted. The directory completion hook should perform + any necessary dequoting. This function should return 1 if it modifies + the directory name pointer passed as an argument. If the directory + completion hook returns 0, it should not modify the directory name + pointer passed as an argument. */ +extern rl_icppfunc_t *rl_directory_completion_hook; + +/* If non-zero, this is the address of a function to call when completing + a directory name. This function takes the address of the directory name + to be modified as an argument. Unlike rl_directory_completion_hook, it + only modifies the directory name used in opendir(2), not what is displayed + when the possible completions are printed or inserted. If set, it takes + precedence over rl_directory_completion_hook. The directory rewrite + hook should perform any necessary dequoting. This function has the same + return value properties as the directory_completion_hook. + + I'm not happy with how this works yet, so it's undocumented. I'm trying + it in bash to see how well it goes. */ +extern rl_icppfunc_t *rl_directory_rewrite_hook; + +/* If non-zero, this is the address of a function for the completer to call + before deciding which character to append to a completed name. It should + modify the directory name passed as an argument if appropriate, and return + non-zero if it modifies the name. This should not worry about dequoting + the filename; that has already happened by the time it gets here. */ +extern rl_icppfunc_t *rl_filename_stat_hook; + +/* If non-zero, this is the address of a function to call when reading + directory entries from the filesystem for completion and comparing + them to the partial word to be completed. The function should + either return its first argument (if no conversion takes place) or + newly-allocated memory. This can, for instance, convert filenames + between character sets for comparison against what's typed at the + keyboard. The returned value is what is added to the list of + matches. The second argument is the length of the filename to be + converted. */ +extern rl_dequote_func_t *rl_filename_rewrite_hook; + +/* Backwards compatibility with previous versions of readline. */ +#define rl_symbolic_link_hook rl_directory_completion_hook + +/* If non-zero, then this is the address of a function to call when + completing a word would normally display the list of possible matches. + This function is called instead of actually doing the display. + It takes three arguments: (char **matches, int num_matches, int max_length) + where MATCHES is the array of strings that matched, NUM_MATCHES is the + number of strings in that array, and MAX_LENGTH is the length of the + longest string in that array. */ +extern rl_compdisp_func_t *rl_completion_display_matches_hook; + +/* Non-zero means that the results of the matches are to be treated + as filenames. This is ALWAYS zero on entry, and can only be changed + within a completion entry finder function. */ +extern int rl_filename_completion_desired; + +/* Non-zero means that the results of the matches are to be quoted using + double quotes (or an application-specific quoting mechanism) if the + filename contains any characters in rl_word_break_chars. This is + ALWAYS non-zero on entry, and can only be changed within a completion + entry finder function. */ +extern int rl_filename_quoting_desired; + +/* Set to a function to quote a filename in an application-specific fashion. + Called with the text to quote, the type of match found (single or multiple) + and a pointer to the quoting character to be used, which the function can + reset if desired. */ +extern rl_quote_func_t *rl_filename_quoting_function; + +/* Function to call to remove quoting characters from a filename. Called + before completion is attempted, so the embedded quotes do not interfere + with matching names in the file system. */ +extern rl_dequote_func_t *rl_filename_dequoting_function; + +/* Function to call to decide whether or not a word break character is + quoted. If a character is quoted, it does not break words for the + completer. */ +extern rl_linebuf_func_t *rl_char_is_quoted_p; + +/* Non-zero means to suppress normal filename completion after the + user-specified completion function has been called. */ +extern int rl_attempted_completion_over; + +/* Set to a character describing the type of completion being attempted by + rl_complete_internal; available for use by application completion + functions. */ +extern int rl_completion_type; + +/* Set to the last key used to invoke one of the completion functions */ +extern int rl_completion_invoking_key; + +/* Up to this many items will be displayed in response to a + possible-completions call. After that, we ask the user if she + is sure she wants to see them all. The default value is 100. */ +extern int rl_completion_query_items; + +/* Character appended to completed words when at the end of the line. The + default is a space. Nothing is added if this is '\0'. */ +extern int rl_completion_append_character; + +/* If set to non-zero by an application completion function, + rl_completion_append_character will not be appended. */ +extern int rl_completion_suppress_append; + +/* Set to any quote character readline thinks it finds before any application + completion function is called. */ +extern int rl_completion_quote_character; + +/* Set to a non-zero value if readline found quoting anywhere in the word to + be completed; set before any application completion function is called. */ +extern int rl_completion_found_quote; + +/* If non-zero, the completion functions don't append any closing quote. + This is set to 0 by rl_complete_internal and may be changed by an + application-specific completion function. */ +extern int rl_completion_suppress_quote; + +/* If non-zero, readline will sort the completion matches. On by default. */ +extern int rl_sort_completion_matches; + +/* If non-zero, a slash will be appended to completed filenames that are + symbolic links to directory names, subject to the value of the + mark-directories variable (which is user-settable). This exists so + that application completion functions can override the user's preference + (set via the mark-symlinked-directories variable) if appropriate. + It's set to the value of _rl_complete_mark_symlink_dirs in + rl_complete_internal before any application-specific completion + function is called, so without that function doing anything, the user's + preferences are honored. */ +extern int rl_completion_mark_symlink_dirs; + +/* If non-zero, then disallow duplicates in the matches. */ +extern int rl_ignore_completion_duplicates; + +/* If this is non-zero, completion is (temporarily) inhibited, and the + completion character will be inserted as any other. */ +extern int rl_inhibit_completion; + +/* Input error; can be returned by (*rl_getc_function) if readline is reading + a top-level command (RL_ISSTATE (RL_STATE_READCMD)). */ +#define READERR (-2) + +/* Definitions available for use by readline clients. */ +#define RL_PROMPT_START_IGNORE '\001' +#define RL_PROMPT_END_IGNORE '\002' + +/* Possible values for do_replace argument to rl_filename_quoting_function, + called by rl_complete_internal. */ +#define NO_MATCH 0 +#define SINGLE_MATCH 1 +#define MULT_MATCH 2 + +/* Possible state values for rl_readline_state */ +#define RL_STATE_NONE 0x000000 /* no state; before first call */ + +#define RL_STATE_INITIALIZING 0x0000001 /* initializing */ +#define RL_STATE_INITIALIZED 0x0000002 /* initialization done */ +#define RL_STATE_TERMPREPPED 0x0000004 /* terminal is prepped */ +#define RL_STATE_READCMD 0x0000008 /* reading a command key */ +#define RL_STATE_METANEXT 0x0000010 /* reading input after ESC */ +#define RL_STATE_DISPATCHING 0x0000020 /* dispatching to a command */ +#define RL_STATE_MOREINPUT 0x0000040 /* reading more input in a command function */ +#define RL_STATE_ISEARCH 0x0000080 /* doing incremental search */ +#define RL_STATE_NSEARCH 0x0000100 /* doing non-inc search */ +#define RL_STATE_SEARCH 0x0000200 /* doing a history search */ +#define RL_STATE_NUMERICARG 0x0000400 /* reading numeric argument */ +#define RL_STATE_MACROINPUT 0x0000800 /* getting input from a macro */ +#define RL_STATE_MACRODEF 0x0001000 /* defining keyboard macro */ +#define RL_STATE_OVERWRITE 0x0002000 /* overwrite mode */ +#define RL_STATE_COMPLETING 0x0004000 /* doing completion */ +#define RL_STATE_SIGHANDLER 0x0008000 /* in readline sighandler */ +#define RL_STATE_UNDOING 0x0010000 /* doing an undo */ +#define RL_STATE_INPUTPENDING 0x0020000 /* rl_execute_next called */ +#define RL_STATE_TTYCSAVED 0x0040000 /* tty special chars saved */ +#define RL_STATE_CALLBACK 0x0080000 /* using the callback interface */ +#define RL_STATE_VIMOTION 0x0100000 /* reading vi motion arg */ +#define RL_STATE_MULTIKEY 0x0200000 /* reading multiple-key command */ +#define RL_STATE_VICMDONCE 0x0400000 /* entered vi command mode at least once */ +#define RL_STATE_REDISPLAYING 0x0800000 /* updating terminal display */ + +#define RL_STATE_DONE 0x1000000 /* done; accepted line */ + +#define RL_SETSTATE(x) (rl_readline_state |= (x)) +#define RL_UNSETSTATE(x) (rl_readline_state &= ~(x)) +#define RL_ISSTATE(x) (rl_readline_state & (x)) + +struct readline_state { + /* line state */ + int point; + int end; + int mark; + char *buffer; + int buflen; + UNDO_LIST *ul; + char *prompt; + + /* global state */ + int rlstate; + int done; + Keymap kmap; + + /* input state */ + rl_command_func_t *lastfunc; + int insmode; + int edmode; + char *kseq; + int kseqlen; + FILE *inf; + FILE *outf; + int pendingin; + char *macro; + + /* signal state */ + int catchsigs; + int catchsigwinch; + + /* search state */ + + /* completion state */ + rl_compentry_func_t *entryfunc; + rl_compentry_func_t *menuentryfunc; + rl_compentry_func_t *ignorefunc; + rl_compentry_func_t *attemptfunc; + char *wordbreakchars; + + + /* options state */ + + /* hook state */ + + /* reserved for future expansion, so the struct size doesn't change */ + char reserved[64]; +}; + +extern int rl_save_state PARAMS((struct readline_state *)); +extern int rl_restore_state PARAMS((struct readline_state *)); + +#ifdef __cplusplus +} +#endif + +#endif /* _READLINE_H_ */ diff --git a/lib/readline/rlconf.h b/lib/readline/rlconf.h index 1a40afce..fa528355 100644 --- a/lib/readline/rlconf.h +++ b/lib/readline/rlconf.h @@ -66,4 +66,14 @@ tty auditing system. */ #define ENABLE_TTY_AUDIT_SUPPORT +/* Defaults for the various editing mode indicators, inserted at the beginning + of the last (maybe only) line of the prompt if show-mode-in-prompt is on */ +#define RL_EMACS_MODESTR_DEFAULT "@" +#define RL_EMACS_MODESTR_DEFLEN 1 + +#define RL_VI_INS_MODESTR_DEFAULT "(ins)" +#define RL_VI_INS_MODESTR_DEFLEN 5 +#define RL_VI_CMD_MODESTR_DEFAULT "(cmd)" +#define RL_VI_CMD_MODESTR_DEFLEN 5 + #endif /* _RLCONF_H_ */ diff --git a/lib/readline/rlconf.h~ b/lib/readline/rlconf.h~ new file mode 100644 index 00000000..1a40afce --- /dev/null +++ b/lib/readline/rlconf.h~ @@ -0,0 +1,69 @@ +/* rlconf.h -- readline configuration definitions */ + +/* Copyright (C) 1992-2012 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Readline is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#if !defined (_RLCONF_H_) +#define _RLCONF_H_ + +/* Define this if you want the vi-mode editing available. */ +#define VI_MODE + +/* Define this to get an indication of file type when listing completions. */ +#define VISIBLE_STATS + +/* Define this to get support for colors when listing completions and in + other places. */ +#define COLOR_SUPPORT + +/* This definition is needed by readline.c, rltty.c, and signals.c. */ +/* If on, then readline handles signals in a way that doesn't suck. */ +#define HANDLE_SIGNALS + +/* Ugly but working hack for binding prefix meta. */ +#define PREFIX_META_HACK + +/* The next-to-last-ditch effort file name for a user-specific init file. */ +#define DEFAULT_INPUTRC "~/.inputrc" + +/* The ultimate last-ditch filenname for an init file -- system-wide. */ +#define SYS_INPUTRC "/etc/inputrc" + +/* If defined, expand tabs to spaces. */ +#define DISPLAY_TABS + +/* If defined, use the terminal escape sequence to move the cursor forward + over a character when updating the line rather than rewriting it. */ +/* #define HACK_TERMCAP_MOTION */ + +/* The string inserted by the `insert comment' command. */ +#define RL_COMMENT_BEGIN_DEFAULT "#" + +/* Define this if you want code that allows readline to be used in an + X `callback' style. */ +#define READLINE_CALLBACKS + +/* Define this if you want the cursor to indicate insert or overwrite mode. */ +/* #define CURSOR_MODE */ + +/* Define this if you want to enable code that talks to the Linux kernel + tty auditing system. */ +#define ENABLE_TTY_AUDIT_SUPPORT + +#endif /* _RLCONF_H_ */ diff --git a/lib/readline/rlprivate.h b/lib/readline/rlprivate.h index 63e2051f..eb6e3f34 100644 --- a/lib/readline/rlprivate.h +++ b/lib/readline/rlprivate.h @@ -457,6 +457,13 @@ extern int _rl_last_c_pos; extern int _rl_suppress_redisplay; extern int _rl_want_redisplay; +extern char *_rl_emacs_mode_str; +extern int _rl_emacs_modestr_len; +extern char *_rl_vi_ins_mode_str; +extern int _rl_vi_ins_modestr_len; +extern char *_rl_vi_cmd_mode_str; +extern int _rl_vi_cmd_modestr_len; + /* isearch.c */ extern char *_rl_isearch_terminators; diff --git a/lib/readline/rlprivate.h~ b/lib/readline/rlprivate.h~ new file mode 100644 index 00000000..63e2051f --- /dev/null +++ b/lib/readline/rlprivate.h~ @@ -0,0 +1,547 @@ +/* rlprivate.h -- functions and variables global to the readline library, + but not intended for use by applications. */ + +/* Copyright (C) 1999-2012 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Readline is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#if !defined (_RL_PRIVATE_H_) +#define _RL_PRIVATE_H_ + +#include "rlconf.h" /* for VISIBLE_STATS */ +#include "rlstdc.h" +#include "posixjmp.h" /* defines procenv_t */ + +/************************************************************************* + * * + * Convenience definitions * + * * + *************************************************************************/ + +#define EMACS_MODE() (rl_editing_mode == emacs_mode) +#define VI_COMMAND_MODE() (rl_editing_mode == vi_mode && _rl_keymap == vi_movement_keymap) +#define VI_INSERT_MODE() (rl_editing_mode == vi_mode && _rl_keymap == vi_insertion_keymap) + +#define RL_CHECK_SIGNALS() \ + do { \ + if (_rl_caught_signal) _rl_signal_handler (_rl_caught_signal); \ + } while (0) + +#define RL_SIG_RECEIVED() (_rl_caught_signal != 0) +#define RL_SIGINT_RECEIVED() (_rl_caught_signal == SIGINT) + +#define CUSTOM_REDISPLAY_FUNC() (rl_redisplay_function != rl_redisplay) +#define CUSTOM_INPUT_FUNC() (rl_getc_function != rl_getc) + +/************************************************************************* + * * + * Global structs undocumented in texinfo manual and not in readline.h * + * * + *************************************************************************/ +/* search types */ +#define RL_SEARCH_ISEARCH 0x01 /* incremental search */ +#define RL_SEARCH_NSEARCH 0x02 /* non-incremental search */ +#define RL_SEARCH_CSEARCH 0x04 /* intra-line char search */ + +/* search flags */ +#define SF_REVERSE 0x01 +#define SF_FOUND 0x02 +#define SF_FAILED 0x04 +#define SF_CHGKMAP 0x08 + +typedef struct __rl_search_context +{ + int type; + int sflags; + + char *search_string; + int search_string_index; + int search_string_size; + + char **lines; + char *allocated_line; + int hlen; + int hindex; + + int save_point; + int save_mark; + int save_line; + int last_found_line; + char *prev_line_found; + + UNDO_LIST *save_undo_list; + + Keymap keymap; /* used when dispatching commands in search string */ + Keymap okeymap; /* original keymap */ + + int history_pos; + int direction; + + int prevc; + int lastc; +#if defined (HANDLE_MULTIBYTE) + char mb[MB_LEN_MAX]; + char pmb[MB_LEN_MAX]; +#endif + + char *sline; + int sline_len; + int sline_index; + + char *search_terminators; +} _rl_search_cxt; + +/* Callback data for reading numeric arguments */ +#define NUM_SAWMINUS 0x01 +#define NUM_SAWDIGITS 0x02 +#define NUM_READONE 0x04 + +typedef int _rl_arg_cxt; + +/* A context for reading key sequences longer than a single character when + using the callback interface. */ +#define KSEQ_DISPATCHED 0x01 +#define KSEQ_SUBSEQ 0x02 +#define KSEQ_RECURSIVE 0x04 + +typedef struct __rl_keyseq_context +{ + int flags; + int subseq_arg; + int subseq_retval; /* XXX */ + Keymap dmap; + + Keymap oldmap; + int okey; + struct __rl_keyseq_context *ocxt; + int childval; +} _rl_keyseq_cxt; + +/* vi-mode commands that use result of motion command to define boundaries */ +#define VIM_DELETE 0x01 +#define VIM_CHANGE 0x02 +#define VIM_YANK 0x04 + +/* various states for vi-mode commands that use motion commands. reflects + RL_READLINE_STATE */ +#define VMSTATE_READ 0x01 +#define VMSTATE_NUMARG 0x02 + +typedef struct __rl_vimotion_context +{ + int op; + int state; + int flags; /* reserved */ + _rl_arg_cxt ncxt; + int numeric_arg; + int start, end; /* rl_point, rl_end */ + int key, motion; /* initial key, motion command */ +} _rl_vimotion_cxt; + +/* fill in more as needed */ +/* `Generic' callback data and functions */ +typedef struct __rl_callback_generic_arg +{ + int count; + int i1, i2; + /* add here as needed */ +} _rl_callback_generic_arg; + +typedef int _rl_callback_func_t PARAMS((_rl_callback_generic_arg *)); + +typedef void _rl_sigcleanup_func_t PARAMS((int, void *)); + +/************************************************************************* + * * + * Global functions undocumented in texinfo manual and not in readline.h * + * * + *************************************************************************/ + +/************************************************************************* + * * + * Global variables undocumented in texinfo manual and not in readline.h * + * * + *************************************************************************/ + +/* complete.c */ +extern int rl_complete_with_tilde_expansion; +#if defined (VISIBLE_STATS) +extern int rl_visible_stats; +#endif /* VISIBLE_STATS */ +#if defined (COLOR_SUPPORT) +extern int _rl_colored_stats; +#endif + +/* readline.c */ +extern int rl_line_buffer_len; +extern int rl_arg_sign; +extern int rl_visible_prompt_length; +extern int rl_byte_oriented; + +/* display.c */ +extern int rl_display_fixed; + +/* parens.c */ +extern int rl_blink_matching_paren; + +/************************************************************************* + * * + * Global functions and variables unused and undocumented * + * * + *************************************************************************/ + +/* kill.c */ +extern int rl_set_retained_kills PARAMS((int)); + +/* terminal.c */ +extern void _rl_set_screen_size PARAMS((int, int)); + +/* undo.c */ +extern int _rl_fix_last_undo_of_type PARAMS((int, int, int)); + +/* util.c */ +extern char *_rl_savestring PARAMS((const char *)); + +/************************************************************************* + * * + * Functions and variables private to the readline library * + * * + *************************************************************************/ + +/* NOTE: Functions and variables prefixed with `_rl_' are + pseudo-global: they are global so they can be shared + between files in the readline library, but are not intended + to be visible to readline callers. */ + +/************************************************************************* + * Undocumented private functions * + *************************************************************************/ + +#if defined(READLINE_CALLBACKS) + +/* readline.c */ +extern void readline_internal_setup PARAMS((void)); +extern char *readline_internal_teardown PARAMS((int)); +extern int readline_internal_char PARAMS((void)); + +extern _rl_keyseq_cxt *_rl_keyseq_cxt_alloc PARAMS((void)); +extern void _rl_keyseq_cxt_dispose PARAMS((_rl_keyseq_cxt *)); +extern void _rl_keyseq_chain_dispose PARAMS((void)); + +extern int _rl_dispatch_callback PARAMS((_rl_keyseq_cxt *)); + +/* callback.c */ +extern _rl_callback_generic_arg *_rl_callback_data_alloc PARAMS((int)); +extern void _rl_callback_data_dispose PARAMS((_rl_callback_generic_arg *)); + +#endif /* READLINE_CALLBACKS */ + +/* bind.c */ +extern char *_rl_untranslate_macro_value PARAMS((char *, int)); + +/* complete.c */ +extern void _rl_reset_completion_state PARAMS((void)); +extern char _rl_find_completion_word PARAMS((int *, int *)); +extern void _rl_free_match_list PARAMS((char **)); + +/* display.c */ +extern char *_rl_strip_prompt PARAMS((char *)); +extern void _rl_reset_prompt PARAMS((void)); +extern void _rl_move_cursor_relative PARAMS((int, const char *)); +extern void _rl_move_vert PARAMS((int)); +extern void _rl_save_prompt PARAMS((void)); +extern void _rl_restore_prompt PARAMS((void)); +extern char *_rl_make_prompt_for_search PARAMS((int)); +extern void _rl_erase_at_end_of_line PARAMS((int)); +extern void _rl_clear_to_eol PARAMS((int)); +extern void _rl_clear_screen PARAMS((void)); +extern void _rl_update_final PARAMS((void)); +extern void _rl_redisplay_after_sigwinch PARAMS((void)); +extern void _rl_clean_up_for_exit PARAMS((void)); +extern void _rl_erase_entire_line PARAMS((void)); +extern int _rl_current_display_line PARAMS((void)); + +/* input.c */ +extern int _rl_any_typein PARAMS((void)); +extern int _rl_input_available PARAMS((void)); +extern int _rl_input_queued PARAMS((int)); +extern void _rl_insert_typein PARAMS((int)); +extern int _rl_unget_char PARAMS((int)); +extern int _rl_pushed_input_available PARAMS((void)); + +/* isearch.c */ +extern _rl_search_cxt *_rl_scxt_alloc PARAMS((int, int)); +extern void _rl_scxt_dispose PARAMS((_rl_search_cxt *, int)); + +extern int _rl_isearch_dispatch PARAMS((_rl_search_cxt *, int)); +extern int _rl_isearch_callback PARAMS((_rl_search_cxt *)); + +extern int _rl_search_getchar PARAMS((_rl_search_cxt *)); + +/* kill.c */ +#define BRACK_PASTE_PREF "\033[200~" +#define BRACK_PASTE_SUFF "\033[201~" + +#define BRACK_PASTE_LAST '~' +#define BRACK_PASTE_SLEN 6 + +#define BRACK_PASTE_INIT "\033[?2004h" +#define BRACK_PASTE_FINI "\033[?2004l" + +/* macro.c */ +extern void _rl_with_macro_input PARAMS((char *)); +extern int _rl_next_macro_key PARAMS((void)); +extern int _rl_prev_macro_key PARAMS((void)); +extern void _rl_push_executing_macro PARAMS((void)); +extern void _rl_pop_executing_macro PARAMS((void)); +extern void _rl_add_macro_char PARAMS((int)); +extern void _rl_kill_kbd_macro PARAMS((void)); + +/* misc.c */ +extern int _rl_arg_overflow PARAMS((void)); +extern void _rl_arg_init PARAMS((void)); +extern int _rl_arg_getchar PARAMS((void)); +extern int _rl_arg_callback PARAMS((_rl_arg_cxt)); +extern void _rl_reset_argument PARAMS((void)); + +extern void _rl_start_using_history PARAMS((void)); +extern int _rl_free_saved_history_line PARAMS((void)); +extern void _rl_set_insert_mode PARAMS((int, int)); + +extern void _rl_revert_all_lines PARAMS((void)); + +/* nls.c */ +extern int _rl_init_eightbit PARAMS((void)); + +/* parens.c */ +extern void _rl_enable_paren_matching PARAMS((int)); + +/* readline.c */ +extern void _rl_init_line_state PARAMS((void)); +extern void _rl_set_the_line PARAMS((void)); +extern int _rl_dispatch PARAMS((int, Keymap)); +extern int _rl_dispatch_subseq PARAMS((int, Keymap, int)); +extern void _rl_internal_char_cleanup PARAMS((void)); + +/* rltty.c */ +extern int _rl_disable_tty_signals PARAMS((void)); +extern int _rl_restore_tty_signals PARAMS((void)); + +/* search.c */ +extern int _rl_nsearch_callback PARAMS((_rl_search_cxt *)); + +/* signals.c */ +extern void _rl_signal_handler PARAMS((int)); + +extern void _rl_block_sigint PARAMS((void)); +extern void _rl_release_sigint PARAMS((void)); +extern void _rl_block_sigwinch PARAMS((void)); +extern void _rl_release_sigwinch PARAMS((void)); + +/* terminal.c */ +extern void _rl_get_screen_size PARAMS((int, int)); +extern void _rl_sigwinch_resize_terminal PARAMS((void)); +extern int _rl_init_terminal_io PARAMS((const char *)); +#ifdef _MINIX +extern void _rl_output_character_function PARAMS((int)); +#else +extern int _rl_output_character_function PARAMS((int)); +#endif +extern void _rl_output_some_chars PARAMS((const char *, int)); +extern int _rl_backspace PARAMS((int)); +extern void _rl_enable_meta_key PARAMS((void)); +extern void _rl_disable_meta_key PARAMS((void)); +extern void _rl_control_keypad PARAMS((int)); +extern void _rl_set_cursor PARAMS((int, int)); + +/* text.c */ +extern void _rl_fix_point PARAMS((int)); +extern int _rl_replace_text PARAMS((const char *, int, int)); +extern int _rl_forward_char_internal PARAMS((int)); +extern int _rl_insert_char PARAMS((int, int)); +extern int _rl_overwrite_char PARAMS((int, int)); +extern int _rl_overwrite_rubout PARAMS((int, int)); +extern int _rl_rubout_char PARAMS((int, int)); +#if defined (HANDLE_MULTIBYTE) +extern int _rl_char_search_internal PARAMS((int, int, char *, int)); +#else +extern int _rl_char_search_internal PARAMS((int, int, int)); +#endif +extern int _rl_set_mark_at_pos PARAMS((int)); + +/* undo.c */ +extern UNDO_LIST *_rl_copy_undo_entry PARAMS((UNDO_LIST *)); +extern UNDO_LIST *_rl_copy_undo_list PARAMS((UNDO_LIST *)); +extern void _rl_free_undo_list PARAMS((UNDO_LIST *)); + +/* util.c */ +#if defined (USE_VARARGS) && defined (PREFER_STDARG) +extern void _rl_ttymsg (const char *, ...) __attribute__((__format__ (printf, 1, 2))); +extern void _rl_errmsg (const char *, ...) __attribute__((__format__ (printf, 1, 2))); +extern void _rl_trace (const char *, ...) __attribute__((__format__ (printf, 1, 2))); +#else +extern void _rl_ttymsg (); +extern void _rl_errmsg (); +extern void _rl_trace (); +#endif +extern void _rl_audit_tty PARAMS((char *)); + +extern int _rl_tropen PARAMS((void)); + +extern int _rl_abort_internal PARAMS((void)); +extern int _rl_null_function PARAMS((int, int)); +extern char *_rl_strindex PARAMS((const char *, const char *)); +extern int _rl_qsort_string_compare PARAMS((char **, char **)); +extern int (_rl_uppercase_p) PARAMS((int)); +extern int (_rl_lowercase_p) PARAMS((int)); +extern int (_rl_pure_alphabetic) PARAMS((int)); +extern int (_rl_digit_p) PARAMS((int)); +extern int (_rl_to_lower) PARAMS((int)); +extern int (_rl_to_upper) PARAMS((int)); +extern int (_rl_digit_value) PARAMS((int)); + +/* vi_mode.c */ +extern void _rl_vi_initialize_line PARAMS((void)); +extern void _rl_vi_reset_last PARAMS((void)); +extern void _rl_vi_set_last PARAMS((int, int, int)); +extern int _rl_vi_textmod_command PARAMS((int)); +extern int _rl_vi_motion_command PARAMS((int)); +extern void _rl_vi_done_inserting PARAMS((void)); +extern int _rl_vi_domove_callback PARAMS((_rl_vimotion_cxt *)); + +/************************************************************************* + * Undocumented private variables * + *************************************************************************/ + +/* bind.c */ +extern const char * const _rl_possible_control_prefixes[]; +extern const char * const _rl_possible_meta_prefixes[]; + +/* callback.c */ +extern _rl_callback_func_t *_rl_callback_func; +extern _rl_callback_generic_arg *_rl_callback_data; + +/* complete.c */ +extern int _rl_complete_show_all; +extern int _rl_complete_show_unmodified; +extern int _rl_complete_mark_directories; +extern int _rl_complete_mark_symlink_dirs; +extern int _rl_completion_prefix_display_length; +extern int _rl_completion_columns; +extern int _rl_print_completions_horizontally; +extern int _rl_completion_case_fold; +extern int _rl_completion_case_map; +extern int _rl_match_hidden_files; +extern int _rl_page_completions; +extern int _rl_skip_completed_text; +extern int _rl_menu_complete_prefix_first; + +/* display.c */ +extern int _rl_vis_botlin; +extern int _rl_last_c_pos; +extern int _rl_suppress_redisplay; +extern int _rl_want_redisplay; + +/* isearch.c */ +extern char *_rl_isearch_terminators; + +extern _rl_search_cxt *_rl_iscxt; + +/* macro.c */ +extern char *_rl_executing_macro; + +/* misc.c */ +extern int _rl_history_preserve_point; +extern int _rl_history_saved_point; + +extern _rl_arg_cxt _rl_argcxt; + +/* nls.c */ +extern int _rl_utf8locale; + +/* readline.c */ +extern int _rl_echoing_p; +extern int _rl_horizontal_scroll_mode; +extern int _rl_mark_modified_lines; +extern int _rl_bell_preference; +extern int _rl_meta_flag; +extern int _rl_convert_meta_chars_to_ascii; +extern int _rl_output_meta_chars; +extern int _rl_bind_stty_chars; +extern int _rl_revert_all_at_newline; +extern int _rl_echo_control_chars; +extern int _rl_show_mode_in_prompt; +extern int _rl_enable_bracketed_paste; +extern char *_rl_comment_begin; +extern unsigned char _rl_parsing_conditionalized_out; +extern Keymap _rl_keymap; +extern FILE *_rl_in_stream; +extern FILE *_rl_out_stream; +extern int _rl_last_command_was_kill; +extern int _rl_eof_char; +extern procenv_t _rl_top_level; +extern _rl_keyseq_cxt *_rl_kscxt; +extern int _rl_keyseq_timeout; + +extern int _rl_executing_keyseq_size; + +/* search.c */ +extern _rl_search_cxt *_rl_nscxt; + +/* signals.c */ +extern int _rl_interrupt_immediately; +extern int volatile _rl_caught_signal; + +extern _rl_sigcleanup_func_t *_rl_sigcleanup; +extern void *_rl_sigcleanarg; + +extern int _rl_echoctl; + +extern int _rl_intr_char; +extern int _rl_quit_char; +extern int _rl_susp_char; + +/* terminal.c */ +extern int _rl_enable_keypad; +extern int _rl_enable_meta; +extern char *_rl_term_clreol; +extern char *_rl_term_clrpag; +extern char *_rl_term_im; +extern char *_rl_term_ic; +extern char *_rl_term_ei; +extern char *_rl_term_DC; +extern char *_rl_term_up; +extern char *_rl_term_dc; +extern char *_rl_term_cr; +extern char *_rl_term_IC; +extern char *_rl_term_forward_char; +extern int _rl_screenheight; +extern int _rl_screenwidth; +extern int _rl_screenchars; +extern int _rl_terminal_can_insert; +extern int _rl_term_autowrap; + +/* undo.c */ +extern int _rl_doing_an_undo; +extern int _rl_undo_group_level; + +/* vi_mode.c */ +extern int _rl_vi_last_command; +extern _rl_vimotion_cxt *_rl_vimvcxt; + +#endif /* _RL_PRIVATE_H_ */ |
