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