diff options
Diffstat (limited to 'libbb/lineedit.c')
-rw-r--r-- | libbb/lineedit.c | 112 |
1 files changed, 62 insertions, 50 deletions
diff --git a/libbb/lineedit.c b/libbb/lineedit.c index 8e2b37853..c8a0f37fe 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c | |||
@@ -457,7 +457,7 @@ static void put_cur_glyph_and_inc_cursor(void) | |||
457 | * have automargin (IOW: it is moving cursor to next line | 457 | * have automargin (IOW: it is moving cursor to next line |
458 | * by itself (which is wrong for VT-10x terminals)), | 458 | * by itself (which is wrong for VT-10x terminals)), |
459 | * this will break things: there will be one extra empty line */ | 459 | * this will break things: there will be one extra empty line */ |
460 | puts("\r"); /* + implicit '\n' */ | 460 | fputs("\r\n", stderr); |
461 | #else | 461 | #else |
462 | /* 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 |
463 | * on the line is printed - cursor stays "over" this char. | 463 | * on the line is printed - cursor stays "over" this char. |
@@ -1302,9 +1302,10 @@ static void showfiles(void) | |||
1302 | ); | 1302 | ); |
1303 | } | 1303 | } |
1304 | if (ENABLE_UNICODE_SUPPORT) | 1304 | if (ENABLE_UNICODE_SUPPORT) |
1305 | puts(printable_string(matches[n])); | 1305 | fputs(printable_string(matches[n]), stderr); |
1306 | else | 1306 | else |
1307 | puts(matches[n]); | 1307 | fputs(matches[n], stderr); |
1308 | bb_putchar_stderr('\n'); | ||
1308 | } | 1309 | } |
1309 | } | 1310 | } |
1310 | 1311 | ||
@@ -1595,8 +1596,8 @@ unsigned FAST_FUNC size_from_HISTFILESIZE(const char *hp) | |||
1595 | # endif | 1596 | # endif |
1596 | if (hp) { | 1597 | if (hp) { |
1597 | size = atoi(hp); | 1598 | size = atoi(hp); |
1598 | if (size <= 0) | 1599 | if (size < 0) |
1599 | return 1; | 1600 | return 0; |
1600 | if (size > MAX_HISTORY) | 1601 | if (size > MAX_HISTORY) |
1601 | return MAX_HISTORY; | 1602 | return MAX_HISTORY; |
1602 | } | 1603 | } |
@@ -1690,18 +1691,21 @@ static void load_history(line_input_t *st_parm) | |||
1690 | /* 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 */ |
1691 | 1692 | ||
1692 | fp = fopen_for_read(st_parm->hist_file); | 1693 | fp = fopen_for_read(st_parm->hist_file); |
1693 | if (fp) { | 1694 | if (!fp) |
1694 | /* clean up old history */ | 1695 | return; |
1695 | for (idx = st_parm->cnt_history; idx > 0;) { | 1696 | |
1696 | idx--; | 1697 | /* clean up old history */ |
1697 | free(st_parm->history[idx]); | 1698 | for (idx = st_parm->cnt_history; idx > 0;) { |
1698 | st_parm->history[idx] = NULL; | 1699 | idx--; |
1699 | } | 1700 | free(st_parm->history[idx]); |
1701 | st_parm->history[idx] = NULL; | ||
1702 | } | ||
1700 | 1703 | ||
1701 | /* fill temp_h[], retaining only last MAX_HISTORY lines */ | 1704 | /* fill temp_h[], retaining only last max_history lines */ |
1702 | memset(temp_h, 0, sizeof(temp_h)); | 1705 | memset(temp_h, 0, sizeof(temp_h)); |
1703 | idx = 0; | 1706 | idx = 0; |
1704 | st_parm->cnt_history_in_file = 0; | 1707 | st_parm->cnt_history_in_file = 0; |
1708 | if (st_parm->max_history != 0) { | ||
1705 | while ((line = xmalloc_fgetline(fp)) != NULL) { | 1709 | while ((line = xmalloc_fgetline(fp)) != NULL) { |
1706 | if (line[0] == '\0') { | 1710 | if (line[0] == '\0') { |
1707 | free(line); | 1711 | free(line); |
@@ -1714,34 +1718,34 @@ static void load_history(line_input_t *st_parm) | |||
1714 | if (idx == st_parm->max_history) | 1718 | if (idx == st_parm->max_history) |
1715 | idx = 0; | 1719 | idx = 0; |
1716 | } | 1720 | } |
1717 | fclose(fp); | 1721 | } |
1718 | 1722 | fclose(fp); | |
1719 | /* find first non-NULL temp_h[], if any */ | ||
1720 | if (st_parm->cnt_history_in_file) { | ||
1721 | while (temp_h[idx] == NULL) { | ||
1722 | idx++; | ||
1723 | if (idx == st_parm->max_history) | ||
1724 | idx = 0; | ||
1725 | } | ||
1726 | } | ||
1727 | 1723 | ||
1728 | /* copy temp_h[] to st_parm->history[] */ | 1724 | /* find first non-NULL temp_h[], if any */ |
1729 | for (i = 0; i < st_parm->max_history;) { | 1725 | if (st_parm->cnt_history_in_file != 0) { |
1730 | line = temp_h[idx]; | 1726 | while (temp_h[idx] == NULL) { |
1731 | if (!line) | ||
1732 | break; | ||
1733 | idx++; | 1727 | idx++; |
1734 | if (idx == st_parm->max_history) | 1728 | if (idx == st_parm->max_history) |
1735 | idx = 0; | 1729 | idx = 0; |
1736 | line_len = strlen(line); | ||
1737 | if (line_len >= MAX_LINELEN) | ||
1738 | line[MAX_LINELEN-1] = '\0'; | ||
1739 | st_parm->history[i++] = line; | ||
1740 | } | 1730 | } |
1741 | st_parm->cnt_history = i; | ||
1742 | if (ENABLE_FEATURE_EDITING_SAVE_ON_EXIT) | ||
1743 | st_parm->cnt_history_in_file = i; | ||
1744 | } | 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; | ||
1745 | } | 1749 | } |
1746 | 1750 | ||
1747 | # if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT | 1751 | # if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT |
@@ -1749,17 +1753,27 @@ void FAST_FUNC save_history(line_input_t *st) | |||
1749 | { | 1753 | { |
1750 | FILE *fp; | 1754 | FILE *fp; |
1751 | 1755 | ||
1752 | if (!st || !st->hist_file) | 1756 | /* bash compat: HISTFILE="" disables history saving */ |
1757 | if (!st || !st->hist_file || !state->hist_file[0]) | ||
1753 | return; | 1758 | return; |
1754 | if (st->cnt_history <= st->cnt_history_in_file) | 1759 | if (st->cnt_history <= st->cnt_history_in_file) |
1755 | 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 */ | ||
1756 | 1762 | ||
1757 | fp = fopen(st->hist_file, "a"); | 1763 | fp = fopen(st->hist_file, (st->max_history == 0 ? "w" : "a")); |
1758 | if (fp) { | 1764 | if (fp) { |
1759 | int i, fd; | 1765 | int i, fd; |
1760 | char *new_name; | 1766 | char *new_name; |
1761 | line_input_t *st_temp; | 1767 | line_input_t *st_temp; |
1762 | 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 | |||
1763 | 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++) |
1764 | fprintf(fp, "%s\n", st->history[i]); | 1778 | fprintf(fp, "%s\n", st->history[i]); |
1765 | fclose(fp); | 1779 | fclose(fp); |
@@ -1769,6 +1783,8 @@ void FAST_FUNC save_history(line_input_t *st) | |||
1769 | st_temp = new_line_input_t(st->flags); | 1783 | st_temp = new_line_input_t(st->flags); |
1770 | st_temp->hist_file = st->hist_file; | 1784 | st_temp->hist_file = st->hist_file; |
1771 | 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) */ | ||
1772 | load_history(st_temp); | 1788 | load_history(st_temp); |
1773 | 1789 | ||
1774 | /* write out temp file and replace hist_file atomically */ | 1790 | /* write out temp file and replace hist_file atomically */ |
@@ -1792,13 +1808,13 @@ static void save_history(char *str) | |||
1792 | int fd; | 1808 | int fd; |
1793 | int len, len2; | 1809 | int len, len2; |
1794 | 1810 | ||
1795 | if (!state->hist_file) | 1811 | /* bash compat: HISTFILE="" disables history saving */ |
1812 | if (!state->hist_file || !state->hist_file[0]) | ||
1796 | return; | 1813 | return; |
1797 | 1814 | ||
1798 | 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); |
1799 | if (fd < 0) | 1816 | if (fd < 0) |
1800 | return; | 1817 | return; |
1801 | xlseek(fd, 0, SEEK_END); /* paranoia */ | ||
1802 | len = strlen(str); | 1818 | len = strlen(str); |
1803 | str[len] = '\n'; /* we (try to) do atomic write */ | 1819 | str[len] = '\n'; /* we (try to) do atomic write */ |
1804 | len2 = full_write(fd, str, len + 1); | 1820 | len2 = full_write(fd, str, len + 1); |
@@ -1853,13 +1869,10 @@ static void remember_in_history(char *str) | |||
1853 | if (str[0] == '\0') | 1869 | if (str[0] == '\0') |
1854 | return; | 1870 | return; |
1855 | i = state->cnt_history; | 1871 | i = state->cnt_history; |
1856 | /* Don't save dupes */ | 1872 | /* Don't save dups */ |
1857 | if (i && strcmp(state->history[i-1], str) == 0) | 1873 | if (i != 0 && strcmp(state->history[i-1], str) == 0) |
1858 | return; | 1874 | return; |
1859 | 1875 | ||
1860 | free(state->history[state->max_history]); /* redundant, paranoia */ | ||
1861 | state->history[state->max_history] = NULL; /* redundant, paranoia */ | ||
1862 | |||
1863 | /* If history[] is full, remove the oldest command */ | 1876 | /* If history[] is full, remove the oldest command */ |
1864 | /* we need to keep history[state->max_history] empty, hence >=, not > */ | 1877 | /* we need to keep history[state->max_history] empty, hence >=, not > */ |
1865 | if (i >= state->max_history) { | 1878 | if (i >= state->max_history) { |
@@ -1872,7 +1885,7 @@ static void remember_in_history(char *str) | |||
1872 | state->cnt_history_in_file--; | 1885 | state->cnt_history_in_file--; |
1873 | # endif | 1886 | # endif |
1874 | } | 1887 | } |
1875 | /* i <= state->max_history-1 */ | 1888 | /* i < state->max_history */ |
1876 | state->history[i++] = xstrdup(str); | 1889 | state->history[i++] = xstrdup(str); |
1877 | /* i <= state->max_history */ | 1890 | /* i <= state->max_history */ |
1878 | state->cur_history = i; | 1891 | state->cur_history = i; |
@@ -2388,7 +2401,6 @@ static int lineedit_read_key(char *read_key_buffer, int timeout) | |||
2388 | errno = EINTR; | 2401 | errno = EINTR; |
2389 | return -1; | 2402 | return -1; |
2390 | } | 2403 | } |
2391 | //FIXME: still races here with signals, but small window to poll() inside read_key | ||
2392 | IF_FEATURE_EDITING_WINCH(S.ok_to_redraw = 1;) | 2404 | IF_FEATURE_EDITING_WINCH(S.ok_to_redraw = 1;) |
2393 | /* errno = 0; - read_key does this itself */ | 2405 | /* errno = 0; - read_key does this itself */ |
2394 | ic = read_key(STDIN_FILENO, read_key_buffer, timeout); | 2406 | ic = read_key(STDIN_FILENO, read_key_buffer, timeout); |