/* readline.c -- a general facility for reading lines of input with emacs style editing and completion. */ /* Copyright (C) 1987, 1989, 1992 Free Software Foundation, Inc. This file is part of the GNU Readline Library, a library for reading lines of text with interactive input and history editing. The GNU Readline Library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. The GNU Readline Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. The GNU General Public License is often shipped with GNU software, and is generally kept in a file called COPYING or LICENSE. If you do not have a copy of the license, write to the Free Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY #if defined (HAVE_CONFIG_H) # include <config.h> #endif #include <sys/types.h> #include "posixstat.h" #include <fcntl.h> #if defined (HAVE_SYS_FILE_H) # include <sys/file.h> #endif /* HAVE_SYS_FILE_H */ #if defined (HAVE_UNISTD_H) # include <unistd.h> #endif /* HAVE_UNISTD_H */ #if defined (HAVE_STDLIB_H) # include <stdlib.h> #else # include "ansi_stdlib.h" #endif /* HAVE_STDLIB_H */ #if defined (HAVE_LOCALE_H) # include <locale.h> #endif #include <stdio.h> #include "posixjmp.h" /* System-specific feature definitions and include files. */ #include "rldefs.h" #if defined (__EMX__) # define INCL_DOSPROCESS # include <os2.h> #endif /* __EMX__ */ /* Some standard library routines. */ #include "readline.h" #include "history.h" #include "rlprivate.h" #include "rlshell.h" #include "xmalloc.h" #ifndef RL_LIBRARY_VERSION # define RL_LIBRARY_VERSION "4.1" #endif /* Evaluates its arguments multiple times. */ #define SWAP(s, e) do { int t; t = s; s = e; e = t; } while (0) /* Forward declarations used in this file. */ void _rl_free_history_entry __P((HIST_ENTRY *)); static char *readline_internal __P((void)); static void readline_initialize_everything __P((void)); static void start_using_history __P((void)); static void bind_arrow_keys __P((void)); static int rl_change_case __P((int, int)); static void readline_default_bindings __P((void)); /* **************************************************************** */ /* */ /* Line editing input utility */ /* */ /* **************************************************************** */ char *rl_library_version = RL_LIBRARY_VERSION; int rl_gnu_readline_p = 1; /* A pointer to the keymap that is currently in use. By default, it is the standard emacs keymap. */ Keymap _rl_keymap = emacs_standard_keymap; /* The current style of editing. */ int rl_editing_mode = emacs_mode; /* Non-zero if we called this function from _rl_dispatch(). It's present so functions can find out whether they were called from a key binding or directly from an application. */ int rl_dispatching; /* Non-zero if the previous command was a kill command. */ int _rl_last_command_was_kill = 0; /* The current value of the numeric argument specified by the user. */ int rl_numeric_arg = 1; /* Non-zero if an argument was typed. */ int rl_explicit_arg = 0; /* Temporary value used while generating the argument. */ int rl_arg_sign = 1; /* Non-zero means we have been called at least once before. */ static int rl_initialized; /* If non-zero, this program is running in an EMACS buffer. */ static int running_in_emacs; /* The current offset in the current input line. */ int rl_point; /* Mark in the current input line. */ int rl_mark; /* Length of the current input line. */ int rl_end; /* Make this non-zero to return the current input_line. */ int rl_done; /* The last function executed by readline. */ Function *rl_last_func = (Function *)NULL; /* Top level environment for readline_internal (). */ procenv_t readline_top_level; /* The streams we interact with. */ FILE *_rl_in_stream, *_rl_out_stream; /* The names of the streams that we do input and output to. */ FILE *rl_instream = (FILE *)NULL; FILE *rl_outstream = (FILE *)NULL; /* Non-zero means echo characters as they are read. */ int readline_echoing_p = 1; /* Current prompt. */ char *rl_prompt; int rl_visible_prompt_length = 0; /* Set to non-zero by calling application if it has already printed rl_prompt and does not want readline to do it the first time. */ int rl_already_prompted = 0; /* The number of characters read in order to type this complete command. */ int rl_key_sequence_length = 0; /* If non-zero, then this is the address of a function to call just before readline_internal_setup () prints the first prompt. */ Function *rl_startup_hook = (Function *)NULL; /* If non-zero, this is the address of a function to call just before readline_internal_setup () returns and readline_internal starts reading input characters. */ Function *rl_pre_input_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. */ int _rl_eof_char = CTRL ('D'); /* Non-zero makes this the next keystroke to read. */ int rl_pending_input = 0; /* Pointer to a useful terminal name. */ 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; /* The style of `bell' notification preferred. This can be set to NO_BELL, AUDIBLE_BELL, or VISIBLE_BELL. */ int _rl_bell_preference = AUDIBLE_BELL; /* String inserted into the line by rl_insert_comment (). */ char *_rl_comment_begin; /* Keymap holding the function currently being executed. */ Keymap rl_executing_keymap; /* Non-zero means to erase entire line, including prompt, on empty input lines. */ int rl_erase_empty_line = 0; /* Non-zero means to read only this many characters rather than up to a character bound to accept-line. */ int rl_num_chars_to_read; /* Line buffer and maintenence. */ char *rl_line_buffer = (char *)NULL; int rl_line_buffer_len = 0; /* Forward declarations used by the display and termcap code. */ /* **************************************************************** */ /* */ /* `Forward' declarations */ /* */ /* **************************************************************** */ /* Non-zero means do not parse any lines other than comments and parser directives. */ unsigned char _rl_parsing_conditionalized_out = 0; /* Non-zero means to convert characters with the meta bit set to escape-prefixed characters so we can indirect through emacs_meta_keymap or vi_escape_keymap. */ int _rl_convert_meta_chars_to_ascii = 1; /* Non-zero means to output characters with the meta bit set directly rather than as a meta-prefixed escape sequence. */ int _rl_output_meta_chars = 0; /* **************************************************************** */ /* */ /* Top Level Functions */ /* */ /* **************************************************************** */ /* 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. An empty PROMPT means none. A return value of NULL means that EOF was encountered. */ char * readline (prompt) char *prompt; { char *value; rl_prompt = prompt; /* If we are at EOF return a NULL string. */ if (rl_pending_input == EOF) { rl_pending_input = 0; return ((char *)NULL); } rl_visible_prompt_length = rl_expand_prompt (rl_prompt); rl_initialize (); (*rl_prep_term_function) (_rl_meta_flag); #if defined (HANDLE_SIGNALS) rl_set_signals (); #endif value = readline_internal (); (*rl_deprep_term_function) (); #if defined (HANDLE_SIGNALS) rl_clear_signals (); #endif return (value); } #if defined (READLINE_CALLBACKS) # define STATIC_CALLBACK #else # define STATIC_CALLBACK static #endif STATIC_CALLBACK void readline_internal_setup () { char *nprompt; _rl_in_stream = rl_instream; _rl_out_stream = rl_outstream; if (rl_startup_hook) (*rl_startup_hook) (); if (readline_echoing_p == 0) { if (rl_prompt && rl_already_prompted == 0) { nprompt = _rl_strip_prompt (rl_prompt); fprintf (_rl_out_stream, "%s", nprompt); fflush (_rl_out_stream); free (nprompt); } } else { if (rl_prompt && rl_already_prompted) rl_on_new_line_with_prompt (); else rl_on_new_line (); (*rl_redisplay_function) (); #if defined (VI_MODE) if (rl_editing_mode == vi_mode) rl_vi_insertion_mode (1, 0); #endif /* VI_MODE */ } if (rl_pre_input_hook) (*rl_pre_input_hook) (); } STATIC_CALLBACK char * readline_internal_teardown (eof) int eof; { char *temp; HIST_ENTRY *entry; /* Restore the original of this history line, iff the line that we are editing was originally in the history, AND the line has changed. */ entry = current_history (); if (entry && rl_undo_list) { temp = savestring (the_line); rl_revert_line (1, 0); entry = replace_history_entry (where_history (), the_line, (histdata_t)NULL); _rl_free_history_entry (entry); strcpy (the_line, temp); free (temp); } /* At any rate, it is highly likely that this line has an undo list. Get rid of it now. */ if (rl_undo_list) free_undo_list (); return (eof ? (char *)NULL : savestring (the_line)); } STATIC_CALLBACK int #if defined (READLINE_CALLBACKS) readline_internal_char () #else readline_internal_charloop () #endif { static int lastc, eof_found; int c, code, lk; lastc = -1; eof_found = 0; #if !defined (READLINE_CALLBACKS) while (rl_done == 0) { #endif lk = _rl_last_command_was_kill; code = setjmp (readline_top_level); if (code) (*rl_redisplay_function) (); if (rl_pending_input == 0) { /* Then initialize the argument and number of keys read. */ _rl_init_argument (); rl_key_sequence_length = 0; } c = rl_read_key (); /* EOF typed to a non-blank line is a <NL>. */ if (c == EOF && rl_end) c = NEWLINE; /* The character _rl_eof_char typed to blank line, and not as the previous character is interpreted as EOF. */ if (((c == _rl_eof_char && lastc != c) || c == EOF) && !rl_end) { #if defined (READLINE_CALLBACKS) return (rl_done = 1); #else eof_found = 1; break; #endif } lastc = c; _rl_dispatch ((unsigned char)c, _rl_keymap); /* If there was no change in _rl_last_command_was_kill, then no kill has taken place. Note that if input is pending we are reading a prefix command, so nothing has changed yet. */ if (rl_pending_input == 0 && lk == _rl_last_command_was_kill) _rl_last_command_was_kill = 0; #if defined (VI_MODE) /* In vi mode, when you exit insert mode, the cursor moves back over the previous character. We explicitly check for that here. */ if (rl_editing_mode == vi_mode && _rl_keymap == vi_movement_keymap) rl_vi_check (); #endif /* VI_MODE */ if (rl_num_chars_to_read && rl_end >= rl_num_chars_to_read) { (*rl_redisplay_function) (); rl_newline (1, '\n'); } if (rl_done == 0) (*rl_redisplay_function) (); /* If the application writer has told us to erase the entire line if the only character typed was something bound to rl_newline, do so. */ if (rl_erase_empty_line && rl_done && rl_last_func == rl_newline && rl_point == 0 && rl_end == 0) _rl_erase_entire_line (); #if defined (READLINE_CALLBACKS) return 0; #else } return (eof_found); #endif } #if defined (READLINE_CALLBACKS) static int readline_internal_charloop () { int eof = 1; while (rl_done == 0) eof = readline_internal_char (); return (eof); } #endif /* READLINE_CALLBACKS */ /* Read a line of input from the global rl_instream, doing output on the global rl_outstream. If rl_prompt is non-null, then that is our prompt. */ static char * readline_internal () { int eof; readline_internal_setup (); eof = readline_internal_charloop (); return (readline_internal_teardown (eof)); } void _rl_init_line_state () { rl_point = rl_end = 0; the_line = rl_line_buffer; the_line[0] = 0; } void _rl_set_the_line () { the_line = rl_line_buffer; } /* Do the command associated with KEY in MAP. If the associated command is really a keymap, then read another key, and dispatch into that map. */ int _rl_dispatch (key, map) register int key; Keymap map; { int r, newkey; char *macro; Function *func; if (META_CHAR (key) && _rl_convert_meta_chars_to_ascii) { if (map[ESC].type == ISKMAP) { if (_rl_defining_kbd_macro) _rl_add_macro_char (ESC); map = FUNCTION_TO_KEYMAP (map, ESC); key = UNMETA (key); rl_key_sequence_length += 2; return (_rl_dispatch (key, map)); } else ding (); return 0; } if (_rl_defining_kbd_macro) _rl_add_macro_char (key); r = 0; switch (map[key].type) { case ISFUNC: func = map[key].function; if (func != (Function *)NULL) { /* Special case rl_do_lowercase_version (). */ if (func == rl_do_lowercase_version) return (_rl_dispatch (_rl_to_lower (key), map)); rl_executing_keymap = map; #if 0 _rl_suppress_redisplay = (map[key].function == rl_insert) && _rl_input_available (); #endif rl_dispatching = 1; r = (*map[key].function)(rl_numeric_arg * rl_arg_sign, key); rl_dispatching = 0; /* If we have input pending, then the last command was a prefix command. Don't change the state of rl_last_func. Otherwise, remember the last command executed in this variable. */ if (!rl_pending_input && map[key].function != rl_digit_argument) rl_last_func = map[key].function; } else { _rl_abort_internal (); return -1; } break; case ISKMAP: if (map[key].function != (Function *)NULL) { rl_key_sequence_length++; newkey = rl_read_key (); r = _rl_dispatch (newkey, FUNCTION_TO_KEYMAP (map, key)); } else { _rl_abort_internal (); return -1; } break; case ISMACR: if (map[key].function != (Function *)NULL) { macro = savestring ((char *)map[key].function); _rl_with_macro_input (macro); return 0; } break; } #if defined (VI_MODE) if (rl_editing_mode == vi_mode && _rl_keymap == vi_movement_keymap && _rl_vi_textmod_command (key)) _rl_vi_set_last (key, rl_numeric_arg, rl_arg_sign); #endif return (r); } /* **************************************************************** */ /* */ /* Initializations */ /* */ /* **************************************************************** */ /* Initialize readline (and terminal if not already). */ int rl_initialize () { /* If we have never been called before, initialize the terminal and data structures. */ if (!rl_initialized) { readline_initialize_everything (); rl_initialized++; } /* Initalize the current line information. */ _rl_init_line_state (); /* We aren't done yet. We haven't even gotten started yet! */ rl_done = 0; /* Tell the history routines what is going on. */ start_using_history (); /* Make the display buffer match the state of the line. */ rl_reset_line_state (); /* No such function typed yet. */ rl_last_func = (Function *)NULL; /* Parsing of key-bindings begins in an enabled state. */ _rl_parsing_conditionalized_out = 0; #if defined (VI_MODE) if (rl_editing_mode == vi_mode) _rl_vi_initialize_line (); #endif return 0; } #if 0 #if defined (__EMX__) static void _emx_build_environ () { TIB *tibp; PIB *pibp; char *t, **tp; int c; DosGetInfoBlocks (&tibp, &pibp); t = pibp->pib_pchenv; for (c = 1; *t; c++) t += strlen (t) + 1; tp = environ = (char **)xmalloc ((c + 1) * sizeof (char *)); t = pibp->pib_pchenv; while (*t) { *tp++ = t; t += strlen (t) + 1; } *tp = 0; } #endif /* __EMX__ */ #endif /* Initialize the entire state of the world. */ static void readline_initialize_everything () { #if 0 #if defined (__EMX__) if (environ == 0) _emx_build_environ (); #endif #endif /* Find out if we are running in Emacs. */ running_in_emacs = get_env_value ("EMACS") != (char *)0; /* Set up input and output if they are not already set up. */ if (!rl_instream) rl_instream = stdin; if (!rl_outstream) rl_outstream = stdout; /* Bind _rl_in_stream and _rl_out_stream immediately. These values may change, but they may also be used before readline_internal () is called. */ _rl_in_stream = rl_instream; _rl_out_stream = rl_outstream; /* Allocate data structures. */ if (rl_line_buffer == 0) rl_line_buffer = xmalloc (rl_line_buffer_len = DEFAULT_BUFFER_SIZE); /* Initialize the terminal interface. */ _rl_init_terminal_io ((char *)NULL); /* Bind tty characters to readline functions. */ readline_default_bindings (); /* Initialize the function names. */ rl_initialize_funmap (); /* Decide whether we should automatically go into eight-bit mode. */ _rl_init_eightbit (); /* Read in the init file. */ rl_read_init_file ((char *)NULL); /* XXX */ if (_rl_horizontal_scroll_mode && _rl_term_autowrap) { screenwidth--; screenchars -= screenheight; } /* Override the effect of any `set keymap' assignments in the inputrc file. */ rl_set_keymap_from_edit_mode (); /* Try to bind a common arrow key prefix, if not already bound. */ bind_arrow_keys (); /* Enable the meta key, if this terminal has one. */ if (_rl_enable_meta) _rl_enable_meta_key (); /* If the completion parser's default word break characters haven't been set yet, then do so now. */ if (rl_completer_word_break_characters == (char *)NULL) rl_completer_word_break_characters = rl_basic_word_break_characters; } /* 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 () { rltty_set_default_bindings (_rl_keymap); } static void bind_arrow_keys_internal () { Function *f; #if defined (__MSDOS__) f = rl_function_of_keyseq ("\033[0A", _rl_keymap, (int *)NULL); if (!f || f == rl_do_lowercase_version) { _rl_bind_if_unbound ("\033[0A", rl_get_previous_history); _rl_bind_if_unbound ("\033[0B", rl_backward); _rl_bind_if_unbound ("\033[0C", rl_forward); _rl_bind_if_unbound ("\033[0D", rl_get_next_history); } #endif f = rl_function_of_keyseq ("\033[A", _rl_keymap, (int *)NULL); if (!f || f == rl_do_lowercase_version) { _rl_bind_if_unbound ("\033[A", rl_get_previous_history); _rl_bind_if_unbound ("\033[B", rl_get_next_history); _rl_bind_if_unbound ("\033[C", rl_forward); _rl_bind_if_unbound ("\033[D", rl_backward); } f = rl_function_of_keyseq ("\033OA", _rl_keymap, (int *)NULL); if (!f || f == rl_do_lowercase_version) { _rl_bind_if_unbound ("\033OA", rl_get_previous_history); _rl_bind_if_unbound ("\033OB", rl_get_next_history); _rl_bind_if_unbound ("\033OC", rl_forward); _rl_bind_if_unbound ("\033OD", rl_backward); } } /* Try and bind the common arrow key prefix after giving termcap and the inputrc file a chance to bind them and create `real' keymaps for the arrow key prefix. */ static void bind_arrow_keys () { Keymap xkeymap; xkeymap = _rl_keymap; _rl_keymap = emacs_standard_keymap; bind_arrow_keys_internal (); #if defined (VI_MODE) _rl_keymap = vi_movement_keymap; bind_arrow_keys_internal (); #endif _rl_keymap = xkeymap; } /* **************************************************************** */ /* */ /* Numeric Arguments */ /* */ /* **************************************************************** */ /* Handle C-u style numeric args, as well as M--, and M-digits. */ static int rl_digit_loop () { int key, c, sawminus, sawdigits; rl_save_prompt (); sawminus = sawdigits = 0; while (1) { if (rl_numeric_arg > 1000000) { sawdigits = rl_explicit_arg = rl_numeric_arg = 0; ding (); rl_restore_prompt (); rl_clear_message (); return 1; } rl_message ("(arg: %d) ", rl_arg_sign * rl_numeric_arg); key = c = rl_read_key (); /* If we see a key bound to `universal-argument' after seeing digits, it ends the argument but is otherwise ignored. */ if (_rl_keymap[c].type == ISFUNC && _rl_keymap[c].function == rl_universal_argument) { if (sawdigits == 0) { rl_numeric_arg *= 4; continue; } else { key = rl_read_key (); rl_restore_prompt (); rl_clear_message (); return (_rl_dispatch (key, _rl_keymap)); } } c = UNMETA (c); if (_rl_digit_p (c)) { rl_numeric_arg = rl_explicit_arg ? (rl_numeric_arg * 10) + c - '0' : c - '0'; sawdigits = rl_explicit_arg = 1; } else if (c == '-' && rl_explicit_arg == 0) { rl_numeric_arg = sawminus = 1; rl_arg_sign = -1; } else { /* Make M-- command equivalent to M--1 command. */ if (sawminus && rl_numeric_arg == 1 && rl_explicit_arg == 0) rl_explicit_arg = 1; rl_restore_prompt (); rl_clear_message (); return (_rl_dispatch (key, _rl_keymap)); } } return 0; } /* Add the current digit to the argument in progress. */ int rl_digit_argument (ignore, key) int ignore, key; { rl_pending_input = key; return (rl_digit_loop ()); } /* What to do when you abort reading an argument. */ int rl_discard_argument () { ding (); rl_clear_message (); _rl_init_argument (); return 0; } /* Create a default argument. */ int _rl_init_argument () { rl_numeric_arg = rl_arg_sign = 1; rl_explicit_arg = 0; return 0; } /* C-u, universal argument. Multiply the current argument by 4. Read a key. If the key has nothing to do with arguments, then dispatch on it. If the key is the abort character then abort. */ int rl_universal_argument (count, key) int count, key; { rl_numeric_arg *= 4; return (rl_digit_loop ()); } /* **************************************************************** */ /* */ /* Insert and Delete */ /* */ /* **************************************************************** */ /* Insert a string of text into the line at point. This is the only way that you should do insertion. rl_insert () calls this function. */ int rl_insert_text (string) char *string; { register int i, l = strlen (string); if (rl_end + l >= rl_line_buffer_len) rl_extend_line_buffer (rl_end + l); for (i = rl_end; i >= rl_point; i--) the_line[i + l] = the_line[i]; strncpy (the_line + rl_point, string, l); /* Remember how to undo this if we aren't undoing something. */ if (!_rl_doing_an_undo) { /* If possible and desirable, concatenate the undos. */ if ((l == 1) && rl_undo_list && (rl_undo_list->what == UNDO_INSERT) && (rl_undo_list->end == rl_point) && (rl_undo_list->end - rl_undo_list->start < 20)) rl_undo_list->end++; else rl_add_undo (UNDO_INSERT, rl_point, rl_point + l, (char *)NULL); } rl_point += l; rl_end += l; the_line[rl_end] = '\0'; return l; } /* Delete the string between FROM and TO. FROM is inclusive, TO is not. */ int rl_delete_text (from, to) int from, to; { register char *text; register int diff, i; /* Fix it if the caller is confused. */ if (from > to) SWAP (from, to); /* fix boundaries */ if (to > rl_end) { to = rl_end; if (from > to) from = to; } text = rl_copy_text (from, to); /* Some versions of strncpy() can't handle overlapping arguments. */ diff = to - from; for (i = from; i < rl_end - diff; i++) the_line[i] = the_line[i + diff]; /* Remember how to undo this delete. */ if (_rl_doing_an_undo == 0) rl_add_undo (UNDO_DELETE, from, to, text); else free (text); rl_end -= diff; the_line[rl_end] = '\0'; return (diff); } /* Fix up point so that it is within the line boundaries after killing text. If FIX_MARK_TOO is non-zero, the mark is forced within line boundaries also. */ #define _RL_FIX_POINT(x) \ do { \ if (x > rl_end) \ x = rl_end; \ else if (x < 0) \ x = 0; \ } while (0) void _rl_fix_point (fix_mark_too) int fix_mark_too; { _RL_FIX_POINT (rl_point); if (fix_mark_too) _RL_FIX_POINT (rl_mark); } #undef _RL_FIX_POINT void _rl_replace_text (text, start, end) char *text; int start, end; { rl_begin_undo_group (); rl_delete_text (start, end + 1); rl_point = start; rl_insert_text (text); rl_end_undo_group (); } /* **************************************************************** */ /* */ /* Readline character functions */ /* */ /* **************************************************************** */ /* This is not a gap editor, just a stupid line input routine. No hair is involved in writing any of the functions, and none should be. */ /* Note that: rl_end is the place in the string that we would place '\0'; i.e., it is always safe to place '\0' there. rl_point is the place in the string where the cursor is. Sometimes this is the same as rl_end. Any command that is called interactively receives two arguments. The first is a count: the numeric arg pased to this command. The second is the key which invoked this command. */ /* **************************************************************** */ /* */ /* Movement Commands */ /* */ /* **************************************************************** */ /* Note that if you `optimize' the display for these functions, you cannot use said functions in other functions which do not do optimizing display. I.e., you will have to update the data base for rl_redisplay, and you might as well let rl_redisplay do that job. */ /* Move forward COUNT characters. */ int rl_forward (count, key) int count, key; { if (count < 0) rl_backward (-count, key); else if (count > 0) { int end = rl_point + count; #if defined (VI_MODE) int lend = rl_end - (rl_editing_mode == vi_mode); #else int lend = rl_end; #endif if (end > lend) { rl_point = lend; ding (); } else rl_point = end; } if (rl_end < 0) rl_end = 0; return 0; } /* Move backward COUNT characters. */ int rl_backward (count, key) int count, key; { if (count < 0) rl_forward (-count, key); else if (count > 0) { if (rl_point < count) { rl_point = 0; ding (); } else rl_point -= count; } return 0; } /* Move to the beginning of the line. */ int rl_beg_of_line (count, key) int count, key; { rl_point = 0; return 0; } /* Move to the end of the line. */ int rl_end_of_line (count, key) int count, key; { rl_point = rl_end; return 0; } /* Move forward a word. We do what Emacs does. */ int rl_forward_word (count, key) int count, key; { int c; if (count < 0) { rl_backward_word (-count, key); return 0; } while (count) { if (rl_point == rl_end) return 0; /* If we are not in a word, move forward until we are in one. Then, move forward until we hit a non-alphabetic character. */ c = the_line[rl_point]; if (alphabetic (c) == 0) { while (++rl_point < rl_end) { c = the_line[rl_point]; if (alphabetic (c)) break; } } if (rl_point == rl_end) return 0; while (++rl_point < rl_end) { c = the_line[rl_point]; if (alphabetic (c) == 0) break; } --count; } return 0; } /* Move backward a word. We do what Emacs does. */ int rl_backward_word (count, key) int count, key; { int c; if (count < 0) { rl_forward_word (-count, key); return 0; } while (count) { if (!rl_point) return 0; /* Like rl_forward_word (), except that we look at the characters just before point. */ c = the_line[rl_point - 1]; if (alphabetic (c) == 0) { while (--rl_point) { c = the_line[rl_point - 1]; if (alphabetic (c)) break; } } while (rl_point) { c = the_line[rl_point - 1]; if (alphabetic (c) == 0) break; else --rl_point; } --count; } return 0; } /* Clear the current line. Numeric argument to C-l does this. */ int rl_refresh_line (ignore1, ignore2) int ignore1, ignore2; { int curr_line; curr_line = _rl_current_display_line (); _rl_move_vert (curr_line); _rl_move_cursor_relative (0, the_line); /* XXX is this right */ _rl_clear_to_eol (0); /* arg of 0 means to not use spaces */ rl_forced_update_display (); rl_display_fixed = 1; return 0; } /* C-l typed to a line without quoting clears the screen, and then reprints the prompt and the current input line. Given a numeric arg, redraw only the current line. */ int rl_clear_screen (count, key) int count, key; { if (rl_explicit_arg) { rl_refresh_line (count, key); return 0; } _rl_clear_screen (); /* calls termcap function to clear screen */ rl_forced_update_display (); rl_display_fixed = 1; return 0; } int rl_arrow_keys (count, c) int count, c; { int ch; ch = rl_read_key (); switch (_rl_to_upper (ch)) { case 'A': rl_get_previous_history (count, ch); break; case 'B': rl_get_next_history (count, ch); break; case 'C': rl_forward (count, ch); break; case 'D': rl_backward (count, ch); break; default: ding (); } return 0; } /* **************************************************************** */ /* */ /* Text commands */ /* */ /* **************************************************************** */ /* Insert the character C at the current location, moving point forward. */ int rl_insert (count, c) int count, c; { register int i; char *string; if (count <= 0) return 0; /* If we can optimize, then do it. But don't let people crash readline because of extra large arguments. */ if (count > 1 && count <= 1024) { string = xmalloc (1 + count); for (i = 0; i < count; i++) string[i] = c; string[i] = '\0'; rl_insert_text (string); free (string); return 0; } if (count > 1024) { int decreaser; char str[1024+1]; for (i = 0; i < 1024; i++) str[i] = c; while (count) { decreaser = (count > 1024 ? 1024 : count); str[decreaser] = '\0'; rl_insert_text (str); count -= decreaser; } return 0; } /* We are inserting a single character. If there is pending input, then make a string of all of the pending characters that are bound to rl_insert, and insert them all. */ if (_rl_any_typein ()) _rl_insert_typein (c); else { /* Inserting a single character. */ char str[2]; str[1] = '\0'; str[0] = c; rl_insert_text (str); } return 0; } /* Insert the next typed character verbatim. */ int rl_quoted_insert (count, key) int count, key; { int c; #if defined (HANDLE_SIGNALS) _rl_disable_tty_signals (); #endif c = rl_read_key (); #if defined (HANDLE_SIGNALS) _rl_restore_tty_signals (); #endif return (rl_insert (count, c)); } /* Insert a tab character. */ int rl_tab_insert (count, key) int count, key; { return (rl_insert (count, '\t')); } /* What to do when a NEWLINE is pressed. We accept the whole line. KEY is the key that invoked this command. I guess it could have meaning in the future. */ int rl_newline (count, key) int count, key; { rl_done = 1; #if defined (VI_MODE) if (rl_editing_mode == vi_mode) { _rl_vi_done_inserting (); _rl_vi_reset_last (); } #endif /* VI_MODE */ /* If we've been asked to erase empty lines, suppress the final update, since _rl_update_final calls crlf(). */ if (rl_erase_empty_line && rl_point == 0 && rl_end == 0) return 0; if (readline_echoing_p) _rl_update_final (); return 0; } /* What to do for some uppercase characters, like meta characters, and some characters appearing in emacs_ctlx_keymap. This function is just a stub, you bind keys to it and the code in _rl_dispatch () is special cased. */ int rl_do_lowercase_version (ignore1, ignore2) int ignore1, ignore2; { return 0; } /* Rubout the character behind point. */ int rl_rubout (count, key) int count, key; { if (count < 0) { rl_delete (-count, key); return 0; } if (!rl_point) { ding (); return -1; } if (count > 1 || rl_explicit_arg) { int orig_point = rl_point; rl_backward (count, key); rl_kill_text (orig_point, rl_point); } else { int c = the_line[--rl_point]; rl_delete_text (rl_point, rl_point + 1); if (rl_point == rl_end && isprint (c) && _rl_last_c_pos) { int l; l = rl_character_len (c, rl_point); _rl_erase_at_end_of_line (l); } } return 0; } /* Delete the character under the cursor. Given a numeric argument, kill that many characters instead. */ int rl_delete (count, key) int count, key; { if (count < 0) return (rl_rubout (-count, key)); if (rl_point == rl_end) { ding (); return -1; } if (count > 1 || rl_explicit_arg) { int orig_point = rl_point; rl_forward (count, key); rl_kill_text (orig_point, rl_point); rl_point = orig_point; return 0; } else return (rl_delete_text (rl_point, rl_point + 1)); } /* Delete the character under the cursor, unless the insertion point is at the end of the line, in which case the character behind the cursor is deleted. COUNT is obeyed and may be used to delete forward or backward that many characters. */ int rl_rubout_or_delete (count, key) int count, key; { if (rl_end != 0 && rl_point == rl_end) return (rl_rubout (count, key)); else return (rl_delete (count, key)); } /* Delete all spaces and tabs around point. */ int rl_delete_horizontal_space (count, ignore) int count, ignore; { int start = 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) { rl_delete_text (start, rl_point); rl_point = start; } return 0; } /* Like the tcsh editing function delete-char-or-list. The eof character is caught before this is invoked, so this really does the same thing as delete-char-or-list-or-eof, as long as it's bound to the eof character. */ int rl_delete_or_show_completions (count, key) int count, key; { if (rl_end != 0 && rl_point == rl_end) return (rl_possible_completions (count, key)); else return (rl_delete (count, key)); } #ifndef RL_COMMENT_BEGIN_DEFAULT #define RL_COMMENT_BEGIN_DEFAULT "#" #endif /* Turn the current line into a comment in shell history. A K*rn shell style function. */ int rl_insert_comment (count, key) int count, key; { rl_beg_of_line (1, key); rl_insert_text (_rl_comment_begin ? _rl_comment_begin : RL_COMMENT_BEGIN_DEFAULT); (*rl_redisplay_function) (); rl_newline (1, '\n'); return (0); } /* **************************************************************** */ /* */ /* Changing Case */ /* */ /* **************************************************************** */ /* The three kinds of things that we know how to do. */ #define UpCase 1 #define DownCase 2 #define CapCase 3 /* Uppercase the word at point. */ int rl_upcase_word (count, key) int count, key; { return (rl_change_case (count, UpCase)); } /* Lowercase the word at point. */ int rl_downcase_word (count, key) int count, key; { return (rl_change_case (count, DownCase)); } /* Upcase the first letter, downcase the rest. */ int rl_capitalize_word (count, key) int count, key; { return (rl_change_case (count, CapCase)); } /* The meaty function. Change the case of COUNT words, performing OP on them. OP is one of UpCase, DownCase, or CapCase. If a negative argument is given, leave point where it started, otherwise, leave it where it moves to. */ static int rl_change_case (count, op) int count, op; { register int start, end; int inword, c; start = rl_point; rl_forward_word (count, 0); end = rl_point; if (count < 0) SWAP (start, end); /* We are going to modify some text, so let's prepare to undo it. */ rl_modifying (start, end); for (inword = 0; start < end; start++) { c = the_line[start]; switch (op) { case UpCase: the_line[start] = _rl_to_upper (c); break; case DownCase: the_line[start] = _rl_to_lower (c); break; case CapCase: the_line[start] = (inword == 0) ? _rl_to_upper (c) : _rl_to_lower (c); inword = alphabetic (the_line[start]); break; default: ding (); return -1; } } rl_point = end; return 0; } /* **************************************************************** */ /* */ /* Transposition */ /* */ /* **************************************************************** */ /* Transpose the words at point. */ int rl_transpose_words (count, key) int count, key; { char *word1, *word2; int w1_beg, w1_end, w2_beg, w2_end; int orig_point = rl_point; if (!count) return 0; /* Find the two words. */ rl_forward_word (count, key); w2_end = rl_point; rl_backward_word (1, key); w2_beg = rl_point; rl_backward_word (count, key); w1_beg = rl_point; rl_forward_word (1, key); w1_end = rl_point; /* Do some check to make sure that there really are two words. */ if ((w1_beg == w2_beg) || (w2_beg < w1_end)) { ding (); rl_point = orig_point; return -1; } /* Get the text of the words. */ word1 = rl_copy_text (w1_beg, w1_end); word2 = rl_copy_text (w2_beg, w2_end); /* We are about to do many insertions and deletions. Remember them as one operation. */ rl_begin_undo_group (); /* Do the stuff at word2 first, so that we don't have to worry about word1 moving. */ rl_point = w2_beg; rl_delete_text (w2_beg, w2_end); rl_insert_text (word1); rl_point = w1_beg; rl_delete_text (w1_beg, w1_end); rl_insert_text (word2); /* This is exactly correct since the text before this point has not changed in length. */ rl_point = w2_end; /* I think that does it. */ rl_end_undo_group (); free (word1); free (word2); return 0; } /* Transpose the characters at point. If point is at the end of the line, then transpose the characters before point. */ int rl_transpose_chars (count, key) int count, key; { char dummy[2]; if (!count) return 0; if (!rl_point || rl_end < 2) { ding (); return -1; } rl_begin_undo_group (); if (rl_point == rl_end) { --rl_point; count = 1; } rl_point--; dummy[0] = the_line[rl_point]; dummy[1] = '\0'; rl_delete_text (rl_point, rl_point + 1); rl_point += count; _rl_fix_point (0); rl_insert_text (dummy); rl_end_undo_group (); return 0; } /* **************************************************************** */ /* */ /* Character Searching */ /* */ /* **************************************************************** */ int _rl_char_search_internal (count, dir, schar) int count, dir, schar; { int pos, inc; pos = rl_point; inc = (dir < 0) ? -1 : 1; while (count) { if ((dir < 0 && pos <= 0) || (dir > 0 && pos >= rl_end)) { ding (); return -1; } pos += inc; do { if (rl_line_buffer[pos] == schar) { count--; if (dir < 0) rl_point = (dir == BTO) ? pos + 1 : pos; else rl_point = (dir == FTO) ? pos - 1 : pos; break; } } while ((dir < 0) ? pos-- : ++pos < rl_end); } return (0); } /* Search COUNT times for a character read from the current input stream. FDIR is the direction to search if COUNT is non-negative; otherwise the search goes in BDIR. */ static int _rl_char_search (count, fdir, bdir) int count, fdir, bdir; { int c; c = rl_read_key (); if (count < 0) return (_rl_char_search_internal (-count, bdir, c)); else return (_rl_char_search_internal (count, fdir, c)); } int rl_char_search (count, key) int count, key; { return (_rl_char_search (count, FFIND, BFIND)); } int rl_backward_char_search (count, key) int count, key; { return (_rl_char_search (count, BFIND, FFIND)); } /* **************************************************************** */ /* */ /* History Utilities */ /* */ /* **************************************************************** */ /* We already have a history library, and that is what we use to control the history features of readline. This is our local interface to the history mechanism. */ /* While we are editing the history, this is the saved version of the original line. */ HIST_ENTRY *saved_line_for_history = (HIST_ENTRY *)NULL; /* Set the history pointer back to the last entry in the history. */ static void start_using_history () { using_history (); if (saved_line_for_history) _rl_free_history_entry (saved_line_for_history); saved_line_for_history = (HIST_ENTRY *)NULL; } /* Free the contents (and containing structure) of a HIST_ENTRY. */ void _rl_free_history_entry (entry) HIST_ENTRY *entry; { if (entry == 0) return; if (entry->line) free (entry->line); free (entry); } /* Perhaps put back the current line if it has changed. */ int maybe_replace_line () { HIST_ENTRY *temp; temp = current_history (); /* If the current line has changed, save the changes. */ if (temp && ((UNDO_LIST *)(temp->data) != rl_undo_list)) { temp = replace_history_entry (where_history (), the_line, (histdata_t)rl_undo_list); free (temp->line); free (temp); } return 0; } /* Put back the saved_line_for_history if there is one. */ int maybe_unsave_line () { int line_len; if (saved_line_for_history) { line_len = strlen (saved_line_for_history->line); if (line_len >= rl_line_buffer_len) rl_extend_line_buffer (line_len); strcpy (the_line, saved_line_for_history->line); rl_undo_list = (UNDO_LIST *)saved_line_for_history->data; _rl_free_history_entry (saved_line_for_history); saved_line_for_history = (HIST_ENTRY *)NULL; rl_end = rl_point = strlen (the_line); } else ding (); return 0; } /* Save the current line in saved_line_for_history. */ int maybe_save_line () { if (saved_line_for_history == 0) { saved_line_for_history = (HIST_ENTRY *)xmalloc (sizeof (HIST_ENTRY)); saved_line_for_history->line = savestring (the_line); saved_line_for_history->data = (char *)rl_undo_list; } return 0; } /* **************************************************************** */ /* */ /* History Commands */ /* */ /* **************************************************************** */ /* Meta-< goes to the start of the history. */ int rl_beginning_of_history (count, key) int count, key; { return (rl_get_previous_history (1 + where_history (), key)); } /* Meta-> goes to the end of the history. (The current line). */ int rl_end_of_history (count, key) int count, key; { maybe_replace_line (); using_history (); maybe_unsave_line (); return 0; } /* Move down to the next history line. */ int rl_get_next_history (count, key) int count, key; { HIST_ENTRY *temp; int line_len; if (count < 0) return (rl_get_previous_history (-count, key)); if (count == 0) return 0; maybe_replace_line (); temp = (HIST_ENTRY *)NULL; while (count) { temp = next_history (); if (!temp) break; --count; } if (temp == 0) maybe_unsave_line (); else { line_len = strlen (temp->line); if (line_len >= rl_line_buffer_len) rl_extend_line_buffer (line_len); strcpy (the_line, temp->line); rl_undo_list = (UNDO_LIST *)temp->data; rl_end = rl_point = strlen (the_line); #if defined (VI_MODE) if (rl_editing_mode == vi_mode) rl_point = 0; #endif /* VI_MODE */ } return 0; } /* Get the previous item out of our interactive history, making it the current line. If there is no previous history, just ding. */ int rl_get_previous_history (count, key) int count, key; { HIST_ENTRY *old_temp, *temp; int line_len; if (count < 0) return (rl_get_next_history (-count, key)); if (count == 0) return 0; /* If we don't have a line saved, then save this one. */ maybe_save_line (); /* If the current line has changed, save the changes. */ maybe_replace_line (); temp = old_temp = (HIST_ENTRY *)NULL; while (count) { temp = previous_history (); if (temp == 0) break; old_temp = temp; --count; } /* If there was a large argument, and we moved back to the start of the history, that is not an error. So use the last value found. */ if (!temp && old_temp) temp = old_temp; if (temp == 0) ding (); else { line_len = strlen (temp->line); if (line_len >= rl_line_buffer_len) rl_extend_line_buffer (line_len); strcpy (the_line, temp->line); rl_undo_list = (UNDO_LIST *)temp->data; rl_end = rl_point = line_len; #if defined (VI_MODE) if (rl_editing_mode == vi_mode) rl_point = 0; #endif /* VI_MODE */ } return 0; } /* **************************************************************** */ /* */ /* The Mark and the Region. */ /* */ /* **************************************************************** */ /* Set the mark at POSITION. */ int _rl_set_mark_at_pos (position) int position; { if (position > rl_end) return -1; rl_mark = position; return 0; } /* A bindable command to set the mark. */ int rl_set_mark (count, key) int count, key; { return (_rl_set_mark_at_pos (rl_explicit_arg ? count : rl_point)); } /* Exchange the position of mark and point. */ int rl_exchange_point_and_mark (count, key) int count, key; { if (rl_mark > rl_end) rl_mark = -1; if (rl_mark == -1) { ding (); return -1; } else SWAP (rl_point, rl_mark); return 0; } /* **************************************************************** */ /* */ /* Editing Modes */ /* */ /* **************************************************************** */ /* How to toggle back and forth between editing modes. */ int rl_vi_editing_mode (count, key) int count, key; { #if defined (VI_MODE) rl_editing_mode = vi_mode; rl_vi_insertion_mode (1, key); #endif /* VI_MODE */ return 0; } int rl_emacs_editing_mode (count, key) int count, key; { rl_editing_mode = emacs_mode; _rl_keymap = emacs_standard_keymap; return 0; }