aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2018-04-09 08:50:34 +0100
committerRon Yorston <rmy@pobox.com>2018-04-09 08:50:34 +0100
commit921c1ab66bad54d4ad8591bb74e41ac985248496 (patch)
tree552a04c691e78e78570e4ec2c83fbc0e59953924
parent5b6f06f5eb8628955262508d153627fe6f2d1c8b (diff)
parenta1870f4807a75663a085c9f5e92870fa7554f0ad (diff)
downloadbusybox-w32-921c1ab66bad54d4ad8591bb74e41ac985248496.tar.gz
busybox-w32-921c1ab66bad54d4ad8591bb74e41ac985248496.tar.bz2
busybox-w32-921c1ab66bad54d4ad8591bb74e41ac985248496.zip
Merge branch 'busybox' into merge
-rw-r--r--Config.in8
-rw-r--r--arch/i386/Makefile8
-rw-r--r--archival/libarchive/decompress_bunzip2.c78
-rw-r--r--archival/libarchive/decompress_gunzip.c1
-rw-r--r--archival/libarchive/decompress_unlzma.c11
-rw-r--r--archival/lzop.c431
-rw-r--r--archival/tar.c4
-rw-r--r--coreutils/mv.c2
-rw-r--r--coreutils/nice.c1
-rw-r--r--coreutils/sort.c90
-rw-r--r--coreutils/test.c1
-rw-r--r--debianutils/start_stop_daemon.c2
-rw-r--r--findutils/xargs.c21
-rw-r--r--include/bb_archive.h2
-rw-r--r--include/libbb.h13
-rw-r--r--init/init.c1
-rw-r--r--libbb/appletlib.c17
-rw-r--r--libbb/ask_confirmation.c14
-rw-r--r--libbb/bb_askpass.c55
-rw-r--r--libbb/copy_file.c2
-rw-r--r--libbb/correct_password.c2
-rw-r--r--libbb/dump.c6
-rw-r--r--libbb/lineedit.c3
-rw-r--r--libbb/remove_file.c13
-rw-r--r--loginutils/cryptpw.c4
-rw-r--r--loginutils/login.c1
-rw-r--r--loginutils/passwd.c10
-rw-r--r--mailutils/mail.c4
-rw-r--r--miscutils/bbconfig.c19
-rw-r--r--miscutils/i2c_tools.c3
-rw-r--r--miscutils/time.c1
-rw-r--r--networking/ftpd.c78
-rw-r--r--networking/ftpgetput.c2
-rw-r--r--networking/httpd.c62
-rw-r--r--networking/ntpd.c1
-rw-r--r--networking/telnet.c17
-rw-r--r--networking/wget.c47
-rw-r--r--runit/chpst.c1
-rw-r--r--runit/sv.c2
-rw-r--r--scripts/kconfig/confdata.c4
-rw-r--r--scripts/kconfig/mconf.c4
-rw-r--r--shell/ash.c251
-rw-r--r--shell/ash_test/ash-arith/arith_nested1.right1
-rwxr-xr-xshell/ash_test/ash-arith/arith_nested1.tests1
-rw-r--r--shell/ash_test/ash-heredoc/heredoc_var_expand1.right4
-rwxr-xr-xshell/ash_test/ash-heredoc/heredoc_var_expand1.tests11
-rw-r--r--shell/ash_test/ash-misc/assignment5.right5
-rwxr-xr-xshell/ash_test/ash-misc/assignment5.tests9
-rw-r--r--shell/ash_test/ash-misc/func5.right3
-rwxr-xr-xshell/ash_test/ash-misc/func5.tests5
-rw-r--r--shell/ash_test/ash-misc/func_compound1.right3
-rwxr-xr-xshell/ash_test/ash-misc/func_compound1.tests4
-rw-r--r--shell/ash_test/ash-parsing/starquoted3.right2
-rwxr-xr-xshell/ash_test/ash-parsing/starquoted3.tests1
-rw-r--r--shell/ash_test/ash-quoting/bkslash_case2.right3
-rwxr-xr-xshell/ash_test/ash-quoting/bkslash_case2.tests13
-rw-r--r--shell/ash_test/ash-quoting/quote_in_varexp1.right2
-rwxr-xr-xshell/ash_test/ash-quoting/quote_in_varexp1.tests2
-rw-r--r--shell/ash_test/ash-quoting/squote_in_varexp3.right1
-rwxr-xr-xshell/ash_test/ash-quoting/squote_in_varexp3.tests1
-rw-r--r--shell/ash_test/ash-redir/redir_exec1.right2
-rwxr-xr-xshell/ash_test/ash-redir/redir_exec1.tests2
-rw-r--r--shell/ash_test/ash-vars/var_bash3.right4
-rw-r--r--shell/ash_test/ash-vars/var_bash4.right16
-rw-r--r--shell/ash_test/ash-vars/var_bash6.right2
-rwxr-xr-xshell/ash_test/ash-vars/var_bash6.tests2
-rw-r--r--shell/ash_test/ash-vars/var_bash7.right1
-rwxr-xr-xshell/ash_test/ash-vars/var_bash7.tests1
-rw-r--r--shell/hush.c868
-rw-r--r--shell/hush_test/hush-arith/arith_nested1.right1
-rwxr-xr-xshell/hush_test/hush-arith/arith_nested1.tests1
-rw-r--r--shell/hush_test/hush-misc/assignment5.right5
-rwxr-xr-xshell/hush_test/hush-misc/assignment5.tests9
-rwxr-xr-xshell/hush_test/hush-misc/func5.tests5
-rw-r--r--shell/hush_test/hush-parsing/starquoted3.right2
-rwxr-xr-xshell/hush_test/hush-parsing/starquoted3.tests1
-rw-r--r--shell/hush_test/hush-psubst/falsetick2.right1
-rwxr-xr-xshell/hush_test/hush-psubst/falsetick2.tests3
-rw-r--r--shell/hush_test/hush-quoting/bkslash_case2.right3
-rwxr-xr-xshell/hush_test/hush-quoting/bkslash_case2.tests13
-rw-r--r--shell/hush_test/hush-quoting/squote_in_varexp3.right1
-rwxr-xr-xshell/hush_test/hush-quoting/squote_in_varexp3.tests1
-rw-r--r--shell/hush_test/hush-redir/redir_backquote1.right11
-rwxr-xr-xshell/hush_test/hush-redir/redir_backquote1.tests19
-rw-r--r--shell/hush_test/hush-redir/redir_exec1.right3
-rwxr-xr-xshell/hush_test/hush-redir/redir_exec1.tests2
-rw-r--r--shell/hush_test/hush-vars/readonly3.right4
-rwxr-xr-xshell/hush_test/hush-vars/readonly3.tests4
-rw-r--r--shell/hush_test/hush-vars/var_bash3.right4
-rw-r--r--shell/hush_test/hush-vars/var_bash4.right16
-rw-r--r--shell/hush_test/hush-vars/var_bash6.right2
-rwxr-xr-xshell/hush_test/hush-vars/var_bash6.tests2
-rw-r--r--shell/hush_test/hush-vars/var_nested1.right3
-rwxr-xr-xshell/hush_test/hush-vars/var_nested1.tests16
-rw-r--r--shell/hush_test/hush-vars/var_nested2.right1
-rwxr-xr-xshell/hush_test/hush-vars/var_nested2.tests2
-rw-r--r--shell/shell_common.c1
-rwxr-xr-xtestsuite/bunzip2.tests16
-rw-r--r--testsuite/bz2_issue_11.bz2bin0 -> 12000 bytes
-rw-r--r--testsuite/bz2_issue_12.bz2bin0 -> 11000 bytes
-rwxr-xr-xtestsuite/unlzma.tests21
-rw-r--r--testsuite/unlzma_issue_1.lzmabin0 -> 171 bytes
-rw-r--r--testsuite/unlzma_issue_2.lzmabin0 -> 261 bytes
-rw-r--r--util-linux/renice.c1
104 files changed, 1482 insertions, 958 deletions
diff --git a/Config.in b/Config.in
index 4697f2a4d..10d8382a5 100644
--- a/Config.in
+++ b/Config.in
@@ -590,6 +590,14 @@ config USE_PORTABLE_CODE
590 compiler other than gcc. 590 compiler other than gcc.
591 If you do use gcc, this option may needlessly increase code size. 591 If you do use gcc, this option may needlessly increase code size.
592 592
593config STACK_OPTIMIZATION_386
594 bool "Use -mpreferred-stack-boundary=2 on i386 arch"
595 default y
596 help
597 This option makes for smaller code, but some libc versions
598 do not work with it (they use SSE instructions without
599 ensuring stack alignment).
600
593comment 'Installation Options ("make install" behavior)' 601comment 'Installation Options ("make install" behavior)'
594 602
595choice 603choice
diff --git a/arch/i386/Makefile b/arch/i386/Makefile
index e6c99c67d..425361fd9 100644
--- a/arch/i386/Makefile
+++ b/arch/i386/Makefile
@@ -2,6 +2,12 @@
2# Build system 2# Build system
3# ========================================================================== 3# ==========================================================================
4 4
5# Allow i486 insns (basically, bswap insn)
6# Do not try to tune for 486+ (might add padding)
7CFLAGS += $(call cc-option,-march=i486 -mtune=i386,)
8
9ifeq ($(CONFIG_STACK_OPTIMIZATION_386),y)
5# -mpreferred-stack-boundary=2 is essential in preventing gcc 4.2.x 10# -mpreferred-stack-boundary=2 is essential in preventing gcc 4.2.x
6# from aligning stack to 16 bytes. (Which is gcc's way of supporting SSE). 11# from aligning stack to 16 bytes. (Which is gcc's way of supporting SSE).
7CFLAGS += $(call cc-option,-march=i386 -mpreferred-stack-boundary=2,) 12CFLAGS += $(call cc-option,-mpreferred-stack-boundary=2,)
13endif
diff --git a/archival/libarchive/decompress_bunzip2.c b/archival/libarchive/decompress_bunzip2.c
index bec89edd3..7ef4e035f 100644
--- a/archival/libarchive/decompress_bunzip2.c
+++ b/archival/libarchive/decompress_bunzip2.c
@@ -100,7 +100,7 @@ struct bunzip_data {
100 unsigned dbufSize; 100 unsigned dbufSize;
101 101
102 /* For I/O error handling */ 102 /* For I/O error handling */
103 jmp_buf jmpbuf; 103 jmp_buf *jmpbuf;
104 104
105 /* Big things go last (register-relative addressing can be larger for big offsets) */ 105 /* Big things go last (register-relative addressing can be larger for big offsets) */
106 uint32_t crc32Table[256]; 106 uint32_t crc32Table[256];
@@ -127,7 +127,7 @@ static unsigned get_bits(bunzip_data *bd, int bits_wanted)
127 /* if "no input fd" case: in_fd == -1, read fails, we jump */ 127 /* if "no input fd" case: in_fd == -1, read fails, we jump */
128 bd->inbufCount = read(bd->in_fd, bd->inbuf, IOBUF_SIZE); 128 bd->inbufCount = read(bd->in_fd, bd->inbuf, IOBUF_SIZE);
129 if (bd->inbufCount <= 0) 129 if (bd->inbufCount <= 0)
130 longjmp(bd->jmpbuf, RETVAL_UNEXPECTED_INPUT_EOF); 130 longjmp(*bd->jmpbuf, RETVAL_UNEXPECTED_INPUT_EOF);
131 bd->inbufPos = 0; 131 bd->inbufPos = 0;
132 } 132 }
133 133
@@ -151,12 +151,12 @@ static unsigned get_bits(bunzip_data *bd, int bits_wanted)
151 151
152 return bits; 152 return bits;
153} 153}
154//#define get_bits(bd, n) (dbg("%d:get_bits()", __LINE__), get_bits(bd, n))
154 155
155/* Unpacks the next block and sets up for the inverse Burrows-Wheeler step. */ 156/* Unpacks the next block and sets up for the inverse Burrows-Wheeler step. */
156static int get_next_block(bunzip_data *bd) 157static int get_next_block(bunzip_data *bd)
157{ 158{
158 struct group_data *hufGroup; 159 int groupCount, selector,
159 int groupCount, *base, *limit, selector,
160 i, j, symCount, symTotal, nSelectors, byteCount[256]; 160 i, j, symCount, symTotal, nSelectors, byteCount[256];
161 uint8_t uc, symToByte[256], mtfSymbol[256], *selectors; 161 uint8_t uc, symToByte[256], mtfSymbol[256], *selectors;
162 uint32_t *dbuf; 162 uint32_t *dbuf;
@@ -179,15 +179,19 @@ static int get_next_block(bunzip_data *bd)
179 i = get_bits(bd, 24); 179 i = get_bits(bd, 24);
180 j = get_bits(bd, 24); 180 j = get_bits(bd, 24);
181 bd->headerCRC = get_bits(bd, 32); 181 bd->headerCRC = get_bits(bd, 32);
182 if ((i == 0x177245) && (j == 0x385090)) return RETVAL_LAST_BLOCK; 182 if ((i == 0x177245) && (j == 0x385090))
183 if ((i != 0x314159) || (j != 0x265359)) return RETVAL_NOT_BZIP_DATA; 183 return RETVAL_LAST_BLOCK;
184 if ((i != 0x314159) || (j != 0x265359))
185 return RETVAL_NOT_BZIP_DATA;
184 186
185 /* We can add support for blockRandomised if anybody complains. There was 187 /* We can add support for blockRandomised if anybody complains. There was
186 some code for this in busybox 1.0.0-pre3, but nobody ever noticed that 188 some code for this in busybox 1.0.0-pre3, but nobody ever noticed that
187 it didn't actually work. */ 189 it didn't actually work. */
188 if (get_bits(bd, 1)) return RETVAL_OBSOLETE_INPUT; 190 if (get_bits(bd, 1))
191 return RETVAL_OBSOLETE_INPUT;
189 origPtr = get_bits(bd, 24); 192 origPtr = get_bits(bd, 24);
190 if (origPtr > bd->dbufSize) return RETVAL_DATA_ERROR; 193 if (origPtr > bd->dbufSize)
194 return RETVAL_DATA_ERROR;
191 195
192 /* mapping table: if some byte values are never used (encoding things 196 /* mapping table: if some byte values are never used (encoding things
193 like ascii text), the compression code removes the gaps to have fewer 197 like ascii text), the compression code removes the gaps to have fewer
@@ -231,13 +235,21 @@ static int get_next_block(bunzip_data *bd)
231 /* Get next value */ 235 /* Get next value */
232 int n = 0; 236 int n = 0;
233 while (get_bits(bd, 1)) { 237 while (get_bits(bd, 1)) {
234 if (n >= groupCount) return RETVAL_DATA_ERROR; 238 if (n >= groupCount)
239 return RETVAL_DATA_ERROR;
235 n++; 240 n++;
236 } 241 }
237 /* Decode MTF to get the next selector */ 242 /* Decode MTF to get the next selector */
238 tmp_byte = mtfSymbol[n]; 243 tmp_byte = mtfSymbol[n];
239 while (--n >= 0) 244 while (--n >= 0)
240 mtfSymbol[n + 1] = mtfSymbol[n]; 245 mtfSymbol[n + 1] = mtfSymbol[n];
246//We catch it later, in the second loop where we use selectors[i].
247//Maybe this is a better place, though?
248// if (tmp_byte >= groupCount) {
249// dbg("%d: selectors[%d]:%d groupCount:%d",
250// __LINE__, i, tmp_byte, groupCount);
251// return RETVAL_DATA_ERROR;
252// }
241 mtfSymbol[0] = selectors[i] = tmp_byte; 253 mtfSymbol[0] = selectors[i] = tmp_byte;
242 } 254 }
243 255
@@ -248,6 +260,8 @@ static int get_next_block(bunzip_data *bd)
248 uint8_t length[MAX_SYMBOLS]; 260 uint8_t length[MAX_SYMBOLS];
249 /* 8 bits is ALMOST enough for temp[], see below */ 261 /* 8 bits is ALMOST enough for temp[], see below */
250 unsigned temp[MAX_HUFCODE_BITS+1]; 262 unsigned temp[MAX_HUFCODE_BITS+1];
263 struct group_data *hufGroup;
264 int *base, *limit;
251 int minLen, maxLen, pp, len_m1; 265 int minLen, maxLen, pp, len_m1;
252 266
253 /* Read Huffman code lengths for each symbol. They're stored in 267 /* Read Huffman code lengths for each symbol. They're stored in
@@ -283,8 +297,10 @@ static int get_next_block(bunzip_data *bd)
283 /* Find largest and smallest lengths in this group */ 297 /* Find largest and smallest lengths in this group */
284 minLen = maxLen = length[0]; 298 minLen = maxLen = length[0];
285 for (i = 1; i < symCount; i++) { 299 for (i = 1; i < symCount; i++) {
286 if (length[i] > maxLen) maxLen = length[i]; 300 if (length[i] > maxLen)
287 else if (length[i] < minLen) minLen = length[i]; 301 maxLen = length[i];
302 else if (length[i] < minLen)
303 minLen = length[i];
288 } 304 }
289 305
290 /* Calculate permute[], base[], and limit[] tables from length[]. 306 /* Calculate permute[], base[], and limit[] tables from length[].
@@ -320,7 +336,8 @@ static int get_next_block(bunzip_data *bd)
320 /* Count symbols coded for at each bit length */ 336 /* Count symbols coded for at each bit length */
321 /* NB: in pathological cases, temp[8] can end ip being 256. 337 /* NB: in pathological cases, temp[8] can end ip being 256.
322 * That's why uint8_t is too small for temp[]. */ 338 * That's why uint8_t is too small for temp[]. */
323 for (i = 0; i < symCount; i++) temp[length[i]]++; 339 for (i = 0; i < symCount; i++)
340 temp[length[i]]++;
324 341
325 /* Calculate limit[] (the largest symbol-coding value at each bit 342 /* Calculate limit[] (the largest symbol-coding value at each bit
326 * length, which is (previous limit<<1)+symbols at this level), and 343 * length, which is (previous limit<<1)+symbols at this level), and
@@ -363,12 +380,22 @@ static int get_next_block(bunzip_data *bd)
363 380
364 runPos = dbufCount = selector = 0; 381 runPos = dbufCount = selector = 0;
365 for (;;) { 382 for (;;) {
383 struct group_data *hufGroup;
384 int *base, *limit;
366 int nextSym; 385 int nextSym;
386 uint8_t ngrp;
367 387
368 /* Fetch next Huffman coding group from list. */ 388 /* Fetch next Huffman coding group from list. */
369 symCount = GROUP_SIZE - 1; 389 symCount = GROUP_SIZE - 1;
370 if (selector >= nSelectors) return RETVAL_DATA_ERROR; 390 if (selector >= nSelectors)
371 hufGroup = bd->groups + selectors[selector++]; 391 return RETVAL_DATA_ERROR;
392 ngrp = selectors[selector++];
393 if (ngrp >= groupCount) {
394 dbg("%d selectors[%d]:%d groupCount:%d",
395 __LINE__, selector-1, ngrp, groupCount);
396 return RETVAL_DATA_ERROR;
397 }
398 hufGroup = bd->groups + ngrp;
372 base = hufGroup->base - 1; 399 base = hufGroup->base - 1;
373 limit = hufGroup->limit - 1; 400 limit = hufGroup->limit - 1;
374 401
@@ -403,7 +430,8 @@ static int get_next_block(bunzip_data *bd)
403 } 430 }
404 /* Figure how many bits are in next symbol and unget extras */ 431 /* Figure how many bits are in next symbol and unget extras */
405 i = hufGroup->minLen; 432 i = hufGroup->minLen;
406 while (nextSym > limit[i]) ++i; 433 while (nextSym > limit[i])
434 ++i;
407 j = hufGroup->maxLen - i; 435 j = hufGroup->maxLen - i;
408 if (j < 0) 436 if (j < 0)
409 return RETVAL_DATA_ERROR; 437 return RETVAL_DATA_ERROR;
@@ -671,7 +699,10 @@ int FAST_FUNC read_bunzip(bunzip_data *bd, char *outbuf, int len)
671/* Because bunzip2 is used for help text unpacking, and because bb_show_usage() 699/* Because bunzip2 is used for help text unpacking, and because bb_show_usage()
672 should work for NOFORK applets too, we must be extremely careful to not leak 700 should work for NOFORK applets too, we must be extremely careful to not leak
673 any allocations! */ 701 any allocations! */
674int FAST_FUNC start_bunzip(bunzip_data **bdp, int in_fd, 702int FAST_FUNC start_bunzip(
703 void *jmpbuf,
704 bunzip_data **bdp,
705 int in_fd,
675 const void *inbuf, int len) 706 const void *inbuf, int len)
676{ 707{
677 bunzip_data *bd; 708 bunzip_data *bd;
@@ -683,11 +714,14 @@ int FAST_FUNC start_bunzip(bunzip_data **bdp, int in_fd,
683 714
684 /* Figure out how much data to allocate */ 715 /* Figure out how much data to allocate */
685 i = sizeof(bunzip_data); 716 i = sizeof(bunzip_data);
686 if (in_fd != -1) i += IOBUF_SIZE; 717 if (in_fd != -1)
718 i += IOBUF_SIZE;
687 719
688 /* Allocate bunzip_data. Most fields initialize to zero. */ 720 /* Allocate bunzip_data. Most fields initialize to zero. */
689 bd = *bdp = xzalloc(i); 721 bd = *bdp = xzalloc(i);
690 722
723 bd->jmpbuf = jmpbuf;
724
691 /* Setup input buffer */ 725 /* Setup input buffer */
692 bd->in_fd = in_fd; 726 bd->in_fd = in_fd;
693 if (-1 == in_fd) { 727 if (-1 == in_fd) {
@@ -702,10 +736,6 @@ int FAST_FUNC start_bunzip(bunzip_data **bdp, int in_fd,
702 /* Init the CRC32 table (big endian) */ 736 /* Init the CRC32 table (big endian) */
703 crc32_filltable(bd->crc32Table, 1); 737 crc32_filltable(bd->crc32Table, 1);
704 738
705 /* Setup for I/O error handling via longjmp */
706 i = setjmp(bd->jmpbuf);
707 if (i) return i;
708
709 /* Ensure that file starts with "BZh['1'-'9']." */ 739 /* Ensure that file starts with "BZh['1'-'9']." */
710 /* Update: now caller verifies 1st two bytes, makes .gz/.bz2 740 /* Update: now caller verifies 1st two bytes, makes .gz/.bz2
711 * integration easier */ 741 * integration easier */
@@ -752,8 +782,12 @@ unpack_bz2_stream(transformer_state_t *xstate)
752 outbuf = xmalloc(IOBUF_SIZE); 782 outbuf = xmalloc(IOBUF_SIZE);
753 len = 0; 783 len = 0;
754 while (1) { /* "Process one BZ... stream" loop */ 784 while (1) { /* "Process one BZ... stream" loop */
785 jmp_buf jmpbuf;
755 786
756 i = start_bunzip(&bd, xstate->src_fd, outbuf + 2, len); 787 /* Setup for I/O error handling via longjmp */
788 i = setjmp(jmpbuf);
789 if (i == 0)
790 i = start_bunzip(&jmpbuf, &bd, xstate->src_fd, outbuf + 2, len);
757 791
758 if (i == 0) { 792 if (i == 0) {
759 while (1) { /* "Produce some output bytes" loop */ 793 while (1) { /* "Produce some output bytes" loop */
diff --git a/archival/libarchive/decompress_gunzip.c b/archival/libarchive/decompress_gunzip.c
index 7483a2cea..32fcb6b51 100644
--- a/archival/libarchive/decompress_gunzip.c
+++ b/archival/libarchive/decompress_gunzip.c
@@ -32,7 +32,6 @@
32 * 32 *
33 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 33 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
34 */ 34 */
35#include <setjmp.h>
36#include "libbb.h" 35#include "libbb.h"
37#include "bb_archive.h" 36#include "bb_archive.h"
38 37
diff --git a/archival/libarchive/decompress_unlzma.c b/archival/libarchive/decompress_unlzma.c
index be4342414..80a453806 100644
--- a/archival/libarchive/decompress_unlzma.c
+++ b/archival/libarchive/decompress_unlzma.c
@@ -11,6 +11,13 @@
11#include "libbb.h" 11#include "libbb.h"
12#include "bb_archive.h" 12#include "bb_archive.h"
13 13
14#if 0
15# define dbg(...) bb_error_msg(__VA_ARGS__)
16#else
17# define dbg(...) ((void)0)
18#endif
19
20
14#if ENABLE_FEATURE_LZMA_FAST 21#if ENABLE_FEATURE_LZMA_FAST
15# define speed_inline ALWAYS_INLINE 22# define speed_inline ALWAYS_INLINE
16# define size_inline 23# define size_inline
@@ -417,6 +424,10 @@ unpack_lzma_stream(transformer_state_t *xstate)
417 for (; num_bits2 != LZMA_NUM_ALIGN_BITS; num_bits2--) 424 for (; num_bits2 != LZMA_NUM_ALIGN_BITS; num_bits2--)
418 rep0 = (rep0 << 1) | rc_direct_bit(rc); 425 rep0 = (rep0 << 1) | rc_direct_bit(rc);
419 rep0 <<= LZMA_NUM_ALIGN_BITS; 426 rep0 <<= LZMA_NUM_ALIGN_BITS;
427 if ((int32_t)rep0 < 0) {
428 dbg("%d rep0:%d", __LINE__, rep0);
429 goto bad;
430 }
420 prob3 = p + LZMA_ALIGN; 431 prob3 = p + LZMA_ALIGN;
421 } 432 }
422 i2 = 1; 433 i2 = 1;
diff --git a/archival/lzop.c b/archival/lzop.c
index fef8cdba3..8f604254c 100644
--- a/archival/lzop.c
+++ b/archival/lzop.c
@@ -438,36 +438,33 @@ typedef struct chksum_t {
438} chksum_t; 438} chksum_t;
439 439
440typedef struct header_t { 440typedef struct header_t {
441 unsigned version; 441 /* used to have auxiliary fields here */
442 unsigned lib_version; 442
443 unsigned version_needed_to_extract; 443 /* Starting from here, the layout and endianness
444 uint32_t flags; 444 * are exactly in on-disk format.
445 uint32_t mode; 445 */
446 uint32_t mtime; 446 uint16_t version_be16;
447 uint32_t gmtdiff; 447 uint16_t lib_version_be16;
448 uint32_t header_checksum; 448 uint16_t version_needed_to_extract_be16;
449 449 uint8_t method;
450 uint32_t extra_field_len; 450 uint8_t level;
451 uint32_t extra_field_checksum; 451 uint32_t flags32; /* be32 on disk, but we keep this field in native order */
452 452 uint32_t mode_be32;
453 unsigned char method; 453 uint32_t mtime_be32;
454 unsigned char level; 454 uint32_t gmtdiff_be32;
455 455 char len_and_name[1+255+1];
456 /* info */
457 char name[255+1];
458} header_t; 456} header_t;
459 457
460struct globals { 458struct globals {
461 /*const uint32_t *lzo_crc32_table;*/ 459 /*const uint32_t *lzo_crc32_table;*/
462 chksum_t chksum_in; 460 chksum_t chksum;
463 chksum_t chksum_out;
464} FIX_ALIASING; 461} FIX_ALIASING;
465#define G (*(struct globals*)bb_common_bufsiz1) 462#define G (*(struct globals*)bb_common_bufsiz1)
466#define INIT_G() do { setup_common_bufsiz(); } while (0)
467//#define G (*ptr_to_globals) 463//#define G (*ptr_to_globals)
468//#define INIT_G() do { 464#define INIT_G() do { \
469// SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); 465 setup_common_bufsiz(); \
470//} while (0) 466 /*SET_PTR_TO_GLOBALS(xzalloc(sizeof(G)));*/ \
467} while (0)
471 468
472 469
473/**********************************************************************/ 470/**********************************************************************/
@@ -548,24 +545,24 @@ lzo_crc32(uint32_t c, const uint8_t* buf, unsigned len)
548} 545}
549 546
550/**********************************************************************/ 547/**********************************************************************/
551static void init_chksum(chksum_t *ct) 548static void init_chksum(void)
552{ 549{
553 ct->f_adler32 = ADLER32_INIT_VALUE; 550 G.chksum.f_adler32 = ADLER32_INIT_VALUE;
554 ct->f_crc32 = CRC32_INIT_VALUE; 551 G.chksum.f_crc32 = CRC32_INIT_VALUE;
555} 552}
556 553
557static void add_bytes_to_chksum(chksum_t *ct, const void* buf, int cnt) 554static void add_bytes_to_chksum(const void* buf, int cnt)
558{ 555{
559 /* We need to handle the two checksums at once, because at the 556 /* We need to handle the two checksums at once, because at the
560 * beginning of the header, we don't know yet which one we'll 557 * beginning of the header, we don't know yet which one we'll
561 * eventually need */ 558 * eventually need */
562 ct->f_adler32 = lzo_adler32(ct->f_adler32, (const uint8_t*)buf, cnt); 559 G.chksum.f_adler32 = lzo_adler32(G.chksum.f_adler32, (const uint8_t*)buf, cnt);
563 ct->f_crc32 = lzo_crc32(ct->f_crc32, (const uint8_t*)buf, cnt); 560 G.chksum.f_crc32 = lzo_crc32(G.chksum.f_crc32, (const uint8_t*)buf, cnt);
564} 561}
565 562
566static uint32_t chksum_getresult(chksum_t *ct, const header_t *h) 563static uint32_t chksum_getresult(uint32_t h_flags32)
567{ 564{
568 return (h->flags & F_H_CRC32) ? ct->f_crc32 : ct->f_adler32; 565 return (h_flags32 & F_H_CRC32) ? G.chksum.f_crc32 : G.chksum.f_adler32;
569} 566}
570 567
571/**********************************************************************/ 568/**********************************************************************/
@@ -575,50 +572,23 @@ static uint32_t read32(void)
575 xread(0, &v, 4); 572 xread(0, &v, 4);
576 return ntohl(v); 573 return ntohl(v);
577} 574}
578
579static void write32(uint32_t v)
580{
581 v = htonl(v);
582 xwrite(1, &v, 4);
583}
584
585static void f_write(const void* buf, int cnt)
586{
587 xwrite(1, buf, cnt);
588 add_bytes_to_chksum(&G.chksum_out, buf, cnt);
589}
590
591static void f_read(void* buf, int cnt) 575static void f_read(void* buf, int cnt)
592{ 576{
593 xread(0, buf, cnt); 577 xread(0, buf, cnt);
594 add_bytes_to_chksum(&G.chksum_in, buf, cnt); 578 add_bytes_to_chksum(buf, cnt);
595} 579}
596 580//static int f_read8(void)
597static int f_read8(void) 581//{
598{ 582// uint8_t v;
599 uint8_t v; 583// f_read(&v, 1);
600 f_read(&v, 1); 584// return v;
601 return v; 585//}
602} 586//static unsigned f_read16(void)
603 587//{
604static void f_write8(uint8_t v) 588// uint16_t v;
605{ 589// f_read(&v, 2);
606 f_write(&v, 1); 590// return ntohs(v);
607} 591//}
608
609static unsigned f_read16(void)
610{
611 uint16_t v;
612 f_read(&v, 2);
613 return ntohs(v);
614}
615
616static void f_write16(uint16_t v)
617{
618 v = htons(v);
619 f_write(&v, 2);
620}
621
622static uint32_t f_read32(void) 592static uint32_t f_read32(void)
623{ 593{
624 uint32_t v; 594 uint32_t v;
@@ -626,34 +596,30 @@ static uint32_t f_read32(void)
626 return ntohl(v); 596 return ntohl(v);
627} 597}
628 598
629static void f_write32(uint32_t v) 599static void write32(uint32_t v)
630{ 600{
631 v = htonl(v); 601 v = htonl(v);
632 f_write(&v, 4); 602 xwrite(1, &v, 4);
633} 603}
634 604static void f_write(const void* buf, int cnt)
635/**********************************************************************/
636static int lzo_get_method(header_t *h)
637{ 605{
638 /* check method */ 606 xwrite(1, buf, cnt);
639 if (h->method == M_LZO1X_1) { 607 add_bytes_to_chksum(buf, cnt);
640 if (h->level == 0)
641 h->level = 3;
642 } else if (h->method == M_LZO1X_1_15) {
643 if (h->level == 0)
644 h->level = 1;
645 } else if (h->method == M_LZO1X_999) {
646 if (h->level == 0)
647 h->level = 9;
648 } else
649 return -1; /* not a LZO method */
650
651 /* check compression level */
652 if (h->level < 1 || h->level > 9)
653 return 15;
654
655 return 0;
656} 608}
609//static void f_write8(uint8_t v)
610//{
611// f_write(&v, 1);
612//}
613//static void f_write16(uint16_t v)
614//{
615// v = htons(v);
616// f_write(&v, 2);
617//}
618//static void f_write32(uint32_t v)
619//{
620// v = htonl(v);
621// f_write(&v, 4);
622//}
657 623
658/**********************************************************************/ 624/**********************************************************************/
659#define LZO_BLOCK_SIZE (256 * 1024l) 625#define LZO_BLOCK_SIZE (256 * 1024l)
@@ -671,52 +637,61 @@ static NOINLINE int lzo_compress(const header_t *h)
671 int r = 0; /* LZO_E_OK */ 637 int r = 0; /* LZO_E_OK */
672 uint8_t *const b1 = xzalloc(block_size); 638 uint8_t *const b1 = xzalloc(block_size);
673 uint8_t *const b2 = xzalloc(MAX_COMPRESSED_SIZE(block_size)); 639 uint8_t *const b2 = xzalloc(MAX_COMPRESSED_SIZE(block_size));
674 unsigned src_len = 0, dst_len = 0;
675 uint32_t d_adler32 = ADLER32_INIT_VALUE; 640 uint32_t d_adler32 = ADLER32_INIT_VALUE;
676 uint32_t d_crc32 = CRC32_INIT_VALUE; 641 uint32_t d_crc32 = CRC32_INIT_VALUE;
677 int l;
678 uint8_t *wrk_mem = NULL; 642 uint8_t *wrk_mem = NULL;
679 643
644 /* Only these methods are possible, see lzo_set_method():
645 * -1: M_LZO1X_1_15
646 * -2..6: M_LZO1X_1
647 * -7..9: M_LZO1X_999 if ENABLE_LZOP_COMPR_HIGH
648 */
680 if (h->method == M_LZO1X_1) 649 if (h->method == M_LZO1X_1)
681 wrk_mem = xzalloc(LZO1X_1_MEM_COMPRESS); 650 wrk_mem = xzalloc(LZO1X_1_MEM_COMPRESS);
682 else if (h->method == M_LZO1X_1_15) 651 else /* check only if it's not the only possibility */
683 wrk_mem = xzalloc(LZO1X_1_15_MEM_COMPRESS); 652 IF_LZOP_COMPR_HIGH(if (h->method == M_LZO1X_1_15))
684 else if (h->method == M_LZO1X_999) 653 wrk_mem = xzalloc(LZO1X_1_15_MEM_COMPRESS);
654#if ENABLE_LZOP_COMPR_HIGH
655 else /* must be h->method == M_LZO1X_999 */
685 wrk_mem = xzalloc(LZO1X_999_MEM_COMPRESS); 656 wrk_mem = xzalloc(LZO1X_999_MEM_COMPRESS);
657#endif
686 658
687 for (;;) { 659 for (;;) {
660 unsigned src_len, dst_len;
661 int l;
662 uint32_t wordbuf[6];
663 uint32_t *wordptr = wordbuf;
664
688 /* read a block */ 665 /* read a block */
689 l = full_read(0, b1, block_size); 666 l = full_read(0, b1, block_size);
690 src_len = (l > 0 ? l : 0); 667 src_len = (l > 0 ? l : 0);
691 668
692 /* write uncompressed block size */ 669 /* write uncompressed block size */
693 write32(src_len);
694
695 /* exit if last block */ 670 /* exit if last block */
696 if (src_len == 0) 671 if (src_len == 0) {
672 write32(0);
697 break; 673 break;
674 }
675 *wordptr++ = htonl(src_len);
698 676
699 /* compute checksum of uncompressed block */ 677 /* compute checksum of uncompressed block */
700 if (h->flags & F_ADLER32_D) 678 if (h->flags32 & F_ADLER32_D)
701 d_adler32 = lzo_adler32(ADLER32_INIT_VALUE, b1, src_len); 679 d_adler32 = lzo_adler32(ADLER32_INIT_VALUE, b1, src_len);
702 if (h->flags & F_CRC32_D) 680 if (h->flags32 & F_CRC32_D)
703 d_crc32 = lzo_crc32(CRC32_INIT_VALUE, b1, src_len); 681 d_crc32 = lzo_crc32(CRC32_INIT_VALUE, b1, src_len);
704 682
705 /* compress */ 683 /* compress */
706 if (h->method == M_LZO1X_1) 684 if (h->method == M_LZO1X_1)
707 r = lzo1x_1_compress(b1, src_len, b2, &dst_len, wrk_mem); 685 r = lzo1x_1_compress(b1, src_len, b2, &dst_len, wrk_mem);
708 else if (h->method == M_LZO1X_1_15) 686 else IF_LZOP_COMPR_HIGH(if (h->method == M_LZO1X_1_15))
709 r = lzo1x_1_15_compress(b1, src_len, b2, &dst_len, wrk_mem); 687 r = lzo1x_1_15_compress(b1, src_len, b2, &dst_len, wrk_mem);
710#if ENABLE_LZOP_COMPR_HIGH 688#if ENABLE_LZOP_COMPR_HIGH
711 else if (h->method == M_LZO1X_999) 689 else /* must be h->method == M_LZO1X_999 */
712 r = lzo1x_999_compress_level(b1, src_len, b2, &dst_len, 690 r = lzo1x_999_compress_level(b1, src_len, b2, &dst_len,
713 wrk_mem, h->level); 691 wrk_mem, h->level);
714#endif 692#endif
715 else
716 bb_error_msg_and_die("internal error");
717
718 if (r != 0) /* not LZO_E_OK */ 693 if (r != 0) /* not LZO_E_OK */
719 bb_error_msg_and_die("internal error - compression failed"); 694 bb_error_msg_and_die("%s: %s", "internal error", "compression");
720 695
721 /* write compressed block size */ 696 /* write compressed block size */
722 if (dst_len < src_len) { 697 if (dst_len < src_len) {
@@ -725,32 +700,38 @@ static NOINLINE int lzo_compress(const header_t *h)
725 unsigned new_len = src_len; 700 unsigned new_len = src_len;
726 r = lzo1x_optimize(b2, dst_len, b1, &new_len /*, NULL*/); 701 r = lzo1x_optimize(b2, dst_len, b1, &new_len /*, NULL*/);
727 if (r != 0 /*LZO_E_OK*/ || new_len != src_len) 702 if (r != 0 /*LZO_E_OK*/ || new_len != src_len)
728 bb_error_msg_and_die("internal error - optimization failed"); 703 bb_error_msg_and_die("%s: %s", "internal error", "optimization");
729 } 704 }
730 write32(dst_len); 705 *wordptr++ = htonl(dst_len);
731 } else { 706 } else {
732 /* data actually expanded => store data uncompressed */ 707 /* data actually expanded => store data uncompressed */
733 write32(src_len); 708 *wordptr++ = htonl(src_len);
734 } 709 }
735 710
736 /* write checksum of uncompressed block */ 711 /* write checksum of uncompressed block */
737 if (h->flags & F_ADLER32_D) 712 if (h->flags32 & F_ADLER32_D)
738 write32(d_adler32); 713 *wordptr++ = htonl(d_adler32);
739 if (h->flags & F_CRC32_D) 714 if (h->flags32 & F_CRC32_D)
740 write32(d_crc32); 715 *wordptr++ = htonl(d_crc32);
741 716
742 if (dst_len < src_len) { 717 if (dst_len < src_len) {
743 /* write checksum of compressed block */ 718 /* write checksum of compressed block */
744 if (h->flags & F_ADLER32_C) 719 if (h->flags32 & F_ADLER32_C)
745 write32(lzo_adler32(ADLER32_INIT_VALUE, b2, dst_len)); 720 *wordptr++ = htonl(lzo_adler32(ADLER32_INIT_VALUE, b2, dst_len));
746 if (h->flags & F_CRC32_C) 721 if (h->flags32 & F_CRC32_C)
747 write32(lzo_crc32(CRC32_INIT_VALUE, b2, dst_len)); 722 *wordptr++ = htonl(lzo_crc32(CRC32_INIT_VALUE, b2, dst_len));
723 }
724 xwrite(1, wordbuf, ((char*)wordptr) - ((char*)wordbuf));
725 if (dst_len < src_len) {
748 /* write compressed block data */ 726 /* write compressed block data */
749 xwrite(1, b2, dst_len); 727 xwrite(1, b2, dst_len);
750 } else { 728 } else {
751 /* write uncompressed block data */ 729 /* write uncompressed block data */
752 xwrite(1, b1, src_len); 730 xwrite(1, b1, src_len);
753 } 731 }
732 // /* if full_read() was nevertheless "short", it was EOF */
733 // if (src_len < block_size)
734 // break;
754 } 735 }
755 736
756 free(wrk_mem); 737 free(wrk_mem);
@@ -777,7 +758,9 @@ static FAST_FUNC void lzo_check(
777/**********************************************************************/ 758/**********************************************************************/
778// decompress a file 759// decompress a file
779/**********************************************************************/ 760/**********************************************************************/
780static NOINLINE int lzo_decompress(const header_t *h) 761// used to have "const header_t *h" parameter, but since it uses
762// only flags32 field, changed to receive only that.
763static NOINLINE int lzo_decompress(uint32_t h_flags32)
781{ 764{
782 unsigned block_size = LZO_BLOCK_SIZE; 765 unsigned block_size = LZO_BLOCK_SIZE;
783 int r; 766 int r;
@@ -822,16 +805,16 @@ static NOINLINE int lzo_decompress(const header_t *h)
822 } 805 }
823 806
824 /* read checksum of uncompressed block */ 807 /* read checksum of uncompressed block */
825 if (h->flags & F_ADLER32_D) 808 if (h_flags32 & F_ADLER32_D)
826 d_adler32 = read32(); 809 d_adler32 = read32();
827 if (h->flags & F_CRC32_D) 810 if (h_flags32 & F_CRC32_D)
828 d_crc32 = read32(); 811 d_crc32 = read32();
829 812
830 /* read checksum of compressed block */ 813 /* read checksum of compressed block */
831 if (src_len < dst_len) { 814 if (src_len < dst_len) {
832 if (h->flags & F_ADLER32_C) 815 if (h_flags32 & F_ADLER32_C)
833 c_adler32 = read32(); 816 c_adler32 = read32();
834 if (h->flags & F_CRC32_C) 817 if (h_flags32 & F_CRC32_C)
835 c_crc32 = read32(); 818 c_crc32 = read32();
836 } 819 }
837 820
@@ -846,11 +829,11 @@ static NOINLINE int lzo_decompress(const header_t *h)
846 829
847 if (!(option_mask32 & OPT_F)) { 830 if (!(option_mask32 & OPT_F)) {
848 /* verify checksum of compressed block */ 831 /* verify checksum of compressed block */
849 if (h->flags & F_ADLER32_C) 832 if (h_flags32 & F_ADLER32_C)
850 lzo_check(ADLER32_INIT_VALUE, 833 lzo_check(ADLER32_INIT_VALUE,
851 b1, src_len, 834 b1, src_len,
852 lzo_adler32, c_adler32); 835 lzo_adler32, c_adler32);
853 if (h->flags & F_CRC32_C) 836 if (h_flags32 & F_CRC32_C)
854 lzo_check(CRC32_INIT_VALUE, 837 lzo_check(CRC32_INIT_VALUE,
855 b1, src_len, 838 b1, src_len,
856 lzo_crc32, c_crc32); 839 lzo_crc32, c_crc32);
@@ -873,11 +856,11 @@ static NOINLINE int lzo_decompress(const header_t *h)
873 856
874 if (!(option_mask32 & OPT_F)) { 857 if (!(option_mask32 & OPT_F)) {
875 /* verify checksum of uncompressed block */ 858 /* verify checksum of uncompressed block */
876 if (h->flags & F_ADLER32_D) 859 if (h_flags32 & F_ADLER32_D)
877 lzo_check(ADLER32_INIT_VALUE, 860 lzo_check(ADLER32_INIT_VALUE,
878 dst, dst_len, 861 dst, dst_len,
879 lzo_adler32, d_adler32); 862 lzo_adler32, d_adler32);
880 if (h->flags & F_CRC32_D) 863 if (h_flags32 & F_CRC32_D)
881 lzo_check(CRC32_INIT_VALUE, 864 lzo_check(CRC32_INIT_VALUE,
882 dst, dst_len, 865 dst, dst_len,
883 lzo_crc32, d_crc32); 866 lzo_crc32, d_crc32);
@@ -917,7 +900,7 @@ static NOINLINE int lzo_decompress(const header_t *h)
917 * -00000020 00 00 2d 67 04 17 00 04 00 00 00 03 ed ec 9d 6d 900 * -00000020 00 00 2d 67 04 17 00 04 00 00 00 03 ed ec 9d 6d
918 * +00000020 00 00 10 5f 00 c1 00 04 00 00 00 03 ed ec 9d 6d 901 * +00000020 00 00 10 5f 00 c1 00 04 00 00 00 03 ed ec 9d 6d
919 * ^^^^^^^^^^^ 902 * ^^^^^^^^^^^
920 * chksum_out 903 * chksum
921 * The rest is identical. 904 * The rest is identical.
922*/ 905*/
923static const unsigned char lzop_magic[9] ALIGN1 = { 906static const unsigned char lzop_magic[9] ALIGN1 = {
@@ -936,138 +919,138 @@ static void check_magic(void)
936/**********************************************************************/ 919/**********************************************************************/
937// lzop file header 920// lzop file header
938/**********************************************************************/ 921/**********************************************************************/
939static void write_header(const header_t *h) 922static void write_header(header_t *h)
940{ 923{
941 int l; 924 char *end;
942 925
943 xwrite(1, lzop_magic, sizeof(lzop_magic)); 926 xwrite(1, lzop_magic, sizeof(lzop_magic));
944 927
945 init_chksum(&G.chksum_out); 928 init_chksum();
946 929
947 f_write16(h->version); 930 /* Our caller leaves name zero-filled, so len == 0 */
948 f_write16(h->lib_version); 931 end = h->len_and_name+1 + 0; /* 0 is strlen(h->len_and_name+1) */
949 f_write16(h->version_needed_to_extract); 932 /* Store length byte */
950 f_write8(h->method); 933 /*h->len_and_name[0] = end - (h->len_and_name+1); - zero already */
951 f_write8(h->level);
952 f_write32(h->flags);
953 f_write32(h->mode);
954 f_write32(h->mtime);
955 f_write32(h->gmtdiff);
956 934
957 l = (int) strlen(h->name); 935 f_write(&h->version_be16, end - (char*)&h->version_be16);
958 f_write8(l);
959 if (l)
960 f_write(h->name, l);
961 936
962 f_write32(chksum_getresult(&G.chksum_out, h)); 937 h->flags32 = htonl(h->flags32); /* native endianness for lzo_compress() */
938
939 write32(chksum_getresult(h->flags32));
963} 940}
964 941
965static int read_header(header_t *h) 942static int read_header(header_t *h)
966{ 943{
967 int r;
968 int l; 944 int l;
969 uint32_t checksum; 945 uint32_t checksum;
946 /* As it stands now, only h->flags32 is used by our caller.
947 * Therefore we don't store many fields in h->FIELD.
948 */
949 unsigned h_version;
950 unsigned h_version_needed_to_extract;
970 951
971 memset(h, 0, sizeof(*h)); 952 init_chksum();
972 h->version_needed_to_extract = 0x0900; /* first lzop version */
973 h->level = 0;
974
975 init_chksum(&G.chksum_in);
976 953
977 h->version = f_read16(); 954 /* We don't support versions < 0.94, since 0.94
978 if (h->version < 0x0900) 955 * came only 2 months after 0.90:
979 return 3; 956 * 0.90 (10 Aug 1997): First public release of lzop
980 h->lib_version = f_read16(); 957 * 0.94 (15 Oct 1997): Header format change
981 if (h->version >= 0x0940) { 958 */
982 h->version_needed_to_extract = f_read16();
983 if (h->version_needed_to_extract > LZOP_VERSION)
984 return 16;
985 if (h->version_needed_to_extract < 0x0900)
986 return 3;
987 }
988 h->method = f_read8();
989 if (h->version >= 0x0940)
990 h->level = f_read8();
991 h->flags = f_read32();
992 if (h->flags & F_H_FILTER)
993 return 16; /* filter not supported */
994 h->mode = f_read32();
995 h->mtime = f_read32();
996 if (h->version >= 0x0940)
997 h->gmtdiff = f_read32();
998 959
999 l = f_read8(); 960 /* Read up to and including name length byte */
1000 if (l > 0) 961 f_read(&h->version_be16, ((char*)&h->len_and_name[1]) - ((char*)&h->version_be16));
1001 f_read(h->name, l);
1002 h->name[l] = 0;
1003 962
1004 checksum = chksum_getresult(&G.chksum_in, h); 963 h_version = htons(h->version_be16);
1005 h->header_checksum = f_read32(); 964 if (h_version < 0x0940)
1006 if (h->header_checksum != checksum) 965 return 3;
1007 return 2; 966 h_version_needed_to_extract = htons(h->version_needed_to_extract_be16);
967 if (h_version_needed_to_extract > LZOP_VERSION)
968 return 16;
969 if (h_version_needed_to_extract < 0x0940)
970 return 3;
1008 971
1009 if (h->method <= 0) 972 if (h->method <= 0)
1010 return 14; 973 return 14;
1011 r = lzo_get_method(h);
1012 if (r != 0)
1013 return r;
1014 974
975 /* former lzo_get_method(h): */
976 if (h->method == M_LZO1X_1) {
977 if (h->level == 0)
978 h->level = 3;
979 } else if (h->method == M_LZO1X_1_15) {
980 if (h->level == 0)
981 h->level = 1;
982 } else if (h->method == M_LZO1X_999) {
983 if (h->level == 0)
984 h->level = 9;
985 } else
986 return -1; /* not a LZO method */
987 /* check compression level */
988 if (h->level < 1 || h->level > 9)
989 return 15;
990
991 h->flags32 = ntohl(h->flags32);
992 if (h->flags32 & F_H_FILTER)
993 return 16; /* filter not supported */
1015 /* check reserved flags */ 994 /* check reserved flags */
1016 if (h->flags & F_RESERVED) 995 if (h->flags32 & F_RESERVED)
1017 return -13; 996 return -13;
1018 997
998 l = h->len_and_name[0];
999 if (l > 0)
1000 /* UNUSED */ f_read(h->len_and_name+1, l);
1001 /* UNUSED h->len_and_name[1+l] = 0; */
1002
1003 checksum = chksum_getresult(h->flags32);
1004 if (read32() != checksum)
1005 return 2;
1006
1019 /* skip extra field [not used yet] */ 1007 /* skip extra field [not used yet] */
1020 if (h->flags & F_H_EXTRA_FIELD) { 1008 if (h->flags32 & F_H_EXTRA_FIELD) {
1009 uint32_t extra_field_len;
1010 uint32_t extra_field_checksum;
1021 uint32_t k; 1011 uint32_t k;
1012 char dummy;
1022 1013
1023 /* note: the checksum also covers the length */ 1014 /* note: the checksum also covers the length */
1024 init_chksum(&G.chksum_in); 1015 init_chksum();
1025 h->extra_field_len = f_read32(); 1016 extra_field_len = f_read32();
1026 for (k = 0; k < h->extra_field_len; k++) 1017 for (k = 0; k < extra_field_len; k++)
1027 f_read8(); 1018 f_read(&dummy, 1);
1028 checksum = chksum_getresult(&G.chksum_in, h); 1019 checksum = chksum_getresult(h->flags32);
1029 h->extra_field_checksum = f_read32(); 1020 extra_field_checksum = read32();
1030 if (h->extra_field_checksum != checksum) 1021 if (extra_field_checksum != checksum)
1031 return 3; 1022 return 3;
1032 } 1023 }
1033 1024
1034 return 0; 1025 return 0;
1035} 1026}
1036 1027
1037static void p_header(header_t *h)
1038{
1039 int r;
1040
1041 r = read_header(h);
1042 if (r == 0)
1043 return;
1044 bb_error_msg_and_die("header_error %d", r);
1045}
1046
1047/**********************************************************************/ 1028/**********************************************************************/
1048// compress 1029// compress
1049/**********************************************************************/ 1030/**********************************************************************/
1050static void lzo_set_method(header_t *h) 1031static void lzo_set_method(header_t *h)
1051{ 1032{
1052 int level = 1; 1033 smallint level;
1034
1035 /* levels 2..6 or none (defaults to level 3) */
1036 h->method = M_LZO1X_1;
1037 level = 5; /* levels 2-6 are actually the same */
1053 1038
1054 if (option_mask32 & OPT_1) { 1039 if (option_mask32 & OPT_1) {
1055 h->method = M_LZO1X_1_15; 1040 h->method = M_LZO1X_1_15;
1056 } else if (option_mask32 & OPT_789) { 1041 level = 1;
1042 }
1043 if (option_mask32 & OPT_789) {
1057#if ENABLE_LZOP_COMPR_HIGH 1044#if ENABLE_LZOP_COMPR_HIGH
1058 h->method = M_LZO1X_999; 1045 h->method = M_LZO1X_999;
1046 level = 9;
1059 if (option_mask32 & OPT_7) 1047 if (option_mask32 & OPT_7)
1060 level = 7; 1048 level = 7;
1061 else if (option_mask32 & OPT_8) 1049 else if (option_mask32 & OPT_8)
1062 level = 8; 1050 level = 8;
1063 else
1064 level = 9;
1065#else 1051#else
1066 bb_error_msg_and_die("high compression not compiled in"); 1052 bb_error_msg_and_die("high compression not compiled in");
1067#endif 1053#endif
1068 } else { /* levels 2..6 or none (defaults to level 3) */
1069 h->method = M_LZO1X_1;
1070 level = 5; /* levels 2-6 are actually the same */
1071 } 1054 }
1072 1055
1073 h->level = level; 1056 h->level = level;
@@ -1082,18 +1065,21 @@ static int do_lzo_compress(void)
1082 1065
1083 lzo_set_method(h); 1066 lzo_set_method(h);
1084 1067
1085 h->version = (LZOP_VERSION & 0xffff); 1068 h->version_be16 = htons(LZOP_VERSION & 0xffff);
1086 h->version_needed_to_extract = 0x0940; 1069 h->version_needed_to_extract_be16 = htons(0x0940);
1087 h->lib_version = lzo_version() & 0xffff; 1070 h->lib_version_be16 = htons(lzo_version() & 0xffff);
1088 1071
1089 h->flags = (F_OS & F_OS_MASK) | (F_CS & F_CS_MASK); 1072 h->flags32 = htonl((F_OS & F_OS_MASK) | (F_CS & F_CS_MASK));
1090 1073
1091 if (!(option_mask32 & OPT_F) || h->method == M_LZO1X_999) { 1074 if (!(option_mask32 & OPT_F) || h->method == M_LZO1X_999) {
1092 h->flags |= F_ADLER32_D; 1075 h->flags32 |= htonl(F_ADLER32_D);
1093 if (option_mask32 & OPT_C) 1076 if (option_mask32 & OPT_C)
1094 h->flags |= F_ADLER32_C; 1077 h->flags32 |= htonl(F_ADLER32_C);
1095 } 1078 }
1079
1080 /* write_header() also converts h->flags32 to native endianness */
1096 write_header(h); 1081 write_header(h);
1082
1097 return lzo_compress(h); 1083 return lzo_compress(h);
1098#undef h 1084#undef h
1099} 1085}
@@ -1103,11 +1089,14 @@ static int do_lzo_compress(void)
1103/**********************************************************************/ 1089/**********************************************************************/
1104static int do_lzo_decompress(void) 1090static int do_lzo_decompress(void)
1105{ 1091{
1092 int r;
1106 header_t header; 1093 header_t header;
1107 1094
1108 check_magic(); 1095 check_magic();
1109 p_header(&header); 1096 r = read_header(&header);
1110 return lzo_decompress(&header); 1097 if (r != 0)
1098 bb_error_msg_and_die("header_error %d", r);
1099 return lzo_decompress(header.flags32);
1111} 1100}
1112 1101
1113static char* FAST_FUNC make_new_name_lzop(char *filename, const char *expected_ext UNUSED_PARAM) 1102static char* FAST_FUNC make_new_name_lzop(char *filename, const char *expected_ext UNUSED_PARAM)
@@ -1132,6 +1121,8 @@ static IF_DESKTOP(long long) int FAST_FUNC pack_lzop(transformer_state_t *xstate
1132int lzop_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 1121int lzop_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1133int lzop_main(int argc UNUSED_PARAM, char **argv) 1122int lzop_main(int argc UNUSED_PARAM, char **argv)
1134{ 1123{
1124 INIT_G();
1125
1135 getopt32(argv, OPTION_STRING); 1126 getopt32(argv, OPTION_STRING);
1136 argv += optind; 1127 argv += optind;
1137 /* -U is "anti -k", invert bit for bbunpack(): */ 1128 /* -U is "anti -k", invert bit for bbunpack(): */
diff --git a/archival/tar.c b/archival/tar.c
index 308ce7701..afd8cfec5 100644
--- a/archival/tar.c
+++ b/archival/tar.c
@@ -799,8 +799,8 @@ static llist_t *append_file_list_to_list(llist_t *list)
799//usage:#define tar_full_usage "\n\n" 799//usage:#define tar_full_usage "\n\n"
800//usage: IF_FEATURE_TAR_CREATE("Create, extract, ") 800//usage: IF_FEATURE_TAR_CREATE("Create, extract, ")
801//usage: IF_NOT_FEATURE_TAR_CREATE("Extract ") 801//usage: IF_NOT_FEATURE_TAR_CREATE("Extract ")
802//usage: "or list files from a tar file\n" 802//usage: "or list files from a tar file"
803//usage: "\nOperation:" 803//usage: "\n"
804//usage: IF_FEATURE_TAR_CREATE( 804//usage: IF_FEATURE_TAR_CREATE(
805//usage: "\n c Create" 805//usage: "\n c Create"
806//usage: ) 806//usage: )
diff --git a/coreutils/mv.c b/coreutils/mv.c
index aeafd1e40..6e11197a1 100644
--- a/coreutils/mv.c
+++ b/coreutils/mv.c
@@ -101,7 +101,7 @@ int mv_main(int argc, char **argv)
101 if (fprintf(stderr, "mv: overwrite '%s'? ", dest) < 0) { 101 if (fprintf(stderr, "mv: overwrite '%s'? ", dest) < 0) {
102 goto RET_1; /* Ouch! fprintf failed! */ 102 goto RET_1; /* Ouch! fprintf failed! */
103 } 103 }
104 if (!bb_ask_confirmation()) { 104 if (!bb_ask_y_confirmation()) {
105 goto RET_0; 105 goto RET_0;
106 } 106 }
107 } 107 }
diff --git a/coreutils/nice.c b/coreutils/nice.c
index aa8b06cce..da1696c42 100644
--- a/coreutils/nice.c
+++ b/coreutils/nice.c
@@ -22,7 +22,6 @@
22//usage: "Change scheduling priority, run PROG\n" 22//usage: "Change scheduling priority, run PROG\n"
23//usage: "\n -n ADJUST Adjust priority by ADJUST" 23//usage: "\n -n ADJUST Adjust priority by ADJUST"
24 24
25#include <sys/resource.h>
26#include "libbb.h" 25#include "libbb.h"
27 26
28int nice_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 27int nice_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
diff --git a/coreutils/sort.c b/coreutils/sort.c
index b39297a26..4d741e76d 100644
--- a/coreutils/sort.c
+++ b/coreutils/sort.c
@@ -18,16 +18,24 @@
18//config: sort is used to sort lines of text in specified files. 18//config: sort is used to sort lines of text in specified files.
19//config: 19//config:
20//config:config FEATURE_SORT_BIG 20//config:config FEATURE_SORT_BIG
21//config: bool "Full SuSv3 compliant sort (support -ktcsbdfiozgM)" 21//config: bool "Full SuSv3 compliant sort (support -ktcbdfiogM)"
22//config: default y 22//config: default y
23//config: depends on SORT 23//config: depends on SORT
24//config: help 24//config: help
25//config: Without this, sort only supports -r, -u, and an integer version 25//config: Without this, sort only supports -rusz, and an integer version
26//config: of -n. Selecting this adds sort keys, floating point support, and 26//config: of -n. Selecting this adds sort keys, floating point support, and
27//config: more. This adds a little over 3k to a nonstatic build on x86. 27//config: more. This adds a little over 3k to a nonstatic build on x86.
28//config: 28//config:
29//config: The SuSv3 sort standard is available at: 29//config: The SuSv3 sort standard is available at:
30//config: http://www.opengroup.org/onlinepubs/007904975/utilities/sort.html 30//config: http://www.opengroup.org/onlinepubs/007904975/utilities/sort.html
31//config:
32//config:config FEATURE_SORT_OPTIMIZE_MEMORY
33//config: bool "Use less memory (but might be slower)"
34//config: default n # defaults to N since we are size-paranoid tribe
35//config: depends on SORT
36//config: help
37//config: Attempt to use less memory (by storing only one copy
38//config: of duplicated lines, and such). Useful if you work on huge files.
31 39
32//applet:IF_SORT(APPLET_NOEXEC(sort, sort, BB_DIR_USR_BIN, BB_SUID_DROP, sort)) 40//applet:IF_SORT(APPLET_NOEXEC(sort, sort, BB_DIR_USR_BIN, BB_SUID_DROP, sort))
33 41
@@ -46,26 +54,22 @@
46//usage: "\n -f Ignore case" 54//usage: "\n -f Ignore case"
47//usage: "\n -i Ignore unprintable characters" 55//usage: "\n -i Ignore unprintable characters"
48//usage: "\n -d Dictionary order (blank or alphanumeric only)" 56//usage: "\n -d Dictionary order (blank or alphanumeric only)"
49//usage: "\n -g General numerical sort"
50//usage: "\n -M Sort month"
51//usage: ) 57//usage: )
52//-h, --human-numeric-sort: compare human readable numbers (e.g., 2K 1G) 58//-h, --human-numeric-sort: compare human readable numbers (e.g., 2K 1G)
53//usage: "\n -n Sort numbers" 59//usage: "\n -n Sort numbers"
54//usage: IF_FEATURE_SORT_BIG( 60//usage: IF_FEATURE_SORT_BIG(
61//usage: "\n -g General numerical sort"
62//usage: "\n -M Sort month"
55//usage: "\n -t CHAR Field separator" 63//usage: "\n -t CHAR Field separator"
56//usage: "\n -k N[,M] Sort by Nth field" 64//usage: "\n -k N[,M] Sort by Nth field"
57//usage: ) 65//usage: )
58//usage: "\n -r Reverse sort order" 66//usage: "\n -r Reverse sort order"
59//usage: IF_FEATURE_SORT_BIG(
60//usage: "\n -s Stable (don't sort ties alphabetically)" 67//usage: "\n -s Stable (don't sort ties alphabetically)"
61//usage: )
62//usage: "\n -u Suppress duplicate lines" 68//usage: "\n -u Suppress duplicate lines"
63//usage: IF_FEATURE_SORT_BIG(
64//usage: "\n -z Lines are terminated by NUL, not newline" 69//usage: "\n -z Lines are terminated by NUL, not newline"
65///////: "\n -m Ignored for GNU compatibility" 70///////: "\n -m Ignored for GNU compatibility"
66///////: "\n -S BUFSZ Ignored for GNU compatibility" 71///////: "\n -S BUFSZ Ignored for GNU compatibility"
67///////: "\n -T TMPDIR Ignored for GNU compatibility" 72///////: "\n -T TMPDIR Ignored for GNU compatibility"
68//usage: )
69//usage: 73//usage:
70//usage:#define sort_example_usage 74//usage:#define sort_example_usage
71//usage: "$ echo -e \"e\\nf\\nb\\nd\\nc\\na\" | sort\n" 75//usage: "$ echo -e \"e\\nf\\nb\\nd\\nc\\na\" | sort\n"
@@ -404,6 +408,15 @@ int sort_main(int argc UNUSED_PARAM, char **argv)
404 int i; 408 int i;
405 int linecount; 409 int linecount;
406 unsigned opts; 410 unsigned opts;
411#if ENABLE_FEATURE_SORT_OPTIMIZE_MEMORY
412 bool can_drop_dups;
413 size_t prev_len = 0;
414 char *prev_line = (char*) "";
415 /* Postpone optimizing if the input is small, < 16k lines:
416 * even just free()ing duplicate lines takes time.
417 */
418 size_t count_to_optimize_dups = 0x3fff;
419#endif
407 420
408 xfunc_error_retval = 2; 421 xfunc_error_retval = 2;
409 422
@@ -412,6 +425,29 @@ int sort_main(int argc UNUSED_PARAM, char **argv)
412 sort_opt_str, 425 sort_opt_str,
413 &str_ignored, &str_ignored, &str_o, &lst_k, &str_t 426 &str_ignored, &str_ignored, &str_o, &lst_k, &str_t
414 ); 427 );
428#if ENABLE_FEATURE_SORT_OPTIMIZE_MEMORY
429 /* Can drop dups only if -u but no "complicating" options,
430 * IOW: if we do a full line compares. Safe options:
431 * -o FILE Output to FILE
432 * -z Lines are terminated by NUL, not newline
433 * -r Reverse sort order
434 * -s Stable (don't sort ties alphabetically)
435 * Not sure about these:
436 * -b Ignore leading blanks
437 * -f Ignore case
438 * -i Ignore unprintable characters
439 * -d Dictionary order (blank or alphanumeric only)
440 * -n Sort numbers
441 * -g General numerical sort
442 * -M Sort month
443 */
444 can_drop_dups = ((opts & ~(FLAG_o|FLAG_z|FLAG_r|FLAG_s)) == FLAG_u);
445 /* Stable sort needs every line to be uniquely allocated,
446 * disable optimization to reuse strings:
447 */
448 if (opts & FLAG_s)
449 count_to_optimize_dups = (size_t)-1L;
450#endif
415 /* global b strips leading and trailing spaces */ 451 /* global b strips leading and trailing spaces */
416 if (opts & FLAG_b) 452 if (opts & FLAG_b)
417 option_mask32 |= FLAG_bb; 453 option_mask32 |= FLAG_bb;
@@ -489,6 +525,44 @@ int sort_main(int argc UNUSED_PARAM, char **argv)
489 char *line = GET_LINE(fp); 525 char *line = GET_LINE(fp);
490 if (!line) 526 if (!line)
491 break; 527 break;
528
529#if ENABLE_FEATURE_SORT_OPTIMIZE_MEMORY
530 if (count_to_optimize_dups != 0)
531 count_to_optimize_dups--;
532 if (count_to_optimize_dups == 0) {
533 size_t len;
534 char *new_line;
535
536 /* On kernel/linux/arch/ *.[ch] files,
537 * this reduces memory usage by 6%.
538 * yes | head -99999999 | sort
539 * goes down from 1900Mb to 380 Mb.
540 */
541 len = strlen(line);
542 if (len <= prev_len) {
543 new_line = prev_line + (prev_len - len);
544 if (strcmp(line, new_line) == 0) {
545 /* it's a tail of the prev line */
546 if (can_drop_dups && prev_len == len) {
547 /* it's identical to prev line */
548 free(line);
549 continue;
550 }
551 free(line);
552 line = new_line;
553 /* continue using longer prev_line
554 * for future tail tests.
555 */
556 goto skip;
557 }
558 }
559 prev_len = len;
560 prev_line = line;
561 skip: ;
562 }
563#else
564//TODO: lighter version which only drops total dups if can_drop_dups == true
565#endif
492 lines = xrealloc_vector(lines, 6, linecount); 566 lines = xrealloc_vector(lines, 6, linecount);
493 lines[linecount++] = line; 567 lines[linecount++] = line;
494 } 568 }
diff --git a/coreutils/test.c b/coreutils/test.c
index a8286525a..824ce3b5a 100644
--- a/coreutils/test.c
+++ b/coreutils/test.c
@@ -76,7 +76,6 @@
76//usage: "1\n" 76//usage: "1\n"
77 77
78#include "libbb.h" 78#include "libbb.h"
79#include <setjmp.h>
80 79
81/* This is a NOFORK applet. Be very careful! */ 80/* This is a NOFORK applet. Be very careful! */
82 81
diff --git a/debianutils/start_stop_daemon.c b/debianutils/start_stop_daemon.c
index c8b7fa8f2..fa77a7e00 100644
--- a/debianutils/start_stop_daemon.c
+++ b/debianutils/start_stop_daemon.c
@@ -116,8 +116,6 @@ Misc options:
116//usage: ) 116//usage: )
117//usage: "\n -q Quiet" 117//usage: "\n -q Quiet"
118 118
119#include <sys/resource.h>
120
121/* Override ENABLE_FEATURE_PIDFILE */ 119/* Override ENABLE_FEATURE_PIDFILE */
122#define WANT_PIDFILE 1 120#define WANT_PIDFILE 1
123#include "libbb.h" 121#include "libbb.h"
diff --git a/findutils/xargs.c b/findutils/xargs.c
index 9e475d7e6..df2300207 100644
--- a/findutils/xargs.c
+++ b/findutils/xargs.c
@@ -570,27 +570,28 @@ static int xargs_ask_confirmation(void)
570{ 570{
571#if !ENABLE_PLATFORM_MINGW32 571#if !ENABLE_PLATFORM_MINGW32
572 FILE *tty_stream; 572 FILE *tty_stream;
573#endif 573 int r;
574 int c, savec;
575 574
576#if !ENABLE_PLATFORM_MINGW32
577 tty_stream = xfopen_for_read(CURRENT_TTY); 575 tty_stream = xfopen_for_read(CURRENT_TTY);
578#endif 576
579 fputs(" ?...", stderr); 577 fputs(" ?...", stderr);
580 fflush_all(); 578 r = bb_ask_y_confirmation_FILE(tty_stream);
581#if !ENABLE_PLATFORM_MINGW32 579
582 c = savec = getc(tty_stream);
583 while (c != EOF && c != '\n')
584 c = getc(tty_stream);
585 fclose(tty_stream); 580 fclose(tty_stream);
586#else 581#else
582 int r, c, savec;
583
584 fputs(" ?...", stderr);
585 fflush_all();
587 c = savec = getche(); 586 c = savec = getche();
588 while (c != EOF && c != '\r') 587 while (c != EOF && c != '\r')
589 c = getche(); 588 c = getche();
590 fputs("\n", stderr); 589 fputs("\n", stderr);
591 fflush_all(); 590 fflush_all();
591 r = (savec == 'y' || savec == 'Y');
592#endif 592#endif
593 return (savec == 'y' || savec == 'Y'); 593
594 return r;
594} 595}
595#else 596#else
596# define xargs_ask_confirmation() 1 597# define xargs_ask_confirmation() 1
diff --git a/include/bb_archive.h b/include/bb_archive.h
index 91d5561fc..084ba9470 100644
--- a/include/bb_archive.h
+++ b/include/bb_archive.h
@@ -224,7 +224,7 @@ const llist_t *find_list_entry2(const llist_t *list, const char *filename) FAST_
224 224
225/* A bit of bunzip2 internals are exposed for compressed help support: */ 225/* A bit of bunzip2 internals are exposed for compressed help support: */
226typedef struct bunzip_data bunzip_data; 226typedef struct bunzip_data bunzip_data;
227int start_bunzip(bunzip_data **bdp, int in_fd, const void *inbuf, int len) FAST_FUNC; 227int start_bunzip(void *, bunzip_data **bdp, int in_fd, const void *inbuf, int len) FAST_FUNC;
228/* NB: read_bunzip returns < 0 on error, or the number of *unfilled* bytes 228/* NB: read_bunzip returns < 0 on error, or the number of *unfilled* bytes
229 * in outbuf. IOW: on EOF returns len ("all bytes are not filled"), not 0: */ 229 * in outbuf. IOW: on EOF returns len ("all bytes are not filled"), not 0: */
230int read_bunzip(bunzip_data *bd, char *outbuf, int len) FAST_FUNC; 230int read_bunzip(bunzip_data *bd, char *outbuf, int len) FAST_FUNC;
diff --git a/include/libbb.h b/include/libbb.h
index d948f88f3..876875045 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -41,6 +41,7 @@
41#include <poll.h> 41#include <poll.h>
42#include <sys/ioctl.h> 42#include <sys/ioctl.h>
43#include <sys/mman.h> 43#include <sys/mman.h>
44#include <sys/resource.h>
44#include <sys/socket.h> 45#include <sys/socket.h>
45#include <sys/stat.h> 46#include <sys/stat.h>
46#include <sys/time.h> 47#include <sys/time.h>
@@ -1456,11 +1457,13 @@ extern int set_loop(char **devname, const char *file, unsigned long long offset,
1456#define BB_LO_FLAGS_READ_ONLY 1 1457#define BB_LO_FLAGS_READ_ONLY 1
1457#define BB_LO_FLAGS_AUTOCLEAR 4 1458#define BB_LO_FLAGS_AUTOCLEAR 4
1458 1459
1459/* Like bb_ask below, but asks on stdin with no timeout. */ 1460/* Returns malloced str */
1460char *bb_ask_stdin(const char * prompt) FAST_FUNC; 1461char *bb_ask_noecho(int fd, int timeout, const char *prompt) FAST_FUNC;
1461//TODO: pass buf pointer or return allocated buf (avoid statics)? 1462/* Like bb_ask_noecho, but asks on stdin with no timeout. */
1462char *bb_ask(const int fd, int timeout, const char * prompt) FAST_FUNC; 1463char *bb_ask_noecho_stdin(const char *prompt) FAST_FUNC;
1463int bb_ask_confirmation(void) FAST_FUNC; 1464
1465int bb_ask_y_confirmation_FILE(FILE *fp) FAST_FUNC;
1466int bb_ask_y_confirmation(void) FAST_FUNC;
1464 1467
1465/* Returns -1 if input is invalid. current_mode is a base for e.g. "u+rw" */ 1468/* Returns -1 if input is invalid. current_mode is a base for e.g. "u+rw" */
1466int bb_parse_mode(const char* s, unsigned cur_mode) FAST_FUNC; 1469int bb_parse_mode(const char* s, unsigned cur_mode) FAST_FUNC;
diff --git a/init/init.c b/init/init.c
index cac165fc7..6439e2bcd 100644
--- a/init/init.c
+++ b/init/init.c
@@ -130,7 +130,6 @@
130#include "libbb.h" 130#include "libbb.h"
131#include "common_bufsiz.h" 131#include "common_bufsiz.h"
132#include <syslog.h> 132#include <syslog.h>
133#include <sys/resource.h>
134#ifdef __linux__ 133#ifdef __linux__
135# include <linux/vt.h> 134# include <linux/vt.h>
136# include <sys/sysinfo.h> 135# include <sys/sysinfo.h>
diff --git a/libbb/appletlib.c b/libbb/appletlib.c
index 8fae11998..6a189a8c9 100644
--- a/libbb/appletlib.c
+++ b/libbb/appletlib.c
@@ -110,14 +110,21 @@ static const char *unpack_usage_messages(void)
110 char *outbuf = NULL; 110 char *outbuf = NULL;
111 bunzip_data *bd; 111 bunzip_data *bd;
112 int i; 112 int i;
113 jmp_buf jmpbuf;
113 114
114 i = start_bunzip(&bd, 115 /* Setup for I/O error handling via longjmp */
116 i = setjmp(jmpbuf);
117 if (i == 0) {
118 i = start_bunzip(&jmpbuf,
119 &bd,
115 /* src_fd: */ -1, 120 /* src_fd: */ -1,
116 /* inbuf: */ packed_usage, 121 /* inbuf: */ packed_usage,
117 /* len: */ sizeof(packed_usage)); 122 /* len: */ sizeof(packed_usage)
118 /* read_bunzip can longjmp to start_bunzip, and ultimately 123 );
119 * end up here with i != 0 on read data errors! Not trivial */ 124 }
120 if (!i) { 125 /* read_bunzip can longjmp and end up here with i != 0
126 * on read data errors! Not trivial */
127 if (i == 0) {
121 /* Cannot use xmalloc: will leak bd in NOFORK case! */ 128 /* Cannot use xmalloc: will leak bd in NOFORK case! */
122 outbuf = malloc_or_warn(sizeof(UNPACKED_USAGE)); 129 outbuf = malloc_or_warn(sizeof(UNPACKED_USAGE));
123 if (outbuf) 130 if (outbuf)
diff --git a/libbb/ask_confirmation.c b/libbb/ask_confirmation.c
index 6fbed89f4..e4814e215 100644
--- a/libbb/ask_confirmation.c
+++ b/libbb/ask_confirmation.c
@@ -1,6 +1,6 @@
1/* vi: set sw=4 ts=4: */ 1/* vi: set sw=4 ts=4: */
2/* 2/*
3 * bb_ask_confirmation implementation for busybox 3 * bb_ask_y_confirmation implementation for busybox
4 * 4 *
5 * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org> 5 * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
6 * 6 *
@@ -8,15 +8,16 @@
8 */ 8 */
9#include "libbb.h" 9#include "libbb.h"
10 10
11/* Read a line from stdin. If the first non-whitespace char is 'y' or 'Y', 11/* Read a line from fp. If the first non-whitespace char is 'y' or 'Y',
12 * return 1. Otherwise return 0. 12 * return 1. Otherwise return 0.
13 */ 13 */
14int FAST_FUNC bb_ask_confirmation(void) 14int FAST_FUNC bb_ask_y_confirmation_FILE(FILE *fp)
15{ 15{
16 char first = 0; 16 char first = 0;
17 int c; 17 int c;
18 18
19 while (((c = getchar()) != EOF) && (c != '\n')) { 19 fflush_all();
20 while (((c = fgetc(fp)) != EOF) && (c != '\n')) {
20 if (first == 0 && !isblank(c)) { 21 if (first == 0 && !isblank(c)) {
21 first = c|0x20; 22 first = c|0x20;
22 } 23 }
@@ -24,3 +25,8 @@ int FAST_FUNC bb_ask_confirmation(void)
24 25
25 return first == 'y'; 26 return first == 'y';
26} 27}
28
29int FAST_FUNC bb_ask_y_confirmation(void)
30{
31 return bb_ask_y_confirmation_FILE(stdin);
32}
diff --git a/libbb/bb_askpass.c b/libbb/bb_askpass.c
index aae35ec41..2dcead35a 100644
--- a/libbb/bb_askpass.c
+++ b/libbb/bb_askpass.c
@@ -13,16 +13,9 @@ static void askpass_timeout(int UNUSED_PARAM ignore)
13{ 13{
14} 14}
15 15
16char* FAST_FUNC bb_ask_stdin(const char *prompt) 16char* FAST_FUNC bb_ask_noecho(int fd, int timeout, const char *prompt)
17{ 17{
18 return bb_ask(STDIN_FILENO, 0, prompt); 18#define MAX_LINE 0xfff
19}
20char* FAST_FUNC bb_ask(const int fd, int timeout, const char *prompt)
21{
22 /* Was static char[BIGNUM] */
23 enum { sizeof_passwd = 128 };
24
25 char *passwd;
26 char *ret; 19 char *ret;
27 int i; 20 int i;
28 struct sigaction sa, oldsa; 21 struct sigaction sa, oldsa;
@@ -37,16 +30,17 @@ char* FAST_FUNC bb_ask(const int fd, int timeout, const char *prompt)
37 30
38 tcgetattr(fd, &oldtio); 31 tcgetattr(fd, &oldtio);
39 tio = oldtio; 32 tio = oldtio;
40#if 0 33 /* Switch off echo. ECHOxyz meaning:
41 /* Switch off UPPERCASE->lowercase conversion (never used since 198x) 34 * ECHO echo input chars
42 * and XON/XOFF (why we want to mess with this??) 35 * ECHOE echo BS-SP-BS on erase character
36 * ECHOK echo kill char specially, not as ^c (ECHOKE controls how exactly)
37 * ECHOKE erase all input via BS-SP-BS on kill char (else go to next line)
38 * ECHOCTL Echo ctrl chars as ^c (else echo verbatim:
39 * e.g. up arrow emits "ESC-something" and thus moves cursor up!)
40 * ECHONL Echo NL even if ECHO is not set
41 * ECHOPRT On erase, echo erased chars
42 * [qwe<BS><BS><BS> input looks like "qwe\ewq/" on screen]
43 */ 43 */
44# ifndef IUCLC
45# define IUCLC 0
46# endif
47 tio.c_iflag &= ~(IUCLC|IXON|IXOFF|IXANY);
48#endif
49 /* Switch off echo */
50 tio.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL); 44 tio.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL);
51 tcsetattr(fd, TCSANOW, &tio); 45 tcsetattr(fd, TCSANOW, &tio);
52 46
@@ -60,21 +54,30 @@ char* FAST_FUNC bb_ask(const int fd, int timeout, const char *prompt)
60 alarm(timeout); 54 alarm(timeout);
61 } 55 }
62 56
63 passwd = auto_string(xmalloc(sizeof_passwd)); 57 ret = NULL;
64 ret = passwd;
65 i = 0; 58 i = 0;
66 while (1) { 59 while (1) {
67 int r = read(fd, &ret[i], 1); 60 int r;
61
62 /* User input is uber-slow, no need to optimize reallocs.
63 * Grow it on every char.
64 */
65 ret = xrealloc(ret, i + 2);
66 r = read(fd, &ret[i], 1);
67
68 if ((i == 0 && r == 0) /* EOF (^D) with no password */ 68 if ((i == 0 && r == 0) /* EOF (^D) with no password */
69 || r < 0 69 || r < 0 /* read is interrupted by timeout or ^C */
70 ) { 70 ) {
71 /* read is interrupted by timeout or ^C */ 71 ret[i] = '\0'; /* paranoia */
72 nuke_str(ret); /* paranoia */
73 free(ret);
72 ret = NULL; 74 ret = NULL;
73 break; 75 break;
74 } 76 }
77
75 if (r == 0 /* EOF */ 78 if (r == 0 /* EOF */
76 || ret[i] == '\r' || ret[i] == '\n' /* EOL */ 79 || ret[i] == '\r' || ret[i] == '\n' /* EOL */
77 || ++i == sizeof_passwd-1 /* line limit */ 80 || ++i == MAX_LINE /* line limit */
78 ) { 81 ) {
79 ret[i] = '\0'; 82 ret[i] = '\0';
80 break; 83 break;
@@ -90,3 +93,7 @@ char* FAST_FUNC bb_ask(const int fd, int timeout, const char *prompt)
90 fflush_all(); 93 fflush_all();
91 return ret; 94 return ret;
92} 95}
96char* FAST_FUNC bb_ask_noecho_stdin(const char *prompt)
97{
98 return bb_ask_noecho(STDIN_FILENO, 0, prompt);
99}
diff --git a/libbb/copy_file.c b/libbb/copy_file.c
index 5372d8680..c60765d95 100644
--- a/libbb/copy_file.c
+++ b/libbb/copy_file.c
@@ -48,7 +48,7 @@ static int ask_and_unlink(const char *dest, int flags)
48 // (No "opening without O_EXCL", no "unlink only if -f") 48 // (No "opening without O_EXCL", no "unlink only if -f")
49 // Or else we will end up having 3 open()s! 49 // Or else we will end up having 3 open()s!
50 fprintf(stderr, "%s: overwrite '%s'? ", applet_name, dest); 50 fprintf(stderr, "%s: overwrite '%s'? ", applet_name, dest);
51 if (!bb_ask_confirmation()) 51 if (!bb_ask_y_confirmation())
52 return 0; /* not allowed to overwrite */ 52 return 0; /* not allowed to overwrite */
53 } 53 }
54 if (unlink(dest) < 0) { 54 if (unlink(dest) < 0) {
diff --git a/libbb/correct_password.c b/libbb/correct_password.c
index a6f7d9b3d..cbe6cb387 100644
--- a/libbb/correct_password.c
+++ b/libbb/correct_password.c
@@ -106,7 +106,7 @@ int FAST_FUNC ask_and_check_password_extended(const struct passwd *pw,
106 if (!pw_pass[0]) /* empty password field? */ 106 if (!pw_pass[0]) /* empty password field? */
107 return CHECKPASS_PW_HAS_EMPTY_PASSWORD; 107 return CHECKPASS_PW_HAS_EMPTY_PASSWORD;
108 108
109 plaintext = bb_ask(STDIN_FILENO, timeout, prompt); 109 plaintext = bb_ask_noecho(STDIN_FILENO, timeout, prompt);
110 if (!plaintext) { 110 if (!plaintext) {
111 /* EOF (such as ^D) or error (such as ^C) or timeout */ 111 /* EOF (such as ^D) or error (such as ^C) or timeout */
112 return -1; 112 return -1;
diff --git a/libbb/dump.c b/libbb/dump.c
index db91fcfe7..5941ef902 100644
--- a/libbb/dump.c
+++ b/libbb/dump.c
@@ -464,11 +464,9 @@ static const char conv_str[] ALIGN1 =
464 "\v" "\\""v""\0" 464 "\v" "\\""v""\0"
465 ; 465 ;
466 466
467
468static void conv_c(PR *pr, unsigned char *p) 467static void conv_c(PR *pr, unsigned char *p)
469{ 468{
470 const char *str = conv_str; 469 const char *str = conv_str;
471 char buf[10];
472 470
473 do { 471 do {
474 if (*p == *str) { 472 if (*p == *str) {
@@ -482,7 +480,9 @@ static void conv_c(PR *pr, unsigned char *p)
482 *pr->cchar = 'c'; 480 *pr->cchar = 'c';
483 printf(pr->fmt, *p); 481 printf(pr->fmt, *p);
484 } else { 482 } else {
485 sprintf(buf, "%03o", (int) *p); 483 char buf[4];
484 /* gcc-8.0.1 needs lots of casts to shut up */
485 sprintf(buf, "%03o", (unsigned)(uint8_t)*p);
486 str = buf; 486 str = buf;
487 strpr: 487 strpr:
488 *pr->cchar = 's'; 488 *pr->cchar = 's';
diff --git a/libbb/lineedit.c b/libbb/lineedit.c
index 36b6abe2b..431f1c048 100644
--- a/libbb/lineedit.c
+++ b/libbb/lineedit.c
@@ -466,7 +466,8 @@ static void put_till_end_and_adv_cursor(void)
466static void goto_new_line(void) 466static void goto_new_line(void)
467{ 467{
468 put_till_end_and_adv_cursor(); 468 put_till_end_and_adv_cursor();
469 if (cmdedit_x != 0) 469 /* "cursor == 0" is only if prompt is "" and user input is empty */
470 if (cursor == 0 || cmdedit_x != 0)
470 bb_putchar('\n'); 471 bb_putchar('\n');
471} 472}
472 473
diff --git a/libbb/remove_file.c b/libbb/remove_file.c
index 8a1324393..86c9a5c56 100644
--- a/libbb/remove_file.c
+++ b/libbb/remove_file.c
@@ -39,9 +39,9 @@ int FAST_FUNC remove_file(const char *path, int flags)
39 if ((!(flags & FILEUTILS_FORCE) && access(path, W_OK) < 0 && isatty(0)) 39 if ((!(flags & FILEUTILS_FORCE) && access(path, W_OK) < 0 && isatty(0))
40 || (flags & FILEUTILS_INTERACTIVE) 40 || (flags & FILEUTILS_INTERACTIVE)
41 ) { 41 ) {
42 fprintf(stderr, "%s: descend into directory '%s'? ", applet_name, 42 fprintf(stderr, "%s: descend into directory '%s'? ",
43 path); 43 applet_name, path);
44 if (!bb_ask_confirmation()) 44 if (!bb_ask_y_confirmation())
45 return 0; 45 return 0;
46 } 46 }
47 47
@@ -67,8 +67,9 @@ int FAST_FUNC remove_file(const char *path, int flags)
67 } 67 }
68 68
69 if (flags & FILEUTILS_INTERACTIVE) { 69 if (flags & FILEUTILS_INTERACTIVE) {
70 fprintf(stderr, "%s: remove directory '%s'? ", applet_name, path); 70 fprintf(stderr, "%s: remove directory '%s'? ",
71 if (!bb_ask_confirmation()) 71 applet_name, path);
72 if (!bb_ask_y_confirmation())
72 return status; 73 return status;
73 } 74 }
74 75
@@ -92,7 +93,7 @@ int FAST_FUNC remove_file(const char *path, int flags)
92 || (flags & FILEUTILS_INTERACTIVE) 93 || (flags & FILEUTILS_INTERACTIVE)
93 ) { 94 ) {
94 fprintf(stderr, "%s: remove '%s'? ", applet_name, path); 95 fprintf(stderr, "%s: remove '%s'? ", applet_name, path);
95 if (!bb_ask_confirmation()) 96 if (!bb_ask_y_confirmation())
96 return 0; 97 return 0;
97 } 98 }
98 99
diff --git a/loginutils/cryptpw.c b/loginutils/cryptpw.c
index 76138a61f..fbb7f0515 100644
--- a/loginutils/cryptpw.c
+++ b/loginutils/cryptpw.c
@@ -133,8 +133,8 @@ int cryptpw_main(int argc UNUSED_PARAM, char **argv)
133 if (!password) { 133 if (!password) {
134 /* Only mkpasswd, and only from tty, prompts. 134 /* Only mkpasswd, and only from tty, prompts.
135 * Otherwise it is a plain read. */ 135 * Otherwise it is a plain read. */
136 password = (ENABLE_MKPASSWD && isatty(STDIN_FILENO) && applet_name[0] == 'm') 136 password = (ENABLE_MKPASSWD && applet_name[0] == 'm' && isatty(STDIN_FILENO))
137 ? bb_ask_stdin("Password: ") 137 ? bb_ask_noecho_stdin("Password: ")
138 : xmalloc_fgetline(stdin) 138 : xmalloc_fgetline(stdin)
139 ; 139 ;
140 /* may still be NULL on EOF/error */ 140 /* may still be NULL on EOF/error */
diff --git a/loginutils/login.c b/loginutils/login.c
index fcdb9592c..25bb5203b 100644
--- a/loginutils/login.c
+++ b/loginutils/login.c
@@ -64,7 +64,6 @@
64#include "libbb.h" 64#include "libbb.h"
65#include "common_bufsiz.h" 65#include "common_bufsiz.h"
66#include <syslog.h> 66#include <syslog.h>
67#include <sys/resource.h>
68 67
69#if ENABLE_SELINUX 68#if ENABLE_SELINUX
70# include <selinux/selinux.h> /* for is_selinux_enabled() */ 69# include <selinux/selinux.h> /* for is_selinux_enabled() */
diff --git a/loginutils/passwd.c b/loginutils/passwd.c
index 3e1ef9abf..59f47fc7b 100644
--- a/loginutils/passwd.c
+++ b/loginutils/passwd.c
@@ -39,7 +39,6 @@
39 39
40#include "libbb.h" 40#include "libbb.h"
41#include <syslog.h> 41#include <syslog.h>
42#include <sys/resource.h> /* setrlimit */
43 42
44static char* new_password(const struct passwd *pw, uid_t myuid, const char *algo) 43static char* new_password(const struct passwd *pw, uid_t myuid, const char *algo)
45{ 44{
@@ -52,7 +51,7 @@ static char* new_password(const struct passwd *pw, uid_t myuid, const char *algo
52 if (myuid != 0 && pw->pw_passwd[0]) { 51 if (myuid != 0 && pw->pw_passwd[0]) {
53 char *encrypted; 52 char *encrypted;
54 53
55 orig = bb_ask_stdin("Old password: "); /* returns ptr to static */ 54 orig = bb_ask_noecho_stdin("Old password: "); /* returns ptr to static */
56 if (!orig) 55 if (!orig)
57 goto err_ret; 56 goto err_ret;
58 encrypted = pw_encrypt(orig, pw->pw_passwd, 1); /* returns malloced str */ 57 encrypted = pw_encrypt(orig, pw->pw_passwd, 1); /* returns malloced str */
@@ -65,11 +64,9 @@ static char* new_password(const struct passwd *pw, uid_t myuid, const char *algo
65 if (ENABLE_FEATURE_CLEAN_UP) 64 if (ENABLE_FEATURE_CLEAN_UP)
66 free(encrypted); 65 free(encrypted);
67 } 66 }
68 orig = xstrdup(orig); /* or else bb_ask_stdin() will destroy it */ 67 newp = bb_ask_noecho_stdin("New password: "); /* returns ptr to static */
69 newp = bb_ask_stdin("New password: "); /* returns ptr to static */
70 if (!newp) 68 if (!newp)
71 goto err_ret; 69 goto err_ret;
72 newp = xstrdup(newp); /* we are going to bb_ask_stdin() again, so save it */
73 if (ENABLE_FEATURE_PASSWD_WEAK_CHECK 70 if (ENABLE_FEATURE_PASSWD_WEAK_CHECK
74 && obscure(orig, newp, pw) 71 && obscure(orig, newp, pw)
75 && myuid != 0 72 && myuid != 0
@@ -77,7 +74,7 @@ static char* new_password(const struct passwd *pw, uid_t myuid, const char *algo
77 goto err_ret; /* non-root is not allowed to have weak passwd */ 74 goto err_ret; /* non-root is not allowed to have weak passwd */
78 } 75 }
79 76
80 cp = bb_ask_stdin("Retype password: "); 77 cp = bb_ask_noecho_stdin("Retype password: ");
81 if (!cp) 78 if (!cp)
82 goto err_ret; 79 goto err_ret;
83 if (strcmp(cp, newp) != 0) { 80 if (strcmp(cp, newp) != 0) {
@@ -99,6 +96,7 @@ static char* new_password(const struct passwd *pw, uid_t myuid, const char *algo
99 if (ENABLE_FEATURE_CLEAN_UP) free(newp); 96 if (ENABLE_FEATURE_CLEAN_UP) free(newp);
100 97
101 nuke_str(cp); 98 nuke_str(cp);
99 if (ENABLE_FEATURE_CLEAN_UP) free(cp);
102 return ret; 100 return ret;
103} 101}
104 102
diff --git a/mailutils/mail.c b/mailutils/mail.c
index eceb89071..7af7edd6c 100644
--- a/mailutils/mail.c
+++ b/mailutils/mail.c
@@ -163,8 +163,8 @@ void FAST_FUNC encode_base64(char *fname, const char *text, const char *eol)
163void FAST_FUNC get_cred_or_die(int fd) 163void FAST_FUNC get_cred_or_die(int fd)
164{ 164{
165 if (isatty(fd)) { 165 if (isatty(fd)) {
166 G.user = xstrdup(bb_ask(fd, /* timeout: */ 0, "User: ")); 166 G.user = bb_ask_noecho(fd, /* timeout: */ 0, "User: ");
167 G.pass = xstrdup(bb_ask(fd, /* timeout: */ 0, "Password: ")); 167 G.pass = bb_ask_noecho(fd, /* timeout: */ 0, "Password: ");
168 } else { 168 } else {
169 G.user = xmalloc_reads(fd, /* maxsize: */ NULL); 169 G.user = xmalloc_reads(fd, /* maxsize: */ NULL);
170 G.pass = xmalloc_reads(fd, /* maxsize: */ NULL); 170 G.pass = xmalloc_reads(fd, /* maxsize: */ NULL);
diff --git a/miscutils/bbconfig.c b/miscutils/bbconfig.c
index 2151c622c..aa42de648 100644
--- a/miscutils/bbconfig.c
+++ b/miscutils/bbconfig.c
@@ -45,13 +45,22 @@ int bbconfig_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
45{ 45{
46#if ENABLE_FEATURE_COMPRESS_BBCONFIG 46#if ENABLE_FEATURE_COMPRESS_BBCONFIG
47 bunzip_data *bd; 47 bunzip_data *bd;
48 int i = start_bunzip(&bd, 48 int i;
49 jmp_buf jmpbuf;
50
51 /* Setup for I/O error handling via longjmp */
52 i = setjmp(jmpbuf);
53 if (i == 0) {
54 i = start_bunzip(&jmpbuf,
55 &bd,
49 /* src_fd: */ -1, 56 /* src_fd: */ -1,
50 /* inbuf: */ bbconfig_config_bz2, 57 /* inbuf: */ bbconfig_config_bz2,
51 /* len: */ sizeof(bbconfig_config_bz2)); 58 /* len: */ sizeof(bbconfig_config_bz2)
52 /* read_bunzip can longjmp to start_bunzip, and ultimately 59 );
53 * end up here with i != 0 on read data errors! Not trivial */ 60 }
54 if (!i) { 61 /* read_bunzip can longjmp and end up here with i != 0
62 * on read data errors! Not trivial */
63 if (i == 0) {
55 /* Cannot use xmalloc: will leak bd in NOFORK case! */ 64 /* Cannot use xmalloc: will leak bd in NOFORK case! */
56 char *outbuf = malloc_or_warn(sizeof(bbconfig_config)); 65 char *outbuf = malloc_or_warn(sizeof(bbconfig_config));
57 if (outbuf) { 66 if (outbuf) {
diff --git a/miscutils/i2c_tools.c b/miscutils/i2c_tools.c
index 82f9842bd..6a2134063 100644
--- a/miscutils/i2c_tools.c
+++ b/miscutils/i2c_tools.c
@@ -421,8 +421,7 @@ static void check_write_funcs(int fd, int mode, int pec)
421static void confirm_or_abort(void) 421static void confirm_or_abort(void)
422{ 422{
423 fprintf(stderr, "Continue? [y/N] "); 423 fprintf(stderr, "Continue? [y/N] ");
424 fflush_all(); 424 if (!bb_ask_y_confirmation())
425 if (!bb_ask_confirmation())
426 bb_error_msg_and_die("aborting"); 425 bb_error_msg_and_die("aborting");
427} 426}
428 427
diff --git a/miscutils/time.c b/miscutils/time.c
index 61f078755..7d6a7a29f 100644
--- a/miscutils/time.c
+++ b/miscutils/time.c
@@ -32,7 +32,6 @@
32//usage: "\n -a Append (else overwrite)" 32//usage: "\n -a Append (else overwrite)"
33 33
34#include "libbb.h" 34#include "libbb.h"
35#include <sys/resource.h> /* getrusage */
36 35
37/* Information on the resources used by a child process. */ 36/* Information on the resources used by a child process. */
38typedef struct { 37typedef struct {
diff --git a/networking/ftpd.c b/networking/ftpd.c
index 8abbf7f57..6ca231c90 100644
--- a/networking/ftpd.c
+++ b/networking/ftpd.c
@@ -57,7 +57,7 @@
57//usage: "[-wvS]"IF_FEATURE_FTPD_AUTHENTICATION(" [-a USER]")" [-t N] [-T N] [DIR]" 57//usage: "[-wvS]"IF_FEATURE_FTPD_AUTHENTICATION(" [-a USER]")" [-t N] [-T N] [DIR]"
58//usage:#define ftpd_full_usage "\n\n" 58//usage:#define ftpd_full_usage "\n\n"
59//usage: IF_NOT_FEATURE_FTPD_AUTHENTICATION( 59//usage: IF_NOT_FEATURE_FTPD_AUTHENTICATION(
60//usage: "Anonymous FTP server. Accesses by clients occur under ftpd's UID.\n" 60//usage: "Anonymous FTP server. Client access occurs under ftpd's UID.\n"
61//usage: ) 61//usage: )
62//usage: IF_FEATURE_FTPD_AUTHENTICATION( 62//usage: IF_FEATURE_FTPD_AUTHENTICATION(
63//usage: "FTP server. " 63//usage: "FTP server. "
@@ -66,9 +66,15 @@
66//usage: "Should be used as inetd service, inetd.conf line:\n" 66//usage: "Should be used as inetd service, inetd.conf line:\n"
67//usage: " 21 stream tcp nowait root ftpd ftpd /files/to/serve\n" 67//usage: " 21 stream tcp nowait root ftpd ftpd /files/to/serve\n"
68//usage: "Can be run from tcpsvd:\n" 68//usage: "Can be run from tcpsvd:\n"
69//usage: " tcpsvd -vE 0.0.0.0 21 ftpd /files/to/serve\n" 69//usage: " tcpsvd -vE 0.0.0.0 21 ftpd /files/to/serve"
70//usage: "\n"
70//usage: "\n -w Allow upload" 71//usage: "\n -w Allow upload"
71//usage: IF_FEATURE_FTPD_AUTHENTICATION( 72//usage: IF_FEATURE_FTPD_AUTHENTICATION(
73//usage: "\n -A No login required, client access occurs under ftpd's UID"
74//
75// if !FTPD_AUTHENTICATION, -A is accepted too, but not shown in --help
76// since it's the only supported mode in that configuration
77//
72//usage: "\n -a USER Enable 'anonymous' login and map it to USER" 78//usage: "\n -a USER Enable 'anonymous' login and map it to USER"
73//usage: ) 79//usage: )
74//usage: "\n -v Log errors to stderr. -vv: verbose log" 80//usage: "\n -v Log errors to stderr. -vv: verbose log"
@@ -1155,11 +1161,12 @@ enum {
1155#if !BB_MMU 1161#if !BB_MMU
1156 OPT_l = (1 << 0), 1162 OPT_l = (1 << 0),
1157 OPT_1 = (1 << 1), 1163 OPT_1 = (1 << 1),
1158 OPT_A = (1 << 2),
1159#endif 1164#endif
1160 OPT_v = (1 << ((!BB_MMU) * 3 + 0)), 1165 BIT_A = (!BB_MMU) * 2,
1161 OPT_S = (1 << ((!BB_MMU) * 3 + 1)), 1166 OPT_A = (1 << (BIT_A + 0)),
1162 OPT_w = (1 << ((!BB_MMU) * 3 + 2)) * ENABLE_FEATURE_FTPD_WRITE, 1167 OPT_v = (1 << (BIT_A + 1)),
1168 OPT_S = (1 << (BIT_A + 2)),
1169 OPT_w = (1 << (BIT_A + 3)) * ENABLE_FEATURE_FTPD_WRITE,
1163}; 1170};
1164 1171
1165int ftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 1172int ftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -1179,24 +1186,25 @@ int ftpd_main(int argc UNUSED_PARAM, char **argv)
1179 verbose_S = 0; 1186 verbose_S = 0;
1180 G.timeout = 2 * 60; 1187 G.timeout = 2 * 60;
1181#if BB_MMU 1188#if BB_MMU
1182 opts = getopt32(argv, "^" "vS" 1189 opts = getopt32(argv, "^" "AvS" IF_FEATURE_FTPD_WRITE("w")
1183 IF_FEATURE_FTPD_WRITE("w") "t:+T:+" IF_FEATURE_FTPD_AUTHENTICATION("a:") 1190 "t:+T:+" IF_FEATURE_FTPD_AUTHENTICATION("a:")
1184 "\0" "vv:SS", 1191 "\0" "vv:SS",
1185 &G.timeout, &abs_timeout, IF_FEATURE_FTPD_AUTHENTICATION(&anon_opt,) 1192 &G.timeout, &abs_timeout, IF_FEATURE_FTPD_AUTHENTICATION(&anon_opt,)
1186 &G.verbose, &verbose_S 1193 &G.verbose, &verbose_S
1187 ); 1194 );
1188#else 1195#else
1189 opts = getopt32(argv, "^" "l1AvS" 1196 opts = getopt32(argv, "^" "l1AvS" IF_FEATURE_FTPD_WRITE("w")
1190 IF_FEATURE_FTPD_WRITE("w") "t:+T:+" IF_FEATURE_FTPD_AUTHENTICATION("a:") 1197 "t:+T:+" IF_FEATURE_FTPD_AUTHENTICATION("a:")
1191 "\0" "vv:SS", 1198 "\0" "vv:SS",
1192 &G.timeout, &abs_timeout, IF_FEATURE_FTPD_AUTHENTICATION(&anon_opt,) 1199 &G.timeout, &abs_timeout, IF_FEATURE_FTPD_AUTHENTICATION(&anon_opt,)
1193 &G.verbose, &verbose_S 1200 &G.verbose, &verbose_S
1194 ); 1201 );
1195 if (opts & (OPT_l|OPT_1)) { 1202 if (opts & (OPT_l|OPT_1)) {
1196 /* Our secret backdoor to ls */ 1203 /* Our secret backdoor to ls: see popen_ls() */
1197 if (fchdir(3) != 0) 1204 if (fchdir(3) != 0)
1198 _exit(127); 1205 _exit(127);
1199 /* memset(&G, 0, sizeof(G)); - ls_main does it */ 1206 /* memset(&G, 0, sizeof(G)); - ls_main does it */
1207 /* NB: in this case -A has a different meaning: like "ls -A" */
1200 return ls_main(/*argc_unused*/ 0, argv); 1208 return ls_main(/*argc_unused*/ 0, argv);
1201 } 1209 }
1202#endif 1210#endif
@@ -1254,30 +1262,32 @@ int ftpd_main(int argc UNUSED_PARAM, char **argv)
1254 signal(SIGALRM, timeout_handler); 1262 signal(SIGALRM, timeout_handler);
1255 1263
1256#if ENABLE_FEATURE_FTPD_AUTHENTICATION 1264#if ENABLE_FEATURE_FTPD_AUTHENTICATION
1257 while (1) { 1265 if (!(opts & OPT_A)) {
1258 uint32_t cmdval = cmdio_get_cmd_and_arg(); 1266 while (1) {
1259 if (cmdval == const_USER) { 1267 uint32_t cmdval = cmdio_get_cmd_and_arg();
1260 if (anon_opt && strcmp(G.ftp_arg, "anonymous") == 0) { 1268 if (cmdval == const_USER) {
1261 pw = getpwnam(anon_opt); 1269 if (anon_opt && strcmp(G.ftp_arg, "anonymous") == 0) {
1262 if (pw) 1270 pw = getpwnam(anon_opt);
1263 break; /* does not even ask for password */ 1271 if (pw)
1264 } 1272 break; /* does not even ask for password */
1265 pw = getpwnam(G.ftp_arg); 1273 }
1266 cmdio_write_raw(STR(FTP_GIVEPWORD)" Please specify password\r\n"); 1274 pw = getpwnam(G.ftp_arg);
1267 } else if (cmdval == const_PASS) { 1275 cmdio_write_raw(STR(FTP_GIVEPWORD)" Specify password\r\n");
1268 if (check_password(pw, G.ftp_arg) > 0) { 1276 } else if (cmdval == const_PASS) {
1269 break; /* login success */ 1277 if (check_password(pw, G.ftp_arg) > 0) {
1278 break; /* login success */
1279 }
1280 cmdio_write_raw(STR(FTP_LOGINERR)" Login failed\r\n");
1281 pw = NULL;
1282 } else if (cmdval == const_QUIT) {
1283 WRITE_OK(FTP_GOODBYE);
1284 return 0;
1285 } else {
1286 cmdio_write_raw(STR(FTP_LOGINERR)" Login with USER+PASS\r\n");
1270 } 1287 }
1271 cmdio_write_raw(STR(FTP_LOGINERR)" Login failed\r\n");
1272 pw = NULL;
1273 } else if (cmdval == const_QUIT) {
1274 WRITE_OK(FTP_GOODBYE);
1275 return 0;
1276 } else {
1277 cmdio_write_raw(STR(FTP_LOGINERR)" Login with USER and PASS\r\n");
1278 } 1288 }
1289 WRITE_OK(FTP_LOGINOK);
1279 } 1290 }
1280 WRITE_OK(FTP_LOGINOK);
1281#endif 1291#endif
1282 1292
1283 /* Do this after auth, else /etc/passwd is not accessible */ 1293 /* Do this after auth, else /etc/passwd is not accessible */
@@ -1309,7 +1319,9 @@ int ftpd_main(int argc UNUSED_PARAM, char **argv)
1309 } 1319 }
1310 1320
1311#if ENABLE_FEATURE_FTPD_AUTHENTICATION 1321#if ENABLE_FEATURE_FTPD_AUTHENTICATION
1312 change_identity(pw); 1322 if (pw)
1323 change_identity(pw);
1324 /* else: -A is in effect */
1313#endif 1325#endif
1314 1326
1315 /* RFC-959 Section 5.1 1327 /* RFC-959 Section 5.1
diff --git a/networking/ftpgetput.c b/networking/ftpgetput.c
index ffc4cd5f1..84ca547ff 100644
--- a/networking/ftpgetput.c
+++ b/networking/ftpgetput.c
@@ -315,7 +315,7 @@ int ftpgetput_main(int argc UNUSED_PARAM, char **argv)
315 INIT_G(); 315 INIT_G();
316 /* Set default values */ 316 /* Set default values */
317 user = "anonymous"; 317 user = "anonymous";
318 password = "busybox@"; 318 password = "busybox";
319 319
320 /* 320 /*
321 * Decipher the command line 321 * Decipher the command line
diff --git a/networking/httpd.c b/networking/httpd.c
index 9439e206c..b52526a78 100644
--- a/networking/httpd.c
+++ b/networking/httpd.c
@@ -392,7 +392,10 @@ static const struct {
392struct globals { 392struct globals {
393 int verbose; /* must be int (used by getopt32) */ 393 int verbose; /* must be int (used by getopt32) */
394 smallint flg_deny_all; 394 smallint flg_deny_all;
395 395#if ENABLE_FEATURE_HTTPD_GZIP
396 /* client can handle gzip / we are going to send gzip */
397 smallint content_gzip;
398#endif
396 unsigned rmt_ip; /* used for IP-based allow/deny rules */ 399 unsigned rmt_ip; /* used for IP-based allow/deny rules */
397 time_t last_mod; 400 time_t last_mod;
398 char *rmt_ip_str; /* for $REMOTE_ADDR and $REMOTE_PORT */ 401 char *rmt_ip_str; /* for $REMOTE_ADDR and $REMOTE_PORT */
@@ -440,14 +443,15 @@ struct globals {
440#if ENABLE_FEATURE_HTTPD_PROXY 443#if ENABLE_FEATURE_HTTPD_PROXY
441 Htaccess_Proxy *proxy; 444 Htaccess_Proxy *proxy;
442#endif 445#endif
443#if ENABLE_FEATURE_HTTPD_GZIP
444 /* client can handle gzip / we are going to send gzip */
445 smallint content_gzip;
446#endif
447}; 446};
448#define G (*ptr_to_globals) 447#define G (*ptr_to_globals)
449#define verbose (G.verbose ) 448#define verbose (G.verbose )
450#define flg_deny_all (G.flg_deny_all ) 449#define flg_deny_all (G.flg_deny_all )
450#if ENABLE_FEATURE_HTTPD_GZIP
451# define content_gzip (G.content_gzip )
452#else
453# define content_gzip 0
454#endif
451#define rmt_ip (G.rmt_ip ) 455#define rmt_ip (G.rmt_ip )
452#define bind_addr_or_port (G.bind_addr_or_port) 456#define bind_addr_or_port (G.bind_addr_or_port)
453#define g_query (G.g_query ) 457#define g_query (G.g_query )
@@ -481,11 +485,6 @@ enum {
481#define hdr_cnt (G.hdr_cnt ) 485#define hdr_cnt (G.hdr_cnt )
482#define http_error_page (G.http_error_page ) 486#define http_error_page (G.http_error_page )
483#define proxy (G.proxy ) 487#define proxy (G.proxy )
484#if ENABLE_FEATURE_HTTPD_GZIP
485# define content_gzip (G.content_gzip )
486#else
487# define content_gzip 0
488#endif
489#define INIT_G() do { \ 488#define INIT_G() do { \
490 setup_common_bufsiz(); \ 489 setup_common_bufsiz(); \
491 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ 490 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
@@ -944,7 +943,7 @@ static char *encodeString(const char *string)
944 if (isalnum(ch)) 943 if (isalnum(ch))
945 *p++ = ch; 944 *p++ = ch;
946 else 945 else
947 p += sprintf(p, "&#%d;", (unsigned char) ch); 946 p += sprintf(p, "&#%u;", (unsigned char) ch);
948 } 947 }
949 *p = '\0'; 948 *p = '\0';
950 return out; 949 return out;
@@ -1040,7 +1039,7 @@ static void log_and_exit(void)
1040 * second packet is delayed for any reason. 1039 * second packet is delayed for any reason.
1041 * responseNum - the result code to send. 1040 * responseNum - the result code to send.
1042 */ 1041 */
1043static void send_headers(int responseNum) 1042static void send_headers(unsigned responseNum)
1044{ 1043{
1045 static const char RFC1123FMT[] ALIGN1 = "%a, %d %b %Y %H:%M:%S GMT"; 1044 static const char RFC1123FMT[] ALIGN1 = "%a, %d %b %Y %H:%M:%S GMT";
1046 /* Fixed size 29-byte string. Example: Sun, 06 Nov 1994 08:49:37 GMT */ 1045 /* Fixed size 29-byte string. Example: Sun, 06 Nov 1994 08:49:37 GMT */
@@ -1052,9 +1051,9 @@ static void send_headers(int responseNum)
1052#if ENABLE_FEATURE_HTTPD_ERROR_PAGES 1051#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
1053 const char *error_page = NULL; 1052 const char *error_page = NULL;
1054#endif 1053#endif
1054 unsigned len;
1055 unsigned i; 1055 unsigned i;
1056 time_t timer = time(NULL); 1056 time_t timer = time(NULL);
1057 int len;
1058 1057
1059 for (i = 0; i < ARRAY_SIZE(http_response_type); i++) { 1058 for (i = 0; i < ARRAY_SIZE(http_response_type); i++) {
1060 if (http_response_type[i] == responseNum) { 1059 if (http_response_type[i] == responseNum) {
@@ -1078,16 +1077,21 @@ static void send_headers(int responseNum)
1078 strftime(date_str, sizeof(date_str), RFC1123FMT, gmtime_r(&timer, &tm)); 1077 strftime(date_str, sizeof(date_str), RFC1123FMT, gmtime_r(&timer, &tm));
1079 /* ^^^ using gmtime_r() instead of gmtime() to not use static data */ 1078 /* ^^^ using gmtime_r() instead of gmtime() to not use static data */
1080 len = sprintf(iobuf, 1079 len = sprintf(iobuf,
1081 "HTTP/1.0 %d %s\r\n" 1080 "HTTP/1.0 %u %s\r\n"
1082 "Content-type: %s\r\n"
1083 "Date: %s\r\n" 1081 "Date: %s\r\n"
1084 "Connection: close\r\n", 1082 "Connection: close\r\n",
1085 responseNum, responseString, 1083 responseNum, responseString,
1086 /* if it's error message, then it's HTML */
1087 (responseNum == HTTP_OK ? found_mime_type : "text/html"),
1088 date_str 1084 date_str
1089 ); 1085 );
1090 1086
1087 if (responseNum != HTTP_OK || found_mime_type) {
1088 len += sprintf(iobuf + len,
1089 "Content-type: %s\r\n",
1090 /* if it's error message, then it's HTML */
1091 (responseNum != HTTP_OK ? "text/html" : found_mime_type)
1092 );
1093 }
1094
1091#if ENABLE_FEATURE_HTTPD_BASIC_AUTH 1095#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
1092 if (responseNum == HTTP_UNAUTHORIZED) { 1096 if (responseNum == HTTP_UNAUTHORIZED) {
1093 len += sprintf(iobuf + len, 1097 len += sprintf(iobuf + len,
@@ -1147,9 +1151,9 @@ static void send_headers(int responseNum)
1147 "Accept-Ranges: bytes\r\n" 1151 "Accept-Ranges: bytes\r\n"
1148#endif 1152#endif
1149 "Last-Modified: %s\r\n" 1153 "Last-Modified: %s\r\n"
1150 "%s %"OFF_FMT"u\r\n", 1154 "%s-Length: %"OFF_FMT"u\r\n",
1151 date_str, 1155 date_str,
1152 content_gzip ? "Transfer-Length:" : "Content-Length:", 1156 content_gzip ? "Transfer" : "Content",
1153 file_size 1157 file_size
1154 ); 1158 );
1155 } 1159 }
@@ -1161,8 +1165,8 @@ static void send_headers(int responseNum)
1161 iobuf[len++] = '\n'; 1165 iobuf[len++] = '\n';
1162 if (infoString) { 1166 if (infoString) {
1163 len += sprintf(iobuf + len, 1167 len += sprintf(iobuf + len,
1164 "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD>\n" 1168 "<HTML><HEAD><TITLE>%u %s</TITLE></HEAD>\n"
1165 "<BODY><H1>%d %s</H1>\n" 1169 "<BODY><H1>%u %s</H1>\n"
1166 "%s\n" 1170 "%s\n"
1167 "</BODY></HTML>\n", 1171 "</BODY></HTML>\n",
1168 responseNum, responseString, 1172 responseNum, responseString,
@@ -1300,9 +1304,9 @@ static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post
1300 continue; 1304 continue;
1301 } 1305 }
1302 if (DEBUG && WIFEXITED(status)) 1306 if (DEBUG && WIFEXITED(status))
1303 bb_error_msg("CGI exited, status=%d", WEXITSTATUS(status)); 1307 bb_error_msg("CGI exited, status=%u", WEXITSTATUS(status));
1304 if (DEBUG && WIFSIGNALED(status)) 1308 if (DEBUG && WIFSIGNALED(status))
1305 bb_error_msg("CGI killed, signal=%d", WTERMSIG(status)); 1309 bb_error_msg("CGI killed, signal=%u", WTERMSIG(status));
1306#endif 1310#endif
1307 break; 1311 break;
1308 } 1312 }
@@ -1533,7 +1537,7 @@ static void send_cgi_and_exit(
1533 if (G.http_accept_language) 1537 if (G.http_accept_language)
1534 setenv1("HTTP_ACCEPT_LANGUAGE", G.http_accept_language); 1538 setenv1("HTTP_ACCEPT_LANGUAGE", G.http_accept_language);
1535 if (post_len) 1539 if (post_len)
1536 putenv(xasprintf("CONTENT_LENGTH=%d", post_len)); 1540 putenv(xasprintf("CONTENT_LENGTH=%u", post_len));
1537 if (cookie) 1541 if (cookie)
1538 setenv1("HTTP_COOKIE", cookie); 1542 setenv1("HTTP_COOKIE", cookie);
1539 if (content_type) 1543 if (content_type)
@@ -1684,8 +1688,8 @@ static NOINLINE void send_file_and_exit(const char *url, int what)
1684 * (happens if you abort downloads from local httpd): */ 1688 * (happens if you abort downloads from local httpd): */
1685 signal(SIGPIPE, SIG_IGN); 1689 signal(SIGPIPE, SIG_IGN);
1686 1690
1687 /* If not found, default is "application/octet-stream" */ 1691 /* If not found, default is to not send "Content-type:" */
1688 found_mime_type = "application/octet-stream"; 1692 /*found_mime_type = NULL; - already is */
1689 suffix = strrchr(url, '.'); 1693 suffix = strrchr(url, '.');
1690 if (suffix) { 1694 if (suffix) {
1691 static const char suffixTable[] ALIGN1 = 1695 static const char suffixTable[] ALIGN1 =
@@ -2543,11 +2547,9 @@ static void mini_httpd_nommu(int server_socket, int argc, char **argv)
2543 */ 2547 */
2544 while (1) { 2548 while (1) {
2545 int n; 2549 int n;
2546 len_and_sockaddr fromAddr;
2547 2550
2548 /* Wait for connections... */ 2551 /* Wait for connections... */
2549 fromAddr.len = LSA_SIZEOF_SA; 2552 n = accept(server_socket, NULL, NULL);
2550 n = accept(server_socket, &fromAddr.u.sa, &fromAddr.len);
2551 if (n < 0) 2553 if (n < 0)
2552 continue; 2554 continue;
2553 2555
@@ -2734,7 +2736,7 @@ int httpd_main(int argc UNUSED_PARAM, char **argv)
2734 2736
2735 xfunc_error_retval = 0; 2737 xfunc_error_retval = 0;
2736 if (opt & OPT_INETD) 2738 if (opt & OPT_INETD)
2737 mini_httpd_inetd(); 2739 mini_httpd_inetd(); /* never returns */
2738#if BB_MMU 2740#if BB_MMU
2739 if (!(opt & OPT_FOREGROUND)) 2741 if (!(opt & OPT_FOREGROUND))
2740 bb_daemonize(0); /* don't change current directory */ 2742 bb_daemonize(0); /* don't change current directory */
diff --git a/networking/ntpd.c b/networking/ntpd.c
index 8205ab271..6cd497090 100644
--- a/networking/ntpd.c
+++ b/networking/ntpd.c
@@ -94,7 +94,6 @@
94#include "libbb.h" 94#include "libbb.h"
95#include <math.h> 95#include <math.h>
96#include <netinet/ip.h> /* For IPTOS_LOWDELAY definition */ 96#include <netinet/ip.h> /* For IPTOS_LOWDELAY definition */
97#include <sys/resource.h> /* setpriority */
98#include <sys/timex.h> 97#include <sys/timex.h>
99#ifndef IPTOS_LOWDELAY 98#ifndef IPTOS_LOWDELAY
100# define IPTOS_LOWDELAY 0x10 99# define IPTOS_LOWDELAY 0x10
diff --git a/networking/telnet.c b/networking/telnet.c
index 8b0df7f5c..15d6a08d8 100644
--- a/networking/telnet.c
+++ b/networking/telnet.c
@@ -89,12 +89,6 @@
89# define TELOPT_NAWS 31 /* window size */ 89# define TELOPT_NAWS 31 /* window size */
90#endif 90#endif
91 91
92#ifdef DOTRACE
93# define TRACE(x, y) do { if (x) printf y; } while (0)
94#else
95# define TRACE(x, y)
96#endif
97
98enum { 92enum {
99 DATABUFSIZE = 128, 93 DATABUFSIZE = 128,
100 IACBUFSIZE = 128, 94 IACBUFSIZE = 128,
@@ -627,10 +621,6 @@ int telnet_main(int argc UNUSED_PARAM, char **argv)
627 621
628 INIT_G(); 622 INIT_G();
629 623
630#if ENABLE_FEATURE_TELNET_WIDTH
631 get_terminal_width_height(0, &G.win_width, &G.win_height);
632#endif
633
634#if ENABLE_FEATURE_TELNET_TTYPE 624#if ENABLE_FEATURE_TELNET_TTYPE
635 G.ttype = getenv("TERM"); 625 G.ttype = getenv("TERM");
636#endif 626#endif
@@ -661,6 +651,11 @@ int telnet_main(int argc UNUSED_PARAM, char **argv)
661 651
662 setsockopt_keepalive(netfd); 652 setsockopt_keepalive(netfd);
663 653
654#if ENABLE_FEATURE_TELNET_WIDTH
655 get_terminal_width_height(0, &G.win_width, &G.win_height);
656//TODO: support dynamic resize?
657#endif
658
664 signal(SIGINT, record_signo); 659 signal(SIGINT, record_signo);
665 660
666 ufds[0].fd = STDIN_FILENO; 661 ufds[0].fd = STDIN_FILENO;
@@ -684,7 +679,6 @@ int telnet_main(int argc UNUSED_PARAM, char **argv)
684 len = safe_read(STDIN_FILENO, G.buf, DATABUFSIZE); 679 len = safe_read(STDIN_FILENO, G.buf, DATABUFSIZE);
685 if (len <= 0) 680 if (len <= 0)
686 doexit(EXIT_SUCCESS); 681 doexit(EXIT_SUCCESS);
687 TRACE(0, ("Read con: %d\n", len));
688 handle_net_output(len); 682 handle_net_output(len);
689 } 683 }
690 684
@@ -694,7 +688,6 @@ int telnet_main(int argc UNUSED_PARAM, char **argv)
694 full_write1_str("Connection closed by foreign host\r\n"); 688 full_write1_str("Connection closed by foreign host\r\n");
695 doexit(EXIT_FAILURE); 689 doexit(EXIT_FAILURE);
696 } 690 }
697 TRACE(0, ("Read netfd (%d): %d\n", netfd, len));
698 handle_net_input(len); 691 handle_net_input(len);
699 } 692 }
700 } /* while (1) */ 693 } /* while (1) */
diff --git a/networking/wget.c b/networking/wget.c
index aaa068ba9..5ff31d278 100644
--- a/networking/wget.c
+++ b/networking/wget.c
@@ -794,12 +794,9 @@ static void spawn_ssl_client(const char *host, int network_fd, int flags)
794static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_sockaddr *lsa) 794static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_sockaddr *lsa)
795{ 795{
796 FILE *sfp; 796 FILE *sfp;
797 char *str; 797 char *pass;
798 int port; 798 int port;
799 799
800 if (!target->user)
801 target->user = xstrdup("anonymous:busybox@");
802
803 sfp = open_socket(lsa); 800 sfp = open_socket(lsa);
804#if ENABLE_FEATURE_WGET_HTTPS 801#if ENABLE_FEATURE_WGET_HTTPS
805 if (target->protocol == P_FTPS) 802 if (target->protocol == P_FTPS)
@@ -810,18 +807,20 @@ static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_
810 bb_error_msg_and_die("%s", G.wget_buf); 807 bb_error_msg_and_die("%s", G.wget_buf);
811 /* note: ftpcmd() sanitizes G.wget_buf, ok to print */ 808 /* note: ftpcmd() sanitizes G.wget_buf, ok to print */
812 809
813 /* 810 /* Split username:password pair */
814 * Splitting username:password pair, 811 pass = (char*)"busybox"; /* password for "anonymous" */
815 * trying to log in 812 if (target->user) {
816 */ 813 pass = strchr(target->user, ':');
817 str = strchr(target->user, ':'); 814 if (pass)
818 if (str) 815 *pass++ = '\0';
819 *str++ = '\0'; 816 }
820 switch (ftpcmd("USER ", target->user, sfp)) { 817
818 /* Log in */
819 switch (ftpcmd("USER ", target->user ?: "anonymous", sfp)) {
821 case 230: 820 case 230:
822 break; 821 break;
823 case 331: 822 case 331:
824 if (ftpcmd("PASS ", str, sfp) == 230) 823 if (ftpcmd("PASS ", pass, sfp) == 230)
825 break; 824 break;
826 /* fall through (failed login) */ 825 /* fall through (failed login) */
827 default: 826 default:
@@ -830,20 +829,16 @@ static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_
830 829
831 ftpcmd("TYPE I", NULL, sfp); 830 ftpcmd("TYPE I", NULL, sfp);
832 831
833 /* 832 /* Query file size */
834 * Querying file size
835 */
836 if (ftpcmd("SIZE ", target->path, sfp) == 213) { 833 if (ftpcmd("SIZE ", target->path, sfp) == 213) {
837 G.content_len = BB_STRTOOFF(G.wget_buf + 4, NULL, 10); 834 G.content_len = BB_STRTOOFF(G.wget_buf + 4, NULL, 10);
838 if (G.content_len < 0 || errno) { 835 if (G.content_len < 0 || errno) {
839 bb_error_msg_and_die("SIZE value is garbage"); 836 bb_error_msg_and_die("bad SIZE value '%s'", G.wget_buf + 4);
840 } 837 }
841 G.got_clen = 1; 838 G.got_clen = 1;
842 } 839 }
843 840
844 /* 841 /* Enter passive mode */
845 * Entering passive mode
846 */
847 if (ENABLE_FEATURE_IPV6 && ftpcmd("EPSV", NULL, sfp) == 229) { 842 if (ENABLE_FEATURE_IPV6 && ftpcmd("EPSV", NULL, sfp) == 229) {
848 /* good */ 843 /* good */
849 } else 844 } else
@@ -1002,11 +997,19 @@ static void NOINLINE retrieve_file_data(FILE *dfp)
1002 if (!G.chunked) 997 if (!G.chunked)
1003 break; 998 break;
1004 999
1005 fgets_trim_sanitize(dfp, NULL); /* Eat empty line */ 1000 /* Each chunk ends with "\r\n" - eat it */
1001 fgets_trim_sanitize(dfp, NULL);
1006 get_clen: 1002 get_clen:
1003 /* chunk size format is "HEXNUM[;name[=val]]\r\n" */
1007 fgets_trim_sanitize(dfp, NULL); 1004 fgets_trim_sanitize(dfp, NULL);
1005 errno = 0;
1008 G.content_len = STRTOOFF(G.wget_buf, NULL, 16); 1006 G.content_len = STRTOOFF(G.wget_buf, NULL, 16);
1009 /* FIXME: error check? */ 1007 /*
1008 * Had a bug with inputs like "ffffffff0001f400"
1009 * smashing the heap later. Ensure >= 0.
1010 */
1011 if (G.content_len < 0 || errno)
1012 bb_error_msg_and_die("bad chunk length '%s'", G.wget_buf);
1010 if (G.content_len == 0) 1013 if (G.content_len == 0)
1011 break; /* all done! */ 1014 break; /* all done! */
1012 G.got_clen = 1; 1015 G.got_clen = 1;
diff --git a/runit/chpst.c b/runit/chpst.c
index 3ecb85cba..c2641ce8a 100644
--- a/runit/chpst.c
+++ b/runit/chpst.c
@@ -135,7 +135,6 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
135//usage: "\n a SIGXCPU after N seconds" 135//usage: "\n a SIGXCPU after N seconds"
136 136
137#include "libbb.h" 137#include "libbb.h"
138#include <sys/resource.h> /* getrlimit */
139 138
140/* 139/*
141Five applets here: chpst, envdir, envuidgid, setuidgid, softlimit. 140Five applets here: chpst, envdir, envuidgid, setuidgid, softlimit.
diff --git a/runit/sv.c b/runit/sv.c
index 86d181872..8054daedf 100644
--- a/runit/sv.c
+++ b/runit/sv.c
@@ -222,6 +222,7 @@ struct globals {
222#define str_equal(s,t) (strcmp((s), (t)) == 0) 222#define str_equal(s,t) (strcmp((s), (t)) == 0)
223 223
224 224
225#if ENABLE_SV || ENABLE_SVC
225static void fatal_cannot(const char *m1) NORETURN; 226static void fatal_cannot(const char *m1) NORETURN;
226static void fatal_cannot(const char *m1) 227static void fatal_cannot(const char *m1)
227{ 228{
@@ -688,6 +689,7 @@ static int sv(char **argv)
688 } 689 }
689 return rc > 99 ? 99 : rc; 690 return rc > 99 ? 99 : rc;
690} 691}
692#endif
691 693
692#if ENABLE_SV 694#if ENABLE_SV
693int sv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 695int sv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c
index 1d77aefb7..b92c2324e 100644
--- a/scripts/kconfig/confdata.c
+++ b/scripts/kconfig/confdata.c
@@ -334,7 +334,9 @@ int conf_write(const char *name)
334 struct symbol *sym; 334 struct symbol *sym;
335 struct menu *menu; 335 struct menu *menu;
336 const char *basename; 336 const char *basename;
337 char dirname[128], tmpname[128], newname[128]; 337 char dirname[128];
338 char tmpname[256];
339 char newname[256];
338 int type, l; 340 int type, l;
339 const char *str; 341 const char *str;
340 time_t now; 342 time_t now;
diff --git a/scripts/kconfig/mconf.c b/scripts/kconfig/mconf.c
index d3ad12d90..9fc837c9f 100644
--- a/scripts/kconfig/mconf.c
+++ b/scripts/kconfig/mconf.c
@@ -31,6 +31,10 @@
31#include <unistd.h> 31#include <unistd.h>
32#include <locale.h> 32#include <locale.h>
33 33
34#ifndef SIGWINCH
35#define SIGWINCH 28
36#endif
37
34#define LKC_DIRECT_LINK 38#define LKC_DIRECT_LINK
35#include "lkc.h" 39#include "lkc.h"
36 40
diff --git a/shell/ash.c b/shell/ash.c
index 5ec035043..35438a887 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -204,7 +204,6 @@
204 204
205#define JOBS ENABLE_ASH_JOB_CONTROL 205#define JOBS ENABLE_ASH_JOB_CONTROL
206 206
207#include <setjmp.h>
208#include <fnmatch.h> 207#include <fnmatch.h>
209#include <sys/times.h> 208#include <sys/times.h>
210#include <sys/utsname.h> /* for setting $HOSTNAME */ 209#include <sys/utsname.h> /* for setting $HOSTNAME */
@@ -5755,7 +5754,7 @@ openredirect(union node *redir)
5755 f = open(fname, O_WRONLY, 0666); 5754 f = open(fname, O_WRONLY, 0666);
5756 if (f < 0) 5755 if (f < 0)
5757 goto ecreate; 5756 goto ecreate;
5758 if (fstat(f, &sb) < 0 && S_ISREG(sb.st_mode)) { 5757 if (!fstat(f, &sb) && S_ISREG(sb.st_mode)) {
5759 close(f); 5758 close(f);
5760 errno = EEXIST; 5759 errno = EEXIST;
5761 goto ecreate; 5760 goto ecreate;
@@ -6255,10 +6254,9 @@ static int substr_atoi(const char *s)
6255 * performs globbing, and thus diverges from what we do). 6254 * performs globbing, and thus diverges from what we do).
6256 */ 6255 */
6257#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */ 6256#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
6258#define EXP_QPAT 0x20 /* pattern in quoted parameter expansion */ 6257#define EXP_VARTILDE2 0x20 /* expand tildes after colons only */
6259#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */ 6258#define EXP_WORD 0x40 /* expand word in parameter expansion */
6260#define EXP_WORD 0x80 /* expand word in parameter expansion */ 6259#define EXP_QUOTED 0x80 /* expand word in double quotes */
6261#define EXP_QUOTED 0x100 /* expand word in double quotes */
6262/* 6260/*
6263 * rmescape() flags 6261 * rmescape() flags
6264 */ 6262 */
@@ -6268,7 +6266,7 @@ static int substr_atoi(const char *s)
6268#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */ 6266#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
6269 6267
6270/* Add CTLESC when necessary. */ 6268/* Add CTLESC when necessary. */
6271#define QUOTES_ESC (EXP_FULL | EXP_CASE | EXP_QPAT) 6269#define QUOTES_ESC (EXP_FULL | EXP_CASE)
6272/* Do not skip NUL characters. */ 6270/* Do not skip NUL characters. */
6273#define QUOTES_KEEPNUL EXP_TILDE 6271#define QUOTES_KEEPNUL EXP_TILDE
6274 6272
@@ -6343,7 +6341,10 @@ ifsbreakup(char *string, struct arglist *arglist)
6343 realifs = ifsset() ? ifsval() : defifs; 6341 realifs = ifsset() ? ifsval() : defifs;
6344 ifsp = &ifsfirst; 6342 ifsp = &ifsfirst;
6345 do { 6343 do {
6344 int afternul;
6345
6346 p = string + ifsp->begoff; 6346 p = string + ifsp->begoff;
6347 afternul = nulonly;
6347 nulonly = ifsp->nulonly; 6348 nulonly = ifsp->nulonly;
6348 ifs = nulonly ? nullstr : realifs; 6349 ifs = nulonly ? nullstr : realifs;
6349 ifsspc = 0; 6350 ifsspc = 0;
@@ -6355,7 +6356,7 @@ ifsbreakup(char *string, struct arglist *arglist)
6355 p++; 6356 p++;
6356 continue; 6357 continue;
6357 } 6358 }
6358 if (!nulonly) 6359 if (!(afternul || nulonly))
6359 ifsspc = (strchr(defifs, *p) != NULL); 6360 ifsspc = (strchr(defifs, *p) != NULL);
6360 /* Ignore IFS whitespace at start */ 6361 /* Ignore IFS whitespace at start */
6361 if (q == start && ifsspc) { 6362 if (q == start && ifsspc) {
@@ -6457,7 +6458,6 @@ rmescapes(char *str, int flag, int *slash_position)
6457 IF_BASH_PATTERN_SUBST('/',) CTLESC, CTLQUOTEMARK, '\0' }; 6458 IF_BASH_PATTERN_SUBST('/',) CTLESC, CTLQUOTEMARK, '\0' };
6458 6459
6459 char *p, *q, *r; 6460 char *p, *q, *r;
6460 unsigned inquotes;
6461 unsigned protect_against_glob; 6461 unsigned protect_against_glob;
6462 unsigned globbing; 6462 unsigned globbing;
6463 6463
@@ -6488,18 +6488,21 @@ rmescapes(char *str, int flag, int *slash_position)
6488 } 6488 }
6489 } 6489 }
6490 6490
6491 inquotes = 0;
6492 globbing = flag & RMESCAPE_GLOB; 6491 globbing = flag & RMESCAPE_GLOB;
6493 protect_against_glob = globbing; 6492 protect_against_glob = globbing;
6494 while (*p) { 6493 while (*p) {
6495 if ((unsigned char)*p == CTLQUOTEMARK) { 6494 if ((unsigned char)*p == CTLQUOTEMARK) {
6496// Note: both inquotes and protect_against_glob only affect whether 6495// Note: protect_against_glob only affect whether
6497// CTLESC,<ch> gets converted to <ch> or to \<ch> 6496// CTLESC,<ch> gets converted to <ch> or to \<ch>
6498 inquotes = ~inquotes;
6499 p++; 6497 p++;
6500 protect_against_glob = globbing; 6498 protect_against_glob = globbing;
6501 continue; 6499 continue;
6502 } 6500 }
6501 if (*p == '\\') {
6502 /* naked back slash */
6503 protect_against_glob = 0;
6504 goto copy;
6505 }
6503 if ((unsigned char)*p == CTLESC) { 6506 if ((unsigned char)*p == CTLESC) {
6504 p++; 6507 p++;
6505#if DEBUG 6508#if DEBUG
@@ -6535,10 +6538,6 @@ rmescapes(char *str, int flag, int *slash_position)
6535 *q++ = '\\'; 6538 *q++ = '\\';
6536 } 6539 }
6537 } 6540 }
6538 } else if (*p == '\\' && !inquotes) {
6539 /* naked back slash */
6540 protect_against_glob = 0;
6541 goto copy;
6542 } 6541 }
6543#if BASH_PATTERN_SUBST 6542#if BASH_PATTERN_SUBST
6544 else if (slash_position && p == str + *slash_position) { 6543 else if (slash_position && p == str + *slash_position) {
@@ -7032,12 +7031,12 @@ argstr(char *p, int flags)
7032 case CTLENDVAR: /* ??? */ 7031 case CTLENDVAR: /* ??? */
7033 goto breakloop; 7032 goto breakloop;
7034 case CTLQUOTEMARK: 7033 case CTLQUOTEMARK:
7035 inquotes ^= EXP_QUOTED;
7036 /* "$@" syntax adherence hack */ 7034 /* "$@" syntax adherence hack */
7037 if (inquotes && !memcmp(p, dolatstr + 1, DOLATSTRLEN - 1)) { 7035 if (!inquotes && !memcmp(p, dolatstr + 1, DOLATSTRLEN - 1)) {
7038 p = evalvar(p + 1, flags | inquotes) + 1; 7036 p = evalvar(p + 1, flags | EXP_QUOTED) + 1;
7039 goto start; 7037 goto start;
7040 } 7038 }
7039 inquotes ^= EXP_QUOTED;
7041 addquote: 7040 addquote:
7042 if (flags & QUOTES_ESC) { 7041 if (flags & QUOTES_ESC) {
7043 p--; 7042 p--;
@@ -7048,16 +7047,6 @@ argstr(char *p, int flags)
7048 case CTLESC: 7047 case CTLESC:
7049 startloc++; 7048 startloc++;
7050 length++; 7049 length++;
7051
7052 /*
7053 * Quoted parameter expansion pattern: remove quote
7054 * unless inside inner quotes or we have a literal
7055 * backslash.
7056 */
7057 if (((flags | inquotes) & (EXP_QPAT | EXP_QUOTED)) ==
7058 EXP_QPAT && *p != '\\')
7059 break;
7060
7061 goto addquote; 7050 goto addquote;
7062 case CTLVAR: 7051 case CTLVAR:
7063 TRACE(("argstr: evalvar('%s')\n", p)); 7052 TRACE(("argstr: evalvar('%s')\n", p));
@@ -7248,15 +7237,24 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
7248 } 7237 }
7249#endif 7238#endif
7250 argstr_flags = EXP_TILDE; 7239 argstr_flags = EXP_TILDE;
7251 if (subtype != VSASSIGN && subtype != VSQUESTION) 7240 if (subtype != VSASSIGN
7252 argstr_flags |= (flag & (EXP_QUOTED | EXP_QPAT) ? EXP_QPAT : EXP_CASE); 7241 && subtype != VSQUESTION
7242#if BASH_SUBSTR
7243 && subtype != VSSUBSTR
7244#endif
7245 ) {
7246 /* EXP_CASE keeps CTLESC's */
7247 argstr_flags = EXP_TILDE | EXP_CASE;
7248 }
7253 argstr(p, argstr_flags); 7249 argstr(p, argstr_flags);
7250 //bb_error_msg("str0:'%s'", (char *)stackblock() + strloc);
7254#if BASH_PATTERN_SUBST 7251#if BASH_PATTERN_SUBST
7255 slash_pos = -1; 7252 slash_pos = -1;
7256 if (repl) { 7253 if (repl) {
7257 slash_pos = expdest - ((char *)stackblock() + strloc); 7254 slash_pos = expdest - ((char *)stackblock() + strloc);
7258 STPUTC('/', expdest); 7255 STPUTC('/', expdest);
7259 argstr(repl + 1, argstr_flags); 7256 //bb_error_msg("repl+1:'%s'", repl + 1);
7257 argstr(repl + 1, EXP_TILDE); /* EXP_TILDE: echo "${v/x/~}" expands ~ ! */
7260 *repl = '/'; 7258 *repl = '/';
7261 } 7259 }
7262#endif 7260#endif
@@ -11182,6 +11180,34 @@ pgetc_eatbnl(void)
11182 return c; 11180 return c;
11183} 11181}
11184 11182
11183struct synstack {
11184 smalluint syntax;
11185 uint8_t innerdq :1;
11186 uint8_t varpushed :1;
11187 uint8_t dblquote :1;
11188 int varnest; /* levels of variables expansion */
11189 int dqvarnest; /* levels of variables expansion within double quotes */
11190 int parenlevel; /* levels of parens in arithmetic */
11191 struct synstack *prev;
11192 struct synstack *next;
11193};
11194
11195static void
11196synstack_push(struct synstack **stack, struct synstack *next, int syntax)
11197{
11198 memset(next, 0, sizeof(*next));
11199 next->syntax = syntax;
11200 next->next = *stack;
11201 (*stack)->prev = next;
11202 *stack = next;
11203}
11204
11205static ALWAYS_INLINE void
11206synstack_pop(struct synstack **stack)
11207{
11208 *stack = (*stack)->next;
11209}
11210
11185/* 11211/*
11186 * To handle the "." command, a stack of input files is used. Pushfile 11212 * To handle the "." command, a stack of input files is used. Pushfile
11187 * adds a new entry to the stack and popfile restores the previous level. 11213 * adds a new entry to the stack and popfile restores the previous level.
@@ -12443,19 +12469,13 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
12443 size_t len; 12469 size_t len;
12444 struct nodelist *bqlist; 12470 struct nodelist *bqlist;
12445 smallint quotef; 12471 smallint quotef;
12446 smallint dblquote;
12447 smallint oldstyle; 12472 smallint oldstyle;
12448 IF_FEATURE_SH_MATH(smallint prevsyntax;) /* syntax before arithmetic */
12449 smallint pssyntax; /* we are expanding a prompt string */ 12473 smallint pssyntax; /* we are expanding a prompt string */
12450 int varnest; /* levels of variables expansion */
12451 IF_FEATURE_SH_MATH(int arinest;) /* levels of arithmetic expansion */
12452 IF_FEATURE_SH_MATH(int parenlevel;) /* levels of parens in arithmetic */
12453 int dqvarnest; /* levels of variables expansion within double quotes */
12454 IF_BASH_DOLLAR_SQUOTE(smallint bash_dollar_squote = 0;) 12474 IF_BASH_DOLLAR_SQUOTE(smallint bash_dollar_squote = 0;)
12475 /* syntax stack */
12476 struct synstack synbase = { };
12477 struct synstack *synstack = &synbase;
12455 12478
12456 bqlist = NULL;
12457 quotef = 0;
12458 IF_FEATURE_SH_MATH(prevsyntax = 0;)
12459#if ENABLE_ASH_EXPAND_PRMT 12479#if ENABLE_ASH_EXPAND_PRMT
12460 pssyntax = (syntax == PSSYNTAX); 12480 pssyntax = (syntax == PSSYNTAX);
12461 if (pssyntax) 12481 if (pssyntax)
@@ -12463,11 +12483,12 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
12463#else 12483#else
12464 pssyntax = 0; /* constant */ 12484 pssyntax = 0; /* constant */
12465#endif 12485#endif
12466 dblquote = (syntax == DQSYNTAX); 12486 synstack->syntax = syntax;
12467 varnest = 0; 12487
12468 IF_FEATURE_SH_MATH(arinest = 0;) 12488 if (syntax == DQSYNTAX)
12469 IF_FEATURE_SH_MATH(parenlevel = 0;) 12489 synstack->dblquote = 1;
12470 dqvarnest = 0; 12490 quotef = 0;
12491 bqlist = NULL;
12471 12492
12472 STARTSTACKSTR(out); 12493 STARTSTACKSTR(out);
12473 loop: 12494 loop:
@@ -12475,9 +12496,9 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
12475 CHECKEND(); /* set c to PEOF if at end of here document */ 12496 CHECKEND(); /* set c to PEOF if at end of here document */
12476 for (;;) { /* until end of line or end of word */ 12497 for (;;) { /* until end of line or end of word */
12477 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */ 12498 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
12478 switch (SIT(c, syntax)) { 12499 switch (SIT(c, synstack->syntax)) {
12479 case CNL: /* '\n' */ 12500 case CNL: /* '\n' */
12480 if (syntax == BASESYNTAX) 12501 if (synstack->syntax == BASESYNTAX)
12481 goto endword; /* exit outer loop */ 12502 goto endword; /* exit outer loop */
12482 USTPUTC(c, out); 12503 USTPUTC(c, out);
12483 nlprompt(); 12504 nlprompt();
@@ -12497,13 +12518,13 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
12497 if (c & 0x100) { 12518 if (c & 0x100) {
12498 /* Unknown escape. Encode as '\z' */ 12519 /* Unknown escape. Encode as '\z' */
12499 c = (unsigned char)c; 12520 c = (unsigned char)c;
12500 if (eofmark == NULL || dblquote) 12521 if (eofmark == NULL || synstack->dblquote)
12501 USTPUTC(CTLESC, out); 12522 USTPUTC(CTLESC, out);
12502 USTPUTC('\\', out); 12523 USTPUTC('\\', out);
12503 } 12524 }
12504 } 12525 }
12505#endif 12526#endif
12506 if (eofmark == NULL || dblquote) 12527 if (!eofmark || synstack->dblquote || synstack->varnest)
12507 USTPUTC(CTLESC, out); 12528 USTPUTC(CTLESC, out);
12508 USTPUTC(c, out); 12529 USTPUTC(c, out);
12509 break; 12530 break;
@@ -12523,20 +12544,13 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
12523 /* Backslash is retained if we are in "str" 12544 /* Backslash is retained if we are in "str"
12524 * and next char isn't dquote-special. 12545 * and next char isn't dquote-special.
12525 */ 12546 */
12526 if (dblquote 12547 if (synstack->dblquote
12527 && c != '\\' 12548 && c != '\\'
12528 && c != '`' 12549 && c != '`'
12529 && c != '$' 12550 && c != '$'
12530 && (c != '"' || eofmark != NULL) 12551 && (c != '"' || (eofmark != NULL && !synstack->varnest))
12552 && (c != '}' || !synstack->varnest)
12531 ) { 12553 ) {
12532//dash survives not doing USTPUTC(CTLESC), but merely by chance:
12533//Example: "\z" gets encoded as "\<CTLESC>z".
12534//rmescapes() then emits "\", "\z", protecting z from globbing.
12535//But it's wrong, should protect _both_ from globbing:
12536//everything in double quotes is not globbed.
12537//Unlike dash, we have a fix in rmescapes() which emits bare "z"
12538//for "<CTLESC>z" since "z" is not glob-special (else unicode may break),
12539//and glob would see "\z" and eat "\". Thus:
12540 USTPUTC(CTLESC, out); /* protect '\' from glob */ 12554 USTPUTC(CTLESC, out); /* protect '\' from glob */
12541 USTPUTC('\\', out); 12555 USTPUTC('\\', out);
12542 } 12556 }
@@ -12546,56 +12560,62 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
12546 } 12560 }
12547 break; 12561 break;
12548 case CSQUOTE: 12562 case CSQUOTE:
12549 syntax = SQSYNTAX; 12563 synstack->syntax = SQSYNTAX;
12550 quotemark: 12564 quotemark:
12551 if (eofmark == NULL) { 12565 if (eofmark == NULL) {
12552 USTPUTC(CTLQUOTEMARK, out); 12566 USTPUTC(CTLQUOTEMARK, out);
12553 } 12567 }
12554 break; 12568 break;
12555 case CDQUOTE: 12569 case CDQUOTE:
12556 syntax = DQSYNTAX; 12570 synstack->syntax = DQSYNTAX;
12557 dblquote = 1; 12571 synstack->dblquote = 1;
12572 toggledq:
12573 if (synstack->varnest)
12574 synstack->innerdq ^= 1;
12558 goto quotemark; 12575 goto quotemark;
12559 case CENDQUOTE: 12576 case CENDQUOTE:
12560 IF_BASH_DOLLAR_SQUOTE(bash_dollar_squote = 0;) 12577 IF_BASH_DOLLAR_SQUOTE(bash_dollar_squote = 0;)
12561 if (eofmark != NULL && varnest == 0) { 12578 if (eofmark != NULL && synstack->varnest == 0) {
12562 USTPUTC(c, out); 12579 USTPUTC(c, out);
12563 } else { 12580 break;
12564 if (dqvarnest == 0) {
12565 syntax = BASESYNTAX;
12566 dblquote = 0;
12567 }
12568 quotef = 1;
12569 goto quotemark;
12570 } 12581 }
12571 break; 12582
12583 if (synstack->dqvarnest == 0) {
12584 synstack->syntax = BASESYNTAX;
12585 synstack->dblquote = 0;
12586 }
12587
12588 quotef = 1;
12589
12590 if (c == '"')
12591 goto toggledq;
12592
12593 goto quotemark;
12572 case CVAR: /* '$' */ 12594 case CVAR: /* '$' */
12573 PARSESUB(); /* parse substitution */ 12595 PARSESUB(); /* parse substitution */
12574 break; 12596 break;
12575 case CENDVAR: /* '}' */ 12597 case CENDVAR: /* '}' */
12576 if (varnest > 0) { 12598 if (!synstack->innerdq && synstack->varnest > 0) {
12577 varnest--; 12599 if (!--synstack->varnest && synstack->varpushed)
12578 if (dqvarnest > 0) { 12600 synstack_pop(&synstack);
12579 dqvarnest--; 12601 else if (synstack->dqvarnest > 0)
12580 } 12602 synstack->dqvarnest--;
12581 c = CTLENDVAR; 12603 c = CTLENDVAR;
12582 } 12604 }
12583 USTPUTC(c, out); 12605 USTPUTC(c, out);
12584 break; 12606 break;
12585#if ENABLE_FEATURE_SH_MATH 12607#if ENABLE_FEATURE_SH_MATH
12586 case CLP: /* '(' in arithmetic */ 12608 case CLP: /* '(' in arithmetic */
12587 parenlevel++; 12609 synstack->parenlevel++;
12588 USTPUTC(c, out); 12610 USTPUTC(c, out);
12589 break; 12611 break;
12590 case CRP: /* ')' in arithmetic */ 12612 case CRP: /* ')' in arithmetic */
12591 if (parenlevel > 0) { 12613 if (synstack->parenlevel > 0) {
12592 parenlevel--; 12614 synstack->parenlevel--;
12593 } else { 12615 } else {
12594 if (pgetc_eatbnl() == ')') { 12616 if (pgetc_eatbnl() == ')') {
12595 c = CTLENDARI; 12617 c = CTLENDARI;
12596 if (--arinest == 0) { 12618 synstack_pop(&synstack);
12597 syntax = prevsyntax;
12598 }
12599 } else { 12619 } else {
12600 /* 12620 /*
12601 * unbalanced parens 12621 * unbalanced parens
@@ -12621,7 +12641,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
12621 case CIGN: 12641 case CIGN:
12622 break; 12642 break;
12623 default: 12643 default:
12624 if (varnest == 0) { 12644 if (synstack->varnest == 0) {
12625#if BASH_REDIR_OUTPUT 12645#if BASH_REDIR_OUTPUT
12626 if (c == '&') { 12646 if (c == '&') {
12627//Can't call pgetc_eatbnl() here, this requires three-deep pungetc() 12647//Can't call pgetc_eatbnl() here, this requires three-deep pungetc()
@@ -12640,12 +12660,12 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
12640 endword: 12660 endword:
12641 12661
12642#if ENABLE_FEATURE_SH_MATH 12662#if ENABLE_FEATURE_SH_MATH
12643 if (syntax == ARISYNTAX) 12663 if (synstack->syntax == ARISYNTAX)
12644 raise_error_syntax("missing '))'"); 12664 raise_error_syntax("missing '))'");
12645#endif 12665#endif
12646 if (syntax != BASESYNTAX && eofmark == NULL) 12666 if (synstack->syntax != BASESYNTAX && eofmark == NULL)
12647 raise_error_syntax("unterminated quoted string"); 12667 raise_error_syntax("unterminated quoted string");
12648 if (varnest != 0) { 12668 if (synstack->varnest != 0) {
12649 /* { */ 12669 /* { */
12650 raise_error_syntax("missing '}'"); 12670 raise_error_syntax("missing '}'");
12651 } 12671 }
@@ -12827,7 +12847,7 @@ parsesub: {
12827 || (c != '(' && c != '{' && !is_name(c) && !is_special(c)) 12847 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
12828 ) { 12848 ) {
12829#if BASH_DOLLAR_SQUOTE 12849#if BASH_DOLLAR_SQUOTE
12830 if (syntax != DQSYNTAX && c == '\'') 12850 if (synstack->syntax != DQSYNTAX && c == '\'')
12831 bash_dollar_squote = 1; 12851 bash_dollar_squote = 1;
12832 else 12852 else
12833#endif 12853#endif
@@ -12847,6 +12867,8 @@ parsesub: {
12847 } 12867 }
12848 } else { 12868 } else {
12849 /* $VAR, $<specialchar>, ${...}, or PEOA/PEOF */ 12869 /* $VAR, $<specialchar>, ${...}, or PEOA/PEOF */
12870 smalluint newsyn = synstack->syntax;
12871
12850 USTPUTC(CTLVAR, out); 12872 USTPUTC(CTLVAR, out);
12851 typeloc = out - (char *)stackblock(); 12873 typeloc = out - (char *)stackblock();
12852 STADJUST(1, out); 12874 STADJUST(1, out);
@@ -12905,6 +12927,8 @@ parsesub: {
12905 static const char types[] ALIGN1 = "}-+?="; 12927 static const char types[] ALIGN1 = "}-+?=";
12906 /* ${VAR...} but not $VAR or ${#VAR} */ 12928 /* ${VAR...} but not $VAR or ${#VAR} */
12907 /* c == first char after VAR */ 12929 /* c == first char after VAR */
12930 int cc = c;
12931
12908 switch (c) { 12932 switch (c) {
12909 case ':': 12933 case ':':
12910 c = pgetc_eatbnl(); 12934 c = pgetc_eatbnl();
@@ -12929,21 +12953,24 @@ parsesub: {
12929 break; 12953 break;
12930 } 12954 }
12931 case '%': 12955 case '%':
12932 case '#': { 12956 case '#':
12933 int cc = c;
12934 subtype = (c == '#' ? VSTRIMLEFT : VSTRIMRIGHT); 12957 subtype = (c == '#' ? VSTRIMLEFT : VSTRIMRIGHT);
12935 c = pgetc_eatbnl(); 12958 c = pgetc_eatbnl();
12936 if (c != cc) 12959 if (c == cc)
12937 goto badsub; 12960 subtype++;
12938 subtype++; 12961 else
12962 pungetc();
12963
12964 newsyn = BASESYNTAX;
12939 break; 12965 break;
12940 }
12941#if BASH_PATTERN_SUBST 12966#if BASH_PATTERN_SUBST
12942 case '/': 12967 case '/':
12943 /* ${v/[/]pattern/repl} */ 12968 /* ${v/[/]pattern/repl} */
12944//TODO: encode pattern and repl separately. 12969//TODO: encode pattern and repl separately.
12945// Currently ${v/$var_with_slash/repl} is horribly broken 12970// Currently cases like: v=1;echo ${v/$((1/1))/ONE}
12971// are broken (should print "ONE")
12946 subtype = VSREPLACE; 12972 subtype = VSREPLACE;
12973 newsyn = BASESYNTAX;
12947 c = pgetc_eatbnl(); 12974 c = pgetc_eatbnl();
12948 if (c != '/') 12975 if (c != '/')
12949 goto badsub; 12976 goto badsub;
@@ -12955,11 +12982,26 @@ parsesub: {
12955 badsub: 12982 badsub:
12956 pungetc(); 12983 pungetc();
12957 } 12984 }
12985
12986 if (newsyn == ARISYNTAX)
12987 newsyn = DQSYNTAX;
12988
12989 if ((newsyn != synstack->syntax || synstack->innerdq)
12990 && subtype != VSNORMAL
12991 ) {
12992 synstack_push(&synstack,
12993 synstack->prev ?: alloca(sizeof(*synstack)),
12994 newsyn);
12995
12996 synstack->varpushed = 1;
12997 synstack->dblquote = newsyn != BASESYNTAX;
12998 }
12999
12958 ((unsigned char *)stackblock())[typeloc] = subtype; 13000 ((unsigned char *)stackblock())[typeloc] = subtype;
12959 if (subtype != VSNORMAL) { 13001 if (subtype != VSNORMAL) {
12960 varnest++; 13002 synstack->varnest++;
12961 if (dblquote) 13003 if (synstack->dblquote)
12962 dqvarnest++; 13004 synstack->dqvarnest++;
12963 } 13005 }
12964 STPUTC('=', out); 13006 STPUTC('=', out);
12965 } 13007 }
@@ -13016,7 +13058,7 @@ parsebackq: {
13016 case '\\': 13058 case '\\':
13017 pc = pgetc(); /* or pgetc_eatbnl()? why (example)? */ 13059 pc = pgetc(); /* or pgetc_eatbnl()? why (example)? */
13018 if (pc != '\\' && pc != '`' && pc != '$' 13060 if (pc != '\\' && pc != '`' && pc != '$'
13019 && (!dblquote || pc != '"') 13061 && (!synstack->dblquote || pc != '"')
13020 ) { 13062 ) {
13021 STPUTC('\\', pout); 13063 STPUTC('\\', pout);
13022 } 13064 }
@@ -13091,10 +13133,11 @@ parsebackq: {
13091 * Parse an arithmetic expansion (indicate start of one and set state) 13133 * Parse an arithmetic expansion (indicate start of one and set state)
13092 */ 13134 */
13093parsearith: { 13135parsearith: {
13094 if (++arinest == 1) { 13136
13095 prevsyntax = syntax; 13137 synstack_push(&synstack,
13096 syntax = ARISYNTAX; 13138 synstack->prev ?: alloca(sizeof(*synstack)),
13097 } 13139 ARISYNTAX);
13140 synstack->dblquote = 1;
13098 USTPUTC(CTLARI, out); 13141 USTPUTC(CTLARI, out);
13099 goto parsearith_return; 13142 goto parsearith_return;
13100} 13143}
@@ -14542,7 +14585,7 @@ init(void)
14542 14585
14543 14586
14544//usage:#define ash_trivial_usage 14587//usage:#define ash_trivial_usage
14545//usage: "[-/+OPTIONS] [-/+o OPT]... [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS]]" 14588//usage: "[-/+OPTIONS] [-/+o OPT]... [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS] / -s [ARGS]]"
14546//usage:#define ash_full_usage "\n\n" 14589//usage:#define ash_full_usage "\n\n"
14547//usage: "Unix shell interpreter" 14590//usage: "Unix shell interpreter"
14548 14591
diff --git a/shell/ash_test/ash-arith/arith_nested1.right b/shell/ash_test/ash-arith/arith_nested1.right
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/shell/ash_test/ash-arith/arith_nested1.right
@@ -0,0 +1 @@
1
diff --git a/shell/ash_test/ash-arith/arith_nested1.tests b/shell/ash_test/ash-arith/arith_nested1.tests
new file mode 100755
index 000000000..28571b833
--- /dev/null
+++ b/shell/ash_test/ash-arith/arith_nested1.tests
@@ -0,0 +1 @@
echo $(( ( $((1)) ) ))
diff --git a/shell/ash_test/ash-heredoc/heredoc_var_expand1.right b/shell/ash_test/ash-heredoc/heredoc_var_expand1.right
new file mode 100644
index 000000000..eb221832d
--- /dev/null
+++ b/shell/ash_test/ash-heredoc/heredoc_var_expand1.right
@@ -0,0 +1,4 @@
1
2Ok1:0
3
4Ok2:0
diff --git a/shell/ash_test/ash-heredoc/heredoc_var_expand1.tests b/shell/ash_test/ash-heredoc/heredoc_var_expand1.tests
new file mode 100755
index 000000000..3b00bab7b
--- /dev/null
+++ b/shell/ash_test/ash-heredoc/heredoc_var_expand1.tests
@@ -0,0 +1,11 @@
1x='*'
2
3cat <<- EOF
4 ${x#'*'}
5EOF
6echo Ok1:$?
7
8cat <<EOF
9${x#'*'}
10EOF
11echo Ok2:$?
diff --git a/shell/ash_test/ash-misc/assignment5.right b/shell/ash_test/ash-misc/assignment5.right
new file mode 100644
index 000000000..a91554c09
--- /dev/null
+++ b/shell/ash_test/ash-misc/assignment5.right
@@ -0,0 +1,5 @@
1Zero1:0
2Zero2:0
3Zero3:0
4Zero4:0 x:1 y:1
5Three:3 x:1 y:1
diff --git a/shell/ash_test/ash-misc/assignment5.tests b/shell/ash_test/ash-misc/assignment5.tests
new file mode 100755
index 000000000..0b8104285
--- /dev/null
+++ b/shell/ash_test/ash-misc/assignment5.tests
@@ -0,0 +1,9 @@
1true; a=1; echo Zero1:$?
2false; a=1; echo Zero2:$?
3false || a=1; echo Zero3:$?
4
5false || x=$? y=`echo $?`; echo Zero4:$? x:$x y:$y
6false || x=$? y=`echo $?; exit 3`; echo Three:$? x:$x y:$y
7
8#ash sets z=1 instead of z=3. disabled for now
9#false || x=$? y=`echo $?; exit 3` z=`echo $?`; echo x:$x y:$y z:$z
diff --git a/shell/ash_test/ash-misc/func5.right b/shell/ash_test/ash-misc/func5.right
index 2c9d316b3..01e79c32a 100644
--- a/shell/ash_test/ash-misc/func5.right
+++ b/shell/ash_test/ash-misc/func5.right
@@ -1,6 +1,3 @@
11 11
22 22
33 33
41
52
63
diff --git a/shell/ash_test/ash-misc/func5.tests b/shell/ash_test/ash-misc/func5.tests
index e967208cc..5c33560bc 100755
--- a/shell/ash_test/ash-misc/func5.tests
+++ b/shell/ash_test/ash-misc/func5.tests
@@ -6,8 +6,3 @@ f 2
6 6
7f() ( echo $1 ) 7f() ( echo $1 )
8f 3 8f 3
9
10f() for i in 1 2 3; do
11 echo $i
12done
13f
diff --git a/shell/ash_test/ash-misc/func_compound1.right b/shell/ash_test/ash-misc/func_compound1.right
new file mode 100644
index 000000000..01e79c32a
--- /dev/null
+++ b/shell/ash_test/ash-misc/func_compound1.right
@@ -0,0 +1,3 @@
11
22
33
diff --git a/shell/ash_test/ash-misc/func_compound1.tests b/shell/ash_test/ash-misc/func_compound1.tests
new file mode 100755
index 000000000..20c8bf18b
--- /dev/null
+++ b/shell/ash_test/ash-misc/func_compound1.tests
@@ -0,0 +1,4 @@
1f() for i in 1 2 3; do
2 echo $i
3done
4f
diff --git a/shell/ash_test/ash-parsing/starquoted3.right b/shell/ash_test/ash-parsing/starquoted3.right
new file mode 100644
index 000000000..fea246c14
--- /dev/null
+++ b/shell/ash_test/ash-parsing/starquoted3.right
@@ -0,0 +1,2 @@
1<a>
2<>
diff --git a/shell/ash_test/ash-parsing/starquoted3.tests b/shell/ash_test/ash-parsing/starquoted3.tests
new file mode 100755
index 000000000..8eefe4245
--- /dev/null
+++ b/shell/ash_test/ash-parsing/starquoted3.tests
@@ -0,0 +1 @@
set -- a ""; space=" "; printf "<%s>\n" "$@"$space
diff --git a/shell/ash_test/ash-quoting/bkslash_case2.right b/shell/ash_test/ash-quoting/bkslash_case2.right
new file mode 100644
index 000000000..8d2038bff
--- /dev/null
+++ b/shell/ash_test/ash-quoting/bkslash_case2.right
@@ -0,0 +1,3 @@
1ok1
2ok2
3Ok:0
diff --git a/shell/ash_test/ash-quoting/bkslash_case2.tests b/shell/ash_test/ash-quoting/bkslash_case2.tests
new file mode 100755
index 000000000..348ddc236
--- /dev/null
+++ b/shell/ash_test/ash-quoting/bkslash_case2.tests
@@ -0,0 +1,13 @@
1x='\abc'
2
3case "$x" in
4\\*) echo ok1;;
5*) echo BUG1;;
6esac
7
8case $x in
9\\*) echo ok2;;
10*) echo BUG2;;
11esac
12
13echo Ok:$?
diff --git a/shell/ash_test/ash-quoting/quote_in_varexp1.right b/shell/ash_test/ash-quoting/quote_in_varexp1.right
new file mode 100644
index 000000000..99a0aea7c
--- /dev/null
+++ b/shell/ash_test/ash-quoting/quote_in_varexp1.right
@@ -0,0 +1,2 @@
1''
2Ok:0
diff --git a/shell/ash_test/ash-quoting/quote_in_varexp1.tests b/shell/ash_test/ash-quoting/quote_in_varexp1.tests
new file mode 100755
index 000000000..1b97b0556
--- /dev/null
+++ b/shell/ash_test/ash-quoting/quote_in_varexp1.tests
@@ -0,0 +1,2 @@
1x="''''"; echo "${x#"${x+''}"''}"
2echo Ok:$?
diff --git a/shell/ash_test/ash-quoting/squote_in_varexp3.right b/shell/ash_test/ash-quoting/squote_in_varexp3.right
new file mode 100644
index 000000000..223b7836f
--- /dev/null
+++ b/shell/ash_test/ash-quoting/squote_in_varexp3.right
@@ -0,0 +1 @@
B
diff --git a/shell/ash_test/ash-quoting/squote_in_varexp3.tests b/shell/ash_test/ash-quoting/squote_in_varexp3.tests
new file mode 100755
index 000000000..028a88fd9
--- /dev/null
+++ b/shell/ash_test/ash-quoting/squote_in_varexp3.tests
@@ -0,0 +1 @@
x=\'B; echo "${x#\'}"
diff --git a/shell/ash_test/ash-redir/redir_exec1.right b/shell/ash_test/ash-redir/redir_exec1.right
new file mode 100644
index 000000000..d4393d10c
--- /dev/null
+++ b/shell/ash_test/ash-redir/redir_exec1.right
@@ -0,0 +1,2 @@
1redir_exec1.tests: line 1: can't create /cant/be/created: nonexistent directory
2First
diff --git a/shell/ash_test/ash-redir/redir_exec1.tests b/shell/ash_test/ash-redir/redir_exec1.tests
new file mode 100755
index 000000000..290e1cb39
--- /dev/null
+++ b/shell/ash_test/ash-redir/redir_exec1.tests
@@ -0,0 +1,2 @@
1v=`echo First >&2` exec >/cant/be/created
2echo One:$?
diff --git a/shell/ash_test/ash-vars/var_bash3.right b/shell/ash_test/ash-vars/var_bash3.right
index a97c850ea..8899d981c 100644
--- a/shell/ash_test/ash-vars/var_bash3.right
+++ b/shell/ash_test/ash-vars/var_bash3.right
@@ -1,6 +1,6 @@
11 a041#c 11 a041#c
22 a041#c 22 a041#c
33 a\041#c 33 a041#c
44 a\041#c 44 a\041#c
55 a\041#c 55 a\041#c
66 a\041#c 66 a\041#c
@@ -17,4 +17,4 @@
1717 a\tc 1717 a\tc
1818 a\tc 1818 a\tc
1919 atc 1919 atc
2020 a\tc 2020 atc
diff --git a/shell/ash_test/ash-vars/var_bash4.right b/shell/ash_test/ash-vars/var_bash4.right
index 0ef1bf661..9067e58e6 100644
--- a/shell/ash_test/ash-vars/var_bash4.right
+++ b/shell/ash_test/ash-vars/var_bash4.right
@@ -3,26 +3,26 @@ Replace str: _\\_\z_
3Pattern: single backslash and star: "replace literal star" 3Pattern: single backslash and star: "replace literal star"
4Unquoted: a_\_z_b\*c 4Unquoted: a_\_z_b\*c
5Unquoted =: a_\_z_b\*c 5Unquoted =: a_\_z_b\*c
6Quoted: a_\_\z_b\*c 6Quoted: a_\_z_b\*c
7Quoted =: a_\_\z_b\*c 7Quoted =: a_\_z_b\*c
8Pattern: double backslash and star: "replace backslash and everything after it" 8Pattern: double backslash and star: "replace backslash and everything after it"
9Unquoted: a*b_\_z_ 9Unquoted: a*b_\_z_
10Unquoted =: a*b_\_z_ 10Unquoted =: a*b_\_z_
11Quoted: a*b_\_\z_ 11Quoted: a*b_\_z_
12Quoted =: a*b_\_\z_ 12Quoted =: a*b_\_z_
13 13
14Source: a\bc 14Source: a\bc
15Replace str: _\\_\z_ 15Replace str: _\\_\z_
16Pattern: single backslash and b: "replace b" 16Pattern: single backslash and b: "replace b"
17Unquoted: a\_\_z_c 17Unquoted: a\_\_z_c
18Unquoted =: a\_\_z_c 18Unquoted =: a\_\_z_c
19Quoted: a\_\_\z_c 19Quoted: a\_\_z_c
20Quoted =: a\_\_\z_c 20Quoted =: a\_\_z_c
21Pattern: double backslash and b: "replace backslash and b" 21Pattern: double backslash and b: "replace backslash and b"
22Unquoted: a_\_z_c 22Unquoted: a_\_z_c
23Unquoted =: a_\_z_c 23Unquoted =: a_\_z_c
24Quoted: a_\_\z_c 24Quoted: a_\_z_c
25Quoted =: a_\_\z_c 25Quoted =: a_\_z_c
26 26
27Source: a\bc 27Source: a\bc
28Replace str: _\\_\z_ (as variable $s) 28Replace str: _\\_\z_ (as variable $s)
diff --git a/shell/ash_test/ash-vars/var_bash6.right b/shell/ash_test/ash-vars/var_bash6.right
index 63fc23df8..115ff8b04 100644
--- a/shell/ash_test/ash-vars/var_bash6.right
+++ b/shell/ash_test/ash-vars/var_bash6.right
@@ -1,5 +1,5 @@
1Expected Actual 1Expected Actual
2a*z : a*z 2a*z : a*z
3\z : \z 3z : z
4a1z a2z: a1z a2z 4a1z a2z: a1z a2z
5z : z 5z : z
diff --git a/shell/ash_test/ash-vars/var_bash6.tests b/shell/ash_test/ash-vars/var_bash6.tests
index cf2e4f020..686834177 100755
--- a/shell/ash_test/ash-vars/var_bash6.tests
+++ b/shell/ash_test/ash-vars/var_bash6.tests
@@ -3,7 +3,7 @@
3>a1z; >a2z; 3>a1z; >a2z;
4 echo 'Expected' 'Actual' 4 echo 'Expected' 'Actual'
5v='a bz'; echo 'a*z :' "${v/a*z/a*z}" 5v='a bz'; echo 'a*z :' "${v/a*z/a*z}"
6v='a bz'; echo '\z :' "${v/a*z/\z}" 6v='a bz'; echo 'z :' "${v/a*z/\z}"
7v='a bz'; echo 'a1z a2z:' ${v/a*z/a*z} 7v='a bz'; echo 'a1z a2z:' ${v/a*z/a*z}
8v='a bz'; echo 'z :' ${v/a*z/\z} 8v='a bz'; echo 'z :' ${v/a*z/\z}
9rm a1z a2z 9rm a1z a2z
diff --git a/shell/ash_test/ash-vars/var_bash7.right b/shell/ash_test/ash-vars/var_bash7.right
new file mode 100644
index 000000000..223b7836f
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_bash7.right
@@ -0,0 +1 @@
B
diff --git a/shell/ash_test/ash-vars/var_bash7.tests b/shell/ash_test/ash-vars/var_bash7.tests
new file mode 100755
index 000000000..c4ce03f7f
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_bash7.tests
@@ -0,0 +1 @@
x=AB; echo "${x#$'\x41'}"
diff --git a/shell/hush.c b/shell/hush.c
index 06fe0e405..d5ea3b21f 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -79,20 +79,6 @@
79 * Some builtins mandated by standards: 79 * Some builtins mandated by standards:
80 * newgrp [GRP]: not a builtin in bash but a suid binary 80 * newgrp [GRP]: not a builtin in bash but a suid binary
81 * which spawns a new shell with new group ID 81 * which spawns a new shell with new group ID
82 * In bash, export builtin is special, its arguments are assignments
83 * and therefore expansion of them should be "one-word" expansion:
84 * $ export i=`echo 'a b'` # export has one arg: "i=a b"
85 * compare with:
86 * $ ls i=`echo 'a b'` # ls has two args: "i=a" and "b"
87 * ls: cannot access i=a: No such file or directory
88 * ls: cannot access b: No such file or directory
89 * Note1: same applies to local builtin.
90 * Note2: bash 3.2.33(1) does this only if export word itself
91 * is not quoted:
92 * $ export i=`echo 'aaa bbb'`; echo "$i"
93 * aaa bbb
94 * $ "export" i=`echo 'aaa bbb'`; echo "$i"
95 * aaa
96 */ 82 */
97//config:config HUSH 83//config:config HUSH
98//config: bool "hush (64 kb)" 84//config: bool "hush (64 kb)"
@@ -326,13 +312,13 @@
326//kbuild:lib-$(CONFIG_BASH_IS_HUSH) += hush.o match.o shell_common.o 312//kbuild:lib-$(CONFIG_BASH_IS_HUSH) += hush.o match.o shell_common.o
327//kbuild:lib-$(CONFIG_HUSH_RANDOM_SUPPORT) += random.o 313//kbuild:lib-$(CONFIG_HUSH_RANDOM_SUPPORT) += random.o
328 314
329/* -i (interactive) and -s (read stdin) are also accepted, 315/* -i (interactive) is also accepted,
330 * but currently do nothing, therefore aren't shown in help. 316 * but does nothing, therefore not shown in help.
331 * NOMMU-specific options are not meant to be used by users, 317 * NOMMU-specific options are not meant to be used by users,
332 * therefore we don't show them either. 318 * therefore we don't show them either.
333 */ 319 */
334//usage:#define hush_trivial_usage 320//usage:#define hush_trivial_usage
335//usage: "[-enxl] [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS]]" 321//usage: "[-enxl] [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS] / -s [ARGS]]"
336//usage:#define hush_full_usage "\n\n" 322//usage:#define hush_full_usage "\n\n"
337//usage: "Unix shell interpreter" 323//usage: "Unix shell interpreter"
338 324
@@ -454,6 +440,7 @@
454#define debug_printf_redir(...) do {} while (0) 440#define debug_printf_redir(...) do {} while (0)
455#define debug_printf_list(...) do {} while (0) 441#define debug_printf_list(...) do {} while (0)
456#define debug_printf_subst(...) do {} while (0) 442#define debug_printf_subst(...) do {} while (0)
443#define debug_printf_prompt(...) do {} while (0)
457#define debug_printf_clean(...) do {} while (0) 444#define debug_printf_clean(...) do {} while (0)
458 445
459#define ERR_PTR ((void*)(long)1) 446#define ERR_PTR ((void*)(long)1)
@@ -489,7 +476,6 @@ static const char hush_version_str[] ALIGN1 = "HUSH_VERSION="BB_VER;
489 */ 476 */
490#if !BB_MMU 477#if !BB_MMU
491typedef struct nommu_save_t { 478typedef struct nommu_save_t {
492 char **new_env;
493 struct variable *old_vars; 479 struct variable *old_vars;
494 char **argv; 480 char **argv;
495 char **argv_from_re_execing; 481 char **argv_from_re_execing;
@@ -566,9 +552,6 @@ static const char *const assignment_flag[] = {
566 552
567typedef struct in_str { 553typedef struct in_str {
568 const char *p; 554 const char *p;
569#if ENABLE_HUSH_INTERACTIVE
570 smallint promptmode; /* 0: PS1, 1: PS2 */
571#endif
572 int peek_buf[2]; 555 int peek_buf[2];
573 int last_char; 556 int last_char;
574 FILE *file; 557 FILE *file;
@@ -623,15 +606,17 @@ typedef enum redir_type {
623 606
624struct command { 607struct command {
625 pid_t pid; /* 0 if exited */ 608 pid_t pid; /* 0 if exited */
626 int assignment_cnt; /* how many argv[i] are assignments? */ 609 unsigned assignment_cnt; /* how many argv[i] are assignments? */
627#if ENABLE_HUSH_LINENO_VAR 610#if ENABLE_HUSH_LINENO_VAR
628 unsigned lineno; 611 unsigned lineno;
629#endif 612#endif
630 smallint cmd_type; /* CMD_xxx */ 613 smallint cmd_type; /* CMD_xxx */
631#define CMD_NORMAL 0 614#define CMD_NORMAL 0
632#define CMD_SUBSHELL 1 615#define CMD_SUBSHELL 1
633#if BASH_TEST2 616#if BASH_TEST2 || ENABLE_HUSH_LOCAL || ENABLE_HUSH_EXPORT || ENABLE_HUSH_READONLY
634/* used for "[[ EXPR ]]" */ 617/* used for "[[ EXPR ]]", and to prevent word splitting and globbing in
618 * "export v=t*"
619 */
635# define CMD_SINGLEWORD_NOGLOB 2 620# define CMD_SINGLEWORD_NOGLOB 2
636#endif 621#endif
637#if ENABLE_HUSH_FUNCTIONS 622#if ENABLE_HUSH_FUNCTIONS
@@ -742,10 +727,8 @@ struct parse_context {
742struct variable { 727struct variable {
743 struct variable *next; 728 struct variable *next;
744 char *varstr; /* points to "name=" portion */ 729 char *varstr; /* points to "name=" portion */
745#if ENABLE_HUSH_LOCAL
746 unsigned func_nest_level;
747#endif
748 int max_len; /* if > 0, name is part of initial env; else name is malloced */ 730 int max_len; /* if > 0, name is part of initial env; else name is malloced */
731 uint16_t var_nest_level;
749 smallint flg_export; /* putenv should be done on this var */ 732 smallint flg_export; /* putenv should be done on this var */
750 smallint flg_read_only; 733 smallint flg_read_only;
751}; 734};
@@ -843,7 +826,7 @@ struct globals {
843 * _AND_ if we decided to act interactively */ 826 * _AND_ if we decided to act interactively */
844 int interactive_fd; 827 int interactive_fd;
845 const char *PS1; 828 const char *PS1;
846 const char *PS2; 829 IF_FEATURE_EDITING_FANCY_PROMPT(const char *PS2;)
847# define G_interactive_fd (G.interactive_fd) 830# define G_interactive_fd (G.interactive_fd)
848#else 831#else
849# define G_interactive_fd 0 832# define G_interactive_fd 0
@@ -891,6 +874,9 @@ struct globals {
891#else 874#else
892# define G_x_mode 0 875# define G_x_mode 0
893#endif 876#endif
877#if ENABLE_HUSH_INTERACTIVE
878 smallint promptmode; /* 0: PS1, 1: PS2 */
879#endif
894 smallint flag_SIGINT; 880 smallint flag_SIGINT;
895#if ENABLE_HUSH_LOOPS 881#if ENABLE_HUSH_LOOPS
896 smallint flag_break_continue; 882 smallint flag_break_continue;
@@ -906,8 +892,9 @@ struct globals {
906# define G_flag_return_in_progress 0 892# define G_flag_return_in_progress 0
907#endif 893#endif
908 smallint exiting; /* used to prevent EXIT trap recursion */ 894 smallint exiting; /* used to prevent EXIT trap recursion */
909 /* These four support $?, $#, and $1 */ 895 /* These support $?, $#, and $1 */
910 smalluint last_exitcode; 896 smalluint last_exitcode;
897 smalluint expand_exitcode;
911 smalluint last_bg_pid_exitcode; 898 smalluint last_bg_pid_exitcode;
912#if ENABLE_HUSH_SET 899#if ENABLE_HUSH_SET
913 /* are global_argv and global_argv[1..n] malloced? (note: not [0]) */ 900 /* are global_argv and global_argv[1..n] malloced? (note: not [0]) */
@@ -933,12 +920,13 @@ struct globals {
933 const char *cwd; 920 const char *cwd;
934 struct variable *top_var; 921 struct variable *top_var;
935 char **expanded_assignments; 922 char **expanded_assignments;
923 struct variable **shadowed_vars_pp;
924 unsigned var_nest_level;
936#if ENABLE_HUSH_FUNCTIONS 925#if ENABLE_HUSH_FUNCTIONS
937 struct function *top_func;
938# if ENABLE_HUSH_LOCAL 926# if ENABLE_HUSH_LOCAL
939 struct variable **shadowed_vars_pp; 927 unsigned func_nest_level; /* solely to prevent "local v" in non-functions */
940 unsigned func_nest_level;
941# endif 928# endif
929 struct function *top_func;
942#endif 930#endif
943 /* Signal and trap handling */ 931 /* Signal and trap handling */
944#if ENABLE_HUSH_FAST 932#if ENABLE_HUSH_FAST
@@ -1261,6 +1249,10 @@ static const struct built_in_command bltins2[] = {
1261# define debug_printf_subst(...) (indent(), fdprintf(2, __VA_ARGS__)) 1249# define debug_printf_subst(...) (indent(), fdprintf(2, __VA_ARGS__))
1262#endif 1250#endif
1263 1251
1252#ifndef debug_printf_prompt
1253# define debug_printf_prompt(...) (indent(), fdprintf(2, __VA_ARGS__))
1254#endif
1255
1264#ifndef debug_printf_clean 1256#ifndef debug_printf_clean
1265# define debug_printf_clean(...) (indent(), fdprintf(2, __VA_ARGS__)) 1257# define debug_printf_clean(...) (indent(), fdprintf(2, __VA_ARGS__))
1266# define DEBUG_CLEAN 1 1258# define DEBUG_CLEAN 1
@@ -1407,7 +1399,7 @@ static void syntax_error_unexpected_ch(unsigned lineno UNUSED_PARAM, int ch)
1407#endif 1399#endif
1408 1400
1409 1401
1410#if ENABLE_HUSH_INTERACTIVE 1402#if ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING_FANCY_PROMPT
1411static void cmdedit_update_prompt(void); 1403static void cmdedit_update_prompt(void);
1412#else 1404#else
1413# define cmdedit_update_prompt() ((void)0) 1405# define cmdedit_update_prompt() ((void)0)
@@ -2136,40 +2128,58 @@ static const char* FAST_FUNC get_local_var_value(const char *name)
2136 return NULL; 2128 return NULL;
2137} 2129}
2138 2130
2131static void handle_changed_special_names(const char *name, unsigned name_len)
2132{
2133 if (ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING_FANCY_PROMPT
2134 && name_len == 3 && name[0] == 'P' && name[1] == 'S'
2135 ) {
2136 cmdedit_update_prompt();
2137 return;
2138 }
2139
2140 if ((ENABLE_HUSH_LINENO_VAR || ENABLE_HUSH_GETOPTS)
2141 && name_len == 6
2142 ) {
2143#if ENABLE_HUSH_LINENO_VAR
2144 if (strncmp(name, "LINENO", 6) == 0) {
2145 G.lineno_var = NULL;
2146 return;
2147 }
2148#endif
2149#if ENABLE_HUSH_GETOPTS
2150 if (strncmp(name, "OPTIND", 6) == 0) {
2151 G.getopt_count = 0;
2152 return;
2153 }
2154#endif
2155 }
2156}
2157
2139/* str holds "NAME=VAL" and is expected to be malloced. 2158/* str holds "NAME=VAL" and is expected to be malloced.
2140 * We take ownership of it. 2159 * We take ownership of it.
2141 */ 2160 */
2142#define SETFLAG_EXPORT (1 << 0) 2161#define SETFLAG_EXPORT (1 << 0)
2143#define SETFLAG_UNEXPORT (1 << 1) 2162#define SETFLAG_UNEXPORT (1 << 1)
2144#define SETFLAG_MAKE_RO (1 << 2) 2163#define SETFLAG_MAKE_RO (1 << 2)
2145#define SETFLAG_LOCAL_SHIFT 3 2164#define SETFLAG_VARLVL_SHIFT 3
2146static int set_local_var(char *str, unsigned flags) 2165static int set_local_var(char *str, unsigned flags)
2147{ 2166{
2148 struct variable **var_pp; 2167 struct variable **cur_pp;
2149 struct variable *cur; 2168 struct variable *cur;
2150 char *free_me = NULL; 2169 char *free_me = NULL;
2151 char *eq_sign; 2170 char *eq_sign;
2152 int name_len; 2171 int name_len;
2153 IF_HUSH_LOCAL(unsigned local_lvl = (flags >> SETFLAG_LOCAL_SHIFT);) 2172 unsigned local_lvl = (flags >> SETFLAG_VARLVL_SHIFT);
2154 2173
2155 eq_sign = strchr(str, '='); 2174 eq_sign = strchr(str, '=');
2156 if (!eq_sign) { /* not expected to ever happen? */ 2175 if (HUSH_DEBUG && !eq_sign)
2157 free(str); 2176 bb_error_msg_and_die("BUG in setvar");
2158 return -1;
2159 }
2160 2177
2161 name_len = eq_sign - str + 1; /* including '=' */ 2178 name_len = eq_sign - str + 1; /* including '=' */
2162#if ENABLE_HUSH_LINENO_VAR 2179 cur_pp = &G.top_var;
2163 if (G.lineno_var) { 2180 while ((cur = *cur_pp) != NULL) {
2164 if (name_len == 7 && strncmp("LINENO", str, 6) == 0)
2165 G.lineno_var = NULL;
2166 }
2167#endif
2168
2169 var_pp = &G.top_var;
2170 while ((cur = *var_pp) != NULL) {
2171 if (strncmp(cur->varstr, str, name_len) != 0) { 2181 if (strncmp(cur->varstr, str, name_len) != 0) {
2172 var_pp = &cur->next; 2182 cur_pp = &cur->next;
2173 continue; 2183 continue;
2174 } 2184 }
2175 2185
@@ -2187,15 +2197,7 @@ static int set_local_var(char *str, unsigned flags)
2187 unsetenv(str); 2197 unsetenv(str);
2188 *eq_sign = '='; 2198 *eq_sign = '=';
2189 } 2199 }
2190#if ENABLE_HUSH_LOCAL 2200 if (cur->var_nest_level < local_lvl) {
2191 if (cur->func_nest_level < local_lvl) {
2192 /* New variable is declared as local,
2193 * and existing one is global, or local
2194 * from enclosing function.
2195 * Remove and save old one: */
2196 *var_pp = cur->next;
2197 cur->next = *G.shadowed_vars_pp;
2198 *G.shadowed_vars_pp = cur;
2199 /* bash 3.2.33(1) and exported vars: 2201 /* bash 3.2.33(1) and exported vars:
2200 * # export z=z 2202 * # export z=z
2201 * # f() { local z=a; env | grep ^z; } 2203 * # f() { local z=a; env | grep ^z; }
@@ -2206,17 +2208,46 @@ static int set_local_var(char *str, unsigned flags)
2206 */ 2208 */
2207 if (cur->flg_export) 2209 if (cur->flg_export)
2208 flags |= SETFLAG_EXPORT; 2210 flags |= SETFLAG_EXPORT;
2211 /* New variable is local ("local VAR=VAL" or
2212 * "VAR=VAL cmd")
2213 * and existing one is global, or local
2214 * on a lower level that new one.
2215 * Remove it from global variable list:
2216 */
2217 *cur_pp = cur->next;
2218 if (G.shadowed_vars_pp) {
2219 /* Save in "shadowed" list */
2220 debug_printf_env("shadowing %s'%s'/%u by '%s'/%u\n",
2221 cur->flg_export ? "exported " : "",
2222 cur->varstr, cur->var_nest_level, str, local_lvl
2223 );
2224 cur->next = *G.shadowed_vars_pp;
2225 *G.shadowed_vars_pp = cur;
2226 } else {
2227 /* Came from pseudo_exec_argv(), no need to save: delete it */
2228 debug_printf_env("shadow-deleting %s'%s'/%u by '%s'/%u\n",
2229 cur->flg_export ? "exported " : "",
2230 cur->varstr, cur->var_nest_level, str, local_lvl
2231 );
2232 if (cur->max_len == 0) /* allocated "VAR=VAL"? */
2233 free_me = cur->varstr; /* then free it later */
2234 free(cur);
2235 }
2209 break; 2236 break;
2210 } 2237 }
2211#endif 2238
2212 if (strcmp(cur->varstr + name_len, eq_sign + 1) == 0) { 2239 if (strcmp(cur->varstr + name_len, eq_sign + 1) == 0) {
2240 debug_printf_env("assignement '%s' does not change anything\n", str);
2213 free_and_exp: 2241 free_and_exp:
2214 free(str); 2242 free(str);
2215 goto exp; 2243 goto exp;
2216 } 2244 }
2245
2246 /* Replace the value in the found "struct variable" */
2217 if (cur->max_len != 0) { 2247 if (cur->max_len != 0) {
2218 if (cur->max_len >= strlen(str)) { 2248 if (cur->max_len >= strnlen(str, cur->max_len + 1)) {
2219 /* This one is from startup env, reuse space */ 2249 /* This one is from startup env, reuse space */
2250 debug_printf_env("reusing startup env for '%s'\n", str);
2220 strcpy(cur->varstr, str); 2251 strcpy(cur->varstr, str);
2221 goto free_and_exp; 2252 goto free_and_exp;
2222 } 2253 }
@@ -2234,11 +2265,11 @@ static int set_local_var(char *str, unsigned flags)
2234 goto set_str_and_exp; 2265 goto set_str_and_exp;
2235 } 2266 }
2236 2267
2237 /* Not found - create new variable struct */ 2268 /* Not found or shadowed - create new variable struct */
2238 cur = xzalloc(sizeof(*cur)); 2269 cur = xzalloc(sizeof(*cur));
2239 IF_HUSH_LOCAL(cur->func_nest_level = local_lvl;) 2270 cur->var_nest_level = local_lvl;
2240 cur->next = *var_pp; 2271 cur->next = *cur_pp;
2241 *var_pp = cur; 2272 *cur_pp = cur;
2242 2273
2243 set_str_and_exp: 2274 set_str_and_exp:
2244 cur->varstr = str; 2275 cur->varstr = str;
@@ -2250,20 +2281,13 @@ static int set_local_var(char *str, unsigned flags)
2250#endif 2281#endif
2251 if (flags & SETFLAG_EXPORT) 2282 if (flags & SETFLAG_EXPORT)
2252 cur->flg_export = 1; 2283 cur->flg_export = 1;
2253 if (name_len == 4 && cur->varstr[0] == 'P' && cur->varstr[1] == 'S')
2254 cmdedit_update_prompt();
2255#if ENABLE_HUSH_GETOPTS
2256 /* defoptindvar is a "OPTIND=..." constant string */
2257 if (strncmp(cur->varstr, defoptindvar, 7) == 0)
2258 G.getopt_count = 0;
2259#endif
2260 if (cur->flg_export) { 2284 if (cur->flg_export) {
2261 if (flags & SETFLAG_UNEXPORT) { 2285 if (flags & SETFLAG_UNEXPORT) {
2262 cur->flg_export = 0; 2286 cur->flg_export = 0;
2263 /* unsetenv was already done */ 2287 /* unsetenv was already done */
2264 } else { 2288 } else {
2265 int i; 2289 int i;
2266 debug_printf_env("%s: putenv '%s'\n", __func__, cur->varstr); 2290 debug_printf_env("%s: putenv '%s'/%u\n", __func__, cur->varstr, cur->var_nest_level);
2267 i = putenv(cur->varstr); 2291 i = putenv(cur->varstr);
2268 /* only now we can free old exported malloced string */ 2292 /* only now we can free old exported malloced string */
2269 free(free_me); 2293 free(free_me);
@@ -2271,6 +2295,9 @@ static int set_local_var(char *str, unsigned flags)
2271 } 2295 }
2272 } 2296 }
2273 free(free_me); 2297 free(free_me);
2298
2299 handle_changed_special_names(cur->varstr, name_len - 1);
2300
2274 return 0; 2301 return 0;
2275} 2302}
2276 2303
@@ -2283,39 +2310,33 @@ static void set_pwd_var(unsigned flag)
2283static int unset_local_var_len(const char *name, int name_len) 2310static int unset_local_var_len(const char *name, int name_len)
2284{ 2311{
2285 struct variable *cur; 2312 struct variable *cur;
2286 struct variable **var_pp; 2313 struct variable **cur_pp;
2287
2288 if (!name)
2289 return EXIT_SUCCESS;
2290 2314
2291#if ENABLE_HUSH_GETOPTS 2315 cur_pp = &G.top_var;
2292 if (name_len == 6 && strncmp(name, "OPTIND", 6) == 0) 2316 while ((cur = *cur_pp) != NULL) {
2293 G.getopt_count = 0; 2317 if (strncmp(cur->varstr, name, name_len) == 0
2294#endif 2318 && cur->varstr[name_len] == '='
2295#if ENABLE_HUSH_LINENO_VAR 2319 ) {
2296 if (name_len == 6 && G.lineno_var && strncmp(name, "LINENO", 6) == 0)
2297 G.lineno_var = NULL;
2298#endif
2299
2300 var_pp = &G.top_var;
2301 while ((cur = *var_pp) != NULL) {
2302 if (strncmp(cur->varstr, name, name_len) == 0 && cur->varstr[name_len] == '=') {
2303 if (cur->flg_read_only) { 2320 if (cur->flg_read_only) {
2304 bb_error_msg("%s: readonly variable", name); 2321 bb_error_msg("%s: readonly variable", name);
2305 return EXIT_FAILURE; 2322 return EXIT_FAILURE;
2306 } 2323 }
2307 *var_pp = cur->next; 2324
2325 *cur_pp = cur->next;
2308 debug_printf_env("%s: unsetenv '%s'\n", __func__, cur->varstr); 2326 debug_printf_env("%s: unsetenv '%s'\n", __func__, cur->varstr);
2309 bb_unsetenv(cur->varstr); 2327 bb_unsetenv(cur->varstr);
2310 if (name_len == 3 && cur->varstr[0] == 'P' && cur->varstr[1] == 'S')
2311 cmdedit_update_prompt();
2312 if (!cur->max_len) 2328 if (!cur->max_len)
2313 free(cur->varstr); 2329 free(cur->varstr);
2314 free(cur); 2330 free(cur);
2315 return EXIT_SUCCESS; 2331
2332 break;
2316 } 2333 }
2317 var_pp = &cur->next; 2334 cur_pp = &cur->next;
2318 } 2335 }
2336
2337 /* Handle "unset PS1" et al even if did not find the variable to unset */
2338 handle_changed_special_names(name, name_len);
2339
2319 return EXIT_SUCCESS; 2340 return EXIT_SUCCESS;
2320} 2341}
2321 2342
@@ -2326,21 +2347,6 @@ static int unset_local_var(const char *name)
2326} 2347}
2327#endif 2348#endif
2328 2349
2329static void unset_vars(char **strings)
2330{
2331 char **v;
2332
2333 if (!strings)
2334 return;
2335 v = strings;
2336 while (*v) {
2337 const char *eq = strchrnul(*v, '=');
2338 unset_local_var_len(*v, (int)(eq - *v));
2339 v++;
2340 }
2341 free(strings);
2342}
2343
2344#if BASH_HOSTNAME_VAR || ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_READ || ENABLE_HUSH_GETOPTS 2350#if BASH_HOSTNAME_VAR || ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_READ || ENABLE_HUSH_GETOPTS
2345static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val) 2351static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val)
2346{ 2352{
@@ -2362,22 +2368,27 @@ static void add_vars(struct variable *var)
2362 var->next = G.top_var; 2368 var->next = G.top_var;
2363 G.top_var = var; 2369 G.top_var = var;
2364 if (var->flg_export) { 2370 if (var->flg_export) {
2365 debug_printf_env("%s: restoring exported '%s'\n", __func__, var->varstr); 2371 debug_printf_env("%s: restoring exported '%s'/%u\n", __func__, var->varstr, var->var_nest_level);
2366 putenv(var->varstr); 2372 putenv(var->varstr);
2367 } else { 2373 } else {
2368 debug_printf_env("%s: restoring variable '%s'\n", __func__, var->varstr); 2374 debug_printf_env("%s: restoring variable '%s'/%u\n", __func__, var->varstr, var->var_nest_level);
2369 } 2375 }
2370 var = next; 2376 var = next;
2371 } 2377 }
2372} 2378}
2373 2379
2374static struct variable *set_vars_and_save_old(char **strings) 2380/* We put strings[i] into variable table and possibly putenv them.
2381 * If variable is read only, we can free the strings[i]
2382 * which attempts to overwrite it.
2383 * The strings[] vector itself is freed.
2384 */
2385static void set_vars_and_save_old(char **strings)
2375{ 2386{
2376 char **s; 2387 char **s;
2377 struct variable *old = NULL;
2378 2388
2379 if (!strings) 2389 if (!strings)
2380 return old; 2390 return;
2391
2381 s = strings; 2392 s = strings;
2382 while (*s) { 2393 while (*s) {
2383 struct variable *var_p; 2394 struct variable *var_p;
@@ -2404,19 +2415,20 @@ static struct variable *set_vars_and_save_old(char **strings)
2404 do { *p = p[1]; p++; } while (*p); 2415 do { *p = p[1]; p++; } while (*p);
2405 goto next; 2416 goto next;
2406 } 2417 }
2407 /* Remove variable from global linked list */ 2418 /* below, set_local_var() with nest level will
2408 debug_printf_env("%s: removing '%s'\n", __func__, var_p->varstr); 2419 * "shadow" (remove) this variable from
2409 *var_pp = var_p->next; 2420 * global linked list.
2410 /* Add it to returned list */ 2421 */
2411 var_p->next = old;
2412 old = var_p;
2413 } 2422 }
2414 set_local_var(*s, SETFLAG_EXPORT); 2423 //bb_error_msg("G.var_nest_level:%d", G.var_nest_level);
2424 set_local_var(*s, (G.var_nest_level << SETFLAG_VARLVL_SHIFT) | SETFLAG_EXPORT);
2425 } else if (HUSH_DEBUG) {
2426 bb_error_msg_and_die("BUG in varexp4");
2415 } 2427 }
2416 next:
2417 s++; 2428 s++;
2429 next: ;
2418 } 2430 }
2419 return old; 2431 free(strings);
2420} 2432}
2421 2433
2422 2434
@@ -2452,36 +2464,36 @@ static void reinit_unicode_for_hush(void)
2452 * \ 2464 * \
2453 * It exercises a lot of corner cases. 2465 * It exercises a lot of corner cases.
2454 */ 2466 */
2467# if ENABLE_FEATURE_EDITING_FANCY_PROMPT
2455static void cmdedit_update_prompt(void) 2468static void cmdedit_update_prompt(void)
2456{ 2469{
2457 if (ENABLE_FEATURE_EDITING_FANCY_PROMPT) { 2470 G.PS1 = get_local_var_value("PS1");
2458 G.PS1 = get_local_var_value("PS1"); 2471 if (G.PS1 == NULL)
2459 if (G.PS1 == NULL) 2472 G.PS1 = "";
2460 G.PS1 = "\\w \\$ "; 2473 G.PS2 = get_local_var_value("PS2");
2461 G.PS2 = get_local_var_value("PS2");
2462 } else {
2463 G.PS1 = NULL;
2464 }
2465 if (G.PS2 == NULL) 2474 if (G.PS2 == NULL)
2466 G.PS2 = "> "; 2475 G.PS2 = "";
2467} 2476}
2468static const char *setup_prompt_string(int promptmode) 2477# endif
2478static const char *setup_prompt_string(void)
2469{ 2479{
2470 const char *prompt_str; 2480 const char *prompt_str;
2471 debug_printf("setup_prompt_string %d ", promptmode); 2481
2472 if (!ENABLE_FEATURE_EDITING_FANCY_PROMPT) { 2482 debug_printf_prompt("%s promptmode:%d\n", __func__, G.promptmode);
2473 /* Set up the prompt */ 2483
2474 if (promptmode == 0) { /* PS1 */ 2484 IF_FEATURE_EDITING_FANCY_PROMPT( prompt_str = G.PS2;)
2485 IF_NOT_FEATURE_EDITING_FANCY_PROMPT(prompt_str = "> ";)
2486 if (G.promptmode == 0) { /* PS1 */
2487 if (!ENABLE_FEATURE_EDITING_FANCY_PROMPT) {
2488 /* No fancy prompts supported, (re)generate "CURDIR $ " by hand */
2475 free((char*)G.PS1); 2489 free((char*)G.PS1);
2476 /* bash uses $PWD value, even if it is set by user. 2490 /* bash uses $PWD value, even if it is set by user.
2477 * It uses current dir only if PWD is unset. 2491 * It uses current dir only if PWD is unset.
2478 * We always use current dir. */ 2492 * We always use current dir. */
2479 G.PS1 = xasprintf("%s %c ", get_cwd(0), (geteuid() != 0) ? '$' : '#'); 2493 G.PS1 = xasprintf("%s %c ", get_cwd(0), (geteuid() != 0) ? '$' : '#');
2480 prompt_str = G.PS1; 2494 }
2481 } else 2495 prompt_str = G.PS1;
2482 prompt_str = G.PS2; 2496 }
2483 } else
2484 prompt_str = (promptmode == 0) ? G.PS1 : G.PS2;
2485 debug_printf("prompt_str '%s'\n", prompt_str); 2497 debug_printf("prompt_str '%s'\n", prompt_str);
2486 return prompt_str; 2498 return prompt_str;
2487} 2499}
@@ -2490,7 +2502,7 @@ static int get_user_input(struct in_str *i)
2490 int r; 2502 int r;
2491 const char *prompt_str; 2503 const char *prompt_str;
2492 2504
2493 prompt_str = setup_prompt_string(i->promptmode); 2505 prompt_str = setup_prompt_string();
2494# if ENABLE_FEATURE_EDITING 2506# if ENABLE_FEATURE_EDITING
2495 for (;;) { 2507 for (;;) {
2496 reinit_unicode_for_hush(); 2508 reinit_unicode_for_hush();
@@ -2560,7 +2572,8 @@ static int fgetc_interactive(struct in_str *i)
2560 if (G_interactive_fd && i->file == stdin) { 2572 if (G_interactive_fd && i->file == stdin) {
2561 /* Returns first char (or EOF), the rest is in i->p[] */ 2573 /* Returns first char (or EOF), the rest is in i->p[] */
2562 ch = get_user_input(i); 2574 ch = get_user_input(i);
2563 i->promptmode = 1; /* PS2 */ 2575 G.promptmode = 1; /* PS2 */
2576 debug_printf_prompt("%s promptmode=%d\n", __func__, G.promptmode);
2564 } else { 2577 } else {
2565 /* Not stdin: script file, sourced file, etc */ 2578 /* Not stdin: script file, sourced file, etc */
2566 do ch = fgetc(i->file); while (ch == '\0'); 2579 do ch = fgetc(i->file); while (ch == '\0');
@@ -2733,7 +2746,6 @@ static int i_peek_and_eat_bkslash_nl(struct in_str *input)
2733static void setup_file_in_str(struct in_str *i, FILE *f) 2746static void setup_file_in_str(struct in_str *i, FILE *f)
2734{ 2747{
2735 memset(i, 0, sizeof(*i)); 2748 memset(i, 0, sizeof(*i));
2736 /* i->promptmode = 0; - PS1 (memset did it) */
2737 i->file = f; 2749 i->file = f;
2738 /* i->p = NULL; */ 2750 /* i->p = NULL; */
2739} 2751}
@@ -2741,7 +2753,6 @@ static void setup_file_in_str(struct in_str *i, FILE *f)
2741static void setup_string_in_str(struct in_str *i, const char *s) 2753static void setup_string_in_str(struct in_str *i, const char *s)
2742{ 2754{
2743 memset(i, 0, sizeof(*i)); 2755 memset(i, 0, sizeof(*i));
2744 /* i->promptmode = 0; - PS1 (memset did it) */
2745 /*i->file = NULL */; 2756 /*i->file = NULL */;
2746 i->p = s; 2757 i->p = s;
2747} 2758}
@@ -3933,14 +3944,37 @@ static int done_word(o_string *word, struct parse_context *ctx)
3933 (ctx->ctx_res_w == RES_SNTX)); 3944 (ctx->ctx_res_w == RES_SNTX));
3934 return (ctx->ctx_res_w == RES_SNTX); 3945 return (ctx->ctx_res_w == RES_SNTX);
3935 } 3946 }
3936# if BASH_TEST2 3947# if defined(CMD_SINGLEWORD_NOGLOB)
3937 if (strcmp(word->data, "[[") == 0) { 3948 if (0
3949# if BASH_TEST2
3950 || strcmp(word->data, "[[") == 0
3951# endif
3952 /* In bash, local/export/readonly are special, args
3953 * are assignments and therefore expansion of them
3954 * should be "one-word" expansion:
3955 * $ export i=`echo 'a b'` # one arg: "i=a b"
3956 * compare with:
3957 * $ ls i=`echo 'a b'` # two args: "i=a" and "b"
3958 * ls: cannot access i=a: No such file or directory
3959 * ls: cannot access b: No such file or directory
3960 * Note: bash 3.2.33(1) does this only if export word
3961 * itself is not quoted:
3962 * $ export i=`echo 'aaa bbb'`; echo "$i"
3963 * aaa bbb
3964 * $ "export" i=`echo 'aaa bbb'`; echo "$i"
3965 * aaa
3966 */
3967 IF_HUSH_LOCAL( || strcmp(word->data, "local") == 0)
3968 IF_HUSH_EXPORT( || strcmp(word->data, "export") == 0)
3969 IF_HUSH_READONLY( || strcmp(word->data, "readonly") == 0)
3970 ) {
3938 command->cmd_type = CMD_SINGLEWORD_NOGLOB; 3971 command->cmd_type = CMD_SINGLEWORD_NOGLOB;
3939 } 3972 }
3940 /* fall through */ 3973 /* fall through */
3941# endif 3974# endif
3942 } 3975 }
3943#endif 3976#endif /* HAS_KEYWORDS */
3977
3944 if (command->group) { 3978 if (command->group) {
3945 /* "{ echo foo; } echo bar" - bad */ 3979 /* "{ echo foo; } echo bar" - bad */
3946 syntax_error_at(word->data); 3980 syntax_error_at(word->data);
@@ -4240,7 +4274,6 @@ static int fetch_heredocs(int heredoc_cnt, struct parse_context *ctx, struct in_
4240 4274
4241 redir->rd_type = REDIRECT_HEREDOC2; 4275 redir->rd_type = REDIRECT_HEREDOC2;
4242 /* redir->rd_dup is (ab)used to indicate <<- */ 4276 /* redir->rd_dup is (ab)used to indicate <<- */
4243bb_error_msg("redir->rd_filename:'%s'", redir->rd_filename);
4244 p = fetch_till_str(&ctx->as_string, input, 4277 p = fetch_till_str(&ctx->as_string, input,
4245 redir->rd_filename, redir->rd_dup); 4278 redir->rd_filename, redir->rd_dup);
4246 if (!p) { 4279 if (!p) {
@@ -4286,6 +4319,11 @@ static int parse_group(o_string *dest, struct parse_context *ctx,
4286 /* dest contains characters seen prior to ( or {. 4319 /* dest contains characters seen prior to ( or {.
4287 * Typically it's empty, but for function defs, 4320 * Typically it's empty, but for function defs,
4288 * it contains function name (without '()'). */ 4321 * it contains function name (without '()'). */
4322#if BB_MMU
4323# define as_string NULL
4324#else
4325 char *as_string = NULL;
4326#endif
4289 struct pipe *pipe_list; 4327 struct pipe *pipe_list;
4290 int endch; 4328 int endch;
4291 struct command *command = ctx->command; 4329 struct command *command = ctx->command;
@@ -4314,7 +4352,7 @@ static int parse_group(o_string *dest, struct parse_context *ctx,
4314 do 4352 do
4315 ch = i_getch(input); 4353 ch = i_getch(input);
4316 while (ch == ' ' || ch == '\t' || ch == '\n'); 4354 while (ch == ' ' || ch == '\t' || ch == '\n');
4317 if (ch != '{') { 4355 if (ch != '{' && ch != '(') {
4318 syntax_error_unexpected_ch(ch); 4356 syntax_error_unexpected_ch(ch);
4319 return 1; 4357 return 1;
4320 } 4358 }
@@ -4336,13 +4374,13 @@ static int parse_group(o_string *dest, struct parse_context *ctx,
4336 } 4374 }
4337#endif 4375#endif
4338 4376
4339#if ENABLE_HUSH_FUNCTIONS 4377 IF_HUSH_FUNCTIONS(skip:)
4340 skip: 4378
4341#endif
4342 endch = '}'; 4379 endch = '}';
4343 if (ch == '(') { 4380 if (ch == '(') {
4344 endch = ')'; 4381 endch = ')';
4345 command->cmd_type = CMD_SUBSHELL; 4382 IF_HUSH_FUNCTIONS(if (command->cmd_type != CMD_FUNCDEF))
4383 command->cmd_type = CMD_SUBSHELL;
4346 } else { 4384 } else {
4347 /* bash does not allow "{echo...", requires whitespace */ 4385 /* bash does not allow "{echo...", requires whitespace */
4348 ch = i_peek(input); 4386 ch = i_peek(input);
@@ -4358,38 +4396,54 @@ static int parse_group(o_string *dest, struct parse_context *ctx,
4358 } 4396 }
4359 } 4397 }
4360 4398
4361 { 4399 pipe_list = parse_stream(&as_string, input, endch);
4362#if BB_MMU
4363# define as_string NULL
4364#else
4365 char *as_string = NULL;
4366#endif
4367 pipe_list = parse_stream(&as_string, input, endch);
4368#if !BB_MMU 4400#if !BB_MMU
4369 if (as_string) 4401 if (as_string)
4370 o_addstr(&ctx->as_string, as_string); 4402 o_addstr(&ctx->as_string, as_string);
4371#endif 4403#endif
4372 /* empty ()/{} or parse error? */ 4404
4373 if (!pipe_list || pipe_list == ERR_PTR) { 4405 /* empty ()/{} or parse error? */
4374 /* parse_stream already emitted error msg */ 4406 if (!pipe_list || pipe_list == ERR_PTR) {
4375 if (!BB_MMU) 4407 /* parse_stream already emitted error msg */
4376 free(as_string); 4408 if (!BB_MMU)
4377 debug_printf_parse("parse_group return 1: " 4409 free(as_string);
4378 "parse_stream returned %p\n", pipe_list); 4410 debug_printf_parse("parse_group return 1: "
4379 return 1; 4411 "parse_stream returned %p\n", pipe_list);
4380 } 4412 return 1;
4381 command->group = pipe_list; 4413 }
4382#if !BB_MMU 4414#if !BB_MMU
4383 as_string[strlen(as_string) - 1] = '\0'; /* plink ')' or '}' */ 4415 as_string[strlen(as_string) - 1] = '\0'; /* plink ')' or '}' */
4384 command->group_as_string = as_string; 4416 command->group_as_string = as_string;
4385 debug_printf_parse("end of group, remembering as:'%s'\n", 4417 debug_printf_parse("end of group, remembering as:'%s'\n",
4386 command->group_as_string); 4418 command->group_as_string);
4387#endif 4419#endif
4388#undef as_string 4420
4421#if ENABLE_HUSH_FUNCTIONS
4422 /* Convert "f() (cmds)" to "f() {(cmds)}" */
4423 if (command->cmd_type == CMD_FUNCDEF && endch == ')') {
4424 struct command *cmd2;
4425
4426 cmd2 = xzalloc(sizeof(*cmd2));
4427 cmd2->cmd_type = CMD_SUBSHELL;
4428 cmd2->group = pipe_list;
4429# if !BB_MMU
4430//UNTESTED!
4431 cmd2->group_as_string = command->group_as_string;
4432 command->group_as_string = xasprintf("(%s)", command->group_as_string);
4433# endif
4434
4435 pipe_list = new_pipe();
4436 pipe_list->cmds = cmd2;
4437 pipe_list->num_cmds = 1;
4389 } 4438 }
4439#endif
4440
4441 command->group = pipe_list;
4442
4390 debug_printf_parse("parse_group return 0\n"); 4443 debug_printf_parse("parse_group return 0\n");
4391 return 0; 4444 return 0;
4392 /* command remains "open", available for possible redirects */ 4445 /* command remains "open", available for possible redirects */
4446#undef as_string
4393} 4447}
4394 4448
4395#if ENABLE_HUSH_TICK || ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_DOLLAR_OPS 4449#if ENABLE_HUSH_TICK || ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_DOLLAR_OPS
@@ -4499,6 +4553,9 @@ static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsign
4499# endif 4553# endif
4500 end_ch &= (DOUBLE_CLOSE_CHAR_FLAG - 1); 4554 end_ch &= (DOUBLE_CLOSE_CHAR_FLAG - 1);
4501 4555
4556 G.promptmode = 1; /* PS2 */
4557 debug_printf_prompt("%s promptmode=%d\n", __func__, G.promptmode);
4558
4502 while (1) { 4559 while (1) {
4503 ch = i_getch(input); 4560 ch = i_getch(input);
4504 if (ch == EOF) { 4561 if (ch == EOF) {
@@ -4564,6 +4621,7 @@ static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsign
4564 continue; 4621 continue;
4565 } 4622 }
4566 } 4623 }
4624 debug_printf_parse("%s return '%s' ch:'%c'\n", __func__, dest->data, ch);
4567 return ch; 4625 return ch;
4568} 4626}
4569#endif /* ENABLE_HUSH_TICK || ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_DOLLAR_OPS */ 4627#endif /* ENABLE_HUSH_TICK || ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_DOLLAR_OPS */
@@ -5477,7 +5535,7 @@ static struct pipe *parse_stream(char **pstring,
5477 goto parse_error2; 5535 goto parse_error2;
5478 default: 5536 default:
5479 if (HUSH_DEBUG) 5537 if (HUSH_DEBUG)
5480 bb_error_msg_and_die("BUG: unexpected %c\n", ch); 5538 bb_error_msg_and_die("BUG: unexpected %c", ch);
5481 } 5539 }
5482 } /* while (1) */ 5540 } /* while (1) */
5483 5541
@@ -5916,11 +5974,11 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
5916 /* pattern uses non-standard expansion. 5974 /* pattern uses non-standard expansion.
5917 * repl should be unbackslashed and globbed 5975 * repl should be unbackslashed and globbed
5918 * by the usual expansion rules: 5976 * by the usual expansion rules:
5919 * >az; >bz; 5977 * >az >bz
5920 * v='a bz'; echo "${v/a*z/a*z}" prints "a*z" 5978 * v='a bz'; echo "${v/a*z/a*z}" #prints "a*z"
5921 * v='a bz'; echo "${v/a*z/\z}" prints "\z" 5979 * v='a bz'; echo "${v/a*z/\z}" #prints "z"
5922 * v='a bz'; echo ${v/a*z/a*z} prints "az" 5980 * v='a bz'; echo ${v/a*z/a*z} #prints "az"
5923 * v='a bz'; echo ${v/a*z/\z} prints "z" 5981 * v='a bz'; echo ${v/a*z/\z} #prints "z"
5924 * (note that a*z _pattern_ is never globbed!) 5982 * (note that a*z _pattern_ is never globbed!)
5925 */ 5983 */
5926 char *pattern, *repl, *t; 5984 char *pattern, *repl, *t;
@@ -5932,7 +5990,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
5932 exp_word = p; 5990 exp_word = p;
5933 p = strchr(p, SPECIAL_VAR_SYMBOL); 5991 p = strchr(p, SPECIAL_VAR_SYMBOL);
5934 *p = '\0'; 5992 *p = '\0';
5935 repl = encode_then_expand_string(exp_word, /*process_bkslash:*/ arg0 & 0x80, /*unbackslash:*/ 1); 5993 repl = encode_then_expand_string(exp_word, /*process_bkslash:*/ 0, /*unbackslash:*/ 1);
5936 debug_printf_varexp("repl:'%s'->'%s'\n", exp_word, repl); 5994 debug_printf_varexp("repl:'%s'->'%s'\n", exp_word, repl);
5937 /* HACK ALERT. We depend here on the fact that 5995 /* HACK ALERT. We depend here on the fact that
5938 * G.global_argv and results of utoa and get_local_var_value 5996 * G.global_argv and results of utoa and get_local_var_value
@@ -6199,6 +6257,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg)
6199 * and $IFS-split */ 6257 * and $IFS-split */
6200 debug_printf_subst("SUBST '%s' first_ch %x\n", arg, first_ch); 6258 debug_printf_subst("SUBST '%s' first_ch %x\n", arg, first_ch);
6201 G.last_exitcode = process_command_subs(&subst_result, arg); 6259 G.last_exitcode = process_command_subs(&subst_result, arg);
6260 G.expand_exitcode = G.last_exitcode;
6202 debug_printf_subst("SUBST RES:%d '%s'\n", G.last_exitcode, subst_result.data); 6261 debug_printf_subst("SUBST RES:%d '%s'\n", G.last_exitcode, subst_result.data);
6203 val = subst_result.data; 6262 val = subst_result.data;
6204 goto store_val; 6263 goto store_val;
@@ -6300,7 +6359,7 @@ static char **expand_strvec_to_strvec(char **argv)
6300 return expand_variables(argv, EXP_FLAG_GLOB | EXP_FLAG_ESC_GLOB_CHARS); 6359 return expand_variables(argv, EXP_FLAG_GLOB | EXP_FLAG_ESC_GLOB_CHARS);
6301} 6360}
6302 6361
6303#if BASH_TEST2 6362#if defined(CMD_SINGLEWORD_NOGLOB)
6304static char **expand_strvec_to_strvec_singleword_noglob(char **argv) 6363static char **expand_strvec_to_strvec_singleword_noglob(char **argv)
6305{ 6364{
6306 return expand_variables(argv, EXP_FLAG_SINGLEWORD); 6365 return expand_variables(argv, EXP_FLAG_SINGLEWORD);
@@ -6308,7 +6367,7 @@ static char **expand_strvec_to_strvec_singleword_noglob(char **argv)
6308#endif 6367#endif
6309 6368
6310/* Used for expansion of right hand of assignments, 6369/* Used for expansion of right hand of assignments,
6311 * $((...)), heredocs, variable espansion parts. 6370 * $((...)), heredocs, variable expansion parts.
6312 * 6371 *
6313 * NB: should NOT do globbing! 6372 * NB: should NOT do globbing!
6314 * "export v=/bin/c*; env | grep ^v=" outputs "v=/bin/c*" 6373 * "export v=/bin/c*; env | grep ^v=" outputs "v=/bin/c*"
@@ -6348,7 +6407,7 @@ static char *expand_string_to_string(const char *str, int do_unbackslash)
6348 return (char*)list; 6407 return (char*)list;
6349} 6408}
6350 6409
6351#if ENABLE_HUSH_CASE 6410#if 0
6352static char* expand_strvec_to_string(char **argv) 6411static char* expand_strvec_to_string(char **argv)
6353{ 6412{
6354 char **list; 6413 char **list;
@@ -6619,8 +6678,10 @@ static void parse_and_run_stream(struct in_str *inp, int end_trigger)
6619 struct pipe *pipe_list; 6678 struct pipe *pipe_list;
6620 6679
6621#if ENABLE_HUSH_INTERACTIVE 6680#if ENABLE_HUSH_INTERACTIVE
6622 if (end_trigger == ';') 6681 if (end_trigger == ';') {
6623 inp->promptmode = 0; /* PS1 */ 6682 G.promptmode = 0; /* PS1 */
6683 debug_printf_prompt("%s promptmode=%d\n", __func__, G.promptmode);
6684 }
6624#endif 6685#endif
6625 pipe_list = parse_stream(NULL, inp, end_trigger); 6686 pipe_list = parse_stream(NULL, inp, end_trigger);
6626 if (!pipe_list || pipe_list == ERR_PTR) { /* EOF/error */ 6687 if (!pipe_list || pipe_list == ERR_PTR) { /* EOF/error */
@@ -7329,8 +7390,9 @@ static void exec_function(char ***to_free,
7329// for "more correctness" we might want to close those extra fds here: 7390// for "more correctness" we might want to close those extra fds here:
7330//? close_saved_fds_and_FILE_fds(); 7391//? close_saved_fds_and_FILE_fds();
7331 7392
7332 /* "we are in function, ok to use return" */ 7393 /* "we are in a function, ok to use return" */
7333 G_flag_return_in_progress = -1; 7394 G_flag_return_in_progress = -1;
7395 G.var_nest_level++;
7334 IF_HUSH_LOCAL(G.func_nest_level++;) 7396 IF_HUSH_LOCAL(G.func_nest_level++;)
7335 7397
7336 /* On MMU, funcp->body is always non-NULL */ 7398 /* On MMU, funcp->body is always non-NULL */
@@ -7350,6 +7412,53 @@ static void exec_function(char ***to_free,
7350# endif 7412# endif
7351} 7413}
7352 7414
7415static void enter_var_nest_level(void)
7416{
7417 G.var_nest_level++;
7418 debug_printf_env("var_nest_level++ %u\n", G.var_nest_level);
7419
7420 /* Try: f() { echo -n .; f; }; f
7421 * struct variable::var_nest_level is uint16_t,
7422 * thus limiting recursion to < 2^16.
7423 * In any case, with 8 Mbyte stack SEGV happens
7424 * not too long after 2^16 recursions anyway.
7425 */
7426 if (G.var_nest_level > 0xff00)
7427 bb_error_msg_and_die("fatal recursion (depth %u)", G.var_nest_level);
7428}
7429
7430static void leave_var_nest_level(void)
7431{
7432 struct variable *cur;
7433 struct variable **cur_pp;
7434
7435 cur_pp = &G.top_var;
7436 while ((cur = *cur_pp) != NULL) {
7437 if (cur->var_nest_level < G.var_nest_level) {
7438 cur_pp = &cur->next;
7439 continue;
7440 }
7441 /* Unexport */
7442 if (cur->flg_export) {
7443 debug_printf_env("unexporting nested '%s'/%u\n", cur->varstr, cur->var_nest_level);
7444 bb_unsetenv(cur->varstr);
7445 }
7446 /* Remove from global list */
7447 *cur_pp = cur->next;
7448 /* Free */
7449 if (!cur->max_len) {
7450 debug_printf_env("freeing nested '%s'/%u\n", cur->varstr, cur->var_nest_level);
7451 free(cur->varstr);
7452 }
7453 free(cur);
7454 }
7455
7456 G.var_nest_level--;
7457 debug_printf_env("var_nest_level-- %u\n", G.var_nest_level);
7458 if (HUSH_DEBUG && (int)G.var_nest_level < 0)
7459 bb_error_msg_and_die("BUG: nesting underflow");
7460}
7461
7353static int run_function(const struct function *funcp, char **argv) 7462static int run_function(const struct function *funcp, char **argv)
7354{ 7463{
7355 int rc; 7464 int rc;
@@ -7358,9 +7467,12 @@ static int run_function(const struct function *funcp, char **argv)
7358 7467
7359 save_and_replace_G_args(&sv, argv); 7468 save_and_replace_G_args(&sv, argv);
7360 7469
7361 /* "we are in function, ok to use return" */ 7470 /* "We are in function, ok to use return" */
7362 sv_flg = G_flag_return_in_progress; 7471 sv_flg = G_flag_return_in_progress;
7363 G_flag_return_in_progress = -1; 7472 G_flag_return_in_progress = -1;
7473
7474 /* Make "local" variables properly shadow previous ones */
7475 IF_HUSH_LOCAL(enter_var_nest_level();)
7364 IF_HUSH_LOCAL(G.func_nest_level++;) 7476 IF_HUSH_LOCAL(G.func_nest_level++;)
7365 7477
7366 /* On MMU, funcp->body is always non-NULL */ 7478 /* On MMU, funcp->body is always non-NULL */
@@ -7375,30 +7487,9 @@ static int run_function(const struct function *funcp, char **argv)
7375 rc = run_list(funcp->body); 7487 rc = run_list(funcp->body);
7376 } 7488 }
7377 7489
7378# if ENABLE_HUSH_LOCAL 7490 IF_HUSH_LOCAL(G.func_nest_level--;)
7379 { 7491 IF_HUSH_LOCAL(leave_var_nest_level();)
7380 struct variable *var;
7381 struct variable **var_pp;
7382 7492
7383 var_pp = &G.top_var;
7384 while ((var = *var_pp) != NULL) {
7385 if (var->func_nest_level < G.func_nest_level) {
7386 var_pp = &var->next;
7387 continue;
7388 }
7389 /* Unexport */
7390 if (var->flg_export)
7391 bb_unsetenv(var->varstr);
7392 /* Remove from global list */
7393 *var_pp = var->next;
7394 /* Free */
7395 if (!var->max_len)
7396 free(var->varstr);
7397 free(var);
7398 }
7399 G.func_nest_level--;
7400 }
7401# endif
7402 G_flag_return_in_progress = sv_flg; 7493 G_flag_return_in_progress = sv_flg;
7403 7494
7404 restore_G_args(&sv, argv); 7495 restore_G_args(&sv, argv);
@@ -7535,10 +7626,10 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
7535 char **argv_expanded) 7626 char **argv_expanded)
7536{ 7627{
7537 const struct built_in_command *x; 7628 const struct built_in_command *x;
7629 struct variable **sv_shadowed;
7538 char **new_env; 7630 char **new_env;
7539#if ENABLE_HUSH_COMMAND 7631 IF_HUSH_COMMAND(char opt_vV = 0;)
7540 char opt_vV = 0; 7632 IF_HUSH_FUNCTIONS(const struct function *funcp;)
7541#endif
7542 7633
7543 new_env = expand_assignments(argv, assignment_cnt); 7634 new_env = expand_assignments(argv, assignment_cnt);
7544 dump_cmd_in_x_mode(new_env); 7635 dump_cmd_in_x_mode(new_env);
@@ -7552,15 +7643,14 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
7552 _exit(EXIT_SUCCESS); 7643 _exit(EXIT_SUCCESS);
7553 } 7644 }
7554 7645
7646 sv_shadowed = G.shadowed_vars_pp;
7555#if BB_MMU 7647#if BB_MMU
7556 set_vars_and_save_old(new_env); 7648 G.shadowed_vars_pp = NULL; /* "don't save, free them instead" */
7557 free(new_env); /* optional */
7558 /* we can also destroy set_vars_and_save_old's return value,
7559 * to save memory */
7560#else 7649#else
7561 nommu_save->new_env = new_env; 7650 G.shadowed_vars_pp = &nommu_save->old_vars;
7562 nommu_save->old_vars = set_vars_and_save_old(new_env);
7563#endif 7651#endif
7652 set_vars_and_save_old(new_env);
7653 G.shadowed_vars_pp = sv_shadowed;
7564 7654
7565 if (argv_expanded) { 7655 if (argv_expanded) {
7566 argv = argv_expanded; 7656 argv = argv_expanded;
@@ -7579,12 +7669,9 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
7579 7669
7580#if ENABLE_HUSH_FUNCTIONS 7670#if ENABLE_HUSH_FUNCTIONS
7581 /* Check if the command matches any functions (this goes before bltins) */ 7671 /* Check if the command matches any functions (this goes before bltins) */
7582 { 7672 funcp = find_function(argv[0]);
7583 const struct function *funcp = find_function(argv[0]); 7673 if (funcp)
7584 if (funcp) { 7674 exec_function(&nommu_save->argv_from_re_execing, funcp, argv);
7585 exec_function(&nommu_save->argv_from_re_execing, funcp, argv);
7586 }
7587 }
7588#endif 7675#endif
7589 7676
7590#if ENABLE_HUSH_COMMAND 7677#if ENABLE_HUSH_COMMAND
@@ -7693,6 +7780,15 @@ static void pseudo_exec(nommu_save_t *nommu_save,
7693 struct command *command, 7780 struct command *command,
7694 char **argv_expanded) 7781 char **argv_expanded)
7695{ 7782{
7783#if ENABLE_HUSH_FUNCTIONS
7784 if (command->cmd_type == CMD_FUNCDEF) {
7785 /* Ignore funcdefs in pipes:
7786 * true | f() { cmd }
7787 */
7788 _exit(0);
7789 }
7790#endif
7791
7696 if (command->argv) { 7792 if (command->argv) {
7697 pseudo_exec_argv(nommu_save, command->argv, 7793 pseudo_exec_argv(nommu_save, command->argv,
7698 command->assignment_cnt, argv_expanded); 7794 command->assignment_cnt, argv_expanded);
@@ -8111,29 +8207,29 @@ static int checkjobs_and_fg_shell(struct pipe *fg_pipe)
8111 * subshell: ( list ) [&] 8207 * subshell: ( list ) [&]
8112 */ 8208 */
8113#if !ENABLE_HUSH_MODE_X 8209#if !ENABLE_HUSH_MODE_X
8114#define redirect_and_varexp_helper(new_env_p, old_vars_p, command, squirrel, argv_expanded) \ 8210#define redirect_and_varexp_helper(old_vars_p, command, squirrel, argv_expanded) \
8115 redirect_and_varexp_helper(new_env_p, old_vars_p, command, squirrel) 8211 redirect_and_varexp_helper(old_vars_p, command, squirrel)
8116#endif 8212#endif
8117static int redirect_and_varexp_helper(char ***new_env_p, 8213static int redirect_and_varexp_helper(
8118 struct variable **old_vars_p,
8119 struct command *command, 8214 struct command *command,
8120 struct squirrel **sqp, 8215 struct squirrel **sqp,
8121 char **argv_expanded) 8216 char **argv_expanded)
8122{ 8217{
8218 /* Assignments occur before redirects. Try:
8219 * a=`sleep 1` sleep 2 3>/qwe/rty
8220 */
8221
8222 char **new_env = expand_assignments(command->argv, command->assignment_cnt);
8223 dump_cmd_in_x_mode(new_env);
8224 dump_cmd_in_x_mode(argv_expanded);
8225 /* this takes ownership of new_env[i] elements, and frees new_env: */
8226 set_vars_and_save_old(new_env);
8227
8123 /* setup_redirects acts on file descriptors, not FILEs. 8228 /* setup_redirects acts on file descriptors, not FILEs.
8124 * This is perfect for work that comes after exec(). 8229 * This is perfect for work that comes after exec().
8125 * Is it really safe for inline use? Experimentally, 8230 * Is it really safe for inline use? Experimentally,
8126 * things seem to work. */ 8231 * things seem to work. */
8127 int rcode = setup_redirects(command, sqp); 8232 return setup_redirects(command, sqp);
8128 if (rcode == 0) {
8129 char **new_env = expand_assignments(command->argv, command->assignment_cnt);
8130 *new_env_p = new_env;
8131 dump_cmd_in_x_mode(new_env);
8132 dump_cmd_in_x_mode(argv_expanded);
8133 if (old_vars_p)
8134 *old_vars_p = set_vars_and_save_old(new_env);
8135 }
8136 return rcode;
8137} 8233}
8138static NOINLINE int run_pipe(struct pipe *pi) 8234static NOINLINE int run_pipe(struct pipe *pi)
8139{ 8235{
@@ -8221,13 +8317,10 @@ static NOINLINE int run_pipe(struct pipe *pi)
8221 argv = command->argv ? command->argv : (char **) &null_ptr; 8317 argv = command->argv ? command->argv : (char **) &null_ptr;
8222 { 8318 {
8223 const struct built_in_command *x; 8319 const struct built_in_command *x;
8224#if ENABLE_HUSH_FUNCTIONS 8320 IF_HUSH_FUNCTIONS(const struct function *funcp;)
8225 const struct function *funcp; 8321 IF_NOT_HUSH_FUNCTIONS(enum { funcp = 0 };)
8226#else 8322 struct variable **sv_shadowed;
8227 enum { funcp = 0 }; 8323 struct variable *old_vars;
8228#endif
8229 char **new_env = NULL;
8230 struct variable *old_vars = NULL;
8231 8324
8232#if ENABLE_HUSH_LINENO_VAR 8325#if ENABLE_HUSH_LINENO_VAR
8233 if (G.lineno_var) 8326 if (G.lineno_var)
@@ -8235,99 +8328,109 @@ static NOINLINE int run_pipe(struct pipe *pi)
8235#endif 8328#endif
8236 8329
8237 if (argv[command->assignment_cnt] == NULL) { 8330 if (argv[command->assignment_cnt] == NULL) {
8238 /* Assignments, but no command */ 8331 /* Assignments, but no command.
8239 /* Ensure redirects take effect (that is, create files). 8332 * Ensure redirects take effect (that is, create files).
8240 * Try "a=t >file" */ 8333 * Try "a=t >file"
8241#if 0 /* A few cases in testsuite fail with this code. FIXME */ 8334 */
8242 rcode = redirect_and_varexp_helper(&new_env, /*old_vars:*/ NULL, command, &squirrel, /*argv_expanded:*/ NULL); 8335 unsigned i;
8243 /* Set shell variables */ 8336 G.expand_exitcode = 0;
8244 if (new_env) { 8337 only_assignments:
8245 argv = new_env;
8246 while (*argv) {
8247 if (set_local_var(*argv, /*flag:*/ 0)) {
8248 /* assignment to readonly var / putenv error? */
8249 rcode = 1;
8250 }
8251 argv++;
8252 }
8253 }
8254 /* Redirect error sets $? to 1. Otherwise,
8255 * if evaluating assignment value set $?, retain it.
8256 * Try "false; q=`exit 2`; echo $?" - should print 2: */
8257 if (rcode == 0)
8258 rcode = G.last_exitcode;
8259 /* Exit, _skipping_ variable restoring code: */
8260 goto clean_up_and_ret0;
8261
8262#else /* Older, bigger, but more correct code */
8263
8264 rcode = setup_redirects(command, &squirrel); 8338 rcode = setup_redirects(command, &squirrel);
8265 restore_redirects(squirrel); 8339 restore_redirects(squirrel);
8340
8266 /* Set shell variables */ 8341 /* Set shell variables */
8267 if (G_x_mode) 8342 if (G_x_mode)
8268 bb_putchar_stderr('+'); 8343 bb_putchar_stderr('+');
8269 while (*argv) { 8344 i = 0;
8270 char *p = expand_string_to_string(*argv, /*unbackslash:*/ 1); 8345 while (i < command->assignment_cnt) {
8346 char *p = expand_string_to_string(argv[i], /*unbackslash:*/ 1);
8271 if (G_x_mode) 8347 if (G_x_mode)
8272 fprintf(stderr, " %s", p); 8348 fprintf(stderr, " %s", p);
8273 debug_printf_exec("set shell var:'%s'->'%s'\n", 8349 debug_printf_env("set shell var:'%s'->'%s'\n", *argv, p);
8274 *argv, p);
8275 if (set_local_var(p, /*flag:*/ 0)) { 8350 if (set_local_var(p, /*flag:*/ 0)) {
8276 /* assignment to readonly var / putenv error? */ 8351 /* assignment to readonly var / putenv error? */
8277 rcode = 1; 8352 rcode = 1;
8278 } 8353 }
8279 argv++; 8354 i++;
8280 } 8355 }
8281 if (G_x_mode) 8356 if (G_x_mode)
8282 bb_putchar_stderr('\n'); 8357 bb_putchar_stderr('\n');
8283 /* Redirect error sets $? to 1. Otherwise, 8358 /* Redirect error sets $? to 1. Otherwise,
8284 * if evaluating assignment value set $?, retain it. 8359 * if evaluating assignment value set $?, retain it.
8285 * Try "false; q=`exit 2`; echo $?" - should print 2: */ 8360 * Else, clear $?:
8361 * false; q=`exit 2`; echo $? - should print 2
8362 * false; x=1; echo $? - should print 0
8363 * Because of the 2nd case, we can't just use G.last_exitcode.
8364 */
8286 if (rcode == 0) 8365 if (rcode == 0)
8287 rcode = G.last_exitcode; 8366 rcode = G.expand_exitcode;
8288 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;) 8367 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
8289 debug_leave(); 8368 debug_leave();
8290 debug_printf_exec("run_pipe: return %d\n", rcode); 8369 debug_printf_exec("run_pipe: return %d\n", rcode);
8291 return rcode; 8370 return rcode;
8292#endif
8293 } 8371 }
8294 8372
8295 /* Expand the rest into (possibly) many strings each */ 8373 /* Expand the rest into (possibly) many strings each */
8296#if BASH_TEST2 8374#if defined(CMD_SINGLEWORD_NOGLOB)
8297 if (command->cmd_type == CMD_SINGLEWORD_NOGLOB) { 8375 if (command->cmd_type == CMD_SINGLEWORD_NOGLOB)
8298 argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt); 8376 argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt);
8299 } else 8377 else
8300#endif 8378#endif
8301 {
8302 argv_expanded = expand_strvec_to_strvec(argv + command->assignment_cnt); 8379 argv_expanded = expand_strvec_to_strvec(argv + command->assignment_cnt);
8303 }
8304 8380
8305 /* if someone gives us an empty string: `cmd with empty output` */ 8381 /* If someone gives us an empty string: `cmd with empty output` */
8306 if (!argv_expanded[0]) { 8382 if (!argv_expanded[0]) {
8307 free(argv_expanded); 8383 free(argv_expanded);
8308 debug_leave(); 8384 /* `false` still has to set exitcode 1 */
8309 return G.last_exitcode; 8385 G.expand_exitcode = G.last_exitcode;
8386 goto only_assignments;
8310 } 8387 }
8311 8388
8312#if ENABLE_HUSH_FUNCTIONS 8389 old_vars = NULL;
8390 sv_shadowed = G.shadowed_vars_pp;
8391
8313 /* Check if argv[0] matches any functions (this goes before bltins) */ 8392 /* Check if argv[0] matches any functions (this goes before bltins) */
8314 funcp = find_function(argv_expanded[0]); 8393 IF_HUSH_FUNCTIONS(funcp = find_function(argv_expanded[0]);)
8315#endif 8394 IF_HUSH_FUNCTIONS(x = NULL;)
8316 x = NULL; 8395 IF_HUSH_FUNCTIONS(if (!funcp))
8317 if (!funcp)
8318 x = find_builtin(argv_expanded[0]); 8396 x = find_builtin(argv_expanded[0]);
8319 if (x || funcp) { 8397 if (x || funcp) {
8320 if (!funcp) { 8398 if (x && x->b_function == builtin_exec && argv_expanded[1] == NULL) {
8321 if (x->b_function == builtin_exec && argv_expanded[1] == NULL) { 8399 debug_printf("exec with redirects only\n");
8322 debug_printf("exec with redirects only\n"); 8400 /*
8323 rcode = setup_redirects(command, NULL); 8401 * Variable assignments are executed, but then "forgotten":
8324 /* rcode=1 can be if redir file can't be opened */ 8402 * a=`sleep 1;echo A` exec 3>&-; echo $a
8325 goto clean_up_and_ret1; 8403 * sleeps, but prints nothing.
8326 } 8404 */
8405 enter_var_nest_level();
8406 G.shadowed_vars_pp = &old_vars;
8407 rcode = redirect_and_varexp_helper(command, /*squirrel:*/ NULL, argv_expanded);
8408 G.shadowed_vars_pp = sv_shadowed;
8409 /* rcode=1 can be if redir file can't be opened */
8410
8411 goto clean_up_and_ret1;
8327 } 8412 }
8328 rcode = redirect_and_varexp_helper(&new_env, &old_vars, command, &squirrel, argv_expanded); 8413
8414 /* Bump var nesting, or this will leak exported $a:
8415 * a=b true; env | grep ^a=
8416 */
8417 enter_var_nest_level();
8418 /* Collect all variables "shadowed" by helper
8419 * (IOW: old vars overridden by "var1=val1 var2=val2 cmd..." syntax)
8420 * into old_vars list:
8421 */
8422 G.shadowed_vars_pp = &old_vars;
8423 rcode = redirect_and_varexp_helper(command, &squirrel, argv_expanded);
8329 if (rcode == 0) { 8424 if (rcode == 0) {
8330 if (!funcp) { 8425 if (!funcp) {
8426 /* Do not collect *to old_vars list* vars shadowed
8427 * by e.g. "local VAR" builtin (collect them
8428 * in the previously nested list instead):
8429 * don't want them to be restored immediately
8430 * after "local" completes.
8431 */
8432 G.shadowed_vars_pp = sv_shadowed;
8433
8331 debug_printf_exec(": builtin '%s' '%s'...\n", 8434 debug_printf_exec(": builtin '%s' '%s'...\n",
8332 x->b_cmd, argv_expanded[1]); 8435 x->b_cmd, argv_expanded[1]);
8333 fflush_all(); 8436 fflush_all();
@@ -8336,72 +8439,74 @@ static NOINLINE int run_pipe(struct pipe *pi)
8336 } 8439 }
8337#if ENABLE_HUSH_FUNCTIONS 8440#if ENABLE_HUSH_FUNCTIONS
8338 else { 8441 else {
8339# if ENABLE_HUSH_LOCAL
8340 struct variable **sv;
8341 sv = G.shadowed_vars_pp;
8342 G.shadowed_vars_pp = &old_vars;
8343# endif
8344 debug_printf_exec(": function '%s' '%s'...\n", 8442 debug_printf_exec(": function '%s' '%s'...\n",
8345 funcp->name, argv_expanded[1]); 8443 funcp->name, argv_expanded[1]);
8346 rcode = run_function(funcp, argv_expanded) & 0xff; 8444 rcode = run_function(funcp, argv_expanded) & 0xff;
8347# if ENABLE_HUSH_LOCAL 8445 /*
8348 G.shadowed_vars_pp = sv; 8446 * But do collect *to old_vars list* vars shadowed
8349# endif 8447 * within function execution. To that end, restore
8448 * this pointer _after_ function run:
8449 */
8450 G.shadowed_vars_pp = sv_shadowed;
8350 } 8451 }
8351#endif 8452#endif
8352 } 8453 }
8353 clean_up_and_ret: 8454 } else
8354 unset_vars(new_env); 8455 if (ENABLE_FEATURE_SH_NOFORK && NUM_APPLETS > 1) {
8355 add_vars(old_vars); 8456 int n = find_applet_by_name(argv_expanded[0]);
8356/* clean_up_and_ret0: */ 8457 if (n < 0 || !APPLET_IS_NOFORK(n))
8357 restore_redirects(squirrel); 8458 goto must_fork;
8358 /* 8459
8359 * Try "usleep 99999999" + ^C + "echo $?" 8460 enter_var_nest_level();
8360 * with FEATURE_SH_NOFORK=y. 8461 /* Collect all variables "shadowed" by helper into old_vars list */
8361 */ 8462 G.shadowed_vars_pp = &old_vars;
8362 if (!funcp) { 8463 rcode = redirect_and_varexp_helper(command, &squirrel, argv_expanded);
8363 /* It was builtin or nofork. 8464 G.shadowed_vars_pp = sv_shadowed;
8364 * if this would be a real fork/execed program, 8465
8365 * it should have died if a fatal sig was received. 8466 if (rcode == 0) {
8366 * But OTOH, there was no separate process, 8467 debug_printf_exec(": run_nofork_applet '%s' '%s'...\n",
8367 * the sig was sent to _shell_, not to non-existing 8468 argv_expanded[0], argv_expanded[1]);
8368 * child. 8469 /*
8369 * Let's just handle ^C only, this one is obvious: 8470 * Note: signals (^C) can't interrupt here.
8370 * we aren't ok with exitcode 0 when ^C was pressed 8471 * We remember them and they will be acted upon
8371 * during builtin/nofork. 8472 * after applet returns.
8473 * This makes applets which can run for a long time
8474 * and/or wait for user input ineligible for NOFORK:
8475 * for example, "yes" or "rm" (rm -i waits for input).
8372 */ 8476 */
8373 if (sigismember(&G.pending_set, SIGINT)) 8477 rcode = run_nofork_applet(n, argv_expanded);
8374 rcode = 128 + SIGINT;
8375 } 8478 }
8479 } else
8480 goto must_fork;
8481
8482 restore_redirects(squirrel);
8376 clean_up_and_ret1: 8483 clean_up_and_ret1:
8377 free(argv_expanded); 8484 leave_var_nest_level();
8378 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;) 8485 add_vars(old_vars);
8379 debug_leave();
8380 debug_printf_exec("run_pipe return %d\n", rcode);
8381 return rcode;
8382 }
8383 8486
8384 if (ENABLE_FEATURE_SH_NOFORK && NUM_APPLETS > 1) { 8487 /*
8385 int n = find_applet_by_name(argv_expanded[0]); 8488 * Try "usleep 99999999" + ^C + "echo $?"
8386 if (n >= 0 && APPLET_IS_NOFORK(n)) { 8489 * with FEATURE_SH_NOFORK=y.
8387 rcode = redirect_and_varexp_helper(&new_env, &old_vars, command, &squirrel, argv_expanded); 8490 */
8388 if (rcode == 0) { 8491 if (!funcp) {
8389 debug_printf_exec(": run_nofork_applet '%s' '%s'...\n", 8492 /* It was builtin or nofork.
8390 argv_expanded[0], argv_expanded[1]); 8493 * if this would be a real fork/execed program,
8391 /* 8494 * it should have died if a fatal sig was received.
8392 * Note: signals (^C) can't interrupt here. 8495 * But OTOH, there was no separate process,
8393 * We remember them and they will be acted upon 8496 * the sig was sent to _shell_, not to non-existing
8394 * after applet returns. 8497 * child.
8395 * This makes applets which can run for a long time 8498 * Let's just handle ^C only, this one is obvious:
8396 * and/or wait for user input ineligible for NOFORK: 8499 * we aren't ok with exitcode 0 when ^C was pressed
8397 * for example, "yes" or "rm" (rm -i waits for input). 8500 * during builtin/nofork.
8398 */ 8501 */
8399 rcode = run_nofork_applet(n, argv_expanded); 8502 if (sigismember(&G.pending_set, SIGINT))
8400 } 8503 rcode = 128 + SIGINT;
8401 goto clean_up_and_ret;
8402 }
8403 } 8504 }
8404 /* It is neither builtin nor applet. We must fork. */ 8505 free(argv_expanded);
8506 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
8507 debug_leave();
8508 debug_printf_exec("run_pipe return %d\n", rcode);
8509 return rcode;
8405 } 8510 }
8406 8511
8407 must_fork: 8512 must_fork:
@@ -8418,7 +8523,6 @@ static NOINLINE int run_pipe(struct pipe *pi)
8418 struct fd_pair pipefds; 8523 struct fd_pair pipefds;
8419#if !BB_MMU 8524#if !BB_MMU
8420 volatile nommu_save_t nommu_save; 8525 volatile nommu_save_t nommu_save;
8421 nommu_save.new_env = NULL;
8422 nommu_save.old_vars = NULL; 8526 nommu_save.old_vars = NULL;
8423 nommu_save.argv = NULL; 8527 nommu_save.argv = NULL;
8424 nommu_save.argv_from_re_execing = NULL; 8528 nommu_save.argv_from_re_execing = NULL;
@@ -8511,7 +8615,6 @@ static NOINLINE int run_pipe(struct pipe *pi)
8511 /* Clean up after vforked child */ 8615 /* Clean up after vforked child */
8512 free(nommu_save.argv); 8616 free(nommu_save.argv);
8513 free(nommu_save.argv_from_re_execing); 8617 free(nommu_save.argv_from_re_execing);
8514 unset_vars(nommu_save.new_env);
8515 add_vars(nommu_save.old_vars); 8618 add_vars(nommu_save.old_vars);
8516#endif 8619#endif
8517 free(argv_expanded); 8620 free(argv_expanded);
@@ -8732,8 +8835,10 @@ static int run_list(struct pipe *pi)
8732#if ENABLE_HUSH_CASE 8835#if ENABLE_HUSH_CASE
8733 if (rword == RES_CASE) { 8836 if (rword == RES_CASE) {
8734 debug_printf_exec("CASE cond_code:%d\n", cond_code); 8837 debug_printf_exec("CASE cond_code:%d\n", cond_code);
8735 case_word = expand_strvec_to_string(pi->cmds->argv); 8838 case_word = expand_string_to_string(pi->cmds->argv[0], 1);
8736 unbackslash(case_word); 8839 debug_printf_exec("CASE word1:'%s'\n", case_word);
8840 //unbackslash(case_word);
8841 //debug_printf_exec("CASE word2:'%s'\n", case_word);
8737 continue; 8842 continue;
8738 } 8843 }
8739 if (rword == RES_MATCH) { 8844 if (rword == RES_MATCH) {
@@ -9060,9 +9165,9 @@ int hush_main(int argc, char **argv)
9060{ 9165{
9061 enum { 9166 enum {
9062 OPT_login = (1 << 0), 9167 OPT_login = (1 << 0),
9168 OPT_s = (1 << 1),
9063 }; 9169 };
9064 unsigned flags; 9170 unsigned flags;
9065 int opt;
9066 unsigned builtin_argc; 9171 unsigned builtin_argc;
9067 char **e; 9172 char **e;
9068 struct variable *cur_var; 9173 struct variable *cur_var;
@@ -9112,6 +9217,14 @@ int hush_main(int argc, char **argv)
9112 /* Export PWD */ 9217 /* Export PWD */
9113 set_pwd_var(SETFLAG_EXPORT); 9218 set_pwd_var(SETFLAG_EXPORT);
9114 9219
9220#if ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING_FANCY_PROMPT
9221 /* Set (but not export) PS1/2 unless already set */
9222 if (!get_local_var_value("PS1"))
9223 set_local_var_from_halves("PS1", "\\w \\$ ");
9224 if (!get_local_var_value("PS2"))
9225 set_local_var_from_halves("PS2", "> ");
9226#endif
9227
9115#if BASH_HOSTNAME_VAR 9228#if BASH_HOSTNAME_VAR
9116 /* Set (but not export) HOSTNAME unless already set */ 9229 /* Set (but not export) HOSTNAME unless already set */
9117 if (!get_local_var_value("HOSTNAME")) { 9230 if (!get_local_var_value("HOSTNAME")) {
@@ -9150,8 +9263,6 @@ int hush_main(int argc, char **argv)
9150 * OPTERR=1 9263 * OPTERR=1
9151 * OPTIND=1 9264 * OPTIND=1
9152 * IFS=$' \t\n' 9265 * IFS=$' \t\n'
9153 * PS1='\s-\v\$ '
9154 * PS2='> '
9155 * PS4='+ ' 9266 * PS4='+ '
9156 */ 9267 */
9157#endif 9268#endif
@@ -9185,7 +9296,7 @@ int hush_main(int argc, char **argv)
9185 flags = (argv[0] && argv[0][0] == '-') ? OPT_login : 0; 9296 flags = (argv[0] && argv[0][0] == '-') ? OPT_login : 0;
9186 builtin_argc = 0; 9297 builtin_argc = 0;
9187 while (1) { 9298 while (1) {
9188 opt = getopt(argc, argv, "+c:exinsl" 9299 int opt = getopt(argc, argv, "+c:exinsl"
9189#if !BB_MMU 9300#if !BB_MMU
9190 "<:$:R:V:" 9301 "<:$:R:V:"
9191# if ENABLE_HUSH_FUNCTIONS 9302# if ENABLE_HUSH_FUNCTIONS
@@ -9243,8 +9354,7 @@ int hush_main(int argc, char **argv)
9243 /* G_interactive_fd++; */ 9354 /* G_interactive_fd++; */
9244 break; 9355 break;
9245 case 's': 9356 case 's':
9246 /* "-s" means "read from stdin", but this is how we always 9357 flags |= OPT_s;
9247 * operate, so simply do nothing here. */
9248 break; 9358 break;
9249 case 'l': 9359 case 'l':
9250 flags |= OPT_login; 9360 flags |= OPT_login;
@@ -9347,7 +9457,8 @@ int hush_main(int argc, char **argv)
9347 */ 9457 */
9348 } 9458 }
9349 9459
9350 if (G.global_argv[1]) { 9460 /* -s is: hush -s ARGV1 ARGV2 (no SCRIPT) */
9461 if (!(flags & OPT_s) && G.global_argv[1]) {
9351 FILE *input; 9462 FILE *input;
9352 /* 9463 /*
9353 * "bash <script>" (which is never interactive (unless -i?)) 9464 * "bash <script>" (which is never interactive (unless -i?))
@@ -9909,8 +10020,8 @@ static int helper_export_local(char **argv, unsigned flags)
9909# if ENABLE_HUSH_LOCAL 10020# if ENABLE_HUSH_LOCAL
9910 /* Is this "local" bltin? */ 10021 /* Is this "local" bltin? */
9911 if (!(flags & (SETFLAG_EXPORT|SETFLAG_UNEXPORT|SETFLAG_MAKE_RO))) { 10022 if (!(flags & (SETFLAG_EXPORT|SETFLAG_UNEXPORT|SETFLAG_MAKE_RO))) {
9912 unsigned lvl = flags >> SETFLAG_LOCAL_SHIFT; 10023 unsigned lvl = flags >> SETFLAG_VARLVL_SHIFT;
9913 if (var && var->func_nest_level == lvl) { 10024 if (var && var->var_nest_level == lvl) {
9914 /* "local x=abc; ...; local x" - ignore second local decl */ 10025 /* "local x=abc; ...; local x" - ignore second local decl */
9915 continue; 10026 continue;
9916 } 10027 }
@@ -9934,6 +10045,7 @@ static int helper_export_local(char **argv, unsigned flags)
9934 /* (Un)exporting/making local NAME=VALUE */ 10045 /* (Un)exporting/making local NAME=VALUE */
9935 name = xstrdup(name); 10046 name = xstrdup(name);
9936 } 10047 }
10048 debug_printf_env("%s: set_local_var('%s')\n", __func__, name);
9937 if (set_local_var(name, flags)) 10049 if (set_local_var(name, flags))
9938 return EXIT_FAILURE; 10050 return EXIT_FAILURE;
9939 } while (*++argv); 10051 } while (*++argv);
@@ -9995,7 +10107,11 @@ static int FAST_FUNC builtin_local(char **argv)
9995 return EXIT_FAILURE; /* bash compat */ 10107 return EXIT_FAILURE; /* bash compat */
9996 } 10108 }
9997 argv++; 10109 argv++;
9998 return helper_export_local(argv, G.func_nest_level << SETFLAG_LOCAL_SHIFT); 10110 /* Since all builtins run in a nested variable level,
10111 * need to use level - 1 here. Or else the variable will be removed at once
10112 * after builtin returns.
10113 */
10114 return helper_export_local(argv, (G.var_nest_level - 1) << SETFLAG_VARLVL_SHIFT);
9999} 10115}
10000#endif 10116#endif
10001 10117
diff --git a/shell/hush_test/hush-arith/arith_nested1.right b/shell/hush_test/hush-arith/arith_nested1.right
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/shell/hush_test/hush-arith/arith_nested1.right
@@ -0,0 +1 @@
1
diff --git a/shell/hush_test/hush-arith/arith_nested1.tests b/shell/hush_test/hush-arith/arith_nested1.tests
new file mode 100755
index 000000000..28571b833
--- /dev/null
+++ b/shell/hush_test/hush-arith/arith_nested1.tests
@@ -0,0 +1 @@
echo $(( ( $((1)) ) ))
diff --git a/shell/hush_test/hush-misc/assignment5.right b/shell/hush_test/hush-misc/assignment5.right
new file mode 100644
index 000000000..a91554c09
--- /dev/null
+++ b/shell/hush_test/hush-misc/assignment5.right
@@ -0,0 +1,5 @@
1Zero1:0
2Zero2:0
3Zero3:0
4Zero4:0 x:1 y:1
5Three:3 x:1 y:1
diff --git a/shell/hush_test/hush-misc/assignment5.tests b/shell/hush_test/hush-misc/assignment5.tests
new file mode 100755
index 000000000..0b8104285
--- /dev/null
+++ b/shell/hush_test/hush-misc/assignment5.tests
@@ -0,0 +1,9 @@
1true; a=1; echo Zero1:$?
2false; a=1; echo Zero2:$?
3false || a=1; echo Zero3:$?
4
5false || x=$? y=`echo $?`; echo Zero4:$? x:$x y:$y
6false || x=$? y=`echo $?; exit 3`; echo Three:$? x:$x y:$y
7
8#ash sets z=1 instead of z=3. disabled for now
9#false || x=$? y=`echo $?; exit 3` z=`echo $?`; echo x:$x y:$y z:$z
diff --git a/shell/hush_test/hush-misc/func5.tests b/shell/hush_test/hush-misc/func5.tests
index 9c5f9fa48..5c33560bc 100755
--- a/shell/hush_test/hush-misc/func5.tests
+++ b/shell/hush_test/hush-misc/func5.tests
@@ -1,9 +1,8 @@
1f() { echo $1; } 1f() { echo $1; }
2f 1 2f 1
3 3
4# hush fails on this syntax, but i've never seen anyone use it ... 4f() ( echo $1; )
5#f() ( echo $1; )
6f 2 5f 2
7 6
8#f() ( echo $1 ) 7f() ( echo $1 )
9f 3 8f 3
diff --git a/shell/hush_test/hush-parsing/starquoted3.right b/shell/hush_test/hush-parsing/starquoted3.right
new file mode 100644
index 000000000..fea246c14
--- /dev/null
+++ b/shell/hush_test/hush-parsing/starquoted3.right
@@ -0,0 +1,2 @@
1<a>
2<>
diff --git a/shell/hush_test/hush-parsing/starquoted3.tests b/shell/hush_test/hush-parsing/starquoted3.tests
new file mode 100755
index 000000000..8eefe4245
--- /dev/null
+++ b/shell/hush_test/hush-parsing/starquoted3.tests
@@ -0,0 +1 @@
set -- a ""; space=" "; printf "<%s>\n" "$@"$space
diff --git a/shell/hush_test/hush-psubst/falsetick2.right b/shell/hush_test/hush-psubst/falsetick2.right
new file mode 100644
index 000000000..670f560f1
--- /dev/null
+++ b/shell/hush_test/hush-psubst/falsetick2.right
@@ -0,0 +1 @@
Two:2 v:[]
diff --git a/shell/hush_test/hush-psubst/falsetick2.tests b/shell/hush_test/hush-psubst/falsetick2.tests
new file mode 100755
index 000000000..cfbd1a5de
--- /dev/null
+++ b/shell/hush_test/hush-psubst/falsetick2.tests
@@ -0,0 +1,3 @@
1v=v
2v=`exit 2` `false`
3echo Two:$? v:"[$v]"
diff --git a/shell/hush_test/hush-quoting/bkslash_case2.right b/shell/hush_test/hush-quoting/bkslash_case2.right
new file mode 100644
index 000000000..8d2038bff
--- /dev/null
+++ b/shell/hush_test/hush-quoting/bkslash_case2.right
@@ -0,0 +1,3 @@
1ok1
2ok2
3Ok:0
diff --git a/shell/hush_test/hush-quoting/bkslash_case2.tests b/shell/hush_test/hush-quoting/bkslash_case2.tests
new file mode 100755
index 000000000..348ddc236
--- /dev/null
+++ b/shell/hush_test/hush-quoting/bkslash_case2.tests
@@ -0,0 +1,13 @@
1x='\abc'
2
3case "$x" in
4\\*) echo ok1;;
5*) echo BUG1;;
6esac
7
8case $x in
9\\*) echo ok2;;
10*) echo BUG2;;
11esac
12
13echo Ok:$?
diff --git a/shell/hush_test/hush-quoting/squote_in_varexp3.right b/shell/hush_test/hush-quoting/squote_in_varexp3.right
new file mode 100644
index 000000000..223b7836f
--- /dev/null
+++ b/shell/hush_test/hush-quoting/squote_in_varexp3.right
@@ -0,0 +1 @@
B
diff --git a/shell/hush_test/hush-quoting/squote_in_varexp3.tests b/shell/hush_test/hush-quoting/squote_in_varexp3.tests
new file mode 100755
index 000000000..028a88fd9
--- /dev/null
+++ b/shell/hush_test/hush-quoting/squote_in_varexp3.tests
@@ -0,0 +1 @@
x=\'B; echo "${x#\'}"
diff --git a/shell/hush_test/hush-redir/redir_backquote1.right b/shell/hush_test/hush-redir/redir_backquote1.right
new file mode 100644
index 000000000..810cc2314
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir_backquote1.right
@@ -0,0 +1,11 @@
1hush: can't open '/cant/be/created': No such file or directory
2First
3One:1 v1:[]
4hush: can't open '/cant/be/created': No such file or directory
5Second
6One:1 v2:[]
7Third
8Zero:0 v3:[]
9Fourth
10Zero:0 v4:[]
11Zero:0 v5:[1]
diff --git a/shell/hush_test/hush-redir/redir_backquote1.tests b/shell/hush_test/hush-redir/redir_backquote1.tests
new file mode 100755
index 000000000..41bb4913c
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir_backquote1.tests
@@ -0,0 +1,19 @@
1v=v
2v=`echo First >&2` `` >/cant/be/created
3echo One:$? v1:"[$v]"
4
5v=v
6v=`echo Second >&2` `true` >/cant/be/created
7echo One:$? v2:"[$v]"
8
9v=v
10v=`echo Third >&2` `true` 2>/dev/null
11echo Zero:$? v3:"[$v]"
12
13v=v
14v=`echo Fourth >&2` `false` 2>/dev/null
15echo Zero:$? v4:"[$v]"
16
17v=v
18v=`echo $?` `false` 2>/dev/null
19echo Zero:$? v5:"[$v]"
diff --git a/shell/hush_test/hush-redir/redir_exec1.right b/shell/hush_test/hush-redir/redir_exec1.right
new file mode 100644
index 000000000..6ff8fc832
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir_exec1.right
@@ -0,0 +1,3 @@
1First
2hush: can't open '/cant/be/created': No such file or directory
3One:1
diff --git a/shell/hush_test/hush-redir/redir_exec1.tests b/shell/hush_test/hush-redir/redir_exec1.tests
new file mode 100755
index 000000000..290e1cb39
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir_exec1.tests
@@ -0,0 +1,2 @@
1v=`echo First >&2` exec >/cant/be/created
2echo One:$?
diff --git a/shell/hush_test/hush-vars/readonly3.right b/shell/hush_test/hush-vars/readonly3.right
new file mode 100644
index 000000000..acd931243
--- /dev/null
+++ b/shell/hush_test/hush-vars/readonly3.right
@@ -0,0 +1,4 @@
1hush: v=2: readonly variable
2hush: v=3: readonly variable
31
4Ok:1
diff --git a/shell/hush_test/hush-vars/readonly3.tests b/shell/hush_test/hush-vars/readonly3.tests
new file mode 100755
index 000000000..17780cfda
--- /dev/null
+++ b/shell/hush_test/hush-vars/readonly3.tests
@@ -0,0 +1,4 @@
1readonly v=1
2# there was a bug causing second assignment to be not checked
3v=2 v=3 echo $v
4echo Ok:$v
diff --git a/shell/hush_test/hush-vars/var_bash3.right b/shell/hush_test/hush-vars/var_bash3.right
index a97c850ea..8899d981c 100644
--- a/shell/hush_test/hush-vars/var_bash3.right
+++ b/shell/hush_test/hush-vars/var_bash3.right
@@ -1,6 +1,6 @@
11 a041#c 11 a041#c
22 a041#c 22 a041#c
33 a\041#c 33 a041#c
44 a\041#c 44 a\041#c
55 a\041#c 55 a\041#c
66 a\041#c 66 a\041#c
@@ -17,4 +17,4 @@
1717 a\tc 1717 a\tc
1818 a\tc 1818 a\tc
1919 atc 1919 atc
2020 a\tc 2020 atc
diff --git a/shell/hush_test/hush-vars/var_bash4.right b/shell/hush_test/hush-vars/var_bash4.right
index 0ef1bf661..9067e58e6 100644
--- a/shell/hush_test/hush-vars/var_bash4.right
+++ b/shell/hush_test/hush-vars/var_bash4.right
@@ -3,26 +3,26 @@ Replace str: _\\_\z_
3Pattern: single backslash and star: "replace literal star" 3Pattern: single backslash and star: "replace literal star"
4Unquoted: a_\_z_b\*c 4Unquoted: a_\_z_b\*c
5Unquoted =: a_\_z_b\*c 5Unquoted =: a_\_z_b\*c
6Quoted: a_\_\z_b\*c 6Quoted: a_\_z_b\*c
7Quoted =: a_\_\z_b\*c 7Quoted =: a_\_z_b\*c
8Pattern: double backslash and star: "replace backslash and everything after it" 8Pattern: double backslash and star: "replace backslash and everything after it"
9Unquoted: a*b_\_z_ 9Unquoted: a*b_\_z_
10Unquoted =: a*b_\_z_ 10Unquoted =: a*b_\_z_
11Quoted: a*b_\_\z_ 11Quoted: a*b_\_z_
12Quoted =: a*b_\_\z_ 12Quoted =: a*b_\_z_
13 13
14Source: a\bc 14Source: a\bc
15Replace str: _\\_\z_ 15Replace str: _\\_\z_
16Pattern: single backslash and b: "replace b" 16Pattern: single backslash and b: "replace b"
17Unquoted: a\_\_z_c 17Unquoted: a\_\_z_c
18Unquoted =: a\_\_z_c 18Unquoted =: a\_\_z_c
19Quoted: a\_\_\z_c 19Quoted: a\_\_z_c
20Quoted =: a\_\_\z_c 20Quoted =: a\_\_z_c
21Pattern: double backslash and b: "replace backslash and b" 21Pattern: double backslash and b: "replace backslash and b"
22Unquoted: a_\_z_c 22Unquoted: a_\_z_c
23Unquoted =: a_\_z_c 23Unquoted =: a_\_z_c
24Quoted: a_\_\z_c 24Quoted: a_\_z_c
25Quoted =: a_\_\z_c 25Quoted =: a_\_z_c
26 26
27Source: a\bc 27Source: a\bc
28Replace str: _\\_\z_ (as variable $s) 28Replace str: _\\_\z_ (as variable $s)
diff --git a/shell/hush_test/hush-vars/var_bash6.right b/shell/hush_test/hush-vars/var_bash6.right
index 63fc23df8..115ff8b04 100644
--- a/shell/hush_test/hush-vars/var_bash6.right
+++ b/shell/hush_test/hush-vars/var_bash6.right
@@ -1,5 +1,5 @@
1Expected Actual 1Expected Actual
2a*z : a*z 2a*z : a*z
3\z : \z 3z : z
4a1z a2z: a1z a2z 4a1z a2z: a1z a2z
5z : z 5z : z
diff --git a/shell/hush_test/hush-vars/var_bash6.tests b/shell/hush_test/hush-vars/var_bash6.tests
index cf2e4f020..686834177 100755
--- a/shell/hush_test/hush-vars/var_bash6.tests
+++ b/shell/hush_test/hush-vars/var_bash6.tests
@@ -3,7 +3,7 @@
3>a1z; >a2z; 3>a1z; >a2z;
4 echo 'Expected' 'Actual' 4 echo 'Expected' 'Actual'
5v='a bz'; echo 'a*z :' "${v/a*z/a*z}" 5v='a bz'; echo 'a*z :' "${v/a*z/a*z}"
6v='a bz'; echo '\z :' "${v/a*z/\z}" 6v='a bz'; echo 'z :' "${v/a*z/\z}"
7v='a bz'; echo 'a1z a2z:' ${v/a*z/a*z} 7v='a bz'; echo 'a1z a2z:' ${v/a*z/a*z}
8v='a bz'; echo 'z :' ${v/a*z/\z} 8v='a bz'; echo 'z :' ${v/a*z/\z}
9rm a1z a2z 9rm a1z a2z
diff --git a/shell/hush_test/hush-vars/var_nested1.right b/shell/hush_test/hush-vars/var_nested1.right
new file mode 100644
index 000000000..f4bc5f4b6
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_nested1.right
@@ -0,0 +1,3 @@
1Expected:AB Actual:AB
2Expected:Ab Actual:Ab
3Expected:ab Actual:ab
diff --git a/shell/hush_test/hush-vars/var_nested1.tests b/shell/hush_test/hush-vars/var_nested1.tests
new file mode 100755
index 000000000..59e4a14fa
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_nested1.tests
@@ -0,0 +1,16 @@
1f() { a=A; b=B; }
2
3a=a
4b=b
5f
6echo Expected:AB Actual:$a$b
7
8a=a
9b=b
10b= f
11echo Expected:Ab Actual:$a$b
12
13a=a
14b=b
15a= b= f
16echo Expected:ab Actual:$a$b
diff --git a/shell/hush_test/hush-vars/var_nested2.right b/shell/hush_test/hush-vars/var_nested2.right
new file mode 100644
index 000000000..c930d971c
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_nested2.right
@@ -0,0 +1 @@
aB
diff --git a/shell/hush_test/hush-vars/var_nested2.tests b/shell/hush_test/hush-vars/var_nested2.tests
new file mode 100755
index 000000000..e8865861e
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_nested2.tests
@@ -0,0 +1,2 @@
1# the bug was easier to trigger in one-liner form
2a=a; b=b; f() { a=A; b=B; }; a= f; echo $a$b
diff --git a/shell/shell_common.c b/shell/shell_common.c
index a44ad0caf..aa0791285 100644
--- a/shell/shell_common.c
+++ b/shell/shell_common.c
@@ -18,7 +18,6 @@
18 */ 18 */
19#include "libbb.h" 19#include "libbb.h"
20#include "shell_common.h" 20#include "shell_common.h"
21#include <sys/resource.h> /* getrlimit */
22 21
23#if !ENABLE_PLATFORM_MINGW32 22#if !ENABLE_PLATFORM_MINGW32
24const char defifsvar[] ALIGN1 = "IFS= \t\n"; 23const char defifsvar[] ALIGN1 = "IFS= \t\n";
diff --git a/testsuite/bunzip2.tests b/testsuite/bunzip2.tests
index fcfce1a31..edb332748 100755
--- a/testsuite/bunzip2.tests
+++ b/testsuite/bunzip2.tests
@@ -552,6 +552,22 @@ if test "${0##*/}" = "bunzip2.tests"; then
552 echo "FAIL: $unpack: pbzip_4m_zeros file" 552 echo "FAIL: $unpack: pbzip_4m_zeros file"
553 FAILCOUNT=$((FAILCOUNT + 1)) 553 FAILCOUNT=$((FAILCOUNT + 1))
554 fi 554 fi
555
556 errout="`${bb}bunzip2 <bz2_issue_11.bz2 2>&1 >/dev/null`"
557 if test x"$errout:$?" = x"bunzip2: bunzip error -5:1"; then
558 echo "PASS: $unpack: bz2_issue_11.bz2 corrupted example"
559 else
560 echo "FAIL: $unpack: bz2_issue_11.bz2 corrupted example"
561 FAILCOUNT=$((FAILCOUNT + 1))
562 fi
563
564 errout="`${bb}bunzip2 <bz2_issue_12.bz2 2>&1 >/dev/null`"
565 if test x"$errout:$?" = x"bunzip2: bunzip error -3:1"; then
566 echo "PASS: $unpack: bz2_issue_12.bz2 corrupted example"
567 else
568 echo "FAIL: $unpack: bz2_issue_12.bz2 corrupted example"
569 FAILCOUNT=$((FAILCOUNT + 1))
570 fi
555fi 571fi
556 572
557exit $((FAILCOUNT <= 255 ? FAILCOUNT : 255)) 573exit $((FAILCOUNT <= 255 ? FAILCOUNT : 255))
diff --git a/testsuite/bz2_issue_11.bz2 b/testsuite/bz2_issue_11.bz2
new file mode 100644
index 000000000..62b252046
--- /dev/null
+++ b/testsuite/bz2_issue_11.bz2
Binary files differ
diff --git a/testsuite/bz2_issue_12.bz2 b/testsuite/bz2_issue_12.bz2
new file mode 100644
index 000000000..4215f08d6
--- /dev/null
+++ b/testsuite/bz2_issue_12.bz2
Binary files differ
diff --git a/testsuite/unlzma.tests b/testsuite/unlzma.tests
new file mode 100755
index 000000000..0e98afe09
--- /dev/null
+++ b/testsuite/unlzma.tests
@@ -0,0 +1,21 @@
1#!/bin/sh
2
3. ./testing.sh
4
5# testing "test name" "commands" "expected result" "file input" "stdin"
6# file input will be file called "input"
7# test can create a file "actual" instead of writing to stdout
8
9# Damaged encrypted streams
10testing "unlzma (bad archive 1)" \
11 "unlzma <unlzma_issue_1.lzma >/dev/null; echo \$?" \
12"1
13" "" ""
14
15# Damaged encrypted streams
16testing "unlzma (bad archive 2)" \
17 "unlzma <unlzma_issue_2.lzma >/dev/null; echo \$?" \
18"1
19" "" ""
20
21exit $FAILCOUNT
diff --git a/testsuite/unlzma_issue_1.lzma b/testsuite/unlzma_issue_1.lzma
new file mode 100644
index 000000000..fb70104ba
--- /dev/null
+++ b/testsuite/unlzma_issue_1.lzma
Binary files differ
diff --git a/testsuite/unlzma_issue_2.lzma b/testsuite/unlzma_issue_2.lzma
new file mode 100644
index 000000000..853f0fc29
--- /dev/null
+++ b/testsuite/unlzma_issue_2.lzma
Binary files differ
diff --git a/util-linux/renice.c b/util-linux/renice.c
index 46704591f..ee0fb3c8e 100644
--- a/util-linux/renice.c
+++ b/util-linux/renice.c
@@ -39,7 +39,6 @@
39//usage: "\n -u Process user names" 39//usage: "\n -u Process user names"
40 40
41#include "libbb.h" 41#include "libbb.h"
42#include <sys/resource.h>
43 42
44int renice_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 43int renice_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
45int renice_main(int argc UNUSED_PARAM, char **argv) 44int renice_main(int argc UNUSED_PARAM, char **argv)