summaryrefslogtreecommitdiff
path: root/text.c
diff options
context:
space:
mode:
Diffstat (limited to 'text.c')
-rw-r--r--text.c525
1 files changed, 509 insertions, 16 deletions
diff --git a/text.c b/text.c
index 30fdaa1..c5281ef 100644
--- a/text.c
+++ b/text.c
@@ -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;
+}