5836a818ec
This reverts commitb558ff043d
. This reverts commit4a11f20659
. The initial import commit failed to retain local changes made to readline's configure.in (and the commit message erroneously stated that there were no local changes that needed to be reapplied). Also the import caused a couple of build errors and a scattering of testsuite regressions throughout many arches. It's probably better to start over with this import, hopefully more carefully next time.
1703 lines
37 KiB
C
1703 lines
37 KiB
C
/* text.c -- text handling commands for readline. */
|
|
|
|
/* Copyright (C) 1987-2010 Free Software Foundation, Inc.
|
|
|
|
This file is part of the GNU Readline Library (Readline), a library
|
|
for reading lines of text with interactive input and history editing.
|
|
|
|
Readline is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Readline is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Readline. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#define READLINE_LIBRARY
|
|
|
|
#if defined (HAVE_CONFIG_H)
|
|
# include <config.h>
|
|
#endif
|
|
|
|
#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>
|
|
|
|
/* System-specific feature definitions and include files. */
|
|
#include "rldefs.h"
|
|
#include "rlmbutil.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"
|
|
|
|
/* Forward declarations. */
|
|
static int rl_change_case PARAMS((int, int));
|
|
static int _rl_char_search PARAMS((int, int, int));
|
|
|
|
#if defined (READLINE_CALLBACKS)
|
|
static int _rl_insert_next_callback PARAMS((_rl_callback_generic_arg *));
|
|
static int _rl_char_search_callback PARAMS((_rl_callback_generic_arg *));
|
|
#endif
|
|
|
|
/* The largest chunk of text that can be inserted in one call to
|
|
rl_insert_text. Text blocks larger than this are divided. */
|
|
#define TEXT_COUNT_MAX 1024
|
|
|
|
/* **************************************************************** */
|
|
/* */
|
|
/* 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_char () calls this
|
|
function. Returns the number of characters inserted. */
|
|
int
|
|
rl_insert_text (string)
|
|
const char *string;
|
|
{
|
|
register int i, l;
|
|
|
|
l = (string && *string) ? strlen (string) : 0;
|
|
if (l == 0)
|
|
return 0;
|
|
|
|
if (rl_end + l >= rl_line_buffer_len)
|
|
rl_extend_line_buffer (rl_end + l);
|
|
|
|
for (i = rl_end; i >= rl_point; i--)
|
|
rl_line_buffer[i + l] = rl_line_buffer[i];
|
|
strncpy (rl_line_buffer + rl_point, string, l);
|
|
|
|
/* Remember how to undo this if we aren't undoing something. */
|
|
if (_rl_doing_an_undo == 0)
|
|
{
|
|
/* 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;
|
|
rl_line_buffer[rl_end] = '\0';
|
|
return l;
|
|
}
|
|
|
|
/* Delete the string between FROM and TO. FROM is inclusive, TO is not.
|
|
Returns the number of characters deleted. */
|
|
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;
|
|
}
|
|
if (from < 0)
|
|
from = 0;
|
|
|
|
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++)
|
|
rl_line_buffer[i] = rl_line_buffer[i + diff];
|
|
|
|
/* Remember how to undo this delete. */
|
|
if (_rl_doing_an_undo == 0)
|
|
rl_add_undo (UNDO_DELETE, from, to, text);
|
|
else
|
|
xfree (text);
|
|
|
|
rl_end -= diff;
|
|
rl_line_buffer[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
|
|
|
|
/* Replace the contents of the line buffer between START and END with
|
|
TEXT. The operation is undoable. To replace the entire line in an
|
|
undoable mode, use _rl_replace_text(text, 0, rl_end); */
|
|
int
|
|
_rl_replace_text (text, start, end)
|
|
const char *text;
|
|
int start, end;
|
|
{
|
|
int n;
|
|
|
|
n = 0;
|
|
rl_begin_undo_group ();
|
|
if (start <= end)
|
|
rl_delete_text (start, end + 1);
|
|
rl_point = start;
|
|
if (*text)
|
|
n = rl_insert_text (text);
|
|
rl_end_undo_group ();
|
|
|
|
return n;
|
|
}
|
|
|
|
/* Replace the current line buffer contents with TEXT. If CLEAR_UNDO is
|
|
non-zero, we free the current undo list. */
|
|
void
|
|
rl_replace_line (text, clear_undo)
|
|
const char *text;
|
|
int clear_undo;
|
|
{
|
|
int len;
|
|
|
|
len = strlen (text);
|
|
if (len >= rl_line_buffer_len)
|
|
rl_extend_line_buffer (len);
|
|
strcpy (rl_line_buffer, text);
|
|
rl_end = len;
|
|
|
|
if (clear_undo)
|
|
rl_free_undo_list ();
|
|
|
|
_rl_fix_point (1);
|
|
}
|
|
|
|
/* **************************************************************** */
|
|
/* */
|
|
/* 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 bytes. */
|
|
int
|
|
rl_forward_byte (count, key)
|
|
int count, key;
|
|
{
|
|
if (count < 0)
|
|
return (rl_backward_byte (-count, key));
|
|
|
|
if (count > 0)
|
|
{
|
|
int end, lend;
|
|
|
|
end = rl_point + count;
|
|
#if defined (VI_MODE)
|
|
lend = rl_end > 0 ? rl_end - (VI_COMMAND_MODE()) : rl_end;
|
|
#else
|
|
lend = rl_end;
|
|
#endif
|
|
|
|
if (end > lend)
|
|
{
|
|
rl_point = lend;
|
|
rl_ding ();
|
|
}
|
|
else
|
|
rl_point = end;
|
|
}
|
|
|
|
if (rl_end < 0)
|
|
rl_end = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
_rl_forward_char_internal (count)
|
|
int count;
|
|
{
|
|
int point;
|
|
|
|
#if defined (HANDLE_MULTIBYTE)
|
|
point = _rl_find_next_mbchar (rl_line_buffer, rl_point, count, MB_FIND_NONZERO);
|
|
|
|
#if defined (VI_MODE)
|
|
if (point >= rl_end && VI_COMMAND_MODE())
|
|
point = _rl_find_prev_mbchar (rl_line_buffer, rl_end, MB_FIND_NONZERO);
|
|
#endif
|
|
|
|
if (rl_end < 0)
|
|
rl_end = 0;
|
|
#else
|
|
point = rl_point + count;
|
|
if (point > rl_end)
|
|
point = rl_end;
|
|
#endif
|
|
|
|
return (point);
|
|
}
|
|
|
|
#if defined (HANDLE_MULTIBYTE)
|
|
/* Move forward COUNT characters. */
|
|
int
|
|
rl_forward_char (count, key)
|
|
int count, key;
|
|
{
|
|
int point;
|
|
|
|
if (MB_CUR_MAX == 1 || rl_byte_oriented)
|
|
return (rl_forward_byte (count, key));
|
|
|
|
if (count < 0)
|
|
return (rl_backward_char (-count, key));
|
|
|
|
if (count > 0)
|
|
{
|
|
if (rl_point == rl_end && EMACS_MODE())
|
|
{
|
|
rl_ding ();
|
|
return 0;
|
|
}
|
|
|
|
point = _rl_forward_char_internal (count);
|
|
|
|
if (rl_point == point)
|
|
rl_ding ();
|
|
|
|
rl_point = point;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#else /* !HANDLE_MULTIBYTE */
|
|
int
|
|
rl_forward_char (count, key)
|
|
int count, key;
|
|
{
|
|
return (rl_forward_byte (count, key));
|
|
}
|
|
#endif /* !HANDLE_MULTIBYTE */
|
|
|
|
/* Backwards compatibility. */
|
|
int
|
|
rl_forward (count, key)
|
|
int count, key;
|
|
{
|
|
return (rl_forward_char (count, key));
|
|
}
|
|
|
|
/* Move backward COUNT bytes. */
|
|
int
|
|
rl_backward_byte (count, key)
|
|
int count, key;
|
|
{
|
|
if (count < 0)
|
|
return (rl_forward_byte (-count, key));
|
|
|
|
if (count > 0)
|
|
{
|
|
if (rl_point < count)
|
|
{
|
|
rl_point = 0;
|
|
rl_ding ();
|
|
}
|
|
else
|
|
rl_point -= count;
|
|
}
|
|
|
|
if (rl_point < 0)
|
|
rl_point = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined (HANDLE_MULTIBYTE)
|
|
/* Move backward COUNT characters. */
|
|
int
|
|
rl_backward_char (count, key)
|
|
int count, key;
|
|
{
|
|
int point;
|
|
|
|
if (MB_CUR_MAX == 1 || rl_byte_oriented)
|
|
return (rl_backward_byte (count, key));
|
|
|
|
if (count < 0)
|
|
return (rl_forward_char (-count, key));
|
|
|
|
if (count > 0)
|
|
{
|
|
point = rl_point;
|
|
|
|
while (count > 0 && point > 0)
|
|
{
|
|
point = _rl_find_prev_mbchar (rl_line_buffer, point, MB_FIND_NONZERO);
|
|
count--;
|
|
}
|
|
if (count > 0)
|
|
{
|
|
rl_point = 0;
|
|
rl_ding ();
|
|
}
|
|
else
|
|
rl_point = point;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#else
|
|
int
|
|
rl_backward_char (count, key)
|
|
int count, key;
|
|
{
|
|
return (rl_backward_byte (count, key));
|
|
}
|
|
#endif
|
|
|
|
/* Backwards compatibility. */
|
|
int
|
|
rl_backward (count, key)
|
|
int count, key;
|
|
{
|
|
return (rl_backward_char (count, key));
|
|
}
|
|
|
|
/* 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. Handles multibyte chars. */
|
|
int
|
|
rl_forward_word (count, key)
|
|
int count, key;
|
|
{
|
|
int c;
|
|
|
|
if (count < 0)
|
|
return (rl_backward_word (-count, key));
|
|
|
|
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 = _rl_char_value (rl_line_buffer, rl_point);
|
|
|
|
if (_rl_walphabetic (c) == 0)
|
|
{
|
|
rl_point = MB_NEXTCHAR (rl_line_buffer, rl_point, 1, MB_FIND_NONZERO);
|
|
while (rl_point < rl_end)
|
|
{
|
|
c = _rl_char_value (rl_line_buffer, rl_point);
|
|
if (_rl_walphabetic (c))
|
|
break;
|
|
rl_point = MB_NEXTCHAR (rl_line_buffer, rl_point, 1, MB_FIND_NONZERO);
|
|
}
|
|
}
|
|
|
|
if (rl_point == rl_end)
|
|
return 0;
|
|
|
|
rl_point = MB_NEXTCHAR (rl_line_buffer, rl_point, 1, MB_FIND_NONZERO);
|
|
while (rl_point < rl_end)
|
|
{
|
|
c = _rl_char_value (rl_line_buffer, rl_point);
|
|
if (_rl_walphabetic (c) == 0)
|
|
break;
|
|
rl_point = MB_NEXTCHAR (rl_line_buffer, rl_point, 1, MB_FIND_NONZERO);
|
|
}
|
|
|
|
--count;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Move backward a word. We do what Emacs does. Handles multibyte chars. */
|
|
int
|
|
rl_backward_word (count, key)
|
|
int count, key;
|
|
{
|
|
int c, p;
|
|
|
|
if (count < 0)
|
|
return (rl_forward_word (-count, key));
|
|
|
|
while (count)
|
|
{
|
|
if (rl_point == 0)
|
|
return 0;
|
|
|
|
/* Like rl_forward_word (), except that we look at the characters
|
|
just before point. */
|
|
|
|
p = MB_PREVCHAR (rl_line_buffer, rl_point, MB_FIND_NONZERO);
|
|
c = _rl_char_value (rl_line_buffer, p);
|
|
|
|
if (_rl_walphabetic (c) == 0)
|
|
{
|
|
rl_point = p;
|
|
while (rl_point > 0)
|
|
{
|
|
p = MB_PREVCHAR (rl_line_buffer, rl_point, MB_FIND_NONZERO);
|
|
c = _rl_char_value (rl_line_buffer, p);
|
|
if (_rl_walphabetic (c))
|
|
break;
|
|
rl_point = p;
|
|
}
|
|
}
|
|
|
|
while (rl_point)
|
|
{
|
|
p = MB_PREVCHAR (rl_line_buffer, rl_point, MB_FIND_NONZERO);
|
|
c = _rl_char_value (rl_line_buffer, p);
|
|
if (_rl_walphabetic (c) == 0)
|
|
break;
|
|
else
|
|
rl_point = p;
|
|
}
|
|
|
|
--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, rl_line_buffer); /* 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_skip_csi_sequence (count, key)
|
|
int count, key;
|
|
{
|
|
int ch;
|
|
|
|
RL_SETSTATE (RL_STATE_MOREINPUT);
|
|
do
|
|
ch = rl_read_key ();
|
|
while (ch >= 0x20 && ch < 0x40);
|
|
RL_UNSETSTATE (RL_STATE_MOREINPUT);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rl_arrow_keys (count, c)
|
|
int count, c;
|
|
{
|
|
int ch;
|
|
|
|
RL_SETSTATE(RL_STATE_MOREINPUT);
|
|
ch = rl_read_key ();
|
|
RL_UNSETSTATE(RL_STATE_MOREINPUT);
|
|
|
|
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':
|
|
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
|
|
rl_forward_char (count, ch);
|
|
else
|
|
rl_forward_byte (count, ch);
|
|
break;
|
|
|
|
case 'D':
|
|
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
|
|
rl_backward_char (count, ch);
|
|
else
|
|
rl_backward_byte (count, ch);
|
|
break;
|
|
|
|
default:
|
|
rl_ding ();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* **************************************************************** */
|
|
/* */
|
|
/* Text commands */
|
|
/* */
|
|
/* **************************************************************** */
|
|
|
|
#ifdef HANDLE_MULTIBYTE
|
|
static char pending_bytes[MB_LEN_MAX];
|
|
static int pending_bytes_length = 0;
|
|
static mbstate_t ps = {0};
|
|
#endif
|
|
|
|
/* 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. */
|
|
int
|
|
_rl_insert_char (count, c)
|
|
int count, c;
|
|
{
|
|
register int i;
|
|
char *string;
|
|
#ifdef HANDLE_MULTIBYTE
|
|
int string_size;
|
|
char incoming[MB_LEN_MAX + 1];
|
|
int incoming_length = 0;
|
|
mbstate_t ps_back;
|
|
static int stored_count = 0;
|
|
#endif
|
|
|
|
if (count <= 0)
|
|
return 0;
|
|
|
|
#if defined (HANDLE_MULTIBYTE)
|
|
if (MB_CUR_MAX == 1 || rl_byte_oriented)
|
|
{
|
|
incoming[0] = c;
|
|
incoming[1] = '\0';
|
|
incoming_length = 1;
|
|
}
|
|
else
|
|
{
|
|
wchar_t wc;
|
|
size_t ret;
|
|
|
|
if (stored_count <= 0)
|
|
stored_count = count;
|
|
else
|
|
count = stored_count;
|
|
|
|
ps_back = ps;
|
|
pending_bytes[pending_bytes_length++] = c;
|
|
ret = mbrtowc (&wc, pending_bytes, pending_bytes_length, &ps);
|
|
|
|
if (ret == (size_t)-2)
|
|
{
|
|
/* Bytes too short to compose character, try to wait for next byte.
|
|
Restore the state of the byte sequence, because in this case the
|
|
effect of mbstate is undefined. */
|
|
ps = ps_back;
|
|
return 1;
|
|
}
|
|
else if (ret == (size_t)-1)
|
|
{
|
|
/* Invalid byte sequence for the current locale. Treat first byte
|
|
as a single character. */
|
|
incoming[0] = pending_bytes[0];
|
|
incoming[1] = '\0';
|
|
incoming_length = 1;
|
|
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));
|
|
}
|
|
else if (ret == (size_t)0)
|
|
{
|
|
incoming[0] = '\0';
|
|
incoming_length = 0;
|
|
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));
|
|
}
|
|
else
|
|
{
|
|
/* We successfully read a single multibyte character. */
|
|
memcpy (incoming, pending_bytes, pending_bytes_length);
|
|
incoming[pending_bytes_length] = '\0';
|
|
incoming_length = pending_bytes_length;
|
|
pending_bytes_length = 0;
|
|
}
|
|
}
|
|
#endif /* HANDLE_MULTIBYTE */
|
|
|
|
/* If we can optimize, then do it. But don't let people crash
|
|
readline because of extra large arguments. */
|
|
if (count > 1 && count <= TEXT_COUNT_MAX)
|
|
{
|
|
#if defined (HANDLE_MULTIBYTE)
|
|
string_size = count * incoming_length;
|
|
string = (char *)xmalloc (1 + string_size);
|
|
|
|
i = 0;
|
|
while (i < string_size)
|
|
{
|
|
strncpy (string + i, incoming, incoming_length);
|
|
i += incoming_length;
|
|
}
|
|
incoming_length = 0;
|
|
stored_count = 0;
|
|
#else /* !HANDLE_MULTIBYTE */
|
|
string = (char *)xmalloc (1 + count);
|
|
|
|
for (i = 0; i < count; i++)
|
|
string[i] = c;
|
|
#endif /* !HANDLE_MULTIBYTE */
|
|
|
|
string[i] = '\0';
|
|
rl_insert_text (string);
|
|
xfree (string);
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (count > TEXT_COUNT_MAX)
|
|
{
|
|
int decreaser;
|
|
#if defined (HANDLE_MULTIBYTE)
|
|
string_size = incoming_length * TEXT_COUNT_MAX;
|
|
string = (char *)xmalloc (1 + string_size);
|
|
|
|
i = 0;
|
|
while (i < string_size)
|
|
{
|
|
strncpy (string + i, incoming, incoming_length);
|
|
i += incoming_length;
|
|
}
|
|
|
|
while (count)
|
|
{
|
|
decreaser = (count > TEXT_COUNT_MAX) ? TEXT_COUNT_MAX : count;
|
|
string[decreaser*incoming_length] = '\0';
|
|
rl_insert_text (string);
|
|
count -= decreaser;
|
|
}
|
|
|
|
xfree (string);
|
|
incoming_length = 0;
|
|
stored_count = 0;
|
|
#else /* !HANDLE_MULTIBYTE */
|
|
char str[TEXT_COUNT_MAX+1];
|
|
|
|
for (i = 0; i < TEXT_COUNT_MAX; i++)
|
|
str[i] = c;
|
|
|
|
while (count)
|
|
{
|
|
decreaser = (count > TEXT_COUNT_MAX ? TEXT_COUNT_MAX : count);
|
|
str[decreaser] = '\0';
|
|
rl_insert_text (str);
|
|
count -= decreaser;
|
|
}
|
|
#endif /* !HANDLE_MULTIBYTE */
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (MB_CUR_MAX == 1 || rl_byte_oriented)
|
|
{
|
|
/* 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. Don't do this if we're current reading input from
|
|
a macro. */
|
|
if ((RL_ISSTATE (RL_STATE_MACROINPUT) == 0) && _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);
|
|
}
|
|
}
|
|
#if defined (HANDLE_MULTIBYTE)
|
|
else
|
|
{
|
|
rl_insert_text (incoming);
|
|
stored_count = 0;
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Overwrite the character at point (or next COUNT characters) with C.
|
|
If C introduces a multibyte character sequence, read the entire sequence
|
|
before starting the overwrite loop. */
|
|
int
|
|
_rl_overwrite_char (count, c)
|
|
int count, c;
|
|
{
|
|
int i;
|
|
#if defined (HANDLE_MULTIBYTE)
|
|
char mbkey[MB_LEN_MAX];
|
|
int k;
|
|
|
|
/* Read an entire multibyte character sequence to insert COUNT times. */
|
|
if (count > 0 && MB_CUR_MAX > 1 && rl_byte_oriented == 0)
|
|
k = _rl_read_mbstring (c, mbkey, MB_LEN_MAX);
|
|
#endif
|
|
|
|
rl_begin_undo_group ();
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
#if defined (HANDLE_MULTIBYTE)
|
|
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
|
|
rl_insert_text (mbkey);
|
|
else
|
|
#endif
|
|
_rl_insert_char (1, c);
|
|
|
|
if (rl_point < rl_end)
|
|
rl_delete (1, c);
|
|
}
|
|
|
|
rl_end_undo_group ();
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rl_insert (count, c)
|
|
int count, c;
|
|
{
|
|
return (rl_insert_mode == RL_IM_INSERT ? _rl_insert_char (count, c)
|
|
: _rl_overwrite_char (count, c));
|
|
}
|
|
|
|
/* Insert the next typed character verbatim. */
|
|
static int
|
|
_rl_insert_next (count)
|
|
int count;
|
|
{
|
|
int c;
|
|
|
|
RL_SETSTATE(RL_STATE_MOREINPUT);
|
|
c = rl_read_key ();
|
|
RL_UNSETSTATE(RL_STATE_MOREINPUT);
|
|
|
|
if (c < 0)
|
|
return -1;
|
|
|
|
#if defined (HANDLE_SIGNALS)
|
|
if (RL_ISSTATE (RL_STATE_CALLBACK) == 0)
|
|
_rl_restore_tty_signals ();
|
|
#endif
|
|
|
|
return (_rl_insert_char (count, c));
|
|
}
|
|
|
|
#if defined (READLINE_CALLBACKS)
|
|
static int
|
|
_rl_insert_next_callback (data)
|
|
_rl_callback_generic_arg *data;
|
|
{
|
|
int count;
|
|
|
|
count = data->count;
|
|
|
|
/* Deregister function, let rl_callback_read_char deallocate data */
|
|
_rl_callback_func = 0;
|
|
_rl_want_redisplay = 1;
|
|
|
|
return _rl_insert_next (count);
|
|
}
|
|
#endif
|
|
|
|
int
|
|
rl_quoted_insert (count, key)
|
|
int count, key;
|
|
{
|
|
/* Let's see...should the callback interface futz with signal handling? */
|
|
#if defined (HANDLE_SIGNALS)
|
|
if (RL_ISSTATE (RL_STATE_CALLBACK) == 0)
|
|
_rl_disable_tty_signals ();
|
|
#endif
|
|
|
|
#if defined (READLINE_CALLBACKS)
|
|
if (RL_ISSTATE (RL_STATE_CALLBACK))
|
|
{
|
|
_rl_callback_data = _rl_callback_data_alloc (count);
|
|
_rl_callback_func = _rl_insert_next_callback;
|
|
return (0);
|
|
}
|
|
#endif
|
|
|
|
return _rl_insert_next (count);
|
|
}
|
|
|
|
/* Insert a tab character. */
|
|
int
|
|
rl_tab_insert (count, key)
|
|
int count, key;
|
|
{
|
|
return (_rl_insert_char (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 (_rl_history_preserve_point)
|
|
_rl_history_saved_point = (rl_point == rl_end) ? -1 : rl_point;
|
|
|
|
RL_SETSTATE(RL_STATE_DONE);
|
|
|
|
#if defined (VI_MODE)
|
|
if (rl_editing_mode == vi_mode)
|
|
{
|
|
_rl_vi_done_inserting ();
|
|
if (_rl_vi_textmod_command (_rl_vi_last_command) == 0) /* XXX */
|
|
_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 rl_crlf(). */
|
|
if (rl_erase_empty_line && rl_point == 0 && rl_end == 0)
|
|
return 0;
|
|
|
|
if (_rl_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;
|
|
}
|
|
|
|
/* This is different from what vi does, so the code's not shared. Emacs
|
|
rubout in overwrite mode has one oddity: it replaces a control
|
|
character that's displayed as two characters (^X) with two spaces. */
|
|
int
|
|
_rl_overwrite_rubout (count, key)
|
|
int count, key;
|
|
{
|
|
int opoint;
|
|
int i, l;
|
|
|
|
if (rl_point == 0)
|
|
{
|
|
rl_ding ();
|
|
return 1;
|
|
}
|
|
|
|
opoint = rl_point;
|
|
|
|
/* L == number of spaces to insert */
|
|
for (i = l = 0; i < count; i++)
|
|
{
|
|
rl_backward_char (1, key);
|
|
l += rl_character_len (rl_line_buffer[rl_point], rl_point); /* not exactly right */
|
|
}
|
|
|
|
rl_begin_undo_group ();
|
|
|
|
if (count > 1 || rl_explicit_arg)
|
|
rl_kill_text (opoint, rl_point);
|
|
else
|
|
rl_delete_text (opoint, rl_point);
|
|
|
|
/* Emacs puts point at the beginning of the sequence of spaces. */
|
|
if (rl_point < rl_end)
|
|
{
|
|
opoint = rl_point;
|
|
_rl_insert_char (l, ' ');
|
|
rl_point = opoint;
|
|
}
|
|
|
|
rl_end_undo_group ();
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Rubout the character behind point. */
|
|
int
|
|
rl_rubout (count, key)
|
|
int count, key;
|
|
{
|
|
if (count < 0)
|
|
return (rl_delete (-count, key));
|
|
|
|
if (!rl_point)
|
|
{
|
|
rl_ding ();
|
|
return -1;
|
|
}
|
|
|
|
if (rl_insert_mode == RL_IM_OVERWRITE)
|
|
return (_rl_overwrite_rubout (count, key));
|
|
|
|
return (_rl_rubout_char (count, key));
|
|
}
|
|
|
|
int
|
|
_rl_rubout_char (count, key)
|
|
int count, key;
|
|
{
|
|
int orig_point;
|
|
unsigned char c;
|
|
|
|
/* Duplicated code because this is called from other parts of the library. */
|
|
if (count < 0)
|
|
return (rl_delete (-count, key));
|
|
|
|
if (rl_point == 0)
|
|
{
|
|
rl_ding ();
|
|
return -1;
|
|
}
|
|
|
|
orig_point = rl_point;
|
|
if (count > 1 || rl_explicit_arg)
|
|
{
|
|
rl_backward_char (count, key);
|
|
rl_kill_text (orig_point, rl_point);
|
|
}
|
|
else if (MB_CUR_MAX == 1 || rl_byte_oriented)
|
|
{
|
|
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 (c) && _rl_last_c_pos)
|
|
{
|
|
int l;
|
|
l = rl_character_len (c, rl_point);
|
|
_rl_erase_at_end_of_line (l);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rl_point = _rl_find_prev_mbchar (rl_line_buffer, rl_point, MB_FIND_NONZERO);
|
|
rl_delete_text (rl_point, orig_point);
|
|
}
|
|
|
|
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;
|
|
{
|
|
int xpoint;
|
|
|
|
if (count < 0)
|
|
return (_rl_rubout_char (-count, key));
|
|
|
|
if (rl_point == rl_end)
|
|
{
|
|
rl_ding ();
|
|
return -1;
|
|
}
|
|
|
|
if (count > 1 || rl_explicit_arg)
|
|
{
|
|
xpoint = rl_point;
|
|
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
|
|
rl_forward_char (count, key);
|
|
else
|
|
rl_forward_byte (count, key);
|
|
|
|
rl_kill_text (xpoint, rl_point);
|
|
rl_point = xpoint;
|
|
}
|
|
else
|
|
{
|
|
xpoint = MB_NEXTCHAR (rl_line_buffer, rl_point, 1, MB_FIND_NONZERO);
|
|
rl_delete_text (rl_point, xpoint);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* 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_char (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;
|
|
|
|
while (rl_point && whitespace (rl_line_buffer[rl_point - 1]))
|
|
rl_point--;
|
|
|
|
start = rl_point;
|
|
|
|
while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point]))
|
|
rl_point++;
|
|
|
|
if (start != rl_point)
|
|
{
|
|
rl_delete_text (start, rl_point);
|
|
rl_point = start;
|
|
}
|
|
|
|
if (rl_point < 0)
|
|
rl_point = 0;
|
|
|
|
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;
|
|
{
|
|
char *rl_comment_text;
|
|
int rl_comment_len;
|
|
|
|
rl_beg_of_line (1, key);
|
|
rl_comment_text = _rl_comment_begin ? _rl_comment_begin : RL_COMMENT_BEGIN_DEFAULT;
|
|
|
|
if (rl_explicit_arg == 0)
|
|
rl_insert_text (rl_comment_text);
|
|
else
|
|
{
|
|
rl_comment_len = strlen (rl_comment_text);
|
|
if (STREQN (rl_comment_text, rl_line_buffer, rl_comment_len))
|
|
rl_delete_text (rl_point, rl_point + rl_comment_len);
|
|
else
|
|
rl_insert_text (rl_comment_text);
|
|
}
|
|
|
|
(*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;
|
|
{
|
|
int start, next, end;
|
|
int inword, c, nc, nop;
|
|
#if defined (HANDLE_MULTIBYTE)
|
|
wchar_t wc, nwc;
|
|
char mb[MB_LEN_MAX+1];
|
|
int mlen;
|
|
size_t m;
|
|
mbstate_t mps;
|
|
#endif
|
|
|
|
start = rl_point;
|
|
rl_forward_word (count, 0);
|
|
end = rl_point;
|
|
|
|
if (op != UpCase && op != DownCase && op != CapCase)
|
|
{
|
|
rl_ding ();
|
|
return -1;
|
|
}
|
|
|
|
if (count < 0)
|
|
SWAP (start, end);
|
|
|
|
#if defined (HANDLE_MULTIBYTE)
|
|
memset (&mps, 0, sizeof (mbstate_t));
|
|
#endif
|
|
|
|
/* We are going to modify some text, so let's prepare to undo it. */
|
|
rl_modifying (start, end);
|
|
|
|
inword = 0;
|
|
while (start < end)
|
|
{
|
|
c = _rl_char_value (rl_line_buffer, start);
|
|
/* This assumes that the upper and lower case versions are the same width. */
|
|
next = MB_NEXTCHAR (rl_line_buffer, start, 1, MB_FIND_NONZERO);
|
|
|
|
if (_rl_walphabetic (c) == 0)
|
|
{
|
|
inword = 0;
|
|
start = next;
|
|
continue;
|
|
}
|
|
|
|
if (op == CapCase)
|
|
{
|
|
nop = inword ? DownCase : UpCase;
|
|
inword = 1;
|
|
}
|
|
else
|
|
nop = op;
|
|
if (MB_CUR_MAX == 1 || rl_byte_oriented || isascii (c))
|
|
{
|
|
nc = (nop == UpCase) ? _rl_to_upper (c) : _rl_to_lower (c);
|
|
rl_line_buffer[start] = nc;
|
|
}
|
|
#if defined (HANDLE_MULTIBYTE)
|
|
else
|
|
{
|
|
m = mbrtowc (&wc, rl_line_buffer + start, end - start, &mps);
|
|
if (MB_INVALIDCH (m))
|
|
wc = (wchar_t)rl_line_buffer[start];
|
|
else if (MB_NULLWCH (m))
|
|
wc = L'\0';
|
|
nwc = (nop == UpCase) ? _rl_to_wupper (wc) : _rl_to_wlower (wc);
|
|
if (nwc != wc) /* just skip unchanged characters */
|
|
{
|
|
mlen = wcrtomb (mb, nwc, &mps);
|
|
if (mlen > 0)
|
|
mb[mlen] = '\0';
|
|
/* Assume the same width */
|
|
strncpy (rl_line_buffer + start, mb, mlen);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
start = next;
|
|
}
|
|
|
|
rl_point = end;
|
|
return 0;
|
|
}
|
|
|
|
/* **************************************************************** */
|
|
/* */
|
|
/* Transposition */
|
|
/* */
|
|
/* **************************************************************** */
|
|
|
|
/* Transpose the words at point. If point is at the end of the line,
|
|
transpose the two words before 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))
|
|
{
|
|
rl_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 ();
|
|
xfree (word1);
|
|
xfree (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;
|
|
{
|
|
#if defined (HANDLE_MULTIBYTE)
|
|
char *dummy;
|
|
int i;
|
|
#else
|
|
char dummy[2];
|
|
#endif
|
|
int char_length, prev_point;
|
|
|
|
if (count == 0)
|
|
return 0;
|
|
|
|
if (!rl_point || rl_end < 2)
|
|
{
|
|
rl_ding ();
|
|
return -1;
|
|
}
|
|
|
|
rl_begin_undo_group ();
|
|
|
|
if (rl_point == rl_end)
|
|
{
|
|
rl_point = MB_PREVCHAR (rl_line_buffer, rl_point, MB_FIND_NONZERO);
|
|
count = 1;
|
|
}
|
|
|
|
prev_point = rl_point;
|
|
rl_point = MB_PREVCHAR (rl_line_buffer, rl_point, MB_FIND_NONZERO);
|
|
|
|
#if defined (HANDLE_MULTIBYTE)
|
|
char_length = prev_point - rl_point;
|
|
dummy = (char *)xmalloc (char_length + 1);
|
|
for (i = 0; i < char_length; i++)
|
|
dummy[i] = rl_line_buffer[rl_point + i];
|
|
dummy[i] = '\0';
|
|
#else
|
|
dummy[0] = rl_line_buffer[rl_point];
|
|
dummy[char_length = 1] = '\0';
|
|
#endif
|
|
|
|
rl_delete_text (rl_point, rl_point + char_length);
|
|
|
|
rl_point = _rl_find_next_mbchar (rl_line_buffer, rl_point, count, MB_FIND_NONZERO);
|
|
|
|
_rl_fix_point (0);
|
|
rl_insert_text (dummy);
|
|
rl_end_undo_group ();
|
|
|
|
#if defined (HANDLE_MULTIBYTE)
|
|
xfree (dummy);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* **************************************************************** */
|
|
/* */
|
|
/* Character Searching */
|
|
/* */
|
|
/* **************************************************************** */
|
|
|
|
int
|
|
#if defined (HANDLE_MULTIBYTE)
|
|
_rl_char_search_internal (count, dir, smbchar, len)
|
|
int count, dir;
|
|
char *smbchar;
|
|
int len;
|
|
#else
|
|
_rl_char_search_internal (count, dir, schar)
|
|
int count, dir, schar;
|
|
#endif
|
|
{
|
|
int pos, inc;
|
|
#if defined (HANDLE_MULTIBYTE)
|
|
int prepos;
|
|
#endif
|
|
|
|
if (dir == 0)
|
|
return -1;
|
|
|
|
pos = rl_point;
|
|
inc = (dir < 0) ? -1 : 1;
|
|
while (count)
|
|
{
|
|
if ((dir < 0 && pos <= 0) || (dir > 0 && pos >= rl_end))
|
|
{
|
|
rl_ding ();
|
|
return -1;
|
|
}
|
|
|
|
#if defined (HANDLE_MULTIBYTE)
|
|
pos = (inc > 0) ? _rl_find_next_mbchar (rl_line_buffer, pos, 1, MB_FIND_ANY)
|
|
: _rl_find_prev_mbchar (rl_line_buffer, pos, MB_FIND_ANY);
|
|
#else
|
|
pos += inc;
|
|
#endif
|
|
do
|
|
{
|
|
#if defined (HANDLE_MULTIBYTE)
|
|
if (_rl_is_mbchar_matched (rl_line_buffer, pos, rl_end, smbchar, len))
|
|
#else
|
|
if (rl_line_buffer[pos] == schar)
|
|
#endif
|
|
{
|
|
count--;
|
|
if (dir < 0)
|
|
rl_point = (dir == BTO) ? _rl_find_next_mbchar (rl_line_buffer, pos, 1, MB_FIND_ANY)
|
|
: pos;
|
|
else
|
|
rl_point = (dir == FTO) ? _rl_find_prev_mbchar (rl_line_buffer, pos, MB_FIND_ANY)
|
|
: pos;
|
|
break;
|
|
}
|
|
#if defined (HANDLE_MULTIBYTE)
|
|
prepos = pos;
|
|
#endif
|
|
}
|
|
#if defined (HANDLE_MULTIBYTE)
|
|
while ((dir < 0) ? (pos = _rl_find_prev_mbchar (rl_line_buffer, pos, MB_FIND_ANY)) != prepos
|
|
: (pos = _rl_find_next_mbchar (rl_line_buffer, pos, 1, MB_FIND_ANY)) != prepos);
|
|
#else
|
|
while ((dir < 0) ? pos-- : ++pos < rl_end);
|
|
#endif
|
|
}
|
|
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. So much is dependent on HANDLE_MULTIBYTE
|
|
that there are two separate versions of this function. */
|
|
#if defined (HANDLE_MULTIBYTE)
|
|
static int
|
|
_rl_char_search (count, fdir, bdir)
|
|
int count, fdir, bdir;
|
|
{
|
|
char mbchar[MB_LEN_MAX];
|
|
int mb_len;
|
|
|
|
mb_len = _rl_read_mbchar (mbchar, MB_LEN_MAX);
|
|
|
|
if (mb_len <= 0)
|
|
return -1;
|
|
|
|
if (count < 0)
|
|
return (_rl_char_search_internal (-count, bdir, mbchar, mb_len));
|
|
else
|
|
return (_rl_char_search_internal (count, fdir, mbchar, mb_len));
|
|
}
|
|
#else /* !HANDLE_MULTIBYTE */
|
|
static int
|
|
_rl_char_search (count, fdir, bdir)
|
|
int count, fdir, bdir;
|
|
{
|
|
int c;
|
|
|
|
RL_SETSTATE(RL_STATE_MOREINPUT);
|
|
c = rl_read_key ();
|
|
RL_UNSETSTATE(RL_STATE_MOREINPUT);
|
|
|
|
if (c < 0)
|
|
return -1;
|
|
|
|
if (count < 0)
|
|
return (_rl_char_search_internal (-count, bdir, c));
|
|
else
|
|
return (_rl_char_search_internal (count, fdir, c));
|
|
}
|
|
#endif /* !HANDLE_MULTIBYTE */
|
|
|
|
#if defined (READLINE_CALLBACKS)
|
|
static int
|
|
_rl_char_search_callback (data)
|
|
_rl_callback_generic_arg *data;
|
|
{
|
|
_rl_callback_func = 0;
|
|
_rl_want_redisplay = 1;
|
|
|
|
return (_rl_char_search (data->count, data->i1, data->i2));
|
|
}
|
|
#endif
|
|
|
|
int
|
|
rl_char_search (count, key)
|
|
int count, key;
|
|
{
|
|
#if defined (READLINE_CALLBACKS)
|
|
if (RL_ISSTATE (RL_STATE_CALLBACK))
|
|
{
|
|
_rl_callback_data = _rl_callback_data_alloc (count);
|
|
_rl_callback_data->i1 = FFIND;
|
|
_rl_callback_data->i2 = BFIND;
|
|
_rl_callback_func = _rl_char_search_callback;
|
|
return (0);
|
|
}
|
|
#endif
|
|
|
|
return (_rl_char_search (count, FFIND, BFIND));
|
|
}
|
|
|
|
int
|
|
rl_backward_char_search (count, key)
|
|
int count, key;
|
|
{
|
|
#if defined (READLINE_CALLBACKS)
|
|
if (RL_ISSTATE (RL_STATE_CALLBACK))
|
|
{
|
|
_rl_callback_data = _rl_callback_data_alloc (count);
|
|
_rl_callback_data->i1 = BFIND;
|
|
_rl_callback_data->i2 = FFIND;
|
|
_rl_callback_func = _rl_char_search_callback;
|
|
return (0);
|
|
}
|
|
#endif
|
|
|
|
return (_rl_char_search (count, BFIND, FFIND));
|
|
}
|
|
|
|
/* **************************************************************** */
|
|
/* */
|
|
/* 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)
|
|
{
|
|
rl_ding ();
|
|
return -1;
|
|
}
|
|
else
|
|
SWAP (rl_point, rl_mark);
|
|
|
|
return 0;
|
|
}
|