source: MondoRescue/branches/3.3/mindi-busybox/libbb/lineedit.c@ 3865

Last change on this file since 3865 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

  • Property svn:eol-style set to native
File size: 70.7 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * Command line editing.
4 *
5 * Copyright (c) 1986-2003 may safely be consumed by a BSD or GPL license.
6 * Written by: Vladimir Oleynik <dzo@simtreas.ru>
7 *
8 * Used ideas:
9 * Adam Rogoyski <rogoyski@cs.utexas.edu>
10 * Dave Cinege <dcinege@psychosis.com>
11 * Jakub Jelinek (c) 1995
12 * Erik Andersen <andersen@codepoet.org> (Majorly adjusted for busybox)
13 *
14 * This code is 'as is' with no warranty.
15 */
16
17/*
18 * Usage and known bugs:
19 * Terminal key codes are not extensive, more needs to be added.
20 * This version was created on Debian GNU/Linux 2.x.
21 * Delete, Backspace, Home, End, and the arrow keys were tested
22 * to work in an Xterm and console. Ctrl-A also works as Home.
23 * 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
32 * take up space on the screen. The redisplay code assumes, unless
33 * told otherwise, that each character in the prompt is a printable
34 * character that takes up one character position on the screen.
35 * You need to tell lineedit that some sequences of characters
36 * in the prompt take up no screen space. Compatibly with readline,
37 * use the \[ escape to begin a sequence of non-printing characters,
38 * and the \] escape to signal the end of such a sequence. Example:
39 *
40 * PS1='\[\033[01;32m\]\u@\h\[\033[01;34m\] \w \$\[\033[00m\] '
41 *
42 * Unicode in PS1 is not fully supported: prompt length calulation is wrong,
43 * resulting in line wrap problems with long (multi-line) input.
44 *
45 * Multi-line PS1 (e.g. PS1="\n[\w]\n$ ") has problems with history
46 * browsing: up/down arrows result in scrolling.
47 * It stems from simplistic "cmdedit_y = cmdedit_prmt_len / cmdedit_termw"
48 * calculation of how many lines the prompt takes.
49 */
50#include "busybox.h"
51#include "NUM_APPLETS.h"
52#include "unicode.h"
53#ifndef _POSIX_VDISABLE
54# define _POSIX_VDISABLE '\0'
55#endif
56
57
58#ifdef TEST
59# define ENABLE_FEATURE_EDITING 0
60# define ENABLE_FEATURE_TAB_COMPLETION 0
61# define ENABLE_FEATURE_USERNAME_COMPLETION 0
62#endif
63
64
65/* Entire file (except TESTing part) sits inside this #if */
66#if ENABLE_FEATURE_EDITING
67
68
69#define ENABLE_USERNAME_OR_HOMEDIR \
70 (ENABLE_FEATURE_USERNAME_COMPLETION || ENABLE_FEATURE_EDITING_FANCY_PROMPT)
71#define IF_USERNAME_OR_HOMEDIR(...)
72#if ENABLE_USERNAME_OR_HOMEDIR
73# undef IF_USERNAME_OR_HOMEDIR
74# define IF_USERNAME_OR_HOMEDIR(...) __VA_ARGS__
75#endif
76
77
78#undef CHAR_T
79#if ENABLE_UNICODE_SUPPORT
80# define BB_NUL ((wchar_t)0)
81# define CHAR_T wchar_t
82static bool BB_isspace(CHAR_T c) { return ((unsigned)c < 256 && isspace(c)); }
83# if ENABLE_FEATURE_EDITING_VI
84static bool BB_isalnum(CHAR_T c) { return ((unsigned)c < 256 && isalnum(c)); }
85# endif
86static bool BB_ispunct(CHAR_T c) { return ((unsigned)c < 256 && ispunct(c)); }
87# undef isspace
88# undef isalnum
89# undef ispunct
90# undef isprint
91# define isspace isspace_must_not_be_used
92# define isalnum isalnum_must_not_be_used
93# define ispunct ispunct_must_not_be_used
94# define isprint isprint_must_not_be_used
95#else
96# define BB_NUL '\0'
97# define CHAR_T char
98# define BB_isspace(c) isspace(c)
99# define BB_isalnum(c) isalnum(c)
100# define BB_ispunct(c) ispunct(c)
101#endif
102#if ENABLE_UNICODE_PRESERVE_BROKEN
103# define unicode_mark_raw_byte(wc) ((wc) | 0x20000000)
104# define unicode_is_raw_byte(wc) ((wc) & 0x20000000)
105#else
106# define unicode_is_raw_byte(wc) 0
107#endif
108
109
110#define ESC "\033"
111
112#define SEQ_CLEAR_TILL_END_OF_SCREEN ESC"[J"
113//#define SEQ_CLEAR_TILL_END_OF_LINE ESC"[K"
114
115
116enum {
117 MAX_LINELEN = CONFIG_FEATURE_EDITING_MAX_LEN < 0x7ff0
118 ? CONFIG_FEATURE_EDITING_MAX_LEN
119 : 0x7ff0
120};
121
122#if ENABLE_USERNAME_OR_HOMEDIR
123static const char null_str[] ALIGN1 = "";
124#endif
125
126/* We try to minimize both static and stack usage. */
127struct lineedit_statics {
128 line_input_t *state;
129
130 volatile unsigned cmdedit_termw; /* = 80; */ /* actual terminal width */
131 sighandler_t previous_SIGWINCH_handler;
132
133 unsigned cmdedit_x; /* real x (col) terminal position */
134 unsigned cmdedit_y; /* pseudoreal y (row) terminal position */
135 unsigned cmdedit_prmt_len; /* length of prompt (without colors etc) */
136
137 unsigned cursor;
138 int command_len; /* must be signed */
139 /* signed maxsize: we want x in "if (x > S.maxsize)"
140 * to _not_ be promoted to unsigned */
141 int maxsize;
142 CHAR_T *command_ps;
143
144 const char *cmdedit_prompt;
145
146#if ENABLE_USERNAME_OR_HOMEDIR
147 char *user_buf;
148 char *home_pwd_buf; /* = (char*)null_str; */
149#endif
150
151#if ENABLE_FEATURE_TAB_COMPLETION
152 char **matches;
153 unsigned num_matches;
154#endif
155
156#if ENABLE_FEATURE_EDITING_VI
157# define DELBUFSIZ 128
158 CHAR_T *delptr;
159 smallint newdelflag; /* whether delbuf should be reused yet */
160 CHAR_T delbuf[DELBUFSIZ]; /* a place to store deleted characters */
161#endif
162#if ENABLE_FEATURE_EDITING_ASK_TERMINAL
163 smallint sent_ESC_br6n;
164#endif
165};
166
167/* See lineedit_ptr_hack.c */
168extern struct lineedit_statics *const lineedit_ptr_to_statics;
169
170#define S (*lineedit_ptr_to_statics)
171#define state (S.state )
172#define cmdedit_termw (S.cmdedit_termw )
173#define previous_SIGWINCH_handler (S.previous_SIGWINCH_handler)
174#define cmdedit_x (S.cmdedit_x )
175#define cmdedit_y (S.cmdedit_y )
176#define cmdedit_prmt_len (S.cmdedit_prmt_len)
177#define cursor (S.cursor )
178#define command_len (S.command_len )
179#define command_ps (S.command_ps )
180#define cmdedit_prompt (S.cmdedit_prompt )
181#define user_buf (S.user_buf )
182#define home_pwd_buf (S.home_pwd_buf )
183#define matches (S.matches )
184#define num_matches (S.num_matches )
185#define delptr (S.delptr )
186#define newdelflag (S.newdelflag )
187#define delbuf (S.delbuf )
188
189#define INIT_S() do { \
190 (*(struct lineedit_statics**)&lineedit_ptr_to_statics) = xzalloc(sizeof(S)); \
191 barrier(); \
192 cmdedit_termw = 80; \
193 IF_USERNAME_OR_HOMEDIR(home_pwd_buf = (char*)null_str;) \
194 IF_FEATURE_EDITING_VI(delptr = delbuf;) \
195} while (0)
196
197static void deinit_S(void)
198{
199#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
200 /* This one is allocated only if FANCY_PROMPT is on
201 * (otherwise it points to verbatim prompt (NOT malloced)) */
202 free((char*)cmdedit_prompt);
203#endif
204#if ENABLE_USERNAME_OR_HOMEDIR
205 free(user_buf);
206 if (home_pwd_buf != null_str)
207 free(home_pwd_buf);
208#endif
209 free(lineedit_ptr_to_statics);
210}
211#define DEINIT_S() deinit_S()
212
213
214#if ENABLE_UNICODE_SUPPORT
215static size_t load_string(const char *src)
216{
217 if (unicode_status == UNICODE_ON) {
218 ssize_t len = mbstowcs(command_ps, src, S.maxsize - 1);
219 if (len < 0)
220 len = 0;
221 command_ps[len] = BB_NUL;
222 return len;
223 } else {
224 unsigned i = 0;
225 while (src[i] && i < S.maxsize - 1) {
226 command_ps[i] = src[i];
227 i++;
228 }
229 command_ps[i] = BB_NUL;
230 return i;
231 }
232}
233static unsigned save_string(char *dst, unsigned maxsize)
234{
235 if (unicode_status == UNICODE_ON) {
236# if !ENABLE_UNICODE_PRESERVE_BROKEN
237 ssize_t len = wcstombs(dst, command_ps, maxsize - 1);
238 if (len < 0)
239 len = 0;
240 dst[len] = '\0';
241 return len;
242# else
243 unsigned dstpos = 0;
244 unsigned srcpos = 0;
245
246 maxsize--;
247 while (dstpos < maxsize) {
248 wchar_t wc;
249 int n = srcpos;
250
251 /* Convert up to 1st invalid byte (or up to end) */
252 while ((wc = command_ps[srcpos]) != BB_NUL
253 && !unicode_is_raw_byte(wc)
254 ) {
255 srcpos++;
256 }
257 command_ps[srcpos] = BB_NUL;
258 n = wcstombs(dst + dstpos, command_ps + n, maxsize - dstpos);
259 if (n < 0) /* should not happen */
260 break;
261 dstpos += n;
262 if (wc == BB_NUL) /* usually is */
263 break;
264
265 /* We do have invalid byte here! */
266 command_ps[srcpos] = wc; /* restore it */
267 srcpos++;
268 if (dstpos == maxsize)
269 break;
270 dst[dstpos++] = (char) wc;
271 }
272 dst[dstpos] = '\0';
273 return dstpos;
274# endif
275 } else {
276 unsigned i = 0;
277 while ((dst[i] = command_ps[i]) != 0)
278 i++;
279 return i;
280 }
281}
282/* I thought just fputwc(c, stdout) would work. But no... */
283static void BB_PUTCHAR(wchar_t c)
284{
285 if (unicode_status == UNICODE_ON) {
286 char buf[MB_CUR_MAX + 1];
287 mbstate_t mbst = { 0 };
288 ssize_t len = wcrtomb(buf, c, &mbst);
289 if (len > 0) {
290 buf[len] = '\0';
291 fputs(buf, stdout);
292 }
293 } else {
294 /* In this case, c is always one byte */
295 putchar(c);
296 }
297}
298# if ENABLE_UNICODE_COMBINING_WCHARS || ENABLE_UNICODE_WIDE_WCHARS
299static wchar_t adjust_width_and_validate_wc(unsigned *width_adj, wchar_t wc)
300# else
301static wchar_t adjust_width_and_validate_wc(wchar_t wc)
302# define adjust_width_and_validate_wc(width_adj, wc) \
303 ((*(width_adj))++, adjust_width_and_validate_wc(wc))
304# endif
305{
306 int w = 1;
307
308 if (unicode_status == UNICODE_ON) {
309 if (wc > CONFIG_LAST_SUPPORTED_WCHAR) {
310 /* note: also true for unicode_is_raw_byte(wc) */
311 goto subst;
312 }
313 w = wcwidth(wc);
314 if ((ENABLE_UNICODE_COMBINING_WCHARS && w < 0)
315 || (!ENABLE_UNICODE_COMBINING_WCHARS && w <= 0)
316 || (!ENABLE_UNICODE_WIDE_WCHARS && w > 1)
317 ) {
318 subst:
319 w = 1;
320 wc = CONFIG_SUBST_WCHAR;
321 }
322 }
323
324# if ENABLE_UNICODE_COMBINING_WCHARS || ENABLE_UNICODE_WIDE_WCHARS
325 *width_adj += w;
326#endif
327 return wc;
328}
329#else /* !UNICODE */
330static size_t load_string(const char *src)
331{
332 safe_strncpy(command_ps, src, S.maxsize);
333 return strlen(command_ps);
334}
335# if ENABLE_FEATURE_TAB_COMPLETION
336static void save_string(char *dst, unsigned maxsize)
337{
338 safe_strncpy(dst, command_ps, maxsize);
339}
340# endif
341# define BB_PUTCHAR(c) bb_putchar(c)
342/* Should never be called: */
343int adjust_width_and_validate_wc(unsigned *width_adj, int wc);
344#endif
345
346
347/* Put 'command_ps[cursor]', cursor++.
348 * Advance cursor on screen. If we reached right margin, scroll text up
349 * and remove terminal margin effect by printing 'next_char' */
350#define HACK_FOR_WRONG_WIDTH 1
351static void put_cur_glyph_and_inc_cursor(void)
352{
353 CHAR_T c = command_ps[cursor];
354 unsigned width = 0;
355 int ofs_to_right;
356
357 if (c == BB_NUL) {
358 /* erase character after end of input string */
359 c = ' ';
360 } else {
361 /* advance cursor only if we aren't at the end yet */
362 cursor++;
363 if (unicode_status == UNICODE_ON) {
364 IF_UNICODE_WIDE_WCHARS(width = cmdedit_x;)
365 c = adjust_width_and_validate_wc(&cmdedit_x, c);
366 IF_UNICODE_WIDE_WCHARS(width = cmdedit_x - width;)
367 } else {
368 cmdedit_x++;
369 }
370 }
371
372 ofs_to_right = cmdedit_x - cmdedit_termw;
373 if (!ENABLE_UNICODE_WIDE_WCHARS || ofs_to_right <= 0) {
374 /* c fits on this line */
375 BB_PUTCHAR(c);
376 }
377
378 if (ofs_to_right >= 0) {
379 /* we go to the next line */
380#if HACK_FOR_WRONG_WIDTH
381 /* This works better if our idea of term width is wrong
382 * and it is actually wider (often happens on serial lines).
383 * Printing CR,LF *forces* cursor to next line.
384 * OTOH if terminal width is correct AND terminal does NOT
385 * have automargin (IOW: it is moving cursor to next line
386 * by itself (which is wrong for VT-10x terminals)),
387 * this will break things: there will be one extra empty line */
388 puts("\r"); /* + implicit '\n' */
389#else
390 /* VT-10x terminals don't wrap cursor to next line when last char
391 * on the line is printed - cursor stays "over" this char.
392 * Need to print _next_ char too (first one to appear on next line)
393 * to make cursor move down to next line.
394 */
395 /* Works ok only if cmdedit_termw is correct. */
396 c = command_ps[cursor];
397 if (c == BB_NUL)
398 c = ' ';
399 BB_PUTCHAR(c);
400 bb_putchar('\b');
401#endif
402 cmdedit_y++;
403 if (!ENABLE_UNICODE_WIDE_WCHARS || ofs_to_right == 0) {
404 width = 0;
405 } else { /* ofs_to_right > 0 */
406 /* wide char c didn't fit on prev line */
407 BB_PUTCHAR(c);
408 }
409 cmdedit_x = width;
410 }
411}
412
413/* Move to end of line (by printing all chars till the end) */
414static void put_till_end_and_adv_cursor(void)
415{
416 while (cursor < command_len)
417 put_cur_glyph_and_inc_cursor();
418}
419
420/* Go to the next line */
421static void goto_new_line(void)
422{
423 put_till_end_and_adv_cursor();
424 if (cmdedit_x != 0)
425 bb_putchar('\n');
426}
427
428static void beep(void)
429{
430 bb_putchar('\007');
431}
432
433static void put_prompt(void)
434{
435 unsigned w;
436
437 fputs(cmdedit_prompt, stdout);
438 fflush_all();
439 cursor = 0;
440 w = cmdedit_termw; /* read volatile var once */
441 cmdedit_y = cmdedit_prmt_len / w; /* new quasireal y */
442 cmdedit_x = cmdedit_prmt_len % w;
443}
444
445/* Move back one character */
446/* (optimized for slow terminals) */
447static void input_backward(unsigned num)
448{
449 if (num > cursor)
450 num = cursor;
451 if (num == 0)
452 return;
453 cursor -= num;
454
455 if ((ENABLE_UNICODE_COMBINING_WCHARS || ENABLE_UNICODE_WIDE_WCHARS)
456 && unicode_status == UNICODE_ON
457 ) {
458 /* correct NUM to be equal to _screen_ width */
459 int n = num;
460 num = 0;
461 while (--n >= 0)
462 adjust_width_and_validate_wc(&num, command_ps[cursor + n]);
463 if (num == 0)
464 return;
465 }
466
467 if (cmdedit_x >= num) {
468 cmdedit_x -= num;
469 if (num <= 4) {
470 /* This is longer by 5 bytes on x86.
471 * Also gets miscompiled for ARM users
472 * (busybox.net/bugs/view.php?id=2274).
473 * printf(("\b\b\b\b" + 4) - num);
474 * return;
475 */
476 do {
477 bb_putchar('\b');
478 } while (--num);
479 return;
480 }
481 printf(ESC"[%uD", num);
482 return;
483 }
484
485 /* Need to go one or more lines up */
486 if (ENABLE_UNICODE_WIDE_WCHARS) {
487 /* With wide chars, it is hard to "backtrack"
488 * and reliably figure out where to put cursor.
489 * Example (<> is a wide char; # is an ordinary char, _ cursor):
490 * |prompt: <><> |
491 * |<><><><><><> |
492 * |_ |
493 * and user presses left arrow. num = 1, cmdedit_x = 0,
494 * We need to go up one line, and then - how do we know that
495 * we need to go *10* positions to the right? Because
496 * |prompt: <>#<>|
497 * |<><><>#<><><>|
498 * |_ |
499 * in this situation we need to go *11* positions to the right.
500 *
501 * A simpler thing to do is to redraw everything from the start
502 * up to new cursor position (which is already known):
503 */
504 unsigned sv_cursor;
505 /* go to 1st column; go up to first line */
506 printf("\r" ESC"[%uA", cmdedit_y);
507 cmdedit_y = 0;
508 sv_cursor = cursor;
509 put_prompt(); /* sets cursor to 0 */
510 while (cursor < sv_cursor)
511 put_cur_glyph_and_inc_cursor();
512 } else {
513 int lines_up;
514 unsigned width;
515 /* num = chars to go back from the beginning of current line: */
516 num -= cmdedit_x;
517 width = cmdedit_termw; /* read volatile var once */
518 /* num=1...w: one line up, w+1...2w: two, etc: */
519 lines_up = 1 + (num - 1) / width;
520 cmdedit_x = (width * cmdedit_y - num) % width;
521 cmdedit_y -= lines_up;
522 /* go to 1st column; go up */
523 printf("\r" ESC"[%uA", lines_up);
524 /* go to correct column.
525 * xterm, konsole, Linux VT interpret 0 as 1 below! wow.
526 * need to *make sure* we skip it if cmdedit_x == 0 */
527 if (cmdedit_x)
528 printf(ESC"[%uC", cmdedit_x);
529 }
530}
531
532/* draw prompt, editor line, and clear tail */
533static void redraw(int y, int back_cursor)
534{
535 if (y > 0) /* up y lines */
536 printf(ESC"[%uA", y);
537 bb_putchar('\r');
538 put_prompt();
539 put_till_end_and_adv_cursor();
540 printf(SEQ_CLEAR_TILL_END_OF_SCREEN);
541 input_backward(back_cursor);
542}
543
544/* Delete the char in front of the cursor, optionally saving it
545 * for later putback */
546#if !ENABLE_FEATURE_EDITING_VI
547static void input_delete(void)
548#define input_delete(save) input_delete()
549#else
550static void input_delete(int save)
551#endif
552{
553 int j = cursor;
554
555 if (j == (int)command_len)
556 return;
557
558#if ENABLE_FEATURE_EDITING_VI
559 if (save) {
560 if (newdelflag) {
561 delptr = delbuf;
562 newdelflag = 0;
563 }
564 if ((delptr - delbuf) < DELBUFSIZ)
565 *delptr++ = command_ps[j];
566 }
567#endif
568
569 memmove(command_ps + j, command_ps + j + 1,
570 /* (command_len + 1 [because of NUL]) - (j + 1)
571 * simplified into (command_len - j) */
572 (command_len - j) * sizeof(command_ps[0]));
573 command_len--;
574 put_till_end_and_adv_cursor();
575 /* Last char is still visible, erase it (and more) */
576 printf(SEQ_CLEAR_TILL_END_OF_SCREEN);
577 input_backward(cursor - j); /* back to old pos cursor */
578}
579
580#if ENABLE_FEATURE_EDITING_VI
581static void put(void)
582{
583 int ocursor;
584 int j = delptr - delbuf;
585
586 if (j == 0)
587 return;
588 ocursor = cursor;
589 /* open hole and then fill it */
590 memmove(command_ps + cursor + j, command_ps + cursor,
591 (command_len - cursor + 1) * sizeof(command_ps[0]));
592 memcpy(command_ps + cursor, delbuf, j * sizeof(command_ps[0]));
593 command_len += j;
594 put_till_end_and_adv_cursor();
595 input_backward(cursor - ocursor - j + 1); /* at end of new text */
596}
597#endif
598
599/* Delete the char in back of the cursor */
600static void input_backspace(void)
601{
602 if (cursor > 0) {
603 input_backward(1);
604 input_delete(0);
605 }
606}
607
608/* Move forward one character */
609static void input_forward(void)
610{
611 if (cursor < command_len)
612 put_cur_glyph_and_inc_cursor();
613}
614
615#if ENABLE_FEATURE_TAB_COMPLETION
616
617//FIXME:
618//needs to be more clever: currently it thinks that "foo\ b<TAB>
619//matches the file named "foo bar", which is untrue.
620//Also, perhaps "foo b<TAB> needs to complete to "foo bar" <cursor>,
621//not "foo bar <cursor>...
622
623static void free_tab_completion_data(void)
624{
625 if (matches) {
626 while (num_matches)
627 free(matches[--num_matches]);
628 free(matches);
629 matches = NULL;
630 }
631}
632
633static void add_match(char *matched)
634{
635 matches = xrealloc_vector(matches, 4, num_matches);
636 matches[num_matches] = matched;
637 num_matches++;
638}
639
640# if ENABLE_FEATURE_USERNAME_COMPLETION
641/* Replace "~user/..." with "/homedir/...".
642 * The parameter is malloced, free it or return it
643 * unchanged if no user is matched.
644 */
645static char *username_path_completion(char *ud)
646{
647 struct passwd *entry;
648 char *tilde_name = ud;
649 char *home = NULL;
650
651 ud++; /* skip ~ */
652 if (*ud == '/') { /* "~/..." */
653 home = home_pwd_buf;
654 } else {
655 /* "~user/..." */
656 ud = strchr(ud, '/');
657 *ud = '\0'; /* "~user" */
658 entry = getpwnam(tilde_name + 1);
659 *ud = '/'; /* restore "~user/..." */
660 if (entry)
661 home = entry->pw_dir;
662 }
663 if (home) {
664 ud = concat_path_file(home, ud);
665 free(tilde_name);
666 tilde_name = ud;
667 }
668 return tilde_name;
669}
670
671/* ~use<tab> - find all users with this prefix.
672 * Return the length of the prefix used for matching.
673 */
674static NOINLINE unsigned complete_username(const char *ud)
675{
676 struct passwd *pw;
677 unsigned userlen;
678
679 ud++; /* skip ~ */
680 userlen = strlen(ud);
681
682 setpwent();
683 while ((pw = getpwent()) != NULL) {
684 /* Null usernames should result in all users as possible completions. */
685 if (/* !ud[0] || */ is_prefixed_with(pw->pw_name, ud)) {
686 add_match(xasprintf("~%s/", pw->pw_name));
687 }
688 }
689 endpwent(); /* don't keep password file open */
690
691 return 1 + userlen;
692}
693# endif /* FEATURE_USERNAME_COMPLETION */
694
695enum {
696 FIND_EXE_ONLY = 0,
697 FIND_DIR_ONLY = 1,
698 FIND_FILE_ONLY = 2,
699};
700
701static int path_parse(char ***p)
702{
703 int npth;
704 const char *pth;
705 char *tmp;
706 char **res;
707
708 if (state->flags & WITH_PATH_LOOKUP)
709 pth = state->path_lookup;
710 else
711 pth = getenv("PATH");
712
713 /* PATH="" or PATH=":"? */
714 if (!pth || !pth[0] || LONE_CHAR(pth, ':'))
715 return 1;
716
717 tmp = (char*)pth;
718 npth = 1; /* path component count */
719 while (1) {
720 tmp = strchr(tmp, ':');
721 if (!tmp)
722 break;
723 tmp++;
724 if (*tmp == '\0')
725 break; /* :<empty> */
726 npth++;
727 }
728
729 *p = res = xmalloc(npth * sizeof(res[0]));
730 res[0] = tmp = xstrdup(pth);
731 npth = 1;
732 while (1) {
733 tmp = strchr(tmp, ':');
734 if (!tmp)
735 break;
736 *tmp++ = '\0'; /* ':' -> '\0' */
737 if (*tmp == '\0')
738 break; /* :<empty> */
739 res[npth++] = tmp;
740 }
741 return npth;
742}
743
744/* Complete command, directory or file name.
745 * Return the length of the prefix used for matching.
746 */
747static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type)
748{
749 char *path1[1];
750 char **paths = path1;
751 int npaths;
752 int i;
753 unsigned pf_len;
754 const char *pfind;
755 char *dirbuf = NULL;
756
757 npaths = 1;
758 path1[0] = (char*)".";
759
760 pfind = strrchr(command, '/');
761 if (!pfind) {
762 if (type == FIND_EXE_ONLY)
763 npaths = path_parse(&paths);
764 pfind = command;
765 } else {
766 /* point to 'l' in "..../last_component" */
767 pfind++;
768 /* dirbuf = ".../.../.../" */
769 dirbuf = xstrndup(command, pfind - command);
770# if ENABLE_FEATURE_USERNAME_COMPLETION
771 if (dirbuf[0] == '~') /* ~/... or ~user/... */
772 dirbuf = username_path_completion(dirbuf);
773# endif
774 path1[0] = dirbuf;
775 }
776 pf_len = strlen(pfind);
777
778#if ENABLE_FEATURE_SH_STANDALONE && NUM_APPLETS != 1
779 if (type == FIND_EXE_ONLY) {
780 const char *p = applet_names;
781
782 while (*p) {
783 if (strncmp(pfind, p, pf_len) == 0)
784 add_match(xstrdup(p));
785 while (*p++ != '\0')
786 continue;
787 }
788 }
789#endif
790
791 for (i = 0; i < npaths; i++) {
792 DIR *dir;
793 struct dirent *next;
794 struct stat st;
795 char *found;
796
797 dir = opendir(paths[i]);
798 if (!dir)
799 continue; /* don't print an error */
800
801 while ((next = readdir(dir)) != NULL) {
802 unsigned len;
803 const char *name_found = next->d_name;
804
805 /* .../<tab>: bash 3.2.0 shows dotfiles, but not . and .. */
806 if (!pfind[0] && DOT_OR_DOTDOT(name_found))
807 continue;
808 /* match? */
809 if (!is_prefixed_with(name_found, pfind))
810 continue; /* no */
811
812 found = concat_path_file(paths[i], name_found);
813 /* NB: stat() first so that we see is it a directory;
814 * but if that fails, use lstat() so that
815 * we still match dangling links */
816 if (stat(found, &st) && lstat(found, &st))
817 goto cont; /* hmm, remove in progress? */
818
819 /* Save only name */
820 len = strlen(name_found);
821 found = xrealloc(found, len + 2); /* +2: for slash and NUL */
822 strcpy(found, name_found);
823
824 if (S_ISDIR(st.st_mode)) {
825 /* name is a directory, add slash */
826 found[len] = '/';
827 found[len + 1] = '\0';
828 } else {
829 /* skip files if looking for dirs only (example: cd) */
830 if (type == FIND_DIR_ONLY)
831 goto cont;
832 }
833 /* add it to the list */
834 add_match(found);
835 continue;
836 cont:
837 free(found);
838 }
839 closedir(dir);
840 } /* for every path */
841
842 if (paths != path1) {
843 free(paths[0]); /* allocated memory is only in first member */
844 free(paths);
845 }
846 free(dirbuf);
847
848 return pf_len;
849}
850
851/* build_match_prefix:
852 * On entry, match_buf contains everything up to cursor at the moment <tab>
853 * was pressed. This function looks at it, figures out what part of it
854 * constitutes the command/file/directory prefix to use for completion,
855 * and rewrites match_buf to contain only that part.
856 */
857#define dbg_bmp 0
858/* Helpers: */
859/* QUOT is used on elements of int_buf[], which are bytes,
860 * not Unicode chars. Therefore it works correctly even in Unicode mode.
861 */
862#define QUOT (UCHAR_MAX+1)
863static void remove_chunk(int16_t *int_buf, int beg, int end)
864{
865 /* beg must be <= end */
866 if (beg == end)
867 return;
868
869 while ((int_buf[beg] = int_buf[end]) != 0)
870 beg++, end++;
871
872 if (dbg_bmp) {
873 int i;
874 for (i = 0; int_buf[i]; i++)
875 bb_putchar((unsigned char)int_buf[i]);
876 bb_putchar('\n');
877 }
878}
879/* Caller ensures that match_buf points to a malloced buffer
880 * big enough to hold strlen(match_buf)*2 + 2
881 */
882static NOINLINE int build_match_prefix(char *match_buf)
883{
884 int i, j;
885 int command_mode;
886 int16_t *int_buf = (int16_t*)match_buf;
887
888 if (dbg_bmp) printf("\n%s\n", match_buf);
889
890 /* Copy in reverse order, since they overlap */
891 i = strlen(match_buf);
892 do {
893 int_buf[i] = (unsigned char)match_buf[i];
894 i--;
895 } while (i >= 0);
896
897 /* Mark every \c as "quoted c" */
898 for (i = 0; int_buf[i]; i++) {
899 if (int_buf[i] == '\\') {
900 remove_chunk(int_buf, i, i + 1);
901 int_buf[i] |= QUOT;
902 }
903 }
904 /* Quote-mark "chars" and 'chars', drop delimiters */
905 {
906 int in_quote = 0;
907 i = 0;
908 while (int_buf[i]) {
909 int cur = int_buf[i];
910 if (!cur)
911 break;
912 if (cur == '\'' || cur == '"') {
913 if (!in_quote || (cur == in_quote)) {
914 in_quote ^= cur;
915 remove_chunk(int_buf, i, i + 1);
916 continue;
917 }
918 }
919 if (in_quote)
920 int_buf[i] = cur | QUOT;
921 i++;
922 }
923 }
924
925 /* Remove everything up to command delimiters:
926 * ';' ';;' '&' '|' '&&' '||',
927 * but careful with '>&' '<&' '>|'
928 */
929 for (i = 0; int_buf[i]; i++) {
930 int cur = int_buf[i];
931 if (cur == ';' || cur == '&' || cur == '|') {
932 int prev = i ? int_buf[i - 1] : 0;
933 if (cur == '&' && (prev == '>' || prev == '<')) {
934 continue;
935 } else if (cur == '|' && prev == '>') {
936 continue;
937 }
938 remove_chunk(int_buf, 0, i + 1 + (cur == int_buf[i + 1]));
939 i = -1; /* back to square 1 */
940 }
941 }
942 /* Remove all `cmd` */
943 for (i = 0; int_buf[i]; i++) {
944 if (int_buf[i] == '`') {
945 for (j = i + 1; int_buf[j]; j++) {
946 if (int_buf[j] == '`') {
947 /* `cmd` should count as a word:
948 * `cmd` c<tab> should search for files c*,
949 * not commands c*. Therefore we don't drop
950 * `cmd` entirely, we replace it with single `.
951 */
952 remove_chunk(int_buf, i, j);
953 goto next;
954 }
955 }
956 /* No closing ` - command mode, remove all up to ` */
957 remove_chunk(int_buf, 0, i + 1);
958 break;
959 next: ;
960 }
961 }
962
963 /* Remove "cmd (" and "cmd {"
964 * Example: "if { c<tab>"
965 * In this example, c should be matched as command pfx.
966 */
967 for (i = 0; int_buf[i]; i++) {
968 if (int_buf[i] == '(' || int_buf[i] == '{') {
969 remove_chunk(int_buf, 0, i + 1);
970 i = -1; /* back to square 1 */
971 }
972 }
973
974 /* Remove leading unquoted spaces */
975 for (i = 0; int_buf[i]; i++)
976 if (int_buf[i] != ' ')
977 break;
978 remove_chunk(int_buf, 0, i);
979
980 /* Determine completion mode */
981 command_mode = FIND_EXE_ONLY;
982 for (i = 0; int_buf[i]; i++) {
983 if (int_buf[i] == ' ' || int_buf[i] == '<' || int_buf[i] == '>') {
984 if (int_buf[i] == ' '
985 && command_mode == FIND_EXE_ONLY
986 && (char)int_buf[0] == 'c'
987 && (char)int_buf[1] == 'd'
988 && i == 2 /* -> int_buf[2] == ' ' */
989 ) {
990 command_mode = FIND_DIR_ONLY;
991 } else {
992 command_mode = FIND_FILE_ONLY;
993 break;
994 }
995 }
996 }
997 if (dbg_bmp) printf("command_mode(0:exe/1:dir/2:file):%d\n", command_mode);
998
999 /* Remove everything except last word */
1000 for (i = 0; int_buf[i]; i++) /* quasi-strlen(int_buf) */
1001 continue;
1002 for (--i; i >= 0; i--) {
1003 int cur = int_buf[i];
1004 if (cur == ' ' || cur == '<' || cur == '>' || cur == '|' || cur == '&') {
1005 remove_chunk(int_buf, 0, i + 1);
1006 break;
1007 }
1008 }
1009
1010 /* Convert back to string of _chars_ */
1011 i = 0;
1012 while ((match_buf[i] = int_buf[i]) != '\0')
1013 i++;
1014
1015 if (dbg_bmp) printf("final match_buf:'%s'\n", match_buf);
1016
1017 return command_mode;
1018}
1019
1020/*
1021 * Display by column (original idea from ls applet,
1022 * very optimized by me [Vladimir] :)
1023 */
1024static void showfiles(void)
1025{
1026 int ncols, row;
1027 int column_width = 0;
1028 int nfiles = num_matches;
1029 int nrows = nfiles;
1030 int l;
1031
1032 /* find the longest file name - use that as the column width */
1033 for (row = 0; row < nrows; row++) {
1034 l = unicode_strwidth(matches[row]);
1035 if (column_width < l)
1036 column_width = l;
1037 }
1038 column_width += 2; /* min space for columns */
1039 ncols = cmdedit_termw / column_width;
1040
1041 if (ncols > 1) {
1042 nrows /= ncols;
1043 if (nfiles % ncols)
1044 nrows++; /* round up fractionals */
1045 } else {
1046 ncols = 1;
1047 }
1048 for (row = 0; row < nrows; row++) {
1049 int n = row;
1050 int nc;
1051
1052 for (nc = 1; nc < ncols && n+nrows < nfiles; n += nrows, nc++) {
1053 printf("%s%-*s", matches[n],
1054 (int)(column_width - unicode_strwidth(matches[n])), ""
1055 );
1056 }
1057 if (ENABLE_UNICODE_SUPPORT)
1058 puts(printable_string(NULL, matches[n]));
1059 else
1060 puts(matches[n]);
1061 }
1062}
1063
1064static const char *is_special_char(char c)
1065{
1066 return strchr(" `\"#$%^&*()=+{}[]:;'|\\<>", c);
1067}
1068
1069static char *quote_special_chars(char *found)
1070{
1071 int l = 0;
1072 char *s = xzalloc((strlen(found) + 1) * 2);
1073
1074 while (*found) {
1075 if (is_special_char(*found))
1076 s[l++] = '\\';
1077 s[l++] = *found++;
1078 }
1079 /* s[l] = '\0'; - already is */
1080 return s;
1081}
1082
1083/* Do TAB completion */
1084static NOINLINE void input_tab(smallint *lastWasTab)
1085{
1086 char *chosen_match;
1087 char *match_buf;
1088 size_t len_found;
1089 /* Length of string used for matching */
1090 unsigned match_pfx_len = match_pfx_len;
1091 int find_type;
1092# if ENABLE_UNICODE_SUPPORT
1093 /* cursor pos in command converted to multibyte form */
1094 int cursor_mb;
1095# endif
1096 if (!(state->flags & TAB_COMPLETION))
1097 return;
1098
1099 if (*lastWasTab) {
1100 /* The last char was a TAB too.
1101 * Print a list of all the available choices.
1102 */
1103 if (num_matches > 0) {
1104 /* cursor will be changed by goto_new_line() */
1105 int sav_cursor = cursor;
1106 goto_new_line();
1107 showfiles();
1108 redraw(0, command_len - sav_cursor);
1109 }
1110 return;
1111 }
1112
1113 *lastWasTab = 1;
1114 chosen_match = NULL;
1115
1116 /* Make a local copy of the string up to the position of the cursor.
1117 * build_match_prefix will expand it into int16_t's, need to allocate
1118 * twice as much as the string_len+1.
1119 * (we then also (ab)use this extra space later - see (**))
1120 */
1121 match_buf = xmalloc(MAX_LINELEN * sizeof(int16_t));
1122# if !ENABLE_UNICODE_SUPPORT
1123 save_string(match_buf, cursor + 1); /* +1 for NUL */
1124# else
1125 {
1126 CHAR_T wc = command_ps[cursor];
1127 command_ps[cursor] = BB_NUL;
1128 save_string(match_buf, MAX_LINELEN);
1129 command_ps[cursor] = wc;
1130 cursor_mb = strlen(match_buf);
1131 }
1132# endif
1133 find_type = build_match_prefix(match_buf);
1134
1135 /* Free up any memory already allocated */
1136 free_tab_completion_data();
1137
1138# if ENABLE_FEATURE_USERNAME_COMPLETION
1139 /* If the word starts with ~ and there is no slash in the word,
1140 * then try completing this word as a username. */
1141 if (state->flags & USERNAME_COMPLETION)
1142 if (match_buf[0] == '~' && strchr(match_buf, '/') == NULL)
1143 match_pfx_len = complete_username(match_buf);
1144# endif
1145 /* If complete_username() did not match,
1146 * try to match a command in $PATH, or a directory, or a file */
1147 if (!matches)
1148 match_pfx_len = complete_cmd_dir_file(match_buf, find_type);
1149
1150 /* Account for backslashes which will be inserted
1151 * by quote_special_chars() later */
1152 {
1153 const char *e = match_buf + strlen(match_buf);
1154 const char *s = e - match_pfx_len;
1155 while (s < e)
1156 if (is_special_char(*s++))
1157 match_pfx_len++;
1158 }
1159
1160 /* Remove duplicates */
1161 if (matches) {
1162 unsigned i, n = 0;
1163 qsort_string_vector(matches, num_matches);
1164 for (i = 0; i < num_matches - 1; ++i) {
1165 //if (matches[i] && matches[i+1]) { /* paranoia */
1166 if (strcmp(matches[i], matches[i+1]) == 0) {
1167 free(matches[i]);
1168 //matches[i] = NULL; /* paranoia */
1169 } else {
1170 matches[n++] = matches[i];
1171 }
1172 //}
1173 }
1174 matches[n++] = matches[i];
1175 num_matches = n;
1176 }
1177
1178 /* Did we find exactly one match? */
1179 if (num_matches != 1) { /* no */
1180 char *cp;
1181 beep();
1182 if (!matches)
1183 goto ret; /* no matches at all */
1184 /* Find common prefix */
1185 chosen_match = xstrdup(matches[0]);
1186 for (cp = chosen_match; *cp; cp++) {
1187 unsigned n;
1188 for (n = 1; n < num_matches; n++) {
1189 if (matches[n][cp - chosen_match] != *cp) {
1190 goto stop;
1191 }
1192 }
1193 }
1194 stop:
1195 if (cp == chosen_match) { /* have unique prefix? */
1196 goto ret; /* no */
1197 }
1198 *cp = '\0';
1199 cp = quote_special_chars(chosen_match);
1200 free(chosen_match);
1201 chosen_match = cp;
1202 len_found = strlen(chosen_match);
1203 } else { /* exactly one match */
1204 /* Next <tab> is not a double-tab */
1205 *lastWasTab = 0;
1206
1207 chosen_match = quote_special_chars(matches[0]);
1208 len_found = strlen(chosen_match);
1209 if (chosen_match[len_found-1] != '/') {
1210 chosen_match[len_found] = ' ';
1211 chosen_match[++len_found] = '\0';
1212 }
1213 }
1214
1215# if !ENABLE_UNICODE_SUPPORT
1216 /* Have space to place the match? */
1217 /* The result consists of three parts with these lengths: */
1218 /* cursor + (len_found - match_pfx_len) + (command_len - cursor) */
1219 /* it simplifies into: */
1220 if ((int)(len_found - match_pfx_len + command_len) < S.maxsize) {
1221 int pos;
1222 /* save tail */
1223 strcpy(match_buf, &command_ps[cursor]);
1224 /* add match and tail */
1225 sprintf(&command_ps[cursor], "%s%s", chosen_match + match_pfx_len, match_buf);
1226 command_len = strlen(command_ps);
1227 /* new pos */
1228 pos = cursor + len_found - match_pfx_len;
1229 /* write out the matched command */
1230 redraw(cmdedit_y, command_len - pos);
1231 }
1232# else
1233 {
1234 /* Use 2nd half of match_buf as scratch space - see (**) */
1235 char *command = match_buf + MAX_LINELEN;
1236 int len = save_string(command, MAX_LINELEN);
1237 /* Have space to place the match? */
1238 /* cursor_mb + (len_found - match_pfx_len) + (len - cursor_mb) */
1239 if ((int)(len_found - match_pfx_len + len) < MAX_LINELEN) {
1240 int pos;
1241 /* save tail */
1242 strcpy(match_buf, &command[cursor_mb]);
1243 /* where do we want to have cursor after all? */
1244 strcpy(&command[cursor_mb], chosen_match + match_pfx_len);
1245 len = load_string(command);
1246 /* add match and tail */
1247 sprintf(&command[cursor_mb], "%s%s", chosen_match + match_pfx_len, match_buf);
1248 command_len = load_string(command);
1249 /* write out the matched command */
1250 /* paranoia: load_string can return 0 on conv error,
1251 * prevent passing pos = (0 - 12) to redraw */
1252 pos = command_len - len;
1253 redraw(cmdedit_y, pos >= 0 ? pos : 0);
1254 }
1255 }
1256# endif
1257 ret:
1258 free(chosen_match);
1259 free(match_buf);
1260}
1261
1262#endif /* FEATURE_TAB_COMPLETION */
1263
1264
1265line_input_t* FAST_FUNC new_line_input_t(int flags)
1266{
1267 line_input_t *n = xzalloc(sizeof(*n));
1268 n->flags = flags;
1269#if MAX_HISTORY > 0
1270 n->max_history = MAX_HISTORY;
1271#endif
1272 return n;
1273}
1274
1275
1276#if MAX_HISTORY > 0
1277
1278unsigned FAST_FUNC size_from_HISTFILESIZE(const char *hp)
1279{
1280 int size = MAX_HISTORY;
1281 if (hp) {
1282 size = atoi(hp);
1283 if (size <= 0)
1284 return 1;
1285 if (size > MAX_HISTORY)
1286 return MAX_HISTORY;
1287 }
1288 return size;
1289}
1290
1291static void save_command_ps_at_cur_history(void)
1292{
1293 if (command_ps[0] != BB_NUL) {
1294 int cur = state->cur_history;
1295 free(state->history[cur]);
1296
1297# if ENABLE_UNICODE_SUPPORT
1298 {
1299 char tbuf[MAX_LINELEN];
1300 save_string(tbuf, sizeof(tbuf));
1301 state->history[cur] = xstrdup(tbuf);
1302 }
1303# else
1304 state->history[cur] = xstrdup(command_ps);
1305# endif
1306 }
1307}
1308
1309/* state->flags is already checked to be nonzero */
1310static int get_previous_history(void)
1311{
1312 if ((state->flags & DO_HISTORY) && state->cur_history) {
1313 save_command_ps_at_cur_history();
1314 state->cur_history--;
1315 return 1;
1316 }
1317 beep();
1318 return 0;
1319}
1320
1321static int get_next_history(void)
1322{
1323 if (state->flags & DO_HISTORY) {
1324 if (state->cur_history < state->cnt_history) {
1325 save_command_ps_at_cur_history(); /* save the current history line */
1326 return ++state->cur_history;
1327 }
1328 }
1329 beep();
1330 return 0;
1331}
1332
1333/* Lists command history. Used by shell 'history' builtins */
1334void FAST_FUNC show_history(const line_input_t *st)
1335{
1336 int i;
1337
1338 if (!st)
1339 return;
1340 for (i = 0; i < st->cnt_history; i++)
1341 printf("%4d %s\n", i, st->history[i]);
1342}
1343
1344# if ENABLE_FEATURE_EDITING_SAVEHISTORY
1345/* We try to ensure that concurrent additions to the history
1346 * do not overwrite each other.
1347 * Otherwise shell users get unhappy.
1348 *
1349 * History file is trimmed lazily, when it grows several times longer
1350 * than configured MAX_HISTORY lines.
1351 */
1352
1353static void free_line_input_t(line_input_t *n)
1354{
1355 int i = n->cnt_history;
1356 while (i > 0)
1357 free(n->history[--i]);
1358 free(n);
1359}
1360
1361/* state->flags is already checked to be nonzero */
1362static void load_history(line_input_t *st_parm)
1363{
1364 char *temp_h[MAX_HISTORY];
1365 char *line;
1366 FILE *fp;
1367 unsigned idx, i, line_len;
1368
1369 /* NB: do not trash old history if file can't be opened */
1370
1371 fp = fopen_for_read(st_parm->hist_file);
1372 if (fp) {
1373 /* clean up old history */
1374 for (idx = st_parm->cnt_history; idx > 0;) {
1375 idx--;
1376 free(st_parm->history[idx]);
1377 st_parm->history[idx] = NULL;
1378 }
1379
1380 /* fill temp_h[], retaining only last MAX_HISTORY lines */
1381 memset(temp_h, 0, sizeof(temp_h));
1382 idx = 0;
1383 st_parm->cnt_history_in_file = 0;
1384 while ((line = xmalloc_fgetline(fp)) != NULL) {
1385 if (line[0] == '\0') {
1386 free(line);
1387 continue;
1388 }
1389 free(temp_h[idx]);
1390 temp_h[idx] = line;
1391 st_parm->cnt_history_in_file++;
1392 idx++;
1393 if (idx == st_parm->max_history)
1394 idx = 0;
1395 }
1396 fclose(fp);
1397
1398 /* find first non-NULL temp_h[], if any */
1399 if (st_parm->cnt_history_in_file) {
1400 while (temp_h[idx] == NULL) {
1401 idx++;
1402 if (idx == st_parm->max_history)
1403 idx = 0;
1404 }
1405 }
1406
1407 /* copy temp_h[] to st_parm->history[] */
1408 for (i = 0; i < st_parm->max_history;) {
1409 line = temp_h[idx];
1410 if (!line)
1411 break;
1412 idx++;
1413 if (idx == st_parm->max_history)
1414 idx = 0;
1415 line_len = strlen(line);
1416 if (line_len >= MAX_LINELEN)
1417 line[MAX_LINELEN-1] = '\0';
1418 st_parm->history[i++] = line;
1419 }
1420 st_parm->cnt_history = i;
1421 if (ENABLE_FEATURE_EDITING_SAVE_ON_EXIT)
1422 st_parm->cnt_history_in_file = i;
1423 }
1424}
1425
1426# if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
1427void save_history(line_input_t *st)
1428{
1429 FILE *fp;
1430
1431 if (!st->hist_file)
1432 return;
1433 if (st->cnt_history <= st->cnt_history_in_file)
1434 return;
1435
1436 fp = fopen(st->hist_file, "a");
1437 if (fp) {
1438 int i, fd;
1439 char *new_name;
1440 line_input_t *st_temp;
1441
1442 for (i = st->cnt_history_in_file; i < st->cnt_history; i++)
1443 fprintf(fp, "%s\n", st->history[i]);
1444 fclose(fp);
1445
1446 /* we may have concurrently written entries from others.
1447 * load them */
1448 st_temp = new_line_input_t(st->flags);
1449 st_temp->hist_file = st->hist_file;
1450 st_temp->max_history = st->max_history;
1451 load_history(st_temp);
1452
1453 /* write out temp file and replace hist_file atomically */
1454 new_name = xasprintf("%s.%u.new", st->hist_file, (int) getpid());
1455 fd = open(new_name, O_WRONLY | O_CREAT | O_TRUNC, 0600);
1456 if (fd >= 0) {
1457 fp = xfdopen_for_write(fd);
1458 for (i = 0; i < st_temp->cnt_history; i++)
1459 fprintf(fp, "%s\n", st_temp->history[i]);
1460 fclose(fp);
1461 if (rename(new_name, st->hist_file) == 0)
1462 st->cnt_history_in_file = st_temp->cnt_history;
1463 }
1464 free(new_name);
1465 free_line_input_t(st_temp);
1466 }
1467}
1468# else
1469static void save_history(char *str)
1470{
1471 int fd;
1472 int len, len2;
1473
1474 if (!state->hist_file)
1475 return;
1476
1477 fd = open(state->hist_file, O_WRONLY | O_CREAT | O_APPEND, 0600);
1478 if (fd < 0)
1479 return;
1480 xlseek(fd, 0, SEEK_END); /* paranoia */
1481 len = strlen(str);
1482 str[len] = '\n'; /* we (try to) do atomic write */
1483 len2 = full_write(fd, str, len + 1);
1484 str[len] = '\0';
1485 close(fd);
1486 if (len2 != len + 1)
1487 return; /* "wtf?" */
1488
1489 /* did we write so much that history file needs trimming? */
1490 state->cnt_history_in_file++;
1491 if (state->cnt_history_in_file > state->max_history * 4) {
1492 char *new_name;
1493 line_input_t *st_temp;
1494
1495 /* we may have concurrently written entries from others.
1496 * load them */
1497 st_temp = new_line_input_t(state->flags);
1498 st_temp->hist_file = state->hist_file;
1499 st_temp->max_history = state->max_history;
1500 load_history(st_temp);
1501
1502 /* write out temp file and replace hist_file atomically */
1503 new_name = xasprintf("%s.%u.new", state->hist_file, (int) getpid());
1504 fd = open(new_name, O_WRONLY | O_CREAT | O_TRUNC, 0600);
1505 if (fd >= 0) {
1506 FILE *fp;
1507 int i;
1508
1509 fp = xfdopen_for_write(fd);
1510 for (i = 0; i < st_temp->cnt_history; i++)
1511 fprintf(fp, "%s\n", st_temp->history[i]);
1512 fclose(fp);
1513 if (rename(new_name, state->hist_file) == 0)
1514 state->cnt_history_in_file = st_temp->cnt_history;
1515 }
1516 free(new_name);
1517 free_line_input_t(st_temp);
1518 }
1519}
1520# endif
1521# else
1522# define load_history(a) ((void)0)
1523# define save_history(a) ((void)0)
1524# endif /* FEATURE_COMMAND_SAVEHISTORY */
1525
1526static void remember_in_history(char *str)
1527{
1528 int i;
1529
1530 if (!(state->flags & DO_HISTORY))
1531 return;
1532 if (str[0] == '\0')
1533 return;
1534 i = state->cnt_history;
1535 /* Don't save dupes */
1536 if (i && strcmp(state->history[i-1], str) == 0)
1537 return;
1538
1539 free(state->history[state->max_history]); /* redundant, paranoia */
1540 state->history[state->max_history] = NULL; /* redundant, paranoia */
1541
1542 /* If history[] is full, remove the oldest command */
1543 /* we need to keep history[state->max_history] empty, hence >=, not > */
1544 if (i >= state->max_history) {
1545 free(state->history[0]);
1546 for (i = 0; i < state->max_history-1; i++)
1547 state->history[i] = state->history[i+1];
1548 /* i == state->max_history-1 */
1549# if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
1550 if (state->cnt_history_in_file)
1551 state->cnt_history_in_file--;
1552# endif
1553 }
1554 /* i <= state->max_history-1 */
1555 state->history[i++] = xstrdup(str);
1556 /* i <= state->max_history */
1557 state->cur_history = i;
1558 state->cnt_history = i;
1559# if ENABLE_FEATURE_EDITING_SAVEHISTORY && !ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
1560 save_history(str);
1561# endif
1562}
1563
1564#else /* MAX_HISTORY == 0 */
1565# define remember_in_history(a) ((void)0)
1566#endif /* MAX_HISTORY */
1567
1568
1569#if ENABLE_FEATURE_EDITING_VI
1570/*
1571 * vi mode implemented 2005 by Paul Fox <pgf@foxharp.boston.ma.us>
1572 */
1573static void
1574vi_Word_motion(int eat)
1575{
1576 CHAR_T *command = command_ps;
1577
1578 while (cursor < command_len && !BB_isspace(command[cursor]))
1579 input_forward();
1580 if (eat) while (cursor < command_len && BB_isspace(command[cursor]))
1581 input_forward();
1582}
1583
1584static void
1585vi_word_motion(int eat)
1586{
1587 CHAR_T *command = command_ps;
1588
1589 if (BB_isalnum(command[cursor]) || command[cursor] == '_') {
1590 while (cursor < command_len
1591 && (BB_isalnum(command[cursor+1]) || command[cursor+1] == '_')
1592 ) {
1593 input_forward();
1594 }
1595 } else if (BB_ispunct(command[cursor])) {
1596 while (cursor < command_len && BB_ispunct(command[cursor+1]))
1597 input_forward();
1598 }
1599
1600 if (cursor < command_len)
1601 input_forward();
1602
1603 if (eat) {
1604 while (cursor < command_len && BB_isspace(command[cursor]))
1605 input_forward();
1606 }
1607}
1608
1609static void
1610vi_End_motion(void)
1611{
1612 CHAR_T *command = command_ps;
1613
1614 input_forward();
1615 while (cursor < command_len && BB_isspace(command[cursor]))
1616 input_forward();
1617 while (cursor < command_len-1 && !BB_isspace(command[cursor+1]))
1618 input_forward();
1619}
1620
1621static void
1622vi_end_motion(void)
1623{
1624 CHAR_T *command = command_ps;
1625
1626 if (cursor >= command_len-1)
1627 return;
1628 input_forward();
1629 while (cursor < command_len-1 && BB_isspace(command[cursor]))
1630 input_forward();
1631 if (cursor >= command_len-1)
1632 return;
1633 if (BB_isalnum(command[cursor]) || command[cursor] == '_') {
1634 while (cursor < command_len-1
1635 && (BB_isalnum(command[cursor+1]) || command[cursor+1] == '_')
1636 ) {
1637 input_forward();
1638 }
1639 } else if (BB_ispunct(command[cursor])) {
1640 while (cursor < command_len-1 && BB_ispunct(command[cursor+1]))
1641 input_forward();
1642 }
1643}
1644
1645static void
1646vi_Back_motion(void)
1647{
1648 CHAR_T *command = command_ps;
1649
1650 while (cursor > 0 && BB_isspace(command[cursor-1]))
1651 input_backward(1);
1652 while (cursor > 0 && !BB_isspace(command[cursor-1]))
1653 input_backward(1);
1654}
1655
1656static void
1657vi_back_motion(void)
1658{
1659 CHAR_T *command = command_ps;
1660
1661 if (cursor <= 0)
1662 return;
1663 input_backward(1);
1664 while (cursor > 0 && BB_isspace(command[cursor]))
1665 input_backward(1);
1666 if (cursor <= 0)
1667 return;
1668 if (BB_isalnum(command[cursor]) || command[cursor] == '_') {
1669 while (cursor > 0
1670 && (BB_isalnum(command[cursor-1]) || command[cursor-1] == '_')
1671 ) {
1672 input_backward(1);
1673 }
1674 } else if (BB_ispunct(command[cursor])) {
1675 while (cursor > 0 && BB_ispunct(command[cursor-1]))
1676 input_backward(1);
1677 }
1678}
1679#endif
1680
1681/* Modelled after bash 4.0 behavior of Ctrl-<arrow> */
1682static void ctrl_left(void)
1683{
1684 CHAR_T *command = command_ps;
1685
1686 while (1) {
1687 CHAR_T c;
1688
1689 input_backward(1);
1690 if (cursor == 0)
1691 break;
1692 c = command[cursor];
1693 if (c != ' ' && !BB_ispunct(c)) {
1694 /* we reached a "word" delimited by spaces/punct.
1695 * go to its beginning */
1696 while (1) {
1697 c = command[cursor - 1];
1698 if (c == ' ' || BB_ispunct(c))
1699 break;
1700 input_backward(1);
1701 if (cursor == 0)
1702 break;
1703 }
1704 break;
1705 }
1706 }
1707}
1708static void ctrl_right(void)
1709{
1710 CHAR_T *command = command_ps;
1711
1712 while (1) {
1713 CHAR_T c;
1714
1715 c = command[cursor];
1716 if (c == BB_NUL)
1717 break;
1718 if (c != ' ' && !BB_ispunct(c)) {
1719 /* we reached a "word" delimited by spaces/punct.
1720 * go to its end + 1 */
1721 while (1) {
1722 input_forward();
1723 c = command[cursor];
1724 if (c == BB_NUL || c == ' ' || BB_ispunct(c))
1725 break;
1726 }
1727 break;
1728 }
1729 input_forward();
1730 }
1731}
1732
1733
1734/*
1735 * read_line_input and its helpers
1736 */
1737
1738#if ENABLE_FEATURE_EDITING_ASK_TERMINAL
1739static void ask_terminal(void)
1740{
1741 /* Ask terminal where is the cursor now.
1742 * lineedit_read_key handles response and corrects
1743 * our idea of current cursor position.
1744 * Testcase: run "echo -n long_line_long_line_long_line",
1745 * then type in a long, wrapping command and try to
1746 * delete it using backspace key.
1747 * Note: we print it _after_ prompt, because
1748 * prompt may contain CR. Example: PS1='\[\r\n\]\w '
1749 */
1750 /* Problem: if there is buffered input on stdin,
1751 * the response will be delivered later,
1752 * possibly to an unsuspecting application.
1753 * Testcase: "sleep 1; busybox ash" + press and hold [Enter].
1754 * Result:
1755 * ~/srcdevel/bbox/fix/busybox.t4 #
1756 * ~/srcdevel/bbox/fix/busybox.t4 #
1757 * ^[[59;34~/srcdevel/bbox/fix/busybox.t4 # <-- garbage
1758 * ~/srcdevel/bbox/fix/busybox.t4 #
1759 *
1760 * Checking for input with poll only makes the race narrower,
1761 * I still can trigger it. Strace:
1762 *
1763 * write(1, "~/srcdevel/bbox/fix/busybox.t4 # ", 33) = 33
1764 * poll([{fd=0, events=POLLIN}], 1, 0) = 0 (Timeout) <-- no input exists
1765 * write(1, "\33[6n", 4) = 4 <-- send the ESC sequence, quick!
1766 * poll([{fd=0, events=POLLIN}], 1, -1) = 1 ([{fd=0, revents=POLLIN}])
1767 * read(0, "\n", 1) = 1 <-- oh crap, user's input got in first
1768 */
1769 struct pollfd pfd;
1770
1771 pfd.fd = STDIN_FILENO;
1772 pfd.events = POLLIN;
1773 if (safe_poll(&pfd, 1, 0) == 0) {
1774 S.sent_ESC_br6n = 1;
1775 fputs(ESC"[6n", stdout);
1776 fflush_all(); /* make terminal see it ASAP! */
1777 }
1778}
1779#else
1780#define ask_terminal() ((void)0)
1781#endif
1782
1783/* Called just once at read_line_input() init time */
1784#if !ENABLE_FEATURE_EDITING_FANCY_PROMPT
1785static void parse_and_put_prompt(const char *prmt_ptr)
1786{
1787 const char *p;
1788 cmdedit_prompt = prmt_ptr;
1789 p = strrchr(prmt_ptr, '\n');
1790 cmdedit_prmt_len = unicode_strwidth(p ? p+1 : prmt_ptr);
1791 put_prompt();
1792}
1793#else
1794static void parse_and_put_prompt(const char *prmt_ptr)
1795{
1796 int prmt_size = 0;
1797 char *prmt_mem_ptr = xzalloc(1);
1798# if ENABLE_USERNAME_OR_HOMEDIR
1799 char *cwd_buf = NULL;
1800# endif
1801 char flg_not_length = '[';
1802 char cbuf[2];
1803
1804 /*cmdedit_prmt_len = 0; - already is */
1805
1806 cbuf[1] = '\0'; /* never changes */
1807
1808 while (*prmt_ptr) {
1809 char timebuf[sizeof("HH:MM:SS")];
1810 char *free_me = NULL;
1811 char *pbuf;
1812 char c;
1813
1814 pbuf = cbuf;
1815 c = *prmt_ptr++;
1816 if (c == '\\') {
1817 const char *cp;
1818 int l;
1819/*
1820 * Supported via bb_process_escape_sequence:
1821 * \a ASCII bell character (07)
1822 * \e ASCII escape character (033)
1823 * \n newline
1824 * \r carriage return
1825 * \\ backslash
1826 * \nnn char with octal code nnn
1827 * Supported:
1828 * \$ if the effective UID is 0, a #, otherwise a $
1829 * \w current working directory, with $HOME abbreviated with a tilde
1830 * Note: we do not support $PROMPT_DIRTRIM=n feature
1831 * \W basename of the current working directory, with $HOME abbreviated with a tilde
1832 * \h hostname up to the first '.'
1833 * \H hostname
1834 * \u username
1835 * \[ begin a sequence of non-printing characters
1836 * \] end a sequence of non-printing characters
1837 * \T current time in 12-hour HH:MM:SS format
1838 * \@ current time in 12-hour am/pm format
1839 * \A current time in 24-hour HH:MM format
1840 * \t current time in 24-hour HH:MM:SS format
1841 * (all of the above work as \A)
1842 * Not supported:
1843 * \! history number of this command
1844 * \# command number of this command
1845 * \j number of jobs currently managed by the shell
1846 * \l basename of the shell's terminal device name
1847 * \s name of the shell, the basename of $0 (the portion following the final slash)
1848 * \V release of bash, version + patch level (e.g., 2.00.0)
1849 * \d date in "Weekday Month Date" format (e.g., "Tue May 26")
1850 * \D{format}
1851 * format is passed to strftime(3).
1852 * An empty format results in a locale-specific time representation.
1853 * The braces are required.
1854 * Mishandled by bb_process_escape_sequence:
1855 * \v version of bash (e.g., 2.00)
1856 */
1857 cp = prmt_ptr;
1858 c = *cp;
1859 if (c != 't') /* don't treat \t as tab */
1860 c = bb_process_escape_sequence(&prmt_ptr);
1861 if (prmt_ptr == cp) {
1862 if (*cp == '\0')
1863 break;
1864 c = *prmt_ptr++;
1865
1866 switch (c) {
1867# if ENABLE_USERNAME_OR_HOMEDIR
1868 case 'u':
1869 pbuf = user_buf ? user_buf : (char*)"";
1870 break;
1871# endif
1872 case 'H':
1873 case 'h':
1874 pbuf = free_me = safe_gethostname();
1875 if (c == 'h')
1876 strchrnul(pbuf, '.')[0] = '\0';
1877 break;
1878 case '$':
1879 c = (geteuid() == 0 ? '#' : '$');
1880 break;
1881 case 'T': /* 12-hour HH:MM:SS format */
1882 case '@': /* 12-hour am/pm format */
1883 case 'A': /* 24-hour HH:MM format */
1884 case 't': /* 24-hour HH:MM:SS format */
1885 /* We show all of them as 24-hour HH:MM */
1886 strftime_HHMMSS(timebuf, sizeof(timebuf), NULL)[-3] = '\0';
1887 pbuf = timebuf;
1888 break;
1889# if ENABLE_USERNAME_OR_HOMEDIR
1890 case 'w': /* current dir */
1891 case 'W': /* basename of cur dir */
1892 if (!cwd_buf) {
1893 cwd_buf = xrealloc_getcwd_or_warn(NULL);
1894 if (!cwd_buf)
1895 cwd_buf = (char *)bb_msg_unknown;
1896 else if (home_pwd_buf[0]) {
1897 char *after_home_user;
1898
1899 /* /home/user[/something] -> ~[/something] */
1900 after_home_user = is_prefixed_with(cwd_buf, home_pwd_buf);
1901 if (after_home_user
1902 && (*after_home_user == '/' || *after_home_user == '\0')
1903 ) {
1904 cwd_buf[0] = '~';
1905 overlapping_strcpy(cwd_buf + 1, after_home_user);
1906 }
1907 }
1908 }
1909 pbuf = cwd_buf;
1910 if (c == 'w')
1911 break;
1912 cp = strrchr(pbuf, '/');
1913 if (cp)
1914 pbuf = (char*)cp + 1;
1915 break;
1916# endif
1917// bb_process_escape_sequence does this now:
1918// case 'e': case 'E': /* \e \E = \033 */
1919// c = '\033';
1920// break;
1921 case 'x': case 'X': {
1922 char buf2[4];
1923 for (l = 0; l < 3;) {
1924 unsigned h;
1925 buf2[l++] = *prmt_ptr;
1926 buf2[l] = '\0';
1927 h = strtoul(buf2, &pbuf, 16);
1928 if (h > UCHAR_MAX || (pbuf - buf2) < l) {
1929 buf2[--l] = '\0';
1930 break;
1931 }
1932 prmt_ptr++;
1933 }
1934 c = (char)strtoul(buf2, NULL, 16);
1935 if (c == 0)
1936 c = '?';
1937 pbuf = cbuf;
1938 break;
1939 }
1940 case '[': case ']':
1941 if (c == flg_not_length) {
1942 /* Toggle '['/']' hex 5b/5d */
1943 flg_not_length ^= 6;
1944 continue;
1945 }
1946 break;
1947 } /* switch */
1948 } /* if */
1949 } /* if */
1950 cbuf[0] = c;
1951 {
1952 int n = strlen(pbuf);
1953 prmt_size += n;
1954 if (c == '\n')
1955 cmdedit_prmt_len = 0;
1956 else if (flg_not_length != ']') {
1957#if 0 /*ENABLE_UNICODE_SUPPORT*/
1958/* Won't work, pbuf is one BYTE string here instead of an one Unicode char string. */
1959/* FIXME */
1960 cmdedit_prmt_len += unicode_strwidth(pbuf);
1961#else
1962 cmdedit_prmt_len += n;
1963#endif
1964 }
1965 }
1966 prmt_mem_ptr = strcat(xrealloc(prmt_mem_ptr, prmt_size+1), pbuf);
1967 free(free_me);
1968 } /* while */
1969
1970# if ENABLE_USERNAME_OR_HOMEDIR
1971 if (cwd_buf != (char *)bb_msg_unknown)
1972 free(cwd_buf);
1973# endif
1974 cmdedit_prompt = prmt_mem_ptr;
1975 put_prompt();
1976}
1977#endif
1978
1979static void cmdedit_setwidth(unsigned w, int redraw_flg)
1980{
1981 cmdedit_termw = w;
1982 if (redraw_flg) {
1983 /* new y for current cursor */
1984 int new_y = (cursor + cmdedit_prmt_len) / w;
1985 /* redraw */
1986 redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), command_len - cursor);
1987 fflush_all();
1988 }
1989}
1990
1991static void win_changed(int nsig)
1992{
1993 int sv_errno = errno;
1994 unsigned width;
1995
1996 get_terminal_width_height(0, &width, NULL);
1997//FIXME: cmdedit_setwidth() -> redraw() -> printf() -> KABOOM! (we are in signal handler!)
1998 cmdedit_setwidth(width, /*redraw_flg:*/ nsig);
1999
2000 errno = sv_errno;
2001}
2002
2003static int lineedit_read_key(char *read_key_buffer, int timeout)
2004{
2005 int64_t ic;
2006#if ENABLE_UNICODE_SUPPORT
2007 char unicode_buf[MB_CUR_MAX + 1];
2008 int unicode_idx = 0;
2009#endif
2010
2011 while (1) {
2012 /* Wait for input. TIMEOUT = -1 makes read_key wait even
2013 * on nonblocking stdin, TIMEOUT = 50 makes sure we won't
2014 * insist on full MB_CUR_MAX buffer to declare input like
2015 * "\xff\n",pause,"ls\n" invalid and thus won't lose "ls".
2016 *
2017 * Note: read_key sets errno to 0 on success.
2018 */
2019 ic = read_key(STDIN_FILENO, read_key_buffer, timeout);
2020 if (errno) {
2021#if ENABLE_UNICODE_SUPPORT
2022 if (errno == EAGAIN && unicode_idx != 0)
2023 goto pushback;
2024#endif
2025 break;
2026 }
2027
2028#if ENABLE_FEATURE_EDITING_ASK_TERMINAL
2029 if ((int32_t)ic == KEYCODE_CURSOR_POS
2030 && S.sent_ESC_br6n
2031 ) {
2032 S.sent_ESC_br6n = 0;
2033 if (cursor == 0) { /* otherwise it may be bogus */
2034 int col = ((ic >> 32) & 0x7fff) - 1;
2035 /*
2036 * Is col > cmdedit_prmt_len?
2037 * If yes (terminal says cursor is farther to the right
2038 * of where we think it should be),
2039 * the prompt wasn't printed starting at col 1,
2040 * there was additional text before it.
2041 */
2042 if ((int)(col - cmdedit_prmt_len) > 0) {
2043 /* Fix our understanding of current x position */
2044 cmdedit_x += (col - cmdedit_prmt_len);
2045 while (cmdedit_x >= cmdedit_termw) {
2046 cmdedit_x -= cmdedit_termw;
2047 cmdedit_y++;
2048 }
2049 }
2050 }
2051 continue;
2052 }
2053#endif
2054
2055#if ENABLE_UNICODE_SUPPORT
2056 if (unicode_status == UNICODE_ON) {
2057 wchar_t wc;
2058
2059 if ((int32_t)ic < 0) /* KEYCODE_xxx */
2060 break;
2061 // TODO: imagine sequence like: 0xff,<left-arrow>: we are currently losing 0xff...
2062
2063 unicode_buf[unicode_idx++] = ic;
2064 unicode_buf[unicode_idx] = '\0';
2065 if (mbstowcs(&wc, unicode_buf, 1) != 1) {
2066 /* Not (yet?) a valid unicode char */
2067 if (unicode_idx < MB_CUR_MAX) {
2068 timeout = 50;
2069 continue;
2070 }
2071 pushback:
2072 /* Invalid sequence. Save all "bad bytes" except first */
2073 read_key_ungets(read_key_buffer, unicode_buf + 1, unicode_idx - 1);
2074# if !ENABLE_UNICODE_PRESERVE_BROKEN
2075 ic = CONFIG_SUBST_WCHAR;
2076# else
2077 ic = unicode_mark_raw_byte(unicode_buf[0]);
2078# endif
2079 } else {
2080 /* Valid unicode char, return its code */
2081 ic = wc;
2082 }
2083 }
2084#endif
2085 break;
2086 }
2087
2088 return ic;
2089}
2090
2091#if ENABLE_UNICODE_BIDI_SUPPORT
2092static int isrtl_str(void)
2093{
2094 int idx = cursor;
2095
2096 while (idx < command_len && unicode_bidi_is_neutral_wchar(command_ps[idx]))
2097 idx++;
2098 return unicode_bidi_isrtl(command_ps[idx]);
2099}
2100#else
2101# define isrtl_str() 0
2102#endif
2103
2104/* leave out the "vi-mode"-only case labels if vi editing isn't
2105 * configured. */
2106#define vi_case(caselabel) IF_FEATURE_EDITING_VI(case caselabel)
2107
2108/* convert uppercase ascii to equivalent control char, for readability */
2109#undef CTRL
2110#define CTRL(a) ((a) & ~0x40)
2111
2112enum {
2113 VI_CMDMODE_BIT = 0x40000000,
2114 /* 0x80000000 bit flags KEYCODE_xxx */
2115};
2116
2117#if ENABLE_FEATURE_REVERSE_SEARCH
2118/* Mimic readline Ctrl-R reverse history search.
2119 * When invoked, it shows the following prompt:
2120 * (reverse-i-search)'': user_input [cursor pos unchanged by Ctrl-R]
2121 * and typing results in search being performed:
2122 * (reverse-i-search)'tmp': cd /tmp [cursor under t in /tmp]
2123 * Search is performed by looking at progressively older lines in history.
2124 * Ctrl-R again searches for the next match in history.
2125 * Backspace deletes last matched char.
2126 * Control keys exit search and return to normal editing (at current history line).
2127 */
2128static int32_t reverse_i_search(void)
2129{
2130 char match_buf[128]; /* for user input */
2131 char read_key_buffer[KEYCODE_BUFFER_SIZE];
2132 const char *matched_history_line;
2133 const char *saved_prompt;
2134 unsigned saved_prmt_len;
2135 int32_t ic;
2136
2137 matched_history_line = NULL;
2138 read_key_buffer[0] = 0;
2139 match_buf[0] = '\0';
2140
2141 /* Save and replace the prompt */
2142 saved_prompt = cmdedit_prompt;
2143 saved_prmt_len = cmdedit_prmt_len;
2144 goto set_prompt;
2145
2146 while (1) {
2147 int h;
2148 unsigned match_buf_len = strlen(match_buf);
2149
2150 fflush_all();
2151//FIXME: correct timeout?
2152 ic = lineedit_read_key(read_key_buffer, -1);
2153
2154 switch (ic) {
2155 case CTRL('R'): /* searching for the next match */
2156 break;
2157
2158 case '\b':
2159 case '\x7f':
2160 /* Backspace */
2161 if (unicode_status == UNICODE_ON) {
2162 while (match_buf_len != 0) {
2163 uint8_t c = match_buf[--match_buf_len];
2164 if ((c & 0xc0) != 0x80) /* start of UTF-8 char? */
2165 break; /* yes */
2166 }
2167 } else {
2168 if (match_buf_len != 0)
2169 match_buf_len--;
2170 }
2171 match_buf[match_buf_len] = '\0';
2172 break;
2173
2174 default:
2175 if (ic < ' '
2176 || (!ENABLE_UNICODE_SUPPORT && ic >= 256)
2177 || (ENABLE_UNICODE_SUPPORT && ic >= VI_CMDMODE_BIT)
2178 ) {
2179 goto ret;
2180 }
2181
2182 /* Append this char */
2183#if ENABLE_UNICODE_SUPPORT
2184 if (unicode_status == UNICODE_ON) {
2185 mbstate_t mbstate = { 0 };
2186 char buf[MB_CUR_MAX + 1];
2187 int len = wcrtomb(buf, ic, &mbstate);
2188 if (len > 0) {
2189 buf[len] = '\0';
2190 if (match_buf_len + len < sizeof(match_buf))
2191 strcpy(match_buf + match_buf_len, buf);
2192 }
2193 } else
2194#endif
2195 if (match_buf_len < sizeof(match_buf) - 1) {
2196 match_buf[match_buf_len] = ic;
2197 match_buf[match_buf_len + 1] = '\0';
2198 }
2199 break;
2200 } /* switch (ic) */
2201
2202 /* Search in history for match_buf */
2203 h = state->cur_history;
2204 if (ic == CTRL('R'))
2205 h--;
2206 while (h >= 0) {
2207 if (state->history[h]) {
2208 char *match = strstr(state->history[h], match_buf);
2209 if (match) {
2210 state->cur_history = h;
2211 matched_history_line = state->history[h];
2212 command_len = load_string(matched_history_line);
2213 cursor = match - matched_history_line;
2214//FIXME: cursor position for Unicode case
2215
2216 free((char*)cmdedit_prompt);
2217 set_prompt:
2218 cmdedit_prompt = xasprintf("(reverse-i-search)'%s': ", match_buf);
2219 cmdedit_prmt_len = unicode_strwidth(cmdedit_prompt);
2220 goto do_redraw;
2221 }
2222 }
2223 h--;
2224 }
2225
2226 /* Not found */
2227 match_buf[match_buf_len] = '\0';
2228 beep();
2229 continue;
2230
2231 do_redraw:
2232 redraw(cmdedit_y, command_len - cursor);
2233 } /* while (1) */
2234
2235 ret:
2236 if (matched_history_line)
2237 command_len = load_string(matched_history_line);
2238
2239 free((char*)cmdedit_prompt);
2240 cmdedit_prompt = saved_prompt;
2241 cmdedit_prmt_len = saved_prmt_len;
2242 redraw(cmdedit_y, command_len - cursor);
2243
2244 return ic;
2245}
2246#endif
2247
2248/* maxsize must be >= 2.
2249 * Returns:
2250 * -1 on read errors or EOF, or on bare Ctrl-D,
2251 * 0 on ctrl-C (the line entered is still returned in 'command'),
2252 * >0 length of input string, including terminating '\n'
2253 */
2254int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *command, int maxsize, int timeout)
2255{
2256 int len;
2257#if ENABLE_FEATURE_TAB_COMPLETION
2258 smallint lastWasTab = 0;
2259#endif
2260 smallint break_out = 0;
2261#if ENABLE_FEATURE_EDITING_VI
2262 smallint vi_cmdmode = 0;
2263#endif
2264 struct termios initial_settings;
2265 struct termios new_settings;
2266 char read_key_buffer[KEYCODE_BUFFER_SIZE];
2267
2268 INIT_S();
2269
2270 if (tcgetattr(STDIN_FILENO, &initial_settings) < 0
2271 || (initial_settings.c_lflag & (ECHO|ICANON)) == ICANON
2272 ) {
2273 /* Happens when e.g. stty -echo was run before.
2274 * But if ICANON is not set, we don't come here.
2275 * (example: interactive python ^Z-backgrounded,
2276 * tty is still in "raw mode").
2277 */
2278 parse_and_put_prompt(prompt);
2279 /* fflush_all(); - done by parse_and_put_prompt */
2280 if (fgets(command, maxsize, stdin) == NULL)
2281 len = -1; /* EOF or error */
2282 else
2283 len = strlen(command);
2284 DEINIT_S();
2285 return len;
2286 }
2287
2288 init_unicode();
2289
2290// FIXME: audit & improve this
2291 if (maxsize > MAX_LINELEN)
2292 maxsize = MAX_LINELEN;
2293 S.maxsize = maxsize;
2294
2295 /* With zero flags, no other fields are ever used */
2296 state = st ? st : (line_input_t*) &const_int_0;
2297#if MAX_HISTORY > 0
2298# if ENABLE_FEATURE_EDITING_SAVEHISTORY
2299 if (state->hist_file)
2300 if (state->cnt_history == 0)
2301 load_history(state);
2302# endif
2303 if (state->flags & DO_HISTORY)
2304 state->cur_history = state->cnt_history;
2305#endif
2306
2307 /* prepare before init handlers */
2308 cmdedit_y = 0; /* quasireal y, not true if line > xt*yt */
2309 command_len = 0;
2310#if ENABLE_UNICODE_SUPPORT
2311 command_ps = xzalloc(maxsize * sizeof(command_ps[0]));
2312#else
2313 command_ps = command;
2314 command[0] = '\0';
2315#endif
2316#define command command_must_not_be_used
2317
2318 new_settings = initial_settings;
2319 /* ~ICANON: unbuffered input (most c_cc[] are disabled, VMIN/VTIME are enabled) */
2320 /* ~ECHO, ~ECHONL: turn off echoing, including newline echoing */
2321 /* ~ISIG: turn off INTR (ctrl-C), QUIT, SUSP */
2322 new_settings.c_lflag &= ~(ICANON | ECHO | ECHONL | ISIG);
2323 /* reads would block only if < 1 char is available */
2324 new_settings.c_cc[VMIN] = 1;
2325 /* no timeout (reads block forever) */
2326 new_settings.c_cc[VTIME] = 0;
2327 /* Should be not needed if ISIG is off: */
2328 /* Turn off CTRL-C */
2329 /* new_settings.c_cc[VINTR] = _POSIX_VDISABLE; */
2330 tcsetattr_stdin_TCSANOW(&new_settings);
2331
2332#if ENABLE_USERNAME_OR_HOMEDIR
2333 {
2334 struct passwd *entry;
2335
2336 entry = getpwuid(geteuid());
2337 if (entry) {
2338 user_buf = xstrdup(entry->pw_name);
2339 home_pwd_buf = xstrdup(entry->pw_dir);
2340 }
2341 }
2342#endif
2343
2344#if 0
2345 for (i = 0; i <= state->max_history; i++)
2346 bb_error_msg("history[%d]:'%s'", i, state->history[i]);
2347 bb_error_msg("cur_history:%d cnt_history:%d", state->cur_history, state->cnt_history);
2348#endif
2349
2350 /* Print out the command prompt, optionally ask where cursor is */
2351 parse_and_put_prompt(prompt);
2352 ask_terminal();
2353
2354 /* Install window resize handler (NB: after *all* init is complete) */
2355//FIXME: save entire sigaction!
2356 previous_SIGWINCH_handler = signal(SIGWINCH, win_changed);
2357 win_changed(0); /* get initial window size */
2358
2359 read_key_buffer[0] = 0;
2360 while (1) {
2361 /*
2362 * The emacs and vi modes share much of the code in the big
2363 * command loop. Commands entered when in vi's command mode
2364 * (aka "escape mode") get an extra bit added to distinguish
2365 * them - this keeps them from being self-inserted. This
2366 * clutters the big switch a bit, but keeps all the code
2367 * in one place.
2368 */
2369 int32_t ic, ic_raw;
2370
2371 fflush_all();
2372 ic = ic_raw = lineedit_read_key(read_key_buffer, timeout);
2373
2374#if ENABLE_FEATURE_REVERSE_SEARCH
2375 again:
2376#endif
2377#if ENABLE_FEATURE_EDITING_VI
2378 newdelflag = 1;
2379 if (vi_cmdmode) {
2380 /* btw, since KEYCODE_xxx are all < 0, this doesn't
2381 * change ic if it contains one of them: */
2382 ic |= VI_CMDMODE_BIT;
2383 }
2384#endif
2385
2386 switch (ic) {
2387 case '\n':
2388 case '\r':
2389 vi_case('\n'|VI_CMDMODE_BIT:)
2390 vi_case('\r'|VI_CMDMODE_BIT:)
2391 /* Enter */
2392 goto_new_line();
2393 break_out = 1;
2394 break;
2395 case CTRL('A'):
2396 vi_case('0'|VI_CMDMODE_BIT:)
2397 /* Control-a -- Beginning of line */
2398 input_backward(cursor);
2399 break;
2400 case CTRL('B'):
2401 vi_case('h'|VI_CMDMODE_BIT:)
2402 vi_case('\b'|VI_CMDMODE_BIT:) /* ^H */
2403 vi_case('\x7f'|VI_CMDMODE_BIT:) /* DEL */
2404 input_backward(1); /* Move back one character */
2405 break;
2406 case CTRL('E'):
2407 vi_case('$'|VI_CMDMODE_BIT:)
2408 /* Control-e -- End of line */
2409 put_till_end_and_adv_cursor();
2410 break;
2411 case CTRL('F'):
2412 vi_case('l'|VI_CMDMODE_BIT:)
2413 vi_case(' '|VI_CMDMODE_BIT:)
2414 input_forward(); /* Move forward one character */
2415 break;
2416 case '\b': /* ^H */
2417 case '\x7f': /* DEL */
2418 if (!isrtl_str())
2419 input_backspace();
2420 else
2421 input_delete(0);
2422 break;
2423 case KEYCODE_DELETE:
2424 if (!isrtl_str())
2425 input_delete(0);
2426 else
2427 input_backspace();
2428 break;
2429#if ENABLE_FEATURE_TAB_COMPLETION
2430 case '\t':
2431 input_tab(&lastWasTab);
2432 break;
2433#endif
2434 case CTRL('K'):
2435 /* Control-k -- clear to end of line */
2436 command_ps[cursor] = BB_NUL;
2437 command_len = cursor;
2438 printf(SEQ_CLEAR_TILL_END_OF_SCREEN);
2439 break;
2440 case CTRL('L'):
2441 vi_case(CTRL('L')|VI_CMDMODE_BIT:)
2442 /* Control-l -- clear screen */
2443 printf(ESC"[H"); /* cursor to top,left */
2444 redraw(0, command_len - cursor);
2445 break;
2446#if MAX_HISTORY > 0
2447 case CTRL('N'):
2448 vi_case(CTRL('N')|VI_CMDMODE_BIT:)
2449 vi_case('j'|VI_CMDMODE_BIT:)
2450 /* Control-n -- Get next command in history */
2451 if (get_next_history())
2452 goto rewrite_line;
2453 break;
2454 case CTRL('P'):
2455 vi_case(CTRL('P')|VI_CMDMODE_BIT:)
2456 vi_case('k'|VI_CMDMODE_BIT:)
2457 /* Control-p -- Get previous command from history */
2458 if (get_previous_history())
2459 goto rewrite_line;
2460 break;
2461#endif
2462 case CTRL('U'):
2463 vi_case(CTRL('U')|VI_CMDMODE_BIT:)
2464 /* Control-U -- Clear line before cursor */
2465 if (cursor) {
2466 command_len -= cursor;
2467 memmove(command_ps, command_ps + cursor,
2468 (command_len + 1) * sizeof(command_ps[0]));
2469 redraw(cmdedit_y, command_len);
2470 }
2471 break;
2472 case CTRL('W'):
2473 vi_case(CTRL('W')|VI_CMDMODE_BIT:)
2474 /* Control-W -- Remove the last word */
2475 while (cursor > 0 && BB_isspace(command_ps[cursor-1]))
2476 input_backspace();
2477 while (cursor > 0 && !BB_isspace(command_ps[cursor-1]))
2478 input_backspace();
2479 break;
2480#if ENABLE_FEATURE_REVERSE_SEARCH
2481 case CTRL('R'):
2482 ic = ic_raw = reverse_i_search();
2483 goto again;
2484#endif
2485
2486#if ENABLE_FEATURE_EDITING_VI
2487 case 'i'|VI_CMDMODE_BIT:
2488 vi_cmdmode = 0;
2489 break;
2490 case 'I'|VI_CMDMODE_BIT:
2491 input_backward(cursor);
2492 vi_cmdmode = 0;
2493 break;
2494 case 'a'|VI_CMDMODE_BIT:
2495 input_forward();
2496 vi_cmdmode = 0;
2497 break;
2498 case 'A'|VI_CMDMODE_BIT:
2499 put_till_end_and_adv_cursor();
2500 vi_cmdmode = 0;
2501 break;
2502 case 'x'|VI_CMDMODE_BIT:
2503 input_delete(1);
2504 break;
2505 case 'X'|VI_CMDMODE_BIT:
2506 if (cursor > 0) {
2507 input_backward(1);
2508 input_delete(1);
2509 }
2510 break;
2511 case 'W'|VI_CMDMODE_BIT:
2512 vi_Word_motion(1);
2513 break;
2514 case 'w'|VI_CMDMODE_BIT:
2515 vi_word_motion(1);
2516 break;
2517 case 'E'|VI_CMDMODE_BIT:
2518 vi_End_motion();
2519 break;
2520 case 'e'|VI_CMDMODE_BIT:
2521 vi_end_motion();
2522 break;
2523 case 'B'|VI_CMDMODE_BIT:
2524 vi_Back_motion();
2525 break;
2526 case 'b'|VI_CMDMODE_BIT:
2527 vi_back_motion();
2528 break;
2529 case 'C'|VI_CMDMODE_BIT:
2530 vi_cmdmode = 0;
2531 /* fall through */
2532 case 'D'|VI_CMDMODE_BIT:
2533 goto clear_to_eol;
2534
2535 case 'c'|VI_CMDMODE_BIT:
2536 vi_cmdmode = 0;
2537 /* fall through */
2538 case 'd'|VI_CMDMODE_BIT: {
2539 int nc, sc;
2540
2541 ic = lineedit_read_key(read_key_buffer, timeout);
2542 if (errno) /* error */
2543 goto return_error_indicator;
2544 if (ic == ic_raw) { /* "cc", "dd" */
2545 input_backward(cursor);
2546 goto clear_to_eol;
2547 break;
2548 }
2549
2550 sc = cursor;
2551 switch (ic) {
2552 case 'w':
2553 case 'W':
2554 case 'e':
2555 case 'E':
2556 switch (ic) {
2557 case 'w': /* "dw", "cw" */
2558 vi_word_motion(vi_cmdmode);
2559 break;
2560 case 'W': /* 'dW', 'cW' */
2561 vi_Word_motion(vi_cmdmode);
2562 break;
2563 case 'e': /* 'de', 'ce' */
2564 vi_end_motion();
2565 input_forward();
2566 break;
2567 case 'E': /* 'dE', 'cE' */
2568 vi_End_motion();
2569 input_forward();
2570 break;
2571 }
2572 nc = cursor;
2573 input_backward(cursor - sc);
2574 while (nc-- > cursor)
2575 input_delete(1);
2576 break;
2577 case 'b': /* "db", "cb" */
2578 case 'B': /* implemented as B */
2579 if (ic == 'b')
2580 vi_back_motion();
2581 else
2582 vi_Back_motion();
2583 while (sc-- > cursor)
2584 input_delete(1);
2585 break;
2586 case ' ': /* "d ", "c " */
2587 input_delete(1);
2588 break;
2589 case '$': /* "d$", "c$" */
2590 clear_to_eol:
2591 while (cursor < command_len)
2592 input_delete(1);
2593 break;
2594 }
2595 break;
2596 }
2597 case 'p'|VI_CMDMODE_BIT:
2598 input_forward();
2599 /* fallthrough */
2600 case 'P'|VI_CMDMODE_BIT:
2601 put();
2602 break;
2603 case 'r'|VI_CMDMODE_BIT:
2604//FIXME: unicode case?
2605 ic = lineedit_read_key(read_key_buffer, timeout);
2606 if (errno) /* error */
2607 goto return_error_indicator;
2608 if (ic < ' ' || ic > 255) {
2609 beep();
2610 } else {
2611 command_ps[cursor] = ic;
2612 bb_putchar(ic);
2613 bb_putchar('\b');
2614 }
2615 break;
2616 case '\x1b': /* ESC */
2617 if (state->flags & VI_MODE) {
2618 /* insert mode --> command mode */
2619 vi_cmdmode = 1;
2620 input_backward(1);
2621 }
2622 /* Handle a few ESC-<key> combinations the same way
2623 * standard readline bindings (IOW: bash) do.
2624 * Often, Alt-<key> generates ESC-<key>.
2625 */
2626 ic = lineedit_read_key(read_key_buffer, 50);
2627 switch (ic) {
2628 //case KEYCODE_LEFT: - bash doesn't do this
2629 case 'b':
2630 ctrl_left();
2631 break;
2632 //case KEYCODE_RIGHT: - bash doesn't do this
2633 case 'f':
2634 ctrl_right();
2635 break;
2636 //case KEYCODE_DELETE: - bash doesn't do this
2637 case 'd': /* Alt-D */
2638 {
2639 /* Delete word forward */
2640 int nc, sc = cursor;
2641 ctrl_right();
2642 nc = cursor - sc;
2643 input_backward(nc);
2644 while (--nc >= 0)
2645 input_delete(1);
2646 break;
2647 }
2648 case '\b': /* Alt-Backspace(?) */
2649 case '\x7f': /* Alt-Backspace(?) */
2650 //case 'w': - bash doesn't do this
2651 {
2652 /* Delete word backward */
2653 int sc = cursor;
2654 ctrl_left();
2655 while (sc-- > cursor)
2656 input_delete(1);
2657 break;
2658 }
2659 }
2660 break;
2661#endif /* FEATURE_COMMAND_EDITING_VI */
2662
2663#if MAX_HISTORY > 0
2664 case KEYCODE_UP:
2665 if (get_previous_history())
2666 goto rewrite_line;
2667 beep();
2668 break;
2669 case KEYCODE_DOWN:
2670 if (!get_next_history())
2671 break;
2672 rewrite_line:
2673 /* Rewrite the line with the selected history item */
2674 /* change command */
2675 command_len = load_string(state->history[state->cur_history] ?
2676 state->history[state->cur_history] : "");
2677 /* redraw and go to eol (bol, in vi) */
2678 redraw(cmdedit_y, (state->flags & VI_MODE) ? 9999 : 0);
2679 break;
2680#endif
2681 case KEYCODE_RIGHT:
2682 input_forward();
2683 break;
2684 case KEYCODE_LEFT:
2685 input_backward(1);
2686 break;
2687 case KEYCODE_CTRL_LEFT:
2688 case KEYCODE_ALT_LEFT: /* bash doesn't do it */
2689 ctrl_left();
2690 break;
2691 case KEYCODE_CTRL_RIGHT:
2692 case KEYCODE_ALT_RIGHT: /* bash doesn't do it */
2693 ctrl_right();
2694 break;
2695 case KEYCODE_HOME:
2696 input_backward(cursor);
2697 break;
2698 case KEYCODE_END:
2699 put_till_end_and_adv_cursor();
2700 break;
2701
2702 default:
2703 if (initial_settings.c_cc[VINTR] != 0
2704 && ic_raw == initial_settings.c_cc[VINTR]
2705 ) {
2706 /* Ctrl-C (usually) - stop gathering input */
2707 goto_new_line();
2708 command_len = 0;
2709 break_out = -1; /* "do not append '\n'" */
2710 break;
2711 }
2712 if (initial_settings.c_cc[VEOF] != 0
2713 && ic_raw == initial_settings.c_cc[VEOF]
2714 ) {
2715 /* Ctrl-D (usually) - delete one character,
2716 * or exit if len=0 and no chars to delete */
2717 if (command_len == 0) {
2718 errno = 0;
2719
2720 case -1: /* error (e.g. EIO when tty is destroyed) */
2721 IF_FEATURE_EDITING_VI(return_error_indicator:)
2722 break_out = command_len = -1;
2723 break;
2724 }
2725 input_delete(0);
2726 break;
2727 }
2728// /* Control-V -- force insert of next char */
2729// if (c == CTRL('V')) {
2730// if (safe_read(STDIN_FILENO, &c, 1) < 1)
2731// goto return_error_indicator;
2732// if (c == 0) {
2733// beep();
2734// break;
2735// }
2736// }
2737 if (ic < ' '
2738 || (!ENABLE_UNICODE_SUPPORT && ic >= 256)
2739 || (ENABLE_UNICODE_SUPPORT && ic >= VI_CMDMODE_BIT)
2740 ) {
2741 /* If VI_CMDMODE_BIT is set, ic is >= 256
2742 * and vi mode ignores unexpected chars.
2743 * Otherwise, we are here if ic is a
2744 * control char or an unhandled ESC sequence,
2745 * which is also ignored.
2746 */
2747 break;
2748 }
2749 if ((int)command_len >= (maxsize - 2)) {
2750 /* Not enough space for the char and EOL */
2751 break;
2752 }
2753
2754 command_len++;
2755 if (cursor == (command_len - 1)) {
2756 /* We are at the end, append */
2757 command_ps[cursor] = ic;
2758 command_ps[cursor + 1] = BB_NUL;
2759 put_cur_glyph_and_inc_cursor();
2760 if (unicode_bidi_isrtl(ic))
2761 input_backward(1);
2762 } else {
2763 /* In the middle, insert */
2764 int sc = cursor;
2765
2766 memmove(command_ps + sc + 1, command_ps + sc,
2767 (command_len - sc) * sizeof(command_ps[0]));
2768 command_ps[sc] = ic;
2769 /* is right-to-left char, or neutral one (e.g. comma) was just added to rtl text? */
2770 if (!isrtl_str())
2771 sc++; /* no */
2772 put_till_end_and_adv_cursor();
2773 /* to prev x pos + 1 */
2774 input_backward(cursor - sc);
2775 }
2776 break;
2777 } /* switch (ic) */
2778
2779 if (break_out)
2780 break;
2781
2782#if ENABLE_FEATURE_TAB_COMPLETION
2783 if (ic_raw != '\t')
2784 lastWasTab = 0;
2785#endif
2786 } /* while (1) */
2787
2788#if ENABLE_FEATURE_EDITING_ASK_TERMINAL
2789 if (S.sent_ESC_br6n) {
2790 /* "sleep 1; busybox ash" + hold [Enter] to trigger.
2791 * We sent "ESC [ 6 n", but got '\n' first, and
2792 * KEYCODE_CURSOR_POS response is now buffered from terminal.
2793 * It's bad already and not much can be done with it
2794 * (it _will_ be visible for the next process to read stdin),
2795 * but without this delay it even shows up on the screen
2796 * as garbage because we restore echo settings with tcsetattr
2797 * before it comes in. UGLY!
2798 */
2799 usleep(20*1000);
2800 }
2801#endif
2802
2803/* End of bug-catching "command_must_not_be_used" trick */
2804#undef command
2805
2806#if ENABLE_UNICODE_SUPPORT
2807 command[0] = '\0';
2808 if (command_len > 0)
2809 command_len = save_string(command, maxsize - 1);
2810 free(command_ps);
2811#endif
2812
2813 if (command_len > 0) {
2814 remember_in_history(command);
2815 }
2816
2817 if (break_out > 0) {
2818 command[command_len++] = '\n';
2819 command[command_len] = '\0';
2820 }
2821
2822#if ENABLE_FEATURE_TAB_COMPLETION
2823 free_tab_completion_data();
2824#endif
2825
2826 /* restore initial_settings */
2827 tcsetattr_stdin_TCSANOW(&initial_settings);
2828 /* restore SIGWINCH handler */
2829 signal(SIGWINCH, previous_SIGWINCH_handler);
2830 fflush_all();
2831
2832 len = command_len;
2833 DEINIT_S();
2834
2835 return len; /* can't return command_len, DEINIT_S() destroys it */
2836}
2837
2838#else /* !FEATURE_EDITING */
2839
2840#undef read_line_input
2841int FAST_FUNC read_line_input(const char* prompt, char* command, int maxsize)
2842{
2843 fputs(prompt, stdout);
2844 fflush_all();
2845 if (!fgets(command, maxsize, stdin))
2846 return -1;
2847 return strlen(command);
2848}
2849
2850#endif /* !FEATURE_EDITING */
2851
2852
2853/*
2854 * Testing
2855 */
2856
2857#ifdef TEST
2858
2859#include <locale.h>
2860
2861const char *applet_name = "debug stuff usage";
2862
2863int main(int argc, char **argv)
2864{
2865 char buff[MAX_LINELEN];
2866 char *prompt =
2867#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
2868 "\\[\\033[32;1m\\]\\u@\\[\\x1b[33;1m\\]\\h:"
2869 "\\[\\033[34;1m\\]\\w\\[\\033[35;1m\\] "
2870 "\\!\\[\\e[36;1m\\]\\$ \\[\\E[0m\\]";
2871#else
2872 "% ";
2873#endif
2874
2875 while (1) {
2876 int l;
2877 l = read_line_input(prompt, buff);
2878 if (l <= 0 || buff[l-1] != '\n')
2879 break;
2880 buff[l-1] = '\0';
2881 printf("*** read_line_input() returned line =%s=\n", buff);
2882 }
2883 printf("*** read_line_input() detect ^D\n");
2884 return 0;
2885}
2886
2887#endif /* TEST */
Note: See TracBrowser for help on using the repository browser.