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