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