aboutsummaryrefslogtreecommitdiff
path: root/libbb
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2025-08-10 07:46:58 +0100
committerRon Yorston <rmy@pobox.com>2025-08-10 07:46:58 +0100
commitc70a65f36c017f6feb7e86415bec1a644e71dbab (patch)
treee1c3afca24fd35bd2ff142cc0ea83151fdfa5996 /libbb
parentdcb4966c5492aeb67494d9ffdc58ff3054add443 (diff)
parent84766710f420dd444e2a03d33a1915ce55661e67 (diff)
downloadbusybox-w32-merge.tar.gz
busybox-w32-merge.tar.bz2
busybox-w32-merge.zip
Merge branch 'busybox' into mergemerge
Diffstat (limited to '')
-rw-r--r--libbb/Config.src4
-rw-r--r--libbb/const_hack.c29
-rw-r--r--libbb/dump.c10
-rw-r--r--libbb/lineedit.c112
-rw-r--r--libbb/poll_with_signals.c48
-rw-r--r--libbb/read_key.c25
6 files changed, 154 insertions, 74 deletions
diff --git a/libbb/Config.src b/libbb/Config.src
index ed6521c33..eff327c2a 100644
--- a/libbb/Config.src
+++ b/libbb/Config.src
@@ -192,8 +192,8 @@ config FEATURE_EDITING_VI
192config FEATURE_EDITING_HISTORY 192config FEATURE_EDITING_HISTORY
193 int "History size" 193 int "History size"
194 # Don't allow way too big values here, code uses fixed "char *history[N]" struct member 194 # Don't allow way too big values here, code uses fixed "char *history[N]" struct member
195 range 0 9999 195 range 0 2000
196 default 255 196 default 200
197 depends on FEATURE_EDITING 197 depends on FEATURE_EDITING
198 help 198 help
199 Specify command history size (0 - disable). 199 Specify command history size (0 - disable).
diff --git a/libbb/const_hack.c b/libbb/const_hack.c
index 75163fede..1d175481b 100644
--- a/libbb/const_hack.c
+++ b/libbb/const_hack.c
@@ -9,18 +9,27 @@
9#include "libbb.h" 9#include "libbb.h"
10 10
11#if defined(__clang_major__) && __clang_major__ >= 9 11#if defined(__clang_major__) && __clang_major__ >= 9
12void FAST_FUNC XZALLOC_CONST_PTR(const void *pptr, size_t size) 12/* Clang/llvm drops assignment to "constant" storage. Silently.
13 * Needs serious convincing to not eliminate the store.
14 */
15static ALWAYS_INLINE void* not_const_pp(const void *p)
13{ 16{
14 ASSIGN_CONST_PTR(pptr, xzalloc(size)); 17 void *pp;
18 asm volatile (
19 "# forget that p points to const"
20 : /*outputs*/ "=r" (pp)
21 : /*inputs*/ "0" (p)
22 );
23 return pp;
15} 24}
16 25void FAST_FUNC ASSIGN_CONST_PTR(const void *pptr, void *v)
17# if ENABLE_PLATFORM_MINGW32 26{
18void FAST_FUNC ASSIGN_CONST_PTR(const void *pptr, const void *v) 27 *(void**)not_const_pp(pptr) = v;
28 barrier();
29}
30void FAST_FUNC XZALLOC_CONST_PTR(const void *pptr, size_t size)
19{ 31{
20 do { 32 *(void**)not_const_pp(pptr) = xzalloc(size);
21 *(void**)not_const_pp(pptr) = (void*)(v); 33 barrier();
22 barrier();
23 } while (0);
24} 34}
25# endif
26#endif 35#endif
diff --git a/libbb/dump.c b/libbb/dump.c
index aa57eca8c..b2abe85af 100644
--- a/libbb/dump.c
+++ b/libbb/dump.c
@@ -703,15 +703,21 @@ static NOINLINE void display(priv_dumper_t* dumper)
703 conv_u(pr, bp); 703 conv_u(pr, bp);
704 break; 704 break;
705 case F_UINT: { 705 case F_UINT: {
706 union {
707 uint16_t uval16;
708 uint32_t uval32;
709 } u;
706 unsigned value = (unsigned char)*bp; 710 unsigned value = (unsigned char)*bp;
707 switch (pr->bcnt) { 711 switch (pr->bcnt) {
708 case 1: 712 case 1:
709 break; 713 break;
710 case 2: 714 case 2:
711 move_from_unaligned16(value, bp); 715 move_from_unaligned16(u.uval16, bp);
716 value = u.uval16;
712 break; 717 break;
713 case 4: 718 case 4:
714 move_from_unaligned32(value, bp); 719 move_from_unaligned32(u.uval32, bp);
720 value = u.uval32;
715 break; 721 break;
716 /* case 8: no users yet */ 722 /* case 8: no users yet */
717 } 723 }
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);
diff --git a/libbb/poll_with_signals.c b/libbb/poll_with_signals.c
new file mode 100644
index 000000000..d3c005418
--- /dev/null
+++ b/libbb/poll_with_signals.c
@@ -0,0 +1,48 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 2025 Denys Vlasenko <vda.linux@googlemail.com>
6 *
7 * Licensed under GPLv2, see file LICENSE in this source tree.
8 */
9//kbuild:lib-$(CONFIG_PLATFORM_POSIX) += poll_with_signals.o
10
11#include "libbb.h"
12
13/* Shells, for example, need their line input and "read" builtin
14 * to be interruptible, and the naive handling of it a-la:
15 * if (bb_got_signal) {
16 * errno = EINTR;
17 * return -1;
18 * }
19 * poll(pfd, 1, -1); // signal here would set EINTR
20 * is racy.
21 * This is a bit heavy-handed, but safe wrt races:
22 */
23int FAST_FUNC check_got_signal_and_poll(struct pollfd pfd[1], int timeout)
24{
25 int n;
26 struct timespec tv;
27 sigset_t orig_mask;
28
29 if (bb_got_signal) /* optimization */
30 goto eintr;
31
32 if (timeout >= 0) {
33 tv.tv_sec = timeout / 1000;
34 tv.tv_nsec = (timeout % 1000) * 1000000;
35 }
36 /* test bb_got_signal, then poll(), atomically wrt signals */
37 sigfillset(&orig_mask);
38 sigprocmask2(SIG_BLOCK, &orig_mask);
39 if (bb_got_signal) {
40 sigprocmask2(SIG_SETMASK, &orig_mask);
41 eintr:
42 errno = EINTR; /* inform the caller that we got a signal */
43 return -1;
44 }
45 n = ppoll(pfd, 1, timeout >= 0 ? &tv : NULL, &orig_mask);
46 sigprocmask2(SIG_SETMASK, &orig_mask);
47 return n;
48}
diff --git a/libbb/read_key.c b/libbb/read_key.c
index 54886cc9c..2414105ee 100644
--- a/libbb/read_key.c
+++ b/libbb/read_key.c
@@ -11,7 +11,7 @@
11 11
12int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout) 12int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout)
13{ 13{
14 struct pollfd pfd; 14 struct pollfd pfd[1];
15 const char *seq; 15 const char *seq;
16 int n; 16 int n;
17 17
@@ -117,8 +117,8 @@ int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout)
117 return windows_read_key(fd, buffer, timeout); 117 return windows_read_key(fd, buffer, timeout);
118#endif 118#endif
119 119
120 pfd.fd = fd; 120 pfd->fd = fd;
121 pfd.events = POLLIN; 121 pfd->events = POLLIN;
122 122
123 buffer++; /* saved chars counter is in buffer[-1] now */ 123 buffer++; /* saved chars counter is in buffer[-1] now */
124 124
@@ -126,12 +126,16 @@ int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout)
126 errno = 0; 126 errno = 0;
127 n = (unsigned char)buffer[-1]; 127 n = (unsigned char)buffer[-1];
128 if (n == 0) { 128 if (n == 0) {
129 /* If no data, wait for input. 129 /* No data. Wait for input. */
130 * If requested, wait TIMEOUT ms. TIMEOUT = -1 is useful 130
131 * if fd can be in non-blocking mode. 131 /* timeout == -2 means "do not poll". Else: */
132 */
133 if (timeout >= -1) { 132 if (timeout >= -1) {
134 n = poll(&pfd, 1, timeout); 133 /* We must poll even if timeout == -1:
134 * we want to be interrupted if signal arrives,
135 * regardless of SA_RESTART-ness of that signal!
136 */
137 /* test bb_got_signal, then poll(), atomically wrt signals */
138 n = check_got_signal_and_poll(pfd, timeout);
135 if (n < 0 && errno == EINTR) 139 if (n < 0 && errno == EINTR)
136 return n; 140 return n;
137 if (n == 0) { 141 if (n == 0) {
@@ -140,6 +144,7 @@ int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout)
140 return -1; 144 return -1;
141 } 145 }
142 } 146 }
147
143 /* It is tempting to read more than one byte here, 148 /* It is tempting to read more than one byte here,
144 * but it breaks pasting. Example: at shell prompt, 149 * but it breaks pasting. Example: at shell prompt,
145 * user presses "c","a","t" and then pastes "\nline\n". 150 * user presses "c","a","t" and then pastes "\nline\n".
@@ -178,7 +183,7 @@ int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout)
178 * so if we block for long it's not really an escape sequence. 183 * so if we block for long it's not really an escape sequence.
179 * Timeout is needed to reconnect escape sequences 184 * Timeout is needed to reconnect escape sequences
180 * split up by transmission over a serial console. */ 185 * split up by transmission over a serial console. */
181 if (safe_poll(&pfd, 1, 50) == 0) { 186 if (safe_poll(pfd, 1, 50) == 0) {
182 /* No more data! 187 /* No more data!
183 * Array is sorted from shortest to longest, 188 * Array is sorted from shortest to longest,
184 * we can't match anything later in array - 189 * we can't match anything later in array -
@@ -227,7 +232,7 @@ int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout)
227 * n = bytes read. Try to read more until we time out. 232 * n = bytes read. Try to read more until we time out.
228 */ 233 */
229 while (n < KEYCODE_BUFFER_SIZE-1) { /* 1 for count byte at buffer[-1] */ 234 while (n < KEYCODE_BUFFER_SIZE-1) { /* 1 for count byte at buffer[-1] */
230 if (safe_poll(&pfd, 1, 50) == 0) { 235 if (safe_poll(pfd, 1, 50) == 0) {
231 /* No more data! */ 236 /* No more data! */
232 break; 237 break;
233 } 238 }