aboutsummaryrefslogtreecommitdiff
path: root/libbb/lineedit.c
diff options
context:
space:
mode:
Diffstat (limited to 'libbb/lineedit.c')
-rw-r--r--libbb/lineedit.c364
1 files changed, 299 insertions, 65 deletions
diff --git a/libbb/lineedit.c b/libbb/lineedit.c
index 151208c1c..c8a0f37fe 100644
--- a/libbb/lineedit.c
+++ b/libbb/lineedit.c
@@ -258,8 +258,14 @@ static NOINLINE const char *get_homedir_or_NULL(void)
258# else 258# else
259 home = getenv("HOME"); 259 home = getenv("HOME");
260# endif 260# endif
261 if (home != NULL && home[0] != '\0') 261 if (home != NULL && home[0] != '\0') {
262# if ENABLE_PLATFORM_MINGW32
263 char *t = auto_string(xstrdup(home));
264 bs_to_slash(t);
265 home = t;
266# endif
262 return home; 267 return home;
268 }
263 269
264 if (!got_user_strings) 270 if (!got_user_strings)
265 get_user_strings(); 271 get_user_strings();
@@ -413,7 +419,7 @@ int adjust_width_and_validate_wc(unsigned *width_adj, int wc);
413/* Put 'command_ps[cursor]', cursor++. 419/* Put 'command_ps[cursor]', cursor++.
414 * Advance cursor on screen. If we reached right margin, scroll text up 420 * Advance cursor on screen. If we reached right margin, scroll text up
415 * and remove terminal margin effect by printing 'next_char' */ 421 * and remove terminal margin effect by printing 'next_char' */
416#define HACK_FOR_WRONG_WIDTH 1 422#define HACK_FOR_WRONG_WIDTH 1 && !ENABLE_PLATFORM_MINGW32
417static void put_cur_glyph_and_inc_cursor(void) 423static void put_cur_glyph_and_inc_cursor(void)
418{ 424{
419 CHAR_T c = command_ps[cursor]; 425 CHAR_T c = command_ps[cursor];
@@ -451,7 +457,7 @@ static void put_cur_glyph_and_inc_cursor(void)
451 * have automargin (IOW: it is moving cursor to next line 457 * have automargin (IOW: it is moving cursor to next line
452 * by itself (which is wrong for VT-10x terminals)), 458 * by itself (which is wrong for VT-10x terminals)),
453 * this will break things: there will be one extra empty line */ 459 * this will break things: there will be one extra empty line */
454 puts("\r"); /* + implicit '\n' */ 460 fputs("\r\n", stderr);
455#else 461#else
456 /* VT-10x terminals don't wrap cursor to next line when last char 462 /* VT-10x terminals don't wrap cursor to next line when last char
457 * on the line is printed - cursor stays "over" this char. 463 * on the line is printed - cursor stays "over" this char.
@@ -476,6 +482,42 @@ static void put_cur_glyph_and_inc_cursor(void)
476 } 482 }
477} 483}
478 484
485#if ENABLE_PLATFORM_MINGW32
486static void inc_cursor(void)
487{
488 CHAR_T c = command_ps[cursor];
489 unsigned width = 0;
490 int ofs_to_right;
491
492 /* advance cursor */
493 cursor++;
494 if (unicode_status == UNICODE_ON) {
495 IF_UNICODE_WIDE_WCHARS(width = cmdedit_x;)
496 c = adjust_width_and_validate_wc(&cmdedit_x, c);
497 IF_UNICODE_WIDE_WCHARS(width = cmdedit_x - width;)
498 } else {
499 cmdedit_x++;
500 }
501
502 ofs_to_right = cmdedit_x - cmdedit_termw;
503 if (!ENABLE_UNICODE_WIDE_WCHARS || ofs_to_right <= 0) {
504 /* cursor remains on this line */
505 printf(ESC"[1C");
506 }
507
508 if (ofs_to_right >= 0) {
509 /* we go to the next line */
510 printf(ESC"[1B");
511 bb_putchar('\r');
512 cmdedit_y++;
513 if (!ENABLE_UNICODE_WIDE_WCHARS || ofs_to_right == 0) {
514 width = 0;
515 }
516 cmdedit_x = width;
517 }
518}
519#endif
520
479/* Move to end of line (by printing all chars till the end) */ 521/* Move to end of line (by printing all chars till the end) */
480static void put_till_end_and_adv_cursor(void) 522static void put_till_end_and_adv_cursor(void)
481{ 523{
@@ -538,6 +580,7 @@ static void input_backward(unsigned num)
538 580
539 if (cmdedit_x >= num) { 581 if (cmdedit_x >= num) {
540 cmdedit_x -= num; 582 cmdedit_x -= num;
583#if !ENABLE_PLATFORM_MINGW32
541 if (num <= 4) { 584 if (num <= 4) {
542 /* This is longer by 5 bytes on x86. 585 /* This is longer by 5 bytes on x86.
543 * Also gets miscompiled for ARM users 586 * Also gets miscompiled for ARM users
@@ -550,6 +593,7 @@ static void input_backward(unsigned num)
550 } while (--num); 593 } while (--num);
551 return; 594 return;
552 } 595 }
596#endif
553 fprintf(stderr, ESC"[%uD", num); 597 fprintf(stderr, ESC"[%uD", num);
554 return; 598 return;
555 } 599 }
@@ -688,7 +732,23 @@ static void input_backspace(void)
688static void input_forward(void) 732static void input_forward(void)
689{ 733{
690 if (cursor < command_len) 734 if (cursor < command_len)
735#if !ENABLE_PLATFORM_MINGW32
691 put_cur_glyph_and_inc_cursor(); 736 put_cur_glyph_and_inc_cursor();
737#else
738 /*
739 * inc_cursor improves forward cursor movement appearance on
740 * win 7/8 console, but it's broken with unicode wide-glyphs,
741 * e.g. paste and move forward over: echo 开开心心过每一天
742 * so disable inc_cursor when unicode is active (which is only
743 * windows 10+, where inc_cursor is not needed anyway).
744 */
745 {
746 if (unicode_status == UNICODE_ON)
747 put_cur_glyph_and_inc_cursor();
748 else
749 inc_cursor();
750 }
751#endif
692} 752}
693 753
694#if ENABLE_FEATURE_TAB_COMPLETION 754#if ENABLE_FEATURE_TAB_COMPLETION
@@ -709,25 +769,56 @@ static void free_tab_completion_data(void)
709 } 769 }
710} 770}
711 771
712static void add_match(char *matched) 772#if !ENABLE_PLATFORM_MINGW32
773# define add_match(m, s) add_match(m)
774#endif
775
776static void add_match(char *matched, int sensitive)
713{ 777{
778# if ENABLE_PLATFORM_MINGW32
779 size_t len;
780# endif
714 unsigned char *p = (unsigned char*)matched; 781 unsigned char *p = (unsigned char*)matched;
715 while (*p) { 782 while (*p) {
716 /* ESC attack fix: drop any string with control chars */ 783 /* ESC attack fix: drop any string with control chars */
717 if (*p < ' ' 784 if (*p < ' '
785# if !ENABLE_PLATFORM_MINGW32
718 || (!ENABLE_UNICODE_SUPPORT && *p >= 0x7f) 786 || (!ENABLE_UNICODE_SUPPORT && *p >= 0x7f)
719 || (ENABLE_UNICODE_SUPPORT && *p == 0x7f) 787 || (ENABLE_UNICODE_SUPPORT && *p == 0x7f)
788# else
789 /*
790 * on Windows, *p > 0x7f is never control:
791 * without unicode active: these are normal codepage chars.
792 * with unicode active: these are UTF8 continuation bytes.
793 */
794 || *p == 0x7f
795# endif
720 ) { 796 ) {
721 free(matched); 797 free(matched);
722 return; 798 return;
723 } 799 }
724 p++; 800 p++;
725 } 801 }
802# if ENABLE_PLATFORM_MINGW32
803 /* The case-sensitivity flag is stored after NUL terminator */
804 len = strlen(matched);
805 matched = xrealloc(matched, len + 2);
806 matched[len + 1] = sensitive;
807# endif
726 matches = xrealloc_vector(matches, 4, num_matches); 808 matches = xrealloc_vector(matches, 4, num_matches);
727 matches[num_matches] = matched; 809 matches[num_matches] = matched;
728 num_matches++; 810 num_matches++;
729} 811}
730 812
813# if ENABLE_PLATFORM_MINGW32
814static int is_case_sensitive(const char *p)
815{
816 while (*p++)
817 ;
818 return *p;
819}
820# endif
821
731# if ENABLE_FEATURE_USERNAME_COMPLETION 822# if ENABLE_FEATURE_USERNAME_COMPLETION
732/* Replace "~user/..." with "/homedir/...". 823/* Replace "~user/..." with "/homedir/...".
733 * The parameter is malloced, free it or return it 824 * The parameter is malloced, free it or return it
@@ -735,13 +826,16 @@ static void add_match(char *matched)
735 */ 826 */
736static char *username_path_completion(char *ud) 827static char *username_path_completion(char *ud)
737{ 828{
829# if !ENABLE_PLATFORM_MINGW32
738 struct passwd *entry; 830 struct passwd *entry;
831#endif
739 char *tilde_name = ud; 832 char *tilde_name = ud;
740 const char *home = NULL; 833 const char *home = NULL;
741 834
742 ud++; /* skip ~ */ 835 ud++; /* skip ~ */
743 if (*ud == '/') { /* "~/..." */ 836 if (*ud == '/') { /* "~/..." */
744 home = get_homedir_or_NULL(); 837 home = get_homedir_or_NULL();
838# if !ENABLE_PLATFORM_MINGW32
745 } else { 839 } else {
746 /* "~user/..." */ 840 /* "~user/..." */
747 ud = strchr(ud, '/'); 841 ud = strchr(ud, '/');
@@ -750,6 +844,7 @@ static char *username_path_completion(char *ud)
750 *ud = '/'; /* restore "~user/..." */ 844 *ud = '/'; /* restore "~user/..." */
751 if (entry) 845 if (entry)
752 home = entry->pw_dir; 846 home = entry->pw_dir;
847# endif
753 } 848 }
754 if (home) { 849 if (home) {
755 ud = concat_path_file(home, ud); 850 ud = concat_path_file(home, ud);
@@ -759,6 +854,7 @@ static char *username_path_completion(char *ud)
759 return tilde_name; 854 return tilde_name;
760} 855}
761 856
857# if !ENABLE_PLATFORM_MINGW32
762/* ~use<tab> - find all users with this prefix. 858/* ~use<tab> - find all users with this prefix.
763 * Return the length of the prefix used for matching. 859 * Return the length of the prefix used for matching.
764 */ 860 */
@@ -774,13 +870,14 @@ static NOINLINE unsigned complete_username(const char *ud)
774 while ((pw = getpwent()) != NULL) { 870 while ((pw = getpwent()) != NULL) {
775 /* Null usernames should result in all users as possible completions. */ 871 /* Null usernames should result in all users as possible completions. */
776 if (/* !ud[0] || */ is_prefixed_with(pw->pw_name, ud)) { 872 if (/* !ud[0] || */ is_prefixed_with(pw->pw_name, ud)) {
777 add_match(xasprintf("~%s/", pw->pw_name)); 873 add_match(xasprintf("~%s/", pw->pw_name), TRUE);
778 } 874 }
779 } 875 }
780 endpwent(); /* don't keep password file open */ 876 endpwent(); /* don't keep password file open */
781 877
782 return 1 + userlen; 878 return 1 + userlen;
783} 879}
880# endif
784# endif /* FEATURE_USERNAME_COMPLETION */ 881# endif /* FEATURE_USERNAME_COMPLETION */
785 882
786enum { 883enum {
@@ -810,7 +907,7 @@ static unsigned path_parse(char ***p)
810 tmp = (char*)pth; 907 tmp = (char*)pth;
811 npth = 1; /* path component count */ 908 npth = 1; /* path component count */
812 while (1) { 909 while (1) {
813 tmp = strchr(tmp, ':'); 910 tmp = strchr(tmp, PATH_SEP);
814 if (!tmp) 911 if (!tmp)
815 break; 912 break;
816 tmp++; 913 tmp++;
@@ -821,7 +918,7 @@ static unsigned path_parse(char ***p)
821 res[0] = tmp = xstrdup(pth); 918 res[0] = tmp = xstrdup(pth);
822 npth = 1; 919 npth = 1;
823 while (1) { 920 while (1) {
824 tmp = strchr(tmp, ':'); 921 tmp = strchr(tmp, PATH_SEP);
825 if (!tmp) 922 if (!tmp)
826 break; 923 break;
827 *tmp++ = '\0'; /* ':' -> '\0' */ 924 *tmp++ = '\0'; /* ':' -> '\0' */
@@ -849,6 +946,17 @@ static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type)
849 path1[0] = (char*)"."; 946 path1[0] = (char*)".";
850 947
851 basecmd = strrchr(command, '/'); 948 basecmd = strrchr(command, '/');
949#if ENABLE_PLATFORM_MINGW32
950 if (!basecmd && has_dos_drive_prefix(command) && command[2] != '\0') {
951 char buffer[PATH_MAX];
952
953 /* path is of form c:path with no '/' */
954 if (get_drive_cwd(command, buffer, PATH_MAX)) {
955 basecmd = command + 2;
956 path1[0] = dirbuf = xstrdup(buffer);
957 }
958 } else
959#endif
852 if (!basecmd) { 960 if (!basecmd) {
853 if (type == FIND_EXE_ONLY) 961 if (type == FIND_EXE_ONLY)
854 npaths = path_parse(&paths); 962 npaths = path_parse(&paths);
@@ -869,9 +977,13 @@ static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type)
869 if (type == FIND_EXE_ONLY && !dirbuf) { 977 if (type == FIND_EXE_ONLY && !dirbuf) {
870# if ENABLE_FEATURE_SH_STANDALONE && NUM_APPLETS != 1 978# if ENABLE_FEATURE_SH_STANDALONE && NUM_APPLETS != 1
871 const char *p = applet_names; 979 const char *p = applet_names;
980# if ENABLE_PLATFORM_MINGW32
981 const char *shpath = state->flags & WITH_PATH_LOOKUP ?
982 state->path_lookup : NULL;
983# endif
872 while (*p) { 984 while (*p) {
873 if (strncmp(basecmd, p, baselen) == 0) 985 if (strncmp(basecmd, p, baselen) == 0 && prefer_applet(p, shpath))
874 add_match(xstrdup(p)); 986 add_match(xstrdup(p), TRUE);
875 while (*p++ != '\0') 987 while (*p++ != '\0')
876 continue; 988 continue;
877 } 989 }
@@ -884,7 +996,7 @@ static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type)
884 if (!b) 996 if (!b)
885 break; 997 break;
886 if (strncmp(basecmd, b, baselen) == 0) 998 if (strncmp(basecmd, b, baselen) == 0)
887 add_match(xstrdup(b)); 999 add_match(xstrdup(b), TRUE);
888 } 1000 }
889 } 1001 }
890# endif 1002# endif
@@ -918,7 +1030,11 @@ static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type)
918 if (!basecmd[0] && DOT_OR_DOTDOT(name_found)) 1030 if (!basecmd[0] && DOT_OR_DOTDOT(name_found))
919 continue; 1031 continue;
920 /* match? */ 1032 /* match? */
1033# if ENABLE_PLATFORM_MINGW32
1034 if (strncasecmp(basecmd, name_found, baselen) != 0)
1035# else
921 if (strncmp(basecmd, name_found, baselen) != 0) 1036 if (strncmp(basecmd, name_found, baselen) != 0)
1037# endif
922 continue; /* no */ 1038 continue; /* no */
923 1039
924 found = concat_path_file(lpath, name_found); 1040 found = concat_path_file(lpath, name_found);
@@ -928,6 +1044,16 @@ static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type)
928 if (stat(found, &st) && lstat(found, &st)) 1044 if (stat(found, &st) && lstat(found, &st))
929 goto cont; /* hmm, remove in progress? */ 1045 goto cont; /* hmm, remove in progress? */
930 1046
1047# if ENABLE_PLATFORM_MINGW32
1048# if ENABLE_ASH_GLOB_OPTIONS
1049 if (state->sh_accept_glob && !state->sh_accept_glob(found))
1050 goto cont;
1051# endif
1052 if (type == FIND_EXE_ONLY && S_ISREG(st.st_mode) &&
1053 !(st.st_mode & S_IXUSR))
1054 goto cont;
1055# endif
1056
931 /* Save only name */ 1057 /* Save only name */
932 len = strlen(name_found); 1058 len = strlen(name_found);
933 found = xrealloc(found, len + 2); /* +2: for slash and NUL */ 1059 found = xrealloc(found, len + 2); /* +2: for slash and NUL */
@@ -946,7 +1072,7 @@ static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type)
946 goto cont; 1072 goto cont;
947 } 1073 }
948 /* add it to the list */ 1074 /* add it to the list */
949 add_match(found); 1075 add_match(found, FALSE);
950 continue; 1076 continue;
951 cont: 1077 cont:
952 free(found); 1078 free(found);
@@ -1011,7 +1137,13 @@ static NOINLINE int build_match_prefix(char *match_buf)
1011 1137
1012 /* Mark every \c as "quoted c" */ 1138 /* Mark every \c as "quoted c" */
1013 for (i = 0; int_buf[i]; i++) { 1139 for (i = 0; int_buf[i]; i++) {
1140#if ENABLE_PLATFORM_MINGW32
1141 /* Trailing backslash is effectively removed which confuses
1142 * the code to display case-preserved filenames. */
1143 if (int_buf[i] == '\\' && int_buf[i+1] != '\0') {
1144#else
1014 if (int_buf[i] == '\\') { 1145 if (int_buf[i] == '\\') {
1146#endif
1015 remove_chunk(int_buf, i, i + 1); 1147 remove_chunk(int_buf, i, i + 1);
1016 int_buf[i] |= QUOT; 1148 int_buf[i] |= QUOT;
1017 } 1149 }
@@ -1170,9 +1302,10 @@ static void showfiles(void)
1170 ); 1302 );
1171 } 1303 }
1172 if (ENABLE_UNICODE_SUPPORT) 1304 if (ENABLE_UNICODE_SUPPORT)
1173 puts(printable_string(matches[n])); 1305 fputs(printable_string(matches[n]), stderr);
1174 else 1306 else
1175 puts(matches[n]); 1307 fputs(matches[n], stderr);
1308 bb_putchar_stderr('\n');
1176 } 1309 }
1177} 1310}
1178 1311
@@ -1212,6 +1345,25 @@ static NOINLINE void input_tab(smallint *lastWasTab)
1212 size_t len_found; 1345 size_t len_found;
1213 /* Length of string used for matching */ 1346 /* Length of string used for matching */
1214 unsigned match_pfx_len = match_pfx_len; 1347 unsigned match_pfx_len = match_pfx_len;
1348# if ENABLE_PLATFORM_MINGW32
1349 int chosen_index = 0;
1350 int chosen_sens = FALSE;
1351# if !ENABLE_UNICODE_SUPPORT
1352 /*
1353 * FIXME: the next three vars are unused with ENABLE_UNICODE_SUPPORT
1354 * because the mingw code which uses them to update a tab-completion
1355 * prefix to the correct case (e.g. ~/desk<tab> to ~/Desktop/) is
1356 * not compiled, and so e.g. ~/desk<tab> completes to ~/desktop/ .
1357 */
1358 unsigned orig_pfx_len;
1359 char *target;
1360 const char *source;
1361# endif
1362# define first_match 0
1363# else
1364# define chosen_index 0
1365# define first_match 1
1366# endif
1215 int find_type; 1367 int find_type;
1216# if ENABLE_UNICODE_SUPPORT 1368# if ENABLE_UNICODE_SUPPORT
1217 /* cursor pos in command converted to multibyte form */ 1369 /* cursor pos in command converted to multibyte form */
@@ -1259,7 +1411,7 @@ static NOINLINE void input_tab(smallint *lastWasTab)
1259 /* Free up any memory already allocated */ 1411 /* Free up any memory already allocated */
1260 free_tab_completion_data(); 1412 free_tab_completion_data();
1261 1413
1262# if ENABLE_FEATURE_USERNAME_COMPLETION 1414# if ENABLE_FEATURE_USERNAME_COMPLETION && !ENABLE_PLATFORM_MINGW32
1263 /* If the word starts with ~ and there is no slash in the word, 1415 /* If the word starts with ~ and there is no slash in the word,
1264 * then try completing this word as a username. */ 1416 * then try completing this word as a username. */
1265 if (state->flags & USERNAME_COMPLETION) 1417 if (state->flags & USERNAME_COMPLETION)
@@ -1276,6 +1428,9 @@ static NOINLINE void input_tab(smallint *lastWasTab)
1276 { 1428 {
1277 const char *e = match_buf + strlen(match_buf); 1429 const char *e = match_buf + strlen(match_buf);
1278 const char *s = e - match_pfx_len; 1430 const char *s = e - match_pfx_len;
1431# if ENABLE_PLATFORM_MINGW32 && !ENABLE_UNICODE_SUPPORT
1432 orig_pfx_len = match_pfx_len;
1433# endif
1279 while (s < e) 1434 while (s < e)
1280 if (is_special_char(*s++)) 1435 if (is_special_char(*s++))
1281 match_pfx_len++; 1436 match_pfx_len++;
@@ -1306,10 +1461,29 @@ static NOINLINE void input_tab(smallint *lastWasTab)
1306 if (!matches) 1461 if (!matches)
1307 goto ret; /* no matches at all */ 1462 goto ret; /* no matches at all */
1308 /* Find common prefix */ 1463 /* Find common prefix */
1309 chosen_match = xstrdup(matches[0]); 1464# if ENABLE_PLATFORM_MINGW32
1465 /* Any comparison involving a filename must be case-insensitive.
1466 * The chosen match should be case-sensitive, if possible */
1467 for (unsigned i = 0; i < num_matches; ++i) {
1468 if (is_case_sensitive(matches[i])) {
1469 chosen_index = i;
1470 chosen_sens = TRUE;
1471 break;
1472 }
1473 }
1474# endif
1475 chosen_match = xstrdup(matches[chosen_index]);
1310 for (cp = chosen_match; *cp; cp++) { 1476 for (cp = chosen_match; *cp; cp++) {
1311 unsigned n; 1477 unsigned n;
1312 for (n = 1; n < num_matches; n++) { 1478 for (n = first_match; n < num_matches; n++) {
1479# if ENABLE_PLATFORM_MINGW32
1480 if (!is_case_sensitive(matches[n]) || !chosen_sens) {
1481 if (tolower(matches[n][cp - chosen_match]) !=
1482 tolower(*cp)) {
1483 goto stop;
1484 }
1485 } else
1486# endif
1313 if (matches[n][cp - chosen_match] != *cp) { 1487 if (matches[n][cp - chosen_match] != *cp) {
1314 goto stop; 1488 goto stop;
1315 } 1489 }
@@ -1346,7 +1520,21 @@ static NOINLINE void input_tab(smallint *lastWasTab)
1346 /* save tail */ 1520 /* save tail */
1347 strcpy(match_buf, &command_ps[cursor]); 1521 strcpy(match_buf, &command_ps[cursor]);
1348 /* add match and tail */ 1522 /* add match and tail */
1523# if ENABLE_PLATFORM_MINGW32
1524 if (match_pfx_len == orig_pfx_len) {
1525 /* replace match prefix to allow for altered case */
1526 target = &command_ps[cursor-match_pfx_len];
1527 source = chosen_match;
1528 }
1529 else {
1530 /* only replace tail of match if special characters are quoted */
1531 target = &command_ps[cursor];
1532 source = chosen_match + match_pfx_len;
1533 }
1534 strcpy(stpcpy(target, source), match_buf);
1535# else
1349 sprintf(&command_ps[cursor], "%s%s", chosen_match + match_pfx_len, match_buf); 1536 sprintf(&command_ps[cursor], "%s%s", chosen_match + match_pfx_len, match_buf);
1537# endif
1350 command_len = strlen(command_ps); 1538 command_len = strlen(command_ps);
1351 /* new pos */ 1539 /* new pos */
1352 pos = cursor + len_found - match_pfx_len; 1540 pos = cursor + len_found - match_pfx_len;
@@ -1382,7 +1570,6 @@ static NOINLINE void input_tab(smallint *lastWasTab)
1382 free(chosen_match); 1570 free(chosen_match);
1383 free(match_buf); 1571 free(match_buf);
1384} 1572}
1385
1386#endif /* FEATURE_TAB_COMPLETION */ 1573#endif /* FEATURE_TAB_COMPLETION */
1387 1574
1388 1575
@@ -1402,11 +1589,15 @@ line_input_t* FAST_FUNC new_line_input_t(int flags)
1402 1589
1403unsigned FAST_FUNC size_from_HISTFILESIZE(const char *hp) 1590unsigned FAST_FUNC size_from_HISTFILESIZE(const char *hp)
1404{ 1591{
1592# if ENABLE_PLATFORM_MINGW32 && DEFAULT_HISTORY > 0 && DEFAULT_HISTORY <= MAX_HISTORY
1593 int size = DEFAULT_HISTORY;
1594# else
1405 int size = MAX_HISTORY; 1595 int size = MAX_HISTORY;
1596# endif
1406 if (hp) { 1597 if (hp) {
1407 size = atoi(hp); 1598 size = atoi(hp);
1408 if (size <= 0) 1599 if (size < 0)
1409 return 1; 1600 return 0;
1410 if (size > MAX_HISTORY) 1601 if (size > MAX_HISTORY)
1411 return MAX_HISTORY; 1602 return MAX_HISTORY;
1412 } 1603 }
@@ -1500,18 +1691,21 @@ static void load_history(line_input_t *st_parm)
1500 /* NB: do not trash old history if file can't be opened */ 1691 /* NB: do not trash old history if file can't be opened */
1501 1692
1502 fp = fopen_for_read(st_parm->hist_file); 1693 fp = fopen_for_read(st_parm->hist_file);
1503 if (fp) { 1694 if (!fp)
1504 /* clean up old history */ 1695 return;
1505 for (idx = st_parm->cnt_history; idx > 0;) { 1696
1506 idx--; 1697 /* clean up old history */
1507 free(st_parm->history[idx]); 1698 for (idx = st_parm->cnt_history; idx > 0;) {
1508 st_parm->history[idx] = NULL; 1699 idx--;
1509 } 1700 free(st_parm->history[idx]);
1701 st_parm->history[idx] = NULL;
1702 }
1510 1703
1511 /* fill temp_h[], retaining only last MAX_HISTORY lines */ 1704 /* fill temp_h[], retaining only last max_history lines */
1512 memset(temp_h, 0, sizeof(temp_h)); 1705 memset(temp_h, 0, sizeof(temp_h));
1513 idx = 0; 1706 idx = 0;
1514 st_parm->cnt_history_in_file = 0; 1707 st_parm->cnt_history_in_file = 0;
1708 if (st_parm->max_history != 0) {
1515 while ((line = xmalloc_fgetline(fp)) != NULL) { 1709 while ((line = xmalloc_fgetline(fp)) != NULL) {
1516 if (line[0] == '\0') { 1710 if (line[0] == '\0') {
1517 free(line); 1711 free(line);
@@ -1524,34 +1718,34 @@ static void load_history(line_input_t *st_parm)
1524 if (idx == st_parm->max_history) 1718 if (idx == st_parm->max_history)
1525 idx = 0; 1719 idx = 0;
1526 } 1720 }
1527 fclose(fp); 1721 }
1528 1722 fclose(fp);
1529 /* find first non-NULL temp_h[], if any */
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 1723
1538 /* copy temp_h[] to st_parm->history[] */ 1724 /* find first non-NULL temp_h[], if any */
1539 for (i = 0; i < st_parm->max_history;) { 1725 if (st_parm->cnt_history_in_file != 0) {
1540 line = temp_h[idx]; 1726 while (temp_h[idx] == NULL) {
1541 if (!line)
1542 break;
1543 idx++; 1727 idx++;
1544 if (idx == st_parm->max_history) 1728 if (idx == st_parm->max_history)
1545 idx = 0; 1729 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 } 1730 }
1551 st_parm->cnt_history = i;
1552 if (ENABLE_FEATURE_EDITING_SAVE_ON_EXIT)
1553 st_parm->cnt_history_in_file = i;
1554 } 1731 }
1732
1733 /* copy temp_h[] to st_parm->history[] */
1734 for (i = 0; i < st_parm->max_history;) {
1735 line = temp_h[idx];
1736 if (!line)
1737 break;
1738 idx++;
1739 if (idx == st_parm->max_history)
1740 idx = 0;
1741 line_len = strlen(line);
1742 if (line_len >= MAX_LINELEN)
1743 line[MAX_LINELEN-1] = '\0';
1744 st_parm->history[i++] = line;
1745 }
1746 st_parm->cnt_history = i;
1747 if (ENABLE_FEATURE_EDITING_SAVE_ON_EXIT)
1748 st_parm->cnt_history_in_file = i;
1555} 1749}
1556 1750
1557# if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT 1751# if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
@@ -1559,17 +1753,27 @@ void FAST_FUNC save_history(line_input_t *st)
1559{ 1753{
1560 FILE *fp; 1754 FILE *fp;
1561 1755
1562 if (!st || !st->hist_file) 1756 /* bash compat: HISTFILE="" disables history saving */
1757 if (!st || !st->hist_file || !state->hist_file[0])
1563 return; 1758 return;
1564 if (st->cnt_history <= st->cnt_history_in_file) 1759 if (st->cnt_history <= st->cnt_history_in_file)
1565 return; 1760 return; /* no new entries were added */
1761 /* note: if st->max_history is 0, we do not abort: we truncate the history to 0 lines */
1566 1762
1567 fp = fopen(st->hist_file, "a"); 1763 fp = fopen(st->hist_file, (st->max_history == 0 ? "w" : "a"));
1568 if (fp) { 1764 if (fp) {
1569 int i, fd; 1765 int i, fd;
1570 char *new_name; 1766 char *new_name;
1571 line_input_t *st_temp; 1767 line_input_t *st_temp;
1572 1768
1769 /* max_history==0 needs special-casing in general code,
1770 * just handle it in a simpler way: */
1771 if (st->max_history == 0) {
1772 /* fopen("w") already truncated it */
1773 fclose(fp);
1774 return;
1775 }
1776
1573 for (i = st->cnt_history_in_file; i < st->cnt_history; i++) 1777 for (i = st->cnt_history_in_file; i < st->cnt_history; i++)
1574 fprintf(fp, "%s\n", st->history[i]); 1778 fprintf(fp, "%s\n", st->history[i]);
1575 fclose(fp); 1779 fclose(fp);
@@ -1579,6 +1783,8 @@ void FAST_FUNC save_history(line_input_t *st)
1579 st_temp = new_line_input_t(st->flags); 1783 st_temp = new_line_input_t(st->flags);
1580 st_temp->hist_file = st->hist_file; 1784 st_temp->hist_file = st->hist_file;
1581 st_temp->max_history = st->max_history; 1785 st_temp->max_history = st->max_history;
1786 /* load no more than max_history last lines */
1787 /* (in unlikely case that file disappeared, st_temp gets empty history) */
1582 load_history(st_temp); 1788 load_history(st_temp);
1583 1789
1584 /* write out temp file and replace hist_file atomically */ 1790 /* write out temp file and replace hist_file atomically */
@@ -1602,13 +1808,13 @@ static void save_history(char *str)
1602 int fd; 1808 int fd;
1603 int len, len2; 1809 int len, len2;
1604 1810
1605 if (!state->hist_file) 1811 /* bash compat: HISTFILE="" disables history saving */
1812 if (!state->hist_file || !state->hist_file[0])
1606 return; 1813 return;
1607 1814
1608 fd = open(state->hist_file, O_WRONLY | O_CREAT | O_APPEND, 0600); 1815 fd = open(state->hist_file, O_WRONLY | O_CREAT | O_APPEND, 0600);
1609 if (fd < 0) 1816 if (fd < 0)
1610 return; 1817 return;
1611 xlseek(fd, 0, SEEK_END); /* paranoia */
1612 len = strlen(str); 1818 len = strlen(str);
1613 str[len] = '\n'; /* we (try to) do atomic write */ 1819 str[len] = '\n'; /* we (try to) do atomic write */
1614 len2 = full_write(fd, str, len + 1); 1820 len2 = full_write(fd, str, len + 1);
@@ -1663,13 +1869,10 @@ static void remember_in_history(char *str)
1663 if (str[0] == '\0') 1869 if (str[0] == '\0')
1664 return; 1870 return;
1665 i = state->cnt_history; 1871 i = state->cnt_history;
1666 /* Don't save dupes */ 1872 /* Don't save dups */
1667 if (i && strcmp(state->history[i-1], str) == 0) 1873 if (i != 0 && strcmp(state->history[i-1], str) == 0)
1668 return; 1874 return;
1669 1875
1670 free(state->history[state->max_history]); /* redundant, paranoia */
1671 state->history[state->max_history] = NULL; /* redundant, paranoia */
1672
1673 /* If history[] is full, remove the oldest command */ 1876 /* If history[] is full, remove the oldest command */
1674 /* we need to keep history[state->max_history] empty, hence >=, not > */ 1877 /* we need to keep history[state->max_history] empty, hence >=, not > */
1675 if (i >= state->max_history) { 1878 if (i >= state->max_history) {
@@ -1682,7 +1885,7 @@ static void remember_in_history(char *str)
1682 state->cnt_history_in_file--; 1885 state->cnt_history_in_file--;
1683# endif 1886# endif
1684 } 1887 }
1685 /* i <= state->max_history-1 */ 1888 /* i < state->max_history */
1686 state->history[i++] = xstrdup(str); 1889 state->history[i++] = xstrdup(str);
1687 /* i <= state->max_history */ 1890 /* i <= state->max_history */
1688 state->cur_history = i; 1891 state->cur_history = i;
@@ -2053,7 +2256,11 @@ static void parse_and_put_prompt(const char *prmt_ptr)
2053 char *after_home_user; 2256 char *after_home_user;
2054 2257
2055 /* /home/user[/something] -> ~[/something] */ 2258 /* /home/user[/something] -> ~[/something] */
2259#if !ENABLE_PLATFORM_MINGW32
2056 after_home_user = is_prefixed_with(cwd_buf, home); 2260 after_home_user = is_prefixed_with(cwd_buf, home);
2261#else
2262 after_home_user = is_prefixed_with_case(cwd_buf, home);
2263#endif
2057 if (after_home_user 2264 if (after_home_user
2058 && (*after_home_user == '/' || *after_home_user == '\0') 2265 && (*after_home_user == '/' || *after_home_user == '\0')
2059 ) { 2266 ) {
@@ -2194,7 +2401,6 @@ static int lineedit_read_key(char *read_key_buffer, int timeout)
2194 errno = EINTR; 2401 errno = EINTR;
2195 return -1; 2402 return -1;
2196 } 2403 }
2197//FIXME: still races here with signals, but small window to poll() inside read_key
2198 IF_FEATURE_EDITING_WINCH(S.ok_to_redraw = 1;) 2404 IF_FEATURE_EDITING_WINCH(S.ok_to_redraw = 1;)
2199 /* errno = 0; - read_key does this itself */ 2405 /* errno = 0; - read_key does this itself */
2200 ic = read_key(STDIN_FILENO, read_key_buffer, timeout); 2406 ic = read_key(STDIN_FILENO, read_key_buffer, timeout);
@@ -2484,7 +2690,11 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
2484 n = get_termios_and_make_raw(STDIN_FILENO, &new_settings, &initial_settings, 0 2690 n = get_termios_and_make_raw(STDIN_FILENO, &new_settings, &initial_settings, 0
2485 | TERMIOS_CLEAR_ISIG /* turn off INTR (ctrl-C), QUIT, SUSP */ 2691 | TERMIOS_CLEAR_ISIG /* turn off INTR (ctrl-C), QUIT, SUSP */
2486 ); 2692 );
2693#if !ENABLE_PLATFORM_MINGW32
2487 if (n != 0 || (initial_settings.c_lflag & (ECHO|ICANON)) == ICANON) { 2694 if (n != 0 || (initial_settings.c_lflag & (ECHO|ICANON)) == ICANON) {
2695#else
2696 if (n != 0 || !isatty(0)) {
2697#endif
2488 /* Happens when e.g. stty -echo was run before. 2698 /* Happens when e.g. stty -echo was run before.
2489 * But if ICANON is not set, we don't come here. 2699 * But if ICANON is not set, we don't come here.
2490 * (example: interactive python ^Z-backgrounded, 2700 * (example: interactive python ^Z-backgrounded,
@@ -2494,8 +2704,12 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
2494 fflush_all(); 2704 fflush_all();
2495 if (fgets(command, maxsize, stdin) == NULL) 2705 if (fgets(command, maxsize, stdin) == NULL)
2496 len = -1; /* EOF or error */ 2706 len = -1; /* EOF or error */
2497 else 2707 else {
2498 len = strlen(command); 2708 len = strlen(command);
2709#if ENABLE_PLATFORM_MINGW32
2710 len = remove_cr(command, len);
2711#endif
2712 }
2499 DEINIT_S(); 2713 DEINIT_S();
2500 return len; 2714 return len;
2501 } 2715 }
@@ -2577,6 +2791,11 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
2577 } 2791 }
2578#endif 2792#endif
2579 ic = ic_raw = lineedit_read_key(read_key_buffer, timeout); 2793 ic = ic_raw = lineedit_read_key(read_key_buffer, timeout);
2794#if ENABLE_PLATFORM_MINGW32
2795 /* scroll to cursor position on any keypress */
2796 if (isatty(fileno(stdin)) && isatty(fileno(stdout)))
2797 move_cursor_row(0);
2798#endif
2580 2799
2581#if ENABLE_FEATURE_REVERSE_SEARCH 2800#if ENABLE_FEATURE_REVERSE_SEARCH
2582 again: 2801 again:
@@ -2638,6 +2857,17 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
2638 input_tab(&lastWasTab); 2857 input_tab(&lastWasTab);
2639 break; 2858 break;
2640#endif 2859#endif
2860#if ENABLE_PLATFORM_MINGW32
2861 case CTRL('Z'):
2862 command_ps[command_len] = '\0';
2863 #if ENABLE_UNICODE_SUPPORT
2864 bs_to_slash_u(command_ps);
2865 #else
2866 bs_to_slash(command_ps);
2867 #endif
2868 redraw(cmdedit_y, 0);
2869 break;
2870#endif
2641 case CTRL('K'): 2871 case CTRL('K'):
2642 /* Control-k -- clear to end of line */ 2872 /* Control-k -- clear to end of line */
2643 command_ps[cursor] = BB_NUL; 2873 command_ps[cursor] = BB_NUL;
@@ -2892,6 +3122,10 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
2892 && ic_raw == initial_settings.c_cc[VINTR] 3122 && ic_raw == initial_settings.c_cc[VINTR]
2893 ) { 3123 ) {
2894 /* Ctrl-C (usually) - stop gathering input */ 3124 /* Ctrl-C (usually) - stop gathering input */
3125#if ENABLE_PLATFORM_MINGW32
3126 if (state->flags & IGNORE_CTRL_C)
3127 break;
3128#endif
2895 command_len = 0; 3129 command_len = 0;
2896 break_out = -1; /* "do not append '\n'" */ 3130 break_out = -1; /* "do not append '\n'" */
2897 break; 3131 break;