1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42#include "libbb.h"
43#include "unicode.h"
44#ifndef _POSIX_VDISABLE
45# define _POSIX_VDISABLE '\0'
46#endif
47
48
49#ifdef TEST
50# define ENABLE_FEATURE_EDITING 0
51# define ENABLE_FEATURE_TAB_COMPLETION 0
52# define ENABLE_FEATURE_USERNAME_COMPLETION 0
53#endif
54
55
56
57#if ENABLE_FEATURE_EDITING
58
59
60#define ENABLE_USERNAME_OR_HOMEDIR \
61 (ENABLE_FEATURE_USERNAME_COMPLETION || ENABLE_FEATURE_EDITING_FANCY_PROMPT)
62#define IF_USERNAME_OR_HOMEDIR(...)
63#if ENABLE_USERNAME_OR_HOMEDIR
64# undef IF_USERNAME_OR_HOMEDIR
65# define IF_USERNAME_OR_HOMEDIR(...) __VA_ARGS__
66#endif
67
68
69#undef CHAR_T
70#if ENABLE_UNICODE_SUPPORT
71# define BB_NUL ((wchar_t)0)
72# define CHAR_T wchar_t
73static bool BB_isspace(CHAR_T c) { return ((unsigned)c < 256 && isspace(c)); }
74# if ENABLE_FEATURE_EDITING_VI
75static bool BB_isalnum(CHAR_T c) { return ((unsigned)c < 256 && isalnum(c)); }
76# endif
77static 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#if ENABLE_UNICODE_PRESERVE_BROKEN
94# define unicode_mark_raw_byte(wc) ((wc) | 0x20000000)
95# define unicode_is_raw_byte(wc) ((wc) & 0x20000000)
96#else
97# define unicode_is_raw_byte(wc) 0
98#endif
99
100
101#define ESC "\033"
102
103#define SEQ_CLEAR_TILL_END_OF_SCREEN ESC"[J"
104
105
106
107enum {
108 MAX_LINELEN = CONFIG_FEATURE_EDITING_MAX_LEN < 0x7ff0
109 ? CONFIG_FEATURE_EDITING_MAX_LEN
110 : 0x7ff0
111};
112
113#if ENABLE_USERNAME_OR_HOMEDIR
114static const char null_str[] ALIGN1 = "";
115#endif
116
117
118struct lineedit_statics {
119 line_input_t *state;
120
121 volatile unsigned cmdedit_termw;
122 sighandler_t previous_SIGWINCH_handler;
123
124 unsigned cmdedit_x;
125 unsigned cmdedit_y;
126 unsigned cmdedit_prmt_len;
127
128 unsigned cursor;
129 int command_len;
130
131
132 int maxsize;
133 CHAR_T *command_ps;
134
135 const char *cmdedit_prompt;
136#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
137 int num_ok_lines;
138#endif
139
140#if ENABLE_USERNAME_OR_HOMEDIR
141 char *user_buf;
142 char *home_pwd_buf;
143#endif
144
145#if ENABLE_FEATURE_TAB_COMPLETION
146 char **matches;
147 unsigned num_matches;
148#endif
149
150#if ENABLE_FEATURE_EDITING_VI
151# define DELBUFSIZ 128
152 CHAR_T *delptr;
153 smallint newdelflag;
154 CHAR_T delbuf[DELBUFSIZ];
155#endif
156#if ENABLE_FEATURE_EDITING_ASK_TERMINAL
157 smallint sent_ESC_br6n;
158#endif
159};
160
161
162extern struct lineedit_statics *const lineedit_ptr_to_statics;
163
164#define S (*lineedit_ptr_to_statics)
165#define state (S.state )
166#define cmdedit_termw (S.cmdedit_termw )
167#define previous_SIGWINCH_handler (S.previous_SIGWINCH_handler)
168#define cmdedit_x (S.cmdedit_x )
169#define cmdedit_y (S.cmdedit_y )
170#define cmdedit_prmt_len (S.cmdedit_prmt_len)
171#define cursor (S.cursor )
172#define command_len (S.command_len )
173#define command_ps (S.command_ps )
174#define cmdedit_prompt (S.cmdedit_prompt )
175#define num_ok_lines (S.num_ok_lines )
176#define user_buf (S.user_buf )
177#define home_pwd_buf (S.home_pwd_buf )
178#define matches (S.matches )
179#define num_matches (S.num_matches )
180#define delptr (S.delptr )
181#define newdelflag (S.newdelflag )
182#define delbuf (S.delbuf )
183
184#define INIT_S() do { \
185 (*(struct lineedit_statics**)&lineedit_ptr_to_statics) = xzalloc(sizeof(S)); \
186 barrier(); \
187 cmdedit_termw = 80; \
188 IF_FEATURE_EDITING_FANCY_PROMPT(num_ok_lines = 1;) \
189 IF_USERNAME_OR_HOMEDIR(home_pwd_buf = (char*)null_str;) \
190} while (0)
191
192static void deinit_S(void)
193{
194#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
195
196
197 free((char*)cmdedit_prompt);
198#endif
199#if ENABLE_USERNAME_OR_HOMEDIR
200 free(user_buf);
201 if (home_pwd_buf != null_str)
202 free(home_pwd_buf);
203#endif
204 free(lineedit_ptr_to_statics);
205}
206#define DEINIT_S() deinit_S()
207
208
209#if ENABLE_UNICODE_SUPPORT
210static size_t load_string(const char *src)
211{
212 if (unicode_status == UNICODE_ON) {
213 ssize_t len = mbstowcs(command_ps, src, S.maxsize - 1);
214 if (len < 0)
215 len = 0;
216 command_ps[len] = BB_NUL;
217 return len;
218 } else {
219 unsigned i = 0;
220 while (src[i] && i < S.maxsize - 1) {
221 command_ps[i] = src[i];
222 i++;
223 }
224 command_ps[i] = BB_NUL;
225 return i;
226 }
227}
228static unsigned save_string(char *dst, unsigned maxsize)
229{
230 if (unicode_status == UNICODE_ON) {
231# if !ENABLE_UNICODE_PRESERVE_BROKEN
232 ssize_t len = wcstombs(dst, command_ps, maxsize - 1);
233 if (len < 0)
234 len = 0;
235 dst[len] = '\0';
236 return len;
237# else
238 unsigned dstpos = 0;
239 unsigned srcpos = 0;
240
241 maxsize--;
242 while (dstpos < maxsize) {
243 wchar_t wc;
244 int n = srcpos;
245
246
247 while ((wc = command_ps[srcpos]) != BB_NUL
248 && !unicode_is_raw_byte(wc)
249 ) {
250 srcpos++;
251 }
252 command_ps[srcpos] = BB_NUL;
253 n = wcstombs(dst + dstpos, command_ps + n, maxsize - dstpos);
254 if (n < 0)
255 break;
256 dstpos += n;
257 if (wc == BB_NUL)
258 break;
259
260
261 command_ps[srcpos] = wc;
262 srcpos++;
263 if (dstpos == maxsize)
264 break;
265 dst[dstpos++] = (char) wc;
266 }
267 dst[dstpos] = '\0';
268 return dstpos;
269# endif
270 } else {
271 unsigned i = 0;
272 while ((dst[i] = command_ps[i]) != 0)
273 i++;
274 return i;
275 }
276}
277
278static void BB_PUTCHAR(wchar_t c)
279{
280 if (unicode_status == UNICODE_ON) {
281 char buf[MB_CUR_MAX + 1];
282 mbstate_t mbst = { 0 };
283 ssize_t len = wcrtomb(buf, c, &mbst);
284 if (len > 0) {
285 buf[len] = '\0';
286 fputs(buf, stdout);
287 }
288 } else {
289
290 putchar(c);
291 }
292}
293# if ENABLE_UNICODE_COMBINING_WCHARS || ENABLE_UNICODE_WIDE_WCHARS
294static wchar_t adjust_width_and_validate_wc(unsigned *width_adj, wchar_t wc)
295# else
296static wchar_t adjust_width_and_validate_wc(wchar_t wc)
297# define adjust_width_and_validate_wc(width_adj, wc) \
298 ((*(width_adj))++, adjust_width_and_validate_wc(wc))
299# endif
300{
301 int w = 1;
302
303 if (unicode_status == UNICODE_ON) {
304 if (wc > CONFIG_LAST_SUPPORTED_WCHAR) {
305
306 goto subst;
307 }
308 w = wcwidth(wc);
309 if ((ENABLE_UNICODE_COMBINING_WCHARS && w < 0)
310 || (!ENABLE_UNICODE_COMBINING_WCHARS && w <= 0)
311 || (!ENABLE_UNICODE_WIDE_WCHARS && w > 1)
312 ) {
313 subst:
314 w = 1;
315 wc = CONFIG_SUBST_WCHAR;
316 }
317 }
318
319# if ENABLE_UNICODE_COMBINING_WCHARS || ENABLE_UNICODE_WIDE_WCHARS
320 *width_adj += w;
321#endif
322 return wc;
323}
324#else
325static size_t load_string(const char *src)
326{
327 safe_strncpy(command_ps, src, S.maxsize);
328 return strlen(command_ps);
329}
330# if ENABLE_FEATURE_TAB_COMPLETION
331static void save_string(char *dst, unsigned maxsize)
332{
333 safe_strncpy(dst, command_ps, maxsize);
334}
335# endif
336# define BB_PUTCHAR(c) bb_putchar(c)
337
338int adjust_width_and_validate_wc(unsigned *width_adj, int wc);
339#endif
340
341
342
343
344
345#define HACK_FOR_WRONG_WIDTH 1
346static void put_cur_glyph_and_inc_cursor(void)
347{
348 CHAR_T c = command_ps[cursor];
349 unsigned width = 0;
350 int ofs_to_right;
351
352 if (c == BB_NUL) {
353
354 c = ' ';
355 } else {
356
357 cursor++;
358 if (unicode_status == UNICODE_ON) {
359 IF_UNICODE_WIDE_WCHARS(width = cmdedit_x;)
360 c = adjust_width_and_validate_wc(&cmdedit_x, c);
361 IF_UNICODE_WIDE_WCHARS(width = cmdedit_x - width;)
362 } else {
363 cmdedit_x++;
364 }
365 }
366
367 ofs_to_right = cmdedit_x - cmdedit_termw;
368 if (!ENABLE_UNICODE_WIDE_WCHARS || ofs_to_right <= 0) {
369
370 BB_PUTCHAR(c);
371 }
372
373 if (ofs_to_right >= 0) {
374
375#if HACK_FOR_WRONG_WIDTH
376
377
378
379
380
381
382
383 puts("\r");
384#else
385
386
387
388
389
390
391 c = command_ps[cursor];
392 if (c == BB_NUL)
393 c = ' ';
394 BB_PUTCHAR(c);
395 bb_putchar('\b');
396#endif
397 cmdedit_y++;
398 if (!ENABLE_UNICODE_WIDE_WCHARS || ofs_to_right == 0) {
399 width = 0;
400 } else {
401
402 BB_PUTCHAR(c);
403 }
404 cmdedit_x = width;
405 }
406}
407
408
409static void put_till_end_and_adv_cursor(void)
410{
411 while (cursor < command_len)
412 put_cur_glyph_and_inc_cursor();
413}
414
415
416static void goto_new_line(void)
417{
418 put_till_end_and_adv_cursor();
419 if (cmdedit_x != 0)
420 bb_putchar('\n');
421}
422
423static void beep(void)
424{
425 bb_putchar('\007');
426}
427
428static void put_prompt(void)
429{
430 unsigned w;
431
432 fputs(cmdedit_prompt, stdout);
433 fflush_all();
434 cursor = 0;
435 w = cmdedit_termw;
436 cmdedit_y = cmdedit_prmt_len / w;
437 cmdedit_x = cmdedit_prmt_len % w;
438}
439
440
441
442static void input_backward(unsigned num)
443{
444 if (num > cursor)
445 num = cursor;
446 if (num == 0)
447 return;
448 cursor -= num;
449
450 if ((ENABLE_UNICODE_COMBINING_WCHARS || ENABLE_UNICODE_WIDE_WCHARS)
451 && unicode_status == UNICODE_ON
452 ) {
453
454 int n = num;
455 num = 0;
456 while (--n >= 0)
457 adjust_width_and_validate_wc(&num, command_ps[cursor + n]);
458 if (num == 0)
459 return;
460 }
461
462 if (cmdedit_x >= num) {
463 cmdedit_x -= num;
464 if (num <= 4) {
465
466
467
468
469
470
471 do {
472 bb_putchar('\b');
473 } while (--num);
474 return;
475 }
476 printf(ESC"[%uD", num);
477 return;
478 }
479
480
481 if (ENABLE_UNICODE_WIDE_WCHARS) {
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499 unsigned sv_cursor;
500
501 printf("\r" ESC"[%uA", cmdedit_y);
502 cmdedit_y = 0;
503 sv_cursor = cursor;
504 put_prompt();
505 while (cursor < sv_cursor)
506 put_cur_glyph_and_inc_cursor();
507 } else {
508 int lines_up;
509 unsigned width;
510
511 num -= cmdedit_x;
512 width = cmdedit_termw;
513
514 lines_up = 1 + (num - 1) / width;
515 cmdedit_x = (width * cmdedit_y - num) % width;
516 cmdedit_y -= lines_up;
517
518 printf("\r" ESC"[%uA", lines_up);
519
520
521
522 if (cmdedit_x)
523 printf(ESC"[%uC", cmdedit_x);
524 }
525}
526
527
528static void redraw(int y, int back_cursor)
529{
530 if (y > 0)
531 printf(ESC"[%uA", y);
532 bb_putchar('\r');
533 put_prompt();
534 put_till_end_and_adv_cursor();
535 printf(SEQ_CLEAR_TILL_END_OF_SCREEN);
536 input_backward(back_cursor);
537}
538
539
540
541#if !ENABLE_FEATURE_EDITING_VI
542static void input_delete(void)
543#define input_delete(save) input_delete()
544#else
545static void input_delete(int save)
546#endif
547{
548 int j = cursor;
549
550 if (j == (int)command_len)
551 return;
552
553#if ENABLE_FEATURE_EDITING_VI
554 if (save) {
555 if (newdelflag) {
556 delptr = delbuf;
557 newdelflag = 0;
558 }
559 if ((delptr - delbuf) < DELBUFSIZ)
560 *delptr++ = command_ps[j];
561 }
562#endif
563
564 memmove(command_ps + j, command_ps + j + 1,
565
566
567 (command_len - j) * sizeof(command_ps[0]));
568 command_len--;
569 put_till_end_and_adv_cursor();
570
571 printf(SEQ_CLEAR_TILL_END_OF_SCREEN);
572 input_backward(cursor - j);
573}
574
575#if ENABLE_FEATURE_EDITING_VI
576static void put(void)
577{
578 int ocursor;
579 int j = delptr - delbuf;
580
581 if (j == 0)
582 return;
583 ocursor = cursor;
584
585 memmove(command_ps + cursor + j, command_ps + cursor,
586 (command_len - cursor + 1) * sizeof(command_ps[0]));
587 memcpy(command_ps + cursor, delbuf, j * sizeof(command_ps[0]));
588 command_len += j;
589 put_till_end_and_adv_cursor();
590 input_backward(cursor - ocursor - j + 1);
591}
592#endif
593
594
595static void input_backspace(void)
596{
597 if (cursor > 0) {
598 input_backward(1);
599 input_delete(0);
600 }
601}
602
603
604static void input_forward(void)
605{
606 if (cursor < command_len)
607 put_cur_glyph_and_inc_cursor();
608}
609
610#if ENABLE_FEATURE_TAB_COMPLETION
611
612
613
614
615
616
617
618static void free_tab_completion_data(void)
619{
620 if (matches) {
621 while (num_matches)
622 free(matches[--num_matches]);
623 free(matches);
624 matches = NULL;
625 }
626}
627
628static void add_match(char *matched)
629{
630 matches = xrealloc_vector(matches, 4, num_matches);
631 matches[num_matches] = matched;
632 num_matches++;
633}
634
635# if ENABLE_FEATURE_USERNAME_COMPLETION
636
637
638
639
640static char *username_path_completion(char *ud)
641{
642 struct passwd *entry;
643 char *tilde_name = ud;
644 char *home = NULL;
645
646 ud++;
647 if (*ud == '/') {
648 home = home_pwd_buf;
649 } else {
650
651 ud = strchr(ud, '/');
652 *ud = '\0';
653 entry = getpwnam(tilde_name + 1);
654 *ud = '/';
655 if (entry)
656 home = entry->pw_dir;
657 }
658 if (home) {
659 ud = concat_path_file(home, ud);
660 free(tilde_name);
661 tilde_name = ud;
662 }
663 return tilde_name;
664}
665
666
667
668
669static NOINLINE unsigned complete_username(const char *ud)
670{
671
672 char line_buff[256];
673 struct passwd pwd;
674 struct passwd *result;
675 unsigned userlen;
676
677 ud++;
678 userlen = strlen(ud);
679
680 setpwent();
681 while (!getpwent_r(&pwd, line_buff, sizeof(line_buff), &result)) {
682
683 if ( strncmp(ud, pwd.pw_name, userlen) == 0) {
684 add_match(xasprintf("~%s/", pwd.pw_name));
685 }
686 }
687 endpwent();
688
689 return 1 + userlen;
690}
691# endif
692
693enum {
694 FIND_EXE_ONLY = 0,
695 FIND_DIR_ONLY = 1,
696 FIND_FILE_ONLY = 2,
697};
698
699static int path_parse(char ***p)
700{
701 int npth;
702 const char *pth;
703 char *tmp;
704 char **res;
705
706 if (state->flags & WITH_PATH_LOOKUP)
707 pth = state->path_lookup;
708 else
709 pth = getenv("PATH");
710
711
712 if (!pth || !pth[0] || LONE_CHAR(pth, ':'))
713 return 1;
714
715 tmp = (char*)pth;
716 npth = 1;
717 while (1) {
718 tmp = strchr(tmp, ':');
719 if (!tmp)
720 break;
721 tmp++;
722 if (*tmp == '\0')
723 break;
724 npth++;
725 }
726
727 *p = res = xmalloc(npth * sizeof(res[0]));
728 res[0] = tmp = xstrdup(pth);
729 npth = 1;
730 while (1) {
731 tmp = strchr(tmp, ':');
732 if (!tmp)
733 break;
734 *tmp++ = '\0';
735 if (*tmp == '\0')
736 break;
737 res[npth++] = tmp;
738 }
739 return npth;
740}
741
742
743
744
745static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type)
746{
747 char *path1[1];
748 char **paths = path1;
749 int npaths;
750 int i;
751 unsigned pf_len;
752 const char *pfind;
753 char *dirbuf = NULL;
754
755 npaths = 1;
756 path1[0] = (char*)".";
757
758 pfind = strrchr(command, '/');
759 if (!pfind) {
760 if (type == FIND_EXE_ONLY)
761 npaths = path_parse(&paths);
762 pfind = command;
763 } else {
764
765 pfind++;
766
767 dirbuf = xstrndup(command, pfind - command);
768# if ENABLE_FEATURE_USERNAME_COMPLETION
769 if (dirbuf[0] == '~')
770 dirbuf = username_path_completion(dirbuf);
771# endif
772 path1[0] = dirbuf;
773 }
774 pf_len = strlen(pfind);
775
776 for (i = 0; i < npaths; i++) {
777 DIR *dir;
778 struct dirent *next;
779 struct stat st;
780 char *found;
781
782 dir = opendir(paths[i]);
783 if (!dir)
784 continue;
785
786 while ((next = readdir(dir)) != NULL) {
787 unsigned len;
788 const char *name_found = next->d_name;
789
790
791 if (!pfind[0] && DOT_OR_DOTDOT(name_found))
792 continue;
793
794 if (strncmp(name_found, pfind, pf_len) != 0)
795 continue;
796
797 found = concat_path_file(paths[i], name_found);
798
799
800
801 if (stat(found, &st) && lstat(found, &st))
802 goto cont;
803
804
805 len = strlen(name_found);
806 found = xrealloc(found, len + 2);
807 strcpy(found, name_found);
808
809 if (S_ISDIR(st.st_mode)) {
810
811 found[len] = '/';
812 found[len + 1] = '\0';
813 } else {
814
815 if (type == FIND_DIR_ONLY)
816 goto cont;
817 }
818
819 add_match(found);
820 continue;
821 cont:
822 free(found);
823 }
824 closedir(dir);
825 }
826
827 if (paths != path1) {
828 free(paths[0]);
829 free(paths);
830 }
831 free(dirbuf);
832
833 return pf_len;
834}
835
836
837
838
839
840
841
842#define dbg_bmp 0
843
844
845
846
847#define QUOT (UCHAR_MAX+1)
848static void remove_chunk(int16_t *int_buf, int beg, int end)
849{
850
851 if (beg == end)
852 return;
853
854 while ((int_buf[beg] = int_buf[end]) != 0)
855 beg++, end++;
856
857 if (dbg_bmp) {
858 int i;
859 for (i = 0; int_buf[i]; i++)
860 bb_putchar((unsigned char)int_buf[i]);
861 bb_putchar('\n');
862 }
863}
864
865
866
867static NOINLINE int build_match_prefix(char *match_buf)
868{
869 int i, j;
870 int command_mode;
871 int16_t *int_buf = (int16_t*)match_buf;
872
873 if (dbg_bmp) printf("\n%s\n", match_buf);
874
875
876 i = strlen(match_buf);
877 do {
878 int_buf[i] = (unsigned char)match_buf[i];
879 i--;
880 } while (i >= 0);
881
882
883 for (i = 0; int_buf[i]; i++) {
884 if (int_buf[i] == '\\') {
885 remove_chunk(int_buf, i, i + 1);
886 int_buf[i] |= QUOT;
887 }
888 }
889
890 {
891 int in_quote = 0;
892 i = 0;
893 while (int_buf[i]) {
894 int cur = int_buf[i];
895 if (!cur)
896 break;
897 if (cur == '\'' || cur == '"') {
898 if (!in_quote || (cur == in_quote)) {
899 in_quote ^= cur;
900 remove_chunk(int_buf, i, i + 1);
901 continue;
902 }
903 }
904 if (in_quote)
905 int_buf[i] = cur | QUOT;
906 i++;
907 }
908 }
909
910
911
912
913
914 for (i = 0; int_buf[i]; i++) {
915 int cur = int_buf[i];
916 if (cur == ';' || cur == '&' || cur == '|') {
917 int prev = i ? int_buf[i - 1] : 0;
918 if (cur == '&' && (prev == '>' || prev == '<')) {
919 continue;
920 } else if (cur == '|' && prev == '>') {
921 continue;
922 }
923 remove_chunk(int_buf, 0, i + 1 + (cur == int_buf[i + 1]));
924 i = -1;
925 }
926 }
927
928 for (i = 0; int_buf[i]; i++) {
929 if (int_buf[i] == '`') {
930 for (j = i + 1; int_buf[j]; j++) {
931 if (int_buf[j] == '`') {
932
933
934
935
936
937 remove_chunk(int_buf, i, j);
938 goto next;
939 }
940 }
941
942 remove_chunk(int_buf, 0, i + 1);
943 break;
944 next: ;
945 }
946 }
947
948
949
950
951
952 for (i = 0; int_buf[i]; i++) {
953 if (int_buf[i] == '(' || int_buf[i] == '{') {
954 remove_chunk(int_buf, 0, i + 1);
955 i = -1;
956 }
957 }
958
959
960 for (i = 0; int_buf[i]; i++)
961 if (int_buf[i] != ' ')
962 break;
963 remove_chunk(int_buf, 0, i);
964
965
966 command_mode = FIND_EXE_ONLY;
967 for (i = 0; int_buf[i]; i++) {
968 if (int_buf[i] == ' ' || int_buf[i] == '<' || int_buf[i] == '>') {
969 if (int_buf[i] == ' '
970 && command_mode == FIND_EXE_ONLY
971 && (char)int_buf[0] == 'c'
972 && (char)int_buf[1] == 'd'
973 && i == 2
974 ) {
975 command_mode = FIND_DIR_ONLY;
976 } else {
977 command_mode = FIND_FILE_ONLY;
978 break;
979 }
980 }
981 }
982 if (dbg_bmp) printf("command_mode(0:exe/1:dir/2:file):%d\n", command_mode);
983
984
985 for (i = 0; int_buf[i]; i++)
986 continue;
987 for (--i; i >= 0; i--) {
988 int cur = int_buf[i];
989 if (cur == ' ' || cur == '<' || cur == '>' || cur == '|' || cur == '&') {
990 remove_chunk(int_buf, 0, i + 1);
991 break;
992 }
993 }
994
995
996 i = 0;
997 while ((match_buf[i] = int_buf[i]) != '\0')
998 i++;
999
1000 if (dbg_bmp) printf("final match_buf:'%s'\n", match_buf);
1001
1002 return command_mode;
1003}
1004
1005
1006
1007
1008
1009static void showfiles(void)
1010{
1011 int ncols, row;
1012 int column_width = 0;
1013 int nfiles = num_matches;
1014 int nrows = nfiles;
1015 int l;
1016
1017
1018 for (row = 0; row < nrows; row++) {
1019 l = unicode_strwidth(matches[row]);
1020 if (column_width < l)
1021 column_width = l;
1022 }
1023 column_width += 2;
1024 ncols = cmdedit_termw / column_width;
1025
1026 if (ncols > 1) {
1027 nrows /= ncols;
1028 if (nfiles % ncols)
1029 nrows++;
1030 } else {
1031 ncols = 1;
1032 }
1033 for (row = 0; row < nrows; row++) {
1034 int n = row;
1035 int nc;
1036
1037 for (nc = 1; nc < ncols && n+nrows < nfiles; n += nrows, nc++) {
1038 printf("%s%-*s", matches[n],
1039 (int)(column_width - unicode_strwidth(matches[n])), ""
1040 );
1041 }
1042 if (ENABLE_UNICODE_SUPPORT)
1043 puts(printable_string(NULL, matches[n]));
1044 else
1045 puts(matches[n]);
1046 }
1047}
1048
1049static const char *is_special_char(char c)
1050{
1051 return strchr(" `\"#$%^&*()=+{}[]:;'|\\<>", c);
1052}
1053
1054static char *quote_special_chars(char *found)
1055{
1056 int l = 0;
1057 char *s = xzalloc((strlen(found) + 1) * 2);
1058
1059 while (*found) {
1060 if (is_special_char(*found))
1061 s[l++] = '\\';
1062 s[l++] = *found++;
1063 }
1064
1065 return s;
1066}
1067
1068
1069static NOINLINE void input_tab(smallint *lastWasTab)
1070{
1071 char *chosen_match;
1072 char *match_buf;
1073 size_t len_found;
1074
1075 unsigned match_pfx_len = match_pfx_len;
1076 int find_type;
1077# if ENABLE_UNICODE_SUPPORT
1078
1079 int cursor_mb;
1080# endif
1081 if (!(state->flags & TAB_COMPLETION))
1082 return;
1083
1084 if (*lastWasTab) {
1085
1086
1087
1088 if (num_matches > 0) {
1089
1090 int sav_cursor = cursor;
1091 goto_new_line();
1092 showfiles();
1093 redraw(0, command_len - sav_cursor);
1094 }
1095 return;
1096 }
1097
1098 *lastWasTab = 1;
1099 chosen_match = NULL;
1100
1101
1102
1103
1104
1105
1106 match_buf = xmalloc(MAX_LINELEN * sizeof(int16_t));
1107# if !ENABLE_UNICODE_SUPPORT
1108 save_string(match_buf, cursor + 1);
1109# else
1110 {
1111 CHAR_T wc = command_ps[cursor];
1112 command_ps[cursor] = BB_NUL;
1113 save_string(match_buf, MAX_LINELEN);
1114 command_ps[cursor] = wc;
1115 cursor_mb = strlen(match_buf);
1116 }
1117# endif
1118 find_type = build_match_prefix(match_buf);
1119
1120
1121 free_tab_completion_data();
1122
1123# if ENABLE_FEATURE_USERNAME_COMPLETION
1124
1125
1126 if (state->flags & USERNAME_COMPLETION)
1127 if (match_buf[0] == '~' && strchr(match_buf, '/') == NULL)
1128 match_pfx_len = complete_username(match_buf);
1129# endif
1130
1131
1132 if (!matches)
1133 match_pfx_len = complete_cmd_dir_file(match_buf, find_type);
1134
1135
1136
1137 {
1138 const char *e = match_buf + strlen(match_buf);
1139 const char *s = e - match_pfx_len;
1140 while (s < e)
1141 if (is_special_char(*s++))
1142 match_pfx_len++;
1143 }
1144
1145
1146 if (matches) {
1147 unsigned i, n = 0;
1148 qsort_string_vector(matches, num_matches);
1149 for (i = 0; i < num_matches - 1; ++i) {
1150
1151 if (strcmp(matches[i], matches[i+1]) == 0) {
1152 free(matches[i]);
1153
1154 } else {
1155 matches[n++] = matches[i];
1156 }
1157
1158 }
1159 matches[n++] = matches[i];
1160 num_matches = n;
1161 }
1162
1163
1164 if (num_matches != 1) {
1165 char *cp;
1166 beep();
1167 if (!matches)
1168 goto ret;
1169
1170 chosen_match = xstrdup(matches[0]);
1171 for (cp = chosen_match; *cp; cp++) {
1172 unsigned n;
1173 for (n = 1; n < num_matches; n++) {
1174 if (matches[n][cp - chosen_match] != *cp) {
1175 goto stop;
1176 }
1177 }
1178 }
1179 stop:
1180 if (cp == chosen_match) {
1181 goto ret;
1182 }
1183 *cp = '\0';
1184 cp = quote_special_chars(chosen_match);
1185 free(chosen_match);
1186 chosen_match = cp;
1187 len_found = strlen(chosen_match);
1188 } else {
1189
1190 *lastWasTab = 0;
1191
1192 chosen_match = quote_special_chars(matches[0]);
1193 len_found = strlen(chosen_match);
1194 if (chosen_match[len_found-1] != '/') {
1195 chosen_match[len_found] = ' ';
1196 chosen_match[++len_found] = '\0';
1197 }
1198 }
1199
1200# if !ENABLE_UNICODE_SUPPORT
1201
1202
1203
1204
1205 if ((int)(len_found - match_pfx_len + command_len) < S.maxsize) {
1206 int pos;
1207
1208 strcpy(match_buf, &command_ps[cursor]);
1209
1210 sprintf(&command_ps[cursor], "%s%s", chosen_match + match_pfx_len, match_buf);
1211 command_len = strlen(command_ps);
1212
1213 pos = cursor + len_found - match_pfx_len;
1214
1215 redraw(cmdedit_y, command_len - pos);
1216 }
1217# else
1218 {
1219
1220 char *command = match_buf + MAX_LINELEN;
1221 int len = save_string(command, MAX_LINELEN);
1222
1223
1224 if ((int)(len_found - match_pfx_len + len) < MAX_LINELEN) {
1225 int pos;
1226
1227 strcpy(match_buf, &command[cursor_mb]);
1228
1229 strcpy(&command[cursor_mb], chosen_match + match_pfx_len);
1230 len = load_string(command);
1231
1232 sprintf(&command[cursor_mb], "%s%s", chosen_match + match_pfx_len, match_buf);
1233 command_len = load_string(command);
1234
1235
1236
1237 pos = command_len - len;
1238 redraw(cmdedit_y, pos >= 0 ? pos : 0);
1239 }
1240 }
1241# endif
1242 ret:
1243 free(chosen_match);
1244 free(match_buf);
1245}
1246
1247#endif
1248
1249
1250line_input_t* FAST_FUNC new_line_input_t(int flags)
1251{
1252 line_input_t *n = xzalloc(sizeof(*n));
1253 n->flags = flags;
1254 n->max_history = MAX_HISTORY;
1255 return n;
1256}
1257
1258
1259#if MAX_HISTORY > 0
1260
1261unsigned size_from_HISTFILESIZE(const char *hp)
1262{
1263 int size = MAX_HISTORY;
1264 if (hp) {
1265 size = atoi(hp);
1266 if (size <= 0)
1267 return 1;
1268 if (size > MAX_HISTORY)
1269 return MAX_HISTORY;
1270 }
1271 return size;
1272}
1273
1274static void save_command_ps_at_cur_history(void)
1275{
1276 if (command_ps[0] != BB_NUL) {
1277 int cur = state->cur_history;
1278 free(state->history[cur]);
1279
1280# if ENABLE_UNICODE_SUPPORT
1281 {
1282 char tbuf[MAX_LINELEN];
1283 save_string(tbuf, sizeof(tbuf));
1284 state->history[cur] = xstrdup(tbuf);
1285 }
1286# else
1287 state->history[cur] = xstrdup(command_ps);
1288# endif
1289 }
1290}
1291
1292
1293static int get_previous_history(void)
1294{
1295 if ((state->flags & DO_HISTORY) && state->cur_history) {
1296 save_command_ps_at_cur_history();
1297 state->cur_history--;
1298 return 1;
1299 }
1300 beep();
1301 return 0;
1302}
1303
1304static int get_next_history(void)
1305{
1306 if (state->flags & DO_HISTORY) {
1307 if (state->cur_history < state->cnt_history) {
1308 save_command_ps_at_cur_history();
1309 return ++state->cur_history;
1310 }
1311 }
1312 beep();
1313 return 0;
1314}
1315
1316# if ENABLE_FEATURE_EDITING_SAVEHISTORY
1317
1318
1319
1320
1321
1322
1323
1324
1325static void free_line_input_t(line_input_t *n)
1326{
1327 int i = n->cnt_history;
1328 while (i > 0)
1329 free(n->history[--i]);
1330 free(n);
1331}
1332
1333
1334static void load_history(line_input_t *st_parm)
1335{
1336 char *temp_h[MAX_HISTORY];
1337 char *line;
1338 FILE *fp;
1339 unsigned idx, i, line_len;
1340
1341
1342
1343 fp = fopen_for_read(st_parm->hist_file);
1344 if (fp) {
1345
1346 for (idx = st_parm->cnt_history; idx > 0;) {
1347 idx--;
1348 free(st_parm->history[idx]);
1349 st_parm->history[idx] = NULL;
1350 }
1351
1352
1353 memset(temp_h, 0, sizeof(temp_h));
1354 idx = 0;
1355 st_parm->cnt_history_in_file = 0;
1356 while ((line = xmalloc_fgetline(fp)) != NULL) {
1357 if (line[0] == '\0') {
1358 free(line);
1359 continue;
1360 }
1361 free(temp_h[idx]);
1362 temp_h[idx] = line;
1363 st_parm->cnt_history_in_file++;
1364 idx++;
1365 if (idx == st_parm->max_history)
1366 idx = 0;
1367 }
1368 fclose(fp);
1369
1370
1371 if (st_parm->cnt_history_in_file) {
1372 while (temp_h[idx] == NULL) {
1373 idx++;
1374 if (idx == st_parm->max_history)
1375 idx = 0;
1376 }
1377 }
1378
1379
1380 for (i = 0; i < st_parm->max_history;) {
1381 line = temp_h[idx];
1382 if (!line)
1383 break;
1384 idx++;
1385 if (idx == st_parm->max_history)
1386 idx = 0;
1387 line_len = strlen(line);
1388 if (line_len >= MAX_LINELEN)
1389 line[MAX_LINELEN-1] = '\0';
1390 st_parm->history[i++] = line;
1391 }
1392 st_parm->cnt_history = i;
1393 if (ENABLE_FEATURE_EDITING_SAVE_ON_EXIT)
1394 st_parm->cnt_history_in_file = i;
1395 }
1396}
1397
1398# if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
1399void save_history(line_input_t *st)
1400{
1401 FILE *fp;
1402
1403 if (!st->hist_file)
1404 return;
1405 if (st->cnt_history <= st->cnt_history_in_file)
1406 return;
1407
1408 fp = fopen(st->hist_file, "a");
1409 if (fp) {
1410 int i, fd;
1411 char *new_name;
1412 line_input_t *st_temp;
1413
1414 for (i = st->cnt_history_in_file; i < st->cnt_history; i++)
1415 fprintf(fp, "%s\n", st->history[i]);
1416 fclose(fp);
1417
1418
1419
1420 st_temp = new_line_input_t(st->flags);
1421 st_temp->hist_file = st->hist_file;
1422 st_temp->max_history = st->max_history;
1423 load_history(st_temp);
1424
1425
1426 new_name = xasprintf("%s.%u.new", st->hist_file, (int) getpid());
1427 fd = open(new_name, O_WRONLY | O_CREAT | O_TRUNC, 0600);
1428 if (fd >= 0) {
1429 fp = xfdopen_for_write(fd);
1430 for (i = 0; i < st_temp->cnt_history; i++)
1431 fprintf(fp, "%s\n", st_temp->history[i]);
1432 fclose(fp);
1433 if (rename(new_name, st->hist_file) == 0)
1434 st->cnt_history_in_file = st_temp->cnt_history;
1435 }
1436 free(new_name);
1437 free_line_input_t(st_temp);
1438 }
1439}
1440# else
1441static void save_history(char *str)
1442{
1443 int fd;
1444 int len, len2;
1445
1446 if (!state->hist_file)
1447 return;
1448
1449 fd = open(state->hist_file, O_WRONLY | O_CREAT | O_APPEND, 0600);
1450 if (fd < 0)
1451 return;
1452 xlseek(fd, 0, SEEK_END);
1453 len = strlen(str);
1454 str[len] = '\n';
1455 len2 = full_write(fd, str, len + 1);
1456 str[len] = '\0';
1457 close(fd);
1458 if (len2 != len + 1)
1459 return;
1460
1461
1462 state->cnt_history_in_file++;
1463 if (state->cnt_history_in_file > state->max_history * 4) {
1464 char *new_name;
1465 line_input_t *st_temp;
1466
1467
1468
1469 st_temp = new_line_input_t(state->flags);
1470 st_temp->hist_file = state->hist_file;
1471 st_temp->max_history = state->max_history;
1472 load_history(st_temp);
1473
1474
1475 new_name = xasprintf("%s.%u.new", state->hist_file, (int) getpid());
1476 fd = open(new_name, O_WRONLY | O_CREAT | O_TRUNC, 0600);
1477 if (fd >= 0) {
1478 FILE *fp;
1479 int i;
1480
1481 fp = xfdopen_for_write(fd);
1482 for (i = 0; i < st_temp->cnt_history; i++)
1483 fprintf(fp, "%s\n", st_temp->history[i]);
1484 fclose(fp);
1485 if (rename(new_name, state->hist_file) == 0)
1486 state->cnt_history_in_file = st_temp->cnt_history;
1487 }
1488 free(new_name);
1489 free_line_input_t(st_temp);
1490 }
1491}
1492# endif
1493# else
1494# define load_history(a) ((void)0)
1495# define save_history(a) ((void)0)
1496# endif
1497
1498static void remember_in_history(char *str)
1499{
1500 int i;
1501
1502 if (!(state->flags & DO_HISTORY))
1503 return;
1504 if (str[0] == '\0')
1505 return;
1506 i = state->cnt_history;
1507
1508 if (i && strcmp(state->history[i-1], str) == 0)
1509 return;
1510
1511 free(state->history[state->max_history]);
1512 state->history[state->max_history] = NULL;
1513
1514
1515
1516 if (i >= state->max_history) {
1517 free(state->history[0]);
1518 for (i = 0; i < state->max_history-1; i++)
1519 state->history[i] = state->history[i+1];
1520
1521# if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
1522 if (state->cnt_history_in_file)
1523 state->cnt_history_in_file--;
1524# endif
1525 }
1526
1527 state->history[i++] = xstrdup(str);
1528
1529 state->cur_history = i;
1530 state->cnt_history = i;
1531# if ENABLE_FEATURE_EDITING_SAVEHISTORY && !ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
1532 save_history(str);
1533# endif
1534 IF_FEATURE_EDITING_FANCY_PROMPT(num_ok_lines++;)
1535}
1536
1537#else
1538# define remember_in_history(a) ((void)0)
1539#endif
1540
1541
1542#if ENABLE_FEATURE_EDITING_VI
1543
1544
1545
1546static void
1547vi_Word_motion(int eat)
1548{
1549 CHAR_T *command = command_ps;
1550
1551 while (cursor < command_len && !BB_isspace(command[cursor]))
1552 input_forward();
1553 if (eat) while (cursor < command_len && BB_isspace(command[cursor]))
1554 input_forward();
1555}
1556
1557static void
1558vi_word_motion(int eat)
1559{
1560 CHAR_T *command = command_ps;
1561
1562 if (BB_isalnum(command[cursor]) || command[cursor] == '_') {
1563 while (cursor < command_len
1564 && (BB_isalnum(command[cursor+1]) || command[cursor+1] == '_')
1565 ) {
1566 input_forward();
1567 }
1568 } else if (BB_ispunct(command[cursor])) {
1569 while (cursor < command_len && BB_ispunct(command[cursor+1]))
1570 input_forward();
1571 }
1572
1573 if (cursor < command_len)
1574 input_forward();
1575
1576 if (eat) {
1577 while (cursor < command_len && BB_isspace(command[cursor]))
1578 input_forward();
1579 }
1580}
1581
1582static void
1583vi_End_motion(void)
1584{
1585 CHAR_T *command = command_ps;
1586
1587 input_forward();
1588 while (cursor < command_len && BB_isspace(command[cursor]))
1589 input_forward();
1590 while (cursor < command_len-1 && !BB_isspace(command[cursor+1]))
1591 input_forward();
1592}
1593
1594static void
1595vi_end_motion(void)
1596{
1597 CHAR_T *command = command_ps;
1598
1599 if (cursor >= command_len-1)
1600 return;
1601 input_forward();
1602 while (cursor < command_len-1 && BB_isspace(command[cursor]))
1603 input_forward();
1604 if (cursor >= command_len-1)
1605 return;
1606 if (BB_isalnum(command[cursor]) || command[cursor] == '_') {
1607 while (cursor < command_len-1
1608 && (BB_isalnum(command[cursor+1]) || command[cursor+1] == '_')
1609 ) {
1610 input_forward();
1611 }
1612 } else if (BB_ispunct(command[cursor])) {
1613 while (cursor < command_len-1 && BB_ispunct(command[cursor+1]))
1614 input_forward();
1615 }
1616}
1617
1618static void
1619vi_Back_motion(void)
1620{
1621 CHAR_T *command = command_ps;
1622
1623 while (cursor > 0 && BB_isspace(command[cursor-1]))
1624 input_backward(1);
1625 while (cursor > 0 && !BB_isspace(command[cursor-1]))
1626 input_backward(1);
1627}
1628
1629static void
1630vi_back_motion(void)
1631{
1632 CHAR_T *command = command_ps;
1633
1634 if (cursor <= 0)
1635 return;
1636 input_backward(1);
1637 while (cursor > 0 && BB_isspace(command[cursor]))
1638 input_backward(1);
1639 if (cursor <= 0)
1640 return;
1641 if (BB_isalnum(command[cursor]) || command[cursor] == '_') {
1642 while (cursor > 0
1643 && (BB_isalnum(command[cursor-1]) || command[cursor-1] == '_')
1644 ) {
1645 input_backward(1);
1646 }
1647 } else if (BB_ispunct(command[cursor])) {
1648 while (cursor > 0 && BB_ispunct(command[cursor-1]))
1649 input_backward(1);
1650 }
1651}
1652#endif
1653
1654
1655static void ctrl_left(void)
1656{
1657 CHAR_T *command = command_ps;
1658
1659 while (1) {
1660 CHAR_T c;
1661
1662 input_backward(1);
1663 if (cursor == 0)
1664 break;
1665 c = command[cursor];
1666 if (c != ' ' && !BB_ispunct(c)) {
1667
1668
1669 while (1) {
1670 c = command[cursor - 1];
1671 if (c == ' ' || BB_ispunct(c))
1672 break;
1673 input_backward(1);
1674 if (cursor == 0)
1675 break;
1676 }
1677 break;
1678 }
1679 }
1680}
1681static void ctrl_right(void)
1682{
1683 CHAR_T *command = command_ps;
1684
1685 while (1) {
1686 CHAR_T c;
1687
1688 c = command[cursor];
1689 if (c == BB_NUL)
1690 break;
1691 if (c != ' ' && !BB_ispunct(c)) {
1692
1693
1694 while (1) {
1695 input_forward();
1696 c = command[cursor];
1697 if (c == BB_NUL || c == ' ' || BB_ispunct(c))
1698 break;
1699 }
1700 break;
1701 }
1702 input_forward();
1703 }
1704}
1705
1706
1707
1708
1709
1710
1711#if ENABLE_FEATURE_EDITING_ASK_TERMINAL
1712static void ask_terminal(void)
1713{
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742 struct pollfd pfd;
1743
1744 pfd.fd = STDIN_FILENO;
1745 pfd.events = POLLIN;
1746 if (safe_poll(&pfd, 1, 0) == 0) {
1747 S.sent_ESC_br6n = 1;
1748 fputs(ESC"[6n", stdout);
1749 fflush_all();
1750 }
1751}
1752#else
1753#define ask_terminal() ((void)0)
1754#endif
1755
1756#if !ENABLE_FEATURE_EDITING_FANCY_PROMPT
1757static void parse_and_put_prompt(const char *prmt_ptr)
1758{
1759 cmdedit_prompt = prmt_ptr;
1760 cmdedit_prmt_len = strlen(prmt_ptr);
1761 put_prompt();
1762}
1763#else
1764static void parse_and_put_prompt(const char *prmt_ptr)
1765{
1766 int prmt_len = 0;
1767 size_t cur_prmt_len = 0;
1768 char flg_not_length = '[';
1769 char *prmt_mem_ptr = xzalloc(1);
1770 char *cwd_buf = xrealloc_getcwd_or_warn(NULL);
1771 char cbuf[2];
1772 char c;
1773 char *pbuf;
1774
1775 cmdedit_prmt_len = 0;
1776
1777 if (!cwd_buf) {
1778 cwd_buf = (char *)bb_msg_unknown;
1779 }
1780
1781 cbuf[1] = '\0';
1782
1783 while (*prmt_ptr) {
1784 char *free_me = NULL;
1785
1786 pbuf = cbuf;
1787 c = *prmt_ptr++;
1788 if (c == '\\') {
1789 const char *cp = prmt_ptr;
1790 int l;
1791
1792 c = bb_process_escape_sequence(&prmt_ptr);
1793 if (prmt_ptr == cp) {
1794 if (*cp == '\0')
1795 break;
1796 c = *prmt_ptr++;
1797
1798 switch (c) {
1799# if ENABLE_USERNAME_OR_HOMEDIR
1800 case 'u':
1801 pbuf = user_buf ? user_buf : (char*)"";
1802 break;
1803# endif
1804 case 'h':
1805 pbuf = free_me = safe_gethostname();
1806 *strchrnul(pbuf, '.') = '\0';
1807 break;
1808 case '$':
1809 c = (geteuid() == 0 ? '#' : '$');
1810 break;
1811# if ENABLE_USERNAME_OR_HOMEDIR
1812 case 'w':
1813
1814 pbuf = cwd_buf;
1815 l = strlen(home_pwd_buf);
1816 if (l != 0
1817 && strncmp(home_pwd_buf, cwd_buf, l) == 0
1818 && (cwd_buf[l]=='/' || cwd_buf[l]=='\0')
1819 && strlen(cwd_buf + l) < PATH_MAX
1820 ) {
1821 pbuf = free_me = xasprintf("~%s", cwd_buf + l);
1822 }
1823 break;
1824# endif
1825 case 'W':
1826 pbuf = cwd_buf;
1827 cp = strrchr(pbuf, '/');
1828 if (cp != NULL && cp != pbuf)
1829 pbuf += (cp-pbuf) + 1;
1830 break;
1831 case '!':
1832 pbuf = free_me = xasprintf("%d", num_ok_lines);
1833 break;
1834 case 'e': case 'E':
1835 c = '\033';
1836 break;
1837 case 'x': case 'X': {
1838 char buf2[4];
1839 for (l = 0; l < 3;) {
1840 unsigned h;
1841 buf2[l++] = *prmt_ptr;
1842 buf2[l] = '\0';
1843 h = strtoul(buf2, &pbuf, 16);
1844 if (h > UCHAR_MAX || (pbuf - buf2) < l) {
1845 buf2[--l] = '\0';
1846 break;
1847 }
1848 prmt_ptr++;
1849 }
1850 c = (char)strtoul(buf2, NULL, 16);
1851 if (c == 0)
1852 c = '?';
1853 pbuf = cbuf;
1854 break;
1855 }
1856 case '[': case ']':
1857 if (c == flg_not_length) {
1858 flg_not_length = (flg_not_length == '[' ? ']' : '[');
1859 continue;
1860 }
1861 break;
1862 }
1863 }
1864 }
1865 cbuf[0] = c;
1866 cur_prmt_len = strlen(pbuf);
1867 prmt_len += cur_prmt_len;
1868 if (flg_not_length != ']')
1869 cmdedit_prmt_len += cur_prmt_len;
1870 prmt_mem_ptr = strcat(xrealloc(prmt_mem_ptr, prmt_len+1), pbuf);
1871 free(free_me);
1872 }
1873
1874 if (cwd_buf != (char *)bb_msg_unknown)
1875 free(cwd_buf);
1876 cmdedit_prompt = prmt_mem_ptr;
1877 put_prompt();
1878}
1879#endif
1880
1881static void cmdedit_setwidth(unsigned w, int redraw_flg)
1882{
1883 cmdedit_termw = w;
1884 if (redraw_flg) {
1885
1886 int new_y = (cursor + cmdedit_prmt_len) / w;
1887
1888 redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), command_len - cursor);
1889 fflush_all();
1890 }
1891}
1892
1893static void win_changed(int nsig)
1894{
1895 int sv_errno = errno;
1896 unsigned width;
1897
1898 get_terminal_width_height(0, &width, NULL);
1899
1900 cmdedit_setwidth(width, nsig);
1901
1902 errno = sv_errno;
1903}
1904
1905static int lineedit_read_key(char *read_key_buffer, int timeout)
1906{
1907 int64_t ic;
1908#if ENABLE_UNICODE_SUPPORT
1909 char unicode_buf[MB_CUR_MAX + 1];
1910 int unicode_idx = 0;
1911#endif
1912
1913 while (1) {
1914
1915
1916
1917
1918
1919
1920
1921 ic = read_key(STDIN_FILENO, read_key_buffer, timeout);
1922 if (errno) {
1923#if ENABLE_UNICODE_SUPPORT
1924 if (errno == EAGAIN && unicode_idx != 0)
1925 goto pushback;
1926#endif
1927 break;
1928 }
1929
1930#if ENABLE_FEATURE_EDITING_ASK_TERMINAL
1931 if ((int32_t)ic == KEYCODE_CURSOR_POS
1932 && S.sent_ESC_br6n
1933 ) {
1934 S.sent_ESC_br6n = 0;
1935 if (cursor == 0) {
1936 int col = ((ic >> 32) & 0x7fff) - 1;
1937 if (col > cmdedit_prmt_len) {
1938 cmdedit_x += (col - cmdedit_prmt_len);
1939 while (cmdedit_x >= cmdedit_termw) {
1940 cmdedit_x -= cmdedit_termw;
1941 cmdedit_y++;
1942 }
1943 }
1944 }
1945 continue;
1946 }
1947#endif
1948
1949#if ENABLE_UNICODE_SUPPORT
1950 if (unicode_status == UNICODE_ON) {
1951 wchar_t wc;
1952
1953 if ((int32_t)ic < 0)
1954 break;
1955
1956
1957 unicode_buf[unicode_idx++] = ic;
1958 unicode_buf[unicode_idx] = '\0';
1959 if (mbstowcs(&wc, unicode_buf, 1) != 1) {
1960
1961 if (unicode_idx < MB_CUR_MAX) {
1962 timeout = 50;
1963 continue;
1964 }
1965 pushback:
1966
1967 read_key_ungets(read_key_buffer, unicode_buf + 1, unicode_idx - 1);
1968# if !ENABLE_UNICODE_PRESERVE_BROKEN
1969 ic = CONFIG_SUBST_WCHAR;
1970# else
1971 ic = unicode_mark_raw_byte(unicode_buf[0]);
1972# endif
1973 } else {
1974
1975 ic = wc;
1976 }
1977 }
1978#endif
1979 break;
1980 }
1981
1982 return ic;
1983}
1984
1985#if ENABLE_UNICODE_BIDI_SUPPORT
1986static int isrtl_str(void)
1987{
1988 int idx = cursor;
1989
1990 while (idx < command_len && unicode_bidi_is_neutral_wchar(command_ps[idx]))
1991 idx++;
1992 return unicode_bidi_isrtl(command_ps[idx]);
1993}
1994#else
1995# define isrtl_str() 0
1996#endif
1997
1998
1999
2000#define vi_case(caselabel) IF_FEATURE_EDITING_VI(case caselabel)
2001
2002
2003#undef CTRL
2004#define CTRL(a) ((a) & ~0x40)
2005
2006enum {
2007 VI_CMDMODE_BIT = 0x40000000,
2008
2009};
2010
2011#if ENABLE_FEATURE_REVERSE_SEARCH
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022static int32_t reverse_i_search(void)
2023{
2024 char match_buf[128];
2025 char read_key_buffer[KEYCODE_BUFFER_SIZE];
2026 const char *matched_history_line;
2027 const char *saved_prompt;
2028 int32_t ic;
2029
2030 matched_history_line = NULL;
2031 read_key_buffer[0] = 0;
2032 match_buf[0] = '\0';
2033
2034
2035 saved_prompt = cmdedit_prompt;
2036 goto set_prompt;
2037
2038 while (1) {
2039 int h;
2040 unsigned match_buf_len = strlen(match_buf);
2041
2042 fflush_all();
2043
2044 ic = lineedit_read_key(read_key_buffer, -1);
2045
2046 switch (ic) {
2047 case CTRL('R'):
2048 break;
2049
2050 case '\b':
2051 case '\x7f':
2052
2053 if (unicode_status == UNICODE_ON) {
2054 while (match_buf_len != 0) {
2055 uint8_t c = match_buf[--match_buf_len];
2056 if ((c & 0xc0) != 0x80)
2057 break;
2058 }
2059 } else {
2060 if (match_buf_len != 0)
2061 match_buf_len--;
2062 }
2063 match_buf[match_buf_len] = '\0';
2064 break;
2065
2066 default:
2067 if (ic < ' '
2068 || (!ENABLE_UNICODE_SUPPORT && ic >= 256)
2069 || (ENABLE_UNICODE_SUPPORT && ic >= VI_CMDMODE_BIT)
2070 ) {
2071 goto ret;
2072 }
2073
2074
2075#if ENABLE_UNICODE_SUPPORT
2076 if (unicode_status == UNICODE_ON) {
2077 mbstate_t mbstate = { 0 };
2078 char buf[MB_CUR_MAX + 1];
2079 int len = wcrtomb(buf, ic, &mbstate);
2080 if (len > 0) {
2081 buf[len] = '\0';
2082 if (match_buf_len + len < sizeof(match_buf))
2083 strcpy(match_buf + match_buf_len, buf);
2084 }
2085 } else
2086#endif
2087 if (match_buf_len < sizeof(match_buf) - 1) {
2088 match_buf[match_buf_len] = ic;
2089 match_buf[match_buf_len + 1] = '\0';
2090 }
2091 break;
2092 }
2093
2094
2095 h = state->cur_history;
2096 if (ic == CTRL('R'))
2097 h--;
2098 while (h >= 0) {
2099 if (state->history[h]) {
2100 char *match = strstr(state->history[h], match_buf);
2101 if (match) {
2102 state->cur_history = h;
2103 matched_history_line = state->history[h];
2104 command_len = load_string(matched_history_line);
2105 cursor = match - matched_history_line;
2106
2107
2108 free((char*)cmdedit_prompt);
2109 set_prompt:
2110 cmdedit_prompt = xasprintf("(reverse-i-search)'%s': ", match_buf);
2111 cmdedit_prmt_len = strlen(cmdedit_prompt);
2112 goto do_redraw;
2113 }
2114 }
2115 h--;
2116 }
2117
2118
2119 match_buf[match_buf_len] = '\0';
2120 beep();
2121 continue;
2122
2123 do_redraw:
2124 redraw(cmdedit_y, command_len - cursor);
2125 }
2126
2127 ret:
2128 if (matched_history_line)
2129 command_len = load_string(matched_history_line);
2130
2131 free((char*)cmdedit_prompt);
2132 cmdedit_prompt = saved_prompt;
2133 cmdedit_prmt_len = strlen(cmdedit_prompt);
2134 redraw(cmdedit_y, command_len - cursor);
2135
2136 return ic;
2137}
2138#endif
2139
2140
2141
2142
2143
2144
2145
2146int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *command, int maxsize, int timeout)
2147{
2148 int len;
2149#if ENABLE_FEATURE_TAB_COMPLETION
2150 smallint lastWasTab = 0;
2151#endif
2152 smallint break_out = 0;
2153#if ENABLE_FEATURE_EDITING_VI
2154 smallint vi_cmdmode = 0;
2155#endif
2156 struct termios initial_settings;
2157 struct termios new_settings;
2158 char read_key_buffer[KEYCODE_BUFFER_SIZE];
2159
2160 INIT_S();
2161
2162 if (tcgetattr(STDIN_FILENO, &initial_settings) < 0
2163 || !(initial_settings.c_lflag & ECHO)
2164 ) {
2165
2166 parse_and_put_prompt(prompt);
2167
2168 if (fgets(command, maxsize, stdin) == NULL)
2169 len = -1;
2170 else
2171 len = strlen(command);
2172 DEINIT_S();
2173 return len;
2174 }
2175
2176 init_unicode();
2177
2178
2179 if (maxsize > MAX_LINELEN)
2180 maxsize = MAX_LINELEN;
2181 S.maxsize = maxsize;
2182
2183
2184 state = st ? st : (line_input_t*) &const_int_0;
2185#if MAX_HISTORY > 0
2186# if ENABLE_FEATURE_EDITING_SAVEHISTORY
2187 if (state->hist_file)
2188 if (state->cnt_history == 0)
2189 load_history(state);
2190# endif
2191 if (state->flags & DO_HISTORY)
2192 state->cur_history = state->cnt_history;
2193#endif
2194
2195
2196 cmdedit_y = 0;
2197 command_len = 0;
2198#if ENABLE_UNICODE_SUPPORT
2199 command_ps = xzalloc(maxsize * sizeof(command_ps[0]));
2200#else
2201 command_ps = command;
2202 command[0] = '\0';
2203#endif
2204#define command command_must_not_be_used
2205
2206 new_settings = initial_settings;
2207
2208
2209
2210 new_settings.c_lflag &= ~(ICANON | ECHO | ECHONL | ISIG);
2211
2212 new_settings.c_cc[VMIN] = 1;
2213
2214 new_settings.c_cc[VTIME] = 0;
2215
2216
2217
2218 tcsetattr_stdin_TCSANOW(&new_settings);
2219
2220#if ENABLE_USERNAME_OR_HOMEDIR
2221 {
2222 struct passwd *entry;
2223
2224 entry = getpwuid(geteuid());
2225 if (entry) {
2226 user_buf = xstrdup(entry->pw_name);
2227 home_pwd_buf = xstrdup(entry->pw_dir);
2228 }
2229 }
2230#endif
2231
2232#if 0
2233 for (i = 0; i <= state->max_history; i++)
2234 bb_error_msg("history[%d]:'%s'", i, state->history[i]);
2235 bb_error_msg("cur_history:%d cnt_history:%d", state->cur_history, state->cnt_history);
2236#endif
2237
2238
2239 parse_and_put_prompt(prompt);
2240 ask_terminal();
2241
2242
2243
2244 previous_SIGWINCH_handler = signal(SIGWINCH, win_changed);
2245 win_changed(0);
2246
2247 read_key_buffer[0] = 0;
2248 while (1) {
2249
2250
2251
2252
2253
2254
2255
2256
2257 int32_t ic, ic_raw;
2258
2259 fflush_all();
2260 ic = ic_raw = lineedit_read_key(read_key_buffer, timeout);
2261
2262#if ENABLE_FEATURE_REVERSE_SEARCH
2263 again:
2264#endif
2265#if ENABLE_FEATURE_EDITING_VI
2266 newdelflag = 1;
2267 if (vi_cmdmode) {
2268
2269
2270 ic |= VI_CMDMODE_BIT;
2271 }
2272#endif
2273
2274 switch (ic) {
2275 case '\n':
2276 case '\r':
2277 vi_case('\n'|VI_CMDMODE_BIT:)
2278 vi_case('\r'|VI_CMDMODE_BIT:)
2279
2280 goto_new_line();
2281 break_out = 1;
2282 break;
2283 case CTRL('A'):
2284 vi_case('0'|VI_CMDMODE_BIT:)
2285
2286 input_backward(cursor);
2287 break;
2288 case CTRL('B'):
2289 vi_case('h'|VI_CMDMODE_BIT:)
2290 vi_case('\b'|VI_CMDMODE_BIT:)
2291 vi_case('\x7f'|VI_CMDMODE_BIT:)
2292 input_backward(1);
2293 break;
2294 case CTRL('E'):
2295 vi_case('$'|VI_CMDMODE_BIT:)
2296
2297 put_till_end_and_adv_cursor();
2298 break;
2299 case CTRL('F'):
2300 vi_case('l'|VI_CMDMODE_BIT:)
2301 vi_case(' '|VI_CMDMODE_BIT:)
2302 input_forward();
2303 break;
2304 case '\b':
2305 case '\x7f':
2306 if (!isrtl_str())
2307 input_backspace();
2308 else
2309 input_delete(0);
2310 break;
2311 case KEYCODE_DELETE:
2312 if (!isrtl_str())
2313 input_delete(0);
2314 else
2315 input_backspace();
2316 break;
2317#if ENABLE_FEATURE_TAB_COMPLETION
2318 case '\t':
2319 input_tab(&lastWasTab);
2320 break;
2321#endif
2322 case CTRL('K'):
2323
2324 command_ps[cursor] = BB_NUL;
2325 command_len = cursor;
2326 printf(SEQ_CLEAR_TILL_END_OF_SCREEN);
2327 break;
2328 case CTRL('L'):
2329 vi_case(CTRL('L')|VI_CMDMODE_BIT:)
2330
2331 printf(ESC"[H");
2332 redraw(0, command_len - cursor);
2333 break;
2334#if MAX_HISTORY > 0
2335 case CTRL('N'):
2336 vi_case(CTRL('N')|VI_CMDMODE_BIT:)
2337 vi_case('j'|VI_CMDMODE_BIT:)
2338
2339 if (get_next_history())
2340 goto rewrite_line;
2341 break;
2342 case CTRL('P'):
2343 vi_case(CTRL('P')|VI_CMDMODE_BIT:)
2344 vi_case('k'|VI_CMDMODE_BIT:)
2345
2346 if (get_previous_history())
2347 goto rewrite_line;
2348 break;
2349#endif
2350 case CTRL('U'):
2351 vi_case(CTRL('U')|VI_CMDMODE_BIT:)
2352
2353 if (cursor) {
2354 command_len -= cursor;
2355 memmove(command_ps, command_ps + cursor,
2356 (command_len + 1) * sizeof(command_ps[0]));
2357 redraw(cmdedit_y, command_len);
2358 }
2359 break;
2360 case CTRL('W'):
2361 vi_case(CTRL('W')|VI_CMDMODE_BIT:)
2362
2363 while (cursor > 0 && BB_isspace(command_ps[cursor-1]))
2364 input_backspace();
2365 while (cursor > 0 && !BB_isspace(command_ps[cursor-1]))
2366 input_backspace();
2367 break;
2368#if ENABLE_FEATURE_REVERSE_SEARCH
2369 case CTRL('R'):
2370 ic = ic_raw = reverse_i_search();
2371 goto again;
2372#endif
2373
2374#if ENABLE_FEATURE_EDITING_VI
2375 case 'i'|VI_CMDMODE_BIT:
2376 vi_cmdmode = 0;
2377 break;
2378 case 'I'|VI_CMDMODE_BIT:
2379 input_backward(cursor);
2380 vi_cmdmode = 0;
2381 break;
2382 case 'a'|VI_CMDMODE_BIT:
2383 input_forward();
2384 vi_cmdmode = 0;
2385 break;
2386 case 'A'|VI_CMDMODE_BIT:
2387 put_till_end_and_adv_cursor();
2388 vi_cmdmode = 0;
2389 break;
2390 case 'x'|VI_CMDMODE_BIT:
2391 input_delete(1);
2392 break;
2393 case 'X'|VI_CMDMODE_BIT:
2394 if (cursor > 0) {
2395 input_backward(1);
2396 input_delete(1);
2397 }
2398 break;
2399 case 'W'|VI_CMDMODE_BIT:
2400 vi_Word_motion(1);
2401 break;
2402 case 'w'|VI_CMDMODE_BIT:
2403 vi_word_motion(1);
2404 break;
2405 case 'E'|VI_CMDMODE_BIT:
2406 vi_End_motion();
2407 break;
2408 case 'e'|VI_CMDMODE_BIT:
2409 vi_end_motion();
2410 break;
2411 case 'B'|VI_CMDMODE_BIT:
2412 vi_Back_motion();
2413 break;
2414 case 'b'|VI_CMDMODE_BIT:
2415 vi_back_motion();
2416 break;
2417 case 'C'|VI_CMDMODE_BIT:
2418 vi_cmdmode = 0;
2419
2420 case 'D'|VI_CMDMODE_BIT:
2421 goto clear_to_eol;
2422
2423 case 'c'|VI_CMDMODE_BIT:
2424 vi_cmdmode = 0;
2425
2426 case 'd'|VI_CMDMODE_BIT: {
2427 int nc, sc;
2428
2429 ic = lineedit_read_key(read_key_buffer, timeout);
2430 if (errno)
2431 goto return_error_indicator;
2432 if (ic == ic_raw) {
2433 input_backward(cursor);
2434 goto clear_to_eol;
2435 break;
2436 }
2437
2438 sc = cursor;
2439 switch (ic) {
2440 case 'w':
2441 case 'W':
2442 case 'e':
2443 case 'E':
2444 switch (ic) {
2445 case 'w':
2446 vi_word_motion(vi_cmdmode);
2447 break;
2448 case 'W':
2449 vi_Word_motion(vi_cmdmode);
2450 break;
2451 case 'e':
2452 vi_end_motion();
2453 input_forward();
2454 break;
2455 case 'E':
2456 vi_End_motion();
2457 input_forward();
2458 break;
2459 }
2460 nc = cursor;
2461 input_backward(cursor - sc);
2462 while (nc-- > cursor)
2463 input_delete(1);
2464 break;
2465 case 'b':
2466 case 'B':
2467 if (ic == 'b')
2468 vi_back_motion();
2469 else
2470 vi_Back_motion();
2471 while (sc-- > cursor)
2472 input_delete(1);
2473 break;
2474 case ' ':
2475 input_delete(1);
2476 break;
2477 case '$':
2478 clear_to_eol:
2479 while (cursor < command_len)
2480 input_delete(1);
2481 break;
2482 }
2483 break;
2484 }
2485 case 'p'|VI_CMDMODE_BIT:
2486 input_forward();
2487
2488 case 'P'|VI_CMDMODE_BIT:
2489 put();
2490 break;
2491 case 'r'|VI_CMDMODE_BIT:
2492
2493 ic = lineedit_read_key(read_key_buffer, timeout);
2494 if (errno)
2495 goto return_error_indicator;
2496 if (ic < ' ' || ic > 255) {
2497 beep();
2498 } else {
2499 command_ps[cursor] = ic;
2500 bb_putchar(ic);
2501 bb_putchar('\b');
2502 }
2503 break;
2504 case '\x1b':
2505 if (state->flags & VI_MODE) {
2506
2507 vi_cmdmode = 1;
2508 input_backward(1);
2509 }
2510
2511
2512
2513
2514 ic = lineedit_read_key(read_key_buffer, timeout);
2515 switch (ic) {
2516
2517 case 'b':
2518 ctrl_left();
2519 break;
2520
2521 case 'f':
2522 ctrl_right();
2523 break;
2524
2525 case 'd':
2526 {
2527
2528 int nc, sc = cursor;
2529 ctrl_right();
2530 nc = cursor - sc;
2531 input_backward(nc);
2532 while (--nc >= 0)
2533 input_delete(1);
2534 break;
2535 }
2536 case '\b':
2537 case '\x7f':
2538
2539 {
2540
2541 int sc = cursor;
2542 ctrl_left();
2543 while (sc-- > cursor)
2544 input_delete(1);
2545 break;
2546 }
2547 }
2548 break;
2549#endif
2550
2551#if MAX_HISTORY > 0
2552 case KEYCODE_UP:
2553 if (get_previous_history())
2554 goto rewrite_line;
2555 beep();
2556 break;
2557 case KEYCODE_DOWN:
2558 if (!get_next_history())
2559 break;
2560 rewrite_line:
2561
2562
2563 command_len = load_string(state->history[state->cur_history] ?
2564 state->history[state->cur_history] : "");
2565
2566 redraw(cmdedit_y, (state->flags & VI_MODE) ? 9999 : 0);
2567 break;
2568#endif
2569 case KEYCODE_RIGHT:
2570 input_forward();
2571 break;
2572 case KEYCODE_LEFT:
2573 input_backward(1);
2574 break;
2575 case KEYCODE_CTRL_LEFT:
2576 case KEYCODE_ALT_LEFT:
2577 ctrl_left();
2578 break;
2579 case KEYCODE_CTRL_RIGHT:
2580 case KEYCODE_ALT_RIGHT:
2581 ctrl_right();
2582 break;
2583 case KEYCODE_HOME:
2584 input_backward(cursor);
2585 break;
2586 case KEYCODE_END:
2587 put_till_end_and_adv_cursor();
2588 break;
2589
2590 default:
2591 if (initial_settings.c_cc[VINTR] != 0
2592 && ic_raw == initial_settings.c_cc[VINTR]
2593 ) {
2594
2595 goto_new_line();
2596 command_len = 0;
2597 break_out = -1;
2598 break;
2599 }
2600 if (initial_settings.c_cc[VEOF] != 0
2601 && ic_raw == initial_settings.c_cc[VEOF]
2602 ) {
2603
2604
2605 if (command_len == 0) {
2606 errno = 0;
2607
2608 case -1:
2609 IF_FEATURE_EDITING_VI(return_error_indicator:)
2610 break_out = command_len = -1;
2611 break;
2612 }
2613 input_delete(0);
2614 break;
2615 }
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625 if (ic < ' '
2626 || (!ENABLE_UNICODE_SUPPORT && ic >= 256)
2627 || (ENABLE_UNICODE_SUPPORT && ic >= VI_CMDMODE_BIT)
2628 ) {
2629
2630
2631
2632
2633
2634
2635 break;
2636 }
2637 if ((int)command_len >= (maxsize - 2)) {
2638
2639 break;
2640 }
2641
2642 command_len++;
2643 if (cursor == (command_len - 1)) {
2644
2645 command_ps[cursor] = ic;
2646 command_ps[cursor + 1] = BB_NUL;
2647 put_cur_glyph_and_inc_cursor();
2648 if (unicode_bidi_isrtl(ic))
2649 input_backward(1);
2650 } else {
2651
2652 int sc = cursor;
2653
2654 memmove(command_ps + sc + 1, command_ps + sc,
2655 (command_len - sc) * sizeof(command_ps[0]));
2656 command_ps[sc] = ic;
2657
2658 if (!isrtl_str())
2659 sc++;
2660 put_till_end_and_adv_cursor();
2661
2662 input_backward(cursor - sc);
2663 }
2664 break;
2665 }
2666
2667 if (break_out)
2668 break;
2669
2670#if ENABLE_FEATURE_TAB_COMPLETION
2671 if (ic_raw != '\t')
2672 lastWasTab = 0;
2673#endif
2674 }
2675
2676#if ENABLE_FEATURE_EDITING_ASK_TERMINAL
2677 if (S.sent_ESC_br6n) {
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687 usleep(20*1000);
2688 }
2689#endif
2690
2691
2692#undef command
2693
2694#if ENABLE_UNICODE_SUPPORT
2695 command[0] = '\0';
2696 if (command_len > 0)
2697 command_len = save_string(command, maxsize - 1);
2698 free(command_ps);
2699#endif
2700
2701 if (command_len > 0)
2702 remember_in_history(command);
2703
2704 if (break_out > 0) {
2705 command[command_len++] = '\n';
2706 command[command_len] = '\0';
2707 }
2708
2709#if ENABLE_FEATURE_TAB_COMPLETION
2710 free_tab_completion_data();
2711#endif
2712
2713
2714 tcsetattr_stdin_TCSANOW(&initial_settings);
2715
2716 signal(SIGWINCH, previous_SIGWINCH_handler);
2717 fflush_all();
2718
2719 len = command_len;
2720 DEINIT_S();
2721
2722 return len;
2723}
2724
2725#else
2726
2727#undef read_line_input
2728int FAST_FUNC read_line_input(const char* prompt, char* command, int maxsize)
2729{
2730 fputs(prompt, stdout);
2731 fflush_all();
2732 if (!fgets(command, maxsize, stdin))
2733 return -1;
2734 return strlen(command);
2735}
2736
2737#endif
2738
2739
2740
2741
2742
2743
2744#ifdef TEST
2745
2746#include <locale.h>
2747
2748const char *applet_name = "debug stuff usage";
2749
2750int main(int argc, char **argv)
2751{
2752 char buff[MAX_LINELEN];
2753 char *prompt =
2754#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
2755 "\\[\\033[32;1m\\]\\u@\\[\\x1b[33;1m\\]\\h:"
2756 "\\[\\033[34;1m\\]\\w\\[\\033[35;1m\\] "
2757 "\\!\\[\\e[36;1m\\]\\$ \\[\\E[0m\\]";
2758#else
2759 "% ";
2760#endif
2761
2762 while (1) {
2763 int l;
2764 l = read_line_input(prompt, buff);
2765 if (l <= 0 || buff[l-1] != '\n')
2766 break;
2767 buff[l-1] = '\0';
2768 printf("*** read_line_input() returned line =%s=\n", buff);
2769 }
2770 printf("*** read_line_input() detect ^D\n");
2771 return 0;
2772}
2773
2774#endif
2775