16 |
|
|
17 |
/* |
/* |
18 |
* Usage and known bugs: |
* Usage and known bugs: |
19 |
* Terminal key codes are not extensive, and more will probably |
* Terminal key codes are not extensive, more needs to be added. |
20 |
* need to be added. This version was created on Debian GNU/Linux 2.x. |
* This version was created on Debian GNU/Linux 2.x. |
21 |
* Delete, Backspace, Home, End, and the arrow keys were tested |
* Delete, Backspace, Home, End, and the arrow keys were tested |
22 |
* to work in an Xterm and console. Ctrl-A also works as Home. |
* to work in an Xterm and console. Ctrl-A also works as Home. |
23 |
* Ctrl-E also works as End. |
* Ctrl-E also works as End. |
24 |
* |
* |
25 |
|
* The following readline-like commands are not implemented: |
26 |
|
* ESC-b -- Move back one word |
27 |
|
* ESC-f -- Move forward one word |
28 |
|
* ESC-d -- Delete forward one word |
29 |
|
* CTL-t -- Transpose two characters |
30 |
|
* |
31 |
* lineedit does not know that the terminal escape sequences do not |
* lineedit does not know that the terminal escape sequences do not |
32 |
* take up space on the screen. The redisplay code assumes, unless |
* take up space on the screen. The redisplay code assumes, unless |
33 |
* told otherwise, that each character in the prompt is a printable |
* told otherwise, that each character in the prompt is a printable |
39 |
* |
* |
40 |
* PS1='\[\033[01;32m\]\u@\h\[\033[01;34m\] \w \$\[\033[00m\] ' |
* PS1='\[\033[01;32m\]\u@\h\[\033[01;34m\] \w \$\[\033[00m\] ' |
41 |
*/ |
*/ |
|
|
|
42 |
#include "libbb.h" |
#include "libbb.h" |
43 |
|
#include "unicode.h" |
44 |
|
|
45 |
/* FIXME: obsolete CONFIG item? */ |
/* FIXME: obsolete CONFIG item? */ |
46 |
#define ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT 0 |
#define ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT 0 |
47 |
|
|
|
|
|
48 |
#ifdef TEST |
#ifdef TEST |
49 |
|
# define ENABLE_FEATURE_EDITING 0 |
50 |
#define ENABLE_FEATURE_EDITING 0 |
# define ENABLE_FEATURE_TAB_COMPLETION 0 |
51 |
#define ENABLE_FEATURE_TAB_COMPLETION 0 |
# define ENABLE_FEATURE_USERNAME_COMPLETION 0 |
52 |
#define ENABLE_FEATURE_USERNAME_COMPLETION 0 |
# define ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT 0 |
53 |
#define ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT 0 |
#endif |
|
|
|
|
#endif /* TEST */ |
|
54 |
|
|
55 |
|
|
56 |
/* Entire file (except TESTing part) sits inside this #if */ |
/* Entire file (except TESTing part) sits inside this #if */ |
57 |
#if ENABLE_FEATURE_EDITING |
#if ENABLE_FEATURE_EDITING |
58 |
|
|
|
#if ENABLE_LOCALE_SUPPORT |
|
|
#define Isprint(c) isprint(c) |
|
|
#else |
|
|
#define Isprint(c) ((c) >= ' ' && (c) != ((unsigned char)'\233')) |
|
|
#endif |
|
59 |
|
|
60 |
#define ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR \ |
#define ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR \ |
61 |
(ENABLE_FEATURE_USERNAME_COMPLETION || ENABLE_FEATURE_EDITING_FANCY_PROMPT) |
(ENABLE_FEATURE_USERNAME_COMPLETION || ENABLE_FEATURE_EDITING_FANCY_PROMPT) |
62 |
#define USE_FEATURE_GETUSERNAME_AND_HOMEDIR(...) |
#define IF_FEATURE_GETUSERNAME_AND_HOMEDIR(...) |
63 |
#if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR |
#if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR |
64 |
#undef USE_FEATURE_GETUSERNAME_AND_HOMEDIR |
#undef IF_FEATURE_GETUSERNAME_AND_HOMEDIR |
65 |
#define USE_FEATURE_GETUSERNAME_AND_HOMEDIR(...) __VA_ARGS__ |
#define IF_FEATURE_GETUSERNAME_AND_HOMEDIR(...) __VA_ARGS__ |
66 |
#endif |
#endif |
67 |
|
|
68 |
|
|
69 |
|
#undef CHAR_T |
70 |
|
#if ENABLE_FEATURE_ASSUME_UNICODE |
71 |
|
# define BB_NUL L'\0' |
72 |
|
# define CHAR_T wchar_t |
73 |
|
static bool BB_isspace(CHAR_T c) { return ((unsigned)c < 256 && isspace(c)); } |
74 |
|
# if ENABLE_FEATURE_EDITING_VI |
75 |
|
static bool BB_isalnum(CHAR_T c) { return ((unsigned)c < 256 && isalnum(c)); } |
76 |
|
# endif |
77 |
|
static bool BB_ispunct(CHAR_T c) { return ((unsigned)c < 256 && ispunct(c)); } |
78 |
|
# undef isspace |
79 |
|
# undef isalnum |
80 |
|
# undef ispunct |
81 |
|
# undef isprint |
82 |
|
# define isspace isspace_must_not_be_used |
83 |
|
# define isalnum isalnum_must_not_be_used |
84 |
|
# define ispunct ispunct_must_not_be_used |
85 |
|
# define isprint isprint_must_not_be_used |
86 |
|
#else |
87 |
|
# define BB_NUL '\0' |
88 |
|
# define CHAR_T char |
89 |
|
# define BB_isspace(c) isspace(c) |
90 |
|
# define BB_isalnum(c) isalnum(c) |
91 |
|
# define BB_ispunct(c) ispunct(c) |
92 |
|
#endif |
93 |
|
|
94 |
|
|
95 |
enum { |
enum { |
96 |
/* We use int16_t for positions, need to limit line len */ |
/* We use int16_t for positions, need to limit line len */ |
97 |
MAX_LINELEN = CONFIG_FEATURE_EDITING_MAX_LEN < 0x7ff0 |
MAX_LINELEN = CONFIG_FEATURE_EDITING_MAX_LEN < 0x7ff0 |
110 |
volatile unsigned cmdedit_termw; /* = 80; */ /* actual terminal width */ |
volatile unsigned cmdedit_termw; /* = 80; */ /* actual terminal width */ |
111 |
sighandler_t previous_SIGWINCH_handler; |
sighandler_t previous_SIGWINCH_handler; |
112 |
|
|
113 |
unsigned cmdedit_x; /* real x terminal position */ |
unsigned cmdedit_x; /* real x (col) terminal position */ |
114 |
unsigned cmdedit_y; /* pseudoreal y terminal position */ |
unsigned cmdedit_y; /* pseudoreal y (row) terminal position */ |
115 |
unsigned cmdedit_prmt_len; /* length of prompt (without colors etc) */ |
unsigned cmdedit_prmt_len; /* length of prompt (without colors etc) */ |
116 |
|
|
117 |
unsigned cursor; |
unsigned cursor; |
118 |
unsigned command_len; |
int command_len; /* must be signed */ |
119 |
char *command_ps; |
/* signed maxsize: we want x in "if (x > S.maxsize)" |
120 |
|
* to _not_ be promoted to unsigned */ |
121 |
|
int maxsize; |
122 |
|
CHAR_T *command_ps; |
123 |
|
|
124 |
const char *cmdedit_prompt; |
const char *cmdedit_prompt; |
125 |
#if ENABLE_FEATURE_EDITING_FANCY_PROMPT |
#if ENABLE_FEATURE_EDITING_FANCY_PROMPT |
138 |
|
|
139 |
#if ENABLE_FEATURE_EDITING_VI |
#if ENABLE_FEATURE_EDITING_VI |
140 |
#define DELBUFSIZ 128 |
#define DELBUFSIZ 128 |
141 |
char *delptr; |
CHAR_T *delptr; |
142 |
smallint newdelflag; /* whether delbuf should be reused yet */ |
smallint newdelflag; /* whether delbuf should be reused yet */ |
143 |
char delbuf[DELBUFSIZ]; /* a place to store deleted characters */ |
CHAR_T delbuf[DELBUFSIZ]; /* a place to store deleted characters */ |
144 |
|
#endif |
145 |
|
#if ENABLE_FEATURE_EDITING_ASK_TERMINAL |
146 |
|
smallint sent_ESC_br6n; |
147 |
#endif |
#endif |
148 |
|
|
149 |
/* Formerly these were big buffers on stack: */ |
/* Formerly these were big buffers on stack: */ |
182 |
(*(struct lineedit_statics**)&lineedit_ptr_to_statics) = xzalloc(sizeof(S)); \ |
(*(struct lineedit_statics**)&lineedit_ptr_to_statics) = xzalloc(sizeof(S)); \ |
183 |
barrier(); \ |
barrier(); \ |
184 |
cmdedit_termw = 80; \ |
cmdedit_termw = 80; \ |
185 |
USE_FEATURE_EDITING_FANCY_PROMPT(num_ok_lines = 1;) \ |
IF_FEATURE_EDITING_FANCY_PROMPT(num_ok_lines = 1;) \ |
186 |
USE_FEATURE_GETUSERNAME_AND_HOMEDIR(home_pwd_buf = (char*)null_str;) \ |
IF_FEATURE_GETUSERNAME_AND_HOMEDIR(home_pwd_buf = (char*)null_str;) \ |
187 |
} while (0) |
} while (0) |
188 |
static void deinit_S(void) |
static void deinit_S(void) |
189 |
{ |
{ |
202 |
#define DEINIT_S() deinit_S() |
#define DEINIT_S() deinit_S() |
203 |
|
|
204 |
|
|
205 |
|
#if ENABLE_FEATURE_ASSUME_UNICODE |
206 |
|
static size_t load_string(const char *src, int maxsize) |
207 |
|
{ |
208 |
|
ssize_t len = mbstowcs(command_ps, src, maxsize - 1); |
209 |
|
if (len < 0) |
210 |
|
len = 0; |
211 |
|
command_ps[len] = L'\0'; |
212 |
|
return len; |
213 |
|
} |
214 |
|
static size_t save_string(char *dst, int maxsize) |
215 |
|
{ |
216 |
|
ssize_t len = wcstombs(dst, command_ps, maxsize - 1); |
217 |
|
if (len < 0) |
218 |
|
len = 0; |
219 |
|
dst[len] = '\0'; |
220 |
|
return len; |
221 |
|
} |
222 |
|
/* I thought just fputwc(c, stdout) would work. But no... */ |
223 |
|
static void BB_PUTCHAR(wchar_t c) |
224 |
|
{ |
225 |
|
char buf[MB_CUR_MAX + 1]; |
226 |
|
mbstate_t mbst = { 0 }; |
227 |
|
ssize_t len = wcrtomb(buf, c, &mbst); |
228 |
|
|
229 |
|
if (len > 0) { |
230 |
|
buf[len] = '\0'; |
231 |
|
fputs(buf, stdout); |
232 |
|
} |
233 |
|
} |
234 |
|
#else |
235 |
|
static size_t load_string(const char *src, int maxsize) |
236 |
|
{ |
237 |
|
safe_strncpy(command_ps, src, maxsize); |
238 |
|
return strlen(command_ps); |
239 |
|
} |
240 |
|
# if ENABLE_FEATURE_TAB_COMPLETION |
241 |
|
static void save_string(char *dst, int maxsize) |
242 |
|
{ |
243 |
|
safe_strncpy(dst, command_ps, maxsize); |
244 |
|
} |
245 |
|
# endif |
246 |
|
# define BB_PUTCHAR(c) bb_putchar(c) |
247 |
|
#endif |
248 |
|
|
249 |
|
|
250 |
/* Put 'command_ps[cursor]', cursor++. |
/* Put 'command_ps[cursor]', cursor++. |
251 |
* Advance cursor on screen. If we reached right margin, scroll text up |
* Advance cursor on screen. If we reached right margin, scroll text up |
252 |
* and remove terminal margin effect by printing 'next_char' */ |
* and remove terminal margin effect by printing 'next_char' */ |
258 |
static void cmdedit_set_out_char(int next_char) |
static void cmdedit_set_out_char(int next_char) |
259 |
#endif |
#endif |
260 |
{ |
{ |
261 |
int c = (unsigned char)command_ps[cursor]; |
CHAR_T c = command_ps[cursor]; |
262 |
|
|
263 |
if (c == '\0') { |
if (c == BB_NUL) { |
264 |
/* erase character after end of input string */ |
/* erase character after end of input string */ |
265 |
c = ' '; |
c = ' '; |
266 |
} |
} |
267 |
#if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT |
#if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT |
268 |
/* Display non-printable characters in reverse */ |
/* Display non-printable characters in reverse */ |
269 |
if (!Isprint(c)) { |
if (!BB_isprint(c)) { |
270 |
if (c >= 128) |
if (c >= 128) |
271 |
c -= 128; |
c -= 128; |
272 |
if (c < ' ') |
if (c < ' ') |
277 |
} else |
} else |
278 |
#endif |
#endif |
279 |
{ |
{ |
280 |
bb_putchar(c); |
BB_PUTCHAR(c); |
281 |
} |
} |
282 |
if (++cmdedit_x >= cmdedit_termw) { |
if (++cmdedit_x >= cmdedit_termw) { |
283 |
/* terminal is scrolled down */ |
/* terminal is scrolled down */ |
299 |
bb_putchar('\b'); |
bb_putchar('\b'); |
300 |
#endif |
#endif |
301 |
} |
} |
302 |
// 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?) |
303 |
cursor++; |
cursor++; |
304 |
} |
} |
305 |
|
|
374 |
|
|
375 |
static void put_prompt(void) |
static void put_prompt(void) |
376 |
{ |
{ |
377 |
|
unsigned w; |
378 |
|
|
379 |
out1str(cmdedit_prompt); |
out1str(cmdedit_prompt); |
380 |
|
fflush_all(); |
381 |
cursor = 0; |
cursor = 0; |
382 |
{ |
w = cmdedit_termw; /* read volatile var once */ |
383 |
unsigned w = cmdedit_termw; /* volatile var */ |
cmdedit_y = cmdedit_prmt_len / w; /* new quasireal y */ |
384 |
cmdedit_y = cmdedit_prmt_len / w; /* new quasireal y */ |
cmdedit_x = cmdedit_prmt_len % w; |
|
cmdedit_x = cmdedit_prmt_len % w; |
|
|
} |
|
385 |
} |
} |
386 |
|
|
387 |
/* draw prompt, editor line, and clear tail */ |
/* draw prompt, editor line, and clear tail */ |
388 |
static void redraw(int y, int back_cursor) |
static void redraw(int y, int back_cursor) |
389 |
{ |
{ |
390 |
if (y > 0) /* up to start y */ |
if (y > 0) /* up to start y */ |
391 |
printf("\033[%dA", y); |
printf("\033[%uA", y); |
392 |
bb_putchar('\r'); |
bb_putchar('\r'); |
393 |
put_prompt(); |
put_prompt(); |
394 |
input_end(); /* rewrite */ |
input_end(); /* rewrite */ |
395 |
printf("\033[J"); /* erase after cursor */ |
printf("\033[J"); /* erase after cursor */ |
396 |
input_backward(back_cursor); |
input_backward(back_cursor); |
397 |
} |
} |
398 |
|
|
421 |
} |
} |
422 |
#endif |
#endif |
423 |
|
|
424 |
overlapping_strcpy(command_ps + j, command_ps + j + 1); |
memmove(command_ps + j, command_ps + j + 1, |
425 |
|
/* (command_len + 1 [because of NUL]) - (j + 1) |
426 |
|
* simplified into (command_len - j) */ |
427 |
|
(command_len - j) * sizeof(command_ps[0])); |
428 |
command_len--; |
command_len--; |
429 |
input_end(); /* rewrite new line */ |
input_end(); /* rewrite new line */ |
430 |
cmdedit_set_out_char(' '); /* erase char */ |
cmdedit_set_out_char(' '); /* erase char */ |
441 |
return; |
return; |
442 |
ocursor = cursor; |
ocursor = cursor; |
443 |
/* open hole and then fill it */ |
/* open hole and then fill it */ |
444 |
memmove(command_ps + cursor + j, command_ps + cursor, command_len - cursor + 1); |
memmove(command_ps + cursor + j, command_ps + cursor, |
445 |
strncpy(command_ps + cursor, delbuf, j); |
(command_len - cursor + 1) * sizeof(command_ps[0])); |
446 |
|
memcpy(command_ps + cursor, delbuf, j * sizeof(command_ps[0])); |
447 |
command_len += j; |
command_len += j; |
448 |
input_end(); /* rewrite new line */ |
input_end(); /* rewrite new line */ |
449 |
input_backward(cursor - ocursor - j + 1); /* at end of new text */ |
input_backward(cursor - ocursor - j + 1); /* at end of new text */ |
680 |
#undef dirbuf |
#undef dirbuf |
681 |
} |
} |
682 |
|
|
683 |
|
/* QUOT is used on elements of int_buf[], which are bytes, |
684 |
|
* not Unicode chars. Therefore it works correctly even in Unicode mode. |
685 |
|
*/ |
686 |
#define QUOT (UCHAR_MAX+1) |
#define QUOT (UCHAR_MAX+1) |
687 |
|
|
688 |
#define collapse_pos(is, in) do { \ |
#define int_buf (S.find_match__int_buf) |
689 |
memmove(int_buf+(is), int_buf+(in), (MAX_LINELEN+1-(is)-(in)) * sizeof(pos_buf[0])); \ |
#define pos_buf (S.find_match__pos_buf) |
690 |
memmove(pos_buf+(is), pos_buf+(in), (MAX_LINELEN+1-(is)-(in)) * sizeof(pos_buf[0])); \ |
/* is must be <= in */ |
691 |
} while (0) |
static void collapse_pos(int is, int in) |
692 |
|
{ |
693 |
static int find_match(char *matchBuf, int *len_with_quotes) |
memmove(int_buf+is, int_buf+in, (MAX_LINELEN+1-in)*sizeof(int_buf[0])); |
694 |
|
memmove(pos_buf+is, pos_buf+in, (MAX_LINELEN+1-in)*sizeof(pos_buf[0])); |
695 |
|
} |
696 |
|
static NOINLINE int find_match(char *matchBuf, int *len_with_quotes) |
697 |
{ |
{ |
698 |
int i, j; |
int i, j; |
699 |
int command_mode; |
int command_mode; |
700 |
int c, c2; |
int c, c2; |
701 |
|
/* Were local, but it uses too much stack */ |
702 |
/* int16_t int_buf[MAX_LINELEN + 1]; */ |
/* int16_t int_buf[MAX_LINELEN + 1]; */ |
703 |
/* int16_t pos_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) |
|
704 |
|
|
705 |
/* set to integer dimension characters and own positions */ |
/* set to integer dimension characters and own positions */ |
706 |
for (i = 0;; i++) { |
for (i = 0;; i++) { |
707 |
int_buf[i] = (unsigned char)matchBuf[i]; |
int_buf[i] = (unsigned char)matchBuf[i]; |
708 |
if (int_buf[i] == 0) { |
if (int_buf[i] == 0) { |
709 |
pos_buf[i] = -1; /* indicator end line */ |
pos_buf[i] = -1; /* end-fo-line indicator */ |
710 |
break; |
break; |
711 |
} |
} |
712 |
pos_buf[i] = i; |
pos_buf[i] = i; |
719 |
int_buf[j] |= QUOT; |
int_buf[j] |= QUOT; |
720 |
i++; |
i++; |
721 |
#if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT |
#if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT |
722 |
if (matchBuf[i] == '\t') /* algorithm equivalent */ |
if (matchBuf[i] == '\t') /* algorithm equivalent */ |
723 |
int_buf[j] = ' ' | QUOT; |
int_buf[j] = ' ' | QUOT; |
724 |
#endif |
#endif |
725 |
} |
} |
762 |
} |
} |
763 |
if (command_mode) { |
if (command_mode) { |
764 |
collapse_pos(0, i + command_mode); |
collapse_pos(0, i + command_mode); |
765 |
i = -1; /* hack incremet */ |
i = -1; /* hack incremet */ |
766 |
} |
} |
767 |
} |
} |
768 |
/* collapse `command...` */ |
/* collapse `command...` */ |
769 |
for (i = 0; int_buf[i]; i++) |
for (i = 0; int_buf[i]; i++) { |
770 |
if (int_buf[i] == '`') { |
if (int_buf[i] == '`') { |
771 |
for (j = i + 1; int_buf[j]; j++) |
for (j = i + 1; int_buf[j]; j++) |
772 |
if (int_buf[j] == '`') { |
if (int_buf[j] == '`') { |
775 |
break; |
break; |
776 |
} |
} |
777 |
if (j) { |
if (j) { |
778 |
/* not found close ` - command mode, collapse all previous */ |
/* not found closing ` - command mode, collapse all previous */ |
779 |
collapse_pos(0, i + 1); |
collapse_pos(0, i + 1); |
780 |
break; |
break; |
781 |
} else |
} else |
782 |
i--; /* hack incremet */ |
i--; /* hack incremet */ |
783 |
} |
} |
784 |
|
} |
785 |
|
|
786 |
/* collapse (command...(command...)...) or {command...{command...}...} */ |
/* collapse (command...(command...)...) or {command...{command...}...} */ |
787 |
c = 0; /* "recursive" level */ |
c = 0; /* "recursive" level */ |
788 |
c2 = 0; |
c2 = 0; |
789 |
for (i = 0; int_buf[i]; i++) |
for (i = 0; int_buf[i]; i++) { |
790 |
if (int_buf[i] == '(' || int_buf[i] == '{') { |
if (int_buf[i] == '(' || int_buf[i] == '{') { |
791 |
if (int_buf[i] == '(') |
if (int_buf[i] == '(') |
792 |
c++; |
c++; |
793 |
else |
else |
794 |
c2++; |
c2++; |
795 |
collapse_pos(0, i + 1); |
collapse_pos(0, i + 1); |
796 |
i = -1; /* hack incremet */ |
i = -1; /* hack incremet */ |
797 |
} |
} |
798 |
for (i = 0; pos_buf[i] >= 0 && (c > 0 || c2 > 0); i++) |
} |
799 |
|
for (i = 0; pos_buf[i] >= 0 && (c > 0 || c2 > 0); i++) { |
800 |
if ((int_buf[i] == ')' && c > 0) || (int_buf[i] == '}' && c2 > 0)) { |
if ((int_buf[i] == ')' && c > 0) || (int_buf[i] == '}' && c2 > 0)) { |
801 |
if (int_buf[i] == ')') |
if (int_buf[i] == ')') |
802 |
c--; |
c--; |
803 |
else |
else |
804 |
c2--; |
c2--; |
805 |
collapse_pos(0, i + 1); |
collapse_pos(0, i + 1); |
806 |
i = -1; /* hack incremet */ |
i = -1; /* hack incremet */ |
807 |
} |
} |
808 |
|
} |
809 |
|
|
810 |
/* skip first not quote space */ |
/* skip first not quote space */ |
811 |
for (i = 0; int_buf[i]; i++) |
for (i = 0; int_buf[i]; i++) |
816 |
|
|
817 |
/* set find mode for completion */ |
/* set find mode for completion */ |
818 |
command_mode = FIND_EXE_ONLY; |
command_mode = FIND_EXE_ONLY; |
819 |
for (i = 0; int_buf[i]; i++) |
for (i = 0; int_buf[i]; i++) { |
820 |
if (int_buf[i] == ' ' || int_buf[i] == '<' || int_buf[i] == '>') { |
if (int_buf[i] == ' ' || int_buf[i] == '<' || int_buf[i] == '>') { |
821 |
if (int_buf[i] == ' ' && command_mode == FIND_EXE_ONLY |
if (int_buf[i] == ' ' && command_mode == FIND_EXE_ONLY |
822 |
&& matchBuf[pos_buf[0]] == 'c' |
&& matchBuf[pos_buf[0]] == 'c' |
828 |
break; |
break; |
829 |
} |
} |
830 |
} |
} |
831 |
|
} |
832 |
for (i = 0; int_buf[i]; i++) |
for (i = 0; int_buf[i]; i++) |
833 |
/* "strlen" */; |
/* "strlen" */; |
834 |
/* find last word */ |
/* find last word */ |
860 |
*len_with_quotes = j ? j - pos_buf[0] : 0; |
*len_with_quotes = j ? j - pos_buf[0] : 0; |
861 |
|
|
862 |
return command_mode; |
return command_mode; |
863 |
|
} |
864 |
#undef int_buf |
#undef int_buf |
865 |
#undef pos_buf |
#undef pos_buf |
|
} |
|
866 |
|
|
867 |
/* |
/* |
868 |
* display by column (original idea from ls applet, |
* display by column (original idea from ls applet, |
876 |
int nrows = nfiles; |
int nrows = nfiles; |
877 |
int l; |
int l; |
878 |
|
|
879 |
/* find the longest file name- use that as the column width */ |
/* find the longest file name - use that as the column width */ |
880 |
for (row = 0; row < nrows; row++) { |
for (row = 0; row < nrows; row++) { |
881 |
l = strlen(matches[row]); |
l = unicode_strlen(matches[row]); |
882 |
if (column_width < l) |
if (column_width < l) |
883 |
column_width = l; |
column_width = l; |
884 |
} |
} |
898 |
|
|
899 |
for (nc = 1; nc < ncols && n+nrows < nfiles; n += nrows, nc++) { |
for (nc = 1; nc < ncols && n+nrows < nfiles; n += nrows, nc++) { |
900 |
printf("%s%-*s", matches[n], |
printf("%s%-*s", matches[n], |
901 |
(int)(column_width - strlen(matches[n])), ""); |
(int)(column_width - unicode_strlen(matches[n])), "" |
902 |
|
); |
903 |
} |
} |
904 |
puts(matches[n]); |
puts(matches[n]); |
905 |
} |
} |
908 |
static char *add_quote_for_spec_chars(char *found) |
static char *add_quote_for_spec_chars(char *found) |
909 |
{ |
{ |
910 |
int l = 0; |
int l = 0; |
911 |
char *s = xmalloc((strlen(found) + 1) * 2); |
char *s = xzalloc((strlen(found) + 1) * 2); |
912 |
|
|
913 |
while (*found) { |
while (*found) { |
914 |
if (strchr(" `\"#$%^&*()=+{}[]:;\'|\\<>", *found)) |
if (strchr(" `\"#$%^&*()=+{}[]:;'|\\<>", *found)) |
915 |
s[l++] = '\\'; |
s[l++] = '\\'; |
916 |
s[l++] = *found++; |
s[l++] = *found++; |
917 |
} |
} |
918 |
s[l] = 0; |
/* s[l] = '\0'; - already is */ |
919 |
return s; |
return s; |
920 |
} |
} |
921 |
|
|
932 |
#define matchBuf (S.input_tab__matchBuf) |
#define matchBuf (S.input_tab__matchBuf) |
933 |
int find_type; |
int find_type; |
934 |
int recalc_pos; |
int recalc_pos; |
935 |
|
#if ENABLE_FEATURE_ASSUME_UNICODE |
936 |
|
/* cursor pos in command converted to multibyte form */ |
937 |
|
int cursor_mb; |
938 |
|
#endif |
939 |
|
|
940 |
*lastWasTab = TRUE; /* flop trigger */ |
*lastWasTab = TRUE; /* flop trigger */ |
941 |
|
|
942 |
/* Make a local copy of the string -- up |
/* Make a local copy of the string -- |
943 |
* to the position of the cursor */ |
* up to the position of the cursor */ |
944 |
tmp = strncpy(matchBuf, command_ps, cursor); |
save_string(matchBuf, cursor + 1); |
945 |
tmp[cursor] = '\0'; |
#if ENABLE_FEATURE_ASSUME_UNICODE |
946 |
|
cursor_mb = strlen(matchBuf); |
947 |
|
#endif |
948 |
|
tmp = matchBuf; |
949 |
|
|
950 |
find_type = find_match(matchBuf, &recalc_pos); |
find_type = find_match(matchBuf, &recalc_pos); |
951 |
|
|
956 |
/* If the word starts with `~' and there is no slash in the word, |
/* If the word starts with `~' and there is no slash in the word, |
957 |
* then try completing this word as a username. */ |
* then try completing this word as a username. */ |
958 |
if (state->flags & USERNAME_COMPLETION) |
if (state->flags & USERNAME_COMPLETION) |
959 |
if (matchBuf[0] == '~' && strchr(matchBuf, '/') == 0) |
if (matchBuf[0] == '~' && strchr(matchBuf, '/') == NULL) |
960 |
username_tab_completion(matchBuf, NULL); |
username_tab_completion(matchBuf, NULL); |
961 |
#endif |
#endif |
962 |
/* Try to match any executable in our path and everything |
/* Try to match any executable in our path and everything |
982 |
num_matches = n + 1; |
num_matches = n + 1; |
983 |
} |
} |
984 |
/* Did we find exactly one match? */ |
/* Did we find exactly one match? */ |
985 |
if (!matches || num_matches > 1) { |
if (!matches || num_matches > 1) { /* no */ |
986 |
beep(); |
beep(); |
987 |
if (!matches) |
if (!matches) |
988 |
return; /* not found */ |
return; /* not found */ |
989 |
/* find minimal match */ |
/* find minimal match */ |
990 |
tmp1 = xstrdup(matches[0]); |
tmp1 = xstrdup(matches[0]); |
991 |
for (tmp = tmp1; *tmp; tmp++) |
for (tmp = tmp1; *tmp; tmp++) { |
992 |
for (len_found = 1; len_found < num_matches; len_found++) |
for (len_found = 1; len_found < num_matches; len_found++) { |
993 |
if (matches[len_found][(tmp - tmp1)] != *tmp) { |
if (matches[len_found][tmp - tmp1] != *tmp) { |
994 |
*tmp = '\0'; |
*tmp = '\0'; |
995 |
break; |
break; |
996 |
} |
} |
997 |
|
} |
998 |
|
} |
999 |
if (*tmp1 == '\0') { /* have unique */ |
if (*tmp1 == '\0') { /* have unique */ |
1000 |
free(tmp1); |
free(tmp1); |
1001 |
return; |
return; |
1013 |
tmp[len_found+1] = '\0'; |
tmp[len_found+1] = '\0'; |
1014 |
} |
} |
1015 |
} |
} |
1016 |
|
|
1017 |
len_found = strlen(tmp); |
len_found = strlen(tmp); |
1018 |
/* have space to placed match? */ |
#if !ENABLE_FEATURE_ASSUME_UNICODE |
1019 |
if ((len_found - strlen(matchBuf) + command_len) < MAX_LINELEN) { |
/* have space to place the match? */ |
1020 |
/* before word for match */ |
/* The result consists of three parts with these lengths: */ |
1021 |
command_ps[cursor - recalc_pos] = '\0'; |
/* (cursor - recalc_pos) + len_found + (command_len - cursor) */ |
1022 |
/* save tail line */ |
/* it simplifies into: */ |
1023 |
|
if ((int)(len_found + command_len - recalc_pos) < S.maxsize) { |
1024 |
|
/* save tail */ |
1025 |
strcpy(matchBuf, command_ps + cursor); |
strcpy(matchBuf, command_ps + cursor); |
1026 |
/* add match */ |
/* add match and tail */ |
1027 |
strcat(command_ps, tmp); |
sprintf(&command_ps[cursor - recalc_pos], "%s%s", tmp, matchBuf); |
|
/* 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 */ |
|
1028 |
command_len = strlen(command_ps); |
command_len = strlen(command_ps); |
1029 |
/* write out the matched command */ |
/* new pos */ |
1030 |
|
recalc_pos = cursor - recalc_pos + len_found; |
1031 |
|
/* write out the matched command */ |
1032 |
redraw(cmdedit_y, command_len - recalc_pos); |
redraw(cmdedit_y, command_len - recalc_pos); |
1033 |
} |
} |
1034 |
|
#else |
1035 |
|
{ |
1036 |
|
char command[MAX_LINELEN]; |
1037 |
|
int len = save_string(command, sizeof(command)); |
1038 |
|
/* have space to place the match? */ |
1039 |
|
/* (cursor_mb - recalc_pos) + len_found + (len - cursor_mb) */ |
1040 |
|
if ((int)(len_found + len - recalc_pos) < MAX_LINELEN) { |
1041 |
|
/* save tail */ |
1042 |
|
strcpy(matchBuf, command + cursor_mb); |
1043 |
|
/* where do we want to have cursor after all? */ |
1044 |
|
strcpy(&command[cursor_mb - recalc_pos], tmp); |
1045 |
|
len = load_string(command, S.maxsize); |
1046 |
|
/* add match and tail */ |
1047 |
|
sprintf(&command[cursor_mb - recalc_pos], "%s%s", tmp, matchBuf); |
1048 |
|
command_len = load_string(command, S.maxsize); |
1049 |
|
/* write out the matched command */ |
1050 |
|
redraw(cmdedit_y, command_len - len); |
1051 |
|
} |
1052 |
|
} |
1053 |
|
#endif |
1054 |
free(tmp); |
free(tmp); |
1055 |
#undef matchBuf |
#undef matchBuf |
1056 |
} else { |
} else { |
1058 |
* just hit TAB again, print a list of all the |
* just hit TAB again, print a list of all the |
1059 |
* available choices... */ |
* available choices... */ |
1060 |
if (matches && num_matches > 0) { |
if (matches && num_matches > 0) { |
1061 |
int sav_cursor = cursor; /* change goto_new_line() */ |
/* changed by goto_new_line() */ |
1062 |
|
int sav_cursor = cursor; |
1063 |
|
|
1064 |
/* Go to the next line */ |
/* Go to the next line */ |
1065 |
goto_new_line(); |
goto_new_line(); |
1072 |
#endif /* FEATURE_COMMAND_TAB_COMPLETION */ |
#endif /* FEATURE_COMMAND_TAB_COMPLETION */ |
1073 |
|
|
1074 |
|
|
1075 |
|
line_input_t* FAST_FUNC new_line_input_t(int flags) |
1076 |
|
{ |
1077 |
|
line_input_t *n = xzalloc(sizeof(*n)); |
1078 |
|
n->flags = flags; |
1079 |
|
return n; |
1080 |
|
} |
1081 |
|
|
1082 |
|
|
1083 |
#if MAX_HISTORY > 0 |
#if MAX_HISTORY > 0 |
1084 |
|
|
1085 |
static void save_command_ps_at_cur_history(void) |
static void save_command_ps_at_cur_history(void) |
1086 |
{ |
{ |
1087 |
if (command_ps[0] != '\0') { |
if (command_ps[0] != BB_NUL) { |
1088 |
int cur = state->cur_history; |
int cur = state->cur_history; |
1089 |
free(state->history[cur]); |
free(state->history[cur]); |
1090 |
|
|
1091 |
|
# if ENABLE_FEATURE_ASSUME_UNICODE |
1092 |
|
{ |
1093 |
|
char tbuf[MAX_LINELEN]; |
1094 |
|
save_string(tbuf, sizeof(tbuf)); |
1095 |
|
state->history[cur] = xstrdup(tbuf); |
1096 |
|
} |
1097 |
|
# else |
1098 |
state->history[cur] = xstrdup(command_ps); |
state->history[cur] = xstrdup(command_ps); |
1099 |
|
# endif |
1100 |
} |
} |
1101 |
} |
} |
1102 |
|
|
1124 |
return 0; |
return 0; |
1125 |
} |
} |
1126 |
|
|
1127 |
#if ENABLE_FEATURE_EDITING_SAVEHISTORY |
# if ENABLE_FEATURE_EDITING_SAVEHISTORY |
1128 |
|
/* We try to ensure that concurrent additions to the history |
1129 |
|
* do not overwrite each other. |
1130 |
|
* Otherwise shell users get unhappy. |
1131 |
|
* |
1132 |
|
* History file is trimmed lazily, when it grows several times longer |
1133 |
|
* than configured MAX_HISTORY lines. |
1134 |
|
*/ |
1135 |
|
|
1136 |
|
static void free_line_input_t(line_input_t *n) |
1137 |
|
{ |
1138 |
|
int i = n->cnt_history; |
1139 |
|
while (i > 0) |
1140 |
|
free(n->history[--i]); |
1141 |
|
free(n); |
1142 |
|
} |
1143 |
|
|
1144 |
/* state->flags is already checked to be nonzero */ |
/* state->flags is already checked to be nonzero */ |
1145 |
static void load_history(const char *fromfile) |
static void load_history(line_input_t *st_parm) |
1146 |
{ |
{ |
1147 |
|
char *temp_h[MAX_HISTORY]; |
1148 |
|
char *line; |
1149 |
FILE *fp; |
FILE *fp; |
1150 |
int hi; |
unsigned idx, i, line_len; |
1151 |
|
|
1152 |
/* NB: do not trash old history if file can't be opened */ |
/* NB: do not trash old history if file can't be opened */ |
1153 |
|
|
1154 |
fp = fopen_for_read(fromfile); |
fp = fopen_for_read(st_parm->hist_file); |
1155 |
if (fp) { |
if (fp) { |
1156 |
/* clean up old history */ |
/* clean up old history */ |
1157 |
for (hi = state->cnt_history; hi > 0;) { |
for (idx = st_parm->cnt_history; idx > 0;) { |
1158 |
hi--; |
idx--; |
1159 |
free(state->history[hi]); |
free(st_parm->history[idx]); |
1160 |
state->history[hi] = NULL; |
st_parm->history[idx] = NULL; |
1161 |
} |
} |
1162 |
|
|
1163 |
for (hi = 0; hi < MAX_HISTORY;) { |
/* fill temp_h[], retaining only last MAX_HISTORY lines */ |
1164 |
char *hl = xmalloc_fgetline(fp); |
memset(temp_h, 0, sizeof(temp_h)); |
1165 |
int l; |
st_parm->cnt_history_in_file = idx = 0; |
1166 |
|
while ((line = xmalloc_fgetline(fp)) != NULL) { |
1167 |
if (!hl) |
if (line[0] == '\0') { |
1168 |
break; |
free(line); |
|
l = strlen(hl); |
|
|
if (l >= MAX_LINELEN) |
|
|
hl[MAX_LINELEN-1] = '\0'; |
|
|
if (l == 0) { |
|
|
free(hl); |
|
1169 |
continue; |
continue; |
1170 |
} |
} |
1171 |
state->history[hi++] = hl; |
free(temp_h[idx]); |
1172 |
|
temp_h[idx] = line; |
1173 |
|
st_parm->cnt_history_in_file++; |
1174 |
|
idx++; |
1175 |
|
if (idx == MAX_HISTORY) |
1176 |
|
idx = 0; |
1177 |
} |
} |
1178 |
fclose(fp); |
fclose(fp); |
1179 |
state->cnt_history = hi; |
|
1180 |
|
/* find first non-NULL temp_h[], if any */ |
1181 |
|
if (st_parm->cnt_history_in_file) { |
1182 |
|
while (temp_h[idx] == NULL) { |
1183 |
|
idx++; |
1184 |
|
if (idx == MAX_HISTORY) |
1185 |
|
idx = 0; |
1186 |
|
} |
1187 |
|
} |
1188 |
|
|
1189 |
|
/* copy temp_h[] to st_parm->history[] */ |
1190 |
|
for (i = 0; i < MAX_HISTORY;) { |
1191 |
|
line = temp_h[idx]; |
1192 |
|
if (!line) |
1193 |
|
break; |
1194 |
|
idx++; |
1195 |
|
if (idx == MAX_HISTORY) |
1196 |
|
idx = 0; |
1197 |
|
line_len = strlen(line); |
1198 |
|
if (line_len >= MAX_LINELEN) |
1199 |
|
line[MAX_LINELEN-1] = '\0'; |
1200 |
|
st_parm->history[i++] = line; |
1201 |
|
} |
1202 |
|
st_parm->cnt_history = i; |
1203 |
} |
} |
1204 |
} |
} |
1205 |
|
|
1206 |
/* state->flags is already checked to be nonzero */ |
/* state->flags is already checked to be nonzero */ |
1207 |
static void save_history(const char *tofile) |
static void save_history(char *str) |
1208 |
{ |
{ |
1209 |
FILE *fp; |
int fd; |
1210 |
|
int len, len2; |
1211 |
|
|
1212 |
fp = fopen_for_write(tofile); |
fd = open(state->hist_file, O_WRONLY | O_CREAT | O_APPEND, 0666); |
1213 |
if (fp) { |
if (fd < 0) |
1214 |
|
return; |
1215 |
|
xlseek(fd, 0, SEEK_END); /* paranoia */ |
1216 |
|
len = strlen(str); |
1217 |
|
str[len] = '\n'; /* we (try to) do atomic write */ |
1218 |
|
len2 = full_write(fd, str, len + 1); |
1219 |
|
str[len] = '\0'; |
1220 |
|
close(fd); |
1221 |
|
if (len2 != len + 1) |
1222 |
|
return; /* "wtf?" */ |
1223 |
|
|
1224 |
|
/* did we write so much that history file needs trimming? */ |
1225 |
|
state->cnt_history_in_file++; |
1226 |
|
if (state->cnt_history_in_file > MAX_HISTORY * 4) { |
1227 |
|
FILE *fp; |
1228 |
|
char *new_name; |
1229 |
|
line_input_t *st_temp; |
1230 |
int i; |
int i; |
1231 |
|
|
1232 |
for (i = 0; i < state->cnt_history; i++) { |
/* we may have concurrently written entries from others. |
1233 |
fprintf(fp, "%s\n", state->history[i]); |
* load them */ |
1234 |
} |
st_temp = new_line_input_t(state->flags); |
1235 |
fclose(fp); |
st_temp->hist_file = state->hist_file; |
1236 |
} |
load_history(st_temp); |
1237 |
} |
|
1238 |
#else |
/* write out temp file and replace hist_file atomically */ |
1239 |
#define load_history(a) ((void)0) |
new_name = xasprintf("%s.%u.new", state->hist_file, (int) getpid()); |
1240 |
#define save_history(a) ((void)0) |
fp = fopen_for_write(new_name); |
1241 |
#endif /* FEATURE_COMMAND_SAVEHISTORY */ |
if (fp) { |
1242 |
|
for (i = 0; i < st_temp->cnt_history; i++) |
1243 |
|
fprintf(fp, "%s\n", st_temp->history[i]); |
1244 |
|
fclose(fp); |
1245 |
|
if (rename(new_name, state->hist_file) == 0) |
1246 |
|
state->cnt_history_in_file = st_temp->cnt_history; |
1247 |
|
} |
1248 |
|
free(new_name); |
1249 |
|
free_line_input_t(st_temp); |
1250 |
|
} |
1251 |
|
} |
1252 |
|
# else |
1253 |
|
# define load_history(a) ((void)0) |
1254 |
|
# define save_history(a) ((void)0) |
1255 |
|
# endif /* FEATURE_COMMAND_SAVEHISTORY */ |
1256 |
|
|
1257 |
static void remember_in_history(const char *str) |
static void remember_in_history(char *str) |
1258 |
{ |
{ |
1259 |
int i; |
int i; |
1260 |
|
|
1283 |
/* i <= MAX_HISTORY */ |
/* i <= MAX_HISTORY */ |
1284 |
state->cur_history = i; |
state->cur_history = i; |
1285 |
state->cnt_history = i; |
state->cnt_history = i; |
1286 |
#if ENABLE_FEATURE_EDITING_SAVEHISTORY |
# if MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY |
1287 |
if ((state->flags & SAVE_HISTORY) && state->hist_file) |
if ((state->flags & SAVE_HISTORY) && state->hist_file) |
1288 |
save_history(state->hist_file); |
save_history(str); |
1289 |
#endif |
# endif |
1290 |
USE_FEATURE_EDITING_FANCY_PROMPT(num_ok_lines++;) |
IF_FEATURE_EDITING_FANCY_PROMPT(num_ok_lines++;) |
1291 |
} |
} |
1292 |
|
|
1293 |
#else /* MAX_HISTORY == 0 */ |
#else /* MAX_HISTORY == 0 */ |
1294 |
#define remember_in_history(a) ((void)0) |
# define remember_in_history(a) ((void)0) |
1295 |
#endif /* MAX_HISTORY */ |
#endif /* MAX_HISTORY */ |
1296 |
|
|
1297 |
|
|
1298 |
|
#if ENABLE_FEATURE_EDITING_VI |
1299 |
/* |
/* |
|
* This function is used to grab a character buffer |
|
|
* from the input file descriptor and allows you to |
|
|
* a string with full command editing (sort of like |
|
|
* a mini readline). |
|
|
* |
|
|
* The following standard commands are not implemented: |
|
|
* ESC-b -- Move back one word |
|
|
* ESC-f -- Move forward one word |
|
|
* ESC-d -- Delete back one word |
|
|
* ESC-h -- Delete forward one word |
|
|
* CTL-t -- Transpose two characters |
|
|
* |
|
|
* Minimalist vi-style command line editing available if configured. |
|
1300 |
* vi mode implemented 2005 by Paul Fox <pgf@foxharp.boston.ma.us> |
* vi mode implemented 2005 by Paul Fox <pgf@foxharp.boston.ma.us> |
1301 |
*/ |
*/ |
|
|
|
|
#if ENABLE_FEATURE_EDITING_VI |
|
1302 |
static void |
static void |
1303 |
vi_Word_motion(char *command, int eat) |
vi_Word_motion(int eat) |
1304 |
{ |
{ |
1305 |
while (cursor < command_len && !isspace(command[cursor])) |
CHAR_T *command = command_ps; |
1306 |
|
|
1307 |
|
while (cursor < command_len && !BB_isspace(command[cursor])) |
1308 |
input_forward(); |
input_forward(); |
1309 |
if (eat) while (cursor < command_len && isspace(command[cursor])) |
if (eat) while (cursor < command_len && BB_isspace(command[cursor])) |
1310 |
input_forward(); |
input_forward(); |
1311 |
} |
} |
1312 |
|
|
1313 |
static void |
static void |
1314 |
vi_word_motion(char *command, int eat) |
vi_word_motion(int eat) |
1315 |
{ |
{ |
1316 |
if (isalnum(command[cursor]) || command[cursor] == '_') { |
CHAR_T *command = command_ps; |
1317 |
|
|
1318 |
|
if (BB_isalnum(command[cursor]) || command[cursor] == '_') { |
1319 |
while (cursor < command_len |
while (cursor < command_len |
1320 |
&& (isalnum(command[cursor+1]) || command[cursor+1] == '_')) |
&& (BB_isalnum(command[cursor+1]) || command[cursor+1] == '_') |
1321 |
|
) { |
1322 |
input_forward(); |
input_forward(); |
1323 |
} else if (ispunct(command[cursor])) { |
} |
1324 |
while (cursor < command_len && ispunct(command[cursor+1])) |
} else if (BB_ispunct(command[cursor])) { |
1325 |
|
while (cursor < command_len && BB_ispunct(command[cursor+1])) |
1326 |
input_forward(); |
input_forward(); |
1327 |
} |
} |
1328 |
|
|
1329 |
if (cursor < command_len) |
if (cursor < command_len) |
1330 |
input_forward(); |
input_forward(); |
1331 |
|
|
1332 |
if (eat && cursor < command_len && isspace(command[cursor])) |
if (eat) { |
1333 |
while (cursor < command_len && isspace(command[cursor])) |
while (cursor < command_len && BB_isspace(command[cursor])) |
1334 |
input_forward(); |
input_forward(); |
1335 |
|
} |
1336 |
} |
} |
1337 |
|
|
1338 |
static void |
static void |
1339 |
vi_End_motion(char *command) |
vi_End_motion(void) |
1340 |
{ |
{ |
1341 |
|
CHAR_T *command = command_ps; |
1342 |
|
|
1343 |
input_forward(); |
input_forward(); |
1344 |
while (cursor < command_len && isspace(command[cursor])) |
while (cursor < command_len && BB_isspace(command[cursor])) |
1345 |
input_forward(); |
input_forward(); |
1346 |
while (cursor < command_len-1 && !isspace(command[cursor+1])) |
while (cursor < command_len-1 && !BB_isspace(command[cursor+1])) |
1347 |
input_forward(); |
input_forward(); |
1348 |
} |
} |
1349 |
|
|
1350 |
static void |
static void |
1351 |
vi_end_motion(char *command) |
vi_end_motion(void) |
1352 |
{ |
{ |
1353 |
|
CHAR_T *command = command_ps; |
1354 |
|
|
1355 |
if (cursor >= command_len-1) |
if (cursor >= command_len-1) |
1356 |
return; |
return; |
1357 |
input_forward(); |
input_forward(); |
1358 |
while (cursor < command_len-1 && isspace(command[cursor])) |
while (cursor < command_len-1 && BB_isspace(command[cursor])) |
1359 |
input_forward(); |
input_forward(); |
1360 |
if (cursor >= command_len-1) |
if (cursor >= command_len-1) |
1361 |
return; |
return; |
1362 |
if (isalnum(command[cursor]) || command[cursor] == '_') { |
if (BB_isalnum(command[cursor]) || command[cursor] == '_') { |
1363 |
while (cursor < command_len-1 |
while (cursor < command_len-1 |
1364 |
&& (isalnum(command[cursor+1]) || command[cursor+1] == '_') |
&& (BB_isalnum(command[cursor+1]) || command[cursor+1] == '_') |
1365 |
) { |
) { |
1366 |
input_forward(); |
input_forward(); |
1367 |
} |
} |
1368 |
} else if (ispunct(command[cursor])) { |
} else if (BB_ispunct(command[cursor])) { |
1369 |
while (cursor < command_len-1 && ispunct(command[cursor+1])) |
while (cursor < command_len-1 && BB_ispunct(command[cursor+1])) |
1370 |
input_forward(); |
input_forward(); |
1371 |
} |
} |
1372 |
} |
} |
1373 |
|
|
1374 |
static void |
static void |
1375 |
vi_Back_motion(char *command) |
vi_Back_motion(void) |
1376 |
{ |
{ |
1377 |
while (cursor > 0 && isspace(command[cursor-1])) |
CHAR_T *command = command_ps; |
1378 |
|
|
1379 |
|
while (cursor > 0 && BB_isspace(command[cursor-1])) |
1380 |
input_backward(1); |
input_backward(1); |
1381 |
while (cursor > 0 && !isspace(command[cursor-1])) |
while (cursor > 0 && !BB_isspace(command[cursor-1])) |
1382 |
input_backward(1); |
input_backward(1); |
1383 |
} |
} |
1384 |
|
|
1385 |
static void |
static void |
1386 |
vi_back_motion(char *command) |
vi_back_motion(void) |
1387 |
{ |
{ |
1388 |
|
CHAR_T *command = command_ps; |
1389 |
|
|
1390 |
if (cursor <= 0) |
if (cursor <= 0) |
1391 |
return; |
return; |
1392 |
input_backward(1); |
input_backward(1); |
1393 |
while (cursor > 0 && isspace(command[cursor])) |
while (cursor > 0 && BB_isspace(command[cursor])) |
1394 |
input_backward(1); |
input_backward(1); |
1395 |
if (cursor <= 0) |
if (cursor <= 0) |
1396 |
return; |
return; |
1397 |
if (isalnum(command[cursor]) || command[cursor] == '_') { |
if (BB_isalnum(command[cursor]) || command[cursor] == '_') { |
1398 |
while (cursor > 0 |
while (cursor > 0 |
1399 |
&& (isalnum(command[cursor-1]) || command[cursor-1] == '_') |
&& (BB_isalnum(command[cursor-1]) || command[cursor-1] == '_') |
1400 |
) { |
) { |
1401 |
input_backward(1); |
input_backward(1); |
1402 |
} |
} |
1403 |
} else if (ispunct(command[cursor])) { |
} else if (BB_ispunct(command[cursor])) { |
1404 |
while (cursor > 0 && ispunct(command[cursor-1])) |
while (cursor > 0 && BB_ispunct(command[cursor-1])) |
1405 |
input_backward(1); |
input_backward(1); |
1406 |
} |
} |
1407 |
} |
} |
1408 |
#endif |
#endif |
1409 |
|
|
1410 |
|
/* Modelled after bash 4.0 behavior of Ctrl-<arrow> */ |
1411 |
|
static void ctrl_left(void) |
1412 |
|
{ |
1413 |
|
CHAR_T *command = command_ps; |
1414 |
|
|
1415 |
|
while (1) { |
1416 |
|
CHAR_T c; |
1417 |
|
|
1418 |
|
input_backward(1); |
1419 |
|
if (cursor == 0) |
1420 |
|
break; |
1421 |
|
c = command[cursor]; |
1422 |
|
if (c != ' ' && !BB_ispunct(c)) { |
1423 |
|
/* we reached a "word" delimited by spaces/punct. |
1424 |
|
* go to its beginning */ |
1425 |
|
while (1) { |
1426 |
|
c = command[cursor - 1]; |
1427 |
|
if (c == ' ' || BB_ispunct(c)) |
1428 |
|
break; |
1429 |
|
input_backward(1); |
1430 |
|
if (cursor == 0) |
1431 |
|
break; |
1432 |
|
} |
1433 |
|
break; |
1434 |
|
} |
1435 |
|
} |
1436 |
|
} |
1437 |
|
static void ctrl_right(void) |
1438 |
|
{ |
1439 |
|
CHAR_T *command = command_ps; |
1440 |
|
|
1441 |
|
while (1) { |
1442 |
|
CHAR_T c; |
1443 |
|
|
1444 |
|
c = command[cursor]; |
1445 |
|
if (c == BB_NUL) |
1446 |
|
break; |
1447 |
|
if (c != ' ' && !BB_ispunct(c)) { |
1448 |
|
/* we reached a "word" delimited by spaces/punct. |
1449 |
|
* go to its end + 1 */ |
1450 |
|
while (1) { |
1451 |
|
input_forward(); |
1452 |
|
c = command[cursor]; |
1453 |
|
if (c == BB_NUL || c == ' ' || BB_ispunct(c)) |
1454 |
|
break; |
1455 |
|
} |
1456 |
|
break; |
1457 |
|
} |
1458 |
|
input_forward(); |
1459 |
|
} |
1460 |
|
} |
1461 |
|
|
1462 |
|
|
1463 |
/* |
/* |
1464 |
* read_line_input and its helpers |
* read_line_input and its helpers |
1465 |
*/ |
*/ |
1466 |
|
|
1467 |
|
#if ENABLE_FEATURE_EDITING_ASK_TERMINAL |
1468 |
|
static void ask_terminal(void) |
1469 |
|
{ |
1470 |
|
/* Ask terminal where is the cursor now. |
1471 |
|
* lineedit_read_key handles response and corrects |
1472 |
|
* our idea of current cursor position. |
1473 |
|
* Testcase: run "echo -n long_line_long_line_long_line", |
1474 |
|
* then type in a long, wrapping command and try to |
1475 |
|
* delete it using backspace key. |
1476 |
|
* Note: we print it _after_ prompt, because |
1477 |
|
* prompt may contain CR. Example: PS1='\[\r\n\]\w ' |
1478 |
|
*/ |
1479 |
|
/* Problem: if there is buffered input on stdin, |
1480 |
|
* the response will be delivered later, |
1481 |
|
* possibly to an unsuspecting application. |
1482 |
|
* Testcase: "sleep 1; busybox ash" + press and hold [Enter]. |
1483 |
|
* Result: |
1484 |
|
* ~/srcdevel/bbox/fix/busybox.t4 # |
1485 |
|
* ~/srcdevel/bbox/fix/busybox.t4 # |
1486 |
|
* ^[[59;34~/srcdevel/bbox/fix/busybox.t4 # <-- garbage |
1487 |
|
* ~/srcdevel/bbox/fix/busybox.t4 # |
1488 |
|
* |
1489 |
|
* Checking for input with poll only makes the race narrower, |
1490 |
|
* I still can trigger it. Strace: |
1491 |
|
* |
1492 |
|
* write(1, "~/srcdevel/bbox/fix/busybox.t4 # ", 33) = 33 |
1493 |
|
* poll([{fd=0, events=POLLIN}], 1, 0) = 0 (Timeout) <-- no input exists |
1494 |
|
* write(1, "\33[6n", 4) = 4 <-- send the ESC sequence, quick! |
1495 |
|
* poll([{fd=0, events=POLLIN}], 1, 4294967295) = 1 ([{fd=0, revents=POLLIN}]) |
1496 |
|
* read(0, "\n", 1) = 1 <-- oh crap, user's input got in first |
1497 |
|
*/ |
1498 |
|
struct pollfd pfd; |
1499 |
|
|
1500 |
|
pfd.fd = STDIN_FILENO; |
1501 |
|
pfd.events = POLLIN; |
1502 |
|
if (safe_poll(&pfd, 1, 0) == 0) { |
1503 |
|
S.sent_ESC_br6n = 1; |
1504 |
|
out1str("\033" "[6n"); |
1505 |
|
fflush_all(); /* make terminal see it ASAP! */ |
1506 |
|
} |
1507 |
|
} |
1508 |
|
#else |
1509 |
|
#define ask_terminal() ((void)0) |
1510 |
|
#endif |
1511 |
|
|
1512 |
#if !ENABLE_FEATURE_EDITING_FANCY_PROMPT |
#if !ENABLE_FEATURE_EDITING_FANCY_PROMPT |
1513 |
static void parse_and_put_prompt(const char *prmt_ptr) |
static void parse_and_put_prompt(const char *prmt_ptr) |
1514 |
{ |
{ |
1552 |
c = *prmt_ptr++; |
c = *prmt_ptr++; |
1553 |
|
|
1554 |
switch (c) { |
switch (c) { |
1555 |
#if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR |
# if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR |
1556 |
case 'u': |
case 'u': |
1557 |
pbuf = user_buf ? user_buf : (char*)""; |
pbuf = user_buf ? user_buf : (char*)""; |
1558 |
break; |
break; |
1559 |
#endif |
# endif |
1560 |
case 'h': |
case 'h': |
1561 |
pbuf = free_me = safe_gethostname(); |
pbuf = free_me = safe_gethostname(); |
1562 |
*strchrnul(pbuf, '.') = '\0'; |
*strchrnul(pbuf, '.') = '\0'; |
1564 |
case '$': |
case '$': |
1565 |
c = (geteuid() == 0 ? '#' : '$'); |
c = (geteuid() == 0 ? '#' : '$'); |
1566 |
break; |
break; |
1567 |
#if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR |
# if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR |
1568 |
case 'w': |
case 'w': |
1569 |
/* /home/user[/something] -> ~[/something] */ |
/* /home/user[/something] -> ~[/something] */ |
1570 |
pbuf = cwd_buf; |
pbuf = cwd_buf; |
1577 |
pbuf = free_me = xasprintf("~%s", cwd_buf + l); |
pbuf = free_me = xasprintf("~%s", cwd_buf + l); |
1578 |
} |
} |
1579 |
break; |
break; |
1580 |
#endif |
# endif |
1581 |
case 'W': |
case 'W': |
1582 |
pbuf = cwd_buf; |
pbuf = cwd_buf; |
1583 |
cp = strrchr(pbuf, '/'); |
cp = strrchr(pbuf, '/'); |
1642 |
int new_y = (cursor + cmdedit_prmt_len) / w; |
int new_y = (cursor + cmdedit_prmt_len) / w; |
1643 |
/* redraw */ |
/* redraw */ |
1644 |
redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), command_len - cursor); |
redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), command_len - cursor); |
1645 |
fflush(stdout); |
fflush_all(); |
1646 |
} |
} |
1647 |
} |
} |
1648 |
|
|
1655 |
signal(SIGWINCH, win_changed); /* rearm ourself */ |
signal(SIGWINCH, win_changed); /* rearm ourself */ |
1656 |
} |
} |
1657 |
|
|
1658 |
/* |
static int lineedit_read_key(char *read_key_buffer) |
1659 |
* The emacs and vi modes share much of the code in the big |
{ |
1660 |
* command loop. Commands entered when in vi's command mode (aka |
int64_t ic; |
1661 |
* "escape mode") get an extra bit added to distinguish them -- |
struct pollfd pfd; |
1662 |
* this keeps them from being self-inserted. This clutters the |
int delay = -1; |
1663 |
* big switch a bit, but keeps all the code in one place. |
#if ENABLE_FEATURE_ASSUME_UNICODE |
1664 |
*/ |
char unicode_buf[MB_CUR_MAX + 1]; |
1665 |
|
int unicode_idx = 0; |
1666 |
|
#endif |
1667 |
|
|
1668 |
|
pfd.fd = STDIN_FILENO; |
1669 |
|
pfd.events = POLLIN; |
1670 |
|
do { |
1671 |
|
#if ENABLE_FEATURE_EDITING_ASK_TERMINAL || ENABLE_FEATURE_ASSUME_UNICODE |
1672 |
|
poll_again: |
1673 |
|
#endif |
1674 |
|
if (read_key_buffer[0] == 0) { |
1675 |
|
/* Wait for input. Can't just call read_key, |
1676 |
|
* it returns at once if stdin |
1677 |
|
* is in non-blocking mode. */ |
1678 |
|
safe_poll(&pfd, 1, delay); |
1679 |
|
} |
1680 |
|
/* Note: read_key sets errno to 0 on success: */ |
1681 |
|
ic = read_key(STDIN_FILENO, read_key_buffer); |
1682 |
|
|
1683 |
|
#if ENABLE_FEATURE_EDITING_ASK_TERMINAL |
1684 |
|
if ((int32_t)ic == KEYCODE_CURSOR_POS |
1685 |
|
&& S.sent_ESC_br6n |
1686 |
|
) { |
1687 |
|
S.sent_ESC_br6n = 0; |
1688 |
|
if (cursor == 0) { /* otherwise it may be bogus */ |
1689 |
|
int col = ((ic >> 32) & 0x7fff) - 1; |
1690 |
|
if (col > cmdedit_prmt_len) { |
1691 |
|
cmdedit_x += (col - cmdedit_prmt_len); |
1692 |
|
while (cmdedit_x >= cmdedit_termw) { |
1693 |
|
cmdedit_x -= cmdedit_termw; |
1694 |
|
cmdedit_y++; |
1695 |
|
} |
1696 |
|
} |
1697 |
|
} |
1698 |
|
goto poll_again; |
1699 |
|
} |
1700 |
|
#endif |
1701 |
|
|
1702 |
#define vbit 0x100 |
#if ENABLE_FEATURE_ASSUME_UNICODE |
1703 |
|
{ |
1704 |
|
wchar_t wc; |
1705 |
|
|
1706 |
|
if ((int32_t)ic < 0) /* KEYCODE_xxx */ |
1707 |
|
return ic; |
1708 |
|
unicode_buf[unicode_idx++] = ic; |
1709 |
|
unicode_buf[unicode_idx] = '\0'; |
1710 |
|
if (mbstowcs(&wc, unicode_buf, 1) != 1 && unicode_idx < MB_CUR_MAX) { |
1711 |
|
delay = 50; |
1712 |
|
goto poll_again; |
1713 |
|
} |
1714 |
|
ic = wc; |
1715 |
|
} |
1716 |
|
#endif |
1717 |
|
} while (errno == EAGAIN); |
1718 |
|
|
1719 |
|
return ic; |
1720 |
|
} |
1721 |
|
|
1722 |
/* leave out the "vi-mode"-only case labels if vi editing isn't |
/* leave out the "vi-mode"-only case labels if vi editing isn't |
1723 |
* configured. */ |
* configured. */ |
1724 |
#define vi_case(caselabel) USE_FEATURE_EDITING(case caselabel) |
#define vi_case(caselabel) IF_FEATURE_EDITING_VI(case caselabel) |
1725 |
|
|
1726 |
/* convert uppercase ascii to equivalent control char, for readability */ |
/* convert uppercase ascii to equivalent control char, for readability */ |
1727 |
#undef CTRL |
#undef CTRL |
1728 |
#define CTRL(a) ((a) & ~0x40) |
#define CTRL(a) ((a) & ~0x40) |
1729 |
|
|
1730 |
/* Returns: |
/* maxsize must be >= 2. |
1731 |
|
* Returns: |
1732 |
* -1 on read errors or EOF, or on bare Ctrl-D, |
* -1 on read errors or EOF, or on bare Ctrl-D, |
1733 |
* 0 on ctrl-C (the line entered is still returned in 'command'), |
* 0 on ctrl-C (the line entered is still returned in 'command'), |
1734 |
* >0 length of input string, including terminating '\n' |
* >0 length of input string, including terminating '\n' |
1739 |
#if ENABLE_FEATURE_TAB_COMPLETION |
#if ENABLE_FEATURE_TAB_COMPLETION |
1740 |
smallint lastWasTab = FALSE; |
smallint lastWasTab = FALSE; |
1741 |
#endif |
#endif |
|
unsigned ic; |
|
|
unsigned char c; |
|
1742 |
smallint break_out = 0; |
smallint break_out = 0; |
1743 |
#if ENABLE_FEATURE_EDITING_VI |
#if ENABLE_FEATURE_EDITING_VI |
1744 |
smallint vi_cmdmode = 0; |
smallint vi_cmdmode = 0; |
|
smalluint prevc; |
|
1745 |
#endif |
#endif |
1746 |
struct termios initial_settings; |
struct termios initial_settings; |
1747 |
struct termios new_settings; |
struct termios new_settings; |
1748 |
|
char read_key_buffer[KEYCODE_BUFFER_SIZE]; |
1749 |
|
|
1750 |
INIT_S(); |
INIT_S(); |
1751 |
|
|
1754 |
) { |
) { |
1755 |
/* Happens when e.g. stty -echo was run before */ |
/* Happens when e.g. stty -echo was run before */ |
1756 |
parse_and_put_prompt(prompt); |
parse_and_put_prompt(prompt); |
1757 |
fflush(stdout); |
/* fflush_all(); - done by parse_and_put_prompt */ |
1758 |
if (fgets(command, maxsize, stdin) == NULL) |
if (fgets(command, maxsize, stdin) == NULL) |
1759 |
len = -1; /* EOF or error */ |
len = -1; /* EOF or error */ |
1760 |
else |
else |
1763 |
return len; |
return len; |
1764 |
} |
} |
1765 |
|
|
1766 |
|
init_unicode(); |
1767 |
|
|
1768 |
// FIXME: audit & improve this |
// FIXME: audit & improve this |
1769 |
if (maxsize > MAX_LINELEN) |
if (maxsize > MAX_LINELEN) |
1770 |
maxsize = MAX_LINELEN; |
maxsize = MAX_LINELEN; |
1771 |
|
S.maxsize = maxsize; |
1772 |
|
|
1773 |
/* With null flags, no other fields are ever used */ |
/* With null flags, no other fields are ever used */ |
1774 |
state = st ? st : (line_input_t*) &const_int_0; |
state = st ? st : (line_input_t*) &const_int_0; |
|
#if ENABLE_FEATURE_EDITING_SAVEHISTORY |
|
|
if ((state->flags & SAVE_HISTORY) && state->hist_file) |
|
|
load_history(state->hist_file); |
|
|
#endif |
|
1775 |
#if MAX_HISTORY > 0 |
#if MAX_HISTORY > 0 |
1776 |
|
# if ENABLE_FEATURE_EDITING_SAVEHISTORY |
1777 |
|
if ((state->flags & SAVE_HISTORY) && state->hist_file) |
1778 |
|
if (state->cnt_history == 0) |
1779 |
|
load_history(state); |
1780 |
|
# endif |
1781 |
if (state->flags & DO_HISTORY) |
if (state->flags & DO_HISTORY) |
1782 |
state->cur_history = state->cnt_history; |
state->cur_history = state->cnt_history; |
1783 |
#endif |
#endif |
1785 |
/* prepare before init handlers */ |
/* prepare before init handlers */ |
1786 |
cmdedit_y = 0; /* quasireal y, not true if line > xt*yt */ |
cmdedit_y = 0; /* quasireal y, not true if line > xt*yt */ |
1787 |
command_len = 0; |
command_len = 0; |
1788 |
|
#if ENABLE_FEATURE_ASSUME_UNICODE |
1789 |
|
command_ps = xzalloc(maxsize * sizeof(command_ps[0])); |
1790 |
|
#else |
1791 |
command_ps = command; |
command_ps = command; |
1792 |
command[0] = '\0'; |
command[0] = '\0'; |
1793 |
|
#endif |
1794 |
|
#define command command_must_not_be_used |
1795 |
|
|
1796 |
new_settings = initial_settings; |
new_settings = initial_settings; |
1797 |
new_settings.c_lflag &= ~ICANON; /* unbuffered input */ |
new_settings.c_lflag &= ~ICANON; /* unbuffered input */ |
1802 |
new_settings.c_cc[VTIME] = 0; |
new_settings.c_cc[VTIME] = 0; |
1803 |
/* Turn off CTRL-C, so we can trap it */ |
/* Turn off CTRL-C, so we can trap it */ |
1804 |
#ifndef _POSIX_VDISABLE |
#ifndef _POSIX_VDISABLE |
1805 |
#define _POSIX_VDISABLE '\0' |
# define _POSIX_VDISABLE '\0' |
1806 |
#endif |
#endif |
1807 |
new_settings.c_cc[VINTR] = _POSIX_VDISABLE; |
new_settings.c_cc[VINTR] = _POSIX_VDISABLE; |
1808 |
tcsetattr_stdin_TCSANOW(&new_settings); |
tcsetattr_stdin_TCSANOW(&new_settings); |
1823 |
#endif |
#endif |
1824 |
|
|
1825 |
#if 0 |
#if 0 |
1826 |
for (ic = 0; ic <= MAX_HISTORY; ic++) |
for (i = 0; i <= MAX_HISTORY; i++) |
1827 |
bb_error_msg("history[%d]:'%s'", ic, state->history[ic]); |
bb_error_msg("history[%d]:'%s'", i, state->history[i]); |
1828 |
bb_error_msg("cur_history:%d cnt_history:%d", state->cur_history, state->cnt_history); |
bb_error_msg("cur_history:%d cnt_history:%d", state->cur_history, state->cnt_history); |
1829 |
#endif |
#endif |
1830 |
|
|
1831 |
/* Print out the command prompt */ |
/* Print out the command prompt, optionally ask where cursor is */ |
1832 |
parse_and_put_prompt(prompt); |
parse_and_put_prompt(prompt); |
1833 |
|
ask_terminal(); |
1834 |
|
|
1835 |
|
read_key_buffer[0] = 0; |
1836 |
while (1) { |
while (1) { |
1837 |
fflush(NULL); |
/* |
1838 |
|
* The emacs and vi modes share much of the code in the big |
1839 |
if (nonblock_safe_read(STDIN_FILENO, &c, 1) < 1) { |
* command loop. Commands entered when in vi's command mode |
1840 |
/* if we can't read input then exit */ |
* (aka "escape mode") get an extra bit added to distinguish |
1841 |
goto prepare_to_die; |
* them - this keeps them from being self-inserted. This |
1842 |
} |
* clutters the big switch a bit, but keeps all the code |
1843 |
|
* in one place. |
1844 |
|
*/ |
1845 |
|
enum { |
1846 |
|
VI_CMDMODE_BIT = 0x40000000, |
1847 |
|
/* 0x80000000 bit flags KEYCODE_xxx */ |
1848 |
|
}; |
1849 |
|
int32_t ic, ic_raw; |
1850 |
|
|
1851 |
ic = c; |
fflush_all(); |
1852 |
|
ic = ic_raw = lineedit_read_key(read_key_buffer); |
1853 |
|
|
1854 |
#if ENABLE_FEATURE_EDITING_VI |
#if ENABLE_FEATURE_EDITING_VI |
1855 |
newdelflag = 1; |
newdelflag = 1; |
1856 |
if (vi_cmdmode) |
if (vi_cmdmode) { |
1857 |
ic |= vbit; |
/* btw, since KEYCODE_xxx are all < 0, this doesn't |
1858 |
|
* change ic if it contains one of them: */ |
1859 |
|
ic |= VI_CMDMODE_BIT; |
1860 |
|
} |
1861 |
#endif |
#endif |
1862 |
|
|
1863 |
switch (ic) { |
switch (ic) { |
1864 |
case '\n': |
case '\n': |
1865 |
case '\r': |
case '\r': |
1866 |
vi_case('\n'|vbit:) |
vi_case('\n'|VI_CMDMODE_BIT:) |
1867 |
vi_case('\r'|vbit:) |
vi_case('\r'|VI_CMDMODE_BIT:) |
1868 |
/* Enter */ |
/* Enter */ |
1869 |
goto_new_line(); |
goto_new_line(); |
1870 |
break_out = 1; |
break_out = 1; |
1871 |
break; |
break; |
1872 |
case CTRL('A'): |
case CTRL('A'): |
1873 |
vi_case('0'|vbit:) |
vi_case('0'|VI_CMDMODE_BIT:) |
1874 |
/* Control-a -- Beginning of line */ |
/* Control-a -- Beginning of line */ |
1875 |
input_backward(cursor); |
input_backward(cursor); |
1876 |
break; |
break; |
1877 |
case CTRL('B'): |
case CTRL('B'): |
1878 |
vi_case('h'|vbit:) |
vi_case('h'|VI_CMDMODE_BIT:) |
1879 |
vi_case('\b'|vbit:) |
vi_case('\b'|VI_CMDMODE_BIT:) |
1880 |
vi_case('\x7f'|vbit:) /* DEL */ |
vi_case('\x7f'|VI_CMDMODE_BIT:) /* DEL */ |
1881 |
/* Control-b -- Move back one character */ |
/* Control-b -- Move back one character */ |
1882 |
input_backward(1); |
input_backward(1); |
1883 |
break; |
break; |
|
case CTRL('C'): |
|
|
vi_case(CTRL('C')|vbit:) |
|
|
/* Control-c -- stop gathering input */ |
|
|
goto_new_line(); |
|
|
command_len = 0; |
|
|
break_out = -1; /* "do not append '\n'" */ |
|
|
break; |
|
|
case CTRL('D'): |
|
|
/* Control-d -- Delete one character, or exit |
|
|
* if the len=0 and no chars to delete */ |
|
|
if (command_len == 0) { |
|
|
errno = 0; |
|
|
prepare_to_die: |
|
|
/* to control stopped jobs */ |
|
|
break_out = command_len = -1; |
|
|
break; |
|
|
} |
|
|
input_delete(0); |
|
|
break; |
|
|
|
|
1884 |
case CTRL('E'): |
case CTRL('E'): |
1885 |
vi_case('$'|vbit:) |
vi_case('$'|VI_CMDMODE_BIT:) |
1886 |
/* Control-e -- End of line */ |
/* Control-e -- End of line */ |
1887 |
input_end(); |
input_end(); |
1888 |
break; |
break; |
1889 |
case CTRL('F'): |
case CTRL('F'): |
1890 |
vi_case('l'|vbit:) |
vi_case('l'|VI_CMDMODE_BIT:) |
1891 |
vi_case(' '|vbit:) |
vi_case(' '|VI_CMDMODE_BIT:) |
1892 |
/* Control-f -- Move forward one character */ |
/* Control-f -- Move forward one character */ |
1893 |
input_forward(); |
input_forward(); |
1894 |
break; |
break; |
|
|
|
1895 |
case '\b': |
case '\b': |
1896 |
case '\x7f': /* DEL */ |
case '\x7f': /* DEL */ |
1897 |
/* Control-h and DEL */ |
/* Control-h and DEL */ |
1898 |
input_backspace(); |
input_backspace(); |
1899 |
break; |
break; |
|
|
|
1900 |
#if ENABLE_FEATURE_TAB_COMPLETION |
#if ENABLE_FEATURE_TAB_COMPLETION |
1901 |
case '\t': |
case '\t': |
1902 |
input_tab(&lastWasTab); |
input_tab(&lastWasTab); |
1903 |
break; |
break; |
1904 |
#endif |
#endif |
|
|
|
1905 |
case CTRL('K'): |
case CTRL('K'): |
1906 |
/* Control-k -- clear to end of line */ |
/* Control-k -- clear to end of line */ |
1907 |
command[cursor] = 0; |
command_ps[cursor] = BB_NUL; |
1908 |
command_len = cursor; |
command_len = cursor; |
1909 |
printf("\033[J"); |
printf("\033[J"); |
1910 |
break; |
break; |
1911 |
case CTRL('L'): |
case CTRL('L'): |
1912 |
vi_case(CTRL('L')|vbit:) |
vi_case(CTRL('L')|VI_CMDMODE_BIT:) |
1913 |
/* Control-l -- clear screen */ |
/* Control-l -- clear screen */ |
1914 |
printf("\033[H"); |
printf("\033[H"); |
1915 |
redraw(0, command_len - cursor); |
redraw(0, command_len - cursor); |
1916 |
break; |
break; |
|
|
|
1917 |
#if MAX_HISTORY > 0 |
#if MAX_HISTORY > 0 |
1918 |
case CTRL('N'): |
case CTRL('N'): |
1919 |
vi_case(CTRL('N')|vbit:) |
vi_case(CTRL('N')|VI_CMDMODE_BIT:) |
1920 |
vi_case('j'|vbit:) |
vi_case('j'|VI_CMDMODE_BIT:) |
1921 |
/* Control-n -- Get next command in history */ |
/* Control-n -- Get next command in history */ |
1922 |
if (get_next_history()) |
if (get_next_history()) |
1923 |
goto rewrite_line; |
goto rewrite_line; |
1924 |
break; |
break; |
1925 |
case CTRL('P'): |
case CTRL('P'): |
1926 |
vi_case(CTRL('P')|vbit:) |
vi_case(CTRL('P')|VI_CMDMODE_BIT:) |
1927 |
vi_case('k'|vbit:) |
vi_case('k'|VI_CMDMODE_BIT:) |
1928 |
/* Control-p -- Get previous command from history */ |
/* Control-p -- Get previous command from history */ |
1929 |
if (get_previous_history()) |
if (get_previous_history()) |
1930 |
goto rewrite_line; |
goto rewrite_line; |
1931 |
break; |
break; |
1932 |
#endif |
#endif |
|
|
|
1933 |
case CTRL('U'): |
case CTRL('U'): |
1934 |
vi_case(CTRL('U')|vbit:) |
vi_case(CTRL('U')|VI_CMDMODE_BIT:) |
1935 |
/* Control-U -- Clear line before cursor */ |
/* Control-U -- Clear line before cursor */ |
1936 |
if (cursor) { |
if (cursor) { |
|
overlapping_strcpy(command, command + cursor); |
|
1937 |
command_len -= cursor; |
command_len -= cursor; |
1938 |
|
memmove(command_ps, command_ps + cursor, |
1939 |
|
(command_len + 1) * sizeof(command_ps[0])); |
1940 |
redraw(cmdedit_y, command_len); |
redraw(cmdedit_y, command_len); |
1941 |
} |
} |
1942 |
break; |
break; |
1943 |
case CTRL('W'): |
case CTRL('W'): |
1944 |
vi_case(CTRL('W')|vbit:) |
vi_case(CTRL('W')|VI_CMDMODE_BIT:) |
1945 |
/* Control-W -- Remove the last word */ |
/* Control-W -- Remove the last word */ |
1946 |
while (cursor > 0 && isspace(command[cursor-1])) |
while (cursor > 0 && BB_isspace(command_ps[cursor-1])) |
1947 |
input_backspace(); |
input_backspace(); |
1948 |
while (cursor > 0 && !isspace(command[cursor-1])) |
while (cursor > 0 && !BB_isspace(command_ps[cursor-1])) |
1949 |
input_backspace(); |
input_backspace(); |
1950 |
break; |
break; |
1951 |
|
|
1952 |
#if ENABLE_FEATURE_EDITING_VI |
#if ENABLE_FEATURE_EDITING_VI |
1953 |
case 'i'|vbit: |
case 'i'|VI_CMDMODE_BIT: |
1954 |
vi_cmdmode = 0; |
vi_cmdmode = 0; |
1955 |
break; |
break; |
1956 |
case 'I'|vbit: |
case 'I'|VI_CMDMODE_BIT: |
1957 |
input_backward(cursor); |
input_backward(cursor); |
1958 |
vi_cmdmode = 0; |
vi_cmdmode = 0; |
1959 |
break; |
break; |
1960 |
case 'a'|vbit: |
case 'a'|VI_CMDMODE_BIT: |
1961 |
input_forward(); |
input_forward(); |
1962 |
vi_cmdmode = 0; |
vi_cmdmode = 0; |
1963 |
break; |
break; |
1964 |
case 'A'|vbit: |
case 'A'|VI_CMDMODE_BIT: |
1965 |
input_end(); |
input_end(); |
1966 |
vi_cmdmode = 0; |
vi_cmdmode = 0; |
1967 |
break; |
break; |
1968 |
case 'x'|vbit: |
case 'x'|VI_CMDMODE_BIT: |
1969 |
input_delete(1); |
input_delete(1); |
1970 |
break; |
break; |
1971 |
case 'X'|vbit: |
case 'X'|VI_CMDMODE_BIT: |
1972 |
if (cursor > 0) { |
if (cursor > 0) { |
1973 |
input_backward(1); |
input_backward(1); |
1974 |
input_delete(1); |
input_delete(1); |
1975 |
} |
} |
1976 |
break; |
break; |
1977 |
case 'W'|vbit: |
case 'W'|VI_CMDMODE_BIT: |
1978 |
vi_Word_motion(command, 1); |
vi_Word_motion(1); |
1979 |
break; |
break; |
1980 |
case 'w'|vbit: |
case 'w'|VI_CMDMODE_BIT: |
1981 |
vi_word_motion(command, 1); |
vi_word_motion(1); |
1982 |
break; |
break; |
1983 |
case 'E'|vbit: |
case 'E'|VI_CMDMODE_BIT: |
1984 |
vi_End_motion(command); |
vi_End_motion(); |
1985 |
break; |
break; |
1986 |
case 'e'|vbit: |
case 'e'|VI_CMDMODE_BIT: |
1987 |
vi_end_motion(command); |
vi_end_motion(); |
1988 |
break; |
break; |
1989 |
case 'B'|vbit: |
case 'B'|VI_CMDMODE_BIT: |
1990 |
vi_Back_motion(command); |
vi_Back_motion(); |
1991 |
break; |
break; |
1992 |
case 'b'|vbit: |
case 'b'|VI_CMDMODE_BIT: |
1993 |
vi_back_motion(command); |
vi_back_motion(); |
1994 |
break; |
break; |
1995 |
case 'C'|vbit: |
case 'C'|VI_CMDMODE_BIT: |
1996 |
vi_cmdmode = 0; |
vi_cmdmode = 0; |
1997 |
/* fall through */ |
/* fall through */ |
1998 |
case 'D'|vbit: |
case 'D'|VI_CMDMODE_BIT: |
1999 |
goto clear_to_eol; |
goto clear_to_eol; |
2000 |
|
|
2001 |
case 'c'|vbit: |
case 'c'|VI_CMDMODE_BIT: |
2002 |
vi_cmdmode = 0; |
vi_cmdmode = 0; |
2003 |
/* fall through */ |
/* fall through */ |
2004 |
case 'd'|vbit: { |
case 'd'|VI_CMDMODE_BIT: { |
2005 |
int nc, sc; |
int nc, sc; |
2006 |
sc = cursor; |
|
2007 |
prevc = ic; |
ic = lineedit_read_key(read_key_buffer); |
2008 |
if (safe_read(STDIN_FILENO, &c, 1) < 1) |
if (errno) /* error */ |
2009 |
goto prepare_to_die; |
goto prepare_to_die; |
2010 |
if (c == (prevc & 0xff)) { |
if (ic == ic_raw) { /* "cc", "dd" */ |
|
/* "cc", "dd" */ |
|
2011 |
input_backward(cursor); |
input_backward(cursor); |
2012 |
goto clear_to_eol; |
goto clear_to_eol; |
2013 |
break; |
break; |
2014 |
} |
} |
2015 |
switch (c) { |
|
2016 |
|
sc = cursor; |
2017 |
|
switch (ic) { |
2018 |
case 'w': |
case 'w': |
2019 |
case 'W': |
case 'W': |
2020 |
case 'e': |
case 'e': |
2021 |
case 'E': |
case 'E': |
2022 |
switch (c) { |
switch (ic) { |
2023 |
case 'w': /* "dw", "cw" */ |
case 'w': /* "dw", "cw" */ |
2024 |
vi_word_motion(command, vi_cmdmode); |
vi_word_motion(vi_cmdmode); |
2025 |
break; |
break; |
2026 |
case 'W': /* 'dW', 'cW' */ |
case 'W': /* 'dW', 'cW' */ |
2027 |
vi_Word_motion(command, vi_cmdmode); |
vi_Word_motion(vi_cmdmode); |
2028 |
break; |
break; |
2029 |
case 'e': /* 'de', 'ce' */ |
case 'e': /* 'de', 'ce' */ |
2030 |
vi_end_motion(command); |
vi_end_motion(); |
2031 |
input_forward(); |
input_forward(); |
2032 |
break; |
break; |
2033 |
case 'E': /* 'dE', 'cE' */ |
case 'E': /* 'dE', 'cE' */ |
2034 |
vi_End_motion(command); |
vi_End_motion(); |
2035 |
input_forward(); |
input_forward(); |
2036 |
break; |
break; |
2037 |
} |
} |
2042 |
break; |
break; |
2043 |
case 'b': /* "db", "cb" */ |
case 'b': /* "db", "cb" */ |
2044 |
case 'B': /* implemented as B */ |
case 'B': /* implemented as B */ |
2045 |
if (c == 'b') |
if (ic == 'b') |
2046 |
vi_back_motion(command); |
vi_back_motion(); |
2047 |
else |
else |
2048 |
vi_Back_motion(command); |
vi_Back_motion(); |
2049 |
while (sc-- > cursor) |
while (sc-- > cursor) |
2050 |
input_delete(1); |
input_delete(1); |
2051 |
break; |
break; |
2053 |
input_delete(1); |
input_delete(1); |
2054 |
break; |
break; |
2055 |
case '$': /* "d$", "c$" */ |
case '$': /* "d$", "c$" */ |
2056 |
clear_to_eol: |
clear_to_eol: |
2057 |
while (cursor < command_len) |
while (cursor < command_len) |
2058 |
input_delete(1); |
input_delete(1); |
2059 |
break; |
break; |
2060 |
} |
} |
2061 |
break; |
break; |
2062 |
} |
} |
2063 |
case 'p'|vbit: |
case 'p'|VI_CMDMODE_BIT: |
2064 |
input_forward(); |
input_forward(); |
2065 |
/* fallthrough */ |
/* fallthrough */ |
2066 |
case 'P'|vbit: |
case 'P'|VI_CMDMODE_BIT: |
2067 |
put(); |
put(); |
2068 |
break; |
break; |
2069 |
case 'r'|vbit: |
case 'r'|VI_CMDMODE_BIT: |
2070 |
if (safe_read(STDIN_FILENO, &c, 1) < 1) |
//FIXME: unicode case? |
2071 |
|
ic = lineedit_read_key(read_key_buffer); |
2072 |
|
if (errno) /* error */ |
2073 |
goto prepare_to_die; |
goto prepare_to_die; |
2074 |
if (c == 0) |
if (ic < ' ' || ic > 255) { |
2075 |
beep(); |
beep(); |
2076 |
else { |
} else { |
2077 |
*(command + cursor) = c; |
command_ps[cursor] = ic; |
2078 |
bb_putchar(c); |
bb_putchar(ic); |
2079 |
bb_putchar('\b'); |
bb_putchar('\b'); |
2080 |
} |
} |
2081 |
break; |
break; |
|
#endif /* FEATURE_COMMAND_EDITING_VI */ |
|
|
|
|
2082 |
case '\x1b': /* ESC */ |
case '\x1b': /* ESC */ |
|
|
|
|
#if ENABLE_FEATURE_EDITING_VI |
|
2083 |
if (state->flags & VI_MODE) { |
if (state->flags & VI_MODE) { |
2084 |
/* ESC: insert mode --> command mode */ |
/* insert mode --> command mode */ |
2085 |
vi_cmdmode = 1; |
vi_cmdmode = 1; |
2086 |
input_backward(1); |
input_backward(1); |
|
break; |
|
|
} |
|
|
#endif |
|
|
/* escape sequence follows */ |
|
|
if (safe_read(STDIN_FILENO, &c, 1) < 1) |
|
|
goto prepare_to_die; |
|
|
/* different vt100 emulations */ |
|
|
if (c == '[' || c == 'O') { |
|
|
vi_case('['|vbit:) |
|
|
vi_case('O'|vbit:) |
|
|
if (safe_read(STDIN_FILENO, &c, 1) < 1) |
|
|
goto prepare_to_die; |
|
|
} |
|
|
if (c >= '1' && c <= '9') { |
|
|
unsigned char dummy; |
|
|
|
|
|
if (safe_read(STDIN_FILENO, &dummy, 1) < 1) |
|
|
goto prepare_to_die; |
|
|
if (dummy != '~') |
|
|
c = '\0'; |
|
2087 |
} |
} |
2088 |
|
break; |
2089 |
|
#endif /* FEATURE_COMMAND_EDITING_VI */ |
2090 |
|
|
|
switch (c) { |
|
|
#if ENABLE_FEATURE_TAB_COMPLETION |
|
|
case '\t': /* Alt-Tab */ |
|
|
input_tab(&lastWasTab); |
|
|
break; |
|
|
#endif |
|
2091 |
#if MAX_HISTORY > 0 |
#if MAX_HISTORY > 0 |
2092 |
case 'A': |
case KEYCODE_UP: |
2093 |
/* Up Arrow -- Get previous command from history */ |
if (get_previous_history()) |
2094 |
if (get_previous_history()) |
goto rewrite_line; |
2095 |
goto rewrite_line; |
beep(); |
2096 |
beep(); |
break; |
2097 |
|
case KEYCODE_DOWN: |
2098 |
|
if (!get_next_history()) |
2099 |
break; |
break; |
|
case 'B': |
|
|
/* Down Arrow -- Get next command in history */ |
|
|
if (!get_next_history()) |
|
|
break; |
|
2100 |
rewrite_line: |
rewrite_line: |
2101 |
/* Rewrite the line with the selected history item */ |
/* Rewrite the line with the selected history item */ |
2102 |
/* change command */ |
/* change command */ |
2103 |
command_len = strlen(strcpy(command, state->history[state->cur_history] ? : "")); |
command_len = load_string(state->history[state->cur_history] ? |
2104 |
/* redraw and go to eol (bol, in vi */ |
state->history[state->cur_history] : "", maxsize); |
2105 |
redraw(cmdedit_y, (state->flags & VI_MODE) ? 9999 : 0); |
/* redraw and go to eol (bol, in vi) */ |
2106 |
break; |
redraw(cmdedit_y, (state->flags & VI_MODE) ? 9999 : 0); |
2107 |
|
break; |
2108 |
#endif |
#endif |
2109 |
case 'C': |
case KEYCODE_RIGHT: |
2110 |
/* Right Arrow -- Move forward one character */ |
input_forward(); |
2111 |
input_forward(); |
break; |
2112 |
break; |
case KEYCODE_LEFT: |
2113 |
case 'D': |
input_backward(1); |
2114 |
/* Left Arrow -- Move back one character */ |
break; |
2115 |
input_backward(1); |
case KEYCODE_CTRL_LEFT: |
2116 |
break; |
ctrl_left(); |
2117 |
case '3': |
break; |
2118 |
/* Delete */ |
case KEYCODE_CTRL_RIGHT: |
2119 |
input_delete(0); |
ctrl_right(); |
2120 |
break; |
break; |
2121 |
case '1': // vt100? linux vt? or what? |
case KEYCODE_DELETE: |
2122 |
case '7': // vt100? linux vt? or what? |
input_delete(0); |
2123 |
case 'H': /* xterm's <Home> */ |
break; |
2124 |
input_backward(cursor); |
case KEYCODE_HOME: |
2125 |
break; |
input_backward(cursor); |
2126 |
case '4': // vt100? linux vt? or what? |
break; |
2127 |
case '8': // vt100? linux vt? or what? |
case KEYCODE_END: |
2128 |
case 'F': /* xterm's <End> */ |
input_end(); |
|
input_end(); |
|
|
break; |
|
|
default: |
|
|
c = '\0'; |
|
|
beep(); |
|
|
} |
|
2129 |
break; |
break; |
2130 |
|
|
2131 |
default: /* If it's regular input, do the normal thing */ |
default: |
2132 |
|
if (initial_settings.c_cc[VINTR] != 0 |
2133 |
/* Control-V -- force insert of next char */ |
&& ic_raw == initial_settings.c_cc[VINTR] |
2134 |
if (c == CTRL('V')) { |
) { |
2135 |
if (safe_read(STDIN_FILENO, &c, 1) < 1) |
/* Ctrl-C (usually) - stop gathering input */ |
2136 |
goto prepare_to_die; |
goto_new_line(); |
2137 |
if (c == 0) { |
command_len = 0; |
2138 |
beep(); |
break_out = -1; /* "do not append '\n'" */ |
2139 |
|
break; |
2140 |
|
} |
2141 |
|
if (initial_settings.c_cc[VEOF] != 0 |
2142 |
|
&& ic_raw == initial_settings.c_cc[VEOF] |
2143 |
|
) { |
2144 |
|
/* Ctrl-D (usually) - delete one character, |
2145 |
|
* or exit if len=0 and no chars to delete */ |
2146 |
|
if (command_len == 0) { |
2147 |
|
errno = 0; |
2148 |
|
#if ENABLE_FEATURE_EDITING_VI |
2149 |
|
prepare_to_die: |
2150 |
|
#endif |
2151 |
|
break_out = command_len = -1; |
2152 |
break; |
break; |
2153 |
} |
} |
2154 |
|
input_delete(0); |
2155 |
|
break; |
2156 |
} |
} |
2157 |
|
// /* Control-V -- force insert of next char */ |
2158 |
#if ENABLE_FEATURE_EDITING_VI |
// if (c == CTRL('V')) { |
2159 |
if (vi_cmdmode) /* Don't self-insert */ |
// if (safe_read(STDIN_FILENO, &c, 1) < 1) |
2160 |
|
// goto prepare_to_die; |
2161 |
|
// if (c == 0) { |
2162 |
|
// beep(); |
2163 |
|
// break; |
2164 |
|
// } |
2165 |
|
// } |
2166 |
|
if (ic < ' ' |
2167 |
|
|| (!ENABLE_FEATURE_ASSUME_UNICODE && ic >= 256) |
2168 |
|
|| (ENABLE_FEATURE_ASSUME_UNICODE && ic >= VI_CMDMODE_BIT) |
2169 |
|
) { |
2170 |
|
/* If VI_CMDMODE_BIT is set, ic is >= 256 |
2171 |
|
* and vi mode ignores unexpected chars. |
2172 |
|
* Otherwise, we are here if ic is a |
2173 |
|
* control char or an unhandled ESC sequence, |
2174 |
|
* which is also ignored. |
2175 |
|
*/ |
2176 |
break; |
break; |
2177 |
#endif |
} |
2178 |
if ((int)command_len >= (maxsize - 2)) /* Need to leave space for enter */ |
if ((int)command_len >= (maxsize - 2)) { |
2179 |
|
/* Not enough space for the char and EOL */ |
2180 |
break; |
break; |
2181 |
|
} |
2182 |
|
|
2183 |
command_len++; |
command_len++; |
2184 |
if (cursor == (command_len - 1)) { /* Append if at the end of the line */ |
if (cursor == (command_len - 1)) { |
2185 |
command[cursor] = c; |
/* We are at the end, append */ |
2186 |
command[cursor+1] = '\0'; |
command_ps[cursor] = ic; |
2187 |
|
command_ps[cursor + 1] = BB_NUL; |
2188 |
cmdedit_set_out_char(' '); |
cmdedit_set_out_char(' '); |
2189 |
} else { /* Insert otherwise */ |
} else { |
2190 |
|
/* In the middle, insert */ |
2191 |
int sc = cursor; |
int sc = cursor; |
2192 |
|
|
2193 |
memmove(command + sc + 1, command + sc, command_len - sc); |
memmove(command_ps + sc + 1, command_ps + sc, |
2194 |
command[sc] = c; |
(command_len - sc) * sizeof(command_ps[0])); |
2195 |
|
command_ps[sc] = ic; |
2196 |
sc++; |
sc++; |
2197 |
/* rewrite from cursor */ |
/* rewrite from cursor */ |
2198 |
input_end(); |
input_end(); |
2200 |
input_backward(cursor - sc); |
input_backward(cursor - sc); |
2201 |
} |
} |
2202 |
break; |
break; |
2203 |
} |
} /* switch (ic) */ |
2204 |
if (break_out) /* Enter is the command terminator, no more input. */ |
|
2205 |
|
if (break_out) |
2206 |
break; |
break; |
2207 |
|
|
2208 |
#if ENABLE_FEATURE_TAB_COMPLETION |
#if ENABLE_FEATURE_TAB_COMPLETION |
2209 |
if (c != '\t') |
if (ic_raw != '\t') |
2210 |
lastWasTab = FALSE; |
lastWasTab = FALSE; |
2211 |
#endif |
#endif |
2212 |
|
} /* while (1) */ |
2213 |
|
|
2214 |
|
#if ENABLE_FEATURE_EDITING_ASK_TERMINAL |
2215 |
|
if (S.sent_ESC_br6n) { |
2216 |
|
/* "sleep 1; busybox ash" + hold [Enter] to trigger. |
2217 |
|
* We sent "ESC [ 6 n", but got '\n' first, and |
2218 |
|
* KEYCODE_CURSOR_POS response is now buffered from terminal. |
2219 |
|
* It's bad already and not much can be done with it |
2220 |
|
* (it _will_ be visible for the next process to read stdin), |
2221 |
|
* but without this delay it even shows up on the screen |
2222 |
|
* as garbage because we restore echo settings with tcsetattr |
2223 |
|
* before it comes in. UGLY! |
2224 |
|
*/ |
2225 |
|
usleep(20*1000); |
2226 |
} |
} |
2227 |
|
#endif |
2228 |
|
|
2229 |
|
/* Stop bug catching using "command_must_not_be_used" trick */ |
2230 |
|
#undef command |
2231 |
|
|
2232 |
|
#if ENABLE_FEATURE_ASSUME_UNICODE |
2233 |
|
command[0] = '\0'; |
2234 |
|
if (command_len > 0) |
2235 |
|
command_len = save_string(command, maxsize - 1); |
2236 |
|
free(command_ps); |
2237 |
|
#endif |
2238 |
|
|
2239 |
if (command_len > 0) |
if (command_len > 0) |
2240 |
remember_in_history(command); |
remember_in_history(command); |
2252 |
tcsetattr_stdin_TCSANOW(&initial_settings); |
tcsetattr_stdin_TCSANOW(&initial_settings); |
2253 |
/* restore SIGWINCH handler */ |
/* restore SIGWINCH handler */ |
2254 |
signal(SIGWINCH, previous_SIGWINCH_handler); |
signal(SIGWINCH, previous_SIGWINCH_handler); |
2255 |
fflush(stdout); |
fflush_all(); |
2256 |
|
|
2257 |
len = command_len; |
len = command_len; |
2258 |
DEINIT_S(); |
DEINIT_S(); |
2260 |
return len; /* can't return command_len, DEINIT_S() destroys it */ |
return len; /* can't return command_len, DEINIT_S() destroys it */ |
2261 |
} |
} |
2262 |
|
|
|
line_input_t* FAST_FUNC new_line_input_t(int flags) |
|
|
{ |
|
|
line_input_t *n = xzalloc(sizeof(*n)); |
|
|
n->flags = flags; |
|
|
return n; |
|
|
} |
|
|
|
|
2263 |
#else |
#else |
2264 |
|
|
2265 |
#undef read_line_input |
#undef read_line_input |
2266 |
int FAST_FUNC read_line_input(const char* prompt, char* command, int maxsize) |
int FAST_FUNC read_line_input(const char* prompt, char* command, int maxsize) |
2267 |
{ |
{ |
2268 |
fputs(prompt, stdout); |
fputs(prompt, stdout); |
2269 |
fflush(stdout); |
fflush_all(); |
2270 |
fgets(command, maxsize, stdin); |
fgets(command, maxsize, stdin); |
2271 |
return strlen(command); |
return strlen(command); |
2272 |
} |
} |
2273 |
|
|
2274 |
#endif /* FEATURE_COMMAND_EDITING */ |
#endif /* FEATURE_EDITING */ |
2275 |
|
|
2276 |
|
|
2277 |
/* |
/* |