aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2021-09-10 14:47:27 +0100
committerRon Yorston <rmy@pobox.com>2021-09-10 14:47:27 +0100
commit3eb1d088e09db204f456ad7b76eced21e429f001 (patch)
treea81b5bb422db5ee97475a8f1a4b5236442ed6f14
parent12bc40da28f85cbe97673671f315f847f1dbbabf (diff)
parent40f2dd7dd2e50c9d81dda4d72bf9c85c4c479a89 (diff)
downloadbusybox-w32-3eb1d088e09db204f456ad7b76eced21e429f001.tar.gz
busybox-w32-3eb1d088e09db204f456ad7b76eced21e429f001.tar.bz2
busybox-w32-3eb1d088e09db204f456ad7b76eced21e429f001.zip
Merge branch 'busybox' into merge
-rw-r--r--coreutils/basename.c62
-rw-r--r--coreutils/shuf.c69
-rw-r--r--editors/awk.c52
-rwxr-xr-xexamples/var_service/fw/run20
-rw-r--r--include/usage.src.h3
-rw-r--r--libbb/xconnect.c22
-rw-r--r--miscutils/chat.c64
-rw-r--r--networking/ftpgetput.c3
-rw-r--r--networking/httpd.c13
-rw-r--r--networking/nc.c2
-rw-r--r--networking/nc_bloaty.c7
-rw-r--r--networking/telnetd.c10
-rw-r--r--networking/udhcp/common.h31
-rw-r--r--networking/udhcp/dhcpd.c35
-rw-r--r--networking/udhcp/dhcprelay.c63
-rw-r--r--shell/ash.c444
-rw-r--r--shell/ash_test/ash-alias/alias_brace.right1
-rwxr-xr-xshell/ash_test/ash-alias/alias_brace.tests16
-rw-r--r--shell/ash_test/ash-alias/alias_case.right1
-rwxr-xr-xshell/ash_test/ash-alias/alias_case.tests8
-rw-r--r--shell/ash_test/ash-misc/control_char3.right2
-rw-r--r--shell/ash_test/ash-misc/control_char4.right2
-rw-r--r--shell/ash_test/ash-misc/exitcode_trap7.right2
-rwxr-xr-xshell/ash_test/ash-misc/exitcode_trap7.tests7
-rw-r--r--shell/ash_test/ash-misc/set-n1.right3
-rwxr-xr-xshell/ash_test/ash-misc/set-n1.tests2
-rw-r--r--shell/ash_test/ash-misc/shift1.right2
-rw-r--r--shell/ash_test/ash-misc/tickquote1.right2
-rw-r--r--shell/ash_test/ash-parsing/groups_and_keywords2.right2
-rw-r--r--shell/ash_test/ash-psubst/emptytick.right4
-rw-r--r--shell/ash_test/ash-vars/param_expand_alt.right4
-rw-r--r--shell/ash_test/ash-vars/param_expand_assign.right14
-rw-r--r--shell/ash_test/ash-vars/param_expand_bash_substring.right10
-rw-r--r--shell/ash_test/ash-vars/param_expand_default.right2
-rw-r--r--shell/ash_test/ash-vars/param_expand_indicate_error.right32
-rw-r--r--shell/ash_test/ash-vars/var6.right4
-rw-r--r--shell/ash_test/ash-vars/var_LINENO2.right3
-rwxr-xr-xshell/ash_test/ash-vars/var_LINENO2.tests8
-rw-r--r--shell/ash_test/ash-vars/var_LINENO3.right2
-rwxr-xr-xshell/ash_test/ash-vars/var_LINENO3.tests2
-rwxr-xr-xshell/ash_test/run-all14
-rw-r--r--shell/hush.c18
-rw-r--r--shell/hush_test/hush-misc/exitcode_trap7.right2
-rwxr-xr-xshell/hush_test/hush-misc/exitcode_trap7.tests7
-rw-r--r--shell/hush_test/hush-misc/set-n1.right3
-rwxr-xr-xshell/hush_test/hush-misc/set-n1.tests2
-rw-r--r--shell/hush_test/hush-vars/var_LINENO2.right3
-rwxr-xr-xshell/hush_test/hush-vars/var_LINENO2.tests8
-rw-r--r--shell/hush_test/hush-vars/var_LINENO3.right2
-rwxr-xr-xshell/hush_test/hush-vars/var_LINENO3.tests2
-rwxr-xr-xtestsuite/awk.tests6
-rwxr-xr-xtestsuite/printf.tests5
-rw-r--r--util-linux/blkdiscard.c13
53 files changed, 683 insertions, 437 deletions
diff --git a/coreutils/basename.c b/coreutils/basename.c
index 0dd2c43c7..0b721c03c 100644
--- a/coreutils/basename.c
+++ b/coreutils/basename.c
@@ -29,9 +29,11 @@
29/* http://www.opengroup.org/onlinepubs/007904975/utilities/basename.html */ 29/* http://www.opengroup.org/onlinepubs/007904975/utilities/basename.html */
30 30
31//usage:#define basename_trivial_usage 31//usage:#define basename_trivial_usage
32//usage: "FILE [SUFFIX]" 32//usage: "FILE [SUFFIX] | -a FILE... | -s SUFFIX FILE..."
33//usage:#define basename_full_usage "\n\n" 33//usage:#define basename_full_usage "\n\n"
34//usage: "Strip directory path and .SUFFIX from FILE" 34//usage: "Strip directory path and SUFFIX from FILE\n"
35//usage: "\n -a All arguments are FILEs"
36//usage: "\n -s SUFFIX Remove SUFFIX (implies -a)"
35//usage: 37//usage:
36//usage:#define basename_example_usage 38//usage:#define basename_example_usage
37//usage: "$ basename /usr/local/bin/foo\n" 39//usage: "$ basename /usr/local/bin/foo\n"
@@ -48,31 +50,43 @@
48int basename_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 50int basename_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
49int basename_main(int argc UNUSED_PARAM, char **argv) 51int basename_main(int argc UNUSED_PARAM, char **argv)
50{ 52{
51 size_t m, n; 53 unsigned opts;
52 char *s; 54 const char *suffix = NULL;
53 55
54 if (argv[1] && strcmp(argv[1], "--") == 0) { 56 /* '+': stop at first non-option */
55 argv++; 57 opts = getopt32(argv, "^+" "as:"
56 } 58 "\0" "-1" /* At least one argument */
57 if (!argv[1]) 59 , &suffix
58 bb_show_usage(); 60 );
61 argv += optind;
59 62
60 /* It should strip slash: /abc/def/ -> def */ 63 do {
61 s = bb_get_last_path_component_strip(*++argv); 64 char *s;
65 size_t m;
62 66
63 m = strlen(s); 67 /* It should strip slash: /abc/def/ -> def */
64 if (*++argv) { 68 s = bb_get_last_path_component_strip(*argv++);
65 if (argv[1]) 69 m = strlen(s);
66 bb_show_usage(); 70 if (!opts) {
67 n = strlen(*argv); 71 if (*argv) {
68 if ((m > n) && (strcmp(s+m-n, *argv) == 0)) { 72 suffix = *argv;
69 m -= n; 73 if (argv[1])
70 /*s[m] = '\0'; - redundant */ 74 bb_show_usage();
75 }
71 } 76 }
72 } 77 if (suffix) {
78 size_t n = strlen(suffix);
79 if ((m > n) && (strcmp(s + m - n, suffix) == 0)) {
80 m -= n;
81 /*s[m] = '\0'; - redundant */
82 }
83 }
84 /* puts(s) will do, but we can do without stdio this way: */
85 s[m++] = '\n';
86 /* NB: != is correct here: */
87 if (full_write(STDOUT_FILENO, s, m) != (ssize_t)m)
88 return EXIT_FAILURE;
89 } while (opts && *argv);
73 90
74 /* puts(s) will do, but we can do without stdio this way: */ 91 return EXIT_SUCCESS;
75 s[m++] = '\n';
76 /* NB: != is correct here: */
77 return full_write(STDOUT_FILENO, s, m) != (ssize_t)m;
78} 92}
diff --git a/coreutils/shuf.c b/coreutils/shuf.c
index 71b27f497..81b0df453 100644
--- a/coreutils/shuf.c
+++ b/coreutils/shuf.c
@@ -44,21 +44,25 @@
44 */ 44 */
45static void shuffle_lines(char **lines, unsigned numlines, unsigned outlines) 45static void shuffle_lines(char **lines, unsigned numlines, unsigned outlines)
46{ 46{
47 unsigned i;
48 unsigned r;
49 char *tmp;
50
51 srand(monotonic_us()); 47 srand(monotonic_us());
52 48
53 for (i = numlines - 1; outlines > 0; i--, outlines--) { 49 while (outlines != 0) {
54 r = rand(); 50 char *tmp;
51 unsigned r = rand();
55 /* RAND_MAX can be as small as 32767 */ 52 /* RAND_MAX can be as small as 32767 */
56 if (i > RAND_MAX) 53 if (numlines > RAND_MAX)
57 r ^= rand() << 15; 54 r ^= rand() << 15;
58 r %= i + 1; 55 r %= numlines;
59 tmp = lines[i]; 56//TODO: the above method is seriously non-uniform when numlines is very large.
60 lines[i] = lines[r]; 57//For example, with numlines of 0xf0000000,
58//values of (r % numlines) in [0, 0x0fffffff] range
59//are more likely: e.g. r=1 and r=0xf0000001 both map to 1,
60//whereas only one value, r=0xefffffff, maps to 0xefffffff.
61 numlines--;
62 tmp = lines[numlines];
63 lines[numlines] = lines[r];
61 lines[r] = tmp; 64 lines[r] = tmp;
65 outlines--;
62 } 66 }
63} 67}
64 68
@@ -67,9 +71,10 @@ int shuf_main(int argc, char **argv)
67{ 71{
68 unsigned opts; 72 unsigned opts;
69 char *opt_i_str, *opt_n_str, *opt_o_str; 73 char *opt_i_str, *opt_n_str, *opt_o_str;
70 unsigned i;
71 char **lines; 74 char **lines;
75 unsigned long long lo = lo;
72 unsigned numlines, outlines; 76 unsigned numlines, outlines;
77 unsigned i;
73 char eol; 78 char eol;
74 79
75 opts = getopt32(argv, "^" 80 opts = getopt32(argv, "^"
@@ -89,8 +94,8 @@ int shuf_main(int argc, char **argv)
89 } else 94 } else
90 if (opts & OPT_i) { 95 if (opts & OPT_i) {
91 /* create a range of numbers */ 96 /* create a range of numbers */
97 unsigned long long hi;
92 char *dash; 98 char *dash;
93 uintptr_t lo, hi;
94 99
95 if (argv[0]) 100 if (argv[0])
96 bb_show_usage(); 101 bb_show_usage();
@@ -100,27 +105,24 @@ int shuf_main(int argc, char **argv)
100 bb_error_msg_and_die("bad range '%s'", opt_i_str); 105 bb_error_msg_and_die("bad range '%s'", opt_i_str);
101 } 106 }
102 *dash = '\0'; 107 *dash = '\0';
103 if (sizeof(lo) == sizeof(int)) { 108 lo = xatoull(opt_i_str);
104 lo = xatou(opt_i_str); 109 hi = xatoull(dash + 1);
105 hi = xatou(dash + 1);
106 } else
107 if (sizeof(lo) == sizeof(long)) {
108 lo = xatoul(opt_i_str);
109 hi = xatoul(dash + 1);
110 } else {
111 lo = xatoull(opt_i_str);
112 hi = xatoull(dash + 1);
113 }
114 *dash = '-'; 110 *dash = '-';
115 if (hi < lo) { 111 if (hi < lo)
116 bb_error_msg_and_die("bad range '%s'", opt_i_str); 112 bb_error_msg_and_die("bad range '%s'", opt_i_str);
113 hi -= lo;
114 if (sizeof(size_t) > sizeof(numlines)) {
115 if (hi >= UINT_MAX)
116 bb_error_msg_and_die("bad range '%s'", opt_i_str);
117 } else {
118 if (hi >= UINT_MAX / sizeof(lines[0]))
119 bb_error_msg_and_die("bad range '%s'", opt_i_str);
117 } 120 }
118 121
119 numlines = (hi+1) - lo; 122 numlines = hi + 1;
120 lines = xmalloc(numlines * sizeof(lines[0])); 123 lines = xmalloc((size_t)numlines * sizeof(lines[0]));
121 for (i = 0; i < numlines; i++) { 124 for (i = 0; i < numlines; i++) {
122 lines[i] = (char*)lo; 125 lines[i] = (char*)(uintptr_t)i;
123 lo++;
124 } 126 }
125 } else { 127 } else {
126 /* default - read lines from stdin or the input file */ 128 /* default - read lines from stdin or the input file */
@@ -163,14 +165,9 @@ int shuf_main(int argc, char **argv)
163 eol = '\0'; 165 eol = '\0';
164 166
165 for (i = numlines - outlines; i < numlines; i++) { 167 for (i = numlines - outlines; i < numlines; i++) {
166 if (opts & OPT_i) { 168 if (opts & OPT_i)
167 if (sizeof(lines[0]) == sizeof(int)) 169 printf("%"LL_FMT"u%c", lo + (uintptr_t)lines[i], eol);
168 printf("%u%c", (unsigned)(uintptr_t)lines[i], eol); 170 else
169 else if (sizeof(lines[0]) == sizeof(long))
170 printf("%lu%c", (unsigned long)(uintptr_t)lines[i], eol);
171 else
172 printf("%"LL_FMT"u%c", (unsigned long long)(uintptr_t)lines[i], eol);
173 } else
174 printf("%s%c", lines[i], eol); 171 printf("%s%c", lines[i], eol);
175 } 172 }
176 173
diff --git a/editors/awk.c b/editors/awk.c
index 75f757dbb..17710e57e 100644
--- a/editors/awk.c
+++ b/editors/awk.c
@@ -2357,36 +2357,55 @@ static char *awk_printf(node *n, size_t *len)
2357 2357
2358 b = NULL; 2358 b = NULL;
2359 i = 0; 2359 i = 0;
2360 while (*f) { /* "print one format spec" loop */ 2360 while (1) { /* "print one format spec" loop */
2361 char *s; 2361 char *s;
2362 char c; 2362 char c;
2363 char sv; 2363 char sv;
2364 var *arg; 2364 var *arg;
2365 size_t slen; 2365 size_t slen;
2366 2366
2367 /* Find end of the next format spec, or end of line */
2367 s = f; 2368 s = f;
2368 while (*f && (*f != '%' || *++f == '%')) 2369 while (1) {
2369 f++; 2370 c = *f;
2370 while (*f && !isalpha(*f)) { 2371 if (!c) /* no percent chars found at all */
2371 if (*f == '*') 2372 goto nul;
2372 syntax_error("%*x formats are not supported");
2373 f++; 2373 f++;
2374 if (c == '%')
2375 break;
2374 } 2376 }
2377 /* we are past % in "....%..." */
2375 c = *f; 2378 c = *f;
2376 if (!c) { 2379 if (!c) /* "....%" */
2377 /* Tail of fmt with no percent chars, 2380 goto nul;
2378 * or "....%" (percent seen, but no format specifier char found) 2381 if (c == '%') { /* "....%%...." */
2379 */ 2382 slen = f - s;
2380 slen = strlen(s); 2383 s = xstrndup(s, slen);
2381 goto tail; 2384 f++;
2385 goto append; /* print "....%" part verbatim */
2382 } 2386 }
2383 sv = *++f; 2387 while (1) {
2384 *f = '\0'; 2388 if (isalpha(c))
2389 break;
2390 if (c == '*')
2391 syntax_error("%*x formats are not supported");
2392 c = *++f;
2393 if (!c) { /* "....%...." and no letter found after % */
2394 /* Example: awk 'BEGIN { printf "^^^%^^^\n"; }' */
2395 nul:
2396 slen = f - s;
2397 goto tail; /* print remaining string, exit loop */
2398 }
2399 }
2400 /* we are at A in "....%...A..." */
2401
2385 arg = evaluate(nextarg(&n), TMPVAR); 2402 arg = evaluate(nextarg(&n), TMPVAR);
2386 2403
2387 /* Result can be arbitrarily long. Example: 2404 /* Result can be arbitrarily long. Example:
2388 * printf "%99999s", "BOOM" 2405 * printf "%99999s", "BOOM"
2389 */ 2406 */
2407 sv = *++f;
2408 *f = '\0';
2390 if (c == 'c') { 2409 if (c == 'c') {
2391 char cc = is_numeric(arg) ? getvar_i(arg) : *getvar_s(arg); 2410 char cc = is_numeric(arg) ? getvar_i(arg) : *getvar_s(arg);
2392 char *r = xasprintf(s, cc ? cc : '^' /* else strlen will be wrong */); 2411 char *r = xasprintf(s, cc ? cc : '^' /* else strlen will be wrong */);
@@ -2405,13 +2424,14 @@ static char *awk_printf(node *n, size_t *len)
2405 } else if (strchr("eEfFgGaA", c)) { 2424 } else if (strchr("eEfFgGaA", c)) {
2406 s = xasprintf(s, d); 2425 s = xasprintf(s, d);
2407 } else { 2426 } else {
2427//TODO: GNU Awk 5.0.1: printf "%W" prints "%W", does not error out
2408 syntax_error(EMSG_INV_FMT); 2428 syntax_error(EMSG_INV_FMT);
2409 } 2429 }
2410 } 2430 }
2411 slen = strlen(s); 2431 slen = strlen(s);
2412 } 2432 }
2413 *f = sv; 2433 *f = sv;
2414 2434 append:
2415 if (i == 0) { 2435 if (i == 0) {
2416 b = s; 2436 b = s;
2417 i = slen; 2437 i = slen;
@@ -2421,7 +2441,7 @@ static char *awk_printf(node *n, size_t *len)
2421 b = xrealloc(b, i + slen + 1); 2441 b = xrealloc(b, i + slen + 1);
2422 strcpy(b + i, s); 2442 strcpy(b + i, s);
2423 i += slen; 2443 i += slen;
2424 if (!c) /* tail? */ 2444 if (!c) /* s is NOT allocated and this is the last part of string? */
2425 break; 2445 break;
2426 free(s); 2446 free(s);
2427 } 2447 }
diff --git a/examples/var_service/fw/run b/examples/var_service/fw/run
index 41078d0ab..15c2b2f0a 100755
--- a/examples/var_service/fw/run
+++ b/examples/var_service/fw/run
@@ -179,7 +179,9 @@ ipt="iptables -t nat -A OUTPUT"
179# OUTGOING TRAFFIC 179# OUTGOING TRAFFIC
180ipt="iptables -t nat -A POSTROUTING" 180ipt="iptables -t nat -A POSTROUTING"
181# Masquerade boxes on my private net 181# Masquerade boxes on my private net
182doit $ipt -s 192.168.0.0/24 -o $extif -j MASQUERADE 182for e in $extif; do
183 doit $ipt -s 192.168.0.0/24 -o $e -j MASQUERADE
184done
183 185
184# *** mangle *** 186# *** mangle ***
185### DEBUG 187### DEBUG
@@ -204,7 +206,9 @@ fi
204doit $ipt -p tcp -j REJECT # Anything else isn't ok. REJECT = irc opens faster 206doit $ipt -p tcp -j REJECT # Anything else isn't ok. REJECT = irc opens faster
205 # (it probes proxy ports, DROP will incur timeout delays) 207 # (it probes proxy ports, DROP will incur timeout delays)
206ipt="iptables -t filter -A INPUT" 208ipt="iptables -t filter -A INPUT"
207doit $ipt -i $extif -j iext 209for e in $extif; do
210 doit $ipt -i $e -j iext
211done
208 212
209 213
210echo; echo "* Enabling forwarding" 214echo; echo "* Enabling forwarding"
@@ -222,12 +226,12 @@ echo; echo "* Routing:"
222ip r l 226ip r l
223echo; echo "* Firewall:" 227echo; echo "* Firewall:"
224{ 228{
225echo '---FILTER--'; 229echo '---FILTER--'
226iptables -v -L -x -n; 230iptables -v -L -x -n
227echo '---NAT-----'; 231echo '---NAT-----'
228iptables -t nat -v -L -x -n; 232iptables -t nat -v -L -x -n
229echo '---MANGLE--'; 233echo '---MANGLE--'
230iptables -t mangle -v -L -x -n; 234iptables -t mangle -v -L -x -n
231} \ 235} \
232| grep -v '^$' | grep -Fv 'bytes target' 236| grep -v '^$' | grep -Fv 'bytes target'
233echo 237echo
diff --git a/include/usage.src.h b/include/usage.src.h
index 1ac252d1b..5d2038834 100644
--- a/include/usage.src.h
+++ b/include/usage.src.h
@@ -31,6 +31,9 @@
31# define ADJTIME_PATH "/etc/adjtime" 31# define ADJTIME_PATH "/etc/adjtime"
32#endif 32#endif
33 33
34#define STR1(s) #s
35#define STR(s) STR1(s)
36
34INSERT 37INSERT
35 38
36#define busybox_notes_usage \ 39#define busybox_notes_usage \
diff --git a/libbb/xconnect.c b/libbb/xconnect.c
index f5c514b2c..65b1cb8de 100644
--- a/libbb/xconnect.c
+++ b/libbb/xconnect.c
@@ -117,27 +117,19 @@ void FAST_FUNC xconnect(int s, const struct sockaddr *saddr, socklen_t addrlen)
117 117
118/* Return port number for a service. 118/* Return port number for a service.
119 * If "port" is a number use it as the port. 119 * If "port" is a number use it as the port.
120 * If "port" is a name it is looked up in /etc/services, 120 * If "port" is a name it is looked up in /etc/services.
121 * if it isnt found return default_port 121 * if NULL, return default_port
122 */ 122 */
123unsigned FAST_FUNC bb_lookup_port(const char *port, const char *protocol, unsigned default_port) 123unsigned FAST_FUNC bb_lookup_port(const char *port, const char *protocol, unsigned port_nr)
124{ 124{
125 unsigned port_nr = default_port;
126 if (port) { 125 if (port) {
127 int old_errno;
128
129 /* Since this is a lib function, we're not allowed to reset errno to 0.
130 * Doing so could break an app that is deferring checking of errno. */
131 old_errno = errno;
132 port_nr = bb_strtou(port, NULL, 10); 126 port_nr = bb_strtou(port, NULL, 10);
133 if (errno || port_nr > 65535) { 127 if (errno || port_nr > 65535) {
134 struct servent *tserv = getservbyname(port, protocol); 128 struct servent *tserv = getservbyname(port, protocol);
135 port_nr = default_port; 129 if (!tserv)
136 if (tserv) 130 bb_error_msg_and_die("bad port '%s'", port);
137 port_nr = ntohs(tserv->s_port); 131 port_nr = ntohs(tserv->s_port);
138//FIXME: else: port string was garbage, but we don't report that???
139 } 132 }
140 errno = old_errno;
141 } 133 }
142 return (uint16_t)port_nr; 134 return (uint16_t)port_nr;
143} 135}
@@ -241,7 +233,7 @@ IF_NOT_FEATURE_IPV6(sa_family_t af = AF_INET;)
241 cp++; /* skip ':' */ 233 cp++; /* skip ':' */
242 port = bb_strtou(cp, NULL, 10); 234 port = bb_strtou(cp, NULL, 10);
243 if (errno || (unsigned)port > 0xffff) { 235 if (errno || (unsigned)port > 0xffff) {
244 bb_error_msg("bad port spec '%s'", org_host); 236 bb_error_msg("bad port '%s'", cp);
245 if (ai_flags & DIE_ON_ERROR) 237 if (ai_flags & DIE_ON_ERROR)
246 xfunc_die(); 238 xfunc_die();
247 return NULL; 239 return NULL;
diff --git a/miscutils/chat.c b/miscutils/chat.c
index f9e12a4ac..83aac37de 100644
--- a/miscutils/chat.c
+++ b/miscutils/chat.c
@@ -201,6 +201,9 @@ int chat_main(int argc UNUSED_PARAM, char **argv)
201 DIR_RECORD, 201 DIR_RECORD,
202 }; 202 };
203 203
204#define inbuf bb_common_bufsiz1
205 setup_common_bufsiz();
206
204 // make x* functions fail with correct exitcode 207 // make x* functions fail with correct exitcode
205 xfunc_error_retval = ERR_IO; 208 xfunc_error_retval = ERR_IO;
206 209
@@ -361,35 +364,36 @@ int chat_main(int argc UNUSED_PARAM, char **argv)
361 // get reply 364 // get reply
362 pfd.fd = STDIN_FILENO; 365 pfd.fd = STDIN_FILENO;
363 pfd.events = POLLIN; 366 pfd.events = POLLIN;
364 while (!exitcode 367 while (exitcode == ERR_OK
365 && poll(&pfd, 1, timeout) > 0 368 && poll(&pfd, 1, timeout) > 0
366 && (pfd.revents & POLLIN) 369 /* && (pfd.revents & POLLIN) - may be untrue (e.g. only POLLERR set) */
367 ) { 370 ) {
368 llist_t *l; 371 llist_t *l;
369 ssize_t delta; 372 ssize_t delta;
370#define buf bb_common_bufsiz1
371 setup_common_bufsiz();
372 373
373 // read next char from device 374 // read next char from device
374 if (safe_read(STDIN_FILENO, buf+buf_len, 1) > 0) { 375 if (safe_read(STDIN_FILENO, inbuf + buf_len, 1) <= 0) {
375 // dump device input if RECORD fname 376 exitcode = ERR_IO;
376 if (record_fd > 0) { 377 goto expect_done;
377 full_write(record_fd, buf+buf_len, 1); 378 }
378 } 379
379 // dump device input if ECHO ON 380 // dump device input if RECORD fname
380 if (echo) { 381 if (record_fd > 0) {
381// if (buf[buf_len] < ' ') { 382 full_write(record_fd, inbuf + buf_len, 1);
382// full_write2_str("^"); 383 }
383// buf[buf_len] += '@'; 384 // dump device input if ECHO ON
384// } 385 if (echo) {
385 full_write(STDERR_FILENO, buf+buf_len, 1); 386// if (inbuf[buf_len] < ' ') {
386 } 387// full_write2_str("^");
387 buf_len++; 388// inbuf[buf_len] += '@';
388 // move input frame if we've reached higher bound 389// }
389 if (buf_len > COMMON_BUFSIZE) { 390 full_write(STDERR_FILENO, inbuf + buf_len, 1);
390 memmove(buf, buf+buf_len-max_len, max_len); 391 }
391 buf_len = max_len; 392 buf_len++;
392 } 393 // move input frame if we've reached higher bound
394 if (buf_len > COMMON_BUFSIZE) {
395 memmove(inbuf, inbuf + buf_len - max_len, max_len);
396 buf_len = max_len;
393 } 397 }
394 // N.B. rule of thumb: values being looked for can 398 // N.B. rule of thumb: values being looked for can
395 // be found only at the end of input buffer 399 // be found only at the end of input buffer
@@ -399,20 +403,20 @@ int chat_main(int argc UNUSED_PARAM, char **argv)
399 // abort condition is met? -> bail out 403 // abort condition is met? -> bail out
400 for (l = aborts, exitcode = ERR_ABORT; l; l = l->link, ++exitcode) { 404 for (l = aborts, exitcode = ERR_ABORT; l; l = l->link, ++exitcode) {
401 size_t len = strlen(l->data); 405 size_t len = strlen(l->data);
402 delta = buf_len-len; 406 delta = buf_len - len;
403 if (delta >= 0 && !memcmp(buf+delta, l->data, len)) 407 if (delta >= 0 && !memcmp(inbuf + delta, l->data, len))
404 goto expect_done; 408 goto expect_done;
405 } 409 }
406 exitcode = ERR_OK; 410 exitcode = ERR_OK;
407 411
408 // expected reply received? -> goto next command 412 // expected reply received? -> goto next command
409 delta = buf_len - expect_len; 413 delta = buf_len - expect_len;
410 if (delta >= 0 && !memcmp(buf+delta, expect, expect_len)) 414 if (delta >= 0 && memcmp(inbuf + delta, expect, expect_len) == 0)
411 goto expect_done; 415 goto expect_done;
412#undef buf
413 } /* while (have data) */ 416 } /* while (have data) */
414 417
415 // device timed out or unexpected reply received 418 // device timed out, or unexpected reply received,
419 // or we got a signal (poll() returned -1 with EINTR).
416 exitcode = ERR_TIMEOUT; 420 exitcode = ERR_TIMEOUT;
417 expect_done: 421 expect_done:
418#if ENABLE_FEATURE_CHAT_NOFAIL 422#if ENABLE_FEATURE_CHAT_NOFAIL
@@ -434,7 +438,7 @@ int chat_main(int argc UNUSED_PARAM, char **argv)
434 } 438 }
435#endif 439#endif
436 // bail out unless we expected successfully 440 // bail out unless we expected successfully
437 if (exitcode) 441 if (exitcode != ERR_OK)
438 break; 442 break;
439 443
440 //----------------------- 444 //-----------------------
@@ -478,7 +482,7 @@ int chat_main(int argc UNUSED_PARAM, char **argv)
478 continue; 482 continue;
479 } 483 }
480 if ('p' == c) { 484 if ('p' == c) {
481 usleep(10000); 485 msleep(10);
482 len--; 486 len--;
483 continue; 487 continue;
484 } 488 }
diff --git a/networking/ftpgetput.c b/networking/ftpgetput.c
index f1d5ca571..ef0973a84 100644
--- a/networking/ftpgetput.c
+++ b/networking/ftpgetput.c
@@ -296,8 +296,7 @@ static const char ftpgetput_longopts[] ALIGN1 =
296int ftpgetput_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 296int ftpgetput_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
297int ftpgetput_main(int argc UNUSED_PARAM, char **argv) 297int ftpgetput_main(int argc UNUSED_PARAM, char **argv)
298{ 298{
299 const char *port = "ftp"; 299 const char *port = NULL;
300 /* socket to ftp server */
301 300
302#if ENABLE_FTPPUT && !ENABLE_FTPGET 301#if ENABLE_FTPPUT && !ENABLE_FTPGET
303# define ftp_action ftp_send 302# define ftp_action ftp_send
diff --git a/networking/httpd.c b/networking/httpd.c
index fcd1627b1..71e3a723f 100644
--- a/networking/httpd.c
+++ b/networking/httpd.c
@@ -102,6 +102,12 @@
102//config: help 102//config: help
103//config: HTTP server. 103//config: HTTP server.
104//config: 104//config:
105//config:config FEATURE_HTTPD_PORT_DEFAULT
106//config: int "Default port"
107//config: default 80
108//config: range 1 65535
109//config: depends on HTTPD
110//config:
105//config:config FEATURE_HTTPD_RANGES 111//config:config FEATURE_HTTPD_RANGES
106//config: bool "Support 'Ranges:' header" 112//config: bool "Support 'Ranges:' header"
107//config: default y 113//config: default y
@@ -277,7 +283,7 @@
277//usage: ) 283//usage: )
278//usage: "\n -f Don't daemonize" 284//usage: "\n -f Don't daemonize"
279//usage: "\n -v[v] Verbose" 285//usage: "\n -v[v] Verbose"
280//usage: "\n -p [IP:]PORT Bind to IP:PORT (default *:80)" 286//usage: "\n -p [IP:]PORT Bind to IP:PORT (default *:"STR(CONFIG_FEATURE_HTTPD_PORT_DEFAULT)")"
281//usage: IF_FEATURE_HTTPD_SETUID( 287//usage: IF_FEATURE_HTTPD_SETUID(
282//usage: "\n -u USER[:GRP] Set uid/gid after binding to port") 288//usage: "\n -u USER[:GRP] Set uid/gid after binding to port")
283//usage: IF_FEATURE_HTTPD_BASIC_AUTH( 289//usage: IF_FEATURE_HTTPD_BASIC_AUTH(
@@ -325,6 +331,9 @@
325 331
326static void send_REQUEST_TIMEOUT_and_exit(int sig) NORETURN; 332static void send_REQUEST_TIMEOUT_and_exit(int sig) NORETURN;
327 333
334#define STR1(s) #s
335#define STR(s) STR1(s)
336
328static const char DEFAULT_PATH_HTTPD_CONF[] ALIGN1 = "/etc"; 337static const char DEFAULT_PATH_HTTPD_CONF[] ALIGN1 = "/etc";
329static const char HTTPD_CONF[] ALIGN1 = "httpd.conf"; 338static const char HTTPD_CONF[] ALIGN1 = "httpd.conf";
330static const char HTTP_200[] ALIGN1 = "HTTP/1.1 200 OK\r\n"; 339static const char HTTP_200[] ALIGN1 = "HTTP/1.1 200 OK\r\n";
@@ -551,7 +560,7 @@ enum {
551 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ 560 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
552 IF_FEATURE_HTTPD_BASIC_AUTH(g_realm = "Web Server Authentication";) \ 561 IF_FEATURE_HTTPD_BASIC_AUTH(g_realm = "Web Server Authentication";) \
553 IF_FEATURE_HTTPD_RANGES(range_start = -1;) \ 562 IF_FEATURE_HTTPD_RANGES(range_start = -1;) \
554 bind_addr_or_port = "80"; \ 563 bind_addr_or_port = STR(CONFIG_FEATURE_HTTPD_PORT_DEFAULT); \
555 index_page = index_html; \ 564 index_page = index_html; \
556 file_size = -1; \ 565 file_size = -1; \
557} while (0) 566} while (0)
diff --git a/networking/nc.c b/networking/nc.c
index 3483edb01..5525e41f9 100644
--- a/networking/nc.c
+++ b/networking/nc.c
@@ -226,7 +226,7 @@ int nc_main(int argc, char **argv)
226 close(sfd); 226 close(sfd);
227 } else { 227 } else {
228 cfd = create_and_connect_stream_or_die(argv[0], 228 cfd = create_and_connect_stream_or_die(argv[0],
229 argv[1] ? bb_lookup_port(argv[1], "tcp", 0) : 0); 229 bb_lookup_port(argv[1], "tcp", 0));
230 } 230 }
231 } 231 }
232 232
diff --git a/networking/nc_bloaty.c b/networking/nc_bloaty.c
index 25b95246f..cfa133eae 100644
--- a/networking/nc_bloaty.c
+++ b/networking/nc_bloaty.c
@@ -813,8 +813,6 @@ int nc_main(int argc UNUSED_PARAM, char **argv)
813 //if (option_mask32 & OPT_o) /* hexdump log */ 813 //if (option_mask32 & OPT_o) /* hexdump log */
814 if (option_mask32 & OPT_p) { /* local source port */ 814 if (option_mask32 & OPT_p) { /* local source port */
815 o_lport = bb_lookup_port(str_p, o_udpmode ? "udp" : "tcp", 0); 815 o_lport = bb_lookup_port(str_p, o_udpmode ? "udp" : "tcp", 0);
816 if (!o_lport)
817 bb_error_msg_and_die("bad local port '%s'", str_p);
818 } 816 }
819 //if (option_mask32 & OPT_r) /* randomize various things */ 817 //if (option_mask32 & OPT_r) /* randomize various things */
820 //if (option_mask32 & OPT_u) /* use UDP */ 818 //if (option_mask32 & OPT_u) /* use UDP */
@@ -827,9 +825,8 @@ int nc_main(int argc UNUSED_PARAM, char **argv)
827 825
828 if (argv[0]) { 826 if (argv[0]) {
829 themaddr = xhost2sockaddr(argv[0], 827 themaddr = xhost2sockaddr(argv[0],
830 argv[1] 828 bb_lookup_port(argv[1], o_udpmode ? "udp" : "tcp", 0)
831 ? bb_lookup_port(argv[1], o_udpmode ? "udp" : "tcp", 0) 829 );
832 : 0);
833 } 830 }
834 831
835 /* create & bind network socket */ 832 /* create & bind network socket */
diff --git a/networking/telnetd.c b/networking/telnetd.c
index de4d733f9..581da1924 100644
--- a/networking/telnetd.c
+++ b/networking/telnetd.c
@@ -68,6 +68,12 @@
68//config: help 68//config: help
69//config: Selecting this will make telnetd able to run standalone. 69//config: Selecting this will make telnetd able to run standalone.
70//config: 70//config:
71//config:config FEATURE_TELNETD_PORT_DEFAULT
72//config: int "Default port"
73//config: default 23
74//config: range 1 65535
75//config: depends on FEATURE_TELNETD_STANDALONE
76//config:
71//config:config FEATURE_TELNETD_INETD_WAIT 77//config:config FEATURE_TELNETD_INETD_WAIT
72//config: bool "Support -w SEC option (inetd wait mode)" 78//config: bool "Support -w SEC option (inetd wait mode)"
73//config: default y 79//config: default y
@@ -103,7 +109,7 @@
103//usage: "\n -K Close connection as soon as login exits" 109//usage: "\n -K Close connection as soon as login exits"
104//usage: "\n (normally wait until all programs close slave pty)" 110//usage: "\n (normally wait until all programs close slave pty)"
105//usage: IF_FEATURE_TELNETD_STANDALONE( 111//usage: IF_FEATURE_TELNETD_STANDALONE(
106//usage: "\n -p PORT Port to listen on" 112//usage: "\n -p PORT Port to listen on. Default "STR(CONFIG_FEATURE_TELNETD_PORT_DEFAULT)
107//usage: "\n -b ADDR[:PORT] Address to bind to" 113//usage: "\n -b ADDR[:PORT] Address to bind to"
108//usage: "\n -F Run in foreground" 114//usage: "\n -F Run in foreground"
109//usage: "\n -i Inetd mode" 115//usage: "\n -i Inetd mode"
@@ -708,7 +714,7 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv)
708 } else { 714 } else {
709 master_fd = 0; 715 master_fd = 0;
710 if (!(opt & OPT_WAIT)) { 716 if (!(opt & OPT_WAIT)) {
711 unsigned portnbr = 23; 717 unsigned portnbr = CONFIG_FEATURE_TELNETD_PORT_DEFAULT;
712 if (opt & OPT_PORT) 718 if (opt & OPT_PORT)
713 portnbr = xatou16(opt_portnbr); 719 portnbr = xatou16(opt_portnbr);
714 master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr); 720 master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr);
diff --git a/networking/udhcp/common.h b/networking/udhcp/common.h
index ca778dab8..e374771cb 100644
--- a/networking/udhcp/common.h
+++ b/networking/udhcp/common.h
@@ -38,13 +38,27 @@ struct dhcp_packet {
38#define BROADCAST_FLAG 0x8000 /* "I need broadcast replies" */ 38#define BROADCAST_FLAG 0x8000 /* "I need broadcast replies" */
39 uint32_t ciaddr; /* client IP (if client is in BOUND, RENEW or REBINDING state) */ 39 uint32_t ciaddr; /* client IP (if client is in BOUND, RENEW or REBINDING state) */
40 uint32_t yiaddr; /* 'your' (client) IP address */ 40 uint32_t yiaddr; /* 'your' (client) IP address */
41 /* IP address of next server to use in bootstrap, returned in DHCPOFFER, DHCPACK by server */ 41 /* IP address of "next server" (usually meant to be an TFTP server)
42 * to use in bootstrap, returned in DHCPOFFER, DHCPACK by server: */
42 uint32_t siaddr_nip; 43 uint32_t siaddr_nip;
43 uint32_t gateway_nip; /* aka 'giaddr': relay agent IP address */ 44 /* RFC 951 (BOOTP): "place my (server) IP address in the 'siaddr' field"
45 * (IOW: unconditionally, not just if we are also a TFTP server).
46 * DHCP servers don't have to do this, they add SERVER_ID option
47 * to their reply packets to let client identify lease-giving server.
48 */
49 uint32_t gateway_nip; /* aka 'giaddr': relay agent IP address, else 0 */
44 uint8_t chaddr[16]; /* link-layer client hardware address (MAC) */ 50 uint8_t chaddr[16]; /* link-layer client hardware address (MAC) */
45 uint8_t sname[64]; /* server host name (ASCIZ) */ 51 uint8_t sname[64]; /* server host name (ASCIZ) */
52 /* RFC 951 (BOOTP): "If the client wishes to restrict booting
53 * to a particular server name, it may place [it] in 'sname'"
54 */
46 uint8_t file[128]; /* boot file name (ASCIZ) */ 55 uint8_t file[128]; /* boot file name (ASCIZ) */
47 uint32_t cookie; /* fixed first four option bytes (99,130,83,99 dec) */ 56 /* RFC 951 (BOOTP): in client requests, "...can be a 'generic' name
57 * such as 'unix' or 'gateway'; this means 'boot the named program
58 * configured for my machine'"
59 */
60 /* BOOTP fields end here, BOOTP says optional uint8_t vend[64] follows */
61 uint32_t cookie; /* DHCP magic bytes: 99,130,83,99 decimal */
48 uint8_t options[DHCP_OPTIONS_BUFSIZE + CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS]; 62 uint8_t options[DHCP_OPTIONS_BUFSIZE + CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS];
49}; 63};
50#define DHCP_PKT_SNAME_LEN 64 64#define DHCP_PKT_SNAME_LEN 64
@@ -142,18 +156,21 @@ struct dhcp_scan_state {
142//#define DHCP_NTP_SERVER 0x2a 156//#define DHCP_NTP_SERVER 0x2a
143//#define DHCP_WINS_SERVER 0x2c 157//#define DHCP_WINS_SERVER 0x2c
144#define DHCP_REQUESTED_IP 0x32 /* 50: sent by client if specific IP is wanted */ 158#define DHCP_REQUESTED_IP 0x32 /* 50: sent by client if specific IP is wanted */
145#define DHCP_LEASE_TIME 0x33 /* 51: */ 159#define DHCP_LEASE_TIME 0x33 /* 51: 32bit big-endian */
146#define DHCP_OPTION_OVERLOAD 0x34 /* 52: */ 160#define DHCP_OPTION_OVERLOAD 0x34 /* 52: 1 byte */
147#define DHCP_MESSAGE_TYPE 0x35 /* 53: */ 161#define DHCP_MESSAGE_TYPE 0x35 /* 53: 1 byte */
148#define DHCP_SERVER_ID 0x36 /* 54: server's IP */ 162#define DHCP_SERVER_ID 0x36 /* 54: server's IP */
149#define DHCP_PARAM_REQ 0x37 /* 55: list of options client wants */ 163#define DHCP_PARAM_REQ 0x37 /* 55: list of options client wants */
150//#define DHCP_ERR_MESSAGE 0x38 /* 56: error message when sending NAK etc */ 164//#define DHCP_ERR_MESSAGE 0x38 /* 56: error message when sending NAK etc */
151#define DHCP_MAX_SIZE 0x39 /* 57: */ 165#define DHCP_MAX_SIZE 0x39 /* 57: 16bit big-endian */
166// 0x3a /* 58: from server: renew time, 32bit big-endian */
167// 0x3b /* 59: from server: rebind time, 32bit big-endian */
152#define DHCP_VENDOR 0x3c /* 60: client's vendor (a string) */ 168#define DHCP_VENDOR 0x3c /* 60: client's vendor (a string) */
153#define DHCP_CLIENT_ID 0x3d /* 61: by default client's MAC addr, but may be arbitrarily long */ 169#define DHCP_CLIENT_ID 0x3d /* 61: by default client's MAC addr, but may be arbitrarily long */
154//#define DHCP_TFTP_SERVER_NAME 0x42 /* 66: same as 'sname' field */ 170//#define DHCP_TFTP_SERVER_NAME 0x42 /* 66: same as 'sname' field */
155//#define DHCP_BOOT_FILE 0x43 /* 67: same as 'file' field */ 171//#define DHCP_BOOT_FILE 0x43 /* 67: same as 'file' field */
156//#define DHCP_USER_CLASS 0x4d /* 77: RFC 3004. set of LASCII strings. "I am a printer" etc */ 172//#define DHCP_USER_CLASS 0x4d /* 77: RFC 3004. set of LASCII strings. "I am a printer" etc */
173// 0x50 /* 80: rapid commit ("I'm ok with getting immediate ACK, not just OFFER"), 0 bytes */
157#define DHCP_FQDN 0x51 /* 81: client asks to update DNS to map its FQDN to its new IP */ 174#define DHCP_FQDN 0x51 /* 81: client asks to update DNS to map its FQDN to its new IP */
158//#define DHCP_PCODE 0x64 /* 100: RFC 4833. IEEE 1003.1 TZ string */ 175//#define DHCP_PCODE 0x64 /* 100: RFC 4833. IEEE 1003.1 TZ string */
159//#define DHCP_TCODE 0x65 /* 101: RFC 4833. Reference to the TZ database string */ 176//#define DHCP_TCODE 0x65 /* 101: RFC 4833. Reference to the TZ database string */
diff --git a/networking/udhcp/dhcpd.c b/networking/udhcp/dhcpd.c
index 91f70970a..0f5edb75c 100644
--- a/networking/udhcp/dhcpd.c
+++ b/networking/udhcp/dhcpd.c
@@ -27,7 +27,7 @@
27//kbuild:lib-$(CONFIG_FEATURE_UDHCP_RFC3397) += domain_codec.o 27//kbuild:lib-$(CONFIG_FEATURE_UDHCP_RFC3397) += domain_codec.o
28 28
29//usage:#define udhcpd_trivial_usage 29//usage:#define udhcpd_trivial_usage
30//usage: "[-fS] [-I ADDR]" IF_FEATURE_UDHCP_PORT(" [-P PORT]") " [CONFFILE]" 30//usage: "[-fS] [-I ADDR] [-a MSEC]" IF_FEATURE_UDHCP_PORT(" [-P PORT]") " [CONFFILE]"
31//usage:#define udhcpd_full_usage "\n\n" 31//usage:#define udhcpd_full_usage "\n\n"
32//usage: "DHCP server\n" 32//usage: "DHCP server\n"
33//usage: "\n -f Run in foreground" 33//usage: "\n -f Run in foreground"
@@ -451,6 +451,8 @@ static NOINLINE void read_config(const char *file)
451 451
452 server_data.start_ip = ntohl(server_data.start_ip); 452 server_data.start_ip = ntohl(server_data.start_ip);
453 server_data.end_ip = ntohl(server_data.end_ip); 453 server_data.end_ip = ntohl(server_data.end_ip);
454 if (server_data.start_ip > server_data.end_ip)
455 bb_error_msg_and_die("bad start/end IP range in %s", file);
454} 456}
455 457
456static void write_leases(void) 458static void write_leases(void)
@@ -612,6 +614,10 @@ static void send_packet_to_relay(struct dhcp_packet *dhcp_pkt)
612 udhcp_send_kernel_packet(dhcp_pkt, 614 udhcp_send_kernel_packet(dhcp_pkt,
613 server_data.server_nip, SERVER_PORT, 615 server_data.server_nip, SERVER_PORT,
614 dhcp_pkt->gateway_nip, SERVER_PORT, 616 dhcp_pkt->gateway_nip, SERVER_PORT,
617 /* Yes, relay agents receive (and send) all their packets on SERVER_PORT,
618 * even those which are clients' requests and would normally
619 * (i.e. without relay) use CLIENT_PORT. See RFC 1542.
620 */
615 server_data.interface); 621 server_data.interface);
616} 622}
617 623
@@ -858,7 +864,6 @@ int udhcpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
858int udhcpd_main(int argc UNUSED_PARAM, char **argv) 864int udhcpd_main(int argc UNUSED_PARAM, char **argv)
859{ 865{
860 int server_socket = -1, retval; 866 int server_socket = -1, retval;
861 uint8_t *state;
862 unsigned timeout_end; 867 unsigned timeout_end;
863 unsigned num_ips; 868 unsigned num_ips;
864 unsigned opt; 869 unsigned opt;
@@ -877,6 +882,12 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv)
877 /* Setup the signal pipe on fds 3,4 - must be before openlog() */ 882 /* Setup the signal pipe on fds 3,4 - must be before openlog() */
878 udhcp_sp_setup(); 883 udhcp_sp_setup();
879 884
885#define OPT_f (1 << 0)
886#define OPT_S (1 << 1)
887#define OPT_I (1 << 2)
888#define OPT_v (1 << 3)
889#define OPT_a (1 << 4)
890#define OPT_P (1 << 5)
880 opt = getopt32(argv, "^" 891 opt = getopt32(argv, "^"
881 "fSI:va:"IF_FEATURE_UDHCP_PORT("P:") 892 "fSI:va:"IF_FEATURE_UDHCP_PORT("P:")
882 "\0" 893 "\0"
@@ -887,24 +898,24 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv)
887 , &str_a 898 , &str_a
888 IF_FEATURE_UDHCP_PORT(, &str_P) 899 IF_FEATURE_UDHCP_PORT(, &str_P)
889 IF_UDHCP_VERBOSE(, &dhcp_verbose) 900 IF_UDHCP_VERBOSE(, &dhcp_verbose)
890 ); 901 );
891 if (!(opt & 1)) { /* no -f */ 902 if (!(opt & OPT_f)) { /* no -f */
892 bb_daemonize_or_rexec(0, argv); 903 bb_daemonize_or_rexec(0, argv);
893 logmode = LOGMODE_NONE; 904 logmode = LOGMODE_NONE;
894 } 905 }
895 /* update argv after the possible vfork+exec in daemonize */ 906 /* update argv after the possible vfork+exec in daemonize */
896 argv += optind; 907 argv += optind;
897 if (opt & 2) { /* -S */ 908 if (opt & OPT_S) {
898 openlog(applet_name, LOG_PID, LOG_DAEMON); 909 openlog(applet_name, LOG_PID, LOG_DAEMON);
899 logmode |= LOGMODE_SYSLOG; 910 logmode |= LOGMODE_SYSLOG;
900 } 911 }
901 if (opt & 4) { /* -I */ 912 if (opt & OPT_I) {
902 len_and_sockaddr *lsa = xhost_and_af2sockaddr(str_I, 0, AF_INET); 913 len_and_sockaddr *lsa = xhost_and_af2sockaddr(str_I, 0, AF_INET);
903 server_data.server_nip = lsa->u.sin.sin_addr.s_addr; 914 server_data.server_nip = lsa->u.sin.sin_addr.s_addr;
904 free(lsa); 915 free(lsa);
905 } 916 }
906#if ENABLE_FEATURE_UDHCP_PORT 917#if ENABLE_FEATURE_UDHCP_PORT
907 if (opt & 32) { /* -P */ 918 if (opt & OPT_P) {
908 SERVER_PORT = xatou16(str_P); 919 SERVER_PORT = xatou16(str_P);
909 CLIENT_PORT = SERVER_PORT + 1; 920 CLIENT_PORT = SERVER_PORT + 1;
910 } 921 }
@@ -960,6 +971,7 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv)
960 struct dhcp_packet packet; 971 struct dhcp_packet packet;
961 int bytes; 972 int bytes;
962 int tv; 973 int tv;
974 uint8_t *msg_type;
963 uint8_t *server_id_opt; 975 uint8_t *server_id_opt;
964 uint8_t *requested_ip_opt; 976 uint8_t *requested_ip_opt;
965 uint32_t requested_nip; 977 uint32_t requested_nip;
@@ -1017,6 +1029,9 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv)
1017 * socket read inside this call is restarted on caught signals. 1029 * socket read inside this call is restarted on caught signals.
1018 */ 1030 */
1019 bytes = udhcp_recv_kernel_packet(&packet, server_socket); 1031 bytes = udhcp_recv_kernel_packet(&packet, server_socket);
1032//NB: we do not check source port here. Should we?
1033//It should be CLIENT_PORT for clients,
1034//or SERVER_PORT for relay agents (in which case giaddr must be != 0.0.0.0)
1020 if (bytes < 0) { 1035 if (bytes < 0) {
1021 /* bytes can also be -2 ("bad packet data") */ 1036 /* bytes can also be -2 ("bad packet data") */
1022 if (bytes == -1 && errno != EINTR) { 1037 if (bytes == -1 && errno != EINTR) {
@@ -1034,8 +1049,8 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv)
1034 bb_info_msg("not a REQUEST%s", ", ignoring packet"); 1049 bb_info_msg("not a REQUEST%s", ", ignoring packet");
1035 continue; 1050 continue;
1036 } 1051 }
1037 state = udhcp_get_option(&packet, DHCP_MESSAGE_TYPE); 1052 msg_type = udhcp_get_option(&packet, DHCP_MESSAGE_TYPE);
1038 if (state == NULL || state[0] < DHCP_MINTYPE || state[0] > DHCP_MAXTYPE) { 1053 if (!msg_type || msg_type[0] < DHCP_MINTYPE || msg_type[0] > DHCP_MAXTYPE) {
1039 bb_info_msg("no or bad message type option%s", ", ignoring packet"); 1054 bb_info_msg("no or bad message type option%s", ", ignoring packet");
1040 continue; 1055 continue;
1041 } 1056 }
@@ -1071,7 +1086,7 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv)
1071 move_from_unaligned32(requested_nip, requested_ip_opt); 1086 move_from_unaligned32(requested_nip, requested_ip_opt);
1072 } 1087 }
1073 1088
1074 switch (state[0]) { 1089 switch (msg_type[0]) {
1075 1090
1076 case DHCPDISCOVER: 1091 case DHCPDISCOVER:
1077 log1("received %s", "DISCOVER"); 1092 log1("received %s", "DISCOVER");
diff --git a/networking/udhcp/dhcprelay.c b/networking/udhcp/dhcprelay.c
index ef9447b4b..2352c34a2 100644
--- a/networking/udhcp/dhcprelay.c
+++ b/networking/udhcp/dhcprelay.c
@@ -17,7 +17,8 @@
17//usage:#define dhcprelay_trivial_usage 17//usage:#define dhcprelay_trivial_usage
18//usage: "CLIENT_IFACE[,CLIENT_IFACE2]... SERVER_IFACE [SERVER_IP]" 18//usage: "CLIENT_IFACE[,CLIENT_IFACE2]... SERVER_IFACE [SERVER_IP]"
19//usage:#define dhcprelay_full_usage "\n\n" 19//usage:#define dhcprelay_full_usage "\n\n"
20//usage: "Relay DHCP requests between clients and server" 20//usage: "Relay DHCP requests between clients and server.\n"
21//usage: "Without SERVER_IP, requests are broadcast on SERVER_IFACE."
21 22
22#include "common.h" 23#include "common.h"
23 24
@@ -31,7 +32,7 @@
31/* This list holds information about clients. The xid_* functions manipulate this list. */ 32/* This list holds information about clients. The xid_* functions manipulate this list. */
32struct xid_item { 33struct xid_item {
33 unsigned timestamp; 34 unsigned timestamp;
34 int client; 35 unsigned iface_no;
35 uint32_t xid; 36 uint32_t xid;
36 struct sockaddr_in ip; 37 struct sockaddr_in ip;
37 struct xid_item *next; 38 struct xid_item *next;
@@ -40,7 +41,7 @@ struct xid_item {
40#define dhcprelay_xid_list (*(struct xid_item*)bb_common_bufsiz1) 41#define dhcprelay_xid_list (*(struct xid_item*)bb_common_bufsiz1)
41#define INIT_G() do { setup_common_bufsiz(); } while (0) 42#define INIT_G() do { setup_common_bufsiz(); } while (0)
42 43
43static struct xid_item *xid_add(uint32_t xid, struct sockaddr_in *ip, int client) 44static struct xid_item *xid_add(uint32_t xid, struct sockaddr_in *ip, unsigned iface_no)
44{ 45{
45 struct xid_item *item; 46 struct xid_item *item;
46 47
@@ -50,7 +51,7 @@ static struct xid_item *xid_add(uint32_t xid, struct sockaddr_in *ip, int client
50 /* add xid entry */ 51 /* add xid entry */
51 item->ip = *ip; 52 item->ip = *ip;
52 item->xid = xid; 53 item->xid = xid;
53 item->client = client; 54 item->iface_no = iface_no;
54 item->timestamp = monotonic_sec(); 55 item->timestamp = monotonic_sec();
55 item->next = dhcprelay_xid_list.next; 56 item->next = dhcprelay_xid_list.next;
56 dhcprelay_xid_list.next = item; 57 dhcprelay_xid_list.next = item;
@@ -127,10 +128,10 @@ static int get_dhcp_packet_type(struct dhcp_packet *p)
127 * make_iface_list - parses client/server interface names 128 * make_iface_list - parses client/server interface names
128 * returns array 129 * returns array
129 */ 130 */
130static char **make_iface_list(char **client_and_server_ifaces, int *client_number) 131static char **make_iface_list(char **client_and_server_ifaces, unsigned *client_number)
131{ 132{
132 char *s, **iface_list; 133 char *s, **iface_list;
133 int i, cn; 134 unsigned i, cn;
134 135
135 /* get number of items */ 136 /* get number of items */
136 cn = 2; /* 1 server iface + at least 1 client one */ 137 cn = 2; /* 1 server iface + at least 1 client one */
@@ -165,9 +166,9 @@ static char **make_iface_list(char **client_and_server_ifaces, int *client_numbe
165/* Creates listen sockets (in fds) bound to client and server ifaces, 166/* Creates listen sockets (in fds) bound to client and server ifaces,
166 * and returns numerically max fd. 167 * and returns numerically max fd.
167 */ 168 */
168static int init_sockets(char **iface_list, int num_clients, int *fds) 169static unsigned init_sockets(char **iface_list, unsigned num_clients, int *fds)
169{ 170{
170 int i, n; 171 unsigned i, n;
171 172
172 n = 0; 173 n = 0;
173 for (i = 0; i < num_clients; i++) { 174 for (i = 0; i < num_clients; i++) {
@@ -195,13 +196,14 @@ static int sendto_ip4(int sock, const void *msg, int msg_len, struct sockaddr_in
195 * p - packet to send 196 * p - packet to send
196 * client - number of the client 197 * client - number of the client
197 */ 198 */
198static void pass_to_server(struct dhcp_packet *p, int packet_len, int client, int *fds, 199static void pass_to_server(struct dhcp_packet *p, int packet_len, unsigned from_iface_no, int *fds,
199 struct sockaddr_in *client_addr, struct sockaddr_in *server_addr) 200 struct sockaddr_in *client_addr, struct sockaddr_in *server_addr)
200{ 201{
201 int type; 202 int type;
202 203
203 /* check packet_type */ 204 /* check packet_type */
204 type = get_dhcp_packet_type(p); 205 type = get_dhcp_packet_type(p);
206//FIXME: the above does not consider packet_len!
205 if (type != DHCPDISCOVER && type != DHCPREQUEST 207 if (type != DHCPDISCOVER && type != DHCPREQUEST
206 && type != DHCPDECLINE && type != DHCPRELEASE 208 && type != DHCPDECLINE && type != DHCPRELEASE
207 && type != DHCPINFORM 209 && type != DHCPINFORM
@@ -210,7 +212,10 @@ static void pass_to_server(struct dhcp_packet *p, int packet_len, int client, in
210 } 212 }
211 213
212 /* create new xid entry */ 214 /* create new xid entry */
213 xid_add(p->xid, client_addr, client); 215 xid_add(p->xid, client_addr, from_iface_no);
216//TODO: since we key request/reply pairs on xid values, shouldn't we drop new requests
217//with xid accidentally matching a xid of one of requests we currently hold
218//waiting for their replies?
214 219
215 /* forward request to server */ 220 /* forward request to server */
216 /* note that we send from fds[0] which is bound to SERVER_PORT (67). 221 /* note that we send from fds[0] which is bound to SERVER_PORT (67).
@@ -229,25 +234,30 @@ static void pass_to_client(struct dhcp_packet *p, int packet_len, int *fds)
229 int type; 234 int type;
230 struct xid_item *item; 235 struct xid_item *item;
231 236
232 /* check xid */
233 item = xid_find(p->xid);
234 if (!item) {
235 return;
236 }
237
238 /* check packet type */ 237 /* check packet type */
239 type = get_dhcp_packet_type(p); 238 type = get_dhcp_packet_type(p);
239//FIXME: the above does not consider packet_len!
240 if (type != DHCPOFFER && type != DHCPACK && type != DHCPNAK) { 240 if (type != DHCPOFFER && type != DHCPACK && type != DHCPNAK) {
241 return; 241 return;
242 } 242 }
243 243
244 /* check xid */
245 item = xid_find(p->xid);
246 if (!item) {
247 return;
248 }
249//NB: RFC 1542 section 4.1 seems to envision the logic that
250//relay agents use giaddr (dhcp_msg.gateway_nip in our code)
251//to find out on which interface to reply.
252//(server is meant to copy giaddr from our request packet to its reply).
253//Above, we don't use that logic, instead we use xid as a key.
254
244//TODO: also do it if (p->flags & htons(BROADCAST_FLAG)) is set! 255//TODO: also do it if (p->flags & htons(BROADCAST_FLAG)) is set!
245 if (item->ip.sin_addr.s_addr == htonl(INADDR_ANY)) 256 if (item->ip.sin_addr.s_addr == htonl(INADDR_ANY))
246 item->ip.sin_addr.s_addr = htonl(INADDR_BROADCAST); 257 item->ip.sin_addr.s_addr = htonl(INADDR_BROADCAST);
247 258
248 if (sendto_ip4(fds[item->client], p, packet_len, &item->ip) != 0) { 259 sendto_ip4(fds[item->iface_no], p, packet_len, &item->ip);
249 return; /* send error occurred */ 260 /* ^^^ if send error occurred, we can't do much, hence no check */
250 }
251 261
252 /* remove xid entry */ 262 /* remove xid entry */
253 xid_del(p->xid); 263 xid_del(p->xid);
@@ -259,7 +269,7 @@ int dhcprelay_main(int argc UNUSED_PARAM, char **argv)
259 struct sockaddr_in server_addr; 269 struct sockaddr_in server_addr;
260 char **iface_list; 270 char **iface_list;
261 int *fds; 271 int *fds;
262 int num_sockets, max_socket; 272 unsigned num_sockets, max_socket;
263 uint32_t our_nip; 273 uint32_t our_nip;
264 274
265 INIT_G(); 275 INIT_G();
@@ -293,7 +303,7 @@ int dhcprelay_main(int argc UNUSED_PARAM, char **argv)
293// every N minutes? 303// every N minutes?
294 fd_set rfds; 304 fd_set rfds;
295 struct timeval tv; 305 struct timeval tv;
296 int i; 306 unsigned i;
297 307
298 FD_ZERO(&rfds); 308 FD_ZERO(&rfds);
299 for (i = 0; i < num_sockets; i++) 309 for (i = 0; i < num_sockets; i++)
@@ -304,15 +314,17 @@ int dhcprelay_main(int argc UNUSED_PARAM, char **argv)
304 int packlen; 314 int packlen;
305 struct dhcp_packet dhcp_msg; 315 struct dhcp_packet dhcp_msg;
306 316
307 /* server */ 317 /* from server */
308 if (FD_ISSET(fds[0], &rfds)) { 318 if (FD_ISSET(fds[0], &rfds)) {
309 packlen = udhcp_recv_kernel_packet(&dhcp_msg, fds[0]); 319 packlen = udhcp_recv_kernel_packet(&dhcp_msg, fds[0]);
320//NB: we do not check source port here. Should we?
321//It should be SERVER_PORT.
310 if (packlen > 0) { 322 if (packlen > 0) {
311 pass_to_client(&dhcp_msg, packlen, fds); 323 pass_to_client(&dhcp_msg, packlen, fds);
312 } 324 }
313 } 325 }
314 326
315 /* clients */ 327 /* from clients */
316 for (i = 1; i < num_sockets; i++) { 328 for (i = 1; i < num_sockets; i++) {
317 struct sockaddr_in client_addr; 329 struct sockaddr_in client_addr;
318 socklen_t addr_size; 330 socklen_t addr_size;
@@ -325,6 +337,11 @@ int dhcprelay_main(int argc UNUSED_PARAM, char **argv)
325 (struct sockaddr *)(&client_addr), &addr_size); 337 (struct sockaddr *)(&client_addr), &addr_size);
326 if (packlen <= 0) 338 if (packlen <= 0)
327 continue; 339 continue;
340//NB: we do not check source port here. Should we?
341//It should be CLIENT_PORT for clients.
342//It can be SERVER_PORT for relay agents (in which case giaddr must be != 0.0.0.0),
343//but is it even supported to chain relay agents like this?
344//(we still copy client_addr.port and use it to reply to the port we got request from)
328 345
329 /* Get our IP on corresponding client_iface */ 346 /* Get our IP on corresponding client_iface */
330// RFC 1542 347// RFC 1542
diff --git a/shell/ash.c b/shell/ash.c
index 508633689..7544204d1 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -329,6 +329,10 @@ typedef long arith_t;
329# define PIPE_BUF 4096 /* amount of buffering in a pipe */ 329# define PIPE_BUF 4096 /* amount of buffering in a pipe */
330#endif 330#endif
331 331
332#ifndef unlikely
333# define unlikely(cond) (cond)
334#endif
335
332#if !ENABLE_PLATFORM_MINGW32 336#if !ENABLE_PLATFORM_MINGW32
333# define is_absolute_path(path) ((path)[0] == '/') 337# define is_absolute_path(path) ((path)[0] == '/')
334#endif 338#endif
@@ -456,6 +460,7 @@ static const char *const optletters_optnames[] = {
456 "a" "allexport", 460 "a" "allexport",
457 "b" "notify", 461 "b" "notify",
458 "u" "nounset", 462 "u" "nounset",
463 "E" "errtrace",
459 "\0" "vi" 464 "\0" "vi"
460#if BASH_PIPEFAIL 465#if BASH_PIPEFAIL
461 ,"\0" "pipefail" 466 ,"\0" "pipefail"
@@ -518,6 +523,7 @@ struct globals_misc {
518#if !ENABLE_PLATFORM_MINGW32 523#if !ENABLE_PLATFORM_MINGW32
519 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */ 524 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
520#endif 525#endif
526 smallint inps4; /* Prevent PS4 nesting. */
521 int savestatus; /* exit status of last command outside traps */ 527 int savestatus; /* exit status of last command outside traps */
522 int rootpid; /* pid of main shell */ 528 int rootpid; /* pid of main shell */
523 /* shell level: 0 for the main shell, 1 for its children, and so on */ 529 /* shell level: 0 for the main shell, 1 for its children, and so on */
@@ -573,23 +579,24 @@ struct globals_misc {
573#define aflag optlist[11] 579#define aflag optlist[11]
574#define bflag optlist[12] 580#define bflag optlist[12]
575#define uflag optlist[13] 581#define uflag optlist[13]
576#define viflag optlist[14] 582#define Eflag optlist[14]
583#define viflag optlist[15]
577#if BASH_PIPEFAIL 584#if BASH_PIPEFAIL
578# define pipefail optlist[15] 585# define pipefail optlist[16]
579#else 586#else
580# define pipefail 0 587# define pipefail 0
581#endif 588#endif
582#if DEBUG 589#if DEBUG
583# define nolog optlist[15 + BASH_PIPEFAIL] 590# define nolog optlist[16 + BASH_PIPEFAIL]
584# define debug optlist[16 + BASH_PIPEFAIL] 591# define debug optlist[17 + BASH_PIPEFAIL]
585#endif 592#endif
586#if ENABLE_PLATFORM_MINGW32 593#if ENABLE_PLATFORM_MINGW32
587# define winxp optlist[15 + BASH_PIPEFAIL + 2*DEBUG] 594# define winxp optlist[16 + BASH_PIPEFAIL + 2*DEBUG]
588# if ENABLE_ASH_NOCONSOLE 595# if ENABLE_ASH_NOCONSOLE
589# define noconsole optlist[16 + BASH_PIPEFAIL + 2*DEBUG] 596# define noconsole optlist[17 + BASH_PIPEFAIL + 2*DEBUG]
590# endif 597# endif
591# if ENABLE_ASH_NOCASEGLOB 598# if ENABLE_ASH_NOCASEGLOB
592# define nocaseglob optlist[16 + BASH_PIPEFAIL + 2*DEBUG+ENABLE_ASH_NOCONSOLE] 599# define nocaseglob optlist[17 + BASH_PIPEFAIL + 2*DEBUG+ENABLE_ASH_NOCONSOLE]
593# endif 600# endif
594#endif 601#endif
595 602
@@ -610,7 +617,11 @@ struct globals_misc {
610 uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */ 617 uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */
611 uint8_t may_have_traps; /* 0: definitely no traps are set, 1: some traps may be set */ 618 uint8_t may_have_traps; /* 0: definitely no traps are set, 1: some traps may be set */
612#endif 619#endif
613 char *trap[NSIG]; 620 char *trap[NSIG + 1];
621/* trap[0] is EXIT trap, trap[NTRAP_ERR] is ERR trap, other trap[i] are signal traps */
622#define NTRAP_ERR NSIG
623#define NTRAP_LAST NSIG
624
614#if !ENABLE_PLATFORM_MINGW32 625#if !ENABLE_PLATFORM_MINGW32
615 char **trap_ptr; /* used only by "trap hack" */ 626 char **trap_ptr; /* used only by "trap hack" */
616#endif 627#endif
@@ -626,6 +637,7 @@ extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc;
626#define exitstatus (G_misc.exitstatus ) 637#define exitstatus (G_misc.exitstatus )
627#define back_exitstatus (G_misc.back_exitstatus ) 638#define back_exitstatus (G_misc.back_exitstatus )
628#define job_warning (G_misc.job_warning) 639#define job_warning (G_misc.job_warning)
640#define inps4 (G_misc.inps4 )
629#define savestatus (G_misc.savestatus ) 641#define savestatus (G_misc.savestatus )
630#define rootpid (G_misc.rootpid ) 642#define rootpid (G_misc.rootpid )
631#define shlvl (G_misc.shlvl ) 643#define shlvl (G_misc.shlvl )
@@ -742,6 +754,9 @@ struct strpush {
742#endif 754#endif
743 char *string; /* remember the string since it may change */ 755 char *string; /* remember the string since it may change */
744 756
757 /* Delay freeing so we can stop nested aliases. */
758 struct strpush *spfree;
759
745 /* Remember last two characters for pungetc. */ 760 /* Remember last two characters for pungetc. */
746 int lastc[2]; 761 int lastc[2];
747 762
@@ -764,6 +779,9 @@ struct parsefile {
764 struct strpush *strpush; /* for pushing strings at this level */ 779 struct strpush *strpush; /* for pushing strings at this level */
765 struct strpush basestrpush; /* so pushing one is fast */ 780 struct strpush basestrpush; /* so pushing one is fast */
766 781
782 /* Delay freeing so we can stop nested aliases. */
783 struct strpush *spfree;
784
767 /* Remember last two characters for pungetc. */ 785 /* Remember last two characters for pungetc. */
768 int lastc[2]; 786 int lastc[2];
769 787
@@ -2366,6 +2384,8 @@ struct globals_var {
2366 struct var varinit[ARRAY_SIZE(varinit_data)]; 2384 struct var varinit[ARRAY_SIZE(varinit_data)];
2367 int lineno; 2385 int lineno;
2368 char linenovar[sizeof("LINENO=") + sizeof(int)*3]; 2386 char linenovar[sizeof("LINENO=") + sizeof(int)*3];
2387 unsigned trap_depth;
2388 bool in_trap_ERR; /* ERR cannot recurse, no need to be a counter */
2369}; 2389};
2370extern struct globals_var *BB_GLOBAL_CONST ash_ptr_to_globals_var; 2390extern struct globals_var *BB_GLOBAL_CONST ash_ptr_to_globals_var;
2371#define G_var (*ash_ptr_to_globals_var) 2391#define G_var (*ash_ptr_to_globals_var)
@@ -2376,6 +2396,8 @@ extern struct globals_var *BB_GLOBAL_CONST ash_ptr_to_globals_var;
2376#define varinit (G_var.varinit ) 2396#define varinit (G_var.varinit )
2377#define lineno (G_var.lineno ) 2397#define lineno (G_var.lineno )
2378#define linenovar (G_var.linenovar ) 2398#define linenovar (G_var.linenovar )
2399#define trap_depth (G_var.trap_depth )
2400#define in_trap_ERR (G_var.in_trap_ERR )
2379#define vifs varinit[0] 2401#define vifs varinit[0]
2380#if ENABLE_ASH_MAIL 2402#if ENABLE_ASH_MAIL
2381# define vmail varinit[1] 2403# define vmail varinit[1]
@@ -2547,7 +2569,7 @@ lookupvar(const char *name)
2547 v->var_func(NULL); 2569 v->var_func(NULL);
2548#endif 2570#endif
2549 if (!(v->flags & VUNSET)) { 2571 if (!(v->flags & VUNSET)) {
2550 if (v == &vlineno && v->var_text == linenovar) { 2572 if (v->var_text == linenovar) {
2551 fmtstr(linenovar+7, sizeof(linenovar)-7, "%d", lineno); 2573 fmtstr(linenovar+7, sizeof(linenovar)-7, "%d", lineno);
2552 } 2574 }
2553 return var_end(v->var_text); 2575 return var_end(v->var_text);
@@ -3439,12 +3461,8 @@ pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
3439#define CENDFILE 11 /* end of file */ 3461#define CENDFILE 11 /* end of file */
3440#define CCTL 12 /* like CWORD, except it must be escaped */ 3462#define CCTL 12 /* like CWORD, except it must be escaped */
3441#define CSPCL 13 /* these terminate a word */ 3463#define CSPCL 13 /* these terminate a word */
3442#define CIGN 14 /* character should be ignored */
3443 3464
3444#define PEOF 256 3465#define PEOF 256
3445#if ENABLE_ASH_ALIAS
3446# define PEOA 257
3447#endif
3448 3466
3449#define USE_SIT_FUNCTION ENABLE_ASH_OPTIMIZE_FOR_SIZE 3467#define USE_SIT_FUNCTION ENABLE_ASH_OPTIMIZE_FOR_SIZE
3450 3468
@@ -3454,49 +3472,43 @@ pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
3454# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8)) 3472# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8))
3455#endif 3473#endif
3456static const uint16_t S_I_T[] ALIGN2 = { 3474static const uint16_t S_I_T[] ALIGN2 = {
3457#if ENABLE_ASH_ALIAS 3475 SIT_ITEM(CSPCL , CWORD , CWORD, CWORD ), /* 0, ' ' */
3458 SIT_ITEM(CSPCL , CIGN , CIGN , CIGN ), /* 0, PEOA */ 3476 SIT_ITEM(CNL , CNL , CNL , CNL ), /* 1, \n */
3459#endif 3477 SIT_ITEM(CWORD , CCTL , CCTL , CWORD ), /* 2, !*-/:=?[]~ */
3460 SIT_ITEM(CSPCL , CWORD , CWORD, CWORD ), /* 1, ' ' */ 3478 SIT_ITEM(CDQUOTE , CENDQUOTE, CWORD, CWORD ), /* 3, '"' */
3461 SIT_ITEM(CNL , CNL , CNL , CNL ), /* 2, \n */ 3479 SIT_ITEM(CVAR , CVAR , CWORD, CVAR ), /* 4, $ */
3462 SIT_ITEM(CWORD , CCTL , CCTL , CWORD ), /* 3, !*-/:=?[]~ */ 3480 SIT_ITEM(CSQUOTE , CWORD , CENDQUOTE, CWORD), /* 5, "'" */
3463 SIT_ITEM(CDQUOTE , CENDQUOTE, CWORD, CWORD ), /* 4, '"' */ 3481 SIT_ITEM(CSPCL , CWORD , CWORD, CLP ), /* 6, ( */
3464 SIT_ITEM(CVAR , CVAR , CWORD, CVAR ), /* 5, $ */ 3482 SIT_ITEM(CSPCL , CWORD , CWORD, CRP ), /* 7, ) */
3465 SIT_ITEM(CSQUOTE , CWORD , CENDQUOTE, CWORD), /* 6, "'" */ 3483 SIT_ITEM(CBACK , CBACK , CCTL , CBACK ), /* 8, \ */
3466 SIT_ITEM(CSPCL , CWORD , CWORD, CLP ), /* 7, ( */ 3484 SIT_ITEM(CBQUOTE , CBQUOTE , CWORD, CBQUOTE), /* 9, ` */
3467 SIT_ITEM(CSPCL , CWORD , CWORD, CRP ), /* 8, ) */ 3485 SIT_ITEM(CENDVAR , CENDVAR , CWORD, CENDVAR), /* 10, } */
3468 SIT_ITEM(CBACK , CBACK , CCTL , CBACK ), /* 9, \ */
3469 SIT_ITEM(CBQUOTE , CBQUOTE , CWORD, CBQUOTE), /* 10, ` */
3470 SIT_ITEM(CENDVAR , CENDVAR , CWORD, CENDVAR), /* 11, } */
3471#if !USE_SIT_FUNCTION 3486#if !USE_SIT_FUNCTION
3472 SIT_ITEM(CENDFILE, CENDFILE , CENDFILE, CENDFILE),/* 12, PEOF */ 3487 SIT_ITEM(CENDFILE, CENDFILE , CENDFILE, CENDFILE),/* 11, PEOF */
3473 SIT_ITEM(CWORD , CWORD , CWORD, CWORD ), /* 13, 0-9A-Za-z */ 3488 SIT_ITEM(CWORD , CWORD , CWORD, CWORD ), /* 12, 0-9A-Za-z */
3474 SIT_ITEM(CCTL , CCTL , CCTL , CCTL ) /* 14, CTLESC ... */ 3489 SIT_ITEM(CCTL , CCTL , CCTL , CCTL ) /* 13, CTLESC ... */
3475#endif 3490#endif
3476#undef SIT_ITEM 3491#undef SIT_ITEM
3477}; 3492};
3478/* Constants below must match table above */ 3493/* Constants below must match table above */
3479enum { 3494enum {
3480#if ENABLE_ASH_ALIAS 3495 CSPCL_CWORD_CWORD_CWORD , /* 0 */
3481 CSPCL_CIGN_CIGN_CIGN , /* 0 */ 3496 CNL_CNL_CNL_CNL , /* 1 */
3482#endif 3497 CWORD_CCTL_CCTL_CWORD , /* 2 */
3483 CSPCL_CWORD_CWORD_CWORD , /* 1 */ 3498 CDQUOTE_CENDQUOTE_CWORD_CWORD , /* 3 */
3484 CNL_CNL_CNL_CNL , /* 2 */ 3499 CVAR_CVAR_CWORD_CVAR , /* 4 */
3485 CWORD_CCTL_CCTL_CWORD , /* 3 */ 3500 CSQUOTE_CWORD_CENDQUOTE_CWORD , /* 5 */
3486 CDQUOTE_CENDQUOTE_CWORD_CWORD , /* 4 */ 3501 CSPCL_CWORD_CWORD_CLP , /* 6 */
3487 CVAR_CVAR_CWORD_CVAR , /* 5 */ 3502 CSPCL_CWORD_CWORD_CRP , /* 7 */
3488 CSQUOTE_CWORD_CENDQUOTE_CWORD , /* 6 */ 3503 CBACK_CBACK_CCTL_CBACK , /* 8 */
3489 CSPCL_CWORD_CWORD_CLP , /* 7 */ 3504 CBQUOTE_CBQUOTE_CWORD_CBQUOTE , /* 9 */
3490 CSPCL_CWORD_CWORD_CRP , /* 8 */ 3505 CENDVAR_CENDVAR_CWORD_CENDVAR , /* 10 */
3491 CBACK_CBACK_CCTL_CBACK , /* 9 */ 3506 CENDFILE_CENDFILE_CENDFILE_CENDFILE, /* 11 */
3492 CBQUOTE_CBQUOTE_CWORD_CBQUOTE , /* 10 */ 3507 CWORD_CWORD_CWORD_CWORD , /* 12 */
3493 CENDVAR_CENDVAR_CWORD_CENDVAR , /* 11 */ 3508 CCTL_CCTL_CCTL_CCTL , /* 13 */
3494 CENDFILE_CENDFILE_CENDFILE_CENDFILE, /* 12 */
3495 CWORD_CWORD_CWORD_CWORD , /* 13 */
3496 CCTL_CCTL_CCTL_CCTL , /* 14 */
3497}; 3509};
3498 3510
3499/* c in SIT(c, syntax) must be an *unsigned char* or PEOA or PEOF, 3511/* c in SIT(c, syntax) must be an *unsigned char* or PEOF,
3500 * caller must ensure proper cast on it if c is *char_ptr! 3512 * caller must ensure proper cast on it if c is *char_ptr!
3501 */ 3513 */
3502#if USE_SIT_FUNCTION 3514#if USE_SIT_FUNCTION
@@ -3514,44 +3526,28 @@ SIT(int c, int syntax)
3514 * but glibc one isn't. With '/' always treated as CWORD, 3526 * but glibc one isn't. With '/' always treated as CWORD,
3515 * both work fine. 3527 * both work fine.
3516 */ 3528 */
3517# if ENABLE_ASH_ALIAS
3518 static const uint8_t syntax_index_table[] ALIGN1 = {
3519 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
3520 7, 8, 3, 3,/*3,*/3, 1, 1, /* "()*-/:;<" */
3521 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
3522 11, 3 /* "}~" */
3523 };
3524# else
3525 static const uint8_t syntax_index_table[] ALIGN1 = { 3529 static const uint8_t syntax_index_table[] ALIGN1 = {
3526 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */ 3530 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
3527 6, 7, 2, 2,/*2,*/2, 0, 0, /* "()*-/:;<" */ 3531 6, 7, 2, 2,/*2,*/2, 0, 0, /* "()*-/:;<" */
3528 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */ 3532 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
3529 10, 2 /* "}~" */ 3533 10, 2 /* "}~" */
3530 }; 3534 };
3531# endif
3532 const char *s; 3535 const char *s;
3533 int indx; 3536 int indx;
3534 3537
3535 if (c == PEOF) 3538 if (c == PEOF)
3536 return CENDFILE; 3539 return CENDFILE;
3537# if ENABLE_ASH_ALIAS 3540 /* Cast is purely for paranoia here,
3538 if (c == PEOA) 3541 * just in case someone passed signed char to us */
3539 indx = 0; 3542 if ((unsigned char)c >= CTL_FIRST
3540 else 3543 && (unsigned char)c <= CTL_LAST
3541# endif 3544 ) {
3542 { 3545 return CCTL;
3543 /* Cast is purely for paranoia here,
3544 * just in case someone passed signed char to us */
3545 if ((unsigned char)c >= CTL_FIRST
3546 && (unsigned char)c <= CTL_LAST
3547 ) {
3548 return CCTL;
3549 }
3550 s = strchrnul(spec_symbls, c);
3551 if (*s == '\0')
3552 return CWORD;
3553 indx = syntax_index_table[s - spec_symbls];
3554 } 3546 }
3547 s = strchrnul(spec_symbls, c);
3548 if (*s == '\0')
3549 return CWORD;
3550 indx = syntax_index_table[s - spec_symbls];
3555 return (S_I_T[indx] >> (syntax*4)) & 0xf; 3551 return (S_I_T[indx] >> (syntax*4)) & 0xf;
3556} 3552}
3557 3553
@@ -3822,9 +3818,6 @@ static const uint8_t syntax_index_table[] ALIGN1 = {
3822 /* 254 */ CWORD_CWORD_CWORD_CWORD, 3818 /* 254 */ CWORD_CWORD_CWORD_CWORD,
3823 /* 255 */ CWORD_CWORD_CWORD_CWORD, 3819 /* 255 */ CWORD_CWORD_CWORD_CWORD,
3824 /* PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE, 3820 /* PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
3825# if ENABLE_ASH_ALIAS
3826 /* PEOA */ CSPCL_CIGN_CIGN_CIGN,
3827# endif
3828}; 3821};
3829 3822
3830#if 1 3823#if 1
@@ -5712,13 +5705,13 @@ clear_traps(void)
5712 char **tp; 5705 char **tp;
5713 5706
5714 INT_OFF; 5707 INT_OFF;
5715 for (tp = trap; tp < &trap[NSIG]; tp++) { 5708 for (tp = trap; tp <= &trap[NTRAP_LAST]; tp++) {
5716 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */ 5709 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
5717 if (trap_ptr == trap) 5710 if (trap_ptr == trap)
5718 free(*tp); 5711 free(*tp);
5719 /* else: it "belongs" to trap_ptr vector, don't free */ 5712 /* else: it "belongs" to trap_ptr vector, don't free */
5720 *tp = NULL; 5713 *tp = NULL;
5721 if ((tp - trap) != 0) 5714 if ((tp - trap) != 0 && (tp - trap) < NSIG)
5722 setsignal(tp - trap); 5715 setsignal(tp - trap);
5723 } 5716 }
5724 } 5717 }
@@ -10007,7 +10000,9 @@ dotrap(void)
10007 *g = 0; 10000 *g = 0;
10008 if (!p) 10001 if (!p)
10009 continue; 10002 continue;
10003 trap_depth++;
10010 evalstring(p, 0); 10004 evalstring(p, 0);
10005 trap_depth--;
10011 if (evalskip != SKIPFUNC) 10006 if (evalskip != SKIPFUNC)
10012 exitstatus = status; 10007 exitstatus = status;
10013 } 10008 }
@@ -10044,6 +10039,9 @@ evaltree(union node *n, int flags)
10044 10039
10045 setstackmark(&smark); 10040 setstackmark(&smark);
10046 10041
10042 if (nflag)
10043 goto out;
10044
10047 if (n == NULL) { 10045 if (n == NULL) {
10048 TRACE(("evaltree(NULL) called\n")); 10046 TRACE(("evaltree(NULL) called\n"));
10049 goto out; 10047 goto out;
@@ -10064,8 +10062,6 @@ evaltree(union node *n, int flags)
10064 goto setstatus; 10062 goto setstatus;
10065 case NREDIR: 10063 case NREDIR:
10066 errlinno = lineno = n->nredir.linno; 10064 errlinno = lineno = n->nredir.linno;
10067 if (funcline)
10068 lineno -= funcline - 1;
10069 expredir(n->nredir.redirect); 10065 expredir(n->nredir.redirect);
10070 pushredir(n->nredir.redirect); 10066 pushredir(n->nredir.redirect);
10071 status = redirectsafe(n->nredir.redirect, REDIR_PUSH); 10067 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
@@ -10078,8 +10074,7 @@ evaltree(union node *n, int flags)
10078 case NCMD: 10074 case NCMD:
10079 evalfn = evalcommand; 10075 evalfn = evalcommand;
10080 checkexit: 10076 checkexit:
10081 if (eflag && !(flags & EV_TESTED)) 10077 checkexit = ~flags & EV_TESTED;
10082 checkexit = ~0;
10083 goto calleval; 10078 goto calleval;
10084 case NFOR: 10079 case NFOR:
10085 evalfn = evalfor; 10080 evalfn = evalfor;
@@ -10101,7 +10096,6 @@ evaltree(union node *n, int flags)
10101 case NAND: 10096 case NAND:
10102 case NOR: 10097 case NOR:
10103 case NSEMI: { 10098 case NSEMI: {
10104
10105#if NAND + 1 != NOR 10099#if NAND + 1 != NOR
10106#error NAND + 1 != NOR 10100#error NAND + 1 != NOR
10107#endif 10101#endif
@@ -10129,8 +10123,7 @@ evaltree(union node *n, int flags)
10129 if (!status) { 10123 if (!status) {
10130 n = n->nif.ifpart; 10124 n = n->nif.ifpart;
10131 goto evaln; 10125 goto evaln;
10132 } 10126 } else if (n->nif.elsepart) {
10133 if (n->nif.elsepart) {
10134 n = n->nif.elsepart; 10127 n = n->nif.elsepart;
10135 goto evaln; 10128 goto evaln;
10136 } 10129 }
@@ -10152,10 +10145,36 @@ evaltree(union node *n, int flags)
10152 */ 10145 */
10153 dotrap(); 10146 dotrap();
10154 10147
10155 if (checkexit & status) 10148 if (checkexit && status) {
10156 raise_exception(EXEND); 10149 if (trap[NTRAP_ERR] && !in_trap_ERR) {
10157 if (flags & EV_EXIT) 10150 int err;
10151 struct jmploc *volatile savehandler = exception_handler;
10152 struct jmploc jmploc;
10153
10154 in_trap_ERR = 1;
10155 trap_depth++;
10156 err = setjmp(jmploc.loc);
10157 if (!err) {
10158 exception_handler = &jmploc;
10159 savestatus = exitstatus;
10160 evalstring(trap[NTRAP_ERR], 0);
10161 }
10162 trap_depth--;
10163 in_trap_ERR = 0;
10164
10165 exception_handler = savehandler;
10166 if (err && exception_type != EXERROR)
10167 longjmp(exception_handler->loc, 1);
10168
10169 exitstatus = savestatus;
10170 }
10171 if (eflag)
10172 goto exexit;
10173 }
10174 if (flags & EV_EXIT) {
10175 exexit:
10158 raise_exception(EXEND); 10176 raise_exception(EXEND);
10177 }
10159 10178
10160 popstackmark(&smark); 10179 popstackmark(&smark);
10161 TRACE(("leaving evaltree (no interrupts)\n")); 10180 TRACE(("leaving evaltree (no interrupts)\n"));
@@ -10221,8 +10240,6 @@ evalfor(union node *n, int flags)
10221 int status = 0; 10240 int status = 0;
10222 10241
10223 errlinno = lineno = n->ncase.linno; 10242 errlinno = lineno = n->ncase.linno;
10224 if (funcline)
10225 lineno -= funcline - 1;
10226 10243
10227 arglist.list = NULL; 10244 arglist.list = NULL;
10228 arglist.lastp = &arglist.list; 10245 arglist.lastp = &arglist.list;
@@ -10253,8 +10270,6 @@ evalcase(union node *n, int flags)
10253 int status = 0; 10270 int status = 0;
10254 10271
10255 errlinno = lineno = n->ncase.linno; 10272 errlinno = lineno = n->ncase.linno;
10256 if (funcline)
10257 lineno -= funcline - 1;
10258 10273
10259 arglist.list = NULL; 10274 arglist.list = NULL;
10260 arglist.lastp = &arglist.list; 10275 arglist.lastp = &arglist.list;
@@ -10289,8 +10304,6 @@ evalsubshell(union node *n, int flags)
10289 int status; 10304 int status;
10290 10305
10291 errlinno = lineno = n->nredir.linno; 10306 errlinno = lineno = n->nredir.linno;
10292 if (funcline)
10293 lineno -= funcline - 1;
10294 10307
10295 expredir(n->nredir.redirect); 10308 expredir(n->nredir.redirect);
10296 if (!backgnd && (flags & EV_EXIT) && !may_have_traps) 10309 if (!backgnd && (flags & EV_EXIT) && !may_have_traps)
@@ -10639,8 +10652,15 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags)
10639 struct jmploc *volatile savehandler; 10652 struct jmploc *volatile savehandler;
10640 struct jmploc jmploc; 10653 struct jmploc jmploc;
10641 int e; 10654 int e;
10655 int savelineno;
10642 int savefuncline; 10656 int savefuncline;
10657 char *savetrap = NULL;
10643 10658
10659 if (!Eflag) {
10660 savetrap = trap[NTRAP_ERR];
10661 trap[NTRAP_ERR] = NULL;
10662 }
10663 savelineno = lineno;
10644 saveparam = shellparam; 10664 saveparam = shellparam;
10645 savefuncline = funcline; 10665 savefuncline = funcline;
10646 savehandler = exception_handler; 10666 savehandler = exception_handler;
@@ -10663,7 +10683,14 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags)
10663 evaltree(func->n.ndefun.body, flags & EV_TESTED); 10683 evaltree(func->n.ndefun.body, flags & EV_TESTED);
10664 funcdone: 10684 funcdone:
10665 INT_OFF; 10685 INT_OFF;
10686 if (savetrap) {
10687 if (!trap[NTRAP_ERR])
10688 trap[NTRAP_ERR] = savetrap;
10689 else
10690 free(savetrap);
10691 }
10666 funcline = savefuncline; 10692 funcline = savefuncline;
10693 lineno = savelineno;
10667 freefunc(func); 10694 freefunc(func);
10668 freeparam(&shellparam); 10695 freeparam(&shellparam);
10669 shellparam = saveparam; 10696 shellparam = saveparam;
@@ -11076,11 +11103,12 @@ evalcommand(union node *cmd, int flags)
11076 int vlocal; 11103 int vlocal;
11077 11104
11078 errlinno = lineno = cmd->ncmd.linno; 11105 errlinno = lineno = cmd->ncmd.linno;
11079 if (funcline)
11080 lineno -= funcline - 1;
11081 11106
11082 /* First expand the arguments. */ 11107 /* First expand the arguments. */
11083 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags)); 11108 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
11109#if BASH_PROCESS_SUBST
11110 redir_stop = redirlist;
11111#endif
11084 file_stop = g_parsefile; 11112 file_stop = g_parsefile;
11085 back_exitstatus = 0; 11113 back_exitstatus = 0;
11086 11114
@@ -11159,7 +11187,11 @@ evalcommand(union node *cmd, int flags)
11159 lastarg = nargv[-1]; 11187 lastarg = nargv[-1];
11160 11188
11161 expredir(cmd->ncmd.redirect); 11189 expredir(cmd->ncmd.redirect);
11190#if !BASH_PROCESS_SUBST
11162 redir_stop = pushredir(cmd->ncmd.redirect); 11191 redir_stop = pushredir(cmd->ncmd.redirect);
11192#else
11193 pushredir(cmd->ncmd.redirect);
11194#endif
11163 preverrout_fd = 2; 11195 preverrout_fd = 2;
11164 if (BASH_XTRACEFD && xflag) { 11196 if (BASH_XTRACEFD && xflag) {
11165 /* NB: bash closes fd == $BASH_XTRACEFD when it is changed. 11197 /* NB: bash closes fd == $BASH_XTRACEFD when it is changed.
@@ -11196,10 +11228,12 @@ evalcommand(union node *cmd, int flags)
11196 } 11228 }
11197 11229
11198 /* Print the command if xflag is set. */ 11230 /* Print the command if xflag is set. */
11199 if (xflag) { 11231 if (xflag && !inps4) {
11200 const char *pfx = ""; 11232 const char *pfx = "";
11201 11233
11234 inps4 = 1;
11202 fdprintf(preverrout_fd, "%s", expandstr(ps4val(), DQSYNTAX)); 11235 fdprintf(preverrout_fd, "%s", expandstr(ps4val(), DQSYNTAX));
11236 inps4 = 0;
11203 11237
11204 sp = varlist.list; 11238 sp = varlist.list;
11205 while (sp) { 11239 while (sp) {
@@ -11483,7 +11517,7 @@ pushstring(char *s, struct alias *ap)
11483 11517
11484 len = strlen(s); 11518 len = strlen(s);
11485 INT_OFF; 11519 INT_OFF;
11486 if (g_parsefile->strpush) { 11520 if (g_parsefile->strpush || g_parsefile->spfree) {
11487 sp = ckzalloc(sizeof(*sp)); 11521 sp = ckzalloc(sizeof(*sp));
11488 sp->prev = g_parsefile->strpush; 11522 sp->prev = g_parsefile->strpush;
11489 } else { 11523 } else {
@@ -11493,6 +11527,7 @@ pushstring(char *s, struct alias *ap)
11493 sp->prev_string = g_parsefile->next_to_pgetc; 11527 sp->prev_string = g_parsefile->next_to_pgetc;
11494 sp->prev_left_in_line = g_parsefile->left_in_line; 11528 sp->prev_left_in_line = g_parsefile->left_in_line;
11495 sp->unget = g_parsefile->unget; 11529 sp->unget = g_parsefile->unget;
11530 sp->spfree = g_parsefile->spfree;
11496 memcpy(sp->lastc, g_parsefile->lastc, sizeof(sp->lastc)); 11531 memcpy(sp->lastc, g_parsefile->lastc, sizeof(sp->lastc));
11497#if ENABLE_ASH_ALIAS 11532#if ENABLE_ASH_ALIAS
11498 sp->ap = ap; 11533 sp->ap = ap;
@@ -11504,11 +11539,11 @@ pushstring(char *s, struct alias *ap)
11504 g_parsefile->next_to_pgetc = s; 11539 g_parsefile->next_to_pgetc = s;
11505 g_parsefile->left_in_line = len; 11540 g_parsefile->left_in_line = len;
11506 g_parsefile->unget = 0; 11541 g_parsefile->unget = 0;
11542 g_parsefile->spfree = NULL;
11507 INT_ON; 11543 INT_ON;
11508} 11544}
11509 11545
11510static void 11546static void popstring(void)
11511popstring(void)
11512{ 11547{
11513 struct strpush *sp = g_parsefile->strpush; 11548 struct strpush *sp = g_parsefile->strpush;
11514 11549
@@ -11523,10 +11558,6 @@ popstring(void)
11523 if (sp->string != sp->ap->val) { 11558 if (sp->string != sp->ap->val) {
11524 free(sp->string); 11559 free(sp->string);
11525 } 11560 }
11526 sp->ap->flag &= ~ALIASINUSE;
11527 if (sp->ap->flag & ALIASDEAD) {
11528 unalias(sp->ap->name);
11529 }
11530 } 11561 }
11531#endif 11562#endif
11532 g_parsefile->next_to_pgetc = sp->prev_string; 11563 g_parsefile->next_to_pgetc = sp->prev_string;
@@ -11534,8 +11565,7 @@ popstring(void)
11534 g_parsefile->unget = sp->unget; 11565 g_parsefile->unget = sp->unget;
11535 memcpy(g_parsefile->lastc, sp->lastc, sizeof(sp->lastc)); 11566 memcpy(g_parsefile->lastc, sp->lastc, sizeof(sp->lastc));
11536 g_parsefile->strpush = sp->prev; 11567 g_parsefile->strpush = sp->prev;
11537 if (sp != &(g_parsefile->basestrpush)) 11568 g_parsefile->spfree = sp;
11538 free(sp);
11539 INT_ON; 11569 INT_ON;
11540} 11570}
11541 11571
@@ -11628,26 +11658,16 @@ preadfd(void)
11628 */ 11658 */
11629//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__) 11659//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__)
11630#define pgetc_debug(...) ((void)0) 11660#define pgetc_debug(...) ((void)0)
11631static int pgetc(void); 11661static int __pgetc(void);
11632static int 11662static int
11633preadbuffer(void) 11663preadbuffer(void)
11634{ 11664{
11635 char *q; 11665 char *q;
11636 int more; 11666 int more;
11637 11667
11638 if (g_parsefile->strpush) { 11668 if (unlikely(g_parsefile->strpush)) {
11639#if ENABLE_ASH_ALIAS
11640 if (g_parsefile->left_in_line == -1
11641 && g_parsefile->strpush->ap
11642 && g_parsefile->next_to_pgetc[-1] != ' '
11643 && g_parsefile->next_to_pgetc[-1] != '\t'
11644 ) {
11645 pgetc_debug("preadbuffer PEOA");
11646 return PEOA;
11647 }
11648#endif
11649 popstring(); 11669 popstring();
11650 return pgetc(); 11670 return __pgetc();
11651 } 11671 }
11652 /* on both branches above g_parsefile->left_in_line < 0. 11672 /* on both branches above g_parsefile->left_in_line < 0.
11653 * "pgetc" needs refilling. 11673 * "pgetc" needs refilling.
@@ -11729,18 +11749,43 @@ preadbuffer(void)
11729static void 11749static void
11730nlprompt(void) 11750nlprompt(void)
11731{ 11751{
11732 g_parsefile->linno++; 11752 if (trap_depth == 0)
11753 g_parsefile->linno++;
11733 setprompt_if(doprompt, 2); 11754 setprompt_if(doprompt, 2);
11734} 11755}
11735static void 11756static void
11736nlnoprompt(void) 11757nlnoprompt(void)
11737{ 11758{
11738 g_parsefile->linno++; 11759 if (trap_depth == 0)
11760 g_parsefile->linno++;
11739 needprompt = doprompt; 11761 needprompt = doprompt;
11740} 11762}
11741 11763
11742static int 11764static void freestrings(struct strpush *sp)
11743pgetc(void) 11765{
11766 INT_OFF;
11767 do {
11768 struct strpush *psp;
11769
11770 if (sp->ap) {
11771 sp->ap->flag &= ~ALIASINUSE;
11772 if (sp->ap->flag & ALIASDEAD) {
11773 unalias(sp->ap->name);
11774 }
11775 }
11776
11777 psp = sp;
11778 sp = sp->spfree;
11779
11780 if (psp != &(g_parsefile->basestrpush))
11781 free(psp);
11782 } while (sp);
11783
11784 g_parsefile->spfree = NULL;
11785 INT_ON;
11786}
11787
11788static int __pgetc(void)
11744{ 11789{
11745 int c; 11790 int c;
11746 11791
@@ -11762,23 +11807,19 @@ pgetc(void)
11762 return c; 11807 return c;
11763} 11808}
11764 11809
11765#if ENABLE_ASH_ALIAS 11810/*
11766static int 11811 * Read a character from the script, returning PEOF on end of file.
11767pgetc_without_PEOA(void) 11812 * Nul characters in the input are silently discarded.
11813 */
11814static int pgetc(void)
11768{ 11815{
11769 int c; 11816 struct strpush *sp = g_parsefile->spfree;
11770 do { 11817
11771 pgetc_debug("pgetc at %d:%p'%s'", 11818 if (unlikely(sp))
11772 g_parsefile->left_in_line, 11819 freestrings(sp);
11773 g_parsefile->next_to_pgetc, 11820
11774 g_parsefile->next_to_pgetc); 11821 return __pgetc();
11775 c = pgetc();
11776 } while (c == PEOA);
11777 return c;
11778} 11822}
11779#else
11780# define pgetc_without_PEOA() pgetc()
11781#endif
11782 11823
11783/* 11824/*
11784 * Undo a call to pgetc. Only two characters may be pushed back. 11825 * Undo a call to pgetc. Only two characters may be pushed back.
@@ -11855,6 +11896,7 @@ pushfile(void)
11855 pf->prev = g_parsefile; 11896 pf->prev = g_parsefile;
11856 pf->pf_fd = -1; 11897 pf->pf_fd = -1;
11857 /*pf->strpush = NULL; - ckzalloc did it */ 11898 /*pf->strpush = NULL; - ckzalloc did it */
11899 /*pf->spfree = NULL;*/
11858 /*pf->basestrpush.prev = NULL;*/ 11900 /*pf->basestrpush.prev = NULL;*/
11859 /*pf->unget = 0;*/ 11901 /*pf->unget = 0;*/
11860 g_parsefile = pf; 11902 g_parsefile = pf;
@@ -11872,8 +11914,12 @@ popfile(void)
11872 if (pf->pf_fd >= 0) 11914 if (pf->pf_fd >= 0)
11873 close(pf->pf_fd); 11915 close(pf->pf_fd);
11874 free(pf->buf); 11916 free(pf->buf);
11875 while (pf->strpush) 11917 if (g_parsefile->spfree)
11918 freestrings(g_parsefile->spfree);
11919 while (pf->strpush) {
11876 popstring(); 11920 popstring();
11921 freestrings(g_parsefile->spfree);
11922 }
11877 g_parsefile = pf->prev; 11923 g_parsefile = pf->prev;
11878 free(pf); 11924 free(pf);
11879 INT_ON; 11925 INT_ON;
@@ -11969,7 +12015,7 @@ setinputstring(char *string)
11969 g_parsefile->next_to_pgetc = string; 12015 g_parsefile->next_to_pgetc = string;
11970 g_parsefile->left_in_line = strlen(string); 12016 g_parsefile->left_in_line = strlen(string);
11971 g_parsefile->buf = NULL; 12017 g_parsefile->buf = NULL;
11972 g_parsefile->linno = 1; 12018 g_parsefile->linno = lineno;
11973 INT_ON; 12019 INT_ON;
11974} 12020}
11975 12021
@@ -12553,27 +12599,28 @@ static union node *andor(void);
12553static union node *pipeline(void); 12599static union node *pipeline(void);
12554static union node *parse_command(void); 12600static union node *parse_command(void);
12555static void parseheredoc(void); 12601static void parseheredoc(void);
12556static int peektoken(void);
12557static int readtoken(void); 12602static int readtoken(void);
12558 12603
12559static union node * 12604static union node *
12560list(int nlflag) 12605list(int nlflag)
12561{ 12606{
12607 int chknl = nlflag & 1 ? 0 : CHKNL;
12562 union node *n1, *n2, *n3; 12608 union node *n1, *n2, *n3;
12563 int tok; 12609 int tok;
12564 12610
12565 n1 = NULL; 12611 n1 = NULL;
12566 for (;;) { 12612 for (;;) {
12567 switch (readtoken()) { 12613 checkkwd = chknl | CHKKWD | CHKALIAS;
12614 tok = readtoken();
12615 switch (tok) {
12568 case TNL: 12616 case TNL:
12569 if (!(nlflag & 1))
12570 break;
12571 parseheredoc(); 12617 parseheredoc();
12572 return n1; 12618 return n1;
12573 12619
12574 case TEOF: 12620 case TEOF:
12575 if (!n1 && (nlflag & 1)) 12621 if (!n1 && !chknl)
12576 n1 = NODE_EOF; 12622 n1 = NODE_EOF;
12623 out_eof:
12577 parseheredoc(); 12624 parseheredoc();
12578 tokpushback++; 12625 tokpushback++;
12579 lasttoken = TEOF; 12626 lasttoken = TEOF;
@@ -12581,8 +12628,7 @@ list(int nlflag)
12581 } 12628 }
12582 12629
12583 tokpushback++; 12630 tokpushback++;
12584 checkkwd = CHKNL | CHKKWD | CHKALIAS; 12631 if (nlflag == 2 && ((1 << tok) & tokendlist))
12585 if (nlflag == 2 && ((1 << peektoken()) & tokendlist))
12586 return n1; 12632 return n1;
12587 nlflag |= 2; 12633 nlflag |= 2;
12588 12634
@@ -12611,15 +12657,16 @@ list(int nlflag)
12611 n1 = n3; 12657 n1 = n3;
12612 } 12658 }
12613 switch (tok) { 12659 switch (tok) {
12614 case TNL:
12615 case TEOF: 12660 case TEOF:
12661 goto out_eof;
12662 case TNL:
12616 tokpushback = 1; 12663 tokpushback = 1;
12617 /* fall through */ 12664 /* fall through */
12618 case TBACKGND: 12665 case TBACKGND:
12619 case TSEMI: 12666 case TSEMI:
12620 break; 12667 break;
12621 default: 12668 default:
12622 if ((nlflag & 1)) 12669 if (!chknl)
12623 raise_error_unexpected_syntax(-1); 12670 raise_error_unexpected_syntax(-1);
12624 tokpushback = 1; 12671 tokpushback = 1;
12625 return n1; 12672 return n1;
@@ -12793,8 +12840,9 @@ simplecmd(void)
12793 switch (t) { 12840 switch (t) {
12794#if BASH_FUNCTION 12841#if BASH_FUNCTION
12795 case TFUNCTION: 12842 case TFUNCTION:
12796 if (peektoken() != TWORD) 12843 if (readtoken() != TWORD)
12797 raise_error_unexpected_syntax(TWORD); 12844 raise_error_unexpected_syntax(TWORD);
12845 tokpushback = 1;
12798 function_flag = 1; 12846 function_flag = 1;
12799 break; 12847 break;
12800#endif 12848#endif
@@ -12831,7 +12879,9 @@ simplecmd(void)
12831#if BASH_FUNCTION 12879#if BASH_FUNCTION
12832 if (function_flag) { 12880 if (function_flag) {
12833 checkkwd = CHKNL | CHKKWD; 12881 checkkwd = CHKNL | CHKKWD;
12834 switch (peektoken()) { 12882 t = readtoken();
12883 tokpushback = 1;
12884 switch (t) {
12835 case TBEGIN: 12885 case TBEGIN:
12836 case TIF: 12886 case TIF:
12837 case TCASE: 12887 case TCASE:
@@ -13184,7 +13234,7 @@ static int
13184readtoken1(int c, int syntax, char *eofmark, int striptabs) 13234readtoken1(int c, int syntax, char *eofmark, int striptabs)
13185{ 13235{
13186 /* NB: syntax parameter fits into smallint */ 13236 /* NB: syntax parameter fits into smallint */
13187 /* c parameter is an unsigned char or PEOF or PEOA */ 13237 /* c parameter is an unsigned char or PEOF */
13188 char *out; 13238 char *out;
13189 size_t len; 13239 size_t len;
13190 struct nodelist *bqlist; 13240 struct nodelist *bqlist;
@@ -13254,7 +13304,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
13254 USTPUTC(c, out); 13304 USTPUTC(c, out);
13255 break; 13305 break;
13256 case CBACK: /* backslash */ 13306 case CBACK: /* backslash */
13257 c = pgetc_without_PEOA(); 13307 c = pgetc();
13258 if (c == PEOF) { 13308 if (c == PEOF) {
13259 USTPUTC(CTLESC, out); 13309 USTPUTC(CTLESC, out);
13260 USTPUTC('\\', out); 13310 USTPUTC('\\', out);
@@ -13361,8 +13411,6 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
13361 break; 13411 break;
13362 case CENDFILE: 13412 case CENDFILE:
13363 goto endword; /* exit outer loop */ 13413 goto endword; /* exit outer loop */
13364 case CIGN:
13365 break;
13366 default: 13414 default:
13367 if (synstack->varnest == 0) { 13415 if (synstack->varnest == 0) {
13368#if BASH_REDIR_OUTPUT 13416#if BASH_REDIR_OUTPUT
@@ -13384,8 +13432,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
13384#endif 13432#endif
13385 goto endword; /* exit outer loop */ 13433 goto endword; /* exit outer loop */
13386 } 13434 }
13387 IF_ASH_ALIAS(if (c != PEOA)) 13435 USTPUTC(c, out);
13388 USTPUTC(c, out);
13389 } 13436 }
13390 c = pgetc_top(synstack); 13437 c = pgetc_top(synstack);
13391 } /* for (;;) */ 13438 } /* for (;;) */
@@ -13436,14 +13483,9 @@ checkend: {
13436 int markloc; 13483 int markloc;
13437 char *p; 13484 char *p;
13438 13485
13439#if ENABLE_ASH_ALIAS
13440 if (c == PEOA)
13441 c = pgetc_without_PEOA();
13442#endif
13443 if (striptabs) { 13486 if (striptabs) {
13444 while (c == '\t') { 13487 while (c == '\t')
13445 c = pgetc_without_PEOA(); 13488 c = pgetc();
13446 }
13447 } 13489 }
13448 13490
13449 markloc = out - (char *)stackblock(); 13491 markloc = out - (char *)stackblock();
@@ -13457,12 +13499,13 @@ checkend: {
13457 * F 13499 * F
13458 * (see heredoc_bkslash_newline2.tests) 13500 * (see heredoc_bkslash_newline2.tests)
13459 */ 13501 */
13460 c = pgetc_without_PEOA(); 13502 c = pgetc();
13461 } 13503 }
13462 13504
13463 if (c == '\n' || c == PEOF) { 13505 if (c == '\n' || c == PEOF) {
13464 c = PEOF; 13506 c = PEOF;
13465 g_parsefile->linno++; 13507 if (trap_depth == 0)
13508 g_parsefile->linno++;
13466 needprompt = doprompt; 13509 needprompt = doprompt;
13467 } else { 13510 } else {
13468 int len_here; 13511 int len_here;
@@ -13581,7 +13624,6 @@ parsesub: {
13581 13624
13582 c = pgetc_eatbnl(); 13625 c = pgetc_eatbnl();
13583 if ((checkkwd & CHKEOFMARK) 13626 if ((checkkwd & CHKEOFMARK)
13584 || c > 255 /* PEOA or PEOF */
13585 || (c != '(' && c != '{' && !is_name(c) && !is_special(c)) 13627 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
13586 ) { 13628 ) {
13587#if BASH_DOLLAR_SQUOTE 13629#if BASH_DOLLAR_SQUOTE
@@ -13604,7 +13646,7 @@ parsesub: {
13604 PARSEBACKQNEW(); 13646 PARSEBACKQNEW();
13605 } 13647 }
13606 } else { 13648 } else {
13607 /* $VAR, $<specialchar>, ${...}, or PEOA/PEOF */ 13649 /* $VAR, $<specialchar>, ${...}, or PEOF */
13608 smalluint newsyn = synstack->syntax; 13650 smalluint newsyn = synstack->syntax;
13609 13651
13610 USTPUTC(CTLVAR, out); 13652 USTPUTC(CTLVAR, out);
@@ -13799,13 +13841,9 @@ parsebackq: {
13799 ) { 13841 ) {
13800 STPUTC('\\', pout); 13842 STPUTC('\\', pout);
13801 } 13843 }
13802 if (pc <= 255 /* not PEOA or PEOF */) { 13844 break;
13803 break;
13804 }
13805 /* fall through */
13806 13845
13807 case PEOF: 13846 case PEOF:
13808 IF_ASH_ALIAS(case PEOA:)
13809 raise_error_syntax("EOF in backquote substitution"); 13847 raise_error_syntax("EOF in backquote substitution");
13810 13848
13811 case '\n': 13849 case '\n':
@@ -13940,7 +13978,7 @@ xxreadtoken(void)
13940 setprompt_if(needprompt, 2); 13978 setprompt_if(needprompt, 2);
13941 for (;;) { /* until token or start of word found */ 13979 for (;;) { /* until token or start of word found */
13942 c = pgetc_eatbnl(); 13980 c = pgetc_eatbnl();
13943 if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA)) 13981 if (c == ' ' || c == '\t')
13944 continue; 13982 continue;
13945 13983
13946 if (c == '#') { 13984 if (c == '#') {
@@ -13998,7 +14036,6 @@ xxreadtoken(void)
13998 c = pgetc_eatbnl(); 14036 c = pgetc_eatbnl();
13999 switch (c) { 14037 switch (c) {
14000 case ' ': case '\t': 14038 case ' ': case '\t':
14001 IF_ASH_ALIAS(case PEOA:)
14002 continue; 14039 continue;
14003 case '#': 14040 case '#':
14004 while ((c = pgetc()) != '\n' && c != PEOF) 14041 while ((c = pgetc()) != '\n' && c != PEOF)
@@ -14058,10 +14095,14 @@ readtoken(void)
14058 if (kwd & CHKNL) { 14095 if (kwd & CHKNL) {
14059 while (t == TNL) { 14096 while (t == TNL) {
14060 parseheredoc(); 14097 parseheredoc();
14098 checkkwd = 0;
14061 t = xxreadtoken(); 14099 t = xxreadtoken();
14062 } 14100 }
14063 } 14101 }
14064 14102
14103 kwd |= checkkwd;
14104 checkkwd = 0;
14105
14065 if (t != TWORD || quoteflag) { 14106 if (t != TWORD || quoteflag) {
14066 goto out; 14107 goto out;
14067 } 14108 }
@@ -14080,7 +14121,7 @@ readtoken(void)
14080 } 14121 }
14081 } 14122 }
14082 14123
14083 if (checkkwd & CHKALIAS) { 14124 if (kwd & CHKALIAS) {
14084#if ENABLE_ASH_ALIAS 14125#if ENABLE_ASH_ALIAS
14085 struct alias *ap; 14126 struct alias *ap;
14086 ap = lookupalias(wordtext, 1); 14127 ap = lookupalias(wordtext, 1);
@@ -14093,7 +14134,6 @@ readtoken(void)
14093#endif 14134#endif
14094 } 14135 }
14095 out: 14136 out:
14096 checkkwd = 0;
14097#if DEBUG 14137#if DEBUG
14098 if (!alreadyseen) 14138 if (!alreadyseen)
14099 TRACE(("token '%s' %s\n", tokname_array[t], t == TWORD ? wordtext : "")); 14139 TRACE(("token '%s' %s\n", tokname_array[t], t == TWORD ? wordtext : ""));
@@ -14103,16 +14143,6 @@ readtoken(void)
14103 return t; 14143 return t;
14104} 14144}
14105 14145
14106static int
14107peektoken(void)
14108{
14109 int t;
14110
14111 t = readtoken();
14112 tokpushback = 1;
14113 return t;
14114}
14115
14116/* 14146/*
14117 * Read and parse a command. Returns NODE_EOF on end of file. 14147 * Read and parse a command. Returns NODE_EOF on end of file.
14118 * (NULL is a valid parse tree indicating a blank line.) 14148 * (NULL is a valid parse tree indicating a blank line.)
@@ -14162,23 +14192,26 @@ parseheredoc(void)
14162static const char * 14192static const char *
14163expandstr(const char *ps, int syntax_type) 14193expandstr(const char *ps, int syntax_type)
14164{ 14194{
14165 union node n; 14195 struct parsefile *file_stop;
14196 struct jmploc *volatile savehandler;
14197 struct heredoc *saveheredoclist;
14198 const char *result;
14166 int saveprompt; 14199 int saveprompt;
14167 struct parsefile *file_stop = g_parsefile;
14168 volatile int saveint;
14169 struct jmploc *volatile savehandler = exception_handler;
14170 struct jmploc jmploc; 14200 struct jmploc jmploc;
14171 const char *volatile result; 14201 union node n;
14172 int err; 14202 int err;
14173 14203
14204 file_stop = g_parsefile;
14205
14174 /* XXX Fix (char *) cast. */ 14206 /* XXX Fix (char *) cast. */
14175 setinputstring((char *)ps); 14207 setinputstring((char *)ps);
14176 14208
14209 saveheredoclist = heredoclist;
14210 heredoclist = NULL;
14177 saveprompt = doprompt; 14211 saveprompt = doprompt;
14178 doprompt = 0; 14212 doprompt = 0;
14179 result = ps; 14213 result = ps;
14180 14214 savehandler = exception_handler;
14181 SAVE_INT(saveint);
14182 err = setjmp(jmploc.loc); 14215 err = setjmp(jmploc.loc);
14183 if (err) 14216 if (err)
14184 goto out; 14217 goto out;
@@ -14188,7 +14221,7 @@ expandstr(const char *ps, int syntax_type)
14188 * PS1='$(date "+%H:%M:%S) > ' 14221 * PS1='$(date "+%H:%M:%S) > '
14189 */ 14222 */
14190 exception_handler = &jmploc; 14223 exception_handler = &jmploc;
14191 readtoken1(pgetc(), syntax_type, FAKEEOFMARK, 0); 14224 readtoken1(pgetc_eatbnl(), syntax_type, FAKEEOFMARK, 0);
14192 14225
14193 n.narg.type = NARG; 14226 n.narg.type = NARG;
14194 n.narg.next = NULL; 14227 n.narg.next = NULL;
@@ -14205,11 +14238,11 @@ out:
14205 exception_handler = savehandler; 14238 exception_handler = savehandler;
14206 if (err && exception_type != EXERROR) 14239 if (err && exception_type != EXERROR)
14207 longjmp(exception_handler->loc, 1); 14240 longjmp(exception_handler->loc, 1);
14208 RESTORE_INT(saveint);
14209 14241
14210 doprompt = saveprompt; 14242 doprompt = saveprompt;
14211 /* Try: PS1='`xxx(`' */ 14243 /* Try: PS1='`xxx(`' */
14212 unwindfiles(file_stop); 14244 unwindfiles(file_stop);
14245 heredoclist = saveheredoclist;
14213 14246
14214 return result; 14247 return result;
14215} 14248}
@@ -14329,9 +14362,6 @@ cmdloop(int top)
14329 if (doing_jobctl) 14362 if (doing_jobctl)
14330 showjobs(SHOW_CHANGED|SHOW_STDERR); 14363 showjobs(SHOW_CHANGED|SHOW_STDERR);
14331#endif 14364#endif
14332#if BASH_PROCESS_SUBST
14333 unwindredir(NULL);
14334#endif
14335 inter = 0; 14365 inter = 0;
14336 if (iflag && top) { 14366 if (iflag && top) {
14337 inter++; 14367 inter++;
@@ -14355,7 +14385,7 @@ cmdloop(int top)
14355 out2str("\nUse \"exit\" to leave shell.\n"); 14385 out2str("\nUse \"exit\" to leave shell.\n");
14356 } 14386 }
14357 numeof++; 14387 numeof++;
14358 } else if (nflag == 0) { 14388 } else {
14359 int i; 14389 int i;
14360 14390
14361#if !ENABLE_PLATFORM_MINGW32 14391#if !ENABLE_PLATFORM_MINGW32
@@ -14746,7 +14776,7 @@ trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
14746 nextopt(nullstr); 14776 nextopt(nullstr);
14747 ap = argptr; 14777 ap = argptr;
14748 if (!*ap) { 14778 if (!*ap) {
14749 for (signo = 0; signo < NSIG; signo++) { 14779 for (signo = 0; signo <= NTRAP_LAST; signo++) {
14750 char *tr = trap_ptr[signo]; 14780 char *tr = trap_ptr[signo];
14751 if (tr) { 14781 if (tr) {
14752 /* note: bash adds "SIG", but only if invoked 14782 /* note: bash adds "SIG", but only if invoked
@@ -14755,7 +14785,7 @@ trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
14755 * We are printing short names: */ 14785 * We are printing short names: */
14756 out1fmt("trap -- %s %s\n", 14786 out1fmt("trap -- %s %s\n",
14757 single_quote(tr), 14787 single_quote(tr),
14758 get_signame(signo)); 14788 (signo == NTRAP_ERR) ? "ERR" : get_signame(signo));
14759 /* trap_ptr != trap only if we are in special-cased `trap` code. 14789 /* trap_ptr != trap only if we are in special-cased `trap` code.
14760 * In this case, we will exit very soon, no need to free(). */ 14790 * In this case, we will exit very soon, no need to free(). */
14761 /* if (trap_ptr != trap && tp[0]) */ 14791 /* if (trap_ptr != trap && tp[0]) */
@@ -14781,7 +14811,7 @@ trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
14781 14811
14782 exitcode = 0; 14812 exitcode = 0;
14783 while (*ap) { 14813 while (*ap) {
14784 signo = get_signum(*ap); 14814 signo = strcmp(*ap, "ERR") == 0 ? NTRAP_ERR : get_signum(*ap);
14785 if (signo < 0) { 14815 if (signo < 0) {
14786 /* Mimic bash message exactly */ 14816 /* Mimic bash message exactly */
14787 ash_msg("%s: invalid signal specification", *ap); 14817 ash_msg("%s: invalid signal specification", *ap);
@@ -14802,7 +14832,7 @@ trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
14802 } 14832 }
14803 free(trap[signo]); 14833 free(trap[signo]);
14804 trap[signo] = action; 14834 trap[signo] = action;
14805 if (signo != 0) 14835 if (signo != 0 && signo < NSIG)
14806 setsignal(signo); 14836 setsignal(signo);
14807 INT_ON; 14837 INT_ON;
14808 next: 14838 next:
@@ -15197,6 +15227,7 @@ exitreset(void)
15197 } 15227 }
15198 evalskip = 0; 15228 evalskip = 0;
15199 loopnest = 0; 15229 loopnest = 0;
15230 inps4 = 0;
15200 15231
15201 /* from expand.c: */ 15232 /* from expand.c: */
15202 ifsfree(); 15233 ifsfree();
@@ -15244,7 +15275,9 @@ exitshell(void)
15244 if (p) { 15275 if (p) {
15245 trap[0] = NULL; 15276 trap[0] = NULL;
15246 evalskip = 0; 15277 evalskip = 0;
15278 trap_depth++;
15247 evalstring(p, 0); 15279 evalstring(p, 0);
15280 trap_depth--;
15248 evalskip = SKIPFUNCDEF; 15281 evalskip = SKIPFUNCDEF;
15249 /*free(p); - we'll exit soon */ 15282 /*free(p); - we'll exit soon */
15250 } 15283 }
@@ -15704,6 +15737,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
15704 // ^^ not necessary since now we special-case fd 0 15737 // ^^ not necessary since now we special-case fd 0
15705 // in save_fd_on_redirect() 15738 // in save_fd_on_redirect()
15706 15739
15740 lineno = 0; // bash compat
15707 // dash: evalstring(minusc, sflag ? 0 : EV_EXIT); 15741 // dash: evalstring(minusc, sflag ? 0 : EV_EXIT);
15708 // The above makes 15742 // The above makes
15709 // ash -sc 'echo $-' 15743 // ash -sc 'echo $-'
diff --git a/shell/ash_test/ash-alias/alias_brace.right b/shell/ash_test/ash-alias/alias_brace.right
new file mode 100644
index 000000000..7326d9603
--- /dev/null
+++ b/shell/ash_test/ash-alias/alias_brace.right
@@ -0,0 +1 @@
Ok
diff --git a/shell/ash_test/ash-alias/alias_brace.tests b/shell/ash_test/ash-alias/alias_brace.tests
new file mode 100755
index 000000000..7571b64ac
--- /dev/null
+++ b/shell/ash_test/ash-alias/alias_brace.tests
@@ -0,0 +1,16 @@
1# Note: bash would need:
2#shopt -s expand_aliases
3# to enable aliases in non-interactive mode
4alias BEGIN={ END=}
5BEGIN
6 cat <<- EOF > /dev/null
7 $(:)
8 EOF
9END
10
11: <<- EOF &&
12 $(:)
13EOF
14BEGIN
15 echo Ok
16END
diff --git a/shell/ash_test/ash-alias/alias_case.right b/shell/ash_test/ash-alias/alias_case.right
new file mode 100644
index 000000000..7326d9603
--- /dev/null
+++ b/shell/ash_test/ash-alias/alias_case.right
@@ -0,0 +1 @@
Ok
diff --git a/shell/ash_test/ash-alias/alias_case.tests b/shell/ash_test/ash-alias/alias_case.tests
new file mode 100755
index 000000000..ed8275875
--- /dev/null
+++ b/shell/ash_test/ash-alias/alias_case.tests
@@ -0,0 +1,8 @@
1# Note: bash would need:
2#shopt -s expand_aliases
3# to enable aliases in non-interactive mode
4alias a="case x in " b=x
5a
6b) echo BAD;;
7*) echo Ok;;
8esac
diff --git a/shell/ash_test/ash-misc/control_char3.right b/shell/ash_test/ash-misc/control_char3.right
index 283e02cbb..654005d24 100644
--- a/shell/ash_test/ash-misc/control_char3.right
+++ b/shell/ash_test/ash-misc/control_char3.right
@@ -1 +1 @@
SHELL: line 1: : not found SHELL: line 0: : not found
diff --git a/shell/ash_test/ash-misc/control_char4.right b/shell/ash_test/ash-misc/control_char4.right
index 2bf18e684..ec9d5fc98 100644
--- a/shell/ash_test/ash-misc/control_char4.right
+++ b/shell/ash_test/ash-misc/control_char4.right
@@ -1 +1 @@
SHELL: line 1: -: not found SHELL: line 0: -: not found
diff --git a/shell/ash_test/ash-misc/exitcode_trap7.right b/shell/ash_test/ash-misc/exitcode_trap7.right
new file mode 100644
index 000000000..07d66e9d9
--- /dev/null
+++ b/shell/ash_test/ash-misc/exitcode_trap7.right
@@ -0,0 +1,2 @@
1Start
2Ok:0
diff --git a/shell/ash_test/ash-misc/exitcode_trap7.tests b/shell/ash_test/ash-misc/exitcode_trap7.tests
new file mode 100755
index 000000000..f4b0eb544
--- /dev/null
+++ b/shell/ash_test/ash-misc/exitcode_trap7.tests
@@ -0,0 +1,7 @@
1$THIS_SH -c '
2cleanup() { set +e; false; }
3set -e
4trap cleanup EXIT
5echo Start
6'
7echo Ok:$?
diff --git a/shell/ash_test/ash-misc/set-n1.right b/shell/ash_test/ash-misc/set-n1.right
new file mode 100644
index 000000000..ac01831a7
--- /dev/null
+++ b/shell/ash_test/ash-misc/set-n1.right
@@ -0,0 +1,3 @@
1set -n stops in -c?
2YES
3Ok:0
diff --git a/shell/ash_test/ash-misc/set-n1.tests b/shell/ash_test/ash-misc/set-n1.tests
new file mode 100755
index 000000000..90d0f9146
--- /dev/null
+++ b/shell/ash_test/ash-misc/set-n1.tests
@@ -0,0 +1,2 @@
1$THIS_SH -c "echo 'set -n stops in -c?'; set -n; echo NO" && echo YES
2echo Ok:$?
diff --git a/shell/ash_test/ash-misc/shift1.right b/shell/ash_test/ash-misc/shift1.right
index b53453c3a..fdba79fd3 100644
--- a/shell/ash_test/ash-misc/shift1.right
+++ b/shell/ash_test/ash-misc/shift1.right
@@ -1,5 +1,5 @@
12 3 4 12 3 4
20: shift: line 1: Illegal number: -1 20: shift: line 0: Illegal number: -1
31 2 3 4 31 2 3 4
42 3 4 42 3 4
53 4 53 4
diff --git a/shell/ash_test/ash-misc/tickquote1.right b/shell/ash_test/ash-misc/tickquote1.right
index 2e661bfe3..e55a31c2d 100644
--- a/shell/ash_test/ash-misc/tickquote1.right
+++ b/shell/ash_test/ash-misc/tickquote1.right
@@ -1 +1 @@
./tickquote1.tests: line 1: syntax error: unterminated quoted string ./tickquote1.tests: line 0: syntax error: unterminated quoted string
diff --git a/shell/ash_test/ash-parsing/groups_and_keywords2.right b/shell/ash_test/ash-parsing/groups_and_keywords2.right
index 3fcbeb662..2ce38fe6e 100644
--- a/shell/ash_test/ash-parsing/groups_and_keywords2.right
+++ b/shell/ash_test/ash-parsing/groups_and_keywords2.right
@@ -1,3 +1,3 @@
1./groups_and_keywords2.tests: eval: line 1: syntax error: unexpected ")" 1./groups_and_keywords2.tests: eval: line 2: syntax error: unexpected ")"
2Fail:2 2Fail:2
3./groups_and_keywords2.tests: line 8: syntax error: unexpected ")" 3./groups_and_keywords2.tests: line 8: syntax error: unexpected ")"
diff --git a/shell/ash_test/ash-psubst/emptytick.right b/shell/ash_test/ash-psubst/emptytick.right
index 459c4f735..8fe9839ca 100644
--- a/shell/ash_test/ash-psubst/emptytick.right
+++ b/shell/ash_test/ash-psubst/emptytick.right
@@ -1,8 +1,8 @@
10 10
20 20
3./emptytick.tests: line 1: : Permission denied 3./emptytick.tests: line 2: : Permission denied
4127 4127
5./emptytick.tests: line 1: : Permission denied 5./emptytick.tests: line 3: : Permission denied
6127 6127
70 70
80 80
diff --git a/shell/ash_test/ash-vars/param_expand_alt.right b/shell/ash_test/ash-vars/param_expand_alt.right
index 1303f8064..d10f1206f 100644
--- a/shell/ash_test/ash-vars/param_expand_alt.right
+++ b/shell/ash_test/ash-vars/param_expand_alt.right
@@ -1,5 +1,5 @@
1SHELL: line 1: syntax error: bad substitution 1SHELL: line 0: syntax error: bad substitution
2SHELL: line 1: syntax error: bad substitution 2SHELL: line 0: syntax error: bad substitution
3__ 3__
4_z_ _z_ 4_z_ _z_
5_ _ _ _ _ 5_ _ _ _ _
diff --git a/shell/ash_test/ash-vars/param_expand_assign.right b/shell/ash_test/ash-vars/param_expand_assign.right
index 6e9ea1379..52eaef9f8 100644
--- a/shell/ash_test/ash-vars/param_expand_assign.right
+++ b/shell/ash_test/ash-vars/param_expand_assign.right
@@ -1,11 +1,11 @@
1SHELL: line 1: syntax error: bad substitution 1SHELL: line 0: syntax error: bad substitution
2SHELL: line 1: syntax error: bad substitution 2SHELL: line 0: syntax error: bad substitution
3SHELL: line 1: syntax error: bad substitution 3SHELL: line 0: syntax error: bad substitution
40 40
5SHELL: line 1: 1: bad variable name 5SHELL: line 0: 1: bad variable name
6SHELL: line 1: 1: bad variable name 6SHELL: line 0: 1: bad variable name
7SHELL: line 1: 1: bad variable name 7SHELL: line 0: 1: bad variable name
8SHELL: line 1: 1: bad variable name 8SHELL: line 0: 1: bad variable name
9_aa 9_aa
10_aa 10_aa
11_aa 11_aa
diff --git a/shell/ash_test/ash-vars/param_expand_bash_substring.right b/shell/ash_test/ash-vars/param_expand_bash_substring.right
index 687dd9002..22a0b2881 100644
--- a/shell/ash_test/ash-vars/param_expand_bash_substring.right
+++ b/shell/ash_test/ash-vars/param_expand_bash_substring.right
@@ -1,8 +1,8 @@
1SHELL: line 1: syntax error: bad substitution 1SHELL: line 0: syntax error: bad substitution
2SHELL: line 1: syntax error: bad substitution 2SHELL: line 0: syntax error: bad substitution
3SHELL: line 1: syntax error: bad substitution 3SHELL: line 0: syntax error: bad substitution
4SHELL: line 1: syntax error: bad substitution 4SHELL: line 0: syntax error: bad substitution
5SHELL: line 1: syntax error: missing '}' 5SHELL: line 0: syntax error: missing '}'
60 60
71 =|| 71 =||
81:1 =|| 81:1 =||
diff --git a/shell/ash_test/ash-vars/param_expand_default.right b/shell/ash_test/ash-vars/param_expand_default.right
index 7a42f67e8..0852105ca 100644
--- a/shell/ash_test/ash-vars/param_expand_default.right
+++ b/shell/ash_test/ash-vars/param_expand_default.right
@@ -1,4 +1,4 @@
1SHELL: line 1: syntax error: bad substitution 1SHELL: line 0: syntax error: bad substitution
2_0 _0 2_0 _0
3_ _ _ _word _word 3_ _ _ _word _word
4_aaaa _aaaa _aaaa _aaaa _aaaa 4_aaaa _aaaa _aaaa _aaaa _aaaa
diff --git a/shell/ash_test/ash-vars/param_expand_indicate_error.right b/shell/ash_test/ash-vars/param_expand_indicate_error.right
index 33afacee0..53ea07181 100644
--- a/shell/ash_test/ash-vars/param_expand_indicate_error.right
+++ b/shell/ash_test/ash-vars/param_expand_indicate_error.right
@@ -1,14 +1,14 @@
1SHELL: line 1: syntax error: bad substitution 1SHELL: line 0: syntax error: bad substitution
21 21
30 30
4==== 4====
5_ 5_
6SHELL: line 1: 1: parameter not set 6SHELL: line 0: 1: parameter not set
7SHELL: line 1: 1: parameter not set or null 7SHELL: line 0: 1: parameter not set or null
8SHELL: line 1: 1: message1 8SHELL: line 0: 1: message1
9SHELL: line 1: 1: message1 9SHELL: line 0: 1: message1
10SHELL: line 1: 1: unset! 10SHELL: line 0: 1: unset!
11SHELL: line 1: 1: null or unset! 11SHELL: line 0: 1: null or unset!
12==== 12====
13_aaaa 13_aaaa
14_aaaa 14_aaaa
@@ -19,20 +19,20 @@ _aaaa
19_aaaa 19_aaaa
20==== 20====
21_ 21_
22SHELL: line 1: f: parameter not set 22SHELL: line 0: f: parameter not set
23SHELL: line 1: f: parameter not set or null 23SHELL: line 0: f: parameter not set or null
24SHELL: line 1: f: message3 24SHELL: line 0: f: message3
25SHELL: line 1: f: message3 25SHELL: line 0: f: message3
26SHELL: line 1: f: unset! 26SHELL: line 0: f: unset!
27SHELL: line 1: f: null or unset! 27SHELL: line 0: f: null or unset!
28==== 28====
29_ 29_
30_ 30_
31SHELL: line 1: f: parameter not set or null 31SHELL: line 0: f: parameter not set or null
32_ 32_
33SHELL: line 1: f: message4 33SHELL: line 0: f: message4
34_ 34_
35SHELL: line 1: f: null or unset! 35SHELL: line 0: f: null or unset!
36==== 36====
37_fff 37_fff
38_fff 38_fff
diff --git a/shell/ash_test/ash-vars/var6.right b/shell/ash_test/ash-vars/var6.right
index b37417fa1..e83f7b5eb 100644
--- a/shell/ash_test/ash-vars/var6.right
+++ b/shell/ash_test/ash-vars/var6.right
@@ -1,2 +1,2 @@
1SHELL: line 1: syntax error: bad substitution 1SHELL: line 0: syntax error: bad substitution
2SHELL: line 1: syntax error: bad substitution 2SHELL: line 0: syntax error: bad substitution
diff --git a/shell/ash_test/ash-vars/var_LINENO2.right b/shell/ash_test/ash-vars/var_LINENO2.right
new file mode 100644
index 000000000..73656647c
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_LINENO2.right
@@ -0,0 +1,3 @@
1Start LINENO=6, calling function
2In function: LINENO=4
3After function: LINENO=8
diff --git a/shell/ash_test/ash-vars/var_LINENO2.tests b/shell/ash_test/ash-vars/var_LINENO2.tests
new file mode 100755
index 000000000..7036dbdc8
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_LINENO2.tests
@@ -0,0 +1,8 @@
1#skip lines: make "line number within function" differ from overall line number
2#skip lines
3f() {
4 echo "In function: LINENO=$LINENO"
5}
6echo "Start LINENO=$LINENO, calling function"
7f
8echo "After function: LINENO=$LINENO"
diff --git a/shell/ash_test/ash-vars/var_LINENO3.right b/shell/ash_test/ash-vars/var_LINENO3.right
new file mode 100644
index 000000000..198c3cc99
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_LINENO3.right
@@ -0,0 +1,2 @@
1LINENO starts from 0 in -c
2and increments on next line: 1
diff --git a/shell/ash_test/ash-vars/var_LINENO3.tests b/shell/ash_test/ash-vars/var_LINENO3.tests
new file mode 100755
index 000000000..4502edfe0
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_LINENO3.tests
@@ -0,0 +1,2 @@
1$THIS_SH -c 'echo "LINENO starts from $LINENO in -c"
2echo "and increments on next line: $LINENO"'
diff --git a/shell/ash_test/run-all b/shell/ash_test/run-all
index caf033577..96703ef12 100755
--- a/shell/ash_test/run-all
+++ b/shell/ash_test/run-all
@@ -1,5 +1,14 @@
1#!/bin/sh 1#!/bin/sh
2 2
3unset LANG LANGUAGE
4unset LC_COLLATE
5unset LC_CTYPE
6unset LC_MONETARY
7unset LC_MESSAGES
8unset LC_NUMERIC
9unset LC_TIME
10unset LC_ALL
11
3TOPDIR=`pwd` 12TOPDIR=`pwd`
4 13
5if test ! -x ash; then 14if test ! -x ash; then
@@ -61,11 +70,12 @@ do_test()
61# echo Running test: "$x" 70# echo Running test: "$x"
62 echo -n "$1/$x:" 71 echo -n "$1/$x:"
63 { 72 {
64 "$THIS_SH" "./$x" >"$name.xx" 2>&1 73 "$THIS_SH" "./$x" 2>&1 | \
74 grep -va "^ash: using fallback suid method$" >"$name.xx"
65 diff -u "$name.xx" "$name.right" >"$TOPDIR/$noslash-$x.fail" \ 75 diff -u "$name.xx" "$name.right" >"$TOPDIR/$noslash-$x.fail" \
66 && rm -f "$name.xx" "$TOPDIR/$noslash-$x.fail" 76 && rm -f "$name.xx" "$TOPDIR/$noslash-$x.fail"
67 } && echo " ok" || echo " fail" 77 } && echo " ok" || echo " fail"
68 done 78 done
69 ) 79 )
70} 80}
71 81
diff --git a/shell/hush.c b/shell/hush.c
index 27092c12f..6d472337f 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -2753,6 +2753,12 @@ static int i_getch(struct in_str *i)
2753 if (ch != '\0') { 2753 if (ch != '\0') {
2754 i->p++; 2754 i->p++;
2755 i->last_char = ch; 2755 i->last_char = ch;
2756#if ENABLE_HUSH_LINENO_VAR
2757 if (ch == '\n') {
2758 G.parse_lineno++;
2759 debug_printf_parse("G.parse_lineno++ = %u\n", G.parse_lineno);
2760 }
2761#endif
2756 return ch; 2762 return ch;
2757 } 2763 }
2758 return EOF; 2764 return EOF;
@@ -7540,11 +7546,11 @@ static void parse_and_run_stream(struct in_str *inp, int end_trigger)
7540static void parse_and_run_string(const char *s) 7546static void parse_and_run_string(const char *s)
7541{ 7547{
7542 struct in_str input; 7548 struct in_str input;
7543 //IF_HUSH_LINENO_VAR(unsigned sv = G.parse_lineno;) 7549 IF_HUSH_LINENO_VAR(unsigned sv = G.parse_lineno;)
7544 7550
7545 setup_string_in_str(&input, s); 7551 setup_string_in_str(&input, s);
7546 parse_and_run_stream(&input, '\0'); 7552 parse_and_run_stream(&input, '\0');
7547 //IF_HUSH_LINENO_VAR(G.parse_lineno = sv;) 7553 IF_HUSH_LINENO_VAR(G.parse_lineno = sv;)
7548} 7554}
7549 7555
7550static void parse_and_run_file(HFILE *fp) 7556static void parse_and_run_file(HFILE *fp)
@@ -9898,7 +9904,8 @@ static int run_list(struct pipe *pi)
9898#if ENABLE_HUSH_LOOPS 9904#if ENABLE_HUSH_LOOPS
9899 G.flag_break_continue = 0; 9905 G.flag_break_continue = 0;
9900#endif 9906#endif
9901 rcode = r = run_pipe(pi); /* NB: rcode is a smalluint, r is int */ 9907 rcode = r = G.o_opt[OPT_O_NOEXEC] ? 0 : run_pipe(pi);
9908 /* NB: rcode is a smalluint, r is int */
9902 if (r != -1) { 9909 if (r != -1) {
9903 /* We ran a builtin, function, or group. 9910 /* We ran a builtin, function, or group.
9904 * rcode is already known 9911 * rcode is already known
@@ -10137,7 +10144,10 @@ static int set_mode(int state, char mode, const char *o_opt)
10137 int idx; 10144 int idx;
10138 switch (mode) { 10145 switch (mode) {
10139 case 'n': 10146 case 'n':
10140 G.o_opt[OPT_O_NOEXEC] = state; 10147 /* set -n has no effect in interactive shell */
10148 /* Try: while set -n; do echo $-; done */
10149 if (!G_interactive_fd)
10150 G.o_opt[OPT_O_NOEXEC] = state;
10141 break; 10151 break;
10142 case 'x': 10152 case 'x':
10143 IF_HUSH_MODE_X(G_x_mode = state;) 10153 IF_HUSH_MODE_X(G_x_mode = state;)
diff --git a/shell/hush_test/hush-misc/exitcode_trap7.right b/shell/hush_test/hush-misc/exitcode_trap7.right
new file mode 100644
index 000000000..07d66e9d9
--- /dev/null
+++ b/shell/hush_test/hush-misc/exitcode_trap7.right
@@ -0,0 +1,2 @@
1Start
2Ok:0
diff --git a/shell/hush_test/hush-misc/exitcode_trap7.tests b/shell/hush_test/hush-misc/exitcode_trap7.tests
new file mode 100755
index 000000000..f4b0eb544
--- /dev/null
+++ b/shell/hush_test/hush-misc/exitcode_trap7.tests
@@ -0,0 +1,7 @@
1$THIS_SH -c '
2cleanup() { set +e; false; }
3set -e
4trap cleanup EXIT
5echo Start
6'
7echo Ok:$?
diff --git a/shell/hush_test/hush-misc/set-n1.right b/shell/hush_test/hush-misc/set-n1.right
new file mode 100644
index 000000000..ac01831a7
--- /dev/null
+++ b/shell/hush_test/hush-misc/set-n1.right
@@ -0,0 +1,3 @@
1set -n stops in -c?
2YES
3Ok:0
diff --git a/shell/hush_test/hush-misc/set-n1.tests b/shell/hush_test/hush-misc/set-n1.tests
new file mode 100755
index 000000000..90d0f9146
--- /dev/null
+++ b/shell/hush_test/hush-misc/set-n1.tests
@@ -0,0 +1,2 @@
1$THIS_SH -c "echo 'set -n stops in -c?'; set -n; echo NO" && echo YES
2echo Ok:$?
diff --git a/shell/hush_test/hush-vars/var_LINENO2.right b/shell/hush_test/hush-vars/var_LINENO2.right
new file mode 100644
index 000000000..73656647c
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_LINENO2.right
@@ -0,0 +1,3 @@
1Start LINENO=6, calling function
2In function: LINENO=4
3After function: LINENO=8
diff --git a/shell/hush_test/hush-vars/var_LINENO2.tests b/shell/hush_test/hush-vars/var_LINENO2.tests
new file mode 100755
index 000000000..7036dbdc8
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_LINENO2.tests
@@ -0,0 +1,8 @@
1#skip lines: make "line number within function" differ from overall line number
2#skip lines
3f() {
4 echo "In function: LINENO=$LINENO"
5}
6echo "Start LINENO=$LINENO, calling function"
7f
8echo "After function: LINENO=$LINENO"
diff --git a/shell/hush_test/hush-vars/var_LINENO3.right b/shell/hush_test/hush-vars/var_LINENO3.right
new file mode 100644
index 000000000..198c3cc99
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_LINENO3.right
@@ -0,0 +1,2 @@
1LINENO starts from 0 in -c
2and increments on next line: 1
diff --git a/shell/hush_test/hush-vars/var_LINENO3.tests b/shell/hush_test/hush-vars/var_LINENO3.tests
new file mode 100755
index 000000000..4502edfe0
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_LINENO3.tests
@@ -0,0 +1,2 @@
1$THIS_SH -c 'echo "LINENO starts from $LINENO in -c"
2echo "and increments on next line: $LINENO"'
diff --git a/testsuite/awk.tests b/testsuite/awk.tests
index dc2ae2e11..bcaafe8fd 100755
--- a/testsuite/awk.tests
+++ b/testsuite/awk.tests
@@ -463,4 +463,10 @@ testing "awk \"cmd\" | getline" \
463 "HELLO\n" \ 463 "HELLO\n" \
464 '' '' 464 '' ''
465 465
466# printf %% should print one % (had a bug where it didn't)
467testing 'awk printf %% prints one %' \
468 "awk 'BEGIN { printf \"%%\n\" }'" \
469 "%\n" \
470 '' ''
471
466exit $FAILCOUNT 472exit $FAILCOUNT
diff --git a/testsuite/printf.tests b/testsuite/printf.tests
index 34a65926e..050edef71 100755
--- a/testsuite/printf.tests
+++ b/testsuite/printf.tests
@@ -79,6 +79,11 @@ testing "printf understands %Ld" \
79 "-5\n""0\n" \ 79 "-5\n""0\n" \
80 "" "" 80 "" ""
81 81
82testing "printf understands %%" \
83 "${bb}printf '%%\n' 2>&1; echo \$?" \
84 "%\n""0\n" \
85 "" ""
86
82testing "printf handles positive numbers for %d" \ 87testing "printf handles positive numbers for %d" \
83 "${bb}printf '%d\n' 3 +3 ' 3' ' +3' 2>&1; echo \$?" \ 88 "${bb}printf '%d\n' 3 +3 ' 3' ' +3' 2>&1; echo \$?" \
84 "3\n"\ 89 "3\n"\
diff --git a/util-linux/blkdiscard.c b/util-linux/blkdiscard.c
index ff2101ed0..7ac8045f9 100644
--- a/util-linux/blkdiscard.c
+++ b/util-linux/blkdiscard.c
@@ -18,10 +18,12 @@
18//usage:#define blkdiscard_trivial_usage 18//usage:#define blkdiscard_trivial_usage
19//usage: "[-o OFS] [-l LEN] [-s] DEVICE" 19//usage: "[-o OFS] [-l LEN] [-s] DEVICE"
20//usage:#define blkdiscard_full_usage "\n\n" 20//usage:#define blkdiscard_full_usage "\n\n"
21//usage: "Discard sectors on DEVICE\n" 21//usage: "Discard sectors on DEVICE\n"
22//usage: "\n -o OFS Byte offset into device" 22//usage: "\n -o OFS Byte offset into device"
23//usage: "\n -l LEN Number of bytes to discard" 23//usage: "\n -l LEN Number of bytes to discard"
24//usage: "\n -s Perform a secure discard" 24//usage: "\n -s Perform a secure discard"
25///////: "\n -f Disable check for mounted filesystem"
26//////////////// -f: accepted but is a nop (we do no check anyway)
25//usage: 27//usage:
26//usage:#define blkdiscard_example_usage 28//usage:#define blkdiscard_example_usage
27//usage: "$ blkdiscard -o 0 -l 1G /dev/sdb" 29//usage: "$ blkdiscard -o 0 -l 1G /dev/sdb"
@@ -51,9 +53,10 @@ int blkdiscard_main(int argc UNUSED_PARAM, char **argv)
51 OPT_OFFSET = (1 << 0), 53 OPT_OFFSET = (1 << 0),
52 OPT_LENGTH = (1 << 1), 54 OPT_LENGTH = (1 << 1),
53 OPT_SECURE = (1 << 2), 55 OPT_SECURE = (1 << 2),
56 OPT_FORCE = (1 << 3), //nop
54 }; 57 };
55 58
56 opts = getopt32(argv, "^" "o:l:s" "\0" "=1", &offset_str, &length_str); 59 opts = getopt32(argv, "^" "o:l:sf" "\0" "=1", &offset_str, &length_str);
57 argv += optind; 60 argv += optind;
58 61
59 fd = xopen(argv[0], O_RDWR|O_EXCL); 62 fd = xopen(argv[0], O_RDWR|O_EXCL);