From 5e98bbab1738932aeeb251684bba6aa550f5885a Mon Sep 17 00:00:00 2001 From: Per Bothner Date: Sun, 16 Jan 1994 03:39:57 +0000 Subject: [PATCH] Merge in changes from bash-1.13. The most obvious one is that the file readline.c has been split into multiple files. * bind.c, complete.c, dispay.c, isearch.c, parens.c, rldefs.h, rltty.c, search.c signals.c, tilde.c, tilde.h, xmalloc.c: New files. --- readline/.Sanitize | 14 +- readline/ChangeLog | 7 + readline/Makefile.in | 70 +- readline/bind.c | 1396 +++++++++++++ readline/chardefs.h | 49 +- readline/complete.c | 1205 +++++++++++ readline/display.c | 801 ++++++++ readline/isearch.c | 378 ++++ readline/keymaps.h | 37 +- readline/parens.c | 115 ++ readline/readline.c | 4563 +++++------------------------------------- readline/rldefs.h | 250 +++ readline/search.c | 271 +++ readline/tilde.c | 396 ++++ readline/tilde.h | 33 + readline/xmalloc.c | 76 + 16 files changed, 5600 insertions(+), 4061 deletions(-) create mode 100644 readline/bind.c create mode 100644 readline/complete.c create mode 100644 readline/display.c create mode 100644 readline/isearch.c create mode 100644 readline/parens.c create mode 100644 readline/rldefs.h create mode 100644 readline/search.c create mode 100644 readline/tilde.c create mode 100644 readline/tilde.h create mode 100644 readline/xmalloc.c diff --git a/readline/.Sanitize b/readline/.Sanitize index 82bc37a481..69bf4744b0 100644 --- a/readline/.Sanitize +++ b/readline/.Sanitize @@ -28,28 +28,40 @@ Things-to-keep: COPYING ChangeLog Makefile.in -configure.bat +bind.c chardefs.h +complete.c config +configure.bat configure.in +display.c doc emacs_keymap.c examples funmap.c history.c history.h +isearch.c keymaps.c keymaps.h +parens.c readline.c readline.h +rldefs.h +rltty.c +search.c +signals.c sysdep-aix.h sysdep-irix.h sysdep-norm.h sysdep-obsd.h sysdep-sco.h sysdep-sysv4.h +tilde.c +tilde.h vi_keymap.c vi_mode.c +xmalloc.c Things-to-lose: diff --git a/readline/ChangeLog b/readline/ChangeLog index ccdb5abb90..1aa1e98b94 100644 --- a/readline/ChangeLog +++ b/readline/ChangeLog @@ -1,3 +1,10 @@ +Sat Jan 15 19:36:12 1994 Per Bothner (bothner@kalessin.cygnus.com) + + Merge in changes from bash-1.13. The most obvious one is + that the file readline.c has been split into multiple files. + * bind.c, complete.c, dispay.c, isearch.c, parens.c, rldefs.h, + rltty.c, search.c signals.c, tilde.c, tilde.h, xmalloc.c: New files. + Sat Dec 11 16:29:17 1993 Steve Chamberlain (sac@thepub.cygnus.com) * readline.c (rl_getc): If GO32, trim high bit from getkey, diff --git a/readline/Makefile.in b/readline/Makefile.in index 635763e595..05762db46c 100644 --- a/readline/Makefile.in +++ b/readline/Makefile.in @@ -81,10 +81,22 @@ CP = cp LOCAL_INCLUDES = -I$(srcdir)/../ -CSOURCES = readline.c history.c funmap.c keymaps.c vi_mode.c \ - emacs_keymap.c vi_keymap.c +# The name of the main library target. +LIBRARY_NAME = libreadline.a + +# The C code source files for this library. +CSOURCES = readline.c funmap.c keymaps.c vi_mode.c parens.c \ + rltty.c complete.c bind.c isearch.c display.c signals.c \ + emacs_keymap.c vi_keymap.c history.c tilde.c xmalloc.c + +# The header files for this library. +HSOURCES = readline.h rldefs.h chardefs.h keymaps.h history.h \ + posixstat.h tilde.h + +OBJECTS = readline.o vi_mode.o funmap.o keymaps.o parens.o search.o \ + rltty.o complete.o bind.o isearch.o display.o signals.o \ + history.o tilde.o xmalloc.o -HSOURCES = readline.h chardefs.h history.h keymaps.h SOURCES = $(CSOURCES) $(HSOURCES) DOCUMENTATION = readline.texi inc-read.texi \ @@ -94,6 +106,17 @@ SUPPORT = COPYING Makefile $(DOCUMENTATION) ChangeLog THINGS_TO_TAR = $(SOURCES) $(SUPPORT) +FLAGS_TO_PASS = \ + "prefix=$(prefix)" \ + "exec_prefix=$(exec_prefix)" \ + "against=$(against)" \ + "MAKEINFO=$(MAKEINFO)" \ + "INSTALL=$(INSTALL)" \ + "INSTALL_PROGRAM=$(INSTALL_PROGRAM)" \ + "INSTALL_DATA=$(INSTALL_DATA)" + +SUBDIRS = doc + #### Host, target, and site specific Makefile fragments come in here. ### @@ -106,11 +129,18 @@ all: libreadline.a check: installcheck: -info: -dvi: -clean-info: force - -rm -f *.info* +info dvi install-info clean-info: force + @$(MAKE) $(FLAGS_TO_PASS) DO=$@ "DODIRS=$(SUBDIRS)" subdir_do + +subdir_do: force + @for i in $(DODIRS); do \ + if [ -f ./$$i/Makefile ] ; then \ + if (cd ./$$i; \ + $(MAKE) $(FLAGS_TO_PASS) $(DO)) ; then true ; \ + else exit 1 ; fi ; \ + else true ; fi ; \ + done history.info: $(srcdir)/history.texi $(MAKEINFO) -o history.info $(srcdir)/history.texi @@ -118,9 +148,9 @@ history.info: $(srcdir)/history.texi readline.info: $(srcdir)/readline.texi $(srcdir)/inc-read.texi $(MAKEINFO) -o readline.info $(srcdir)/readline.texi -libreadline.a: readline.o history.o funmap.o keymaps.o tilde.o vi_mode.o +libreadline.a: $(OBJECTS) $(RM) -f libreadline.a - $(AR) $(AR_FLAGS) libreadline.a readline.o history.o funmap.o keymaps.o tilde.o vi_mode.o + $(AR) $(AR_FLAGS) libreadline.a $(OBJECTS) $(RANLIB) libreadline.a readline.o: readline.h chardefs.h keymaps.h history.h readline.c vi_mode.c @@ -146,35 +176,13 @@ readline.tar.Z: readline.tar compress -f readline.tar install: - -parent=`echo $(libdir)|sed -e 's@/[^/]*$$@@'`; \ - if [ -d $$parent ] ; then true ; else mkdir $$parent ; fi - -if [ -d $(libdir) ] ; then true ; else mkdir $(libdir) ; fi $(INSTALL_DATA) libreadline.a $(libdir)/libreadline.a $(RANLIB) $(libdir)/libreadline.a - -parent=`echo $(includedir)|sed -e 's@/[^/]*$$@@'`; \ - if [ -d $$parent ] ; then true ; else mkdir $$parent ; fi - -if [ -d $(includedir) ] ; then true ; else mkdir $(includedir) ; fi - -if [ -d $(includedir)/readline ] ; then true ; else mkdir $(includedir)/readline ; fi $(INSTALL_DATA) $(srcdir)/readline.h $(includedir)/readline/readline.h $(INSTALL_DATA) $(srcdir)/keymaps.h $(includedir)/readline/keymaps.h $(INSTALL_DATA) $(srcdir)/chardefs.h $(includedir)/readline/chardefs.h -install-info: info -# -parent=`echo $(infodir)|sed -e 's@/[^/]*$$@@'`; \ -# if [ -d $$parent ] ; then true ; else mkdir $$parent ; fi -# -if [ -d $(infodir) ] ; then true ; else mkdir $(infodir) ; fi -# for i in *.info* ; do \ -# $(INSTALL_DATA) $$i $(infodir)/$$i ; \ -# done - includes: - -parent=`echo $(includedir)|sed -e 's@/[^/]*$$@@'`; \ - if [ -d $$parent ] ; then true ; else mkdir $$parent ; fi - -if [ -d $(includedir) ] ; then true ; else mkdir $(includedir) ; fi - -if [ ! -r $(includedir)/readline ]; then\ - mkdir $(includedir)/readline;\ - chmod a+r $(includedir)/readline;\ - fi $(INSTALL_FILE) $(srcdir)/readline.h $(includedir)/readline/readline.h $(INSTALL_FILE) $(srcdir)/keymaps.h $(includedir)/readline/keymaps.h $(INSTALL_FILE) $(srcdir)/chardefs.h $(includedir)/readline/chardefs.h diff --git a/readline/bind.c b/readline/bind.c new file mode 100644 index 0000000000..a7ffe25dfa --- /dev/null +++ b/readline/bind.c @@ -0,0 +1,1396 @@ +/* bind.c -- key binding and startup file support for the readline library. */ + +/* Copyright (C) 1987, 1989, 1992 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library, a library for + reading lines of text with interactive input and history editing. + + The GNU Readline Library is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 1, or + (at your option) any later version. + + The GNU Readline Library is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied warranty + of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + The GNU General Public License is often shipped with GNU software, and + is generally kept in a file called COPYING or LICENSE. If you do not + have a copy of the license, write to the Free Software Foundation, + 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include "sysdep.h" +#include +#include +#include +#ifndef NO_SYS_FILE +#include +#endif + +#include +/* Not all systems declare ERRNO in errno.h... and some systems #define it! */ +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +/* System-specific feature definitions and include files. */ +#include "rldefs.h" + +/* Some standard library routines. */ +#include "readline.h" +#include "history.h" + +#if !defined (strchr) && !defined (__STDC__) +extern char *strchr (), *strrchr (); +#endif /* !strchr && !__STDC__ */ + +extern char *tilde_expand (); + +extern int _rl_horizontal_scroll_mode; +extern int _rl_mark_modified_lines; +extern int _rl_prefer_visible_bell; +extern int _rl_meta_flag; +extern int rl_blink_matching_paren; +extern int _rl_convert_meta_chars_to_ascii; +#if defined (VISIBLE_STATS) +extern int rl_visible_stats; +#endif /* VISIBLE_STATS */ +extern int rl_complete_with_tilde_expansion; +extern int rl_completion_query_items; + +extern int rl_explicit_arg; +extern int rl_editing_mode; +extern unsigned short _rl_parsing_conditionalized_out; +extern Keymap _rl_keymap; + +extern char *possible_control_prefixes[], *possible_meta_prefixes[]; + +extern char **rl_funmap_names (); + +static void rl_generic_bind (); +static int glean_key_from_name (); +static int stricmp (), strnicmp (); + +#if defined (STATIC_MALLOC) +static char *xmalloc (), *xrealloc (); +#else +extern char *xmalloc (), *xrealloc (); +#endif /* STATIC_MALLOC */ + +/* **************************************************************** */ +/* */ +/* Binding keys */ +/* */ +/* **************************************************************** */ + +/* rl_add_defun (char *name, Function *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. */ +rl_add_defun (name, function, key) + char *name; + Function *function; + int key; +{ + if (key != -1) + rl_bind_key (key, function); + rl_add_funmap_entry (name, function); +} + +/* Bind KEY to FUNCTION. Returns non-zero if KEY is out of range. */ +int +rl_bind_key (key, function) + int key; + Function *function; +{ + if (key < 0) + return (key); + + if (META_CHAR (key) && _rl_convert_meta_chars_to_ascii) + { + if (_rl_keymap[ESC].type == ISKMAP) + { + Keymap escmap = (Keymap)_rl_keymap[ESC].function; + + 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; + 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; + Function *function; + Keymap map; +{ + int result; + Keymap oldmap = _rl_keymap; + + _rl_keymap = map; + result = rl_bind_key (key, function); + _rl_keymap = oldmap; + return (result); +} + +/* 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, (Function *)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, (Function *)NULL, map)); +} + +/* 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. */ +rl_set_key (keyseq, function, map) + char *keyseq; + Function *function; + Keymap map; +{ + rl_generic_bind (ISFUNC, keyseq, function, map); +} + +/* 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. */ +rl_macro_bind (keyseq, macro, map) + char *keyseq, *macro; + Keymap map; +{ + char *macro_keys; + int macro_keys_len; + + macro_keys = (char *)xmalloc ((2 * strlen (macro)) + 1); + + if (rl_translate_keyseq (macro, macro_keys, ¯o_keys_len)) + { + free (macro_keys); + return; + } + rl_generic_bind (ISMACR, keyseq, macro_keys, map); +} + +/* 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. */ + +static void +rl_generic_bind (type, keyseq, data, map) + int type; + char *keyseq, *data; + Keymap map; +{ + char *keys; + int keys_len; + register int i; + + /* If no keys to bind to, exit right away. */ + if (!keyseq || !*keyseq) + { + if (type == ISMACR) + free (data); + return; + } + + keys = (char *)alloca (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)) + return; + + /* Bind keys, making new keymaps as necessary. */ + for (i = 0; i < keys_len; i++) + { + int ic = (int) ((unsigned char)keys[i]); + + if (_rl_convert_meta_chars_to_ascii && META_CHAR (ic)) + { + ic = UNMETA (ic); + if (map[ESC].type == ISKMAP) + map = (Keymap) map[ESC].function; + } + + if ((i + 1) < keys_len) + { + if (map[ic].type != ISKMAP) + { + if (map[ic].type == ISMACR) + free ((char *)map[ic].function); + + map[ic].type = ISKMAP; + map[ic].function = (Function *)rl_make_bare_keymap (); + } + map = (Keymap)map[ic].function; + } + else + { + if (map[ic].type == ISMACR) + free ((char *)map[ic].function); + + map[ic].function = (Function *)data; + map[ic].type = type; + } + } +} + +/* 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. */ +rl_translate_keyseq (seq, array, len) + char *seq, *array; + int *len; +{ + register int i, c, l = 0; + + for (i = 0; c = seq[i]; i++) + { + if (c == '\\') + { + c = seq[++i]; + + if (!c) + break; + + if (((c == 'C' || c == 'M') && seq[i + 1] == '-') || + (c == 'e')) + { + /* Handle special case of backwards define. */ + if (strncmp (&seq[i], "C-\\M-", 5) == 0) + { + array[l++] = ESC; + i += 5; + array[l++] = CTRL (to_upper (seq[i])); + if (!seq[i]) + i--; + continue; + } + + switch (c) + { + case 'M': + i++; + array[l++] = ESC; + break; + + case 'C': + i += 2; + /* Special hack for C-?... */ + if (seq[i] == '?') + array[l++] = RUBOUT; + else + array[l++] = CTRL (to_upper (seq[i])); + break; + + case 'e': + array[l++] = ESC; + } + + continue; + } + } + array[l++] = c; + } + + *len = l; + array[l] = '\0'; + return (0); +} + +/* Return a pointer to the function that STRING represents. + If STRING doesn't have a matching function, then a NULL pointer + is returned. */ +Function * +rl_named_function (string) + char *string; +{ + register int i; + + rl_initialize_funmap (); + + for (i = 0; funmap[i]; i++) + if (stricmp (funmap[i]->name, string) == 0) + return (funmap[i]->function); + return ((Function *)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). */ +Function * +rl_function_of_keyseq (keyseq, map, type) + char *keyseq; + Keymap map; + int *type; +{ + register int i; + + if (!map) + map = _rl_keymap; + + for (i = 0; keyseq && keyseq[i]; i++) + { + int ic = keyseq[i]; + + if (META_CHAR (ic) && _rl_convert_meta_chars_to_ascii) + { + if (map[ESC].type != ISKMAP) + { + if (type) + *type = map[ESC].type; + + return (map[ESC].function); + } + else + { + map = (Keymap)map[ESC].function; + ic = UNMETA (ic); + } + } + + if (map[ic].type == ISKMAP) + { + /* If this is the last key in the key sequence, return the + map. */ + if (!keyseq[i + 1]) + { + if (type) + *type = ISKMAP; + + return (map[ic].function); + } + else + map = (Keymap)map[ic].function; + } + else + { + if (type) + *type = map[ic].type; + + return (map[ic].function); + } + } +} + +/* The last key bindings file read. */ +static char *last_readline_init_file = (char *)NULL; + +/* Re-read the current keybindings file. */ +rl_re_read_init_file (count, ignore) + int count, ignore; +{ + rl_read_init_file ((char *)NULL); +} + +/* The final, last-ditch effort file name for an init file. */ +#ifdef __MSDOS__ +/* Don't know what to do, but this is a guess */ +#define DEFAULT_INPUTRC "/INPUTRC"; +#else +#define DEFAULT_INPUTRC "~/.inputrc" +#endif + +/* Do key bindings from a file. If FILENAME is NULL it defaults + to `~/.inputrc'. If the file existed and could be opened and + read, 0 is returned, otherwise errno is returned. */ +int +rl_read_init_file (filename) + char *filename; +{ + register int i; + char *buffer, *openname, *line, *end; + struct stat finfo; + int file; + + /* Default the filename. */ + if (!filename) + { + if (last_readline_init_file) + filename = last_readline_init_file; + else + filename = DEFAULT_INPUTRC; + } + + openname = tilde_expand (filename); + + if (!openname || *openname == '\000') + return ENOENT; + + if ((stat (openname, &finfo) < 0) || + (file = open (openname, O_RDONLY, 0666)) < 0) + { + free (openname); + return (errno); + } + else + free (openname); + + if (last_readline_init_file) + free (last_readline_init_file); + + last_readline_init_file = savestring (filename); + + /* Read the file into BUFFER. */ + buffer = (char *)xmalloc ((int)finfo.st_size + 1); + i = read (file, buffer, finfo.st_size); + close (file); + + if (i != finfo.st_size) + return (errno); + + /* Loop over the lines in the file. Lines that start with `#' are + comments; all other lines are commands for readline initialization. */ + line = buffer; + end = buffer + finfo.st_size; + while (line < end) + { + /* Find the end of this line. */ + for (i = 0; line + i != end && line[i] != '\n'; i++); + + /* Mark end of line. */ + line[i] = '\0'; + + /* 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; + } + free (buffer); + return (0); +} + +/* **************************************************************** */ +/* */ +/* Parser Directives */ +/* */ +/* **************************************************************** */ + +/* Conditionals. */ + +/* Calling programs set this to have their argv[0]. */ +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 = 0; +static int if_stack_size = 0; + +/* 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 && strnicmp (args, "term=", 5) == 0) + { + char *tem, *tname; + + /* Terminals like "aaa-60" are equivalent to "aaa". */ + tname = savestring (rl_terminal_name); + tem = (char*) strrchr (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. */ + if ((stricmp (args + 5, tname) == 0) || + (stricmp (args + 5, rl_terminal_name) == 0)) + _rl_parsing_conditionalized_out = 0; + else + _rl_parsing_conditionalized_out = 1; + + free (tname); + } +#if defined (VI_MODE) + else if (strnicmp (args, "mode=", 5) == 0) + { + int mode; + + if (stricmp (args + 5, "emacs") == 0) + mode = emacs_mode; + else if (stricmp (args + 5, "vi") == 0) + mode = vi_mode; + else + mode = no_mode; + + if (mode == rl_editing_mode) + _rl_parsing_conditionalized_out = 0; + else + _rl_parsing_conditionalized_out = 1; + } +#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 (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) + { + /* Error message? */ + return 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++) + 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 + { + /* *** What, no error message? *** */ + } + return 0; +} + +/* Associate textual names with actual functions. */ +static struct { + char *name; + Function *function; +} parser_directives [] = { + { "if", parser_if }, + { "endif", parser_endif }, + { "else", parser_else }, + { (char *)0x0, (Function *)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 (stricmp (directive, parser_directives[i].name) == 0) + { + (*parser_directives[i].function) (args); + return (0); + } + + /* *** Should an error message be output? */ + return (1); +} + +/* Ugly but working hack for binding prefix meta. */ +#define PREFIX_META_HACK + +static int substring_member_of_array (); + +/* 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. */ +rl_parse_and_bind (string) + char *string; +{ + char *funname, *kname; + register int c, i; + int key, equivalency; + + while (string && whitespace (*string)) + string++; + + if (!string || !*string || *string == '#') + return; + + /* If this is a parser directive, act on it. */ + if (*string == '$') + { + handle_parser_directive (&string[1]); + return; + } + + /* If we aren't supposed to be parsing right now, then we're done. */ + if (_rl_parsing_conditionalized_out) + return; + + i = 0; + /* If this keyname is a complex key expression surrounded by quotes, + advance to after the matching close quote. This code allows the + backslash to quote characters in the key expression. */ + if (*string == '"') + { + int passc = 0; + + for (i = 1; c = string[i]; i++) + { + if (passc) + { + passc = 0; + continue; + } + + if (c == '\\') + { + passc++; + continue; + } + + if (c == '"') + break; + } + } + + /* 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 (stricmp (string, "set") == 0) + { + char *var = string + i; + char *value; + + /* Make VAR point to start of variable name. */ + while (*var && whitespace (*var)) var++; + + /* Make value point to start of value string. */ + value = var; + while (*value && !whitespace (*value)) value++; + if (*value) + *value++ = '\0'; + while (*value && whitespace (*value)) value++; + + rl_variable_bind (var, value); + return; + } + + /* Skip any whitespace between keyname and funname. */ + for (; string[i] && whitespace (string[i]); i++); + funname = &string[i]; + + /* Now isolate funname. + For straight function names just look for whitespace, since + that will signify the end of the string. But this could be a + macro definition. In that case, the string is quoted, so skip + to the matching delimiter. We allow the backslash to quote the + delimiter characters in the macro body. */ + /* This code exists to allow whitespace in macro expansions, which + would otherwise be gobbled up by the next `for' loop.*/ + /* XXX - it may be desirable to allow backslash quoting only if " is + the quoted string delimiter, like the shell. */ + if (*funname == '\'' || *funname == '"') + { + int delimiter = string[i++]; + int passc = 0; + + for (; c = string[i]; i++) + { + if (passc) + { + passc = 0; + continue; + } + + if (c == '\\') + { + passc = 1; + continue; + } + + if (c == delimiter) + break; + } + if (c) + i++; + } + + /* Advance to the end of the string. */ + for (; string[i] && !whitespace (string[i]); i++); + + /* No extra whitespace at the end of the string. */ + string[i] = '\0'; + + /* Handle equivalency bindings here. Make the left-hand side be exactly + whatever the right-hand evaluates to, including keymaps. */ + if (equivalency) + { + return; + } + + /* If this is a new-style key-binding, then do the binding with + rl_set_key (). Otherwise, let the older code deal with it. */ + if (*string == '"') + { + char *seq = (char *)alloca (1 + strlen (string)); + register int j, k = 0; + int passc = 0; + + for (j = 1; 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_set_key (seq, rl_named_function (funname), _rl_keymap); + + return; + } + + /* Get the actual character we want to deal with. */ + kname = (char*) strrchr (string, '-'); + if (!kname) + kname = string; + else + kname++; + + key = glean_key_from_name (kname); + + /* Add in control and meta bits. */ + if (substring_member_of_array (string, possible_control_prefixes)) + key = CTRL (to_upper (key)); + + if (substring_member_of_array (string, possible_meta_prefixes)) + key = META (key); + + /* Temporary. Handle old-style keyname with macro-binding. */ + if (*funname == '\'' || *funname == '"') + { + char seq[2]; + int fl = strlen (funname); + + seq[0] = key; seq[1] = '\0'; + if (fl && funname[fl - 1] == *funname) + funname[fl - 1] = '\0'; + + rl_macro_bind (seq, &funname[1], _rl_keymap); + } +#if defined (PREFIX_META_HACK) + /* Ugly, but working hack to keep prefix-meta around. */ + else if (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)); +} + +/* 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. */ + +static struct { + char *name; + int *value; +} boolean_varlist [] = { + { "horizontal-scroll-mode", &_rl_horizontal_scroll_mode }, + { "mark-modified-lines", &_rl_mark_modified_lines }, + { "prefer-visible-bell", &_rl_prefer_visible_bell }, + { "meta-flag", &_rl_meta_flag }, + { "blink-matching-paren", &rl_blink_matching_paren }, + { "convert-meta", &_rl_convert_meta_chars_to_ascii }, +#if defined (VISIBLE_STATS) + { "visible-stats", &rl_visible_stats }, +#endif /* VISIBLE_STATS */ + { "expand-tilde", &rl_complete_with_tilde_expansion }, + { (char *)NULL, (int *)NULL } +}; + +rl_variable_bind (name, value) + char *name, *value; +{ + register int i; + + /* Check for simple variables first. */ + for (i = 0; boolean_varlist[i].name; i++) + { + if (stricmp (name, boolean_varlist[i].name) == 0) + { + /* A variable is TRUE if the "value" is "on", "1" or "". */ + if ((!*value) || + (stricmp (value, "On") == 0) || + (value[0] == '1' && value[1] == '\0')) + *boolean_varlist[i].value = 1; + else + *boolean_varlist[i].value = 0; + return; + } + } + + /* Not a boolean variable, so check for specials. */ + + /* Editing mode change? */ + if (stricmp (name, "editing-mode") == 0) + { + if (strnicmp (value, "vi", 2) == 0) + { +#if defined (VI_MODE) + _rl_keymap = vi_insertion_keymap; + rl_editing_mode = vi_mode; +#else +#if defined (NOTDEF) + /* What state is the terminal in? I'll tell you: + non-determinate! That means we cannot do any output. */ + ding (); +#endif /* NOTDEF */ +#endif /* VI_MODE */ + } + else if (strnicmp (value, "emacs", 5) == 0) + { + _rl_keymap = emacs_standard_keymap; + rl_editing_mode = emacs_mode; + } + } + + /* Comment string change? */ + else if (stricmp (name, "comment-begin") == 0) + { +#if defined (VI_MODE) + extern char *rl_vi_comment_begin; + + if (*value) + { + if (rl_vi_comment_begin) + free (rl_vi_comment_begin); + + rl_vi_comment_begin = savestring (value); + } +#endif /* VI_MODE */ + } + else if (stricmp (name, "completion-query-items") == 0) + { + int nval = 100; + if (*value) + { + nval = atoi (value); + if (nval < 0) + nval = 0; + } + rl_completion_query_items = nval; + } +} + +/* Return the character which matches NAME. + For example, `Space' returns ' '. */ + +typedef struct { + char *name; + int value; +} assoc_list; + +static assoc_list name_key_alist[] = { + { "DEL", 0x7f }, + { "ESC", '\033' }, + { "Escape", '\033' }, + { "LFD", '\n' }, + { "Newline", '\n' }, + { "RET", '\r' }, + { "Return", '\r' }, + { "Rubout", 0x7f }, + { "SPC", ' ' }, + { "Space", ' ' }, + { "Tab", 0x09 }, + { (char *)0x0, 0 } +}; + +static int +glean_key_from_name (name) + char *name; +{ + register int i; + + for (i = 0; name_key_alist[i].name; i++) + if (stricmp (name, name_key_alist[i].name) == 0) + return (name_key_alist[i].value); + + return (*(unsigned char *)name); /* XXX was return (*name) */ +} + +/* Auxiliary functions to manage keymaps. */ +static struct { + char *name; + Keymap map; +} keymap_names[] = { + { "emacs", emacs_standard_keymap }, + { "emacs-standard", emacs_standard_keymap }, + { "emacs-meta", emacs_meta_keymap }, + { "emacs-ctlx", emacs_ctlx_keymap }, +#if defined (VI_MODE) + { "vi", vi_movement_keymap }, + { "vi-move", vi_movement_keymap }, + { "vi-command", vi_movement_keymap }, + { "vi-insert", vi_insertion_keymap }, +#endif /* VI_MODE */ + { (char *)0x0, (Keymap)0x0 } +}; + +Keymap +rl_get_keymap_by_name (name) + char *name; +{ + register int i; + + for (i = 0; keymap_names[i].name; i++) + if (strcmp (name, keymap_names[i].name) == 0) + return (keymap_names[i].map); + return ((Keymap) 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; + else if (rl_editing_mode == vi_mode) + _rl_keymap = vi_insertion_keymap; +} + +/* **************************************************************** */ +/* */ +/* 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 (ignore) + int ignore; +{ + register int i; + char **funmap_names; + + funmap_names = rl_funmap_names (); + + if (!funmap_names) + return; + + for (i = 0; funmap_names[i]; i++) + fprintf (rl_outstream, "%s\n", funmap_names[i]); + + free (funmap_names); +} + +/* Return a NULL terminated array of strings which represent the key + sequences that are used to invoke FUNCTION in MAP. */ +static char ** +invoking_keyseqs_in_map (function, map) + Function *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 < 128; 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 = (char *)xmalloc (5); + + if (CTRL_P (key)) + sprintf (keyname, "\\C-%c", 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'; + } + + if (result_index + 2 > result_size) + result = (char **) xrealloc + (result, (result_size += 10) * sizeof (char *)); + + result[result_index++] = keyname; + result[result_index] = (char *)NULL; + } + break; + + case ISKMAP: + { + char **seqs = (char **)NULL; + + /* 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 = + invoking_keyseqs_in_map (function, (Keymap)map[key].function); + + if (seqs) + { + register int i; + + for (i = 0; seqs[i]; i++) + { + char *keyname = (char *)xmalloc (6 + strlen (seqs[i])); + + if (key == ESC) + sprintf (keyname, "\\e"); + else if (CTRL_P (key)) + sprintf (keyname, "\\C-%c", 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]); + + if (result_index + 2 > result_size) + result = (char **) xrealloc + (result, (result_size += 10) * sizeof (char *)); + + result[result_index++] = keyname; + result[result_index] = (char *)NULL; + } + } + } + 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) + Function *function; +{ + return (invoking_keyseqs_in_map (function, _rl_keymap)); +} + +/* 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) + int count; +{ + void rl_function_dumper (); + + rl_function_dumper (rl_explicit_arg); + rl_on_new_line (); + return (0); +} + +/* 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; + char **names; + char *name; + + names = rl_funmap_names (); + + fprintf (rl_outstream, "\n"); + + for (i = 0; name = names[i]; i++) + { + Function *function; + char **invokers; + + function = rl_named_function (name); + invokers = invoking_keyseqs_in_map (function, _rl_keymap); + + if (print_readably) + { + if (!invokers) + fprintf (rl_outstream, "# %s (not bound)\n", name); + else + { + register int j; + + for (j = 0; invokers[j]; j++) + { + fprintf (rl_outstream, "\"%s\": %s\n", + invokers[j], name); + free (invokers[j]); + } + + free (invokers); + } + } + else + { + if (!invokers) + fprintf (rl_outstream, "%s is not bound to any keys\n", + name); + else + { + register int j; + + fprintf (rl_outstream, "%s can be found on ", name); + + for (j = 0; invokers[j] && j < 5; j++) + { + fprintf (rl_outstream, "\"%s\"%s", invokers[j], + invokers[j + 1] ? ", " : ".\n"); + } + + if (j == 5 && invokers[j]) + fprintf (rl_outstream, "...\n"); + + for (j = 0; invokers[j]; j++) + free (invokers[j]); + + free (invokers); + } + } + } +} + + +/* **************************************************************** */ +/* */ +/* String Utility Functions */ +/* */ +/* **************************************************************** */ + +static char *strindex (); + +/* Return non-zero if any members of ARRAY are a substring in STRING. */ +static int +substring_member_of_array (string, array) + char *string, **array; +{ + while (*array) + { + if (strindex (string, *array)) + return (1); + array++; + } + return (0); +} + +/* Whoops, Unix doesn't have strnicmp. */ + +/* Compare at most COUNT characters from string1 to string2. Case + doesn't matter. */ +static int +strnicmp (string1, string2, count) + char *string1, *string2; +{ + register char ch1, ch2; + + while (count) + { + ch1 = *string1++; + ch2 = *string2++; + if (to_upper(ch1) == to_upper(ch2)) + count--; + else break; + } + return (count); +} + +/* strcmp (), but caseless. */ +static int +stricmp (string1, string2) + char *string1, *string2; +{ + register char ch1, ch2; + + while (*string1 && *string2) + { + ch1 = *string1++; + ch2 = *string2++; + if (to_upper(ch1) != to_upper(ch2)) + return (1); + } + return (*string1 | *string2); +} + +/* Determine if s2 occurs in s1. If so, return a pointer to the + match in s1. The compare is case insensitive. */ +static char * +strindex (s1, s2) + register char *s1, *s2; +{ + register int i, l = strlen (s2); + register int len = strlen (s1); + + for (i = 0; (len - i) >= l; i++) + if (strnicmp (&s1[i], s2, l) == 0) + return (s1 + i); + return ((char *)NULL); +} diff --git a/readline/chardefs.h b/readline/chardefs.h index 9749ae489f..43d8539c79 100644 --- a/readline/chardefs.h +++ b/readline/chardefs.h @@ -1,8 +1,21 @@ /* chardefs.h -- Character definitions for readline. */ #ifndef _CHARDEFS_ +#define _CHARDEFS_ + +#include + +#if defined (HAVE_STRING_H) +# include +#else +# include +#endif /* HAVE_STRING_H */ #ifndef savestring -#define savestring(x) (char *)strcpy (xmalloc (1 + strlen (x)), (x)) +extern char *xmalloc (); +# ifndef strcpy +extern char *strcpy (); +# endif +#define savestring(x) strcpy (xmalloc (1 + strlen (x)), (x)) #endif #ifndef whitespace @@ -14,11 +27,13 @@ #endif /* Some character stuff. */ -#define control_character_threshold 0x020 /* smaller than this is control */ -#define meta_character_threshold 0x07f /* larger than this is Meta. */ +#define control_character_threshold 0x020 /* Smaller than this is control. */ +#define meta_character_threshold 0x07f /* Larger than this is Meta. */ #define control_character_bit 0x40 /* 0x000000, must be off. */ #define meta_character_bit 0x080 /* x0000000, must be on. */ +#define largest_char 255 /* Largest character value. */ +#define META_CHAR(c) ((c) > meta_character_threshold && (c) <= largest_char) #define CTRL(c) ((c) & (~control_character_bit)) #define META(c) ((c) | meta_character_bit) @@ -38,13 +53,41 @@ #define CTRL_P(c) ((c) < control_character_threshold) #define META_P(c) ((c) > meta_character_threshold) +#ifndef NEWLINE #define NEWLINE '\n' +#endif + +#ifndef RETURN #define RETURN CTRL('M') +#endif + +#ifndef RUBOUT #define RUBOUT 0x07f +#endif + +#ifndef TAB #define TAB '\t' +#endif + +#ifdef ABORT_CHAR +#undef ABORT_CHAR +#endif #define ABORT_CHAR CTRL('G') + +#ifdef PAGE +#undef PAGE +#endif #define PAGE CTRL('L') + +#ifdef SPACE +#undef SPACE +#endif #define SPACE 0x020 + +#ifdef ESC +#undef ESC +#endif + #define ESC CTRL('[') #endif /* _CHARDEFS_ */ diff --git a/readline/complete.c b/readline/complete.c new file mode 100644 index 0000000000..7b733a3e80 --- /dev/null +++ b/readline/complete.c @@ -0,0 +1,1205 @@ +/* complete.c -- filename completion for readline. */ + +/* Copyright (C) 1987, 1989, 1992 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library, a library for + reading lines of text with interactive input and history editing. + + The GNU Readline Library is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 1, or + (at your option) any later version. + + The GNU Readline Library is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied warranty + of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + The GNU General Public License is often shipped with GNU software, and + is generally kept in a file called COPYING or LICENSE. If you do not + have a copy of the license, write to the Free Software Foundation, + 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include "sysdep.h" +#include +#include +#include +#if !defined (NO_SYS_FILE) +# include +#endif /* !NO_SYS_FILE */ + +#include +/* Not all systems declare ERRNO in errno.h... and some systems #define it! */ +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +/* These next are for filename completion. Perhaps this belongs + in a different place. */ +#ifndef __MSDOS__ +#include +#endif /* __MSDOS__ */ +#if defined (USG) && !defined (isc386) && !defined (sgi) +extern struct passwd *getpwuid (), *getpwent (); +#endif +#if defined (isc386) && !defined (__STDC__) && defined (_POSIX_SOURCE) +extern struct passwd *getpwent (); +#endif + +/* System-specific feature definitions and include files. */ +#include "rldefs.h" + +/* Some standard library routines. */ +#include "readline.h" + +#if !defined (strchr) +extern char *strchr (); +#endif /* !strchr */ +#if !defined (strrchr) +extern char *strrchr (); +#endif /* !strrchr*/ + +extern char *tilde_expand (); +extern char *rl_copy_text (); + +extern Function *rl_last_func; +extern int rl_editing_mode; +extern int screenwidth; + +static int compare_strings (); +static char *rl_strpbrk (); + +#if defined (STATIC_MALLOC) +static char *xmalloc (), *xrealloc (); +#else +extern char *xmalloc (), *xrealloc (); +#endif /* STATIC_MALLOC */ + +/* 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. */ +Function *rl_symbolic_link_hook = (Function *)NULL; + +/* Non-zero means readline completion functions perform tilde expansion. */ +int rl_complete_with_tilde_expansion = 0; + +#define VISIBLE_STATS + +#if defined (VISIBLE_STATS) +static int stat_char (); + +/* 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 */ + +/* **************************************************************** */ +/* */ +/* Completion matching, from readline's point of view. */ +/* */ +/* **************************************************************** */ + +/* Pointer to the generator function for completion_matches (). + NULL means to use filename_entry_function (), the default filename + completer. */ +Function *rl_completion_entry_function = (Function *)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. */ +Function *rl_attempted_completion_function = (Function *)NULL; + +/* Local variable states what happened during the last completion attempt. */ +static int completion_changed_buffer = 0; + +/* Complete the word at or before point. You have supplied the function + that does the initial simple matching selection algorithm (see + completion_matches ()). The default is to do filename completion. */ + +rl_complete (ignore, invoking_key) + int ignore, invoking_key; +{ + if (rl_last_func == rl_complete && !completion_changed_buffer) + rl_complete_internal ('?'); + else + rl_complete_internal (TAB); +} + +/* List the possible completions. See description of rl_complete (). */ +rl_possible_completions (ignore, invoking_key) +{ + rl_complete_internal ('?'); +} + +rl_insert_completions (ignore, invoking_key) + int ignore, invoking_key; +{ + rl_complete_internal ('*'); +} + +/* The user must press "y" or "n". Non-zero return means "y" pressed. */ +get_y_or_n () +{ + int c; + + for (;;) + { + c = rl_read_key (); + if (c == 'y' || c == 'Y') + return (1); + if (c == 'n' || c == 'N') + return (0); + if (c == ABORT_CHAR) + rl_abort (); + ding (); + } +} + +/* 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. */ +int rl_completion_query_items = 100; + +/* 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\"\\'`@$><=" */ +char *rl_basic_word_break_characters = " \t\n\"\\'`@$><=;|&{("; + +/* 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. */ +char *rl_completer_word_break_characters = (char *)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. */ +char *rl_completer_quote_characters = (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. */ +char *rl_special_prefixes = (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; + +/* 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. */ +Function *rl_ignore_some_completions_function = (Function *)NULL; + +/* 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. */ +rl_complete_internal (what_to_do) + int what_to_do; +{ + char *filename_completion_function (); + char **completion_matches (), **matches; + Function *our_func; + int start, scan, end, delimiter = 0; + char *text, *saved_line_buffer; + char quote_char = '\0'; + char *replacement; + + if (rl_line_buffer) + saved_line_buffer = savestring (rl_line_buffer); + else + saved_line_buffer = (char *)NULL; + + if (rl_completion_entry_function) + our_func = rl_completion_entry_function; + else + our_func = (int (*)())filename_completion_function; + + /* Only the completion entry function can change this. */ + rl_filename_completion_desired = 0; + + /* We now look backwards for the start of a filename/variable word. */ + end = rl_point; + + if (rl_point) + { + 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. + [FIXME: Doesn't yet handle '\' escapes to quote quotes. */ + for (scan = 0; scan < end; scan++) + { + if (quote_char != '\0') + { + /* Ignore everything until the matching close quote char. */ + if (rl_line_buffer[scan] == quote_char) + { + /* Found matching close quote. 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; + } + } + } + if (rl_point == end) + { + /* 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 do completion. */ + while (--rl_point && + !strchr (rl_completer_word_break_characters, + rl_line_buffer[rl_point])) {;} + } + + /* If we are at a word break, then advance past it. */ + if (strchr (rl_completer_word_break_characters, rl_line_buffer[rl_point])) + { + /* If the character that caused the word break was a quoting + character, then remember it as the delimiter. */ + if (strchr ("\"'", rl_line_buffer[rl_point]) && (end - rl_point) > 1) + delimiter = rl_line_buffer[rl_point]; + + /* 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 || + !strchr (rl_special_prefixes, rl_line_buffer[rl_point])) + rl_point++; + } + } + + start = rl_point; + rl_point = end; + text = rl_copy_text (start, end); + + /* 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 = + (char **)(*rl_attempted_completion_function) (text, start, end); + + if (matches) + { + if (matches == (char **)-1) + matches = (char **)NULL; + our_func = (Function *)NULL; + goto after_usual_completion; + } + } + + matches = completion_matches (text, our_func); + + after_usual_completion: + free (text); + + if (!matches) + ding (); + else + { + register int i; + + some_matches: + + /* 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) + { + char *lowest_common; + int j, newlen = 0; + + /* Sort the items. */ + /* It is safe to sort this array, because the lowest common + denominator found in matches[0] will remain in place. */ + for (i = 0; matches[i]; i++); + qsort (matches, i, sizeof (char *), compare_strings); + + /* Remember the lowest common denominator for it may be unique. */ + lowest_common = savestring (matches[0]); + + for (i = 0; matches[i + 1]; i++) + { + if (strcmp (matches[i], matches[i + 1]) == 0) + { + free (matches[i]); + matches[i] = (char *)-1; + } + else + newlen++; + } + + /* We have marked all the dead slots with (char *)-1. + Copy all the non-dead entries into a new array. */ + { + char **temp_array = + (char **)xmalloc ((3 + newlen) * sizeof (char *)); + + for (i = 1, j = 1; matches[i]; i++) + { + if (matches[i] != (char *)-1) + temp_array[j++] = matches[i]; + } + + temp_array[j] = (char *)NULL; + + if (matches[0] != (char *)-1) + free (matches[0]); + + free (matches); + + matches = temp_array; + } + + /* Place the lowest common denominator back in [0]. */ + matches[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 (matches[0], matches[1]) == 0) + { + free (matches[1]); + matches[1] = (char *)NULL; + } + } + + switch (what_to_do) + { + case TAB: + /* 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 && + our_func == (int (*)())filename_completion_function) + (void)(*rl_ignore_some_completions_function)(matches); + + /* 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 = matches[0]; + + if (matches[0] && rl_completer_quote_characters && !quote_char && + rl_filename_completion_desired) + { + int do_replace; + + do_replace = 0; + + /* If there is only a single match, see if we need to + quote it. */ + if (!matches[1] && + rl_strpbrk (matches[0], rl_completer_word_break_characters)) + do_replace = 1; + + /* If there are multiple matches, check to see if any of them + require that the substring be quoted. */ + for (i = 1; matches[i] != NULL; i++) + if (rl_strpbrk (matches[i], rl_completer_word_break_characters)) + { + do_replace = 1; + break; + } + if (do_replace) + { +#if defined (SHELL) + /* XXX - experimental */ + /* Single-quote the replacement, since we found an + embedded word break character in a potential match. */ + char *rtext; + extern char *single_quote (); /* in builtins/common.c */ + + rtext = single_quote (matches[0]); + replacement = (char *)alloca (strlen (rtext) + 1); + strcpy (replacement, rtext); + free (rtext); +#else /* !SHELL */ + /* Found an embedded word break character in a potential + match, so we need to prepend a quote character if we + are replacing the completion string. */ + replacement = (char *)alloca (strlen (matches[0]) + 2); + quote_char = *rl_completer_quote_characters; + *replacement = quote_char; + strcpy (replacement + 1, matches[0]); +#endif /* SHELL */ + } + } + if (replacement) + { + rl_delete_text (start, rl_point); + rl_point = start; + rl_insert_text (replacement); + } + + /* If there are more matches, ring the bell to indicate. + If this was the only match, and we are hacking files, + check the file to see if it was a directory. If so, + add a '/' to the name. If not, and we are at the end + of the line, then add a space. */ + if (matches[1]) + { + if (rl_editing_mode != vi_mode) + ding (); /* There are other matches remaining. */ + } + else + { + char temp_string[4]; + int temp_string_index = 0; + + if (quote_char) + temp_string[temp_string_index++] = quote_char; + + temp_string[temp_string_index++] = delimiter ? delimiter : ' '; + temp_string[temp_string_index++] = '\0'; + + if (rl_filename_completion_desired) + { + struct stat finfo; + char *filename = tilde_expand (matches[0]); + + if ((stat (filename, &finfo) == 0) && + S_ISDIR (finfo.st_mode)) + { + if (rl_line_buffer[rl_point] != '/') + rl_insert_text ("/"); + } + else + { + if (rl_point == rl_end) + rl_insert_text (temp_string); + } + free (filename); + } + else + { + if (rl_point == rl_end) + rl_insert_text (temp_string); + } + } + break; + + case '*': + { + int i = 1; + + rl_delete_text (start, rl_point); + rl_point = start; + rl_begin_undo_group (); + if (matches[1]) + { + while (matches[i]) + { + rl_insert_text (matches[i++]); + rl_insert_text (" "); + } + } + else + { + rl_insert_text (matches[0]); + rl_insert_text (" "); + } + rl_end_undo_group (); + } + break; + + case '?': + { + int len, count, limit, max = 0; + int j, k, l; + + /* Handle simple case first. What if there is only one answer? */ + if (!matches[1]) + { + char *temp; + + if (rl_filename_completion_desired) + temp = strrchr (matches[0], '/'); + else + temp = (char *)NULL; + + if (!temp) + temp = matches[0]; + else + temp++; + + crlf (); + fprintf (rl_outstream, "%s", temp); +#if defined (VISIBLE_STATS) + if (rl_filename_completion_desired && rl_visible_stats) + { + int extension_char; + + extension_char = stat_char (matches[0]); + if (extension_char) + putc (extension_char, rl_outstream); + } +#endif /* VISIBLE_STATS */ + crlf (); + goto restart; + } + + /* There is more than one answer. Find out how many there are, + and find out what the maximum printed length of a single entry + is. */ + for (i = 1; matches[i]; i++) + { + char *temp; + int name_length; + + /* If we are hacking filenames, then only count the characters + after the last slash in the pathname. */ + if (rl_filename_completion_desired) + temp = strrchr (matches[i], '/'); + else + temp = (char *)NULL; + + if (!temp) + temp = matches[i]; + else + temp++; + + name_length = strlen (temp); + + if (name_length > max) + max = name_length; + } + + len = i - 1; + + /* If there are many items, then ask the user if she + really wants to see them all. */ + if (len >= rl_completion_query_items) + { + crlf (); + fprintf (rl_outstream, + "There are %d possibilities. Do you really", len); + crlf (); + fprintf (rl_outstream, "wish to see them all? (y or n)"); + fflush (rl_outstream); + if (!get_y_or_n ()) + { + crlf (); + goto restart; + } + } + /* How many items of MAX length can we fit in the screen window? */ + max += 2; + limit = screenwidth / max; + if (limit != 1 && (limit * max == screenwidth)) + limit--; + + /* Avoid a possible floating exception. If max > screenwidth, + 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. */ + if (len < limit) + count = 1; + + /* Sort the items if they are not already sorted. */ + if (!rl_ignore_completion_duplicates) + qsort (matches, len, sizeof (char *), compare_strings); + + /* Print the sorted items, up-and-down alphabetically, like + ls might. */ + crlf (); + + for (i = 1; i < count + 1; i++) + { + for (j = 0, l = i; j < limit; j++) + { + if (l > len || !matches[l]) + { + break; + } + else + { + char *temp = (char *)NULL; + int printed_length; + + if (rl_filename_completion_desired) + temp = strrchr (matches[l], '/'); + + if (!temp) + temp = matches[l]; + else + temp++; + + printed_length = strlen (temp); + fprintf (rl_outstream, "%s", temp); + +#if defined (VISIBLE_STATS) + if (rl_filename_completion_desired && + rl_visible_stats) + { + int extension_char; + + extension_char = stat_char (matches[l]); + + if (extension_char) + { + putc (extension_char, rl_outstream); + printed_length++; + } + } +#endif /* VISIBLE_STATS */ + + if (j + 1 < limit) + { + for (k = 0; k < max - printed_length; k++) + putc (' ', rl_outstream); + } + } + l += count; + } + crlf (); + } + restart: + + rl_on_new_line (); + } + break; + + default: + abort (); + } + + for (i = 0; matches[i]; i++) + free (matches[i]); + free (matches); + } + + /* Check to see if the line has changed through all of this manipulation. */ + if (saved_line_buffer) + { + if (strcmp (rl_line_buffer, saved_line_buffer) != 0) + completion_changed_buffer = 1; + else + completion_changed_buffer = 0; + + free (saved_line_buffer); + } +} + +#if defined (VISIBLE_STATS) +/* Return the character which best describes FILENAME. + `@' for symbolic links + `/' for directories + `*' for executables + `=' for sockets */ +static int +stat_char (filename) + char *filename; +{ + struct stat finfo; + int character = 0; + + if (stat (filename, &finfo) == -1) + return (character); + + if (S_ISDIR (finfo.st_mode)) + character = '/'; +#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 */ + else if (S_ISREG (finfo.st_mode)) + { + if (access (filename, X_OK) == 0) + character = '*'; + } + return (character); +} +#endif /* VISIBLE_STATS */ + +/* Stupid comparison routine for qsort () ing strings. */ +static int +compare_strings (s1, s2) + char **s1, **s2; +{ + return (strcmp (*s1, *s2)); +} + +/* A completion function for usernames. + TEXT contains a partial username preceded by a random + character (usually `~'). */ +char * +username_completion_function (text, state) + int state; + char *text; +{ +#ifdef __GO32__ + return (char *)NULL; +#else /* !__GO32__ */ + static char *username = (char *)NULL; + static struct passwd *entry; + static int namelen, first_char, first_char_loc; + + if (!state) + { + if (username) + free (username); + + first_char = *text; + + if (first_char == '~') + first_char_loc = 1; + else + first_char_loc = 0; + + username = savestring (&text[first_char_loc]); + namelen = strlen (username); + setpwent (); + } + + while (entry = getpwent ()) + { + if (strncmp (username, entry->pw_name, namelen) == 0) + break; + } + + if (!entry) + { + endpwent (); + return ((char *)NULL); + } + else + { + char *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 /* !__GO32__ */ +} + + +/* **************************************************************** */ +/* */ +/* Completion */ +/* */ +/* **************************************************************** */ + +/* Non-zero means that case is not significant in completion. */ +int completion_case_fold = 0; + +/* 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 ** +completion_matches (text, entry_function) + char *text; + char *(*entry_function) (); +{ + /* Number of slots in match_list. */ + int match_list_size; + + /* The list of matches. */ + char **match_list = + (char **)xmalloc (((match_list_size = 10) + 1) * sizeof (char *)); + + /* Number of matches actually found. */ + int matches = 0; + + /* Temporary string binder. */ + char *string; + + match_list[1] = (char *)NULL; + + while (string = (*entry_function) (text, matches)) + { + if (matches + 1 == match_list_size) + match_list = (char **)xrealloc + (match_list, ((match_list_size += 10) + 1) * sizeof (char *)); + + 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) + { + register int i = 1; + int low = 100000; /* Count of max-matched characters. */ + + /* If only one match, just use that. */ + if (matches == 1) + { + match_list[0] = match_list[1]; + match_list[1] = (char *)NULL; + } + else + { + /* Otherwise, compare each member of the list with + the next, finding out where they stop matching. */ + + while (i < matches) + { + register int c1, c2, si; + + if (completion_case_fold) + { + for (si = 0; + (c1 = to_lower(match_list[i][si])) && + (c2 = to_lower(match_list[i + 1][si])); + si++) + if (c1 != c2) break; + } + else + { + for (si = 0; + (c1 = match_list[i][si]) && + (c2 = match_list[i + 1][si]); + si++) + if (c1 != c2) break; + } + + if (low > si) low = si; + i++; + } + match_list[0] = (char *)xmalloc (low + 1); + strncpy (match_list[0], match_list[1], low); + match_list[0][low] = '\0'; + } + } + else /* There were no matches. */ + { + free (match_list); + match_list = (char **)NULL; + } + return (match_list); +} + +/* 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 * +filename_completion_function (text, state) + int state; + char *text; +{ + static DIR *directory; + static char *filename = (char *)NULL; + static char *dirname = (char *)NULL; + static char *users_dirname = (char *)NULL; + static int filename_len; + + dirent *entry = (dirent *)NULL; + + /* If we don't have any state, then do some initialization. */ + if (!state) + { + char *temp; + + if (dirname) free (dirname); + if (filename) free (filename); + if (users_dirname) free (users_dirname); + + filename = savestring (text); + if (!*text) text = "."; + dirname = savestring (text); + + temp = strrchr (dirname, '/'); + + if (temp) + { + strcpy (filename, ++temp); + *temp = '\0'; + } + else + strcpy (dirname, "."); + + /* We aren't done yet. We also support the "~user" syntax. */ + + /* Save the version of the directory that the user typed. */ + users_dirname = savestring (dirname); + { + char *temp_dirname; + + temp_dirname = tilde_expand (dirname); + free (dirname); + dirname = temp_dirname; + + if (rl_symbolic_link_hook) + (*rl_symbolic_link_hook) (&dirname); + } + directory = opendir (dirname); + 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/te. 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. */ + + while (directory && (entry = readdir (directory))) + { + /* Special case for no filename. + All entries except "." and ".." match. */ + if (!filename_len) + { + if ((strcmp (entry->d_name, ".") != 0) && + (strcmp (entry->d_name, "..") != 0)) + break; + } + else + { + /* Otherwise, if these match upto the length of filename, then + it is a match. */ + if (((int)D_NAMLEN (entry)) >= filename_len && + (entry->d_name[0] == filename[0]) && + (strncmp (filename, entry->d_name, filename_len) == 0)) + { + break; + } + } + } + + if (!entry) + { + if (directory) + { + closedir (directory); + directory = (DIR *)NULL; + } + + if (dirname) + { + free (dirname); + dirname = (char *)NULL; + } + if (filename) + { + free (filename); + filename = (char *)NULL; + } + if (users_dirname) + { + free (users_dirname); + users_dirname = (char *)NULL; + } + + return (char *)NULL; + } + else + { + char *temp; + + if (dirname && (strcmp (dirname, ".") != 0)) + { + if (rl_complete_with_tilde_expansion && *users_dirname == '~') + { + int dirlen = strlen (dirname); + temp = (char *)xmalloc (2 + dirlen + D_NAMLEN (entry)); + strcpy (temp, dirname); + /* Canonicalization cuts off any final slash present. We need + to add it back. */ + if (dirname[dirlen - 1] != '/') + { + temp[dirlen] = '/'; + temp[dirlen + 1] = '\0'; + } + } + else + { + temp = (char *) + xmalloc (1 + strlen (users_dirname) + D_NAMLEN (entry)); + strcpy (temp, users_dirname); + } + + strcat (temp, entry->d_name); + } + else + { + temp = (savestring (entry->d_name)); + } + return (temp); + } +} + +/* A function for simple tilde expansion. */ +int +rl_tilde_expand (ignore, key) + int ignore, key; +{ + register int start, end; + char *homedir; + + end = rl_point; + start = end - 1; + + if (rl_point == rl_end && rl_line_buffer[rl_point] == '~') + { + homedir = tilde_expand ("~"); + goto insert; + } + else if (rl_line_buffer[start] != '~') + { + for (; !whitespace (rl_line_buffer[start]) && start >= 0; start--); + start++; + } + + end = start; + do + { + end++; + } + while (!whitespace (rl_line_buffer[end]) && end < rl_end); + + if (whitespace (rl_line_buffer[end]) || end >= rl_end) + end--; + + /* If the first character of the current word is a tilde, perform + tilde expansion and insert the result. If not a tilde, do + nothing. */ + if (rl_line_buffer[start] == '~') + { + char *temp; + int len; + + len = end - start + 1; + temp = (char *)alloca (len + 1); + strncpy (temp, rl_line_buffer + start, len); + temp[len] = '\0'; + homedir = tilde_expand (temp); + + insert: + rl_begin_undo_group (); + rl_delete_text (start, end + 1); + rl_point = start; + rl_insert_text (homedir); + rl_end_undo_group (); + } + + return (0); +} + +/* Find the first occurrence in STRING1 of any character from STRING2. + Return a pointer to the character in STRING1. */ +static char * +rl_strpbrk (string1, string2) + char *string1, *string2; +{ + register char *scan; + + for (; *string1; string1++) + { + for (scan = string2; *scan; scan++) + { + if (*string1 == *scan) + { + return (string1); + } + } + } + return ((char *)NULL); +} + +#if defined (STATIC_MALLOC) + +/* **************************************************************** */ +/* */ +/* xmalloc and xrealloc () */ +/* */ +/* **************************************************************** */ + +static void memory_error_and_abort (); + +static char * +xmalloc (bytes) + int bytes; +{ + char *temp = (char *)malloc (bytes); + + if (!temp) + memory_error_and_abort (); + return (temp); +} + +static char * +xrealloc (pointer, bytes) + char *pointer; + int bytes; +{ + char *temp; + + if (!pointer) + temp = (char *)malloc (bytes); + else + temp = (char *)realloc (pointer, bytes); + + if (!temp) + memory_error_and_abort (); + + return (temp); +} + +static void +memory_error_and_abort () +{ + fprintf (stderr, "readline: Out of virtual memory!\n"); + abort (); +} +#endif /* STATIC_MALLOC */ diff --git a/readline/display.c b/readline/display.c new file mode 100644 index 0000000000..c889318a3d --- /dev/null +++ b/readline/display.c @@ -0,0 +1,801 @@ +/* display.c -- readline redisplay facility. */ + +/* Copyright (C) 1987, 1989, 1992 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library, a library for + reading lines of text with interactive input and history editing. + + The GNU Readline Library is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 1, or + (at your option) any later version. + + The GNU Readline Library is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied warranty + of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + The GNU General Public License is often shipped with GNU software, and + is generally kept in a file called COPYING or LICENSE. If you do not + have a copy of the license, write to the Free Software Foundation, + 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include + +/* System-specific feature definitions and include files. */ +#include "rldefs.h" + +/* Some standard library routines. */ +#include "readline.h" +#include "history.h" + +#if !defined (strrchr) +extern char *strrchr (); +#endif /* !strchr */ + +/* Global and pseudo-global variables and functions + imported from readline.c. */ +extern char *rl_prompt; +extern int readline_echoing_p; +extern char *term_clreol, *term_im, *term_ic, *term_ei, *term_DC; +/* Termcap variables. */ +extern char *term_up, *term_dc, *term_cr, *term_IC; +extern int screenheight, screenwidth, terminal_can_insert; + +extern void _rl_output_some_chars (); +extern void _rl_output_character_function (); + +extern int _rl_convert_meta_chars_to_ascii; +extern int _rl_horizontal_scroll_mode; +extern int _rl_mark_modified_lines; +extern int _rl_prefer_visible_bell; + +/* Pseudo-global functions (local to the readline library) exported + by this file. */ +void _rl_move_cursor_relative (), _rl_output_some_chars (); +void _rl_move_vert (); + +static void update_line (), clear_to_eol (); +static void delete_chars (), insert_some_chars (); + +extern char *xmalloc (), *xrealloc (); + +/* **************************************************************** */ +/* */ +/* 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. Carefull attention needs + to be paid to the vertical position variables. + + handling of terminals with autowrap on (incl. DEC braindamage) + could be improved a bit. Right now I just cheat and decrement + screenwidth by one. */ + +/* 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. */ + +/* What YOU turn on when you have handled all redisplay yourself. */ +int rl_display_fixed = 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; + +/* Pseudo-global variables declared here. */ +/* The visible cursor position. If you print some text, adjust this. */ +int _rl_last_c_pos = 0; +int _rl_last_v_pos = 0; + +/* 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 = 0; + +/* The line display buffers. One is the line currently displayed on + the screen. The other is the line about to be displayed. */ +static char *visible_line = (char *)NULL; +static char *invisible_line = (char *)NULL; + +/* A buffer for `modeline' messages. */ +static char msg_buf[128]; + +/* Non-zero forces the redisplay even if we thought it was unnecessary. */ +static int forced_display = 0; + +/* Default and initial buffer size. Can grow. */ +static int line_size = 1024; + +/* Basic redisplay algorithm. */ +rl_redisplay () +{ + register int in, out, c, linenum; + register char *line = invisible_line; + char *prompt_this_line; + int c_pos = 0; + int inv_botlin = 0; /* Number of lines in newly drawn buffer. */ + + if (!readline_echoing_p) + return; + + if (!rl_display_prompt) + rl_display_prompt = ""; + + if (!invisible_line) + { + visible_line = (char *)xmalloc (line_size); + invisible_line = (char *)xmalloc (line_size); + line = invisible_line; + for (in = 0; in < line_size; in++) + { + visible_line[in] = 0; + invisible_line[in] = 1; + } + rl_on_new_line (); + } + + /* Draw the line into the buffer. */ + c_pos = -1; + + /* Mark the line as modified or not. We only do this for history + lines. */ + out = 0; + if (_rl_mark_modified_lines && current_history () && rl_undo_list) + { + line[out++] = '*'; + line[out] = '\0'; + } + + /* 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; + + prompt_this_line = strrchr (rl_display_prompt, '\n'); + if (!prompt_this_line) + prompt_this_line = rl_display_prompt; + else + { + prompt_this_line++; + if (forced_display) + _rl_output_some_chars + (rl_display_prompt, prompt_this_line - rl_display_prompt); + } + + strncpy (line + out, prompt_this_line, strlen (prompt_this_line)); + out += strlen (prompt_this_line); + line[out] = '\0'; + + for (in = 0; in < rl_end; in++) + { + c = (unsigned char)rl_line_buffer[in]; + + 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) + c_pos = out; + + if (META_CHAR (c)) + { + if (_rl_convert_meta_chars_to_ascii) + { + sprintf (line + out, "\\%o", c); + out += 4; + } + else + line[out++] = c; + } +#define DISPLAY_TABS +#if defined (DISPLAY_TABS) + else if (c == '\t') + { + register int newout = (out | (int)7) + 1; + while (out < newout) + line[out++] = ' '; + } +#endif + else if (c < ' ') + { + line[out++] = '^'; + line[out++] = UNCTRL (c); /* XXX was c ^ 0x40 */ + } + else if (c == 127) + { + line[out++] = '^'; + line[out++] = '?'; + } + else + line[out++] = c; + } + line[out] = '\0'; + if (c_pos < 0) + c_pos = out; + + /* 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. */ + + if (!_rl_horizontal_scroll_mode && term_up && *term_up) + { + int total_screen_chars = (screenwidth * screenheight); + + 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. I'll fix this in a minute. */ + if (out >= total_screen_chars) + out = total_screen_chars - 1; + + /* Number of screen lines to display. */ + inv_botlin = out / screenwidth; + + /* For each line in the buffer, do the updating display. */ + for (linenum = 0; linenum <= inv_botlin; linenum++) + update_line (linenum > _rl_vis_botlin ? "" + : &visible_line[linenum * screenwidth], + &invisible_line[linenum * screenwidth], + linenum); + + /* 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 = &visible_line[linenum * screenwidth]; + _rl_move_vert (linenum); + _rl_move_cursor_relative (0, tt); + clear_to_eol + ((linenum == _rl_vis_botlin) ? strlen (tt) : screenwidth); + } + } + _rl_vis_botlin = inv_botlin; + + /* Move the cursor where it should be. */ + _rl_move_vert (c_pos / screenwidth); + _rl_move_cursor_relative (c_pos % screenwidth, + &invisible_line[(c_pos / screenwidth) * screenwidth]); + } + } + else /* Do horizontal scrolling. */ + { + int lmargin; + + /* Always at top line. */ + _rl_last_v_pos = 0; + + /* If the display position of the cursor would be off the edge + of the screen, start the display of this line at an offset that + leaves the cursor on the screen. */ + if (c_pos - last_lmargin > screenwidth - 2) + lmargin = (c_pos / (screenwidth / 3) - 2) * (screenwidth / 3); + else if (c_pos - last_lmargin < 1) + lmargin = ((c_pos - 1) / (screenwidth / 3)) * (screenwidth / 3); + else + lmargin = last_lmargin; + + /* 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 (lmargin + screenwidth < out) + line[lmargin + screenwidth - 1] = '>'; + + if (!rl_display_fixed || forced_display || lmargin != last_lmargin) + { + forced_display = 0; + update_line (&visible_line[last_lmargin], + &invisible_line[lmargin], 0); + + _rl_move_cursor_relative (c_pos - lmargin, &invisible_line[lmargin]); + last_lmargin = lmargin; + } + } + fflush (rl_outstream); + + /* Swap visible and non-visible lines. */ + { + char *temp = visible_line; + visible_line = invisible_line; + invisible_line = temp; + rl_display_fixed = 0; + } +} + +/* 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 handeled. + + Could be made even smarter, but this works well enough */ +static void +update_line (old, new, current_line) + register char *old, *new; + int current_line; +{ + register char *ofd, *ols, *oe, *nfd, *nls, *ne; + int lendiff, wsatend; + + /* Find first difference. */ + for (ofd = old, nfd = new; + (ofd - old < screenwidth) && *ofd && (*ofd == *nfd); + ofd++, nfd++) + ; + + /* Move to the end of the screen line. */ + for (oe = ofd; ((oe - old) < screenwidth) && *oe; oe++); + for (ne = nfd; ((ne - new) < screenwidth) && *ne; ne++); + + /* If no difference, continue to next line. */ + if (ofd == oe && nfd == ne) + return; + + wsatend = 1; /* flag for trailing whitespace */ + ols = oe - 1; /* find last same */ + nls = ne - 1; + while ((ols > ofd) && (nls > nfd) && (*ols == *nls)) + { + if (*ols != ' ') + wsatend = 0; + ols--; + nls--; + } + + if (wsatend) + { + ols = oe; + nls = ne; + } + else if (*ols != *nls) + { + if (*ols) /* don't step past the NUL */ + ols++; + if (*nls) + nls++; + } + + _rl_move_vert (current_line); + _rl_move_cursor_relative (ofd - old, old); + + /* if (len (new) > len (old)) */ + lendiff = (nls - nfd) - (ols - ofd); + + /* Insert (diff (len (old), len (new)) ch. */ + if (lendiff > 0) + { + if (terminal_can_insert) + { + /* Sometimes it is cheaper to print the characters rather than + use the terminal's capabilities. */ + if ((2 * (ne - nfd)) < lendiff && !term_IC) + { + _rl_output_some_chars (nfd, (ne - nfd)); + _rl_last_c_pos += (ne - nfd); + } + else + { + if (*ols) + { + insert_some_chars (nfd, lendiff); + _rl_last_c_pos += lendiff; + } + else + { + /* 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, lendiff); + _rl_last_c_pos += lendiff; + } + /* Copy (new) chars to screen from first diff to last match. */ + if (((nls - nfd) - lendiff) > 0) + { + _rl_output_some_chars (&nfd[lendiff], ((nls - nfd) - lendiff)); + _rl_last_c_pos += ((nls - nfd) - lendiff); + } + } + } + else + { /* cannot insert chars, write to EOL */ + _rl_output_some_chars (nfd, (ne - nfd)); + _rl_last_c_pos += (ne - nfd); + } + } + else /* Delete characters from line. */ + { + /* If possible and inexpensive to use terminal deletion, then do so. */ + if (term_dc && (2 * (ne - nfd)) >= (-lendiff)) + { + if (lendiff) + delete_chars (-lendiff); /* delete (diff) characters */ + + /* Copy (new) chars to screen from first diff to last match */ + if ((nls - nfd) > 0) + { + _rl_output_some_chars (nfd, (nls - nfd)); + _rl_last_c_pos += (nls - nfd); + } + } + /* Otherwise, print over the existing material. */ + else + { + _rl_output_some_chars (nfd, (ne - nfd)); + _rl_last_c_pos += (ne - nfd); + clear_to_eol ((oe - old) - (ne - new)); + } + } +} + +/* Tell the update routines that we have moved onto a new (empty) line. */ +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; +} + +/* Actually update the display, period. */ +rl_forced_update_display () +{ + if (visible_line) + { + register char *temp = visible_line; + + while (*temp) *temp++ = '\0'; + } + rl_on_new_line (); + forced_display++; + rl_redisplay (); +} + +/* Move the cursor from _rl_last_c_pos to NEW, which are buffer indices. + 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; + char *data; +{ + register int i; + + /* It may be faster to output a CR, and then move forwards instead + of moving backwards. */ + if (new + 1 < _rl_last_c_pos - new) + { +#ifdef __MSDOS__ + putc('\r', rl_outstream); +#else + tputs (term_cr, 1, _rl_output_character_function); +#endif + _rl_last_c_pos = 0; + } + + if (_rl_last_c_pos == new) return; + + if (_rl_last_c_pos < new) + { + /* 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. */ +#if defined (HACK_TERMCAP_MOTION) + extern char *term_forward_char; + + if (term_forward_char) + for (i = _rl_last_c_pos; i < new; i++) + tputs (term_forward_char, 1, _rl_output_character_function); + else + for (i = _rl_last_c_pos; i < new; i++) + putc (data[i], rl_outstream); +#else + for (i = _rl_last_c_pos; i < new; i++) + putc (data[i], rl_outstream); +#endif /* HACK_TERMCAP_MOTION */ + } + else + backspace (_rl_last_c_pos - new); + _rl_last_c_pos = new; +} + +/* 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 > screenheight) + return; + +#ifdef __GO32__ + { + int row, col; + ScreenGetCursor (&row, &col); + ScreenSetCursor ((row + to - _rl_last_v_pos), col); + } +#else /* __GO32__ */ + if ((delta = to - _rl_last_v_pos) > 0) + { + for (i = 0; i < delta; i++) + putc ('\n', rl_outstream); + tputs (term_cr, 1, _rl_output_character_function); + _rl_last_c_pos = 0; + } + else + { /* delta < 0 */ + if (term_up && *term_up) + for (i = 0; i < -delta; i++) + tputs (term_up, 1, _rl_output_character_function); + } +#endif /* !__GO32__ */ + _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. */ +rl_show_char (c) + int c; +{ + if (META_CHAR (c) && _rl_convert_meta_chars_to_ascii) + { + fprintf (rl_outstream, "M-"); + c = UNMETA (c); + } + +#if defined (DISPLAY_TABS) + if (c < 32 && c != '\t') +#else + if (c < 32) +#endif /* !DISPLAY_TABS */ + { + + c += 64; + } + + putc (c, rl_outstream); + fflush (rl_outstream); +} + +int +rl_character_len (c, pos) + register int c, pos; +{ + if (META_CHAR (c)) + return (_rl_convert_meta_chars_to_ascii ? 4 : 1); + + if (c == '\t') + { +#if defined (DISPLAY_TABS) + return (((pos | (int)7) + 1) - pos); +#else + return (2); +#endif /* !DISPLAY_TABS */ + } + + if (isprint (c)) + return (1); + else + return (2); +} + +/* How to print things in the "echo-area". The prompt is treated as a + mini-modeline. */ + +#if defined (HAVE_VARARGS_H) +rl_message (va_alist) + va_dcl +{ + char *format; + va_list args; + + va_start (args); + format = va_arg (args, char *); + vsprintf (msg_buf, format, args); + va_end (args); + + rl_display_prompt = msg_buf; + rl_redisplay (); +} +#else /* !HAVE_VARARGS_H */ +rl_message (format, arg1, arg2) + char *format; +{ + sprintf (msg_buf, format, arg1, arg2); + rl_display_prompt = msg_buf; + rl_redisplay (); +} +#endif /* !HAVE_VARARGS_H */ + +/* How to clear things from the "echo-area". */ +rl_clear_message () +{ + rl_display_prompt = rl_prompt; + rl_redisplay (); +} + +rl_reset_line_state () +{ + rl_on_new_line (); + + rl_display_prompt = rl_prompt ? rl_prompt : ""; + forced_display = 1; +} + +/* 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; + + backspace (l); + for (i = 0; i < l; i++) + putc (' ', rl_outstream); + 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, */ +static void +clear_to_eol (count) + int count; +{ +#ifndef __GO32__ + if (term_clreol) + { + tputs (term_clreol, 1, _rl_output_character_function); + } + else +#endif /* !__GO32__ */ + { + register int i; + + /* Do one more character space. */ + count++; + + for (i = 0; i < count; i++) + putc (' ', rl_outstream); + + backspace (count); + } +} +/* Insert COUNT characters from STRING to the output stream. */ +static void +insert_some_chars (string, count) + char *string; + int count; +{ +#ifdef __GO32__ + int row, col, width; + char *row_start; + + ScreenGetCursor (&row, &col); + width = ScreenCols (); + row_start = ScreenPrimary + (row * width); + memcpy (row_start + col + count, row_start + col, width - col - count); + /* Place the text on the screen. */ + _rl_output_some_chars (string, count); +#else /* __GO32__ */ + /* If IC is defined, then we do not have to "enter" insert mode. */ + if (term_IC) + { + char *tgoto (), *buffer; + buffer = tgoto (term_IC, 0, count); + tputs (buffer, 1, _rl_output_character_function); + _rl_output_some_chars (string, count); + } + else + { + register int i; + + /* If we have to turn on insert-mode, then do so. */ + if (term_im && *term_im) + tputs (term_im, 1, _rl_output_character_function); + + /* If there is a special command for inserting characters, then + use that first to open up the space. */ + if (term_ic && *term_ic) + { + for (i = count; i--; ) + tputs (term_ic, 1, _rl_output_character_function); + } + + /* Print the text. */ + _rl_output_some_chars (string, count); + + /* If there is a string to turn off insert mode, we had best use + it now. */ + if (term_ei && *term_ei) + tputs (term_ei, 1, _rl_output_character_function); + } +#endif /* __GO32__ */ +} + +/* Delete COUNT characters from the display line. */ +static void +delete_chars (count) + int count; +{ +#if defined (__GO32__) + int row, col, width; + char *row_start; + + ScreenGetCursor (&row, &col); + width = ScreenCols (); + row_start = ScreenPrimary + (row * width); + memcpy (row_start + col, row_start + col + count, width - col - count); + memset (row_start + width - count, 0, count * 2); +#else /* !__GO32__ */ + if (count > screenwidth) + return; + + if (term_DC && *term_DC) + { + char *tgoto (), *buffer; + buffer = tgoto (term_DC, 0, count); + tputs (buffer, 1, _rl_output_character_function); + } + else + { + if (term_dc && *term_dc) + while (count--) + tputs (term_dc, 1, _rl_output_character_function); + } +#endif /* !__GO32__ */ +} diff --git a/readline/isearch.c b/readline/isearch.c new file mode 100644 index 0000000000..9b44c9342c --- /dev/null +++ b/readline/isearch.c @@ -0,0 +1,378 @@ +/* **************************************************************** */ +/* */ +/* I-Search and Searching */ +/* */ +/* **************************************************************** */ + +/* Copyright (C) 1987,1989 Free Software Foundation, Inc. + + This file contains the Readline Library (the Library), a set of + routines for providing Emacs style line input to programs that ask + for it. + + The Library is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + The Library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + The GNU General Public License is often shipped with GNU software, and + is generally kept in a file called COPYING or LICENSE. If you do not + have a copy of the license, write to the Free Software Foundation, + 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include + +#if defined (__GNUC__) +# define alloca __builtin_alloca +#else +# if defined (sparc) || defined (HAVE_ALLOCA_H) +# include +# endif +#endif + +#include "readline.h" +#include "history.h" + +extern Keymap _rl_keymap; +extern HIST_ENTRY *saved_line_for_history; +extern int rl_line_buffer_len; +extern int rl_point, rl_end; +extern char *rl_prompt, *rl_line_buffer; + +/* Remove these declarations when we have a complete libgnu.a. */ +extern char *xmalloc (), *xrealloc (); + +static void rl_search_history (); + +/* Search backwards through the history looking for a string which is typed + interactively. Start with the current line. */ +rl_reverse_search_history (sign, key) + int sign; + int key; +{ + rl_search_history (-sign, key); +} + +/* Search forwards through the history looking for a string which is typed + interactively. Start with the current line. */ +rl_forward_search_history (sign, key) + int sign; + int key; +{ + rl_search_history (sign, key); +} + +/* Display the current state of the search in the echo-area. + SEARCH_STRING contains the string that is being searched for, + DIRECTION is zero for forward, or 1 for reverse, + WHERE is the history list number of the current line. If it is + -1, then this line is the starting one. */ +static void +rl_display_search (search_string, reverse_p, where) + char *search_string; + int reverse_p, where; +{ + char *message = (char *)NULL; + + message = + (char *)xmalloc (1 + (search_string ? strlen (search_string) : 0) + 30); + + *message = '\0'; + +#if defined (NOTDEF) + if (where != -1) + sprintf (message, "[%d]", where + history_base); +#endif /* NOTDEF */ + + strcat (message, "("); + + if (reverse_p) + strcat (message, "reverse-"); + + strcat (message, "i-search)`"); + + if (search_string) + strcat (message, search_string); + + strcat (message, "': "); + rl_message ("%s", message, 0); + free (message); + rl_redisplay (); +} + +/* Search through the history looking for an interactively typed string. + This is analogous to i-search. We start the search in the current line. + DIRECTION is which direction to search; >= 0 means forward, < 0 means + backwards. */ +static void +rl_search_history (direction, invoking_key) + int direction; + int invoking_key; +{ + /* The string that the user types in to search for. */ + char *search_string; + + /* The current length of SEARCH_STRING. */ + int search_string_index; + + /* The amount of space that SEARCH_STRING has allocated to it. */ + int search_string_size; + + /* The list of lines to search through. */ + char **lines; + + /* The length of LINES. */ + int hlen; + + /* Where we get LINES from. */ + HIST_ENTRY **hlist = history_list (); + + register int i = 0; + int orig_point = rl_point; + int orig_line = where_history (); + int last_found_line = orig_line; + int c, done = 0; + + /* The line currently being searched. */ + char *sline; + + /* Offset in that line. */ + int index; + + /* Non-zero if we are doing a reverse search. */ + int reverse = (direction < 0); + + /* Create an arrary of pointers to the lines that we want to search. */ + maybe_replace_line (); + if (hlist) + for (i = 0; hlist[i]; i++); + + /* Allocate space for this many lines, +1 for the current input line, + and remember those lines. */ + lines = (char **)alloca ((1 + (hlen = i)) * sizeof (char *)); + for (i = 0; i < hlen; i++) + lines[i] = hlist[i]->line; + + if (saved_line_for_history) + lines[i] = saved_line_for_history->line; + else + /* So I have to type it in this way instead. */ + { + char *alloced_line; + + /* Keep that MIPS alloca () happy. */ + alloced_line = (char *)alloca (1 + strlen (rl_line_buffer)); + lines[i] = alloced_line; + strcpy (lines[i], &rl_line_buffer[0]); + } + + hlen++; + + /* The line where we start the search. */ + i = orig_line; + + /* Initialize search parameters. */ + search_string = (char *)xmalloc (search_string_size = 128); + *search_string = '\0'; + search_string_index = 0; + + /* Normalize DIRECTION into 1 or -1. */ + if (direction >= 0) + direction = 1; + else + direction = -1; + + rl_display_search (search_string, reverse, -1); + + sline = rl_line_buffer; + index = rl_point; + + while (!done) + { + c = rl_read_key (); + + /* Hack C to Do What I Mean. */ + { + Function *f = (Function *)NULL; + + if (_rl_keymap[c].type == ISFUNC) + { + f = _rl_keymap[c].function; + + if (f == rl_reverse_search_history) + c = reverse ? -1 : -2; + else if (f == rl_forward_search_history) + c = !reverse ? -1 : -2; + } + } + + switch (c) + { + case ESC: + done = 1; + continue; + + /* case invoking_key: */ + case -1: + goto search_again; + + /* switch directions */ + case -2: + direction = -direction; + reverse = (direction < 0); + + goto do_search; + + case CTRL ('G'): + strcpy (rl_line_buffer, lines[orig_line]); + rl_point = orig_point; + rl_end = strlen (rl_line_buffer); + rl_clear_message (); + return; + + default: + if (c < 32 || c > 126) + { + rl_execute_next (c); + done = 1; + continue; + } + else + { + if (search_string_index + 2 >= search_string_size) + search_string = (char *)xrealloc + (search_string, (search_string_size += 128)); + search_string[search_string_index++] = c; + search_string[search_string_index] = '\0'; + goto do_search; + + search_again: + + if (!search_string_index) + continue; + else + { + if (reverse) + --index; + else + if (index != strlen (sline)) + ++index; + else + ding (); + } + do_search: + + while (1) + { + if (reverse) + { + while (index >= 0) + if (strncmp + (search_string, sline + index, search_string_index) + == 0) + goto string_found; + else + index--; + } + else + { + register int limit = + (strlen (sline) - search_string_index) + 1; + + while (index < limit) + { + if (strncmp (search_string, + sline + index, + search_string_index) == 0) + goto string_found; + index++; + } + } + + next_line: + i += direction; + + /* At limit for direction? */ + if ((reverse && i < 0) || + (!reverse && i == hlen)) + goto search_failed; + + sline = lines[i]; + if (reverse) + index = strlen (sline); + else + index = 0; + + /* If the search string is longer than the current + line, no match. */ + if (search_string_index > (int)strlen (sline)) + goto next_line; + + /* Start actually searching. */ + if (reverse) + index -= search_string_index; + } + + search_failed: + /* We cannot find the search string. Ding the bell. */ + ding (); + i = last_found_line; + break; + + string_found: + /* We have found the search string. Just display it. But don't + actually move there in the history list until the user accepts + the location. */ + { + int line_len; + + line_len = strlen (lines[i]); + + if (line_len >= rl_line_buffer_len) + rl_extend_line_buffer (line_len); + + strcpy (rl_line_buffer, lines[i]); + rl_point = index; + rl_end = line_len; + last_found_line = i; + rl_display_search + (search_string, reverse, (i == orig_line) ? -1 : i); + } + } + } + continue; + } + + /* The searching is over. The user may have found the string that she + was looking for, or else she may have exited a failing search. If + INDEX is -1, then that shows that the string searched for was not + found. We use this to determine where to place rl_point. */ + { + int now = last_found_line; + + /* First put back the original state. */ + strcpy (rl_line_buffer, lines[orig_line]); + + /* Free the search string. */ + free (search_string); + + if (now < orig_line) + rl_get_previous_history (orig_line - now); + else + rl_get_next_history (now - orig_line); + + /* If the index of the "matched" string is less than zero, then the + final search string was never matched, so put point somewhere + reasonable. */ + if (index < 0) + index = strlen (rl_line_buffer); + + rl_point = index; + rl_clear_message (); + } +} diff --git a/readline/keymaps.h b/readline/keymaps.h index 3c577b398f..5fd0cba150 100644 --- a/readline/keymaps.h +++ b/readline/keymaps.h @@ -1,5 +1,25 @@ /* keymaps.h -- Manipulation of readline keymaps. */ +/* Copyright (C) 1987, 1989, 1992 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library, a library for + reading lines of text with interactive input and history editing. + + The GNU Readline Library is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 1, or + (at your option) any later version. + + The GNU Readline Library is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied warranty + of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + The GNU General Public License is often shipped with GNU software, and + is generally kept in a file called COPYING or LICENSE. If you do not + have a copy of the license, write to the Free Software Foundation, + 675 Mass Ave, Cambridge, MA 02139, USA. */ + #ifndef _KEYMAPS_H_ #define _KEYMAPS_H_ @@ -20,12 +40,17 @@ typedef struct _keymap_entry { Function *function; } KEYMAP_ENTRY; +/* This must be large enough to hold bindings for all of the characters + in a desired character set (e.g, 128 for ASCII, 256 for ISO Latin-x, + and so on). */ +#define KEYMAP_SIZE 256 + /* I wanted to make the above structure contain a union of: union { Function *function; struct _keymap_entry *keymap; } value; but this made it impossible for me to create a static array. Maybe I need C lessons. */ -typedef KEYMAP_ENTRY KEYMAP_ENTRY_ARRAY[128]; +typedef KEYMAP_ENTRY KEYMAP_ENTRY_ARRAY[KEYMAP_SIZE]; typedef KEYMAP_ENTRY *Keymap; /* The values that TYPE can have in a keymap entry. */ @@ -48,6 +73,14 @@ Keymap rl_copy_keymap (); the Meta digits bound to produce numeric arguments. */ Keymap rl_make_keymap (); -#endif /* _KEYMAPS_H_ */ +/* Return the keymap corresponding to a given name. Names look like + `emacs' or `emacs-meta' or `vi-insert'. */ +Keymap rl_get_keymap_by_name (); +/* Return the current keymap. */ +Keymap rl_get_keymap (); +/* Set the current keymap to MAP. */ +void rl_set_keymap (); + +#endif /* _KEYMAPS_H_ */ diff --git a/readline/parens.c b/readline/parens.c new file mode 100644 index 0000000000..2c96012352 --- /dev/null +++ b/readline/parens.c @@ -0,0 +1,115 @@ +/* parens.c -- Implemenation of matching parenthesis feature. */ + +/* Copyright (C) 1987, 1989, 1992 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library, a library for + reading lines of text with interactive input and history editing. + + The GNU Readline Library is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 1, or + (at your option) any later version. + + The GNU Readline Library is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied warranty + of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + The GNU General Public License is often shipped with GNU software, and + is generally kept in a file called COPYING or LICENSE. If you do not + have a copy of the license, write to the Free Software Foundation, + 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include +#include +#include "readline.h" + +/* Non-zero means try to blink the matching open parenthesis when the + close parenthesis is inserted. */ +#if defined (FD_SET) +int rl_blink_matching_paren = 1; +#else /* !FD_SET */ +int rl_blink_matching_paren = 0; +#endif /* !FD_SET */ + +static int find_matching_open (); + +rl_insert_close (count, invoking_key) + int count, invoking_key; +{ + extern int rl_explicit_arg; + + if (rl_explicit_arg || !rl_blink_matching_paren) + rl_insert (count, invoking_key); + else + { +#if defined (FD_SET) + int orig_point, match_point, ready; + struct timeval timer; + fd_set readfds; + + rl_insert (1, invoking_key); + rl_redisplay (); + match_point = + find_matching_open (rl_line_buffer, rl_point - 2, invoking_key); + + /* Emacs might message or ring the bell here, but I don't. */ + if (match_point < 0) + return; + + FD_ZERO (&readfds); + FD_SET (fileno (rl_instream), &readfds); + timer.tv_sec = 1; + timer.tv_usec = 500; + + orig_point = rl_point; + rl_point = match_point; + rl_redisplay (); + ready = select (1, &readfds, (fd_set *)NULL, (fd_set *)NULL, &timer); + rl_point = orig_point; +#else /* !FD_SET */ + rl_insert (count, invoking_key); +#endif /* !FD_SET */ + } +} + +static int +find_matching_open (string, from, closer) + char *string; + int from, closer; +{ + register int i; + int opener, level, delimiter; + + switch (closer) + { + case ']': opener = '['; break; + case '}': opener = '{'; break; + case ')': opener = '('; break; + default: + return (-1); + } + + level = 1; /* The closer passed in counts as 1. */ + delimiter = 0; /* Delimited state unknown. */ + + for (i = from; i > -1; i--) + { + if (delimiter && (string[i] == delimiter)) + delimiter = 0; + else if ((string[i] == '\'') || (string[i] == '"')) + delimiter = rl_line_buffer[i]; + else if (!delimiter && (string[i] == closer)) + level++; + else if (!delimiter && (string[i] == opener)) + level--; + + if (!level) + break; + } + return (i); +} + + + diff --git a/readline/readline.c b/readline/readline.c index 32ca4b7c57..7e5a820bcd 100644 --- a/readline/readline.c +++ b/readline/readline.c @@ -1,36 +1,27 @@ /* readline.c -- a general facility for reading lines of input - with emacs style editing and completion. */ + with emacs style editing and completion. */ /* Copyright 1987, 1989, 1991, 1992 Free Software Foundation, Inc. - This file contains the Readline Library (the Library), a set of - routines for providing Emacs style line input to programs that ask - for it. + This file is part of the GNU Readline Library, a library for + reading lines of text with interactive input and history editing. - The Library is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or + The GNU Readline Library is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 1, or (at your option) any later version. - The Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + The GNU Readline Library is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied warranty + of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -/* Remove these declarations when we have a complete libgnu.a. */ -/* #define STATIC_MALLOC */ -#if !defined (STATIC_MALLOC) -extern char *xmalloc (), *xrealloc (); -#else -static char *xmalloc (), *xrealloc (); -#endif /* STATIC_MALLOC */ + The GNU General Public License is often shipped with GNU software, and + is generally kept in a file called COPYING or LICENSE. If you do not + have a copy of the license, write to the Free Software Foundation, + 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "sysdep.h" -#include #include #include #ifndef NO_SYS_FILE @@ -38,216 +29,76 @@ static char *xmalloc (), *xrealloc (); #endif #include +/* This is needed to include support for TIOCGWINSZ and window resizing. */ +#if defined (OSF1) || defined (BSD386) || defined (_386BSD) || defined (AIX) +# include +#endif /* OSF1 */ + #if defined (HAVE_UNISTD_H) # include #endif - -#define NEW_TTY_DRIVER -#define HAVE_BSD_SIGNALS -/* #define USE_XON_XOFF */ - -#ifdef __MSDOS__ -#undef NEW_TTY_DRIVER -#undef HAVE_BSD_SIGNALS -#endif - -/* Some USG machines have BSD signal handling (sigblock, sigsetmask, etc.) */ -#if defined (USG) && !defined (hpux) -#undef HAVE_BSD_SIGNALS -#endif - -/* System V machines use termio. */ -#if !defined (_POSIX_VERSION) -# if defined (USG) || defined (hpux) || defined (Xenix) || defined (sgi) || defined (DGUX) -# undef NEW_TTY_DRIVER -# define TERMIO_TTY_DRIVER -# include -# if !defined (TCOON) -# define TCOON 1 -# endif -# endif /* USG || hpux || Xenix || sgi || DUGX */ -#endif /* !_POSIX_VERSION */ - -/* Posix systems use termios and the Posix signal functions. */ -#if defined (_POSIX_VERSION) -# if !defined (TERMIOS_MISSING) -# undef NEW_TTY_DRIVER -# define TERMIOS_TTY_DRIVER -# include -# endif /* !TERMIOS_MISSING */ -# define HAVE_POSIX_SIGNALS -# if !defined (O_NDELAY) -# define O_NDELAY O_NONBLOCK /* Posix-style non-blocking i/o */ -# endif /* O_NDELAY */ -#endif /* _POSIX_VERSION */ - -/* Other (BSD) machines use sgtty. */ -#if defined (NEW_TTY_DRIVER) -#include -#endif - -/* Define _POSIX_VDISABLE if we are not using the `new' tty driver and - it is not already defined. It is used both to determine if a - special character is disabled and to disable certain special - characters. Posix systems should set to 0, USG systems to -1. */ -#if !defined (NEW_TTY_DRIVER) && !defined (_POSIX_VDISABLE) -# if defined (_POSIX_VERSION) -# define _POSIX_VDISABLE 0 -# else /* !_POSIX_VERSION */ -# define _POSIX_VDISABLE -1 -# endif /* !_POSIX_VERSION */ -#endif /* !NEW_TTY_DRIVER && !_POSIX_VDISABLE */ -/* Define some macros for dealing with assorted signalling disciplines. - - These macros provide a way to use signal blocking and disabling - without smothering your code in a pile of #ifdef's. - - SIGNALS_UNBLOCK; Stop blocking all signals. - - { - SIGNALS_DECLARE_SAVED (name); Declare a variable to save the - signal blocking state. - ... - SIGNALS_BLOCK (SIGSTOP, name); Block a signal, and save the previous - state for restoration later. - ... - SIGNALS_RESTORE (name); Restore previous signals. - } - -*/ - -#ifdef HAVE_POSIX_SIGNALS - /* POSIX signals */ - -#define SIGNALS_UNBLOCK \ - do { sigset_t set; \ - sigemptyset (&set); \ - sigprocmask (SIG_SETMASK, &set, (sigset_t *)NULL); \ - } while (0) - -#define SIGNALS_DECLARE_SAVED(name) sigset_t name - -#define SIGNALS_BLOCK(SIG, saved) \ - do { sigset_t set; \ - sigemptyset (&set); \ - sigaddset (&set, SIG); \ - sigprocmask (SIG_BLOCK, &set, &saved); \ - } while (0) - -#define SIGNALS_RESTORE(saved) \ - sigprocmask (SIG_SETMASK, &saved, (sigset_t *)NULL) - - -#else /* HAVE_POSIX_SIGNALS */ -#ifdef HAVE_BSD_SIGNALS - /* BSD signals */ - -#define SIGNALS_UNBLOCK sigsetmask (0) -#define SIGNALS_DECLARE_SAVED(name) int name -#define SIGNALS_BLOCK(SIG, saved) saved = sigblock (sigmask (SIG)) -#define SIGNALS_RESTORE(saved) sigsetmask (saved) - - -#else /* HAVE_BSD_SIGNALS */ - /* None of the Above */ - -#define SIGNALS_UNBLOCK /* nothing */ -#define SIGNALS_DECLARE_SAVED(name) /* nothing */ -#define SIGNALS_BLOCK(SIG, saved) /* nothing */ -#define SIGNALS_RESTORE(saved) /* nothing */ - - -#endif /* HAVE_BSD_SIGNALS */ -#endif /* HAVE_POSIX_SIGNALS */ - -/* End of signal handling definitions. */ - - #include +/* Not all systems declare ERRNO in errno.h... and some systems #define it! */ +#if !defined (errno) extern int errno; +#endif /* !errno */ + +extern char * getenv (); #include #include -/* Posix macro to check file in statbuf for directory-ness. */ -#if defined (S_IFDIR) && !defined (S_ISDIR) -#define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR) -#endif - -#ifndef __MSDOS__ -/* These next are for filename completion. Perhaps this belongs - in a different place. */ -#include -#endif /* __MSDOS__ */ - -#if defined (USG) && !defined (isc386) && !defined (sgi) -struct passwd *getpwuid (), *getpwent (); -#endif - -/* #define HACK_TERMCAP_MOTION */ +/* System-specific feature definitions and include files. */ +#include "rldefs.h" /* Some standard library routines. */ #include "readline.h" #include "history.h" -#ifndef digit -#define digit(c) ((c) >= '0' && (c) <= '9') -#endif - -#ifndef isletter -#define isletter(c) (((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z')) -#endif - -#ifndef digit_value -#define digit_value(c) ((c) - '0') -#endif - -#ifndef member -#define member(c, s) ((c) ? index ((s), (c)) : 0) -#endif - -#ifndef isident -#define isident(c) ((isletter(c) || digit(c) || c == '_')) -#endif - -#ifndef exchange -#define exchange(x, y) {int temp = x; x = y; y = temp;} -#endif +/* 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. */ -#if !defined (rindex) -extern char *rindex (); -#endif /* rindex */ +/* Functions imported from other files in the library. */ +extern char *tgetstr (); +extern void rl_prep_terminal (), rl_deprep_terminal (); +extern Function *rl_function_of_keyseq (); +extern char *tilde_expand (); -#if !defined (index) -extern char *index (); -#endif /* index */ +/* External redisplay functions and variables from display.c */ +extern void rl_redisplay (); +extern void _rl_move_vert (); -extern char *getenv (); -extern char *tilde_expand (); +extern void _rl_erase_at_end_of_line (); +extern void _rl_move_cursor_relative (); -static update_line (); -static void output_character_function (); -static delete_chars (); -static void insert_some_chars (); +extern int _rl_vis_botlin; +extern int _rl_last_c_pos; +extern int rl_display_fixed; -#if defined (VOID_SIGHANDLER) -# define sighandler void -#else -# define sighandler int -#endif /* VOID_SIGHANDLER */ +/* Variables imported from complete.c. */ +extern char *rl_completer_word_break_characters; +extern char *rl_basic_word_break_characters; +extern Function *rl_symbolic_link_hook; +extern int rl_completion_query_items; +extern int rl_complete_with_tilde_expansion; -/* This typedef is equivalant to the one for Function; it allows us - to say SigHandler *foo = signal (SIGKILL, SIG_IGN); */ -typedef sighandler SigHandler (); +/* Forward declarations used in this file. */ +void rl_dispatch (); +void free_history_entry (); +void _rl_output_character_function (); +void _rl_set_screen_size (); -/* If on, then readline handles signals in a way that doesn't screw. */ -#define HANDLE_SIGNALS +#if !defined (_GO32_) +static void readline_default_bindings (); +#endif /* !_GO32_ */ -#ifdef __GO32__ -#include -#undef HANDLE_SIGNALS -#endif +#if defined (_GO32_) +# include +# undef HANDLE_SIGNALS +#endif /* _GO32_ */ /* **************************************************************** */ @@ -256,13 +107,11 @@ typedef sighandler SigHandler (); /* */ /* **************************************************************** */ +static char *LibraryVersion = "2.0 (Cygnus)"; + /* A pointer to the keymap that is currently in use. By default, it is the standard emacs keymap. */ -Keymap keymap = emacs_standard_keymap; - -#define no_mode -1 -#define vi_mode 0 -#define emacs_mode 1 +Keymap _rl_keymap = emacs_standard_keymap; /* The current style of editing. */ int rl_editing_mode = emacs_mode; @@ -307,7 +156,8 @@ static jmp_buf readline_top_level; static FILE *in_stream, *out_stream; /* The names of the streams that we do input and output to. */ -FILE *rl_instream, *rl_outstream; +FILE *rl_instream = (FILE *)NULL; +FILE *rl_outstream = (FILE *)NULL; /* Non-zero means echo characters as they are read. */ int readline_echoing_p = 1; @@ -322,17 +172,12 @@ int rl_key_sequence_length = 0; before readline_internal () prints the first prompt. */ Function *rl_startup_hook = (Function *)NULL; -/* 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. */ -Function *rl_symbolic_link_hook = (Function *)NULL; - /* What we use internally. You should always refer to RL_LINE_BUFFER. */ static char *the_line; /* The character that can generate an EOF. Really read from the terminal driver... just defaulted here. */ -static int eof_char = CTRL ('D'); +int _rl_eof_char = CTRL ('D'); /* Non-zero makes this the next keystroke to read. */ int rl_pending_input = 0; @@ -340,11 +185,26 @@ int rl_pending_input = 0; /* Pointer to a useful terminal name. */ char *rl_terminal_name = (char *)NULL; +/* Non-zero means to always use horizontal scrolling in line display. */ +int _rl_horizontal_scroll_mode = 0; + +/* Non-zero means to display an asterisk at the starts of history lines + which have been modified. */ +int _rl_mark_modified_lines = 0; + +/* Non-zero means to use a visible bell if one is available rather than + simply ringing the terminal bell. */ +int _rl_prefer_visible_bell = 0; + /* Line buffer and maintenence. */ char *rl_line_buffer = (char *)NULL; int rl_line_buffer_len = 0; #define DEFAULT_BUFFER_SIZE 256 +#if defined (VISIBLE_STATS) +int rl_visible_stats = 0; +#endif /* VISIBLE_STATS */ + /* **************************************************************** */ /* */ @@ -354,15 +214,17 @@ int rl_line_buffer_len = 0; /* Non-zero means do not parse any lines other than comments and parser directives. */ -static unsigned char parsing_conditionalized_out = 0; - -/* Caseless strcmp (). */ -static int stricmp (), strnicmp (); -static char *strpbrk (); +unsigned char _rl_parsing_conditionalized_out = 0; /* Non-zero means to save keys that we dispatch on in a kbd macro. */ static int defining_kbd_macro = 0; +/* Non-zero means to convert characters with the meta bit set to + escape-prefixed characters so we can indirect through + emacs_meta_keymap or vi_escape_keymap. */ +int _rl_convert_meta_chars_to_ascii = 1; + +static int doing_an_undo; /* **************************************************************** */ /* */ @@ -370,8 +232,8 @@ static int defining_kbd_macro = 0; /* */ /* **************************************************************** */ -static void rl_prep_terminal (), rl_deprep_terminal (); -static void clear_to_eol (), rl_generic_bind (); +/* Non-zero means treat 0200 bit in terminal input as Meta bit. */ +int _rl_meta_flag = 0; /* Forward declaration */ /* Read a line of input. Prompt with PROMPT. A NULL PROMPT means none. A return value of NULL means that EOF was encountered. */ @@ -392,7 +254,7 @@ readline (prompt) } rl_initialize (); - rl_prep_terminal (); + rl_prep_terminal (_rl_meta_flag); #if defined (HANDLE_SIGNALS) rl_set_signals (); @@ -446,7 +308,9 @@ readline_internal () while (!rl_done) { int lk = last_command_was_kill; - int code = setjmp (readline_top_level); + int code; + + code = setjmp (readline_top_level); if (code) rl_redisplay (); @@ -464,16 +328,16 @@ readline_internal () if (c == EOF && rl_end) c = NEWLINE; - /* The character eof_char typed to blank line, and not as the + /* The character _rl_eof_char typed to blank line, and not as the previous character is interpreted as EOF. */ - if (((c == eof_char && lastc != c) || c == EOF) && !rl_end) + if (((c == _rl_eof_char && lastc != c) || c == EOF) && !rl_end) { eof_found = 1; break; } lastc = c; - rl_dispatch (c, keymap); + rl_dispatch (c, _rl_keymap); /* If there was no change in last_command_was_kill, then no kill has taken place. Note that if input is pending we are reading @@ -487,7 +351,7 @@ readline_internal () #if defined (VI_MODE) /* In vi mode, when you exit insert mode, the cursor moves back over the previous character. We explicitly check for that here. */ - if (rl_editing_mode == vi_mode && keymap == vi_movement_keymap) + if (rl_editing_mode == vi_mode && _rl_keymap == vi_movement_keymap) rl_vi_check (); #endif /* VI_MODE */ @@ -524,151 +388,6 @@ readline_internal () return (savestring (the_line)); } - -/* **************************************************************** */ -/* */ -/* Signal Handling */ -/* */ -/* **************************************************************** */ - -#if defined (SIGWINCH) -static SigHandler *old_sigwinch = (SigHandler *)NULL; - -static sighandler -rl_handle_sigwinch (sig) - int sig; -{ - char *term; - - term = rl_terminal_name; - - if (readline_echoing_p) - { - if (!term) - term = getenv ("TERM"); - if (!term) - term = "dumb"; - rl_reset_terminal (term); -#if defined (NOTDEF) - crlf (); - rl_forced_update_display (); -#endif /* NOTDEF */ - } - - if (old_sigwinch && - old_sigwinch != (SigHandler *)SIG_IGN && - old_sigwinch != (SigHandler *)SIG_DFL) - (*old_sigwinch) (sig); -#if !defined (VOID_SIGHANDLER) - return (0); -#endif /* VOID_SIGHANDLER */ -} -#endif /* SIGWINCH */ - -#if defined (HANDLE_SIGNALS) -/* Interrupt handling. */ -static SigHandler - *old_int = (SigHandler *)NULL, - *old_tstp = (SigHandler *)NULL, - *old_ttou = (SigHandler *)NULL, - *old_ttin = (SigHandler *)NULL, - *old_cont = (SigHandler *)NULL, - *old_alrm = (SigHandler *)NULL; - -/* Handle an interrupt character. */ -static sighandler -rl_signal_handler (sig) - int sig; -{ -#if !defined (HAVE_BSD_SIGNALS) - /* Since the signal will not be blocked while we are in the signal - handler, ignore it until rl_clear_signals resets the catcher. */ - if (sig == SIGINT) - signal (sig, SIG_IGN); -#endif /* !HAVE_BSD_SIGNALS */ - - switch (sig) - { - case SIGINT: - free_undo_list (); - rl_clear_message (); - rl_init_argument (); - -#if defined (SIGTSTP) - case SIGTSTP: - case SIGTTOU: - case SIGTTIN: -#endif /* SIGTSTP */ - case SIGALRM: - rl_clean_up_for_exit (); - rl_deprep_terminal (); - rl_clear_signals (); - rl_pending_input = 0; - - kill (getpid (), sig); - - SIGNALS_UNBLOCK; - - rl_prep_terminal (); - rl_set_signals (); - } - -#if !defined (VOID_SIGHANDLER) - return (0); -#endif /* !VOID_SIGHANDLER */ -} - -rl_set_signals () -{ - old_int = (SigHandler *)signal (SIGINT, rl_signal_handler); - if (old_int == (SigHandler *)SIG_IGN) - signal (SIGINT, SIG_IGN); - - old_alrm = (SigHandler *)signal (SIGALRM, rl_signal_handler); - if (old_alrm == (SigHandler *)SIG_IGN) - signal (SIGALRM, SIG_IGN); - -#if defined (SIGTSTP) - old_tstp = (SigHandler *)signal (SIGTSTP, rl_signal_handler); - if (old_tstp == (SigHandler *)SIG_IGN) - signal (SIGTSTP, SIG_IGN); -#endif -#if defined (SIGTTOU) - old_ttou = (SigHandler *)signal (SIGTTOU, rl_signal_handler); - old_ttin = (SigHandler *)signal (SIGTTIN, rl_signal_handler); - - if (old_tstp == (SigHandler *)SIG_IGN) - { - signal (SIGTTOU, SIG_IGN); - signal (SIGTTIN, SIG_IGN); - } -#endif - -#if defined (SIGWINCH) - old_sigwinch = (SigHandler *)signal (SIGWINCH, rl_handle_sigwinch); -#endif -} - -rl_clear_signals () -{ - signal (SIGINT, old_int); - signal (SIGALRM, old_alrm); - -#if defined (SIGTSTP) - signal (SIGTSTP, old_tstp); -#endif - -#if defined (SIGTTOU) - signal (SIGTTOU, old_ttou); - signal (SIGTTIN, old_ttin); -#endif - -#if defined (SIGWINCH) - signal (SIGWINCH, old_sigwinch); -#endif -} -#endif /* HANDLE_SIGNALS */ - /* **************************************************************** */ /* */ @@ -676,12 +395,6 @@ rl_clear_signals () /* */ /* **************************************************************** */ -#if defined (USE_XON_XOFF) -/* If the terminal was in xoff state when we got to it, then xon_char - contains the character that is supposed to start it again. */ -static int xon_char, xoff_state; -#endif /* USE_XON_XOFF */ - static int pop_index = 0, push_index = 0, ibuffer_len = 511; static unsigned char ibuffer[512]; @@ -754,29 +467,31 @@ rl_unget_char (key) /* If a character is available to be read, then read it and stuff it into IBUFFER. Otherwise, just return. */ +void rl_gather_tyi () { #ifdef __GO32__ char input; - if (isatty(0)) - { - int i = rl_getc(); - if (i != EOF) - rl_stuff_char(i); - } - else - if (kbhit() && ibuffer_space()) - rl_stuff_char(getkey()); + if (isatty (0)) + { + int i = rl_getc (); + if (i != EOF) + rl_stuff_char (i); + } + else if (kbhit () && ibuffer_space ()) + rl_stuff_char (getkey ()); #else + int tty = fileno (in_stream); register int tem, result = -1; - long chars_avail; + int chars_avail; char input; #if defined (FIONREAD) result = ioctl (tty, FIONREAD, &chars_avail); #endif +#if defined (O_NDELAY) if (result == -1) { int flags; @@ -790,6 +505,7 @@ rl_gather_tyi () if (chars_avail == -1 && errno == EAGAIN) return; } +#endif /* O_NDELAY */ /* If there's nothing available, don't waste time trying to read something. */ @@ -860,26 +576,31 @@ rl_read_key () return (c); } -/* I'm beginning to hate the declaration rules for various compilers. */ +/* Found later in this file. */ static void add_macro_char (), with_macro_input (); /* Do the command associated with KEY in MAP. If the associated command is really a keymap, then read another key, and dispatch into that map. */ +void rl_dispatch (key, map) register int key; Keymap map; { +#if defined (VI_MODE) + extern int _rl_vi_last_command, _rl_vi_last_repeat, _rl_vi_last_arg_sign; +#endif if (defining_kbd_macro) add_macro_char (key); - if (key > 127 && key < 256) + if (META_CHAR (key) && _rl_convert_meta_chars_to_ascii) { if (map[ESC].type == ISKMAP) { map = (Keymap)map[ESC].function; - key -= 128; + key = UNMETA (key); + rl_key_sequence_length += 2; rl_dispatch (key, map); } else @@ -945,6 +666,15 @@ rl_dispatch (key, map) } break; } +#if defined (VI_MODE) + if (rl_editing_mode == vi_mode && _rl_keymap == vi_movement_keymap && + rl_vi_textmod_command (key)) + { + _rl_vi_last_command = key; + _rl_vi_last_repeat = rl_numeric_arg; + _rl_vi_last_arg_sign = rl_arg_sign; + } +#endif } @@ -1131,8 +861,6 @@ rl_call_last_kbd_macro (count, ignore) /* Initliaze readline (and terminal if not already). */ rl_initialize () { - extern char *rl_display_prompt; - /* If we have never been called before, initialize the terminal and data structures. */ if (!rl_initialized) @@ -1153,21 +881,13 @@ rl_initialize () start_using_history (); /* Make the display buffer match the state of the line. */ - { - extern char *rl_display_prompt; - extern int forced_display; - - rl_on_new_line (); - - rl_display_prompt = rl_prompt ? rl_prompt : ""; - forced_display = 1; - } + rl_reset_line_state (); /* No such function typed yet. */ rl_last_func = (Function *)NULL; /* Parsing of key-bindings begins in an enabled state. */ - parsing_conditionalized_out = 0; + _rl_parsing_conditionalized_out = 0; } /* Initialize the entire state of the world. */ @@ -1176,12 +896,18 @@ readline_initialize_everything () /* Find out if we are running in Emacs. */ running_in_emacs = getenv ("EMACS"); - /* Set up input and output if they aren't already. */ + /* Set up input and output if they are not already set up. */ if (!rl_instream) rl_instream = stdin; + if (!rl_outstream) rl_outstream = stdout; + /* Bind in_stream and out_stream immediately. These values may change, + but they may also be used before readline_internal () is called. */ + in_stream = rl_instream; + out_stream = rl_outstream; + /* Allocate data structures. */ if (!rl_line_buffer) rl_line_buffer = @@ -1202,9 +928,6 @@ readline_initialize_everything () /* If the completion parser's default word break characters haven't been set yet, then do so now. */ { - extern char *rl_completer_word_break_characters; - extern char *rl_basic_word_break_characters; - if (rl_completer_word_break_characters == (char *)NULL) rl_completer_word_break_characters = rl_basic_word_break_characters; } @@ -1213,101 +936,10 @@ readline_initialize_everything () /* If this system allows us to look at the values of the regular input editing characters, then bind them to their readline equivalents, iff the characters are not bound to keymaps. */ +static void readline_default_bindings () { -#ifndef __GO32__ - -#if defined (NEW_TTY_DRIVER) - struct sgttyb ttybuff; - int tty = fileno (rl_instream); - - if (ioctl (tty, TIOCGETP, &ttybuff) != -1) - { - int erase, kill; - - erase = ttybuff.sg_erase; - kill = ttybuff.sg_kill; - - if (erase != -1 && keymap[erase].type == ISFUNC) - keymap[erase].function = rl_rubout; - - if (kill != -1 && keymap[kill].type == ISFUNC) - keymap[kill].function = rl_unix_line_discard; - } - -#if defined (TIOCGLTC) - { - struct ltchars lt; - - if (ioctl (tty, TIOCGLTC, <) != -1) - { - int erase, nextc; - - erase = lt.t_werasc; - nextc = lt.t_lnextc; - - if (erase != -1 && keymap[erase].type == ISFUNC) - keymap[erase].function = rl_unix_word_rubout; - - if (nextc != -1 && keymap[nextc].type == ISFUNC) - keymap[nextc].function = rl_quoted_insert; - } - } -#endif /* TIOCGLTC */ -#else /* not NEW_TTY_DRIVER */ - -#if defined (TERMIOS_TTY_DRIVER) - struct termios ttybuff; -#else - struct termio ttybuff; -#endif /* TERMIOS_TTY_DRIVER */ - int tty = fileno (rl_instream); - -#if defined (TERMIOS_TTY_DRIVER) - if (tcgetattr (tty, &ttybuff) != -1) -#else - if (ioctl (tty, TCGETA, &ttybuff) != -1) -#endif /* !TERMIOS_TTY_DRIVER */ - { - int erase, kill; - - erase = ttybuff.c_cc[VERASE]; - kill = ttybuff.c_cc[VKILL]; - - if (erase != _POSIX_VDISABLE && - keymap[(unsigned char)erase].type == ISFUNC) - keymap[(unsigned char)erase].function = rl_rubout; - - if (kill != _POSIX_VDISABLE && - keymap[(unsigned char)kill].type == ISFUNC) - keymap[(unsigned char)kill].function = rl_unix_line_discard; - -#if defined (VLNEXT) && defined (TERMIOS_TTY_DRIVER) - { - int nextc; - - nextc = ttybuff.c_cc[VLNEXT]; - - if (nextc != _POSIX_VDISABLE && - keymap[(unsigned char)nextc].type == ISFUNC) - keymap[(unsigned char)nextc].function = rl_quoted_insert; - } -#endif /* VLNEXT && TERMIOS_TTY_DRIVER */ - -#if defined (VWERASE) - { - int werase; - - werase = ttybuff.c_cc[VWERASE]; - - if (werase != _POSIX_VDISABLE && - keymap[(unsigned char)werase].type == ISFUNC) - keymap[(unsigned char)werase].function = rl_unix_word_rubout; - } -#endif /* VWERASE */ - } -#endif /* !NEW_TTY_DRIVER */ -#endif /* def __GO32__ */ + rltty_set_default_bindings (_rl_keymap); } @@ -1356,11 +988,11 @@ rl_digit_loop () int key, c; while (1) { - rl_message ("(arg: %d) ", rl_arg_sign * rl_numeric_arg, 0); + rl_message ("(arg: %d) ", rl_arg_sign * rl_numeric_arg); key = c = rl_read_key (); - if (keymap[c].type == ISFUNC && - keymap[c].function == rl_universal_argument) + if (_rl_keymap[c].type == ISFUNC && + _rl_keymap[c].function == rl_universal_argument) { rl_numeric_arg *= 4; continue; @@ -1384,706 +1016,163 @@ rl_digit_loop () else { rl_clear_message (); - rl_dispatch (key, keymap); + rl_dispatch (key, _rl_keymap); return; } } } } - /* **************************************************************** */ /* */ -/* Display stuff */ +/* Terminal and Termcap */ /* */ /* **************************************************************** */ -/* 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. Carefull attention needs - to be paid to the vertical position variables. - - handling of terminals with autowrap on (incl. DEC braindamage) - could be improved a bit. Right now I just cheat and decrement - screenwidth by one. */ +static char *term_buffer = (char *)NULL; +static char *term_string_buffer = (char *)NULL; -/* 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. +/* Non-zero means this terminal can't really do anything. */ +int dumb_term = 0; +/* On Solaris2, sys/types.h #includes sys/reg.h, which #defines PC. + Unfortunately, PC is a global variable used by the termcap library. */ +#undef PC - 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. */ +#if !defined (__linux__) +char PC; +char *BC, *UP; +#endif /* __linux__ */ -/* Termcap variables: */ -extern char *term_up, *term_dc, *term_cr; -extern int screenheight, screenwidth, terminal_can_insert; +/* Some strings to control terminal actions. These are output by tputs (). */ +char *term_goto, *term_clreol, *term_cr, *term_clrpag, *term_backspace; -/* What YOU turn on when you have handled all redisplay yourself. */ -int rl_display_fixed = 0; +int screenwidth, screenheight; -/* The visible cursor position. If you print some text, adjust this. */ -int last_c_pos = 0; -int last_v_pos = 0; +/* Non-zero if we determine that the terminal can do character insertion. */ +int terminal_can_insert = 0; -/* 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 = 0; +/* How to insert characters. */ +char *term_im, *term_ei, *term_ic, *term_ip, *term_IC; -/* The line display buffers. One is the line currently displayed on - the screen. The other is the line about to be displayed. */ -static char *visible_line = (char *)NULL; -static char *invisible_line = (char *)NULL; +/* How to delete characters. */ +char *term_dc, *term_DC; -/* Number of lines currently on screen minus 1. */ -int vis_botlin = 0; +#if defined (HACK_TERMCAP_MOTION) +char *term_forward_char; +#endif /* HACK_TERMCAP_MOTION */ -/* A buffer for `modeline' messages. */ -char msg_buf[128]; +/* How to go up a line. */ +char *term_up; -/* Non-zero forces the redisplay even if we thought it was unnecessary. */ -int forced_display = 0; +/* A visible bell, if the terminal can be made to flash the screen. */ +char *visible_bell; -/* 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; +/* Non-zero means that this terminal has a meta key. */ +int term_has_meta; -/* Default and initial buffer size. Can grow. */ -static int line_size = 1024; +/* The string to write to turn on the meta key, if this term has one. */ +char *term_mm; -/* Non-zero means to always use horizontal scrolling in line display. */ -static int horizontal_scroll_mode = 0; +/* The string to write to turn off the meta key, if this term has one. */ +char *term_mo; -/* Non-zero means to display an asterisk at the starts of history lines - which have been modified. */ -static int mark_modified_lines = 0; +/* The key sequences output by the arrow keys, if this terminal has any. */ +char *term_ku, *term_kd, *term_kr, *term_kl; -/* Non-zero means to use a visible bell if one is available rather than - simply ringing the terminal bell. */ -static int prefer_visible_bell = 0; - -/* I really disagree with this, but my boss (among others) insists that we - support compilers that don't work. I don't think we are gaining by doing - so; what is the advantage in producing better code if we can't use it? */ -/* The following two declarations belong inside the - function block, not here. */ -static void move_cursor_relative (); -static void output_some_chars (); -static void output_character_function (); -static int compare_strings (); - -/* Basic redisplay algorithm. */ -rl_redisplay () +/* Re-initialize the terminal considering that the TERM/TERMCAP variable + has changed. */ +rl_reset_terminal (terminal_name) + char *terminal_name; { - register int in, out, c, linenum; - register char *line = invisible_line; - char *prompt_this_line; - int c_pos = 0; - int inv_botlin = 0; /* Number of lines in newly drawn buffer. */ - - extern int readline_echoing_p; - - if (!readline_echoing_p) - return; + init_terminal_io (terminal_name); +} - if (!rl_display_prompt) - rl_display_prompt = ""; +/* Set readline's idea of the screen size. TTY is a file descriptor open + to the terminal. If IGNORE_ENV is true, we do not pay attention to the + values of $LINES and $COLUMNS. The tests for TERM_STRING_BUFFER being + non-null serve to check whether or not we have initialized termcap. */ +void +_rl_set_screen_size (tty, ignore_env) + int tty, ignore_env; +{ +#if defined (TIOCGWINSZ) + struct winsize window_size; +#endif /* TIOCGWINSZ */ - if (!invisible_line) +#if defined (TIOCGWINSZ) + if (ioctl (tty, TIOCGWINSZ, &window_size) == 0) { - visible_line = (char *)xmalloc (line_size); - invisible_line = (char *)xmalloc (line_size); - line = invisible_line; - for (in = 0; in < line_size; in++) - { - visible_line[in] = 0; - invisible_line[in] = 1; - } - rl_on_new_line (); + screenwidth = (int) window_size.ws_col; + screenheight = (int) window_size.ws_row; } +#endif /* TIOCGWINSZ */ - /* Draw the line into the buffer. */ - c_pos = -1; - - /* Mark the line as modified or not. We only do this for history - lines. */ - out = 0; - if (mark_modified_lines && current_history () && rl_undo_list) + /* Environment variable COLUMNS overrides setting of "co" if IGNORE_ENV + is unset. */ + if (screenwidth <= 0) { - line[out++] = '*'; - line[out] = '\0'; - } + char *sw; - /* 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 callers misconception. */ - if (visible_line[0] != invisible_line[0]) - rl_display_fixed = 0; + if (!ignore_env && (sw = getenv ("COLUMNS"))) + screenwidth = atoi (sw); - prompt_this_line = rindex (rl_display_prompt, '\n'); - if (!prompt_this_line) - prompt_this_line = rl_display_prompt; - else - { - prompt_this_line++; - if (forced_display) - output_some_chars (rl_display_prompt, - prompt_this_line - rl_display_prompt); + if (screenwidth <= 0 && term_string_buffer) + screenwidth = tgetnum ("co"); } - strncpy (line + out, prompt_this_line, strlen (prompt_this_line)); - out += strlen (prompt_this_line); - line[out] = '\0'; - - for (in = 0; in < rl_end; in++) + /* Environment variable LINES overrides setting of "li" if IGNORE_ENV + is unset. */ + if (screenheight <= 0) { - c = (unsigned char)the_line[in]; + char *sh; - if (out + 1 >= line_size) - { - line_size *= 2; - visible_line = (char *)xrealloc (visible_line, line_size); - invisible_line = (char *)xrealloc (invisible_line, line_size); - line = invisible_line; - } + if (!ignore_env && (sh = getenv ("LINES"))) + screenheight = atoi (sh); - if (in == rl_point) - c_pos = out; - - if (c > 127) - { - line[out++] = 'M'; - line[out++] = '-'; - line[out++] = c - 128; - } -#define DISPLAY_TABS -#if defined (DISPLAY_TABS) - else if (c == '\t') - { - register int newout = (out | (int)7) + 1; - while (out < newout) - line[out++] = ' '; - } -#endif - else if (c < 32) - { - line[out++] = 'C'; - line[out++] = '-'; - line[out++] = c + 64; - } - else if (c == 127) - { - line[out++] = 'C'; - line[out++] = '-'; - line[out++] = '?'; - } - else - line[out++] = c; + if (screenheight <= 0 && term_string_buffer) + screenheight = tgetnum ("li"); } - line[out] = '\0'; - if (c_pos < 0) - c_pos = out; - /* 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 all else fails, default to 80x24 terminal. */ + if (screenwidth <= 0) + screenwidth = 80; - /* 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. */ + if (screenheight <= 0) + screenheight = 24; - if (!horizontal_scroll_mode && term_up && *term_up) - { - int total_screen_chars = (screenwidth * screenheight); - - 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. I'll fix this in a minute. */ - if (out >= total_screen_chars) - out = total_screen_chars - 1; - - /* Number of screen lines to display. */ - inv_botlin = out / screenwidth; - - /* For each line in the buffer, do the updating display. */ - for (linenum = 0; linenum <= inv_botlin; linenum++) - update_line (linenum > vis_botlin ? "" - : &visible_line[linenum * screenwidth], - &invisible_line[linenum * screenwidth], - linenum); - - /* We may have deleted some lines. If so, clear the left over - blank ones at the bottom out. */ - if (vis_botlin > inv_botlin) - { - char *tt; - for (; linenum <= vis_botlin; linenum++) - { - tt = &visible_line[linenum * screenwidth]; - move_vert (linenum); - move_cursor_relative (0, tt); - clear_to_eol ((linenum == vis_botlin)? - strlen (tt) : screenwidth); - } - } - vis_botlin = inv_botlin; - - /* Move the cursor where it should be. */ - move_vert (c_pos / screenwidth); - move_cursor_relative (c_pos % screenwidth, - &invisible_line[(c_pos / screenwidth) * screenwidth]); - } - } - else /* Do horizontal scrolling. */ - { - int lmargin; - - /* Always at top line. */ - last_v_pos = 0; - - /* If the display position of the cursor would be off the edge - of the screen, start the display of this line at an offset that - leaves the cursor on the screen. */ - if (c_pos - last_lmargin > screenwidth - 2) - lmargin = (c_pos / (screenwidth / 3) - 2) * (screenwidth / 3); - else if (c_pos - last_lmargin < 1) - lmargin = ((c_pos - 1) / (screenwidth / 3)) * (screenwidth / 3); - else - lmargin = last_lmargin; - - /* 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 (lmargin + screenwidth < out) - line[lmargin + screenwidth - 1] = '>'; - - if (!rl_display_fixed || forced_display || lmargin != last_lmargin) - { - forced_display = 0; - update_line (&visible_line[last_lmargin], - &invisible_line[lmargin], 0); - - move_cursor_relative (c_pos - lmargin, &invisible_line[lmargin]); - last_lmargin = lmargin; - } - } - fflush (out_stream); - - /* Swap visible and non-visible lines. */ - { - char *temp = visible_line; - visible_line = invisible_line; - invisible_line = temp; - rl_display_fixed = 0; - } -} - -/* 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 handeled. - - Could be made even smarter, but this works well enough */ -static -update_line (old, new, current_line) - register char *old, *new; - int current_line; -{ - register char *ofd, *ols, *oe, *nfd, *nls, *ne; - int lendiff, wsatend; - - /* Find first difference. */ - for (ofd = old, nfd = new; - (ofd - old < screenwidth) && *ofd && (*ofd == *nfd); - ofd++, nfd++) - ; - - /* Move to the end of the screen line. */ - for (oe = ofd; ((oe - old) < screenwidth) && *oe; oe++); - for (ne = nfd; ((ne - new) < screenwidth) && *ne; ne++); - - /* If no difference, continue to next line. */ - if (ofd == oe && nfd == ne) - return; - - wsatend = 1; /* flag for trailing whitespace */ - ols = oe - 1; /* find last same */ - nls = ne - 1; - while ((*ols == *nls) && (ols > ofd) && (nls > nfd)) - { - if (*ols != ' ') - wsatend = 0; - ols--; - nls--; - } - - if (wsatend) - { - ols = oe; - nls = ne; - } - else if (*ols != *nls) - { - if (*ols) /* don't step past the NUL */ - ols++; - if (*nls) - nls++; - } - - move_vert (current_line); - move_cursor_relative (ofd - old, old); - - /* if (len (new) > len (old)) */ - lendiff = (nls - nfd) - (ols - ofd); - - /* Insert (diff(len(old),len(new)) ch */ - if (lendiff > 0) - { - if (terminal_can_insert) - { - extern char *term_IC; - - /* Sometimes it is cheaper to print the characters rather than - use the terminal's capabilities. */ - if ((2 * (ne - nfd)) < lendiff && !term_IC) - { - output_some_chars (nfd, (ne - nfd)); - last_c_pos += (ne - nfd); - } - else - { - if (*ols) - { - insert_some_chars (nfd, lendiff); - last_c_pos += lendiff; - } - else - { - /* At the end of a line the characters do not have to - be "inserted". They can just be placed on the screen. */ - output_some_chars (nfd, lendiff); - last_c_pos += lendiff; - } - /* Copy (new) chars to screen from first diff to last match. */ - if (((nls - nfd) - lendiff) > 0) - { - output_some_chars (&nfd[lendiff], ((nls - nfd) - lendiff)); - last_c_pos += ((nls - nfd) - lendiff); - } - } - } - else - { /* cannot insert chars, write to EOL */ - output_some_chars (nfd, (ne - nfd)); - last_c_pos += (ne - nfd); - } - } - else /* Delete characters from line. */ - { - /* If possible and inexpensive to use terminal deletion, then do so. */ - if (term_dc && (2 * (ne - nfd)) >= (-lendiff)) - { - if (lendiff) - delete_chars (-lendiff); /* delete (diff) characters */ - - /* Copy (new) chars to screen from first diff to last match */ - if ((nls - nfd) > 0) - { - output_some_chars (nfd, (nls - nfd)); - last_c_pos += (nls - nfd); - } - } - /* Otherwise, print over the existing material. */ - else - { - output_some_chars (nfd, (ne - nfd)); - last_c_pos += (ne - nfd); - clear_to_eol ((oe - old) - (ne - new)); - } - } -} - -/* (PWP) tell the update routines that we have moved onto a - new (empty) line. */ -rl_on_new_line () -{ - if (visible_line) - visible_line[0] = '\0'; - - last_c_pos = last_v_pos = 0; - vis_botlin = last_lmargin = 0; -} - -/* Actually update the display, period. */ -rl_forced_update_display () -{ - if (visible_line) - { - register char *temp = visible_line; - - while (*temp) *temp++ = '\0'; - } - rl_on_new_line (); - forced_display++; - rl_redisplay (); -} - -/* Move the cursor from last_c_pos to NEW, which are buffer indices. - DATA is the contents of the screen line of interest; i.e., where - the movement is being done. */ -static void -move_cursor_relative (new, data) - int new; - char *data; -{ - register int i; - - /* It may be faster to output a CR, and then move forwards instead - of moving backwards. */ - if (new + 1 < last_c_pos - new) - { -#ifdef __MSDOS__ - putc('\r', out_stream); -#else - tputs (term_cr, 1, output_character_function); -#endif - last_c_pos = 0; - } - - if (last_c_pos == new) return; - - if (last_c_pos < new) - { - /* 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. */ -#if defined (HACK_TERMCAP_MOTION) - extern char *term_forward_char; - - if (term_forward_char) - for (i = last_c_pos; i < new; i++) - tputs (term_forward_char, 1, output_character_function); - else - for (i = last_c_pos; i < new; i++) - putc (data[i], out_stream); -#else - for (i = last_c_pos; i < new; i++) - putc (data[i], out_stream); -#endif /* HACK_TERMCAP_MOTION */ - } - else - backspace (last_c_pos - new); - last_c_pos = new; -} - -/* PWP: move the cursor up or down. */ -move_vert (to) - int to; -{ - void output_character_function (); - register int delta, i; - - if (last_v_pos == to) return; - - if (to > screenheight) - return; - -#ifdef __GO32__ - { - int cur_r, cur_c; - ScreenGetCursor(&cur_r, &cur_c); - ScreenSetCursor(cur_r+to-last_v_pos, cur_c); - } -#else /* __GO32__ */ - if ((delta = to - last_v_pos) > 0) - { - for (i = 0; i < delta; i++) - putc ('\n', out_stream); - tputs (term_cr, 1, output_character_function); - last_c_pos = 0; - } - else - { /* delta < 0 */ - if (term_up && *term_up) - for (i = 0; i < -delta; i++) - tputs (term_up, 1, output_character_function); - } -#endif /* __GO32__ */ - last_v_pos = to; /* now to is here */ -} - -/* Physically print C on out_stream. This is for functions which know - how to optimize the display. */ -rl_show_char (c) - int c; -{ - if (c > 127) - { - fprintf (out_stream, "M-"); - c -= 128; - } - -#if defined (DISPLAY_TABS) - if (c < 32 && c != '\t') -#else - if (c < 32) +#if defined (SHELL) + /* If we're being compiled as part of bash, set the environment + variables $LINES and $COLUMNS to new values. */ + set_lines_and_columns (screenheight, screenwidth); #endif - { - - c += 64; - } - - putc (c, out_stream); - fflush (out_stream); -} - -#if defined (DISPLAY_TABS) -int -rl_character_len (c, pos) - register int c, pos; -{ - if (c < ' ' || c > 126) - { - if (c == '\t') - return (((pos | (int)7) + 1) - pos); - else - return (3); - } - else - return (1); -} -#else -int -rl_character_len (c) - int c; -{ - if (c < ' ' || c > 126) - return (3); - else - return (1); -} -#endif /* DISPLAY_TAB */ - -/* How to print things in the "echo-area". The prompt is treated as a - mini-modeline. */ -rl_message (string, arg1, arg2) - char *string; -{ - sprintf (msg_buf, string, arg1, arg2); - rl_display_prompt = msg_buf; - rl_redisplay (); -} - -/* How to clear things from the "echo-area". */ -rl_clear_message () -{ - rl_display_prompt = rl_prompt; - rl_redisplay (); -} - -/* **************************************************************** */ -/* */ -/* Terminal and Termcap */ -/* */ -/* **************************************************************** */ - -static char *term_buffer = (char *)NULL; -static char *term_string_buffer = (char *)NULL; - -/* Non-zero means this terminal can't really do anything. */ -int dumb_term = 0; - -/* On Solaris2, sys/types.h brings in sys/reg.h, - which screws up the Termcap variable PC, used below. */ - -#undef PC - -char PC; -char *BC, *UP; - -/* Some strings to control terminal actions. These are output by tputs (). */ -char *term_goto, *term_clreol, *term_cr, *term_clrpag, *term_backspace; - -int screenwidth, screenheight; - -/* Non-zero if we determine that the terminal can do character insertion. */ -int terminal_can_insert = 0; - -/* How to insert characters. */ -char *term_im, *term_ei, *term_ic, *term_ip, *term_IC; - -/* How to delete characters. */ -char *term_dc, *term_DC; - -#if defined (HACK_TERMCAP_MOTION) -char *term_forward_char; -#endif /* HACK_TERMCAP_MOTION */ - -/* How to go up a line. */ -char *term_up; - -/* A visible bell, if the terminal can be made to flash the screen. */ -char *visible_bell; -/* Re-initialize the terminal considering that the TERM/TERMCAP variable - has changed. */ -rl_reset_terminal (terminal_name) - char *terminal_name; -{ - init_terminal_io (terminal_name); + screenwidth--; } init_terminal_io (terminal_name) char *terminal_name; { #ifdef __GO32__ - screenwidth = ScreenCols(); - screenheight = ScreenRows(); + screenwidth = ScreenCols (); + screenheight = ScreenRows (); term_cr = "\r"; term_im = term_ei = term_ic = term_IC = (char *)NULL; term_up = term_dc = term_DC = visible_bell = (char *)NULL; + + /* Does the _GO32_ have a meta key? I don't know. */ + term_has_meta = 0; + term_mm = term_mo = (char *)NULL; + + /* It probably has arrow keys, but I don't know what they are. */ + term_ku = term_kd = term_kr = term_kl = (char *)NULL; + #if defined (HACK_TERMCAP_MOTION) - term_forward_char = (char *)NULL; + term_forward_char = (char *)NULL; #endif terminal_can_insert = 0; return; -#else - extern char *tgetstr (); +#else /* !__GO32__ */ char *term, *buffer; -#if defined (TIOCGWINSZ) - struct winsize window_size; -#endif int tty; term = terminal_name ? terminal_name : getenv ("TERM"); @@ -2109,6 +1198,7 @@ init_terminal_io (terminal_name) term_cr = "\r"; term_im = term_ei = term_ic = term_IC = (char *)NULL; term_up = term_dc = term_DC = visible_bell = (char *)NULL; + term_ku = term_kd = term_kl = term_kr = (char *)NULL; #if defined (HACK_TERMCAP_MOTION) term_forward_char = (char *)NULL; #endif @@ -2138,27 +1228,8 @@ init_terminal_io (terminal_name) tty = 0; screenwidth = screenheight = 0; -#if defined (TIOCGWINSZ) - if (ioctl (tty, TIOCGWINSZ, &window_size) == 0) - { - screenwidth = (int) window_size.ws_col; - screenheight = (int) window_size.ws_row; - } -#endif - - if (screenwidth <= 0 || screenheight <= 0) - { - screenwidth = tgetnum ("co"); - screenheight = tgetnum ("li"); - } - - screenwidth--; - if (screenwidth <= 0) - screenwidth = 79; - - if (screenheight <= 0) - screenheight = 24; + _rl_set_screen_size (tty, 0); term_im = tgetstr ("im", &buffer); term_ei = tgetstr ("ei", &buffer); @@ -2176,104 +1247,87 @@ init_terminal_io (terminal_name) term_DC = tgetstr ("DC", &buffer); visible_bell = tgetstr ("vb", &buffer); + + /* Check to see if this terminal has a meta key. */ + term_has_meta = (tgetflag ("km") || tgetflag ("MT")); + if (term_has_meta) + { + term_mm = tgetstr ("mm", &buffer); + term_mo = tgetstr ("mo", &buffer); + } + else + { + term_mm = (char *)NULL; + term_mo = (char *)NULL; + } + + /* Attempt to find and bind the arrow keys. Do not override already + bound keys in an overzealous attempt, however. */ + term_ku = tgetstr ("ku", &buffer); + term_kd = tgetstr ("kd", &buffer); + term_kr = tgetstr ("kr", &buffer); + term_kl = tgetstr ("kl", &buffer); + + if (term_ku) + { + Function *func; + + func = rl_function_of_keyseq (term_ku, _rl_keymap, (int *)NULL); + + if (!func || func == rl_do_lowercase_version) + rl_set_key (term_ku, rl_get_previous_history, _rl_keymap); + } + + if (term_kd) + { + Function *func; + + func = rl_function_of_keyseq (term_kd, _rl_keymap, (int *)NULL); + + if (!func || func == rl_do_lowercase_version) + rl_set_key (term_kd, rl_get_next_history, _rl_keymap); + } + + if (term_kr) + { + Function *func; + + func = rl_function_of_keyseq (term_kr, _rl_keymap, (int *)NULL); + + if (!func || func == rl_do_lowercase_version) + rl_set_key (term_kr, rl_forward, _rl_keymap); + } + + if (term_kl) + { + Function *func; + + func = rl_function_of_keyseq (term_kl, _rl_keymap, (int *)NULL); + + if (!func || func == rl_do_lowercase_version) + rl_set_key (term_kl, rl_backward, _rl_keymap); + } #endif /* !__GO32__ */ } /* A function for the use of tputs () */ -static void -output_character_function (c) +void +_rl_output_character_function (c) int c; { putc (c, out_stream); } /* Write COUNT characters from STRING to the output stream. */ -static void -output_some_chars (string, count) +void +_rl_output_some_chars (string, count) char *string; int count; { fwrite (string, 1, count, out_stream); } -/* Delete COUNT characters from the display line. */ -static -delete_chars (count) - int count; -{ -#ifdef __GO32__ - int r, c, w; - ScreenGetCursor(&r, &c); - w = ScreenCols(); - memcpy(ScreenPrimary+r*w+c, ScreenPrimary+r*w+c+count, w-c-count); - memset(ScreenPrimary+r*w+w-count, 0, count*2); -#else /* __GO32__ */ - if (count > screenwidth) - return; - - if (term_DC && *term_DC) - { - char *tgoto (), *buffer; - buffer = tgoto (term_DC, 0, count); - tputs (buffer, 1, output_character_function); - } - else - { - if (term_dc && *term_dc) - while (count--) - tputs (term_dc, 1, output_character_function); - } -#endif /* __GO32__ */ -} - -/* Insert COUNT characters from STRING to the output stream. */ -static void -insert_some_chars (string, count) - char *string; - int count; -{ -#ifdef __GO32__ - int r, c, w; - ScreenGetCursor(&r, &c); - w = ScreenCols(); - memcpy(ScreenPrimary+r*w+c+count, ScreenPrimary+r*w+c, w-c-count); - /* Print the text. */ - output_some_chars (string, count); -#else /* __GO32__ */ - /* If IC is defined, then we do not have to "enter" insert mode. */ - if (term_IC) - { - char *tgoto (), *buffer; - buffer = tgoto (term_IC, 0, count); - tputs (buffer, 1, output_character_function); - output_some_chars (string, count); - } - else - { - register int i; - - /* If we have to turn on insert-mode, then do so. */ - if (term_im && *term_im) - tputs (term_im, 1, output_character_function); - /* If there is a special command for inserting characters, then - use that first to open up the space. */ - if (term_ic && *term_ic) - { - for (i = count; i--; ) - tputs (term_ic, 1, output_character_function); - } - - /* Print the text. */ - output_some_chars (string, count); - - /* If there is a string to turn off insert mode, we had best use - it now. */ - if (term_ei && *term_ei) - tputs (term_ei, 1, output_character_function); - } -#endif /* __GO32__ */ -} /* Move the cursor back. */ backspace (count) @@ -2284,7 +1338,7 @@ backspace (count) #ifndef __GO32__ if (term_backspace) for (i = 0; i < count; i++) - tputs (term_backspace, 1, output_character_function); + tputs (term_backspace, 1, _rl_output_character_function); else #endif /* !__GO32__ */ for (i = 0; i < count; i++) @@ -2295,342 +1349,11 @@ backspace (count) crlf () { #if defined (NEW_TTY_DRIVER) - tputs (term_cr, 1, output_character_function); + tputs (term_cr, 1, _rl_output_character_function); #endif /* NEW_TTY_DRIVER */ putc ('\n', out_stream); } -/* Clear to the end of the line. COUNT is the minimum - number of character spaces to clear, */ -static void -clear_to_eol (count) - int count; -{ -#ifndef __GO32__ - if (term_clreol) - { - tputs (term_clreol, 1, output_character_function); - } - else -#endif /* !__GO32__ */ - { - register int i; - - /* Do one more character space. */ - count++; - - for (i = 0; i < count; i++) - putc (' ', out_stream); - - backspace (count); - } -} - - -/* **************************************************************** */ -/* */ -/* Saving and Restoring the TTY */ -/* */ -/* **************************************************************** */ - -/* Non-zero means that the terminal is in a prepped state. */ -static int terminal_prepped = 0; - -#if defined (NEW_TTY_DRIVER) - -/* Standard flags, including ECHO. */ -static int original_tty_flags = 0; - -/* Local mode flags, like LPASS8. */ -static int local_mode_flags = 0; - -/* Terminal characters. This has C-s and C-q in it. */ -static struct tchars original_tchars; - -/* Local special characters. This has the interrupt characters in it. */ -#if defined (TIOCGLTC) -static struct ltchars original_ltchars; -#endif - -/* We use this to get and set the tty_flags. */ -static struct sgttyb the_ttybuff; - -/* Put the terminal in CBREAK mode so that we can detect key presses. */ -static void -rl_prep_terminal () -{ -#ifndef __GO32__ - int tty = fileno (rl_instream); - SIGNALS_DECLARE_SAVED (saved_signals); - - if (terminal_prepped) - return; - - SIGNALS_BLOCK (SIGINT, saved_signals); - - /* We always get the latest tty values. Maybe stty changed them. */ - ioctl (tty, TIOCGETP, &the_ttybuff); - original_tty_flags = the_ttybuff.sg_flags; - - readline_echoing_p = (original_tty_flags & ECHO); - -#if defined (TIOCLGET) - ioctl (tty, TIOCLGET, &local_mode_flags); -#endif - -#if !defined (ANYP) -# define ANYP (EVENP | ODDP) -#endif - - /* If this terminal doesn't care how the 8th bit is used, - then we can use it for the meta-key. We check by seeing - if BOTH odd and even parity are allowed. */ - if (the_ttybuff.sg_flags & ANYP) - { -#if defined (PASS8) - the_ttybuff.sg_flags |= PASS8; -#endif - - /* Hack on local mode flags if we can. */ -#if defined (TIOCLGET) && defined (LPASS8) - { - int flags; - flags = local_mode_flags | LPASS8; - ioctl (tty, TIOCLSET, &flags); - } -#endif /* TIOCLGET && LPASS8 */ - } - -#if defined (TIOCGETC) - { - struct tchars temp; - - ioctl (tty, TIOCGETC, &original_tchars); - temp = original_tchars; - -#if defined (USE_XON_XOFF) - /* Get rid of C-s and C-q. - We remember the value of startc (C-q) so that if the terminal is in - xoff state, the user can xon it by pressing that character. */ - xon_char = temp.t_startc; - temp.t_stopc = -1; - temp.t_startc = -1; - - /* If there is an XON character, bind it to restart the output. */ - if (xon_char != -1) - rl_bind_key (xon_char, rl_restart_output); -#endif /* USE_XON_XOFF */ - - /* If there is an EOF char, bind eof_char to it. */ - if (temp.t_eofc != -1) - eof_char = temp.t_eofc; - -#if defined (NO_KILL_INTR) - /* Get rid of C-\ and C-c. */ - temp.t_intrc = temp.t_quitc = -1; -#endif /* NO_KILL_INTR */ - - ioctl (tty, TIOCSETC, &temp); - } -#endif /* TIOCGETC */ - -#if defined (TIOCGLTC) - { - struct ltchars temp; - - ioctl (tty, TIOCGLTC, &original_ltchars); - temp = original_ltchars; - - /* Make the interrupt keys go away. Just enough to make people - happy. */ - temp.t_dsuspc = -1; /* C-y */ - temp.t_lnextc = -1; /* C-v */ - - ioctl (tty, TIOCSLTC, &temp); - } -#endif /* TIOCGLTC */ - - the_ttybuff.sg_flags &= ~(ECHO | CRMOD); - the_ttybuff.sg_flags |= CBREAK; - ioctl (tty, TIOCSETN, &the_ttybuff); - - terminal_prepped = 1; - - SIGNALS_RESTORE (saved_signals); -#endif /* !__GO32__ */ -} - -/* Restore the terminal to its original state. */ -static void -rl_deprep_terminal () -{ -#ifndef __GO32__ - int tty = fileno (rl_instream); - SIGNALS_DECLARE_SAVED (saved_signals); - - if (!terminal_prepped) - return; - - SIGNALS_BLOCK (SIGINT, saved_signals); - - the_ttybuff.sg_flags = original_tty_flags; - ioctl (tty, TIOCSETN, &the_ttybuff); - readline_echoing_p = 1; - -#if defined (TIOCLGET) - ioctl (tty, TIOCLSET, &local_mode_flags); -#endif - -#if defined (TIOCSLTC) - ioctl (tty, TIOCSLTC, &original_ltchars); -#endif - -#if defined (TIOCSETC) - ioctl (tty, TIOCSETC, &original_tchars); -#endif - terminal_prepped = 0; - - SIGNALS_RESTORE (saved_signals); -#endif /* !__GO32 */ -} - -#else /* !defined (NEW_TTY_DRIVER) */ - -#if !defined (VMIN) -#define VMIN VEOF -#endif - -#if !defined (VTIME) -#define VTIME VEOL -#endif - -#ifndef __GO32__ -#if defined (TERMIOS_TTY_DRIVER) -static struct termios otio; -#else -static struct termio otio; -#endif /* !TERMIOS_TTY_DRIVER */ -#endif /* __GO32__ */ - -static void -rl_prep_terminal () -{ -#ifndef __GO32__ - int tty = fileno (rl_instream); -#if defined (TERMIOS_TTY_DRIVER) - struct termios tio; -#else - struct termio tio; -#endif /* !TERMIOS_TTY_DRIVER */ - - SIGNALS_DECLARE_SAVED (saved_signals); - - if (terminal_prepped) - return; - - /* Try to keep this function from being INTerrupted. We can do it - on POSIX and systems with BSD-like signal handling. */ - SIGNALS_BLOCK (SIGINT, saved_signals); - -#if defined (TERMIOS_TTY_DRIVER) - tcgetattr (tty, &tio); -#else - ioctl (tty, TCGETA, &tio); -#endif /* !TERMIOS_TTY_DRIVER */ - - otio = tio; - - readline_echoing_p = (tio.c_lflag & ECHO); - - tio.c_lflag &= ~(ICANON|ECHO); - - if (otio.c_cc[VEOF] != _POSIX_VDISABLE) - eof_char = otio.c_cc[VEOF]; - -#if defined (USE_XON_XOFF) -#if defined (IXANY) - tio.c_iflag &= ~(IXON|IXOFF|IXANY); -#else - /* `strict' Posix systems do not define IXANY. */ - tio.c_iflag &= ~(IXON|IXOFF); -#endif /* IXANY */ -#endif /* USE_XON_XOFF */ - - /* Only turn this off if we are using all 8 bits. */ - /* |ISTRIP|INPCK */ - tio.c_iflag &= ~(ISTRIP | INPCK); - - /* Make sure we differentiate between CR and NL on input. */ - tio.c_iflag &= ~(ICRNL | INLCR); - -#if !defined (HANDLE_SIGNALS) - tio.c_lflag &= ~ISIG; -#else - tio.c_lflag |= ISIG; -#endif - - tio.c_cc[VMIN] = 1; - tio.c_cc[VTIME] = 0; - - /* Turn off characters that we need on Posix systems with job control, - just to be sure. This includes ^Y and ^V. This should not really - be necessary. */ -#if defined (TERMIOS_TTY_DRIVER) && defined (_POSIX_JOB_CONTROL) - -#if defined (VLNEXT) - tio.c_cc[VLNEXT] = _POSIX_VDISABLE; -#endif - -#if defined (VDSUSP) - tio.c_cc[VDSUSP] = _POSIX_VDISABLE; -#endif - -#endif /* POSIX && JOB_CONTROL */ - -#if defined (TERMIOS_TTY_DRIVER) - tcsetattr (tty, TCSADRAIN, &tio); - tcflow (tty, TCOON); /* Simulate a ^Q. */ -#else - ioctl (tty, TCSETAW, &tio); - ioctl (tty, TCXONC, 1); /* Simulate a ^Q. */ -#endif /* !TERMIOS_TTY_DRIVER */ - - terminal_prepped = 1; - - SIGNALS_RESTORE (saved_signals); -#endif /* !__GO32__ */ -} - -static void -rl_deprep_terminal () -{ -#ifndef __GO32__ - int tty = fileno (rl_instream); - - /* Try to keep this function from being INTerrupted. We can do it - on POSIX and systems with BSD-like signal handling. */ - SIGNALS_DECLARE_SAVED (saved_signals); - - if (!terminal_prepped) - return; - - SIGNALS_BLOCK (SIGINT, saved_signals); - -#if defined (TERMIOS_TTY_DRIVER) - tcsetattr (tty, TCSADRAIN, &otio); - tcflow (tty, TCOON); /* Simulate a ^Q. */ -#else /* TERMIOS_TTY_DRIVER */ - ioctl (tty, TCSETAW, &otio); - ioctl (tty, TCXONC, 1); /* Simulate a ^Q. */ -#endif /* !TERMIOS_TTY_DRIVER */ - - terminal_prepped = 0; - - SIGNALS_RESTORE (saved_signals); -#endif /* !__GO32__ */ -} -#endif /* NEW_TTY_DRIVER */ - /* **************************************************************** */ /* */ @@ -2652,7 +1375,7 @@ alphabetic (c) return (1); if (allow_pathname_alphabetic_chars) - return ((int)rindex (pathname_alphabetic_chars, c)); + return ((int) strchr (pathname_alphabetic_chars, c)); else return (0); } @@ -2672,8 +1395,8 @@ ding () if (readline_echoing_p) { #ifndef __GO32__ - if (prefer_visible_bell && visible_bell) - tputs (visible_bell, 1, output_character_function); + if (_rl_prefer_visible_bell && visible_bell) + tputs (visible_bell, 1, _rl_output_character_function); else #endif /* !__GO32__ */ { @@ -2702,12 +1425,8 @@ rl_abort () /* Return a copy of the string between FROM and TO. FROM is inclusive, TO is not. */ -#if defined (sun) /* Yes, that's right, some crufty function in sunview is - called rl_copy (). */ -static -#endif char * -rl_copy (from, to) +rl_copy_text (from, to) int from, to; { register int length; @@ -2755,7 +1474,6 @@ rl_extend_line_buffer (len) rl_insert_text (string) char *string; { - extern int doing_an_undo; register int i, l = strlen (string); if (rl_end + l >= rl_line_buffer_len) @@ -2788,7 +1506,6 @@ rl_insert_text (string) rl_delete_text (from, to) int from, to; { - extern int doing_an_undo; register char *text; /* Fix it if the caller is confused. */ @@ -2798,7 +1515,7 @@ rl_delete_text (from, to) from = to; to = t; } - text = rl_copy (from, to); + text = rl_copy_text (from, to); strncpy (the_line + from, the_line + to, rl_end - to); /* Remember how to undo this delete. */ @@ -2856,7 +1573,7 @@ rl_forward (count) while (count) { #if defined (VI_MODE) - if (rl_point == (rl_end - (rl_editing_mode == vi_mode))) + if (rl_point >= (rl_end - (rl_editing_mode == vi_mode))) #else if (rl_point == rl_end) #endif /* VI_MODE */ @@ -2984,22 +1701,23 @@ rl_backward_word (count) /* Clear the current line. Numeric argument to C-l does this. */ rl_refresh_line () { - int curr_line = last_c_pos / screenwidth; - extern char *term_clreol; + int curr_line = _rl_last_c_pos / screenwidth; - move_vert(curr_line); - move_cursor_relative (0, the_line); /* XXX is this right */ + _rl_move_vert (curr_line); + _rl_move_cursor_relative (0, the_line); /* XXX is this right */ #ifdef __GO32__ { - int r, c, w; - ScreenGetCursor(&r, &c); - w = ScreenCols(); - memset(ScreenPrimary+r*w+c, 0, (w-c)*2); + int row, col, width, row_start; + + ScreenGetCursor (&row, &col); + width = ScreenCols (); + row_start = ScreenPrimary + (row * width); + memset (row_start + col, 0, (width - col) * 2); } #else /* __GO32__ */ if (term_clreol) - tputs (term_clreol, 1, output_character_function); + tputs (term_clreol, 1, _rl_output_character_function); #endif /* __GO32__/else */ rl_forced_update_display (); @@ -3011,8 +1729,6 @@ rl_refresh_line () the current line. */ rl_clear_screen () { - extern char *term_clrpag; - if (rl_explicit_arg) { rl_refresh_line (); @@ -3021,7 +1737,7 @@ rl_clear_screen () #ifndef __GO32__ if (term_clrpag) - tputs (term_clrpag, 1, output_character_function); + tputs (term_clrpag, 1, _rl_output_character_function); else #endif /* !__GO32__ */ crlf (); @@ -3081,7 +1797,7 @@ rl_insert (count, c) readline because of extra large arguments. */ if (count > 1 && count < 1024) { - string = alloca (1 + count); + string = (char *)alloca (1 + count); for (i = 0; i < count; i++) string[i] = c; @@ -3095,7 +1811,7 @@ rl_insert (count, c) { int decreaser; - string = alloca (1024 + 1); + string = (char *)alloca (1024 + 1); for (i = 0; i < 1024; i++) string[i] = c; @@ -3119,12 +1835,12 @@ rl_insert (count, c) int key = 0, t; i = 0; - string = alloca (ibuffer_len + 1); + string = (char *)alloca (ibuffer_len + 1); string[i++] = c; while ((t = rl_get_char (&key)) && - (keymap[key].type == ISFUNC && - keymap[key].function == rl_insert)) + (_rl_keymap[key].type == ISFUNC && + _rl_keymap[key].function == rl_insert)) string[i++] = key; if (t) @@ -3137,7 +1853,7 @@ rl_insert (count, c) else { /* Inserting a single character. */ - string = alloca (2); + string = (char *)alloca (2); string[1] = '\0'; string[0] = c; @@ -3149,7 +1865,9 @@ rl_insert (count, c) rl_quoted_insert (count) int count; { - int c = rl_read_key (); + int c; + + c = rl_read_key (); rl_insert (count, c); } @@ -3171,19 +1889,21 @@ rl_newline (count, key) #if defined (VI_MODE) { - extern int vi_doing_insert; - if (vi_doing_insert) + extern int _rl_vi_doing_insert; + if (_rl_vi_doing_insert) { rl_end_undo_group (); - vi_doing_insert = 0; + _rl_vi_doing_insert = 0; } } + rl_vi_set_last (); + #endif /* VI_MODE */ if (readline_echoing_p) { - move_vert (vis_botlin); - vis_botlin = 0; + _rl_move_vert (_rl_vis_botlin); + _rl_vis_botlin = 0; crlf (); fflush (out_stream); rl_display_fixed++; @@ -3194,8 +1914,8 @@ rl_clean_up_for_exit () { if (readline_echoing_p) { - move_vert (vis_botlin); - vis_botlin = 0; + _rl_move_vert (_rl_vis_botlin); + _rl_vis_botlin = 0; fflush (out_stream); rl_restart_output (); } @@ -3226,7 +1946,7 @@ rl_rubout (count) return; } - if (count > 1) + if (count > 1 || rl_explicit_arg) { int orig_point = rl_point; rl_backward (count); @@ -3237,14 +1957,11 @@ rl_rubout (count) int c = the_line[--rl_point]; rl_delete_text (rl_point, rl_point + 1); - if (rl_point == rl_end && alphabetic (c) && last_c_pos) + if (rl_point == rl_end && isprint (c) && _rl_last_c_pos) { - backspace (1); - putc (' ', out_stream); - backspace (1); - last_c_pos--; - visible_line[last_c_pos] = '\0'; - rl_display_fixed++; + int l; + l = rl_character_len (c, rl_point); + _rl_erase_at_end_of_line (l); } } } @@ -3266,7 +1983,7 @@ rl_delete (count, invoking_key) return; } - if (count > 1) + if (count > 1 || rl_explicit_arg) { int orig_point = rl_point; rl_forward (count); @@ -3277,6 +1994,28 @@ rl_delete (count, invoking_key) rl_delete_text (rl_point, rl_point + 1); } +/* Delete all spaces and tabs around point. */ +rl_delete_horizontal_space (count, ignore) + int count, ignore; +{ + int start = rl_point; + + while (rl_point && whitespace (the_line[rl_point - 1])) + rl_point--; + + start = rl_point; + + while (rl_point < rl_end && whitespace (the_line[rl_point])) + rl_point++; + + if (start == rl_point) + return; + else + { + rl_delete_text (start, rl_point); + rl_point = start; + } +} /* **************************************************************** */ /* */ @@ -3292,15 +2031,20 @@ rl_delete (count, invoking_key) using behaviour that they expect. */ rl_unix_word_rubout () { - if (!rl_point) ding (); - else { - int orig_point = rl_point; - while (rl_point && whitespace (the_line[rl_point - 1])) - rl_point--; - while (rl_point && !whitespace (the_line[rl_point - 1])) - rl_point--; - rl_kill_text (rl_point, orig_point); - } + if (!rl_point) + ding (); + else + { + int orig_point = rl_point; + + while (rl_point && whitespace (the_line[rl_point - 1])) + rl_point--; + + while (rl_point && !whitespace (the_line[rl_point - 1])) + rl_point--; + + rl_kill_text (rl_point, orig_point); + } } /* Here is C-u doing what Unix does. You don't *have* to use these @@ -3311,15 +2055,16 @@ rl_unix_word_rubout () doing. */ rl_unix_line_discard () { - if (!rl_point) ding (); - else { - rl_kill_text (rl_point, 0); - rl_point = 0; - } + if (!rl_point) + ding (); + else + { + rl_kill_text (rl_point, 0); + rl_point = 0; + } } - /* **************************************************************** */ /* */ /* Commands For Typos */ @@ -3452,8 +2197,8 @@ rl_transpose_words (count) } /* Get the text of the words. */ - word1 = rl_copy (w1_beg, w1_end); - word2 = rl_copy (w2_beg, w2_end); + word1 = rl_copy_text (w1_beg, w1_end); + word2 = rl_copy_text (w2_beg, w2_end); /* We are about to do many insertions and deletions. Remember them as one operation. */ @@ -3483,729 +2228,60 @@ rl_transpose_words (count) rl_transpose_chars (count) int count; { + char dummy[2]; + if (!count) return; - if (!rl_point || rl_end < 2) { - ding (); - return; - } + if (!rl_point || rl_end < 2) + { + ding (); + return; + } - while (count) + rl_begin_undo_group (); + + if (rl_point == rl_end) { - if (rl_point == rl_end) - { - int t = the_line[rl_point - 1]; + --rl_point; + count = 1; + } + rl_point--; - the_line[rl_point - 1] = the_line[rl_point - 2]; - the_line[rl_point - 2] = t; - } - else - { - int t = the_line[rl_point]; + dummy[0] = the_line[rl_point]; + dummy[1] = '\0'; - the_line[rl_point] = the_line[rl_point - 1]; - the_line[rl_point - 1] = t; + rl_delete_text (rl_point, rl_point + 1); - if (count < 0 && rl_point) - rl_point--; - else - rl_point++; - } + rl_point += count; + if (rl_point > rl_end) + rl_point = rl_end; + else if (rl_point < 0) + rl_point = 0; + rl_insert_text (dummy); - if (count < 0) - count++; - else - count--; - } + rl_end_undo_group (); } - /* **************************************************************** */ /* */ -/* Bogus Flow Control */ +/* Undo, and Undoing */ /* */ /* **************************************************************** */ -rl_restart_output (count, key) - int count, key; -{ - int fildes = fileno (rl_outstream); -#if defined (TIOCSTART) -#if defined (apollo) - ioctl (&fildes, TIOCSTART, 0); -#else - ioctl (fildes, TIOCSTART, 0); -#endif /* apollo */ +/* Non-zero tells rl_delete_text and rl_insert_text to not add to + the undo list. */ +static int doing_an_undo = 0; -#else -# if defined (TERMIOS_TTY_DRIVER) - tcflow (fildes, TCOON); -# else -# if defined (TCXONC) - ioctl (fildes, TCXONC, TCOON); -# endif /* TCXONC */ -# endif /* !TERMIOS_TTY_DRIVER */ -#endif /* TIOCSTART */ -} +/* The current undo list for THE_LINE. */ +UNDO_LIST *rl_undo_list = (UNDO_LIST *)NULL; -rl_stop_output (count, key) - int count, key; -{ - int fildes = fileno (rl_instream); - -#if defined (TIOCSTOP) -# if defined (apollo) - ioctl (&fildes, TIOCSTOP, 0); -# else - ioctl (fildes, TIOCSTOP, 0); -# endif /* apollo */ -#else -# if defined (TERMIOS_TTY_DRIVER) - tcflow (fildes, TCOOFF); -# else -# if defined (TCXONC) - ioctl (fildes, TCXONC, TCOON); -# endif /* TCXONC */ -# endif /* !TERMIOS_TTY_DRIVER */ -#endif /* TIOCSTOP */ -} - -/* **************************************************************** */ -/* */ -/* Completion matching, from readline's point of view. */ -/* */ -/* **************************************************************** */ - -/* Pointer to the generator function for completion_matches (). - NULL means to use filename_entry_function (), the default filename - completer. */ -Function *rl_completion_entry_function = (Function *)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. */ -Function *rl_attempted_completion_function = (Function *)NULL; - -/* Local variable states what happened during the last completion attempt. */ -static int completion_changed_buffer = 0; - -/* Complete the word at or before point. You have supplied the function - that does the initial simple matching selection algorithm (see - completion_matches ()). The default is to do filename completion. */ - -rl_complete (ignore, invoking_key) - int ignore, invoking_key; -{ - if (rl_last_func == rl_complete && !completion_changed_buffer) - rl_complete_internal ('?'); - else - rl_complete_internal (TAB); -} - -/* List the possible completions. See description of rl_complete (). */ -rl_possible_completions () -{ - rl_complete_internal ('?'); -} - -/* The user must press "y" or "n". Non-zero return means "y" pressed. */ -get_y_or_n () -{ - int c; - loop: - c = rl_read_key (); - if (c == 'y' || c == 'Y') return (1); - if (c == 'n' || c == 'N') return (0); - if (c == ABORT_CHAR) rl_abort (); - ding (); goto loop; -} - -/* 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. */ -int rl_completion_query_items = 100; - -/* 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\"\\'`@$><=" */ -char *rl_basic_word_break_characters = " \t\n\"\\'`@$><=;|&{("; - -/* 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. */ -char *rl_completer_word_break_characters = (char *)NULL; - -/* The list of characters which are used to quote a substring of the command - line. Command 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. */ -char *rl_completer_quote_characters = (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. */ -char *rl_special_prefixes = (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; - -/* 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. */ -Function *rl_ignore_some_completions_function = (Function *)NULL; - -/* 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. */ -rl_complete_internal (what_to_do) - int what_to_do; -{ - char *filename_completion_function (); - char **completion_matches (), **matches; - Function *our_func; - int start, scan, end, delimiter = 0; - char *text, *saved_line_buffer; - char quote_char = '\0'; - char *replacement; - - if (the_line) - saved_line_buffer = savestring (the_line); - else - saved_line_buffer = (char *)NULL; - - if (rl_completion_entry_function) - our_func = rl_completion_entry_function; - else - our_func = (int (*)())filename_completion_function; - - /* Only the completion entry function can change this. */ - rl_filename_completion_desired = 0; - - /* We now look backwards for the start of a filename/variable word. */ - end = rl_point; - - if (rl_point) - { - if (rl_completer_quote_characters) - { - /* We have a list of characters which can be used in pairs to quote - substrings for completion. Try to find the start of an unclosed - quoted substring. - FIXME: Doesn't yet handle '\' escapes to hid embedded quotes */ - for (scan = 0; scan < end; scan++) - { - if (quote_char != '\0') - { - /* Ignore everything until the matching close quote char */ - if (the_line[scan] == quote_char) - { - /* Found matching close quote. Abandon this substring. */ - quote_char = '\0'; - rl_point = end; - } - } - else if (rindex (rl_completer_quote_characters, the_line[scan])) - { - /* Found start of a quoted substring. */ - quote_char = the_line[scan]; - rl_point = scan + 1; - } - } - } - if (rl_point == end) - { - /* 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 do completion. */ - while (--rl_point && - !rindex (rl_completer_word_break_characters, - the_line[rl_point])) {;} - } - - /* If we are at a word break, then advance past it. */ - if (rindex (rl_completer_word_break_characters, the_line[rl_point])) - { - /* If the character that caused the word break was a quoting - character, then remember it as the delimiter. */ - if (rindex ("\"'", the_line[rl_point]) && (end - rl_point) > 1) - delimiter = the_line[rl_point]; - - /* 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 || - !rindex (rl_special_prefixes, the_line[rl_point])) - rl_point++; - } - } - - start = rl_point; - rl_point = end; - text = rl_copy (start, end); - - /* 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 = - (char **)(*rl_attempted_completion_function) (text, start, end); - - if (matches) - { - our_func = (Function *)NULL; - goto after_usual_completion; - } - } - - matches = completion_matches (text, our_func); - - after_usual_completion: - free (text); - - if (!matches) - ding (); - else - { - register int i; - - some_matches: - - /* 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) - { - char *lowest_common; - int j, newlen = 0; - - /* Sort the items. */ - /* It is safe to sort this array, because the lowest common - denominator found in matches[0] will remain in place. */ - for (i = 0; matches[i]; i++); - qsort (matches, i, sizeof (char *), compare_strings); - - /* Remember the lowest common denominator for it may be unique. */ - lowest_common = savestring (matches[0]); - - for (i = 0; matches[i + 1]; i++) - { - if (strcmp (matches[i], matches[i + 1]) == 0) - { - free (matches[i]); - matches[i] = (char *)-1; - } - else - newlen++; - } - - /* We have marked all the dead slots with (char *)-1. - Copy all the non-dead entries into a new array. */ - { - char **temp_array = - (char **)malloc ((3 + newlen) * sizeof (char *)); - - for (i = 1, j = 1; matches[i]; i++) - { - if (matches[i] != (char *)-1) - temp_array[j++] = matches[i]; - } - - temp_array[j] = (char *)NULL; - - if (matches[0] != (char *)-1) - free (matches[0]); - - free (matches); - - matches = temp_array; - } - - /* Place the lowest common denominator back in [0]. */ - matches[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 (matches[0], matches[1]) == 0) - { - free (matches[1]); - matches[1] = (char *)NULL; - } - } - - switch (what_to_do) - { - case TAB: - /* 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 && - our_func == (int (*)())filename_completion_function) - (void)(*rl_ignore_some_completions_function)(matches); - - /* If we are doing completions 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 = matches[0]; - if (matches[0] != NULL - && rl_completer_quote_characters != NULL - && (quote_char == '\0')) - { - for (i = 1; matches[i] != NULL; i++) - { - if (strpbrk (matches[i], rl_completer_word_break_characters)) - { - /* Found an embedded word break character in a potential - match, so need to prepend a quote character if we are - replacing the completion string. */ - replacement = (char *)alloca (strlen (matches[0]) + 2); - quote_char = *rl_completer_quote_characters; - *replacement = quote_char; - strcpy (replacement + 1, matches[0]); - break; - } - } - } - if (replacement) - { - rl_delete_text (start, rl_point); - rl_point = start; - rl_insert_text (replacement); - } - - /* If there are more matches, ring the bell to indicate. - If this was the only match, and we are hacking files, - check the file to see if it was a directory. If so, - add a '/' to the name. If not, and we are at the end - of the line, then add a space. */ - if (matches[1]) - { - ding (); /* There are other matches remaining. */ - } - else - { - char temp_string[16]; - int temp_index = 0; - - if (quote_char) - { - temp_string[temp_index++] = quote_char; - } - temp_string[temp_index++] = delimiter ? delimiter : ' '; - temp_string[temp_index++] = '\0'; - - if (rl_filename_completion_desired) - { - struct stat finfo; - char *filename = tilde_expand (matches[0]); - - if ((stat (filename, &finfo) == 0) && - S_ISDIR (finfo.st_mode)) - { - if (the_line[rl_point] != '/') - rl_insert_text ("/"); - } - else - { - if (rl_point == rl_end) - rl_insert_text (temp_string); - } - free (filename); - } - else - { - if (rl_point == rl_end) - rl_insert_text (temp_string); - } - } - break; - - case '*': - { - int i = 1; - - rl_delete_text (start, rl_point); - rl_point = start; - rl_begin_undo_group (); - if (matches[1]) - { - while (matches[i]) - { - rl_insert_text (matches[i++]); - rl_insert_text (" "); - } - } - else - { - rl_insert_text (matches[0]); - rl_insert_text (" "); - } - rl_end_undo_group (); - } - break; - - case '?': - { - int len, count, limit, max = 0; - int j, k, l; - - /* Handle simple case first. What if there is only one answer? */ - if (!matches[1]) - { - char *temp; - - if (rl_filename_completion_desired) - temp = rindex (matches[0], '/'); - else - temp = (char *)NULL; - - if (!temp) - temp = matches[0]; - else - temp++; - - crlf (); - fprintf (out_stream, "%s", temp); - crlf (); - goto restart; - } - - /* There is more than one answer. Find out how many there are, - and find out what the maximum printed length of a single entry - is. */ - for (i = 1; matches[i]; i++) - { - char *temp = (char *)NULL; - - /* If we are hacking filenames, then only count the characters - after the last slash in the pathname. */ - if (rl_filename_completion_desired) - temp = rindex (matches[i], '/'); - else - temp = (char *)NULL; - - if (!temp) - temp = matches[i]; - else - temp++; - - if (strlen (temp) > max) - max = strlen (temp); - } - - len = i; - - /* If there are many items, then ask the user if she - really wants to see them all. */ - if (len >= rl_completion_query_items) - { - crlf (); - fprintf (out_stream, - "There are %d possibilities. Do you really", len); - crlf (); - fprintf (out_stream, "wish to see them all? (y or n)"); - fflush (out_stream); - if (!get_y_or_n ()) - { - crlf (); - goto restart; - } - } - /* How many items of MAX length can we fit in the screen window? */ - max += 2; - limit = screenwidth / max; - if (limit != 1 && (limit * max == screenwidth)) - limit--; - - /* Avoid a possible floating exception. If max > screenwidth, - 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. */ - if (len < limit) count = 1; - - /* Sort the items if they are not already sorted. */ - if (!rl_ignore_completion_duplicates) - qsort (matches, len, sizeof (char *), compare_strings); - - /* Print the sorted items, up-and-down alphabetically, like - ls might. */ - crlf (); - - for (i = 1; i < count + 1; i++) - { - for (j = 0, l = i; j < limit; j++) - { - if (l > len || !matches[l]) - { - break; - } - else - { - char *temp = (char *)NULL; - - if (rl_filename_completion_desired) - temp = rindex (matches[l], '/'); - else - temp = (char *)NULL; - - if (!temp) - temp = matches[l]; - else - temp++; - - fprintf (out_stream, "%s", temp); - for (k = 0; k < max - strlen (temp); k++) - putc (' ', out_stream); - } - l += count; - } - crlf (); - } - restart: - - rl_on_new_line (); - } - break; - - default: - abort (); - } - - for (i = 0; matches[i]; i++) - free (matches[i]); - free (matches); - } - - /* Check to see if the line has changed through all of this manipulation. */ - if (saved_line_buffer) - { - if (strcmp (the_line, saved_line_buffer) != 0) - completion_changed_buffer = 1; - else - completion_changed_buffer = 0; - - free (saved_line_buffer); - } -} - -/* Stupid comparison routine for qsort () ing strings. */ -static int -compare_strings (s1, s2) - char **s1, **s2; -{ - return (strcmp (*s1, *s2)); -} - -/* A completion function for usernames. - TEXT contains a partial username preceded by a random - character (usually `~'). */ -char * -username_completion_function (text, state) - int state; - char *text; -{ -#ifdef __GO32__ - return (char *)NULL; -#else /* !__GO32__ */ - static char *username = (char *)NULL; - static struct passwd *entry; - static int namelen, first_char, first_char_loc; - - if (!state) - { - if (username) - free (username); - - first_char = *text; - - if (first_char == '~') - first_char_loc = 1; - else - first_char_loc = 0; - - username = savestring (&text[first_char_loc]); - namelen = strlen (username); - setpwent (); - } - - while (entry = getpwent ()) - { - if (strncmp (username, entry->pw_name, namelen) == 0) - break; - } - - if (!entry) - { - endpwent (); - return ((char *)NULL); - } - else - { - char *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 /* !__GO32__ */ -} - -/* **************************************************************** */ -/* */ -/* Undo, and Undoing */ -/* */ -/* **************************************************************** */ - -/* Non-zero tells rl_delete_text and rl_insert_text to not add to - the undo list. */ -int doing_an_undo = 0; - -/* The current undo list for THE_LINE. */ -UNDO_LIST *rl_undo_list = (UNDO_LIST *)NULL; - -/* Remember how to undo something. Concatenate some undos if that - seems right. */ -rl_add_undo (what, start, end, text) - enum undo_code what; - int start, end; - char *text; +/* Remember how to undo something. Concatenate some undos if that + seems right. */ +rl_add_undo (what, start, end, text) + enum undo_code what; + int start, end; + char *text; { UNDO_LIST *temp = (UNDO_LIST *)xmalloc (sizeof (UNDO_LIST)); temp->what = what; @@ -4219,15 +2295,17 @@ rl_add_undo (what, start, end, text) /* Free the existing undo list. */ free_undo_list () { - while (rl_undo_list) { - UNDO_LIST *release = rl_undo_list; - rl_undo_list = rl_undo_list->next; + while (rl_undo_list) + { + UNDO_LIST *release = rl_undo_list; + rl_undo_list = rl_undo_list->next; - if (release->what == UNDO_DELETE) - free (release->text); + if (release->what == UNDO_DELETE) + free (release->text); - free (release); - } + free (release); + } + rl_undo_list = (UNDO_LIST *)NULL; } /* Undo the next thing in the list. Return 0 if there @@ -4270,7 +2348,11 @@ undo_thing: if (waiting_for_begin) waiting_for_begin--; else +#if 0 abort (); +#else + ding (); +#endif break; } @@ -4311,7 +2393,7 @@ rl_modifying (start, end) if (start != end) { - char *temp = rl_copy (start, end); + char *temp = rl_copy_text (start, end); rl_begin_undo_group (); rl_add_undo (UNDO_DELETE, start, end, temp); rl_add_undo (UNDO_INSERT, start, end, (char *)NULL); @@ -4322,11 +2404,13 @@ rl_modifying (start, end) /* Revert the current line to its previous state. */ rl_revert_line () { - if (!rl_undo_list) ding (); - else { - while (rl_undo_list) - rl_do_undo (); - } + if (!rl_undo_list) + ding (); + else + { + while (rl_undo_list) + rl_do_undo (); + } } /* Do some undoing of things that were done. */ @@ -4373,6 +2457,7 @@ start_using_history () } /* Free the contents (and containing structure) of a HIST_ENTRY. */ +void free_history_entry (entry) HIST_ENTRY *entry; { @@ -4555,351 +2640,67 @@ rl_get_previous_history (count) } } - +/* Make C be the next command to be executed. */ +rl_execute_next (c) + int c; +{ + rl_pending_input = c; +} + /* **************************************************************** */ /* */ -/* I-Search and Searching */ +/* The Mark and the Region. */ /* */ /* **************************************************************** */ -/* Search backwards through the history looking for a string which is typed - interactively. Start with the current line. */ -rl_reverse_search_history (sign, key) - int sign; - int key; +/* Set the mark at POSITION. */ +rl_set_mark (position) + int position; { - rl_search_history (-sign, key); -} + if (position > rl_end) + return; -/* Search forwards through the history looking for a string which is typed - interactively. Start with the current line. */ -rl_forward_search_history (sign, key) - int sign; - int key; -{ - rl_search_history (sign, key); + rl_mark = position; } -/* Display the current state of the search in the echo-area. - SEARCH_STRING contains the string that is being searched for, - DIRECTION is zero for forward, or 1 for reverse, - WHERE is the history list number of the current line. If it is - -1, then this line is the starting one. */ -rl_display_search (search_string, reverse_p, where) - char *search_string; - int reverse_p, where; +/* Exchange the position of mark and point. */ +rl_exchange_mark_and_point () { - char *message = (char *)NULL; - - message = alloca (1 + (search_string ? strlen (search_string) : 0) + 30); - - *message = '\0'; - -#if defined (NOTDEF) - if (where != -1) - sprintf (message, "[%d]", where + history_base); -#endif /* NOTDEF */ + if (rl_mark > rl_end) + rl_mark = -1; - strcat (message, "("); - - if (reverse_p) - strcat (message, "reverse-"); - - strcat (message, "i-search)`"); - - if (search_string) - strcat (message, search_string); + if (rl_mark == -1) + { + ding (); + return; + } + else + { + int temp = rl_point; - strcat (message, "': "); - rl_message (message, 0, 0); - rl_redisplay (); + rl_point = rl_mark; + rl_mark = temp; + } } -/* Search through the history looking for an interactively typed string. - This is analogous to i-search. We start the search in the current line. - DIRECTION is which direction to search; >= 0 means forward, < 0 means - backwards. */ -rl_search_history (direction, invoking_key) - int direction; - int invoking_key; -{ - /* The string that the user types in to search for. */ - char *search_string = alloca (128); - - /* The current length of SEARCH_STRING. */ - int search_string_index; + +/* **************************************************************** */ +/* */ +/* Killing Mechanism */ +/* */ +/* **************************************************************** */ - /* The list of lines to search through. */ - char **lines; +/* What we assume for a max number of kills. */ +#define DEFAULT_MAX_KILLS 10 - /* The length of LINES. */ - int hlen; +/* The real variable to look at to find out when to flush kills. */ +int rl_max_kills = DEFAULT_MAX_KILLS; - /* Where we get LINES from. */ - HIST_ENTRY **hlist = history_list (); +/* Where to store killed text. */ +char **rl_kill_ring = (char **)NULL; - register int i = 0; - int orig_point = rl_point; - int orig_line = where_history (); - int last_found_line = orig_line; - int c, done = 0; - - /* The line currently being searched. */ - char *sline; - - /* Offset in that line. */ - int index; - - /* Non-zero if we are doing a reverse search. */ - int reverse = (direction < 0); - - /* Create an arrary of pointers to the lines that we want to search. */ - maybe_replace_line (); - if (hlist) - for (i = 0; hlist[i]; i++); - - /* Allocate space for this many lines, +1 for the current input line, - and remember those lines. */ - lines = alloca ((1 + (hlen = i)) * sizeof (char *)); - for (i = 0; i < hlen; i++) - lines[i] = hlist[i]->line; - - if (saved_line_for_history) - lines[i] = saved_line_for_history->line; - else - /* So I have to type it in this way instead. */ - { - char *alloced_line; - - /* Keep that mips alloca happy. */ - alloced_line = alloca (1 + strlen (the_line)); - lines[i] = alloced_line; - strcpy (lines[i], &the_line[0]); - } - - hlen++; - - /* The line where we start the search. */ - i = orig_line; - - /* Initialize search parameters. */ - *search_string = '\0'; - search_string_index = 0; - - /* Normalize DIRECTION into 1 or -1. */ - if (direction >= 0) - direction = 1; - else - direction = -1; - - rl_display_search (search_string, reverse, -1); - - sline = the_line; - index = rl_point; - - while (!done) - { - c = rl_read_key (); - - /* Hack C to Do What I Mean. */ - { - Function *f = (Function *)NULL; - - if (keymap[c].type == ISFUNC) - { - f = keymap[c].function; - - if (f == rl_reverse_search_history) - c = reverse ? -1 : -2; - else if (f == rl_forward_search_history) - c = !reverse ? -1 : -2; - } - } - - switch (c) - { - case ESC: - done = 1; - continue; - - /* case invoking_key: */ - case -1: - goto search_again; - - /* switch directions */ - case -2: - direction = -direction; - reverse = (direction < 0); - - goto do_search; - - case CTRL ('G'): - strcpy (the_line, lines[orig_line]); - rl_point = orig_point; - rl_end = strlen (the_line); - rl_clear_message (); - return; - - default: - if (c < 32 || c > 126) - { - rl_execute_next (c); - done = 1; - continue; - } - else - { - search_string[search_string_index++] = c; - search_string[search_string_index] = '\0'; - goto do_search; - - search_again: - - if (!search_string_index) - continue; - else - { - if (reverse) - --index; - else - if (index != strlen (sline)) - ++index; - else - ding (); - } - do_search: - - while (1) - { - if (reverse) - { - while (index >= 0) - if (strncmp - (search_string, sline + index, search_string_index) - == 0) - goto string_found; - else - index--; - } - else - { - register int limit = - (strlen (sline) - search_string_index) + 1; - - while (index < limit) - { - if (strncmp (search_string, - sline + index, - search_string_index) == 0) - goto string_found; - index++; - } - } - - next_line: - i += direction; - - /* At limit for direction? */ - if ((reverse && i < 0) || - (!reverse && i == hlen)) - goto search_failed; - - sline = lines[i]; - if (reverse) - index = strlen (sline); - else - index = 0; - - /* If the search string is longer than the current - line, no match. */ - if (search_string_index > strlen (sline)) - goto next_line; - - /* Start actually searching. */ - if (reverse) - index -= search_string_index; - } - - search_failed: - /* We cannot find the search string. Ding the bell. */ - ding (); - i = last_found_line; - break; - - string_found: - /* We have found the search string. Just display it. But don't - actually move there in the history list until the user accepts - the location. */ - { - int line_len; - - line_len = strlen (lines[i]); - - if (line_len >= rl_line_buffer_len) - rl_extend_line_buffer (line_len); - - strcpy (the_line, lines[i]); - rl_point = index; - rl_end = line_len; - last_found_line = i; - rl_display_search - (search_string, reverse, (i == orig_line) ? -1 : i); - } - } - } - continue; - } - - /* The searching is over. The user may have found the string that she - was looking for, or else she may have exited a failing search. If - INDEX is -1, then that shows that the string searched for was not - found. We use this to determine where to place rl_point. */ - { - int now = last_found_line; - - /* First put back the original state. */ - strcpy (the_line, lines[orig_line]); - - if (now < orig_line) - rl_get_previous_history (orig_line - now); - else - rl_get_next_history (now - orig_line); - - /* If the index of the "matched" string is less than zero, then the - final search string was never matched, so put point somewhere - reasonable. */ - if (index < 0) - index = strlen (the_line); - - rl_point = index; - rl_clear_message (); - } -} - -/* Make C be the next command to be executed. */ -rl_execute_next (c) - int c; -{ - rl_pending_input = c; -} - -/* **************************************************************** */ -/* */ -/* Killing Mechanism */ -/* */ -/* **************************************************************** */ - -/* What we assume for a max number of kills. */ -#define DEFAULT_MAX_KILLS 10 - -/* The real variable to look at to find out when to flush kills. */ -int rl_max_kills = DEFAULT_MAX_KILLS; - -/* Where to store killed text. */ -char **rl_kill_ring = (char **)NULL; - -/* Where we are in the kill ring. */ -int rl_kill_index = 0; +/* Where we are in the kill ring. */ +int rl_kill_index = 0; /* How many slots we have in the kill ring. */ int rl_kill_ring_length = 0; @@ -4919,7 +2720,7 @@ rl_kill_text (from, to) int from, to; { int slot; - char *text = rl_copy (from, to); + char *text = rl_copy_text (from, to); /* Is there anything to kill? */ if (from == to) @@ -5087,7 +2888,10 @@ rl_backward_kill_line (direction) /* Yank back the last killed text. This ignores arguments. */ rl_yank () { - if (!rl_kill_ring) rl_abort (); + if (!rl_kill_ring) + rl_abort (); + + rl_set_mark (rl_point); rl_insert_text (rl_kill_ring[rl_kill_index]); } @@ -5147,7 +2951,7 @@ rl_yank_nth_arg (count, ignore) rl_begin_undo_group (); #if defined (VI_MODE) - /* Vi mode always inserts a space befoe yanking the argument, and it + /* Vi mode always inserts a space before yanking the argument, and it inserts it right *after* rl_point. */ if (rl_editing_mode == vi_mode) rl_point++; @@ -5174,1361 +2978,72 @@ rl_vi_editing_mode () rl_emacs_editing_mode () { rl_editing_mode = emacs_mode; - keymap = emacs_standard_keymap; + _rl_keymap = emacs_standard_keymap; } /* **************************************************************** */ /* */ -/* Completion */ +/* USG (System V) Support */ /* */ /* **************************************************************** */ -/* Non-zero means that case is not significant in completion. */ -int completion_case_fold = 0; - -/* 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 ** -completion_matches (text, entry_function) - char *text; - char *(*entry_function) (); +int +rl_getc (stream) + FILE *stream; { - /* Number of slots in match_list. */ - int match_list_size; - - /* The list of matches. */ - char **match_list = - (char **)xmalloc (((match_list_size = 10) + 1) * sizeof (char *)); - - /* Number of matches actually found. */ - int matches = 0; - - /* Temporary string binder. */ - char *string; + int result; + unsigned char c; - match_list[1] = (char *)NULL; +#ifdef __GO32__ + if (isatty (0)) + return (getkey () & 0x7f); +#endif /* __GO32__ */ - while (string = (*entry_function) (text, matches)) + while (1) { - if (matches + 1 == match_list_size) - match_list = (char **)xrealloc - (match_list, ((match_list_size += 10) + 1) * sizeof (char *)); + result = read (fileno (stream), &c, sizeof (unsigned char)); - match_list[++matches] = string; - match_list[matches + 1] = (char *)NULL; - } + if (result == sizeof (unsigned char)) + return (c); - /* If there were any matches, then look through them finding out the - lowest common denominator. That then becomes match_list[0]. */ - if (matches) - { - register int i = 1; - int low = 100000; /* Count of max-matched characters. */ + /* If zero characters are returned, then the file that we are + reading from is empty! Return EOF in that case. */ + if (result == 0) + return (EOF); - /* If only one match, just use that. */ - if (matches == 1) - { - match_list[0] = match_list[1]; - match_list[1] = (char *)NULL; - } - else +#if defined (EWOULDBLOCK) + if (errno == EWOULDBLOCK) { - /* Otherwise, compare each member of the list with - the next, finding out where they stop matching. */ + int flags; - while (i < matches) + if ((flags = fcntl (fileno (stream), F_GETFL, 0)) < 0) + return (EOF); + if (flags & O_NDELAY) { - register int c1, c2, si; - - if (completion_case_fold) - { - for (si = 0; - (c1 = to_lower(match_list[i][si])) && - (c2 = to_lower(match_list[i + 1][si])); - si++) - if (c1 != c2) break; - } - else - { - for (si = 0; - (c1 = match_list[i][si]) && - (c2 = match_list[i + 1][si]); - si++) - if (c1 != c2) break; - } - - if (low > si) low = si; - i++; + flags &= ~O_NDELAY; + fcntl (fileno (stream), F_SETFL, flags); + continue; } - match_list[0] = (char *)xmalloc (low + 1); - strncpy (match_list[0], match_list[1], low); - match_list[0][low] = '\0'; - } - } - else /* There were no matches. */ - { - free (match_list); - match_list = (char **)NULL; - } - return (match_list); -} - -/* 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 * -filename_completion_function (text, state) - int state; - char *text; -{ - static DIR *directory; - static char *filename = (char *)NULL; - static char *dirname = (char *)NULL; - static char *users_dirname = (char *)NULL; - static int filename_len; - - dirent *entry = (dirent *)NULL; - - /* If we don't have any state, then do some initialization. */ - if (!state) - { - char *temp; - - if (dirname) free (dirname); - if (filename) free (filename); - if (users_dirname) free (users_dirname); - - filename = savestring (text); - if (!*text) text = "."; - dirname = savestring (text); - - temp = rindex (dirname, '/'); - - if (temp) - { - strcpy (filename, ++temp); - *temp = '\0'; - } - else - strcpy (dirname, "."); - - /* We aren't done yet. We also support the "~user" syntax. */ - - /* Save the version of the directory that the user typed. */ - users_dirname = savestring (dirname); - { - char *temp_dirname; - - temp_dirname = tilde_expand (dirname); - free (dirname); - dirname = temp_dirname; - - if (rl_symbolic_link_hook) - (*rl_symbolic_link_hook) (&dirname); - } - directory = opendir (dirname); - 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/te. If the directory name - contains globbing characters, then build an array of directories to - glob on, and glob on the first one. */ - - /* Now that we have some state, we can read the directory. */ - - while (directory && (entry = readdir (directory))) - { - /* Special case for no filename. - All entries except "." and ".." match. */ - if (!filename_len) - { - if ((strcmp (entry->d_name, ".") != 0) && - (strcmp (entry->d_name, "..") != 0)) - break; - } - else - { - /* Otherwise, if these match upto the length of filename, then - it is a match. */ - if (entry->d_name[0] == filename[0] && /* Quick test */ - (strncmp (filename, entry->d_name, filename_len) == 0)) - { - break; - } - } - } - - if (!entry) - { - if (directory) - { - closedir (directory); - directory = (DIR *)NULL; - } - return (char *)NULL; - } - else - { - char *temp; - - if (dirname && (strcmp (dirname, ".") != 0)) - { - temp = (char *) - xmalloc (1 + strlen (users_dirname) + strlen (entry->d_name)); - strcpy (temp, users_dirname); - strcat (temp, entry->d_name); - } - else - { - temp = (savestring (entry->d_name)); + continue; } - return (temp); - } -} - - -/* **************************************************************** */ -/* */ -/* Binding keys */ -/* */ -/* **************************************************************** */ - -/* rl_add_defun (char *name, Function *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. */ -rl_add_defun (name, function, key) - char *name; - Function *function; - int key; -{ - if (key != -1) - rl_bind_key (key, function); - rl_add_funmap_entry (name, function); -} - -/* Bind KEY to FUNCTION. Returns non-zero if KEY is out of range. */ -int -rl_bind_key (key, function) - int key; - Function *function; -{ - if (key < 0) - return (key); +#endif /* EWOULDBLOCK */ - if (key > 127 && key < 256) - { - if (keymap[ESC].type == ISKMAP) +#if defined (_POSIX_VERSION) && defined (EAGAIN) && defined (O_NONBLOCK) + if (errno == EAGAIN) { - Keymap escmap = (Keymap)keymap[ESC].function; - - key -= 128; - escmap[key].type = ISFUNC; - escmap[key].function = function; - return (0); - } - return (key); - } - - keymap[key].type = ISFUNC; - keymap[key].function = function; - 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; - Function *function; - Keymap map; -{ - int result; - Keymap oldmap = keymap; - - keymap = map; - result = rl_bind_key (key, function); - keymap = oldmap; - return (result); -} - -/* 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, (Function *)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, (Function *)NULL, map)); -} - -/* 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. */ -rl_set_key (keyseq, function, map) - char *keyseq; - Function *function; - Keymap map; -{ - rl_generic_bind (ISFUNC, keyseq, function, map); -} - -/* 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. */ -rl_macro_bind (keyseq, macro, map) - char *keyseq, *macro; - Keymap map; -{ - char *macro_keys; - int macro_keys_len; - - macro_keys = (char *)xmalloc ((2 * strlen (macro)) + 1); - - if (rl_translate_keyseq (macro, macro_keys, ¯o_keys_len)) - { - free (macro_keys); - return; - } - rl_generic_bind (ISMACR, keyseq, macro_keys, map); -} + int flags; -/* 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. */ - -static void -rl_generic_bind (type, keyseq, data, map) - int type; - char *keyseq, *data; - Keymap map; -{ - char *keys; - int keys_len; - register int i; - - /* If no keys to bind to, exit right away. */ - if (!keyseq || !*keyseq) - { - if (type == ISMACR) - free (data); - return; - } - - keys = alloca (1 + (2 * strlen (keyseq))); - - /* Translate the ASCII representation of KEYSEQ into an array - of characters. Stuff the characters into ARRAY, and the - length of ARRAY into LENGTH. */ - if (rl_translate_keyseq (keyseq, keys, &keys_len)) - return; - - /* Bind keys, making new keymaps as necessary. */ - for (i = 0; i < keys_len; i++) - { - if (i + 1 < keys_len) - { - if (map[keys[i]].type != ISKMAP) + if ((flags = fcntl (fileno (stream), F_GETFL, 0)) < 0) + return (EOF); + if (flags & O_NONBLOCK) { - if (map[i].type == ISMACR) - free ((char *)map[i].function); - - map[keys[i]].type = ISKMAP; - map[keys[i]].function = (Function *)rl_make_bare_keymap (); - } - map = (Keymap)map[keys[i]].function; - } - else - { - if (map[keys[i]].type == ISMACR) - free ((char *)map[keys[i]].function); - - map[keys[i]].function = (Function *)data; - map[keys[i]].type = type; - } - } -} - -/* 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. */ -rl_translate_keyseq (seq, array, len) - char *seq, *array; - int *len; -{ - register int i, c, l = 0; - - for (i = 0; c = seq[i]; i++) - { - if (c == '\\') - { - c = seq[++i]; - - if (!c) - break; - - if (((c == 'C' || c == 'M') && seq[i + 1] == '-') || - (c == 'e')) - { - /* Handle special case of backwards define. */ - if (strncmp (&seq[i], "C-\\M-", 5) == 0) - { - array[l++] = ESC; - i += 5; - array[l++] = CTRL (to_upper (seq[i])); - if (!seq[i]) - i--; - continue; - } - - switch (c) - { - case 'M': - i++; - array[l++] = ESC; - break; - - case 'C': - i += 2; - /* Special hack for C-?... */ - if (seq[i] == '?') - array[l++] = RUBOUT; - else - array[l++] = CTRL (to_upper (seq[i])); - break; - - case 'e': - array[l++] = ESC; - } - + flags &= ~O_NONBLOCK; + fcntl (fileno (stream), F_SETFL, flags); continue; } } - array[l++] = c; - } - - *len = l; - array[l] = '\0'; - return (0); -} - -/* Return a pointer to the function that STRING represents. - If STRING doesn't have a matching function, then a NULL pointer - is returned. */ -Function * -rl_named_function (string) - char *string; -{ - register int i; - - for (i = 0; funmap[i]; i++) - if (stricmp (funmap[i]->name, string) == 0) - return (funmap[i]->function); - return ((Function *)NULL); -} - -/* The last key bindings file read. */ -#ifdef __MSDOS__ -/* Don't know what to do, but this is a guess */ -static char *last_readline_init_file = "/INPUTRC"; -#else -static char *last_readline_init_file = "~/.inputrc"; -#endif - -/* Re-read the current keybindings file. */ -rl_re_read_init_file (count, ignore) - int count, ignore; -{ - rl_read_init_file ((char *)NULL); -} - -/* Do key bindings from a file. If FILENAME is NULL it defaults - to `~/.inputrc'. If the file existed and could be opened and - read, 0 is returned, otherwise errno is returned. */ -int -rl_read_init_file (filename) - char *filename; -{ - register int i; - char *buffer, *openname, *line, *end; - struct stat finfo; - int file; - - /* Default the filename. */ - if (!filename) - filename = last_readline_init_file; - - openname = tilde_expand (filename); - - if (!openname || *openname == '\000') - return ENOENT; - - if ((stat (openname, &finfo) < 0) || - (file = open (openname, O_RDONLY, 0666)) < 0) - { - free (openname); - return (errno); - } - else - free (openname); - - last_readline_init_file = filename; - - /* Read the file into BUFFER. */ - buffer = (char *)xmalloc (finfo.st_size + 1); - i = read (file, buffer, finfo.st_size); - close (file); - - if (i != finfo.st_size) - return (errno); - - /* Loop over the lines in the file. Lines that start with `#' are - comments; all other lines are commands for readline initialization. */ - line = buffer; - end = buffer + finfo.st_size; - while (line < end) - { - /* Find the end of this line. */ - for (i = 0; line + i != end && line[i] != '\n'; i++); - - /* Mark end of line. */ - line[i] = '\0'; - - /* If the line is not a comment, then parse it. */ - if (*line != '#') - rl_parse_and_bind (line); - - /* Move to the next line. */ - line += i + 1; - } - return (0); -} - -/* **************************************************************** */ -/* */ -/* Parser Directives */ -/* */ -/* **************************************************************** */ - -/* Conditionals. */ - -/* Calling programs set this to have their argv[0]. */ -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 = 0; -static int if_stack_size = 0; - -/* Push parsing_conditionalized_out, and set parser state based on ARGS. */ -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++] = 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 (parsing_conditionalized_out) - return; - - /* 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 && strnicmp (args, "term=", 5) == 0) - { - char *tem, *tname; - - /* Terminals like "aaa-60" are equivalent to "aaa". */ - tname = savestring (rl_terminal_name); - tem = rindex (tname, '-'); - if (tem) - *tem = '\0'; - - if (stricmp (args + 5, tname) == 0) - parsing_conditionalized_out = 0; - else - parsing_conditionalized_out = 1; - } -#if defined (VI_MODE) - else if (strnicmp (args, "mode=", 5) == 0) - { - int mode; - - if (stricmp (args + 5, "emacs") == 0) - mode = emacs_mode; - else if (stricmp (args + 5, "vi") == 0) - mode = vi_mode; - else - mode = no_mode; - - if (mode == rl_editing_mode) - parsing_conditionalized_out = 0; - else - parsing_conditionalized_out = 1; - } -#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 (stricmp (args, rl_readline_name) == 0) - parsing_conditionalized_out = 0; - else - parsing_conditionalized_out = 1; -} - -/* Invert the current parser state if there is anything on the stack. */ -parser_else (args) - char *args; -{ - register int i; - - if (!if_stack_depth) - { - /* Error message? */ - return; - } - - /* 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++) - if (if_stack[i] == 1) - return; - - /* Invert the state of parsing if at top level. */ - parsing_conditionalized_out = !parsing_conditionalized_out; -} - -/* Terminate a conditional, popping the value of - parsing_conditionalized_out from the stack. */ -parser_endif (args) - char *args; -{ - if (if_stack_depth) - parsing_conditionalized_out = if_stack[--if_stack_depth]; - else - { - /* *** What, no error message? *** */ - } -} - -/* Associate textual names with actual functions. */ -static struct { - char *name; - Function *function; -} parser_directives [] = { - { "if", parser_if }, - { "endif", parser_endif }, - { "else", parser_else }, - { (char *)0x0, (Function *)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 (stricmp (directive, parser_directives[i].name) == 0) - { - (*parser_directives[i].function) (args); - return (0); - } - - /* *** Should an error message be output? */ - return (1); -} - -/* Ugly but working hack for binding prefix meta. */ -#define PREFIX_META_HACK - -static int substring_member_of_array (); - -/* 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. */ -rl_parse_and_bind (string) - char *string; -{ - extern char *possible_control_prefixes[], *possible_meta_prefixes[]; - char *funname, *kname; - register int c; - int key, i; - - while (string && whitespace (*string)) - string++; - - if (!string || !*string || *string == '#') - return; - - /* If this is a parser directive, act on it. */ - if (*string == '$') - { - handle_parser_directive (&string[1]); - return; - } - - /* If we are supposed to be skipping parsing right now, then do it. */ - if (parsing_conditionalized_out) - return; - - i = 0; - /* If this keyname is a complex key expression surrounded by quotes, - advance to after the matching close quote. */ - if (*string == '"') - { - for (i = 1; c = string[i]; i++) - { - if (c == '"' && string[i - 1] != '\\') - break; - } - } - - /* Advance to the colon (:) or whitespace which separates the two objects. */ - for (; (c = string[i]) && c != ':' && c != ' ' && c != '\t'; i++ ); - - /* Mark the end of the command (or keyname). */ - if (string[i]) - string[i++] = '\0'; - - /* If this is a command to set a variable, then do that. */ - if (stricmp (string, "set") == 0) - { - char *var = string + i; - char *value; - - /* Make VAR point to start of variable name. */ - while (*var && whitespace (*var)) var++; - - /* Make value point to start of value string. */ - value = var; - while (*value && !whitespace (*value)) value++; - if (*value) - *value++ = '\0'; - while (*value && whitespace (*value)) value++; - - rl_variable_bind (var, value); - return; - } - - /* 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. */ - if (*funname == '\'' || *funname == '"') - { - int delimiter = string[i++]; - - for (; c = string[i]; i++) - { - if (c == delimiter && string[i - 1] != '\\') - break; - } - if (c) - i++; - } - - /* Advance to the end of the string. */ - for (; string[i] && !whitespace (string[i]); i++); - - /* No extra whitespace at the end of the string. */ - string[i] = '\0'; - - /* If this is a new-style key-binding, then do the binding with - rl_set_key (). Otherwise, let the older code deal with it. */ - if (*string == '"') - { - char *seq = alloca (1 + strlen (string)); - register int j, k = 0; - - for (j = 1; string[j]; j++) - { - if (string[j] == '"' && string[j - 1] != '\\') - break; - - seq[k++] = string[j]; - } - seq[k] = '\0'; - - /* Binding macro? */ - if (*funname == '\'' || *funname == '"') - { - j = strlen (funname); - - if (j && funname[j - 1] == *funname) - funname[j - 1] = '\0'; - - rl_macro_bind (seq, &funname[1], keymap); - } - else - rl_set_key (seq, rl_named_function (funname), keymap); - - return; - } - - /* Get the actual character we want to deal with. */ - kname = rindex (string, '-'); - if (!kname) - kname = string; - else - kname++; - - key = glean_key_from_name (kname); - - /* Add in control and meta bits. */ - if (substring_member_of_array (string, possible_control_prefixes)) - key = CTRL (to_upper (key)); - - if (substring_member_of_array (string, possible_meta_prefixes)) - key = META (key); - - /* Temporary. Handle old-style keyname with macro-binding. */ - if (*funname == '\'' || *funname == '"') - { - char seq[2]; - int fl = strlen (funname); - - seq[0] = key; seq[1] = '\0'; - if (fl && funname[fl - 1] == *funname) - funname[fl - 1] = '\0'; - - rl_macro_bind (seq, &funname[1], keymap); - } -#if defined (PREFIX_META_HACK) - /* Ugly, but working hack to keep prefix-meta around. */ - else if (stricmp (funname, "prefix-meta") == 0) - { - char seq[2]; - - seq[0] = key; - seq[1] = '\0'; - rl_generic_bind (ISKMAP, seq, (char *)emacs_meta_keymap, keymap); - } -#endif /* PREFIX_META_HACK */ - else - rl_bind_key (key, rl_named_function (funname)); -} - -rl_variable_bind (name, value) - char *name, *value; -{ - if (stricmp (name, "editing-mode") == 0) - { - if (strnicmp (value, "vi", 2) == 0) - { -#if defined (VI_MODE) - keymap = vi_insertion_keymap; - rl_editing_mode = vi_mode; -#else -#if defined (NOTDEF) - /* What state is the terminal in? I'll tell you: - non-determinate! That means we cannot do any output. */ - ding (); -#endif /* NOTDEF */ -#endif /* VI_MODE */ - } - else if (strnicmp (value, "emacs", 5) == 0) - { - keymap = emacs_standard_keymap; - rl_editing_mode = emacs_mode; - } - } - else if (stricmp (name, "horizontal-scroll-mode") == 0) - { - if (!*value || stricmp (value, "On") == 0) - horizontal_scroll_mode = 1; - else - horizontal_scroll_mode = 0; - } - else if (stricmp (name, "mark-modified-lines") == 0) - { - if (!*value || stricmp (value, "On") == 0) - mark_modified_lines = 1; - else - mark_modified_lines = 0; - } - else if (stricmp (name, "prefer-visible-bell") == 0) - { - if (!*value || stricmp (value, "On") == 0) - prefer_visible_bell = 1; - else - prefer_visible_bell = 0; - } - else if (stricmp (name, "comment-begin") == 0) - { -#if defined (VI_MODE) - extern char *rl_vi_comment_begin; - - if (*value) - { - if (rl_vi_comment_begin) - free (rl_vi_comment_begin); - - rl_vi_comment_begin = savestring (value); - } -#endif /* VI_MODE */ - } -} - -/* Return the character which matches NAME. - For example, `Space' returns ' '. */ - -typedef struct { - char *name; - int value; -} assoc_list; - -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 } -}; - -int -glean_key_from_name (name) - char *name; -{ - register int i; - - for (i = 0; name_key_alist[i].name; i++) - if (stricmp (name, name_key_alist[i].name) == 0) - return (name_key_alist[i].value); - - return (*name); -} - - -/* **************************************************************** */ -/* */ -/* 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 (ignore) - int ignore; -{ - register int i; - char **funmap_names; - extern char **rl_funmap_names (); - - funmap_names = rl_funmap_names (); - - if (!funmap_names) - return; - - for (i = 0; funmap_names[i]; i++) - fprintf (rl_outstream, "%s\n", funmap_names[i]); - - free (funmap_names); -} - -/* Return a NULL terminated array of strings which represent the key - sequences that are used to invoke FUNCTION in MAP. */ -static char ** -invoking_keyseqs_in_map (function, map) - Function *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 < 128; 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 = (char *)xmalloc (5); - - if (CTRL_P (key)) - sprintf (keyname, "\\C-%c", to_lower (UNCTRL (key))); - else if (key == RUBOUT) - sprintf (keyname, "\\C-?"); - else - sprintf (keyname, "%c", key); - - if (result_index + 2 > result_size) - { - if (!result) - result = (char **) xmalloc - ((result_size = 10) * sizeof (char *)); - else - result = (char **) xrealloc - (result, (result_size += 10) * sizeof (char *)); - } - - result[result_index++] = keyname; - result[result_index] = (char *)NULL; - } - break; - - case ISKMAP: - { - char **seqs = (char **)NULL; - - /* 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 = - invoking_keyseqs_in_map (function, (Keymap)map[key].function); - - if (seqs) - { - register int i; - - for (i = 0; seqs[i]; i++) - { - char *keyname = (char *)xmalloc (6 + strlen (seqs[i])); - - if (key == ESC) - sprintf (keyname, "\\e"); - else if (CTRL_P (key)) - sprintf (keyname, "\\C-%c", to_lower (UNCTRL (key))); - else if (key == RUBOUT) - sprintf (keyname, "\\C-?"); - else - sprintf (keyname, "%c", key); - - strcat (keyname, seqs[i]); - - if (result_index + 2 > result_size) - { - if (!result) - result = (char **) - xmalloc ((result_size = 10) * sizeof (char *)); - else - result = (char **) - xrealloc (result, - (result_size += 10) * sizeof (char *)); - } - - result[result_index++] = keyname; - result[result_index] = (char *)NULL; - } - } - } - 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) - Function *function; -{ - return (invoking_keyseqs_in_map (function, keymap)); -} - -/* 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) - int count; -{ - void rl_function_dumper (); - - rl_function_dumper (rl_explicit_arg); - rl_on_new_line (); - return (0); -} - -/* 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; - char **rl_funmap_names (), **names; - char *name; - - names = rl_funmap_names (); - - fprintf (rl_outstream, "\n"); - - for (i = 0; name = names[i]; i++) - { - Function *function; - char **invokers; - - function = rl_named_function (name); - invokers = invoking_keyseqs_in_map (function, keymap); - - if (print_readably) - { - if (!invokers) - fprintf (rl_outstream, "# %s (not bound)\n", name); - else - { - register int j; - - for (j = 0; invokers[j]; j++) - { - fprintf (rl_outstream, "\"%s\": %s\n", - invokers[j], name); - free (invokers[j]); - } - - free (invokers); - } - } - else - { - if (!invokers) - fprintf (rl_outstream, "%s is not bound to any keys\n", - name); - else - { - register int j; - - fprintf (rl_outstream, "%s can be found on ", name); - - for (j = 0; invokers[j] && j < 5; j++) - { - fprintf (rl_outstream, "\"%s\"%s", invokers[j], - invokers[j + 1] ? ", " : ".\n"); - } - - if (j == 5 && invokers[j]) - fprintf (rl_outstream, "...\n"); - - for (j = 0; invokers[j]; j++) - free (invokers[j]); - - free (invokers); - } - } - } -} - - -/* **************************************************************** */ -/* */ -/* String Utility Functions */ -/* */ -/* **************************************************************** */ - -static char *strindex (); - -/* Return pointer to first occurance in STRING1 of any character from STRING2, - or NULL if no occurance found. */ -static char * -strpbrk (string1, string2) - char *string1, *string2; -{ - register char *scan; - - for (; *string1 != '\0'; string1++) - { - for (scan = string2; *scan != '\0'; scan++) - { - if (*string1 == *scan) - { - return (string1); - } - } - } - return (NULL); -} - -/* Return non-zero if any members of ARRAY are a substring in STRING. */ -static int -substring_member_of_array (string, array) - char *string, **array; -{ - while (*array) - { - if (strindex (string, *array)) - return (1); - array++; - } - return (0); -} - -/* Whoops, Unix doesn't have strnicmp. */ - -/* Compare at most COUNT characters from string1 to string2. Case - doesn't matter. */ -static int -strnicmp (string1, string2, count) - char *string1, *string2; -{ - register char ch1, ch2; - - while (count) - { - ch1 = *string1++; - ch2 = *string2++; - if (to_upper(ch1) == to_upper(ch2)) - count--; - else break; - } - return (count); -} - -/* strcmp (), but caseless. */ -static int -stricmp (string1, string2) - char *string1, *string2; -{ - register char ch1, ch2; - - while (*string1 && *string2) - { - ch1 = *string1++; - ch2 = *string2++; - if (to_upper(ch1) != to_upper(ch2)) - return (1); - } - return (*string1 | *string2); -} - -/* Determine if s2 occurs in s1. If so, return a pointer to the - match in s1. The compare is case insensitive. */ -static char * -strindex (s1, s2) - register char *s1, *s2; -{ - register int i, l = strlen (s2); - register int len = strlen (s1); - - for (i = 0; (len - i) >= l; i++) - if (strnicmp (&s1[i], s2, l) == 0) - return (s1 + i); - return ((char *)NULL); -} - - -/* **************************************************************** */ -/* */ -/* USG (System V) Support */ -/* */ -/* **************************************************************** */ - -/* When compiling and running in the `Posix' environment, Ultrix does - not restart system calls, so this needs to do it. */ -int -rl_getc (stream) - FILE *stream; -{ - int result; - unsigned char c; - -#ifdef __GO32__ - if (isatty(0)) - return getkey(); -#endif /* __GO32__ */ - - while (1) - { - result = read (fileno (stream), &c, sizeof (char)); - - if (result == sizeof (char)) - return (c); - - /* If zero characters are returned, then the file that we are - reading from is empty! Return EOF in that case. */ - if (result == 0) - return (EOF); +#endif /* _POSIX_VERSION && EAGAIN && O_NONBLOCK */ #ifndef __GO32__ /* If the error that we received was SIGINT, then try again, diff --git a/readline/rldefs.h b/readline/rldefs.h new file mode 100644 index 0000000000..83411bc400 --- /dev/null +++ b/readline/rldefs.h @@ -0,0 +1,250 @@ +/* rldefs.h -- an attempt to isolate some of the system-specific defines + for readline. This should be included after any files that define + system-specific constants like _POSIX_VERSION or USG. */ + +/* Copyright (C) 1987,1989 Free Software Foundation, Inc. + + This file contains the Readline Library (the Library), a set of + routines for providing Emacs style line input to programs that ask + for it. + + The Library is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + The Library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + The GNU General Public License is often shipped with GNU software, and + is generally kept in a file called COPYING or LICENSE. If you do not + have a copy of the license, write to the Free Software Foundation, + 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#if !defined (_RLDEFS_H) +#define _RLDEFS_H + +#if defined (__GNUC__) +# undef alloca +# define alloca __builtin_alloca +#else +# if defined (sparc) || defined (HAVE_ALLOCA_H) +# include +# endif +#endif + +#define NEW_TTY_DRIVER +#define HAVE_BSD_SIGNALS +/* #define USE_XON_XOFF */ + +#ifdef __MSDOS__ +#undef NEW_TTY_DRIVER +#undef HAVE_BSD_SIGNALS +#endif + +#if defined (__linux__) +# include +#endif /* __linux__ */ + +/* Some USG machines have BSD signal handling (sigblock, sigsetmask, etc.) */ +#if defined (USG) && !defined (hpux) +# undef HAVE_BSD_SIGNALS +#endif + +/* System V machines use termio. */ +#if !defined (_POSIX_VERSION) +# if defined (USG) || defined (hpux) || defined (Xenix) || defined (sgi) || defined (DGUX) +# undef NEW_TTY_DRIVER +# define TERMIO_TTY_DRIVER +# include +# if !defined (TCOON) +# define TCOON 1 +# endif +# endif /* USG || hpux || Xenix || sgi || DUGX */ +#endif /* !_POSIX_VERSION */ + +/* Posix systems use termios and the Posix signal functions. */ +#if defined (_POSIX_VERSION) +# if !defined (TERMIOS_MISSING) +# undef NEW_TTY_DRIVER +# define TERMIOS_TTY_DRIVER +# include +# endif /* !TERMIOS_MISSING */ +# define HAVE_POSIX_SIGNALS +# if !defined (O_NDELAY) +# define O_NDELAY O_NONBLOCK /* Posix-style non-blocking i/o */ +# endif /* O_NDELAY */ +#endif /* _POSIX_VERSION */ + +/* System V.3 machines have the old 4.1 BSD `reliable' signal interface. */ +#if !defined (HAVE_BSD_SIGNALS) && !defined (HAVE_POSIX_SIGNALS) +# if defined (USGr3) +# if !defined (HAVE_USG_SIGHOLD) +# define HAVE_USG_SIGHOLD +# endif /* !HAVE_USG_SIGHOLD */ +# endif /* USGr3 */ +#endif /* !HAVE_BSD_SIGNALS && !HAVE_POSIX_SIGNALS */ + +/* Other (BSD) machines use sgtty. */ +#if defined (NEW_TTY_DRIVER) +# include +#endif + +/* Define _POSIX_VDISABLE if we are not using the `new' tty driver and + it is not already defined. It is used both to determine if a + special character is disabled and to disable certain special + characters. Posix systems should set to 0, USG systems to -1. */ +#if !defined (NEW_TTY_DRIVER) && !defined (_POSIX_VDISABLE) +# if defined (_POSIX_VERSION) +# define _POSIX_VDISABLE 0 +# else /* !_POSIX_VERSION */ +# define _POSIX_VDISABLE -1 +# endif /* !_POSIX_VERSION */ +#endif /* !NEW_TTY_DRIVER && !_POSIX_VDISABLE */ + +#if 1 +# define D_NAMLEN(d) strlen ((d)->d_name) +#else /* !1 */ + +#if !defined (SHELL) && (defined (_POSIX_VERSION) || defined (USGr3)) +# if !defined (HAVE_DIRENT_H) +# define HAVE_DIRENT_H +# endif /* !HAVE_DIRENT_H */ +#endif /* !SHELL && (_POSIX_VERSION || USGr3) */ + +#if defined (HAVE_DIRENT_H) +# include +# if !defined (direct) +# define direct dirent +# endif /* !direct */ +# define D_NAMLEN(d) strlen ((d)->d_name) +#else /* !HAVE_DIRENT_H */ +# define D_NAMLEN(d) ((d)->d_namlen) +# if defined (USG) +# if defined (Xenix) +# include +# else /* !Xenix (but USG...) */ +# include "ndir.h" +# endif /* !Xenix */ +# else /* !USG */ +# include +# endif /* !USG */ +#endif /* !HAVE_DIRENT_H */ +#endif /* !1 */ + +#if defined (USG) && defined (TIOCGWINSZ) && !defined (Linux) +# include +# if defined (HAVE_SYS_PTEM_H) +# include +# endif /* HAVE_SYS_PTEM_H */ +# if defined (HAVE_SYS_PTE_H) +# include +# endif /* HAVE_SYS_PTE_H */ +#endif /* USG && TIOCGWINSZ && !Linux */ + +/* Posix macro to check file in statbuf for directory-ness. + This requires that be included before this test. */ +#if defined (S_IFDIR) && !defined (S_ISDIR) +#define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR) +#endif + +/* Decide which flavor of the header file describing the C library + string functions to include and include it. */ + +#if defined (USG) || defined (NeXT) +# if !defined (HAVE_STRING_H) +# define HAVE_STRING_H +# endif /* !HAVE_STRING_H */ +#endif /* USG || NeXT */ + +#if defined (HAVE_STRING_H) +# include +#else /* !HAVE_STRING_H */ +# include +#endif /* !HAVE_STRING_H */ + +#if !defined (strchr) && !defined (__STDC__) +extern char *strchr (), *strrchr (); +#endif /* !strchr && !__STDC__ */ + +#if defined (HAVE_VARARGS_H) +# include +#endif /* HAVE_VARARGS_H */ + +/* This definition is needed by readline.c, rltty.c, and signals.c. */ +/* If on, then readline handles signals in a way that doesn't screw. */ +#define HANDLE_SIGNALS + +#if !defined (emacs_mode) +# define no_mode -1 +# define vi_mode 0 +# define emacs_mode 1 +#endif + +/* Define some macros for dealing with assorted signalling disciplines. + + These macros provide a way to use signal blocking and disabling + without smothering your code in a pile of #ifdef's. + + SIGNALS_UNBLOCK; Stop blocking all signals. + + { + SIGNALS_DECLARE_SAVED (name); Declare a variable to save the + signal blocking state. + ... + SIGNALS_BLOCK (SIGSTOP, name); Block a signal, and save the previous + state for restoration later. + ... + SIGNALS_RESTORE (name); Restore previous signals. + } + +*/ + +#ifdef HAVE_POSIX_SIGNALS + /* POSIX signals */ + +#define SIGNALS_UNBLOCK \ + do { sigset_t set; \ + sigemptyset (&set); \ + sigprocmask (SIG_SETMASK, &set, (sigset_t *)NULL); \ + } while (0) + +#define SIGNALS_DECLARE_SAVED(name) sigset_t name + +#define SIGNALS_BLOCK(SIG, saved) \ + do { sigset_t set; \ + sigemptyset (&set); \ + sigaddset (&set, SIG); \ + sigprocmask (SIG_BLOCK, &set, &saved); \ + } while (0) + +#define SIGNALS_RESTORE(saved) \ + sigprocmask (SIG_SETMASK, &saved, (sigset_t *)NULL) + + +#else /* HAVE_POSIX_SIGNALS */ +#ifdef HAVE_BSD_SIGNALS + /* BSD signals */ + +#define SIGNALS_UNBLOCK sigsetmask (0) +#define SIGNALS_DECLARE_SAVED(name) int name +#define SIGNALS_BLOCK(SIG, saved) saved = sigblock (sigmask (SIG)) +#define SIGNALS_RESTORE(saved) sigsetmask (saved) + + +#else /* HAVE_BSD_SIGNALS */ + /* None of the Above */ + +#define SIGNALS_UNBLOCK /* nothing */ +#define SIGNALS_DECLARE_SAVED(name) /* nothing */ +#define SIGNALS_BLOCK(SIG, saved) /* nothing */ +#define SIGNALS_RESTORE(saved) /* nothing */ + + +#endif /* HAVE_BSD_SIGNALS */ +#endif /* HAVE_POSIX_SIGNALS */ + +/* End of signal handling definitions. */ +#endif /* !_RLDEFS_H */ diff --git a/readline/search.c b/readline/search.c new file mode 100644 index 0000000000..ea98c6f897 --- /dev/null +++ b/readline/search.c @@ -0,0 +1,271 @@ +/* search.c - code for non-incremental searching in emacs and vi modes. */ + +/* Copyright (C) 1992 Free Software Foundation, Inc. + + This file is part of the Readline Library (the Library), a set of + routines for providing Emacs style line input to programs that ask + for it. + + The Library is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + The Library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + The GNU General Public License is often shipped with GNU software, and + is generally kept in a file called COPYING or LICENSE. If you do not + have a copy of the license, write to the Free Software Foundation, + 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include + +#if defined (__GNUC__) +# define alloca __builtin_alloca +#else +# if defined (sparc) || defined (HAVE_ALLOCA_H) +# include +# endif +#endif + +#include "readline.h" +#include "history.h" + +extern char *xmalloc (), *xrealloc (); + +/* Variables imported from readline.c */ +extern int rl_point, rl_end, rl_line_buffer_len; +extern Keymap _rl_keymap; +extern char *rl_prompt; +extern char *rl_line_buffer; +extern HIST_ENTRY *saved_line_for_history; + +static char *noninc_search_string = (char *) NULL; +static int noninc_history_pos = 0; + +/* Search the history list for STRING starting at absolute history position + POS. If STRING begins with `^', the search must match STRING at the + beginning of a history line, otherwise a full substring match is performed + for STRING. DIR < 0 means to search backwards through the history list, + DIR >= 0 means to search forward. */ +static int +noninc_search_from_pos (string, pos, dir) + char *string; + int pos, dir; +{ + int ret, old; + + old = where_history (); + history_set_pos (pos); + + if (*string == '^') + ret = history_search_prefix (string + 1, dir); + else + ret = history_search (string, dir); + + if (ret != -1) + ret = where_history (); + + history_set_pos (old); + return (ret); +} + +/* Search for a line in the history containing STRING. If DIR is < 0, the + search is backwards through previous entries, else through subsequent + entries. */ +static void +noninc_dosearch (string, dir) + char *string; + int dir; +{ + int oldpos, pos; + HIST_ENTRY *entry; + + if (string == 0 || *string == 0 || noninc_history_pos < 0) + { + ding (); + return; + } + + pos = noninc_search_from_pos (string, noninc_history_pos + dir, dir); + if (pos == -1) + { + /* Search failed, current history position unchanged. */ + maybe_unsave_line (); + rl_clear_message (); + rl_point = 0; + ding (); + return; + } + + noninc_history_pos = pos; + + oldpos = where_history (); + history_set_pos (noninc_history_pos); + entry = current_history (); + history_set_pos (oldpos); + + { + int line_len; + + line_len = strlen (entry->line); + if (line_len >= rl_line_buffer_len) + rl_extend_line_buffer (line_len); + strcpy (rl_line_buffer, entry->line); + } + + rl_undo_list = (UNDO_LIST *)entry->data; + rl_end = strlen (rl_line_buffer); + rl_point = 0; + rl_clear_message (); + + if (saved_line_for_history) + free_history_entry (saved_line_for_history); + saved_line_for_history = (HIST_ENTRY *)NULL; +} + +/* Search non-interactively through the history list. DIR < 0 means to + search backwards through the history of previous commands; otherwise + the search is for commands subsequent to the current position in the + history list. PCHAR is the character to use for prompting when reading + the search string; if not specified (0), it defaults to `:'. */ +static void +noninc_search (dir, pchar) + int dir; + int pchar; +{ + int saved_point, c, pmtlen; + char *p; + + maybe_save_line (); + saved_point = rl_point; + + /* Use the line buffer to read the search string. */ + rl_line_buffer[0] = 0; + rl_end = rl_point = 0; + + pmtlen = (rl_prompt && *rl_prompt) ? strlen (rl_prompt) : 0; + p = (char *)alloca (2 + pmtlen); + if (pmtlen) + strcpy (p, rl_prompt); + p[pmtlen] = pchar ? pchar : ':'; + p[pmtlen + 1] = '\0'; + + rl_message (p, 0, 0); + + /* Read the search string. */ + while (c = rl_read_key ()) + { + switch (c) + { + case CTRL('H'): + case RUBOUT: + if (rl_point == 0) + { + maybe_unsave_line (); + rl_clear_message (); + rl_point = saved_point; + return; + } + /* FALLTHROUGH */ + + case CTRL('W'): + case CTRL('U'): + rl_dispatch (c, _rl_keymap); + break; + + case RETURN: + case NEWLINE: + goto dosearch; + /* NOTREACHED */ + break; + + case CTRL('C'): + case CTRL('G'): + maybe_unsave_line (); + rl_clear_message (); + rl_point = saved_point; + ding (); + return; + + default: + rl_insert (1, c); + break; + } + rl_redisplay (); + } + + dosearch: + /* If rl_point == 0, we want to re-use the previous search string and + start from the saved history position. If there's no previous search + string, punt. */ + if (rl_point == 0) + { + if (!noninc_search_string) + { + ding (); + return; + } + } + else + { + /* We want to start the search from the current history position. */ + noninc_history_pos = where_history (); + if (noninc_search_string) + free (noninc_search_string); + noninc_search_string = savestring (rl_line_buffer); + } + + noninc_dosearch (noninc_search_string, dir); +} + +/* Search forward through the history list for a string. If the vi-mode + code calls this, KEY will be `?'. */ +rl_noninc_forward_search (count, key) + int count, key; +{ + if (key == '?') + noninc_search (1, '?'); + else + noninc_search (1, 0); +} + +/* Reverse search the history list for a string. If the vi-mode code + calls this, KEY will be `/'. */ +rl_noninc_reverse_search (count, key) + int count, key; +{ + if (key == '/') + noninc_search (-1, '/'); + else + noninc_search (-1, 0); +} + +/* Search forward through the history list for the last string searched + for. If there is no saved search string, abort. */ +rl_noninc_forward_search_again (count, key) + int count, key; +{ + if (!noninc_search_string) + { + ding (); + return (-1); + } + noninc_dosearch (noninc_search_string, 1); +} + +/* Reverse search in the history list for the last string searched + for. If there is no saved search string, abort. */ +rl_noninc_reverse_search_again (count, key) + int count, key; +{ + if (!noninc_search_string) + { + ding (); + return (-1); + } + noninc_dosearch (noninc_search_string, -1); +} diff --git a/readline/tilde.c b/readline/tilde.c new file mode 100644 index 0000000000..22890f4e6f --- /dev/null +++ b/readline/tilde.c @@ -0,0 +1,396 @@ +/* tilde.c -- Tilde expansion code (~/foo := $HOME/foo). */ + +/* Copyright (C) 1988,1989 Free Software Foundation, Inc. + + This file is part of GNU 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 1, 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; see the file COPYING. If not, write to the Free + Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#if defined (__GNUC__) +# undef alloca +# define alloca __builtin_alloca +#else /* !__GNUC__ */ +# if defined (_AIX) + #pragma alloca +# else /* !_AIX */ +# if defined (HAVE_ALLOCA_H) +# include +# endif /* HAVE_ALLOCA_H */ +# endif /* !AIX */ +#endif /* !__GNUC__ */ + +#if defined (HAVE_STRING_H) +# include +#else /* !HAVE_STRING_H */ +# include +#endif /* !HAVE_STRING_H */ + +#if defined (HAVE_STDLIB_H) +# include +#else +# include "ansi_stdlib.h" +#endif /* HAVE_STDLIB_H */ + +#include +#include + +#if !defined (sgi) && !defined (isc386) +extern struct passwd *getpwnam (), *getpwuid (); +#endif /* !sgi */ + +#if !defined (savestring) +extern char *xmalloc (); +# ifndef strcpy +extern char *strcpy (); +# endif +#define savestring(x) strcpy (xmalloc (1 + strlen (x)), (x)) +#endif /* !savestring */ + +#if !defined (NULL) +# if defined (__STDC__) +# define NULL ((void *) 0) +# else +# define NULL 0x0 +# endif /* !__STDC__ */ +#endif /* !NULL */ + +#if defined (TEST) || defined (STATIC_MALLOC) +static char *xmalloc (), *xrealloc (); +#else +extern char *xmalloc (), *xrealloc (); +#endif /* TEST || STATIC_MALLOC */ + +/* The default value of tilde_additional_prefixes. This is set to + whitespace preceding a tilde so that simple programs which do not + perform any word separation get desired behaviour. */ +static char *default_prefixes[] = + { " ~", "\t~", (char *)NULL }; + +/* The default value of tilde_additional_suffixes. This is set to + whitespace or newline so that simple programs which do not + perform any word separation get desired behaviour. */ +static char *default_suffixes[] = + { " ", "\n", (char *)NULL }; + +/* If non-null, this contains the address of a function to call if the + standard meaning for expanding a tilde fails. The function is called + with the text (sans tilde, as in "foo"), and returns a malloc()'ed string + which is the expansion, or a NULL pointer if there is no expansion. */ +Function *tilde_expansion_failure_hook = (Function *)NULL; + +/* When non-null, this is a NULL terminated array of strings which + are duplicates for a tilde prefix. Bash uses this to expand + `=~' and `:~'. */ +char **tilde_additional_prefixes = default_prefixes; + +/* When non-null, this is a NULL terminated array of strings which match + the end of a username, instead of just "/". Bash sets this to + `:' and `=~'. */ +char **tilde_additional_suffixes = default_suffixes; + +/* Find the start of a tilde expansion in STRING, and return the index of + the tilde which starts the expansion. Place the length of the text + which identified this tilde starter in LEN, excluding the tilde itself. */ +static int +tilde_find_prefix (string, len) + char *string; + int *len; +{ + register int i, j, string_len; + register char **prefixes = tilde_additional_prefixes; + + string_len = strlen (string); + *len = 0; + + if (!*string || *string == '~') + return (0); + + if (prefixes) + { + for (i = 0; i < string_len; i++) + { + for (j = 0; prefixes[j]; j++) + { + if (strncmp (string + i, prefixes[j], strlen (prefixes[j])) == 0) + { + *len = strlen (prefixes[j]) - 1; + return (i + *len); + } + } + } + } + return (string_len); +} + +/* Find the end of a tilde expansion in STRING, and return the index of + the character which ends the tilde definition. */ +static int +tilde_find_suffix (string) + char *string; +{ + register int i, j, string_len; + register char **suffixes = tilde_additional_suffixes; + + string_len = strlen (string); + + for (i = 0; i < string_len; i++) + { + if (string[i] == '/' || !string[i]) + break; + + for (j = 0; suffixes && suffixes[j]; j++) + { + if (strncmp (string + i, suffixes[j], strlen (suffixes[j])) == 0) + return (i); + } + } + return (i); +} + +/* Return a new string which is the result of tilde expanding STRING. */ +char * +tilde_expand (string) + char *string; +{ + char *result, *tilde_expand_word (); + int result_size, result_index; + + result_size = result_index = 0; + result = (char *)NULL; + + /* Scan through STRING expanding tildes as we come to them. */ + while (1) + { + register int start, end; + char *tilde_word, *expansion; + int len; + + /* Make START point to the tilde which starts the expansion. */ + start = tilde_find_prefix (string, &len); + + /* Copy the skipped text into the result. */ + if ((result_index + start + 1) > result_size) + result = (char *)xrealloc (result, 1 + (result_size += (start + 20))); + + strncpy (result + result_index, string, start); + result_index += start; + + /* Advance STRING to the starting tilde. */ + string += start; + + /* Make END be the index of one after the last character of the + username. */ + end = tilde_find_suffix (string); + + /* If both START and END are zero, we are all done. */ + if (!start && !end) + break; + + /* Expand the entire tilde word, and copy it into RESULT. */ + tilde_word = (char *)xmalloc (1 + end); + strncpy (tilde_word, string, end); + tilde_word[end] = '\0'; + string += end; + + expansion = tilde_expand_word (tilde_word); + free (tilde_word); + + len = strlen (expansion); + if ((result_index + len + 1) > result_size) + result = (char *)xrealloc (result, 1 + (result_size += (len + 20))); + + strcpy (result + result_index, expansion); + result_index += len; + free (expansion); + } + + result[result_index] = '\0'; + + return (result); +} + +/* Do the work of tilde expansion on FILENAME. FILENAME starts with a + tilde. If there is no expansion, call tilde_expansion_failure_hook. */ +char * +tilde_expand_word (filename) + char *filename; +{ + char *dirname; + + dirname = filename ? savestring (filename) : (char *)NULL; + + if (dirname && *dirname == '~') + { + char *temp_name; + if (!dirname[1] || dirname[1] == '/') + { + /* Prepend $HOME to the rest of the string. */ + char *temp_home = (char *)getenv ("HOME"); + + /* If there is no HOME variable, look up the directory in + the password database. */ + if (!temp_home) + { + struct passwd *entry; + + entry = getpwuid (getuid ()); + if (entry) + temp_home = entry->pw_dir; + } + + temp_name = (char *)alloca (1 + strlen (&dirname[1]) + + (temp_home ? strlen (temp_home) : 0)); + temp_name[0] = '\0'; + if (temp_home) + strcpy (temp_name, temp_home); + strcat (temp_name, &dirname[1]); + free (dirname); + dirname = savestring (temp_name); + } + else + { + struct passwd *user_entry; + char *username = (char *)alloca (257); + int i, c; + + for (i = 1; c = dirname[i]; i++) + { + if (c == '/') + break; + else + username[i - 1] = c; + } + username[i - 1] = '\0'; + + if (!(user_entry = getpwnam (username))) + { + /* If the calling program has a special syntax for + expanding tildes, and we couldn't find a standard + expansion, then let them try. */ + if (tilde_expansion_failure_hook) + { + char *expansion; + + expansion = + (char *)(*tilde_expansion_failure_hook) (username); + + if (expansion) + { + temp_name = (char *)alloca (1 + strlen (expansion) + + strlen (&dirname[i])); + strcpy (temp_name, expansion); + strcat (temp_name, &dirname[i]); + free (expansion); + goto return_name; + } + } + /* We shouldn't report errors. */ + } + else + { + temp_name = (char *)alloca (1 + strlen (user_entry->pw_dir) + + strlen (&dirname[i])); + strcpy (temp_name, user_entry->pw_dir); + strcat (temp_name, &dirname[i]); + return_name: + free (dirname); + dirname = savestring (temp_name); + } + endpwent (); + } + } + return (dirname); +} + + +#if defined (TEST) +#undef NULL +#include + +main (argc, argv) + int argc; + char **argv; +{ + char *result, line[512]; + int done = 0; + + while (!done) + { + printf ("~expand: "); + fflush (stdout); + + if (!gets (line)) + strcpy (line, "done"); + + if ((strcmp (line, "done") == 0) || + (strcmp (line, "quit") == 0) || + (strcmp (line, "exit") == 0)) + { + done = 1; + break; + } + + result = tilde_expand (line); + printf (" --> %s\n", result); + free (result); + } + exit (0); +} + +static void memory_error_and_abort (); + +static char * +xmalloc (bytes) + int bytes; +{ + char *temp = (char *)malloc (bytes); + + if (!temp) + memory_error_and_abort (); + return (temp); +} + +static char * +xrealloc (pointer, bytes) + char *pointer; + int bytes; +{ + char *temp; + + if (!pointer) + temp = (char *)malloc (bytes); + else + temp = (char *)realloc (pointer, bytes); + + if (!temp) + memory_error_and_abort (); + + return (temp); +} + +static void +memory_error_and_abort () +{ + fprintf (stderr, "readline: Out of virtual memory!\n"); + abort (); +} + +/* + * Local variables: + * compile-command: "gcc -g -DTEST -o tilde tilde.c" + * end: + */ +#endif /* TEST */ diff --git a/readline/tilde.h b/readline/tilde.h new file mode 100644 index 0000000000..4f808fee65 --- /dev/null +++ b/readline/tilde.h @@ -0,0 +1,33 @@ +/* tilde.h: Externally available variables and function in libtilde.a. */ + +/* Function pointers can be declared as (Function *)foo. */ +#if !defined (__FUNCTION_DEF) +# define __FUNCTION_DEF +typedef int Function (); +typedef void VFunction (); +typedef char *CPFunction (); +typedef char **CPPFunction (); +#endif /* _FUNCTION_DEF */ + +/* If non-null, this contains the address of a function to call if the + standard meaning for expanding a tilde fails. The function is called + with the text (sans tilde, as in "foo"), and returns a malloc()'ed string + which is the expansion, or a NULL pointer if there is no expansion. */ +extern Function *tilde_expansion_failure_hook; + +/* When non-null, this is a NULL terminated array of strings which + are duplicates for a tilde prefix. Bash uses this to expand + `=~' and `:~'. */ +extern char **tilde_additional_prefixes; + +/* When non-null, this is a NULL terminated array of strings which match + the end of a username, instead of just "/". Bash sets this to + `:' and `=~'. */ +extern char **tilde_additional_suffixes; + +/* Return a new string which is the result of tilde expanding STRING. */ +extern char *tilde_expand (); + +/* Do the work of tilde expansion on FILENAME. FILENAME starts with a + tilde. If there is no expansion, call tilde_expansion_failure_hook. */ +extern char *tilde_expand_word (); diff --git a/readline/xmalloc.c b/readline/xmalloc.c new file mode 100644 index 0000000000..0ed49ddad6 --- /dev/null +++ b/readline/xmalloc.c @@ -0,0 +1,76 @@ +/* xmalloc.c -- safe versions of malloc and realloc */ + +/* Copyright (C) 1991 Free Software Foundation, Inc. + + This file is part of GNU 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 1, 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; see the file COPYING. If not, write to the Free + Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#if defined (ALREADY_HAVE_XMALLOC) +#else +#include + +#if defined (HAVE_STDLIB_H) +# include +#endif /* HAVE_STDLIB_H */ + +static void memory_error_and_abort (); + +/* **************************************************************** */ +/* */ +/* Memory Allocation and Deallocation. */ +/* */ +/* **************************************************************** */ + +/* Return a pointer to free()able block of memory large enough + to hold BYTES number of bytes. If the memory cannot be allocated, + print an error message and abort. */ +char * +xmalloc (bytes) + int bytes; +{ + char *temp = (char *)malloc (bytes); + + if (!temp) + memory_error_and_abort ("xmalloc"); + return (temp); +} + +char * +xrealloc (pointer, bytes) + char *pointer; + int bytes; +{ + char *temp; + + if (!pointer) + temp = (char *)malloc (bytes); + else + temp = (char *)realloc (pointer, bytes); + + if (!temp) + memory_error_and_abort ("xrealloc"); + return (temp); +} + +static void +memory_error_and_abort (fname) + char *fname; +{ + fprintf (stderr, "%s: Out of virtual memory!\n", fname); + abort (); +} +#endif /* !ALREADY_HAVE_XMALLOC */ -- 2.34.1