*
* PS1='\[\033[01;32m\]\u@\h\[\033[01;34m\] \w \$\[\033[00m\] '
*/
-
#include "libbb.h"
-
+#include "unicode.h"
/* FIXME: obsolete CONFIG item? */
#define ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT 0
-
#ifdef TEST
-
-#define ENABLE_FEATURE_EDITING 0
-#define ENABLE_FEATURE_TAB_COMPLETION 0
-#define ENABLE_FEATURE_USERNAME_COMPLETION 0
-#define ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT 0
-
-#endif /* TEST */
+# define ENABLE_FEATURE_EDITING 0
+# define ENABLE_FEATURE_TAB_COMPLETION 0
+# define ENABLE_FEATURE_USERNAME_COMPLETION 0
+# define ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT 0
+#endif
/* Entire file (except TESTing part) sits inside this #if */
#if ENABLE_FEATURE_EDITING
-#if ENABLE_LOCALE_SUPPORT
-#define Isprint(c) isprint(c)
-#else
-#define Isprint(c) ((c) >= ' ' && (c) != ((unsigned char)'\233'))
-#endif
#define ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR \
(ENABLE_FEATURE_USERNAME_COMPLETION || ENABLE_FEATURE_EDITING_FANCY_PROMPT)
#define IF_FEATURE_GETUSERNAME_AND_HOMEDIR(...) __VA_ARGS__
#endif
+
+#undef CHAR_T
+#if ENABLE_FEATURE_ASSUME_UNICODE
+# define BB_NUL L'\0'
+# define CHAR_T wchar_t
+# define BB_isspace(c) iswspace(c)
+# define BB_isalnum(c) iswalnum(c)
+# define BB_ispunct(c) iswpunct(c)
+# define BB_isprint(c) iswprint(c)
+/* this catches bugs */
+# undef isspace
+# undef isalnum
+# undef ispunct
+# undef isprint
+# define isspace isspace_must_not_be_used
+# define isalnum isalnum_must_not_be_used
+# define ispunct ispunct_must_not_be_used
+# define isprint isprint_must_not_be_used
+#else
+# define BB_NUL '\0'
+# define CHAR_T char
+# define BB_isspace(c) isspace(c)
+# define BB_isalnum(c) isalnum(c)
+# define BB_ispunct(c) ispunct(c)
+# if ENABLE_LOCALE_SUPPORT
+# define BB_isprint(c) isprint(c)
+# else
+# define BB_isprint(c) ((c) >= ' ' && (c) != ((unsigned char)'\233'))
+# endif
+#endif
+
+
enum {
/* We use int16_t for positions, need to limit line len */
MAX_LINELEN = CONFIG_FEATURE_EDITING_MAX_LEN < 0x7ff0
unsigned cursor;
unsigned command_len;
- char *command_ps;
+ /* *int* maxsize: we want x in "if (x > S.maxsize)"
+ * to _not_ be promoted to unsigned */
+ int maxsize;
+ CHAR_T *command_ps;
const char *cmdedit_prompt;
#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
#if ENABLE_FEATURE_EDITING_VI
#define DELBUFSIZ 128
- char *delptr;
+ CHAR_T *delptr;
smallint newdelflag; /* whether delbuf should be reused yet */
- char delbuf[DELBUFSIZ]; /* a place to store deleted characters */
+ CHAR_T delbuf[DELBUFSIZ]; /* a place to store deleted characters */
#endif
/* Formerly these were big buffers on stack: */
#define DEINIT_S() deinit_S()
+#if ENABLE_FEATURE_ASSUME_UNICODE
+static size_t load_string(const char *src, int maxsize)
+{
+ ssize_t len = mbstowcs(command_ps, src, maxsize - 1);
+ if (len < 0)
+ len = 0;
+ command_ps[len] = L'\0';
+ return len;
+}
+static size_t save_string(char *dst, int maxsize)
+{
+ ssize_t len = wcstombs(dst, command_ps, maxsize - 1);
+ if (len < 0)
+ len = 0;
+ dst[len] = '\0';
+ return len;
+}
+/* I thought just fputwc(c, stdout) would work. But no... */
+static void BB_PUTCHAR(wchar_t c)
+{
+ char buf[MB_CUR_MAX + 1];
+ mbstate_t mbst = { 0 };
+ ssize_t len = wcrtomb(buf, c, &mbst);
+
+ if (len > 0) {
+ buf[len] = '\0';
+ fputs(buf, stdout);
+ }
+}
+#else
+static size_t load_string(const char *src, int maxsize)
+{
+ safe_strncpy(command_ps, src, maxsize);
+ return strlen(command_ps);
+}
+# if ENABLE_FEATURE_TAB_COMPLETION
+static void save_string(char *dst, int maxsize)
+{
+ safe_strncpy(dst, command_ps, maxsize);
+}
+# endif
+# define BB_PUTCHAR(c) bb_putchar(c)
+#endif
+
+
/* Put 'command_ps[cursor]', cursor++.
* Advance cursor on screen. If we reached right margin, scroll text up
* and remove terminal margin effect by printing 'next_char' */
static void cmdedit_set_out_char(int next_char)
#endif
{
- int c = (unsigned char)command_ps[cursor];
+ CHAR_T c = command_ps[cursor];
- if (c == '\0') {
+ if (c == BB_NUL) {
/* erase character after end of input string */
c = ' ';
}
#if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT
/* Display non-printable characters in reverse */
- if (!Isprint(c)) {
+ if (!BB_isprint(c)) {
if (c >= 128)
c -= 128;
if (c < ' ')
} else
#endif
{
- bb_putchar(c);
+ BB_PUTCHAR(c);
}
if (++cmdedit_x >= cmdedit_termw) {
/* terminal is scrolled down */
bb_putchar('\b');
#endif
}
-// Huh? What if command_ps[cursor] == '\0' (we are at the end already?)
+// Huh? What if command_ps[cursor] == BB_NUL (we are at the end already?)
cursor++;
}
/* draw prompt, editor line, and clear tail */
static void redraw(int y, int back_cursor)
{
- if (y > 0) /* up to start y */
- printf("\033[%dA", y);
+ if (y > 0) /* up to start y */
+ printf("\033[%uA", y);
bb_putchar('\r');
put_prompt();
- input_end(); /* rewrite */
- printf("\033[J"); /* erase after cursor */
+ input_end(); /* rewrite */
+ printf("\033[J"); /* erase after cursor */
input_backward(back_cursor);
}
}
#endif
- overlapping_strcpy(command_ps + j, command_ps + j + 1);
+ memmove(command_ps + j, command_ps + j + 1,
+ /* (command_len + 1 [because of NUL]) - (j + 1)
+ * simplified into (command_len - j) */
+ (command_len - j) * sizeof(command_ps[0]));
command_len--;
input_end(); /* rewrite new line */
cmdedit_set_out_char(' '); /* erase char */
return;
ocursor = cursor;
/* open hole and then fill it */
- memmove(command_ps + cursor + j, command_ps + cursor, command_len - cursor + 1);
- strncpy(command_ps + cursor, delbuf, j);
+ memmove(command_ps + cursor + j, command_ps + cursor,
+ (command_len - cursor + 1) * sizeof(command_ps[0]));
+ memcpy(command_ps + cursor, delbuf, j * sizeof(command_ps[0]));
command_len += j;
input_end(); /* rewrite new line */
input_backward(cursor - ocursor - j + 1); /* at end of new text */
#undef dirbuf
}
+/* QUOT is used on elements of int_buf[], which are bytes,
+ * not Unicode chars. Therefore it works correctly even in Unicode mode.
+ */
#define QUOT (UCHAR_MAX+1)
-#define collapse_pos(is, in) do { \
- memmove(int_buf+(is), int_buf+(in), (MAX_LINELEN+1-(is)-(in)) * sizeof(pos_buf[0])); \
- memmove(pos_buf+(is), pos_buf+(in), (MAX_LINELEN+1-(is)-(in)) * sizeof(pos_buf[0])); \
-} while (0)
-
-static int find_match(char *matchBuf, int *len_with_quotes)
+#define int_buf (S.find_match__int_buf)
+#define pos_buf (S.find_match__pos_buf)
+/* is must be <= in */
+static void collapse_pos(int is, int in)
+{
+ memmove(int_buf+is, int_buf+in, (MAX_LINELEN+1-in)*sizeof(int_buf[0]));
+ memmove(pos_buf+is, pos_buf+in, (MAX_LINELEN+1-in)*sizeof(pos_buf[0]));
+}
+static NOINLINE int find_match(char *matchBuf, int *len_with_quotes)
{
int i, j;
int command_mode;
int c, c2;
+/* Were local, but it uses too much stack */
/* int16_t int_buf[MAX_LINELEN + 1]; */
/* int16_t pos_buf[MAX_LINELEN + 1]; */
-#define int_buf (S.find_match__int_buf)
-#define pos_buf (S.find_match__pos_buf)
/* set to integer dimension characters and own positions */
for (i = 0;; i++) {
int_buf[i] = (unsigned char)matchBuf[i];
if (int_buf[i] == 0) {
- pos_buf[i] = -1; /* indicator end line */
+ pos_buf[i] = -1; /* end-fo-line indicator */
break;
}
pos_buf[i] = i;
int_buf[j] |= QUOT;
i++;
#if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT
- if (matchBuf[i] == '\t') /* algorithm equivalent */
+ if (matchBuf[i] == '\t') /* algorithm equivalent */
int_buf[j] = ' ' | QUOT;
#endif
}
}
if (command_mode) {
collapse_pos(0, i + command_mode);
- i = -1; /* hack incremet */
+ i = -1; /* hack incremet */
}
}
/* collapse `command...` */
- for (i = 0; int_buf[i]; i++)
+ for (i = 0; int_buf[i]; i++) {
if (int_buf[i] == '`') {
for (j = i + 1; int_buf[j]; j++)
if (int_buf[j] == '`') {
break;
}
if (j) {
- /* not found close ` - command mode, collapse all previous */
+ /* not found closing ` - command mode, collapse all previous */
collapse_pos(0, i + 1);
break;
} else
- i--; /* hack incremet */
+ i--; /* hack incremet */
}
+ }
/* collapse (command...(command...)...) or {command...{command...}...} */
- c = 0; /* "recursive" level */
+ c = 0; /* "recursive" level */
c2 = 0;
- for (i = 0; int_buf[i]; i++)
+ for (i = 0; int_buf[i]; i++) {
if (int_buf[i] == '(' || int_buf[i] == '{') {
if (int_buf[i] == '(')
c++;
else
c2++;
collapse_pos(0, i + 1);
- i = -1; /* hack incremet */
+ i = -1; /* hack incremet */
}
- for (i = 0; pos_buf[i] >= 0 && (c > 0 || c2 > 0); i++)
+ }
+ for (i = 0; pos_buf[i] >= 0 && (c > 0 || c2 > 0); i++) {
if ((int_buf[i] == ')' && c > 0) || (int_buf[i] == '}' && c2 > 0)) {
if (int_buf[i] == ')')
c--;
else
c2--;
collapse_pos(0, i + 1);
- i = -1; /* hack incremet */
+ i = -1; /* hack incremet */
}
+ }
/* skip first not quote space */
for (i = 0; int_buf[i]; i++)
/* set find mode for completion */
command_mode = FIND_EXE_ONLY;
- for (i = 0; int_buf[i]; i++)
+ for (i = 0; int_buf[i]; i++) {
if (int_buf[i] == ' ' || int_buf[i] == '<' || int_buf[i] == '>') {
if (int_buf[i] == ' ' && command_mode == FIND_EXE_ONLY
&& matchBuf[pos_buf[0]] == 'c'
break;
}
}
+ }
for (i = 0; int_buf[i]; i++)
/* "strlen" */;
/* find last word */
*len_with_quotes = j ? j - pos_buf[0] : 0;
return command_mode;
+}
#undef int_buf
#undef pos_buf
-}
/*
* display by column (original idea from ls applet,
int nrows = nfiles;
int l;
- /* find the longest file name- use that as the column width */
+ /* find the longest file name - use that as the column width */
for (row = 0; row < nrows; row++) {
- l = strlen(matches[row]);
+ l = bb_mbstrlen(matches[row]);
if (column_width < l)
column_width = l;
}
for (nc = 1; nc < ncols && n+nrows < nfiles; n += nrows, nc++) {
printf("%s%-*s", matches[n],
- (int)(column_width - strlen(matches[n])), "");
+ (int)(column_width - bb_mbstrlen(matches[n])), ""
+ );
}
puts(matches[n]);
}
static char *add_quote_for_spec_chars(char *found)
{
int l = 0;
- char *s = xmalloc((strlen(found) + 1) * 2);
+ char *s = xzalloc((strlen(found) + 1) * 2);
while (*found) {
- if (strchr(" `\"#$%^&*()=+{}[]:;\'|\\<>", *found))
+ if (strchr(" `\"#$%^&*()=+{}[]:;'|\\<>", *found))
s[l++] = '\\';
s[l++] = *found++;
}
- s[l] = 0;
+ /* s[l] = '\0'; - already is */
return s;
}
#define matchBuf (S.input_tab__matchBuf)
int find_type;
int recalc_pos;
+#if ENABLE_FEATURE_ASSUME_UNICODE
+ /* cursor pos in command converted to multibyte form */
+ int cursor_mb;
+#endif
*lastWasTab = TRUE; /* flop trigger */
- /* Make a local copy of the string -- up
- * to the position of the cursor */
- tmp = strncpy(matchBuf, command_ps, cursor);
- tmp[cursor] = '\0';
+ /* Make a local copy of the string --
+ * up to the position of the cursor */
+ save_string(matchBuf, cursor + 1);
+#if ENABLE_FEATURE_ASSUME_UNICODE
+ cursor_mb = strlen(matchBuf);
+#endif
+ tmp = matchBuf;
find_type = find_match(matchBuf, &recalc_pos);
/* If the word starts with `~' and there is no slash in the word,
* then try completing this word as a username. */
if (state->flags & USERNAME_COMPLETION)
- if (matchBuf[0] == '~' && strchr(matchBuf, '/') == 0)
+ if (matchBuf[0] == '~' && strchr(matchBuf, '/') == NULL)
username_tab_completion(matchBuf, NULL);
#endif
/* Try to match any executable in our path and everything
num_matches = n + 1;
}
/* Did we find exactly one match? */
- if (!matches || num_matches > 1) {
+ if (!matches || num_matches > 1) { /* no */
beep();
if (!matches)
return; /* not found */
/* find minimal match */
tmp1 = xstrdup(matches[0]);
- for (tmp = tmp1; *tmp; tmp++)
- for (len_found = 1; len_found < num_matches; len_found++)
- if (matches[len_found][(tmp - tmp1)] != *tmp) {
+ for (tmp = tmp1; *tmp; tmp++) {
+ for (len_found = 1; len_found < num_matches; len_found++) {
+ if (matches[len_found][tmp - tmp1] != *tmp) {
*tmp = '\0';
break;
}
+ }
+ }
if (*tmp1 == '\0') { /* have unique */
free(tmp1);
return;
tmp[len_found+1] = '\0';
}
}
+
len_found = strlen(tmp);
- /* have space to placed match? */
- if ((len_found - strlen(matchBuf) + command_len) < MAX_LINELEN) {
- /* before word for match */
- command_ps[cursor - recalc_pos] = '\0';
- /* save tail line */
+#if !ENABLE_FEATURE_ASSUME_UNICODE
+ /* have space to place the match? */
+ /* The result consists of three parts with these lengths: */
+ /* (cursor - recalc_pos) + len_found + (command_len - cursor) */
+ /* it simplifies into: */
+ if ((int)(len_found + command_len - recalc_pos) < S.maxsize) {
+ /* save tail */
strcpy(matchBuf, command_ps + cursor);
- /* add match */
- strcat(command_ps, tmp);
- /* add tail */
- strcat(command_ps, matchBuf);
- /* back to begin word for match */
- input_backward(recalc_pos);
- /* new pos */
- recalc_pos = cursor + len_found;
- /* new len */
+ /* add match and tail */
+ sprintf(&command_ps[cursor - recalc_pos], "%s%s", tmp, matchBuf);
command_len = strlen(command_ps);
- /* write out the matched command */
+ /* new pos */
+ recalc_pos = cursor - recalc_pos + len_found;
+ /* write out the matched command */
redraw(cmdedit_y, command_len - recalc_pos);
}
+#else
+ {
+ char command[MAX_LINELEN];
+ int len = save_string(command, sizeof(command));
+ /* have space to place the match? */
+ /* (cursor_mb - recalc_pos) + len_found + (len - cursor_mb) */
+ if ((int)(len_found + len - recalc_pos) < MAX_LINELEN) {
+ /* save tail */
+ strcpy(matchBuf, command + cursor_mb);
+ /* where do we want to have cursor after all? */
+ strcpy(&command[cursor_mb - recalc_pos], tmp);
+ len = load_string(command, S.maxsize);
+ /* add match and tail */
+ sprintf(&command[cursor_mb - recalc_pos], "%s%s", tmp, matchBuf);
+ command_len = load_string(command, S.maxsize);
+ /* write out the matched command */
+ redraw(cmdedit_y, command_len - len);
+ }
+ }
+#endif
free(tmp);
#undef matchBuf
} else {
* just hit TAB again, print a list of all the
* available choices... */
if (matches && num_matches > 0) {
- int sav_cursor = cursor; /* change goto_new_line() */
+ /* changed by goto_new_line() */
+ int sav_cursor = cursor;
/* Go to the next line */
goto_new_line();
static void save_command_ps_at_cur_history(void)
{
- if (command_ps[0] != '\0') {
+ if (command_ps[0] != BB_NUL) {
int cur = state->cur_history;
free(state->history[cur]);
+
+#if ENABLE_FEATURE_ASSUME_UNICODE
+ {
+ char tbuf[MAX_LINELEN];
+ save_string(tbuf, sizeof(tbuf));
+ state->history[cur] = xstrdup(tbuf);
+ }
+#else
state->history[cur] = xstrdup(command_ps);
+#endif
}
}
#if ENABLE_FEATURE_EDITING_VI
static void
-vi_Word_motion(char *command, int eat)
+vi_Word_motion(int eat)
{
- while (cursor < command_len && !isspace(command[cursor]))
+ CHAR_T *command = command_ps;
+
+ while (cursor < command_len && !BB_isspace(command[cursor]))
input_forward();
- if (eat) while (cursor < command_len && isspace(command[cursor]))
+ if (eat) while (cursor < command_len && BB_isspace(command[cursor]))
input_forward();
}
static void
-vi_word_motion(char *command, int eat)
+vi_word_motion(int eat)
{
- if (isalnum(command[cursor]) || command[cursor] == '_') {
+ CHAR_T *command = command_ps;
+
+ if (BB_isalnum(command[cursor]) || command[cursor] == '_') {
while (cursor < command_len
- && (isalnum(command[cursor+1]) || command[cursor+1] == '_'))
+ && (BB_isalnum(command[cursor+1]) || command[cursor+1] == '_')
+ ) {
input_forward();
- } else if (ispunct(command[cursor])) {
- while (cursor < command_len && ispunct(command[cursor+1]))
+ }
+ } else if (BB_ispunct(command[cursor])) {
+ while (cursor < command_len && BB_ispunct(command[cursor+1]))
input_forward();
}
if (cursor < command_len)
input_forward();
- if (eat && cursor < command_len && isspace(command[cursor]))
- while (cursor < command_len && isspace(command[cursor]))
+ if (eat) {
+ while (cursor < command_len && BB_isspace(command[cursor]))
input_forward();
+ }
}
static void
-vi_End_motion(char *command)
+vi_End_motion(void)
{
+ CHAR_T *command = command_ps;
+
input_forward();
- while (cursor < command_len && isspace(command[cursor]))
+ while (cursor < command_len && BB_isspace(command[cursor]))
input_forward();
- while (cursor < command_len-1 && !isspace(command[cursor+1]))
+ while (cursor < command_len-1 && !BB_isspace(command[cursor+1]))
input_forward();
}
static void
-vi_end_motion(char *command)
+vi_end_motion(void)
{
+ CHAR_T *command = command_ps;
+
if (cursor >= command_len-1)
return;
input_forward();
- while (cursor < command_len-1 && isspace(command[cursor]))
+ while (cursor < command_len-1 && BB_isspace(command[cursor]))
input_forward();
if (cursor >= command_len-1)
return;
- if (isalnum(command[cursor]) || command[cursor] == '_') {
+ if (BB_isalnum(command[cursor]) || command[cursor] == '_') {
while (cursor < command_len-1
- && (isalnum(command[cursor+1]) || command[cursor+1] == '_')
+ && (BB_isalnum(command[cursor+1]) || command[cursor+1] == '_')
) {
input_forward();
}
- } else if (ispunct(command[cursor])) {
- while (cursor < command_len-1 && ispunct(command[cursor+1]))
+ } else if (BB_ispunct(command[cursor])) {
+ while (cursor < command_len-1 && BB_ispunct(command[cursor+1]))
input_forward();
}
}
static void
-vi_Back_motion(char *command)
+vi_Back_motion(void)
{
- while (cursor > 0 && isspace(command[cursor-1]))
+ CHAR_T *command = command_ps;
+
+ while (cursor > 0 && BB_isspace(command[cursor-1]))
input_backward(1);
- while (cursor > 0 && !isspace(command[cursor-1]))
+ while (cursor > 0 && !BB_isspace(command[cursor-1]))
input_backward(1);
}
static void
-vi_back_motion(char *command)
+vi_back_motion(void)
{
+ CHAR_T *command = command_ps;
+
if (cursor <= 0)
return;
input_backward(1);
- while (cursor > 0 && isspace(command[cursor]))
+ while (cursor > 0 && BB_isspace(command[cursor]))
input_backward(1);
if (cursor <= 0)
return;
- if (isalnum(command[cursor]) || command[cursor] == '_') {
+ if (BB_isalnum(command[cursor]) || command[cursor] == '_') {
while (cursor > 0
- && (isalnum(command[cursor-1]) || command[cursor-1] == '_')
+ && (BB_isalnum(command[cursor-1]) || command[cursor-1] == '_')
) {
input_backward(1);
}
- } else if (ispunct(command[cursor])) {
- while (cursor > 0 && ispunct(command[cursor-1]))
+ } else if (BB_ispunct(command[cursor])) {
+ while (cursor > 0 && BB_ispunct(command[cursor-1]))
input_backward(1);
}
}
{
int64_t ic;
struct pollfd pfd;
+ int delay = -1;
+#if ENABLE_FEATURE_ASSUME_UNICODE
+ char unicode_buf[MB_CUR_MAX + 1];
+ int unicode_idx = 0;
+#endif
pfd.fd = STDIN_FILENO;
pfd.events = POLLIN;
/* Wait for input. Can't just call read_key,
* it returns at once if stdin
* is in non-blocking mode. */
- safe_poll(&pfd, 1, -1);
+ safe_poll(&pfd, 1, delay);
}
/* Note: read_key sets errno to 0 on success: */
ic = read_key(STDIN_FILENO, read_key_buffer);
}
goto poll_again;
}
+
+#if ENABLE_FEATURE_ASSUME_UNICODE
+ {
+ wchar_t wc;
+
+ if ((int32_t)ic < 0) /* KEYCODE_xxx */
+ return ic;
+ unicode_buf[unicode_idx++] = ic;
+ unicode_buf[unicode_idx] = '\0';
+ if (mbstowcs(&wc, unicode_buf, 1) != 1 && unicode_idx < MB_CUR_MAX) {
+ delay = 50;
+ goto poll_again;
+ }
+ ic = wc;
+ }
+#endif
} while (errno == EAGAIN);
+
return ic;
}
-/*
- * The emacs and vi modes share much of the code in the big
- * command loop. Commands entered when in vi's command mode (aka
- * "escape mode") get an extra bit added to distinguish them --
- * this keeps them from being self-inserted. This clutters the
- * big switch a bit, but keeps all the code in one place.
- */
-
-#define VI_CMDMODE_BIT 0x100
-
/* leave out the "vi-mode"-only case labels if vi editing isn't
* configured. */
-#define vi_case(caselabel) IF_FEATURE_EDITING(case caselabel)
+#define vi_case(caselabel) IF_FEATURE_EDITING_VI(case caselabel)
/* convert uppercase ascii to equivalent control char, for readability */
#undef CTRL
#define CTRL(a) ((a) & ~0x40)
-/* Returns:
+/* maxsize must be >= 2.
+ * Returns:
* -1 on read errors or EOF, or on bare Ctrl-D,
* 0 on ctrl-C (the line entered is still returned in 'command'),
* >0 length of input string, including terminating '\n'
#if ENABLE_FEATURE_TAB_COMPLETION
smallint lastWasTab = FALSE;
#endif
- int ic;
smallint break_out = 0;
#if ENABLE_FEATURE_EDITING_VI
smallint vi_cmdmode = 0;
return len;
}
+ check_unicode_in_env();
+
// FIXME: audit & improve this
if (maxsize > MAX_LINELEN)
maxsize = MAX_LINELEN;
+ S.maxsize = maxsize;
/* With null flags, no other fields are ever used */
state = st ? st : (line_input_t*) &const_int_0;
/* prepare before init handlers */
cmdedit_y = 0; /* quasireal y, not true if line > xt*yt */
command_len = 0;
+#if ENABLE_FEATURE_ASSUME_UNICODE
+ command_ps = xzalloc(maxsize * sizeof(command_ps[0]));
+#else
command_ps = command;
command[0] = '\0';
+#endif
+#define command command_must_not_be_used
new_settings = initial_settings;
new_settings.c_lflag &= ~ICANON; /* unbuffered input */
#endif
#if 0
- for (ic = 0; ic <= MAX_HISTORY; ic++)
- bb_error_msg("history[%d]:'%s'", ic, state->history[ic]);
+ for (i = 0; i <= MAX_HISTORY; i++)
+ bb_error_msg("history[%d]:'%s'", i, state->history[i]);
bb_error_msg("cur_history:%d cnt_history:%d", state->cur_history, state->cnt_history);
#endif
read_key_buffer[0] = 0;
while (1) {
+ /*
+ * The emacs and vi modes share much of the code in the big
+ * command loop. Commands entered when in vi's command mode
+ * (aka "escape mode") get an extra bit added to distinguish
+ * them - this keeps them from being self-inserted. This
+ * clutters the big switch a bit, but keeps all the code
+ * in one place.
+ */
+ enum {
+ VI_CMDMODE_BIT = 0x40000000,
+ /* 0x80000000 bit flags KEYCODE_xxx */
+ };
+ int32_t ic;
+
fflush(NULL);
ic = lineedit_read_key(read_key_buffer);
* if the len=0 and no chars to delete */
if (command_len == 0) {
errno = 0;
+#if ENABLE_FEATURE_EDITING_VI
prepare_to_die:
+#endif
/* to control stopped jobs */
break_out = command_len = -1;
break;
#endif
case CTRL('K'):
/* Control-k -- clear to end of line */
- command[cursor] = 0;
+ command_ps[cursor] = BB_NUL;
command_len = cursor;
printf("\033[J");
break;
vi_case(CTRL('U')|VI_CMDMODE_BIT:)
/* Control-U -- Clear line before cursor */
if (cursor) {
- overlapping_strcpy(command, command + cursor);
command_len -= cursor;
+ memmove(command_ps, command_ps + cursor,
+ (command_len + 1) * sizeof(command_ps[0]));
redraw(cmdedit_y, command_len);
}
break;
case CTRL('W'):
vi_case(CTRL('W')|VI_CMDMODE_BIT:)
/* Control-W -- Remove the last word */
- while (cursor > 0 && isspace(command[cursor-1]))
+ while (cursor > 0 && BB_isspace(command_ps[cursor-1]))
input_backspace();
- while (cursor > 0 && !isspace(command[cursor-1]))
+ while (cursor > 0 && !BB_isspace(command_ps[cursor-1]))
input_backspace();
break;
}
break;
case 'W'|VI_CMDMODE_BIT:
- vi_Word_motion(command, 1);
+ vi_Word_motion(1);
break;
case 'w'|VI_CMDMODE_BIT:
- vi_word_motion(command, 1);
+ vi_word_motion(1);
break;
case 'E'|VI_CMDMODE_BIT:
- vi_End_motion(command);
+ vi_End_motion();
break;
case 'e'|VI_CMDMODE_BIT:
- vi_end_motion(command);
+ vi_end_motion();
break;
case 'B'|VI_CMDMODE_BIT:
- vi_Back_motion(command);
+ vi_Back_motion();
break;
case 'b'|VI_CMDMODE_BIT:
- vi_back_motion(command);
+ vi_back_motion();
break;
case 'C'|VI_CMDMODE_BIT:
vi_cmdmode = 0;
case 'E':
switch (ic) {
case 'w': /* "dw", "cw" */
- vi_word_motion(command, vi_cmdmode);
+ vi_word_motion(vi_cmdmode);
break;
case 'W': /* 'dW', 'cW' */
- vi_Word_motion(command, vi_cmdmode);
+ vi_Word_motion(vi_cmdmode);
break;
case 'e': /* 'de', 'ce' */
- vi_end_motion(command);
+ vi_end_motion();
input_forward();
break;
case 'E': /* 'dE', 'cE' */
- vi_End_motion(command);
+ vi_End_motion();
input_forward();
break;
}
case 'b': /* "db", "cb" */
case 'B': /* implemented as B */
if (ic == 'b')
- vi_back_motion(command);
+ vi_back_motion();
else
- vi_Back_motion(command);
+ vi_Back_motion();
while (sc-- > cursor)
input_delete(1);
break;
if (ic < ' ' || ic > 255) {
beep();
} else {
- command[cursor] = ic;
+ command_ps[cursor] = ic;
bb_putchar(ic);
bb_putchar('\b');
}
rewrite_line:
/* Rewrite the line with the selected history item */
/* change command */
- command_len = strlen(strcpy(command, state->history[state->cur_history] ? : ""));
+ command_len = load_string(state->history[state->cur_history] ? : "", maxsize);
/* redraw and go to eol (bol, in vi) */
redraw(cmdedit_y, (state->flags & VI_MODE) ? 9999 : 0);
break;
// break;
// }
// }
- if (ic < ' ' || ic > 255) {
+ if (ic < ' '
+ || (!ENABLE_FEATURE_ASSUME_UNICODE && ic >= 256)
+ || (ENABLE_FEATURE_ASSUME_UNICODE && ic >= VI_CMDMODE_BIT)
+ ) {
/* If VI_CMDMODE_BIT is set, ic is >= 256
* and command mode ignores unexpected chars.
* Otherwise, we are here if ic is a
command_len++;
if (cursor == (command_len - 1)) {
/* We are at the end, append */
- command[cursor] = ic;
- command[cursor + 1] = '\0';
+ command_ps[cursor] = ic;
+ command_ps[cursor + 1] = BB_NUL;
cmdedit_set_out_char(' ');
} else {
/* In the middle, insert */
int sc = cursor;
- memmove(command + sc + 1, command + sc, command_len - sc);
- command[sc] = ic;
+ memmove(command_ps + sc + 1, command_ps + sc,
+ (command_len - sc) * sizeof(command_ps[0]));
+ command_ps[sc] = ic;
sc++;
/* rewrite from cursor */
input_end();
#endif
} /* while (1) */
+/* Stop bug catching using "command_must_not_be_used" trick */
+#undef command
+
+#if ENABLE_FEATURE_ASSUME_UNICODE
+ command_len = save_string(command, maxsize - 1);
+ free(command_ps);
+#endif
+
if (command_len > 0)
remember_in_history(command);
return strlen(command);
}
-#endif /* FEATURE_COMMAND_EDITING */
+#endif /* FEATURE_EDITING */
/*