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