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