diff options
Diffstat (limited to 'text.c')
-rw-r--r-- | text.c | 525 |
1 files changed, 509 insertions, 16 deletions
@@ -1,6 +1,6 @@ /* text.c -- text handling commands for readline. */ -/* Copyright (C) 1987-2021 Free Software Foundation, Inc. +/* Copyright (C) 1987-2021,2023-2024 Free Software Foundation, Inc. This file is part of the GNU Readline Library (Readline), a library for reading lines of text with interactive input and history editing. @@ -85,7 +85,8 @@ int _rl_optimize_typeahead = 1; /* rl_insert tries to read typeahead */ int rl_insert_text (const char *string) { - register int i, l; + register int i; + size_t l; l = (string && *string) ? strlen (string) : 0; if (l == 0) @@ -704,7 +705,11 @@ static mbstate_t ps = {0}; /* Insert the character C at the current location, moving point forward. If C introduces a multibyte sequence, we read the whole sequence and - then insert the multibyte char into the line buffer. */ + then insert the multibyte char into the line buffer. + If C == 0, we immediately insert any pending partial multibyte character, + assuming that we have read a character that doesn't map to self-insert. + This doesn't completely handle characters that are part of a multibyte + character but map to editing functions. */ int _rl_insert_char (int count, int c) { @@ -718,11 +723,28 @@ _rl_insert_char (int count, int c) static int stored_count = 0; #endif +#if !defined (HANDLE_MULTIBYTE) if (count <= 0) return 0; +#else + if (count < 0) + return 0; + if (count == 0) + { + if (pending_bytes_length == 0) + return 0; + if (stored_count <= 0) + stored_count = count; + else + count = stored_count; -#if defined (HANDLE_MULTIBYTE) - if (MB_CUR_MAX == 1 || rl_byte_oriented) + memcpy (incoming, pending_bytes, pending_bytes_length); + incoming[pending_bytes_length] = '\0'; + incoming_length = pending_bytes_length; + pending_bytes_length = 0; + memset (&ps, 0, sizeof (mbstate_t)); + } + else if (MB_CUR_MAX == 1 || rl_byte_oriented) { incoming[0] = c; incoming[1] = '\0'; @@ -730,6 +752,9 @@ _rl_insert_char (int count, int c) } else if (_rl_utf8locale && (c & 0x80) == 0) { + if (pending_bytes_length) + _rl_insert_char (0, 0); + incoming[0] = c; incoming[1] = '\0'; incoming_length = 1; @@ -764,7 +789,8 @@ _rl_insert_char (int count, int c) incoming[1] = '\0'; incoming_length = 1; pending_bytes_length--; - memmove (pending_bytes, pending_bytes + 1, pending_bytes_length); + if (pending_bytes_length) + memmove (pending_bytes, pending_bytes + 1, pending_bytes_length); /* Clear the state of the byte sequence, because in this case the effect of mbstate is undefined. */ memset (&ps, 0, sizeof (mbstate_t)); @@ -827,7 +853,11 @@ _rl_insert_char (int count, int c) rl_insert_text (string); xfree (string); +#if defined (HANDLE_MULTIBYTE) + return (pending_bytes_length != 0); +#else return 0; +#endif } if (count > TEXT_COUNT_MAX) @@ -860,6 +890,8 @@ _rl_insert_char (int count, int c) xfree (string); incoming_length = 0; stored_count = 0; + + return (pending_bytes_length != 0); #else /* !HANDLE_MULTIBYTE */ char str[TEXT_COUNT_MAX+1]; @@ -873,9 +905,9 @@ _rl_insert_char (int count, int c) rl_insert_text (str); count -= decreaser; } -#endif /* !HANDLE_MULTIBYTE */ return 0; +#endif /* !HANDLE_MULTIBYTE */ } if (MB_CUR_MAX == 1 || rl_byte_oriented) @@ -903,9 +935,11 @@ _rl_insert_char (int count, int c) rl_insert_text (incoming); stored_count = 0; } -#endif - + + return (pending_bytes_length != 0); +#else return 0; +#endif } /* Overwrite the character at point (or next COUNT characters) with C. @@ -983,6 +1017,11 @@ rl_insert (int count, int c) break; } + /* If we didn't insert n and there are pending bytes, we need to insert + them if _rl_insert_char didn't do that on its own. */ + if (r == 1 && rl_insert_mode == RL_IM_INSERT) + r = _rl_insert_char (0, 0); /* flush partial multibyte char */ + if (n != (unsigned short)-2) /* -2 = sentinel value for having inserted N */ { /* setting rl_pending_input inhibits setting rl_last_func so we do it @@ -992,7 +1031,6 @@ rl_insert (int count, int c) rl_executing_keyseq[rl_key_sequence_length = 0] = '\0'; r = rl_execute_next (n); } - return r; } @@ -1054,6 +1092,8 @@ _rl_insert_next_callback (_rl_callback_generic_arg *data) int rl_quoted_insert (int count, int key) { + int r; + /* Let's see...should the callback interface futz with signal handling? */ #if defined (HANDLE_SIGNALS) if (RL_ISSTATE (RL_STATE_CALLBACK) == 0) @@ -1072,15 +1112,17 @@ rl_quoted_insert (int count, int key) /* A negative count means to quote the next -COUNT characters. */ if (count < 0) { - int r; - do r = _rl_insert_next (1); while (r == 0 && ++count < 0); - return r; } + else + r = _rl_insert_next (count); - return _rl_insert_next (count); + if (r == 1) + _rl_insert_char (0, 0); /* insert partial multibyte character */ + + return r; } /* Insert a tab character. */ @@ -1229,11 +1271,12 @@ _rl_rubout_char (int count, int key) c = rl_line_buffer[--rl_point]; rl_delete_text (rl_point, orig_point); /* The erase-at-end-of-line hack is of questionable merit now. */ - if (rl_point == rl_end && ISPRINT ((unsigned char)c) && _rl_last_c_pos) + if (rl_point == rl_end && ISPRINT ((unsigned char)c) && _rl_last_c_pos && _rl_last_v_pos == 0) { int l; l = rl_character_len (c, rl_point); - _rl_erase_at_end_of_line (l); + if (_rl_last_c_pos >= l) + _rl_erase_at_end_of_line (l); } } else @@ -1885,3 +1928,453 @@ rl_mark_active_p (void) { return (mark_active); } + +/* **************************************************************** */ +/* */ +/* Reading a string entered from the keyboard */ +/* */ +/* **************************************************************** */ + +/* A very simple set of functions to read a string from the keyboard using + the line buffer as temporary storage. The caller can set a completion + function to perform completion on TAB and SPACE. */ + +/* XXX - this is all very similar to the search stuff but with a different + CXT. */ + +static HIST_ENTRY *_rl_saved_line_for_readstr; +_rl_readstr_cxt *_rl_rscxt; + +_rl_readstr_cxt * +_rl_rscxt_alloc (int flags) +{ + _rl_readstr_cxt *cxt; + + cxt = (_rl_readstr_cxt *)xmalloc (sizeof (_rl_readstr_cxt)); + + cxt->flags = flags; + + cxt->save_point = rl_point; + cxt->save_mark = rl_mark; + cxt->save_line = where_history (); + + cxt->prevc = cxt->lastc = 0; + + cxt->compfunc = NULL; + + return cxt; +} + +void +_rl_rscxt_dispose (_rl_readstr_cxt *cxt, int flags) +{ + xfree (cxt); +} + +/* This isn't used yet */ +void +_rl_free_saved_readstr_line () +{ + if (_rl_saved_line_for_readstr) + /* This doesn't free any saved undo list, if it needs to, + rl_clear_history shows how to do it. */ + _rl_free_saved_line (_rl_saved_line_for_readstr); + _rl_saved_line_for_readstr = (HIST_ENTRY *)NULL; +} + +void +_rl_unsave_saved_readstr_line () +{ + if (_rl_saved_line_for_readstr) + { + _rl_free_undo_list (rl_undo_list); + _rl_unsave_line (_rl_saved_line_for_readstr); /* restores rl_undo_list */ + } + _rl_saved_line_for_readstr = (HIST_ENTRY *)NULL; +} + +_rl_readstr_cxt * +_rl_readstr_init (int pchar, int flags) +{ + _rl_readstr_cxt *cxt; + char *p; + + cxt = _rl_rscxt_alloc (flags); + + rl_maybe_replace_line (); + _rl_saved_line_for_readstr = _rl_alloc_saved_line (); + + rl_undo_list = 0; + + rl_line_buffer[0] = 0; + rl_end = rl_point = 0; + + p = _rl_make_prompt_for_search (pchar ? pchar : '@'); + cxt->flags |= READSTR_FREEPMT; + rl_message ("%s", p); + xfree (p); + + RL_SETSTATE (RL_STATE_READSTR); + + _rl_rscxt = cxt; + + return cxt; +} + +int +_rl_readstr_cleanup (_rl_readstr_cxt *cxt, int r) +{ + _rl_rscxt_dispose (cxt, 0); + _rl_rscxt = 0; + + RL_UNSETSTATE (RL_STATE_READSTR); + + return (r != 1); +} + +void +_rl_readstr_restore (_rl_readstr_cxt *cxt) +{ + _rl_unsave_saved_readstr_line (); /* restores rl_undo_list */ + rl_point = cxt->save_point; + rl_mark = cxt->save_mark; + if (cxt->flags & READSTR_FREEPMT) + rl_restore_prompt (); /* _rl_make_prompt_for_search saved it */ + cxt->flags &= ~READSTR_FREEPMT; + rl_clear_message (); + _rl_fix_point (1); +} + +int +_rl_readstr_sigcleanup (_rl_readstr_cxt *cxt, int r) +{ + if (cxt->flags & READSTR_FREEPMT) + rl_restore_prompt (); /* _rl_make_prompt_for_search saved it */ + cxt->flags &= ~READSTR_FREEPMT; + return (_rl_readstr_cleanup (cxt, r)); +} + +int +_rl_readstr_getchar (_rl_readstr_cxt *cxt) +{ + int c; + + cxt->prevc = cxt->lastc; + + /* Read a key and decide how to proceed. */ + RL_SETSTATE(RL_STATE_MOREINPUT); + c = cxt->lastc = rl_read_key (); + RL_UNSETSTATE(RL_STATE_MOREINPUT); + +#if defined (HANDLE_MULTIBYTE) + /* This ends up with C (and LASTC) being set to the last byte of the + multibyte character. In most cases c == lastc == mb[0] */ + if (c >= 0 && MB_CUR_MAX > 1 && rl_byte_oriented == 0) + c = cxt->lastc = _rl_read_mbstring (cxt->lastc, cxt->mb, MB_LEN_MAX); +#endif + + RL_CHECK_SIGNALS (); + return c; +} + +/* Process just-read character C according to readstr context CXT. Return -1 + if the caller should abort the read, 0 if we should break out of the + loop, and 1 if we should continue to read characters. This can perform + completion on the string read so far (stored in rl_line_buffer) if the + caller has set up a completion function. The completion function can + return -1 to indicate that we should abort the read. If we return -1 + we will call _rl_readstr_restore to clean up the state, leaving the caller + to free the context. */ +int +_rl_readstr_dispatch (_rl_readstr_cxt *cxt, int c) +{ + int n; + + if (c < 0) + c = CTRL ('C'); + + /* could consider looking up the function bound to they key and dispatching + off that, but you want most characters inserted by default without having + to quote. */ + switch (c) + { + case CTRL('W'): + rl_unix_word_rubout (1, c); + break; + + case CTRL('U'): + rl_unix_line_discard (1, c); + break; + + case CTRL('Q'): + case CTRL('V'): + n = rl_quoted_insert (1, c); + if (n < 0) + { + _rl_readstr_restore (cxt); + return -1; + } + cxt->lastc = (rl_point > 0) ? rl_line_buffer[rl_point - 1] : rl_line_buffer[0]; /* preserve prevc */ + break; + + case RETURN: + case NEWLINE: + return 0; + + case CTRL('H'): + case RUBOUT: + if (rl_point == 0) + { + _rl_readstr_restore (cxt); + return -1; + } + _rl_rubout_char (1, c); + break; + + case CTRL('C'): + case CTRL('G'): + rl_ding (); + _rl_readstr_restore (cxt); + return -1; + + case ESC: + /* Allow users to bracketed-paste text into the string. + Similar code is in search.c:_rl_nsearch_dispatch(). */ + if (_rl_enable_bracketed_paste && ((n = _rl_nchars_available ()) >= (BRACK_PASTE_SLEN-1))) + { + if (_rl_read_bracketed_paste_prefix (c) == 1) + rl_bracketed_paste_begin (1, c); + else + { + c = rl_read_key (); /* get the ESC that got pushed back */ + _rl_insert_char (1, c); + } + } + else + _rl_insert_char (1, c); + break; + + case ' ': + if ((cxt->flags & READSTR_NOSPACE) == 0) + { + _rl_insert_char (1, c); + break; + } + /* FALLTHROUGH */ + case TAB: + /* Perform completion if the caller has set a completion function. */ + n = (cxt->compfunc) ? (*cxt->compfunc) (cxt, c) : _rl_insert_char (1, c); + if (n < 0) + { + _rl_readstr_restore (cxt); + return -1; + } + break; + +#if 0 + case CTRL('_'): + rl_do_undo (); + break; +#endif + + default: +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + rl_insert_text (cxt->mb); + else +#endif + _rl_insert_char (1, c); + break; + } + + (*rl_redisplay_function) (); + rl_deactivate_mark (); + return 1; +} + +/* **************************************************************** */ +/* */ +/* Reading and Executing named commands */ +/* */ +/* **************************************************************** */ + +/* A completion generator for bindable readline command names. */ +static char * +readcmd_completion_function (const char *text, int state) +{ + static const char **cmdlist = NULL; + static size_t lind, nlen; + const char *cmdname; + + if (state == 0) + { + if (cmdlist) + free (cmdlist); + + cmdlist = rl_funmap_names (); + lind = 0; + nlen = RL_STRLEN (text); + } + if (cmdlist == 0 || cmdlist[lind] == 0) + return (char *)NULL; + + while (cmdlist[lind]) + { + cmdname = cmdlist[lind++]; + if (STREQN (text, cmdname, nlen)) + return (savestring (cmdname)); + } + return ((char *)NULL); +} + +static void +_rl_display_cmdname_matches (char **matches) +{ + size_t len, max, i; + int old; + + old = rl_filename_completion_desired; + rl_filename_completion_desired = 0; + + /* There is more than one match. Find out how many there are, + and find the maximum printed length of a single entry. */ + for (max = 0, i = 1; matches[i]; i++) + { + len = strlen (matches[i]); + + if (len > max) + max = len; + } + len = i - 1; + + rl_display_match_list (matches, len, max); + rl_filename_completion_desired = old; + + rl_forced_update_display (); + rl_display_fixed = 1; +} + +static int +_rl_readcmd_complete (_rl_readstr_cxt *cxt, int c) +{ + char **matches; + char *prefix; + size_t plen; + + matches = rl_completion_matches (rl_line_buffer, readcmd_completion_function); + + if (RL_SIG_RECEIVED()) + { + _rl_free_match_list (matches); + matches = 0; + RL_CHECK_SIGNALS (); + return -1; + } + else if (matches == 0) + rl_ding (); + + /* Whether or not there are multiple matches, we just want to append the + new characters in matches[0]. We display possible matches if we didn't + append anything. */ + if (matches) + { + prefix = matches[0]; + plen = strlen (prefix); + + if (plen > rl_end) + { + size_t n; + for (n = rl_end; n < plen && prefix[n]; n++) + _rl_insert_char (1, prefix[n]); + } + else if (matches[1]) + _rl_display_cmdname_matches (matches); + _rl_free_match_list (matches); + } + + return 0; +} + +/* Use the readstr functions to read a bindable command name using the + line buffer, with completion. */ +static char * +_rl_read_command_name () +{ + _rl_readstr_cxt *cxt; + char *ret; + int c, r; + + cxt = _rl_readstr_init ('!', READSTR_NOSPACE); + cxt->compfunc = _rl_readcmd_complete; + + /* skip callback stuff for now */ + r = 0; + while (1) + { + c = _rl_readstr_getchar (cxt); + + if (c < 0) + { + _rl_readstr_restore (cxt); + _rl_readstr_cleanup (cxt, r); + return NULL; + } + + if (c == 0) + break; + + r = _rl_readstr_dispatch (cxt, c); + if (r < 0) + { + _rl_readstr_cleanup (cxt, r); + return NULL; /* dispatch function cleans up */ + } + else if (r == 0) + break; + } + + ret = savestring (rl_line_buffer); + + /* Now restore the original line and perform one final redisplay. */ + _rl_readstr_restore (cxt); + (*rl_redisplay_function) (); + + /* And free up the context. */ + _rl_readstr_cleanup (cxt, r); + return ret; +} + +/* Read a command name from the keyboard and execute it as if the bound key + sequence had been entered. */ +int +rl_execute_named_command (int count, int key) +{ + char *command; + rl_command_func_t *func; + int r; + + command = _rl_read_command_name (); + if (command == 0 || *command == '\0') + return 1; + if (func = rl_named_function (command)) + { + int prev, ostate; + + prev = rl_dispatching; + ostate = RL_ISSTATE (RL_STATE_DISPATCHING); + rl_dispatching = 1; + RL_SETSTATE (RL_STATE_DISPATCHING); /* make sure it's set */ + r = (*func) (count, key); + if (ostate == 0) + RL_UNSETSTATE (RL_STATE_DISPATCHING); /* unset it if it wasn't set */ + rl_dispatching = prev; + } + else + { + rl_ding (); + r = 1; + } + + free (command); + return r; +} |