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