aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2020-11-12 08:27:51 +0000
committerRon Yorston <rmy@pobox.com>2020-11-12 08:27:51 +0000
commitead8b92e3d66ab45235e137f85fb3a529dcc64ef (patch)
treeaf268270382dad969218063d4a8120fc91a9e631
parent567728c22dddea4ed33b8a69641ba2e0c3f1f600 (diff)
parent64981b4c8e88812c322bee3832f1d421ff670ed5 (diff)
downloadbusybox-w32-ead8b92e3d66ab45235e137f85fb3a529dcc64ef.tar.gz
busybox-w32-ead8b92e3d66ab45235e137f85fb3a529dcc64ef.tar.bz2
busybox-w32-ead8b92e3d66ab45235e137f85fb3a529dcc64ef.zip
Merge branch 'busybox' into merge
-rw-r--r--archival/libarchive/get_header_tar.c2
-rw-r--r--archival/tar.c12
-rw-r--r--coreutils/chmod.c13
-rw-r--r--coreutils/chown.c9
-rw-r--r--coreutils/test.c82
-rw-r--r--coreutils/uudecode.c6
-rw-r--r--debianutils/run_parts.c12
-rw-r--r--editors/diff.c22
-rw-r--r--findutils/find.c17
-rw-r--r--findutils/grep.c24
-rw-r--r--findutils/xargs.c21
-rw-r--r--include/dump.h15
-rw-r--r--include/libbb.h34
-rw-r--r--libbb/appletlib.c9
-rw-r--r--libbb/dump.c70
-rw-r--r--libbb/hash_md5_sha.c75
-rw-r--r--libbb/inet_cksum.c4
-rw-r--r--libbb/kernel_version.c20
-rw-r--r--libbb/loop.c4
-rw-r--r--libbb/ptr_to_globals.c4
-rw-r--r--libbb/recursive_action.c72
-rw-r--r--libbb/update_passwd.c15
-rw-r--r--mailutils/reformime.c5
-rw-r--r--modutils/depmod.c12
-rw-r--r--modutils/modprobe-small.c3
-rw-r--r--modutils/modprobe.c14
-rw-r--r--networking/httpd.c7
-rw-r--r--networking/ip.c2
-rw-r--r--networking/libiproute/iprule.c32
-rw-r--r--networking/netstat.c28
-rw-r--r--networking/nslookup.c5
-rw-r--r--networking/ntpd.c9
-rw-r--r--networking/ping.c4
-rw-r--r--networking/traceroute.c2
-rw-r--r--networking/udhcp/common.c10
-rw-r--r--networking/udhcp/common.h6
-rw-r--r--networking/udhcp/d6_dhcpc.c2
-rw-r--r--networking/udhcp/d6_packet.c2
-rw-r--r--networking/udhcp/dhcpc.c4
-rw-r--r--networking/udhcp/packet.c4
-rw-r--r--procps/mpstat.c2
-rw-r--r--selinux/chcon.c8
-rw-r--r--selinux/setfiles.c8
-rw-r--r--shell/ash.c145
-rw-r--r--shell/ash_test/ash-misc/wait7.right2
-rwxr-xr-xshell/ash_test/ash-misc/wait7.tests7
-rw-r--r--shell/ash_test/ash-signals/usage.right14
-rwxr-xr-xshell/ash_test/ash-signals/usage.tests23
-rw-r--r--shell/hush.c70
-rw-r--r--shell/hush_test/hush-bugs/strops5_bug.right2
-rwxr-xr-xshell/hush_test/hush-bugs/strops5_bug.tests10
-rw-r--r--shell/hush_test/hush-misc/sig_exitcode.right4
-rw-r--r--shell/hush_test/hush-misc/wait7.right2
-rwxr-xr-xshell/hush_test/hush-misc/wait7.tests7
-rw-r--r--shell/hush_test/hush-psubst/tick6.right1
-rwxr-xr-xshell/hush_test/hush-psubst/tick6.tests1
-rw-r--r--shell/hush_test/hush-signals/catch.right2
-rw-r--r--shell/hush_test/hush-signals/signal1.right20
-rwxr-xr-xshell/hush_test/hush-signals/signal1.tests28
-rw-r--r--shell/hush_test/hush-signals/signal_read2.right2
-rw-r--r--shell/hush_test/hush-signals/subshell.right2
-rw-r--r--shell/hush_test/hush-test2/andor1.right6
-rwxr-xr-xshell/hush_test/hush-test2/andor1.tests7
-rw-r--r--shell/hush_test/hush-test2/noglob1.right2
-rwxr-xr-xshell/hush_test/hush-test2/noglob1.tests3
-rw-r--r--shell/hush_test/hush-test2/strops1.right8
-rwxr-xr-xshell/hush_test/hush-test2/strops1.tests15
-rw-r--r--shell/hush_test/hush-test2/strops2.right6
-rwxr-xr-xshell/hush_test/hush-test2/strops2.tests12
-rw-r--r--shell/hush_test/hush-test2/strops3.right7
-rwxr-xr-xshell/hush_test/hush-test2/strops3.tests13
-rw-r--r--shell/hush_test/hush-test2/strops4.right2
-rwxr-xr-xshell/hush_test/hush-test2/strops4.tests3
-rw-r--r--shell/hush_test/hush-test2/strops5.right3
-rwxr-xr-xshell/hush_test/hush-test2/strops5.tests13
-rw-r--r--shell/math.c4
-rwxr-xr-xtestsuite/hexdump.tests19
-rwxr-xr-xtestsuite/xargs.tests8
-rwxr-xr-xtestsuite/xxd.tests34
-rw-r--r--util-linux/getopt.c5
-rw-r--r--util-linux/hexdump.c61
-rw-r--r--util-linux/hexdump_xxd.c77
-rw-r--r--util-linux/losetup.c2
-rw-r--r--util-linux/lspci.c11
-rw-r--r--util-linux/lsusb.c10
-rw-r--r--util-linux/mdev.c20
-rw-r--r--util-linux/mount.c65
-rw-r--r--util-linux/setpriv.c9
-rw-r--r--util-linux/switch_root.c4
-rw-r--r--util-linux/volume_id/get_devname.c14
90 files changed, 1009 insertions, 487 deletions
diff --git a/archival/libarchive/get_header_tar.c b/archival/libarchive/get_header_tar.c
index b3131ff2d..2ab3c04b8 100644
--- a/archival/libarchive/get_header_tar.c
+++ b/archival/libarchive/get_header_tar.c
@@ -352,7 +352,7 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
352 /* case 0: */ 352 /* case 0: */
353 case '0': 353 case '0':
354#if ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY 354#if ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY
355 if (last_char_is(file_header->name, '/')) { 355 if (file_header->name && last_char_is(file_header->name, '/')) {
356 goto set_dir; 356 goto set_dir;
357 } 357 }
358#endif 358#endif
diff --git a/archival/tar.c b/archival/tar.c
index c7642a50e..1796d4c60 100644
--- a/archival/tar.c
+++ b/archival/tar.c
@@ -299,7 +299,8 @@ static void writeLongname(int fd, int type, const char *name, int dir)
299 header.typeflag = type; 299 header.typeflag = type;
300 strcpy(header.name, "././@LongLink"); 300 strcpy(header.name, "././@LongLink");
301 /* This sets mode/uid/gid/mtime to "00...00<NUL>" strings */ 301 /* This sets mode/uid/gid/mtime to "00...00<NUL>" strings */
302 memset(header.mode, '0', sizeof(struct prefilled)); 302 memset((char*)&header + offsetof(struct tar_header_t, mode), /* make gcc-9.x happy */
303 '0', sizeof(struct prefilled));
303 header.mode [sizeof(header.mode ) - 1] = '\0'; 304 header.mode [sizeof(header.mode ) - 1] = '\0';
304 header.uid [sizeof(header.uid ) - 1] = '\0'; 305 header.uid [sizeof(header.uid ) - 1] = '\0';
305 header.gid [sizeof(header.gid ) - 1] = '\0'; 306 header.gid [sizeof(header.gid ) - 1] = '\0';
@@ -492,10 +493,11 @@ static int exclude_file(const llist_t *excluded_files, const char *file)
492# define exclude_file(excluded_files, file) 0 493# define exclude_file(excluded_files, file) 0
493# endif 494# endif
494 495
495static int FAST_FUNC writeFileToTarball(const char *fileName, struct stat *statbuf, 496static int FAST_FUNC writeFileToTarball(struct recursive_state *state,
496 void *userData, int depth UNUSED_PARAM) 497 const char *fileName,
498 struct stat *statbuf)
497{ 499{
498 struct TarBallInfo *tbInfo = (struct TarBallInfo *) userData; 500 struct TarBallInfo *tbInfo = (struct TarBallInfo *) state->userData;
499 const char *header_name; 501 const char *header_name;
500 int inputFileFd = -1; 502 int inputFileFd = -1;
501 503
@@ -714,7 +716,7 @@ static NOINLINE int writeTarFile(
714 /* Read the directory/files and iterate over them one at a time */ 716 /* Read the directory/files and iterate over them one at a time */
715 while (filelist) { 717 while (filelist) {
716 if (!recursive_action(filelist->data, recurseFlags, 718 if (!recursive_action(filelist->data, recurseFlags,
717 writeFileToTarball, writeFileToTarball, tbInfo, 0) 719 writeFileToTarball, writeFileToTarball, tbInfo)
718 ) { 720 ) {
719 errorFlag = TRUE; 721 errorFlag = TRUE;
720 } 722 }
diff --git a/coreutils/chmod.c b/coreutils/chmod.c
index 27e9b6b86..d2988c490 100644
--- a/coreutils/chmod.c
+++ b/coreutils/chmod.c
@@ -65,12 +65,14 @@
65 * symbolic links encountered during recursive directory traversals. 65 * symbolic links encountered during recursive directory traversals.
66 */ 66 */
67 67
68static int FAST_FUNC fileAction(const char *fileName, struct stat *statbuf, void* param, int depth) 68static int FAST_FUNC fileAction(struct recursive_state *state,
69 const char *fileName,
70 struct stat *statbuf)
69{ 71{
70 mode_t newmode; 72 mode_t newmode;
71 73
72 /* match coreutils behavior */ 74 /* match coreutils behavior */
73 if (depth == 0) { 75 if (state->depth == 0) {
74 /* statbuf holds lstat result, but we need stat (follow link) */ 76 /* statbuf holds lstat result, but we need stat (follow link) */
75 if (stat(fileName, statbuf)) 77 if (stat(fileName, statbuf))
76 goto err; 78 goto err;
@@ -79,9 +81,9 @@ static int FAST_FUNC fileAction(const char *fileName, struct stat *statbuf, void
79 return TRUE; 81 return TRUE;
80 } 82 }
81 83
82 newmode = bb_parse_mode((char *)param, statbuf->st_mode); 84 newmode = bb_parse_mode((char *)state->userData, statbuf->st_mode);
83 if (newmode == (mode_t)-1) 85 if (newmode == (mode_t)-1)
84 bb_error_msg_and_die("invalid mode '%s'", (char *)param); 86 bb_error_msg_and_die("invalid mode '%s'", (char *)state->userData);
85 87
86 if (chmod(fileName, newmode) == 0) { 88 if (chmod(fileName, newmode) == 0) {
87 if (OPT_VERBOSE 89 if (OPT_VERBOSE
@@ -136,8 +138,7 @@ int chmod_main(int argc UNUSED_PARAM, char **argv)
136 OPT_RECURSE, // recurse 138 OPT_RECURSE, // recurse
137 fileAction, // file action 139 fileAction, // file action
138 fileAction, // dir action 140 fileAction, // dir action
139 smode, // user data 141 smode) // user data
140 0) // depth
141 ) { 142 ) {
142 retval = EXIT_FAILURE; 143 retval = EXIT_FAILURE;
143 } 144 }
diff --git a/coreutils/chown.c b/coreutils/chown.c
index a1c5c0224..ffccc6cce 100644
--- a/coreutils/chown.c
+++ b/coreutils/chown.c
@@ -97,10 +97,10 @@ struct param_t {
97 chown_fptr chown_func; 97 chown_fptr chown_func;
98}; 98};
99 99
100static int FAST_FUNC fileAction(const char *fileName, struct stat *statbuf, 100static int FAST_FUNC fileAction(struct recursive_state *state UNUSED_PARAM,
101 void *vparam, int depth UNUSED_PARAM) 101 const char *fileName, struct stat *statbuf)
102{ 102{
103#define param (*(struct param_t*)vparam) 103#define param (*(struct param_t*)state->userData)
104#define opt option_mask32 104#define opt option_mask32
105 uid_t u = (param.ugid.uid == (uid_t)-1L) ? statbuf->st_uid : param.ugid.uid; 105 uid_t u = (param.ugid.uid == (uid_t)-1L) ? statbuf->st_uid : param.ugid.uid;
106 gid_t g = (param.ugid.gid == (gid_t)-1L) ? statbuf->st_gid : param.ugid.gid; 106 gid_t g = (param.ugid.gid == (gid_t)-1L) ? statbuf->st_gid : param.ugid.gid;
@@ -159,8 +159,7 @@ int chown_main(int argc UNUSED_PARAM, char **argv)
159 flags, /* flags */ 159 flags, /* flags */
160 fileAction, /* file action */ 160 fileAction, /* file action */
161 fileAction, /* dir action */ 161 fileAction, /* dir action */
162 &param, /* user data */ 162 &param) /* user data */
163 0) /* depth */
164 ) { 163 ) {
165 retval = EXIT_FAILURE; 164 retval = EXIT_FAILURE;
166 } 165 }
diff --git a/coreutils/test.c b/coreutils/test.c
index a08986130..ac7b546a3 100644
--- a/coreutils/test.c
+++ b/coreutils/test.c
@@ -76,6 +76,8 @@
76//usage: "1\n" 76//usage: "1\n"
77 77
78#include "libbb.h" 78#include "libbb.h"
79#include <regex.h>
80#include <fnmatch.h>
79 81
80/* This is a NOFORK applet. Be very careful! */ 82/* This is a NOFORK applet. Be very careful! */
81 83
@@ -146,6 +148,14 @@
146 148
147#define TEST_DEBUG 0 149#define TEST_DEBUG 0
148 150
151#if ENABLE_TEST2 \
152 || (ENABLE_ASH_BASH_COMPAT && ENABLE_ASH_TEST) \
153 || (ENABLE_HUSH_BASH_COMPAT && ENABLE_HUSH_TEST)
154# define BASH_TEST2 1
155#else
156# define BASH_TEST2 0
157#endif
158
149enum token { 159enum token {
150 EOI, 160 EOI,
151 161
@@ -184,6 +194,10 @@ enum token {
184 STRLT, 194 STRLT,
185 STRGT, 195 STRGT,
186 196
197#if BASH_TEST2
198 REGEX,
199#endif
200
187 INTEQ, /* int ops */ 201 INTEQ, /* int ops */
188 INTNE, 202 INTNE,
189 INTGE, 203 INTGE,
@@ -257,6 +271,9 @@ static const char *const TOKSTR[] = {
257 "STRNE", 271 "STRNE",
258 "STRLT", 272 "STRLT",
259 "STRGT", 273 "STRGT",
274#if BASH_TEST2
275 "REGEX",
276#endif
260 "INTEQ", 277 "INTEQ",
261 "INTNE", 278 "INTNE",
262 "INTGE", 279 "INTGE",
@@ -320,6 +337,9 @@ static const struct operator_t ops_table[] = {
320 { /* "!=" */ STRNE , BINOP }, 337 { /* "!=" */ STRNE , BINOP },
321 { /* "<" */ STRLT , BINOP }, 338 { /* "<" */ STRLT , BINOP },
322 { /* ">" */ STRGT , BINOP }, 339 { /* ">" */ STRGT , BINOP },
340#if BASH_TEST2
341 { /* "=~" */ REGEX , BINOP },
342#endif
323 { /* "-eq"*/ INTEQ , BINOP }, 343 { /* "-eq"*/ INTEQ , BINOP },
324 { /* "-ne"*/ INTNE , BINOP }, 344 { /* "-ne"*/ INTNE , BINOP },
325 { /* "-ge"*/ INTGE , BINOP }, 345 { /* "-ge"*/ INTGE , BINOP },
@@ -332,6 +352,10 @@ static const struct operator_t ops_table[] = {
332 { /* "!" */ UNOT , BUNOP }, 352 { /* "!" */ UNOT , BUNOP },
333 { /* "-a" */ BAND , BBINOP }, 353 { /* "-a" */ BAND , BBINOP },
334 { /* "-o" */ BOR , BBINOP }, 354 { /* "-o" */ BOR , BBINOP },
355#if BASH_TEST2
356 { /* "&&" */ BAND , BBINOP },
357 { /* "||" */ BOR , BBINOP },
358#endif
335 { /* "(" */ LPAREN , PAREN }, 359 { /* "(" */ LPAREN , PAREN },
336 { /* ")" */ RPAREN , PAREN }, 360 { /* ")" */ RPAREN , PAREN },
337}; 361};
@@ -365,6 +389,9 @@ static const char ops_texts[] ALIGN1 =
365 "!=" "\0" 389 "!=" "\0"
366 "<" "\0" 390 "<" "\0"
367 ">" "\0" 391 ">" "\0"
392#if BASH_TEST2
393 "=~" "\0"
394#endif
368 "-eq" "\0" 395 "-eq" "\0"
369 "-ne" "\0" 396 "-ne" "\0"
370 "-ge" "\0" 397 "-ge" "\0"
@@ -377,6 +404,10 @@ static const char ops_texts[] ALIGN1 =
377 "!" "\0" 404 "!" "\0"
378 "-a" "\0" 405 "-a" "\0"
379 "-o" "\0" 406 "-o" "\0"
407#if BASH_TEST2
408 "&&" "\0"
409 "||" "\0"
410#endif
380 "(" "\0" 411 "(" "\0"
381 ")" "\0" 412 ")" "\0"
382; 413;
@@ -397,6 +428,9 @@ struct test_statics {
397 const struct operator_t *last_operator; 428 const struct operator_t *last_operator;
398 gid_t *group_array; 429 gid_t *group_array;
399 int ngroups; 430 int ngroups;
431#if BASH_TEST2
432 bool bash_test2;
433#endif
400 jmp_buf leaving; 434 jmp_buf leaving;
401}; 435};
402 436
@@ -408,6 +442,7 @@ extern struct test_statics *const test_ptr_to_statics;
408#define last_operator (S.last_operator) 442#define last_operator (S.last_operator)
409#define group_array (S.group_array ) 443#define group_array (S.group_array )
410#define ngroups (S.ngroups ) 444#define ngroups (S.ngroups )
445#define bash_test2 (S.bash_test2 )
411#define leaving (S.leaving ) 446#define leaving (S.leaving )
412 447
413#define INIT_S() do { \ 448#define INIT_S() do { \
@@ -501,6 +536,20 @@ static enum token check_operator(const char *s)
501 n = index_in_strings(ops_texts, s); 536 n = index_in_strings(ops_texts, s);
502 if (n < 0) 537 if (n < 0)
503 return OPERAND; 538 return OPERAND;
539
540#if BASH_TEST2
541 if (ops_table[n].op_num == REGEX && !bash_test2) {
542 /* =~ is only for [[ ]] */
543 return OPERAND;
544 }
545 if (ops_table[n].op_num == BAND || ops_table[n].op_num == BOR) {
546 /* [ ] accepts -a and -o but not && and || */
547 /* [[ ]] accepts && and || but not -a and -o */
548 if (bash_test2 == (s[0] == '-'))
549 return OPERAND;
550 }
551#endif
552
504 last_operator = &ops_table[n]; 553 last_operator = &ops_table[n];
505 return ops_table[n].op_num; 554 return ops_table[n].op_num;
506} 555}
@@ -536,6 +585,29 @@ static int binop(void)
536 /*if (op->op_num == INTLT)*/ 585 /*if (op->op_num == INTLT)*/
537 return val1 < val2; 586 return val1 < val2;
538 } 587 }
588#if BASH_TEST2
589 if (bash_test2) {
590 if (op->op_num == STREQ) {
591 val1 = fnmatch(opnd2, opnd1, 0);
592 return val1 == 0;
593 }
594 if (op->op_num == STRNE) {
595 val1 = fnmatch(opnd2, opnd1, 0);
596 return val1 != 0;
597 }
598 if (op->op_num == REGEX) {
599 regex_t re_buffer;
600 memset(&re_buffer, 0, sizeof(re_buffer));
601 if (regcomp(&re_buffer, opnd2, REG_EXTENDED)) { // REG_NEWLINE?
602 /* Bad regex */
603 longjmp(leaving, 2); /* [[ a =~ * ]]; echo $? - prints 2 (silently, no error msg) */
604 }
605 val1 = regexec(&re_buffer, opnd1, 0, NULL, 0);
606 regfree(&re_buffer);
607 return val1 == 0;
608 }
609 }
610#endif
539 if (is_str_op(op->op_num)) { 611 if (is_str_op(op->op_num)) {
540 val1 = strcmp(opnd1, opnd2); 612 val1 = strcmp(opnd1, opnd2);
541 if (op->op_num == STREQ) 613 if (op->op_num == STREQ)
@@ -824,6 +896,9 @@ int test_main(int argc, char **argv)
824{ 896{
825 int res; 897 int res;
826 const char *arg0; 898 const char *arg0;
899#if BASH_TEST2
900 bool bt2 = 0;
901#endif
827 902
828 arg0 = bb_basename(argv[0]); 903 arg0 = bb_basename(argv[0]);
829 if ((ENABLE_TEST1 || ENABLE_TEST2 || ENABLE_ASH_TEST || ENABLE_HUSH_TEST) 904 if ((ENABLE_TEST1 || ENABLE_TEST2 || ENABLE_ASH_TEST || ENABLE_HUSH_TEST)
@@ -840,6 +915,9 @@ int test_main(int argc, char **argv)
840 bb_simple_error_msg("missing ]]"); 915 bb_simple_error_msg("missing ]]");
841 return 2; 916 return 2;
842 } 917 }
918#if BASH_TEST2
919 bt2 = 1;
920#endif
843 } 921 }
844 argv[argc] = NULL; 922 argv[argc] = NULL;
845 } 923 }
@@ -848,6 +926,10 @@ int test_main(int argc, char **argv)
848 /* We must do DEINIT_S() prior to returning */ 926 /* We must do DEINIT_S() prior to returning */
849 INIT_S(); 927 INIT_S();
850 928
929#if BASH_TEST2
930 bash_test2 = bt2;
931#endif
932
851 res = setjmp(leaving); 933 res = setjmp(leaving);
852 if (res) 934 if (res)
853 goto ret; 935 goto ret;
diff --git a/coreutils/uudecode.c b/coreutils/uudecode.c
index dc8ef5cca..5b2edd649 100644
--- a/coreutils/uudecode.c
+++ b/coreutils/uudecode.c
@@ -110,9 +110,7 @@ static void FAST_FUNC read_stduu(FILE *src_stream, FILE *dst_stream, int flags U
110 } 110 }
111 bb_simple_error_msg_and_die("short file"); 111 bb_simple_error_msg_and_die("short file");
112} 112}
113#endif
114 113
115#if ENABLE_UUDECODE
116int uudecode_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 114int uudecode_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
117int uudecode_main(int argc UNUSED_PARAM, char **argv) 115int uudecode_main(int argc UNUSED_PARAM, char **argv)
118{ 116{
@@ -202,10 +200,10 @@ int base64_main(int argc UNUSED_PARAM, char **argv)
202 *--argv = (char*)"-"; 200 *--argv = (char*)"-";
203 src_stream = xfopen_stdin(argv[0]); 201 src_stream = xfopen_stdin(argv[0]);
204 if (opts) { 202 if (opts) {
205 read_base64(src_stream, stdout, /*flags:*/ (char)EOF); 203 read_base64(src_stream, stdout, /*flags:*/ (unsigned char)EOF);
206 } else { 204 } else {
207 enum { 205 enum {
208 SRC_BUF_SIZE = 76/4*3, /* This *MUST* be a multiple of 3 */ 206 SRC_BUF_SIZE = 76 / 4 * 3, /* this *MUST* be a multiple of 3 */
209 DST_BUF_SIZE = 4 * ((SRC_BUF_SIZE + 2) / 3), 207 DST_BUF_SIZE = 4 * ((SRC_BUF_SIZE + 2) / 3),
210 }; 208 };
211 char src_buf[SRC_BUF_SIZE]; 209 char src_buf[SRC_BUF_SIZE];
diff --git a/debianutils/run_parts.c b/debianutils/run_parts.c
index c80cc863f..585a4b58f 100644
--- a/debianutils/run_parts.c
+++ b/debianutils/run_parts.c
@@ -131,12 +131,13 @@ static int bb_alphasort(const void *p1, const void *p2)
131 return (option_mask32 & OPT_r) ? -r : r; 131 return (option_mask32 & OPT_r) ? -r : r;
132} 132}
133 133
134static int FAST_FUNC act(const char *file, struct stat *statbuf, void *args UNUSED_PARAM, int depth) 134static int FAST_FUNC act(struct recursive_state *state,
135 const char *file, struct stat *statbuf)
135{ 136{
136 if (depth == 1) 137 if (state->depth == 0)
137 return TRUE; 138 return TRUE;
138 139
139 if (depth == 2 140 if (state->depth == 1
140 && ( !(statbuf->st_mode & (S_IFREG | S_IFLNK)) 141 && ( !(statbuf->st_mode & (S_IFREG | S_IFLNK))
141 || invalid_name(file) 142 || invalid_name(file)
142 || (!(option_mask32 & OPT_l) && access(file, X_OK) != 0)) 143 || (!(option_mask32 & OPT_l) && access(file, X_OK) != 0))
@@ -199,9 +200,8 @@ int run_parts_main(int argc UNUSED_PARAM, char **argv)
199 ACTION_RECURSE|ACTION_FOLLOWLINKS, 200 ACTION_RECURSE|ACTION_FOLLOWLINKS,
200 act, /* file action */ 201 act, /* file action */
201 act, /* dir action */ 202 act, /* dir action */
202 NULL, /* user data */ 203 NULL /* user data */
203 1 /* depth */ 204 );
204 );
205 205
206 if (!names) 206 if (!names)
207 return 0; 207 return 0;
diff --git a/editors/diff.c b/editors/diff.c
index 093e7b498..a1c51ebb9 100644
--- a/editors/diff.c
+++ b/editors/diff.c
@@ -824,11 +824,11 @@ struct dlist {
824}; 824};
825 825
826/* This function adds a filename to dl, the directory listing. */ 826/* This function adds a filename to dl, the directory listing. */
827static int FAST_FUNC add_to_dirlist(const char *filename, 827static int FAST_FUNC add_to_dirlist(struct recursive_state *state,
828 struct stat *sb UNUSED_PARAM, 828 const char *filename,
829 void *userdata, int depth UNUSED_PARAM) 829 struct stat *sb UNUSED_PARAM)
830{ 830{
831 struct dlist *const l = userdata; 831 struct dlist *const l = state->userData;
832 const char *file = filename + l->len; 832 const char *file = filename + l->len;
833 while (*file == '/') 833 while (*file == '/')
834 file++; 834 file++;
@@ -841,12 +841,12 @@ static int FAST_FUNC add_to_dirlist(const char *filename,
841/* If recursion is not set, this function adds the directory 841/* If recursion is not set, this function adds the directory
842 * to the list and prevents recursive_action from recursing into it. 842 * to the list and prevents recursive_action from recursing into it.
843 */ 843 */
844static int FAST_FUNC skip_dir(const char *filename, 844static int FAST_FUNC skip_dir(struct recursive_state *state,
845 struct stat *sb, void *userdata, 845 const char *filename,
846 int depth) 846 struct stat *sb)
847{ 847{
848 if (!(option_mask32 & FLAG(r)) && depth) { 848 if (!(option_mask32 & FLAG(r)) && state->depth) {
849 add_to_dirlist(filename, sb, userdata, depth); 849 add_to_dirlist(state, filename, sb);
850 return SKIP; 850 return SKIP;
851 } 851 }
852 if (!(option_mask32 & FLAG(N))) { 852 if (!(option_mask32 & FLAG(N))) {
@@ -854,7 +854,7 @@ static int FAST_FUNC skip_dir(const char *filename,
854 * which do not exist on the "other side". 854 * which do not exist on the "other side".
855 * Testcase: diff -r /tmp / 855 * Testcase: diff -r /tmp /
856 * (it would recurse deep into /proc without this code) */ 856 * (it would recurse deep into /proc without this code) */
857 struct dlist *const l = userdata; 857 struct dlist *const l = state->userData;
858 filename += l->len; 858 filename += l->len;
859 if (filename[0]) { 859 if (filename[0]) {
860 struct stat osb; 860 struct stat osb;
@@ -889,7 +889,7 @@ static void diffdir(char *p[2], const char *s_start)
889 * add_to_dirlist will remove it. */ 889 * add_to_dirlist will remove it. */
890 list[i].len = strlen(p[i]); 890 list[i].len = strlen(p[i]);
891 recursive_action(p[i], ACTION_RECURSE | ACTION_FOLLOWLINKS, 891 recursive_action(p[i], ACTION_RECURSE | ACTION_FOLLOWLINKS,
892 add_to_dirlist, skip_dir, &list[i], 0); 892 add_to_dirlist, skip_dir, &list[i]);
893 /* Sort dl alphabetically. 893 /* Sort dl alphabetically.
894 * GNU diff does this ignoring any number of trailing dots. 894 * GNU diff does this ignoring any number of trailing dots.
895 * We don't, so for us dotted files almost always are 895 * We don't, so for us dotted files almost always are
diff --git a/findutils/find.c b/findutils/find.c
index 121f8fd03..f26c01c1e 100644
--- a/findutils/find.c
+++ b/findutils/find.c
@@ -889,10 +889,10 @@ ACTF(links)
889} 889}
890#endif 890#endif
891 891
892static int FAST_FUNC fileAction(const char *fileName, 892static int FAST_FUNC fileAction(
893 struct stat *statbuf, 893 struct recursive_state *state IF_NOT_FEATURE_FIND_MAXDEPTH(UNUSED_PARAM),
894 void *userData UNUSED_PARAM, 894 const char *fileName,
895 int depth IF_NOT_FEATURE_FIND_MAXDEPTH(UNUSED_PARAM)) 895 struct stat *statbuf)
896{ 896{
897 int r; 897 int r;
898 int same_fs = 1; 898 int same_fs = 1;
@@ -911,12 +911,12 @@ static int FAST_FUNC fileAction(const char *fileName,
911#endif 911#endif
912 912
913#if ENABLE_FEATURE_FIND_MAXDEPTH 913#if ENABLE_FEATURE_FIND_MAXDEPTH
914 if (depth < G.minmaxdepth[0]) { 914 if (state->depth < G.minmaxdepth[0]) {
915 if (same_fs) 915 if (same_fs)
916 return TRUE; /* skip this, continue recursing */ 916 return TRUE; /* skip this, continue recursing */
917 return SKIP; /* stop recursing */ 917 return SKIP; /* stop recursing */
918 } 918 }
919 if (depth > G.minmaxdepth[1]) 919 if (state->depth > G.minmaxdepth[1])
920 return SKIP; /* stop recursing */ 920 return SKIP; /* stop recursing */
921#endif 921#endif
922 922
@@ -927,7 +927,7 @@ static int FAST_FUNC fileAction(const char *fileName,
927 927
928#if ENABLE_FEATURE_FIND_MAXDEPTH 928#if ENABLE_FEATURE_FIND_MAXDEPTH
929 if (S_ISDIR(statbuf->st_mode)) { 929 if (S_ISDIR(statbuf->st_mode)) {
930 if (depth == G.minmaxdepth[1]) 930 if (state->depth == G.minmaxdepth[1])
931 return SKIP; 931 return SKIP;
932 } 932 }
933#endif 933#endif
@@ -1574,8 +1574,7 @@ int find_main(int argc UNUSED_PARAM, char **argv)
1574 G.recurse_flags,/* flags */ 1574 G.recurse_flags,/* flags */
1575 fileAction, /* file action */ 1575 fileAction, /* file action */
1576 fileAction, /* dir action */ 1576 fileAction, /* dir action */
1577 NULL, /* user data */ 1577 NULL) /* user data */
1578 0) /* depth */
1579 ) { 1578 ) {
1580 G.exitstatus |= EXIT_FAILURE; 1579 G.exitstatus |= EXIT_FAILURE;
1581 } 1580 }
diff --git a/findutils/grep.c b/findutils/grep.c
index b456ed467..10cca83e7 100644
--- a/findutils/grep.c
+++ b/findutils/grep.c
@@ -656,10 +656,9 @@ static void load_pattern_list(llist_t **lst, char *pattern)
656 llist_add_to(lst, new_grep_list_data(p, 0)); 656 llist_add_to(lst, new_grep_list_data(p, 0));
657} 657}
658 658
659static int FAST_FUNC file_action_grep(const char *filename, 659static int FAST_FUNC file_action_grep(struct recursive_state *state UNUSED_PARAM,
660 struct stat *statbuf, 660 const char *filename,
661 void* matched, 661 struct stat *statbuf)
662 int depth UNUSED_PARAM)
663{ 662{
664 FILE *file; 663 FILE *file;
665 664
@@ -686,7 +685,7 @@ static int FAST_FUNC file_action_grep(const char *filename,
686 return 0; 685 return 0;
687 } 686 }
688 cur_file = filename; 687 cur_file = filename;
689 *(int*)matched |= grep_file(file); 688 *(int*)state->userData |= grep_file(file);
690 fclose(file); 689 fclose(file);
691 return 1; 690 return 1;
692} 691}
@@ -694,15 +693,16 @@ static int FAST_FUNC file_action_grep(const char *filename,
694static int grep_dir(const char *dir) 693static int grep_dir(const char *dir)
695{ 694{
696 int matched = 0; 695 int matched = 0;
697 recursive_action(dir, 696 recursive_action(dir, 0
698 /* recurse=yes */ ACTION_RECURSE | 697 | ACTION_RECURSE
699 /* followLinks=always */ ((option_mask32 & OPT_R) ? ACTION_FOLLOWLINKS : 0) | 698 | ((option_mask32 & OPT_R) ? ACTION_FOLLOWLINKS : 0)
700 /* followLinks=command line only */ ACTION_FOLLOWLINKS_L0 | 699 | ACTION_FOLLOWLINKS_L0 /* grep -r ... SYMLINK follows it */
701 /* depthFirst=yes */ ACTION_DEPTHFIRST, 700 | ACTION_DEPTHFIRST
701 | 0,
702 /* fileAction= */ file_action_grep, 702 /* fileAction= */ file_action_grep,
703 /* dirAction= */ NULL, 703 /* dirAction= */ NULL,
704 /* userData= */ &matched, 704 /* userData= */ &matched
705 /* depth= */ 0); 705 );
706 return matched; 706 return matched;
707} 707}
708 708
diff --git a/findutils/xargs.c b/findutils/xargs.c
index 71350d470..1f8d95168 100644
--- a/findutils/xargs.c
+++ b/findutils/xargs.c
@@ -83,9 +83,11 @@
83/* This is a NOEXEC applet. Be very careful! */ 83/* This is a NOEXEC applet. Be very careful! */
84 84
85 85
86//#define dbg_msg(...) bb_error_msg(__VA_ARGS__) 86#if 0
87#define dbg_msg(...) ((void)0) 87# define dbg_msg(...) bb_error_msg(__VA_ARGS__)
88 88#else
89# define dbg_msg(...) ((void)0)
90#endif
89 91
90#ifdef TEST 92#ifdef TEST
91# ifndef ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION 93# ifndef ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION
@@ -539,9 +541,18 @@ static char* FAST_FUNC process_stdin_with_replace(int n_max_chars, int n_max_arg
539 541
540 while (1) { 542 while (1) {
541 int c = getchar(); 543 int c = getchar();
544 if (p == buf) {
545 if (c == EOF)
546 goto ret; /* last line is empty, return "" */
547 if (c == G.eol_ch)
548 continue; /* empty line, ignore */
549 /* Skip leading whitespace of each line: try
550 * echo -e ' \t\v1 2 3 ' | xargs -I% echo '[%]'
551 */
552 if (ISSPACE(c))
553 continue;
554 }
542 if (c == EOF || c == G.eol_ch) { 555 if (c == EOF || c == G.eol_ch) {
543 if (p == buf)
544 goto ret; /* empty line */
545 c = '\0'; 556 c = '\0';
546 } 557 }
547 *p++ = c; 558 *p++ = c;
diff --git a/include/dump.h b/include/dump.h
index 4c237ef05..9193a6925 100644
--- a/include/dump.h
+++ b/include/dump.h
@@ -2,20 +2,6 @@
2 2
3PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN 3PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
4 4
5#define F_IGNORE 0x01 /* %_A */
6#define F_SETREP 0x02 /* rep count set, not default */
7#define F_ADDRESS 0x001 /* print offset */
8#define F_BPAD 0x002 /* blank pad */
9#define F_C 0x004 /* %_c */
10#define F_CHAR 0x008 /* %c */
11#define F_DBL 0x010 /* %[EefGf] */
12#define F_INT 0x020 /* %[di] */
13#define F_P 0x040 /* %_p */
14#define F_STR 0x080 /* %s */
15#define F_U 0x100 /* %_u */
16#define F_UINT 0x200 /* %[ouXx] */
17#define F_TEXT 0x400 /* no conversions */
18
19enum dump_vflag_t { ALL, DUP, FIRST, WAIT }; /* -v values */ 5enum dump_vflag_t { ALL, DUP, FIRST, WAIT }; /* -v values */
20 6
21typedef struct PR { 7typedef struct PR {
@@ -46,6 +32,7 @@ typedef struct dumper_t {
46 off_t dump_skip; /* bytes to skip */ 32 off_t dump_skip; /* bytes to skip */
47 int dump_length; /* max bytes to read */ 33 int dump_length; /* max bytes to read */
48 smallint dump_vflag; /*enum dump_vflag_t*/ 34 smallint dump_vflag; /*enum dump_vflag_t*/
35 const char *eofstring;
49 FS *fshead; 36 FS *fshead;
50} dumper_t; 37} dumper_t;
51 38
diff --git a/include/libbb.h b/include/libbb.h
index c09db66eb..eb8b63895 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -357,12 +357,13 @@ struct BUG_off_t_size_is_misdetected {
357#endif 357#endif
358#endif 358#endif
359 359
360#if defined(__GLIBC__) 360#if defined(errno)
361/* glibc uses __errno_location() to get a ptr to errno */ 361/* If errno is a define, assume it's "define errno (*__errno_location())"
362/* We can just memorize it once - no multithreading in busybox :) */ 362 * and we will cache it's result in this variable */
363extern int *const bb_errno; 363extern int *const bb_errno;
364#undef errno 364#undef errno
365#define errno (*bb_errno) 365#define errno (*bb_errno)
366#define bb_cached_errno_ptr 1
366#endif 367#endif
367 368
368#if !(ULONG_MAX > 0xffffffff) 369#if !(ULONG_MAX > 0xffffffff)
@@ -454,15 +455,23 @@ enum {
454 ACTION_FOLLOWLINKS = (1 << 1), 455 ACTION_FOLLOWLINKS = (1 << 1),
455 ACTION_FOLLOWLINKS_L0 = (1 << 2), 456 ACTION_FOLLOWLINKS_L0 = (1 << 2),
456 ACTION_DEPTHFIRST = (1 << 3), 457 ACTION_DEPTHFIRST = (1 << 3),
457 /*ACTION_REVERSE = (1 << 4), - unused */ 458 ACTION_QUIET = (1 << 4),
458 ACTION_QUIET = (1 << 5), 459 ACTION_DANGLING_OK = (1 << 5),
459 ACTION_DANGLING_OK = (1 << 6),
460}; 460};
461typedef uint8_t recurse_flags_t; 461typedef uint8_t recurse_flags_t;
462extern int recursive_action(const char *fileName, unsigned flags, 462typedef struct recursive_state {
463 int FAST_FUNC (*fileAction)(const char *fileName, struct stat* statbuf, void* userData, int depth), 463 unsigned flags;
464 int FAST_FUNC (*dirAction)(const char *fileName, struct stat* statbuf, void* userData, int depth), 464 unsigned depth;
465 void* userData, unsigned depth) FAST_FUNC; 465 void *userData;
466 int FAST_FUNC (*fileAction)(struct recursive_state *state, const char *fileName, struct stat* statbuf);
467 int FAST_FUNC (*dirAction)(struct recursive_state *state, const char *fileName, struct stat* statbuf);
468} recursive_state_t;
469int recursive_action(const char *fileName, unsigned flags,
470 int FAST_FUNC (*fileAction)(struct recursive_state *state, const char *fileName, struct stat* statbuf),
471 int FAST_FUNC (*dirAction)(struct recursive_state *state, const char *fileName, struct stat* statbuf),
472 void *userData
473) FAST_FUNC;
474
466extern int device_open(const char *device, int mode) FAST_FUNC; 475extern int device_open(const char *device, int mode) FAST_FUNC;
467enum { GETPTY_BUFSIZE = 16 }; /* more than enough for "/dev/ttyXXX" */ 476enum { GETPTY_BUFSIZE = 16 }; /* more than enough for "/dev/ttyXXX" */
468extern int xgetpty(char *line) FAST_FUNC; 477extern int xgetpty(char *line) FAST_FUNC;
@@ -856,7 +865,7 @@ ssize_t recv_from_to(int fd, void *buf, size_t len, int flags,
856 struct sockaddr *to, 865 struct sockaddr *to,
857 socklen_t sa_size) FAST_FUNC; 866 socklen_t sa_size) FAST_FUNC;
858 867
859uint16_t inet_cksum(uint16_t *addr, int len) FAST_FUNC; 868uint16_t inet_cksum(const void *addr, int len) FAST_FUNC;
860int parse_pasv_epsv(char *buf) FAST_FUNC; 869int parse_pasv_epsv(char *buf) FAST_FUNC;
861 870
862/* 0 if argv[0] is NULL: */ 871/* 0 if argv[0] is NULL: */
@@ -1558,7 +1567,8 @@ int del_loop(const char *device) FAST_FUNC;
1558 * malloc and return it in *devname. 1567 * malloc and return it in *devname.
1559 * return value is the opened fd to the loop device, or < on error 1568 * return value is the opened fd to the loop device, or < on error
1560 */ 1569 */
1561int set_loop(char **devname, const char *file, unsigned long long offset, unsigned flags) FAST_FUNC; 1570int set_loop(char **devname, const char *file, unsigned long long offset,
1571 unsigned long long sizelimit, unsigned flags) FAST_FUNC;
1562/* These constants match linux/loop.h (without BB_ prefix): */ 1572/* These constants match linux/loop.h (without BB_ prefix): */
1563#define BB_LO_FLAGS_READ_ONLY 1 1573#define BB_LO_FLAGS_READ_ONLY 1
1564#define BB_LO_FLAGS_AUTOCLEAR 4 1574#define BB_LO_FLAGS_AUTOCLEAR 4
diff --git a/libbb/appletlib.c b/libbb/appletlib.c
index 77a9c3b7d..6ed86e12b 100644
--- a/libbb/appletlib.c
+++ b/libbb/appletlib.c
@@ -25,6 +25,11 @@
25 * 25 *
26 * FEATURE_INSTALLER or FEATURE_SUID will still link printf routines in. :( 26 * FEATURE_INSTALLER or FEATURE_SUID will still link printf routines in. :(
27 */ 27 */
28
29/* Define this accessor before we #define "errno" our way */
30#include <errno.h>
31static inline int *get_perrno(void) { return &errno; }
32
28#include "busybox.h" 33#include "busybox.h"
29 34
30#if !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \ 35#if !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \
@@ -318,8 +323,8 @@ void lbb_prepare(const char *applet
318void lbb_prepare(const char *applet 323void lbb_prepare(const char *applet
319 IF_FEATURE_INDIVIDUAL(, char **argv)) 324 IF_FEATURE_INDIVIDUAL(, char **argv))
320{ 325{
321#ifdef __GLIBC__ 326#ifdef bb_cached_errno_ptr
322 (*(int **)not_const_pp(&bb_errno)) = __errno_location(); 327 (*(int **)not_const_pp(&bb_errno)) = get_perrno();
323 barrier(); 328 barrier();
324#endif 329#endif
325 applet_name = applet; 330 applet_name = applet;
diff --git a/libbb/dump.c b/libbb/dump.c
index 8029cca0e..1ba1132b3 100644
--- a/libbb/dump.c
+++ b/libbb/dump.c
@@ -13,13 +13,19 @@
13#include "libbb.h" 13#include "libbb.h"
14#include "dump.h" 14#include "dump.h"
15 15
16static const char dot_flags_width_chars[] ALIGN1 = ".#-+ 0123456789"; 16#define F_IGNORE 0x01 /* %_A */
17 17#define F_SETREP 0x02 /* rep count set, not default */
18static const char size_conv_str[] ALIGN1 = 18#define F_ADDRESS 0x001 /* print offset */
19"\x1\x4\x4\x4\x4\x4\x4\x8\x8\x8\x8\010cdiouxXeEfgG"; 19#define F_BPAD 0x002 /* blank pad */
20 20#define F_C 0x004 /* %_c */
21static const char int_convs[] ALIGN1 = "diouxX"; 21#define F_CHAR 0x008 /* %c */
22 22#define F_DBL 0x010 /* %[EefGf] */
23#define F_INT 0x020 /* %[di] */
24#define F_P 0x040 /* %_p */
25#define F_STR 0x080 /* %s */
26#define F_U 0x100 /* %_u */
27#define F_UINT 0x200 /* %[ouXx] */
28#define F_TEXT 0x400 /* no conversions */
23 29
24typedef struct priv_dumper_t { 30typedef struct priv_dumper_t {
25 dumper_t pub; 31 dumper_t pub;
@@ -39,6 +45,13 @@ typedef struct priv_dumper_t {
39 unsigned char *get__savp; 45 unsigned char *get__savp;
40} priv_dumper_t; 46} priv_dumper_t;
41 47
48static const char dot_flags_width_chars[] ALIGN1 = ".#-+ 0123456789";
49
50static const char size_conv_str[] ALIGN1 =
51"\x1\x4\x4\x4\x4\x4\x4\x8\x8\x8\x8\010cdiouxXeEfgG";
52
53static const char int_convs[] ALIGN1 = "diouxX";
54
42dumper_t* FAST_FUNC alloc_dumper(void) 55dumper_t* FAST_FUNC alloc_dumper(void)
43{ 56{
44 priv_dumper_t *dumper = xzalloc(sizeof(*dumper)); 57 priv_dumper_t *dumper = xzalloc(sizeof(*dumper));
@@ -48,7 +61,6 @@ dumper_t* FAST_FUNC alloc_dumper(void)
48 return &dumper->pub; 61 return &dumper->pub;
49} 62}
50 63
51
52static NOINLINE int bb_dump_size(FS *fs) 64static NOINLINE int bb_dump_size(FS *fs)
53{ 65{
54 FU *fu; 66 FU *fu;
@@ -284,7 +296,9 @@ static NOINLINE void rewrite(priv_dumper_t *dumper, FS *fs)
284 * repeat it as necessary. 296 * repeat it as necessary.
285 * 297 *
286 * if rep count is greater than 1, no trailing whitespace 298 * if rep count is greater than 1, no trailing whitespace
287 * gets output from the last iteration of the format unit. 299 * gets output from the last iteration of the format unit:
300 * 2/1 "%02x " prints "XX XX", not "XX XX "
301 * 2/1 "%02x\n" prints "XX\nXX", not "XX\nXX\n"
288 */ 302 */
289 for (fu = fs->nextfu; fu; fu = fu->nextfu) { 303 for (fu = fs->nextfu; fu; fu = fu->nextfu) {
290 if (!fu->nextfu 304 if (!fu->nextfu
@@ -394,7 +408,6 @@ static unsigned char *get(priv_dumper_t *dumper)
394 if (dumper->pub.dump_vflag != DUP) { 408 if (dumper->pub.dump_vflag != DUP) {
395 puts("*"); 409 puts("*");
396 } 410 }
397 return NULL;
398 } 411 }
399 memset(dumper->get__curp + nread, 0, need); 412 memset(dumper->get__curp + nread, 0, need);
400 dumper->eaddress = dumper->address + nread; 413 dumper->eaddress = dumper->address + nread;
@@ -453,7 +466,7 @@ static void bpad(PR *pr)
453 for (p2 = ++p1; *p1 && strchr(" -0+#", *p1); ++p1) 466 for (p2 = ++p1; *p1 && strchr(" -0+#", *p1); ++p1)
454 if (pr->nospace) 467 if (pr->nospace)
455 pr->nospace--; 468 pr->nospace--;
456 while ((*p2++ = *p1++) != 0) 469 while ((*p2++ = *p1++) != '\0')
457 continue; 470 continue;
458} 471}
459 472
@@ -520,36 +533,43 @@ static void conv_u(PR *pr, unsigned char *p)
520 533
521static void display(priv_dumper_t* dumper) 534static void display(priv_dumper_t* dumper)
522{ 535{
523 FS *fs; 536 unsigned char *bp;
524 FU *fu;
525 PR *pr;
526 int cnt;
527 unsigned char *bp, *savebp;
528 off_t saveaddress;
529 unsigned char savech = '\0'; 537 unsigned char savech = '\0';
530 538
531 while ((bp = get(dumper)) != NULL) { 539 while ((bp = get(dumper)) != NULL) {
540 FS *fs;
541 unsigned char *savebp;
542 off_t saveaddress;
543
532 fs = dumper->pub.fshead; 544 fs = dumper->pub.fshead;
533 savebp = bp; 545 savebp = bp;
534 saveaddress = dumper->address; 546 saveaddress = dumper->address;
535 for (; fs; fs = fs->nextfs, bp = savebp, dumper->address = saveaddress) { 547 for (; fs; fs = fs->nextfs, bp = savebp, dumper->address = saveaddress) {
548 FU *fu;
536 for (fu = fs->nextfu; fu; fu = fu->nextfu) { 549 for (fu = fs->nextfu; fu; fu = fu->nextfu) {
550 int cnt;
537 if (fu->flags & F_IGNORE) { 551 if (fu->flags & F_IGNORE) {
538 break; 552 break;
539 } 553 }
540 for (cnt = fu->reps; cnt; --cnt) { 554 for (cnt = fu->reps; cnt; --cnt) {
555 PR *pr;
541 for (pr = fu->nextpr; pr; dumper->address += pr->bcnt, 556 for (pr = fu->nextpr; pr; dumper->address += pr->bcnt,
542 bp += pr->bcnt, pr = pr->nextpr) { 557 bp += pr->bcnt, pr = pr->nextpr) {
543 if (dumper->eaddress && dumper->address >= dumper->eaddress 558 if (dumper->eaddress
544 && !(pr->flags & (F_TEXT | F_BPAD)) 559 && dumper->address >= dumper->eaddress
545 ) { 560 ) {
546 bpad(pr); 561 if (dumper->pub.eofstring) {
562 /* xxd support: requested to not pad incomplete blocks */
563 fputs(dumper->pub.eofstring, stdout);
564 return;
565 }
566 if (!(pr->flags & (F_TEXT | F_BPAD)))
567 bpad(pr);
547 } 568 }
548 if (cnt == 1 && pr->nospace) { 569 if (cnt == 1 && pr->nospace) {
549 savech = *pr->nospace; 570 savech = *pr->nospace;
550 *pr->nospace = '\0'; 571 *pr->nospace = '\0';
551 } 572 }
552/* PRINT; */
553 switch (pr->flags) { 573 switch (pr->flags) {
554 case F_ADDRESS: 574 case F_ADDRESS:
555 printf(pr->fmt, (unsigned) dumper->address); 575 printf(pr->fmt, (unsigned) dumper->address);
@@ -638,7 +658,9 @@ static void display(priv_dumper_t* dumper)
638 } 658 }
639 } 659 }
640 } 660 }
661
641 if (dumper->endfu) { 662 if (dumper->endfu) {
663 PR *pr;
642 /* 664 /*
643 * if eaddress not set, error or file size was multiple 665 * if eaddress not set, error or file size was multiple
644 * of blocksize, and no partial block ever found. 666 * of blocksize, and no partial block ever found.
@@ -695,8 +717,7 @@ void FAST_FUNC bb_dump_add(dumper_t* pub_dumper, const char *fmt)
695{ 717{
696 const char *p; 718 const char *p;
697 FS *tfs; 719 FS *tfs;
698 FU *tfu, **nextfupp; 720 FU **nextfupp;
699 const char *savep;
700 721
701 /* start new linked list of format units */ 722 /* start new linked list of format units */
702 tfs = xzalloc(sizeof(FS)); /*DBU:[dave@cray.com] start out NULL */ 723 tfs = xzalloc(sizeof(FS)); /*DBU:[dave@cray.com] start out NULL */
@@ -713,6 +734,9 @@ void FAST_FUNC bb_dump_add(dumper_t* pub_dumper, const char *fmt)
713 /* take the format string and break it up into format units */ 734 /* take the format string and break it up into format units */
714 p = fmt; 735 p = fmt;
715 for (;;) { 736 for (;;) {
737 FU *tfu;
738 const char *savep;
739
716 p = skip_whitespace(p); 740 p = skip_whitespace(p);
717 if (*p == '\0') { 741 if (*p == '\0') {
718 break; 742 break;
diff --git a/libbb/hash_md5_sha.c b/libbb/hash_md5_sha.c
index 9db79ea8b..d8f210173 100644
--- a/libbb/hash_md5_sha.c
+++ b/libbb/hash_md5_sha.c
@@ -38,35 +38,6 @@ static ALWAYS_INLINE uint64_t rotl64(uint64_t x, unsigned n)
38 return (x << n) | (x >> (64 - n)); 38 return (x << n) | (x >> (64 - n));
39} 39}
40 40
41/* Feed data through a temporary buffer.
42 * The internal buffer remembers previous data until it has 64
43 * bytes worth to pass on.
44 */
45static void FAST_FUNC common64_hash(md5_ctx_t *ctx, const void *buffer, size_t len)
46{
47 unsigned bufpos = ctx->total64 & 63;
48
49 ctx->total64 += len;
50
51 while (1) {
52 unsigned remaining = 64 - bufpos;
53 if (remaining > len)
54 remaining = len;
55 /* Copy data into aligned buffer */
56 memcpy(ctx->wbuffer + bufpos, buffer, remaining);
57 len -= remaining;
58 buffer = (const char *)buffer + remaining;
59 bufpos += remaining;
60 /* Clever way to do "if (bufpos != N) break; ... ; bufpos = 0;" */
61 bufpos -= 64;
62 if (bufpos != 0)
63 break;
64 /* Buffer is filled up, process it */
65 ctx->process_block(ctx);
66 /*bufpos = 0; - already is */
67 }
68}
69
70/* Process the remaining bytes in the buffer */ 41/* Process the remaining bytes in the buffer */
71static void FAST_FUNC common64_end(md5_ctx_t *ctx, int swap_needed) 42static void FAST_FUNC common64_end(md5_ctx_t *ctx, int swap_needed)
72{ 43{
@@ -449,7 +420,29 @@ void FAST_FUNC md5_begin(md5_ctx_t *ctx)
449/* Used also for sha1 and sha256 */ 420/* Used also for sha1 and sha256 */
450void FAST_FUNC md5_hash(md5_ctx_t *ctx, const void *buffer, size_t len) 421void FAST_FUNC md5_hash(md5_ctx_t *ctx, const void *buffer, size_t len)
451{ 422{
452 common64_hash(ctx, buffer, len); 423 unsigned bufpos = ctx->total64 & 63;
424
425 ctx->total64 += len;
426
427 while (1) {
428 unsigned remaining = 64 - bufpos;
429 if (remaining > len)
430 remaining = len;
431 /* Copy data into aligned buffer */
432 memcpy(ctx->wbuffer + bufpos, buffer, remaining);
433 len -= remaining;
434 buffer = (const char *)buffer + remaining;
435 bufpos += remaining;
436
437 /* Clever way to do "if (bufpos != N) break; ... ; bufpos = 0;" */
438 bufpos -= 64;
439 if (bufpos != 0)
440 break;
441
442 /* Buffer is filled up, process it */
443 ctx->process_block(ctx);
444 /*bufpos = 0; - already is */
445 }
453} 446}
454 447
455/* Process the remaining bytes in the buffer and put result from CTX 448/* Process the remaining bytes in the buffer and put result from CTX
@@ -816,7 +809,7 @@ void FAST_FUNC sha512_begin(sha512_ctx_t *ctx)
816 int i; 809 int i;
817 /* Two extra iterations zero out ctx->total64[2] */ 810 /* Two extra iterations zero out ctx->total64[2] */
818 uint64_t *tp = ctx->total64; 811 uint64_t *tp = ctx->total64;
819 for (i = 0; i < 2+8; i++) 812 for (i = 0; i < 8 + 2; i++)
820 tp[i] = ((uint64_t)(init256[i]) << 32) + init512_lo[i]; 813 tp[i] = ((uint64_t)(init256[i]) << 32) + init512_lo[i];
821 /*ctx->total64[0] = ctx->total64[1] = 0; - already done */ 814 /*ctx->total64[0] = ctx->total64[1] = 0; - already done */
822} 815}
@@ -832,22 +825,7 @@ void FAST_FUNC sha512_hash(sha512_ctx_t *ctx, const void *buffer, size_t len)
832 ctx->total64[0] += len; 825 ctx->total64[0] += len;
833 if (ctx->total64[0] < len) 826 if (ctx->total64[0] < len)
834 ctx->total64[1]++; 827 ctx->total64[1]++;
835# if 0
836 remaining = 128 - bufpos;
837 828
838 /* Hash whole blocks */
839 while (len >= remaining) {
840 memcpy(ctx->wbuffer + bufpos, buffer, remaining);
841 buffer = (const char *)buffer + remaining;
842 len -= remaining;
843 remaining = 128;
844 bufpos = 0;
845 sha512_process_block128(ctx);
846 }
847
848 /* Save last, partial blosk */
849 memcpy(ctx->wbuffer + bufpos, buffer, len);
850# else
851 while (1) { 829 while (1) {
852 remaining = 128 - bufpos; 830 remaining = 128 - bufpos;
853 if (remaining > len) 831 if (remaining > len)
@@ -857,15 +835,16 @@ void FAST_FUNC sha512_hash(sha512_ctx_t *ctx, const void *buffer, size_t len)
857 len -= remaining; 835 len -= remaining;
858 buffer = (const char *)buffer + remaining; 836 buffer = (const char *)buffer + remaining;
859 bufpos += remaining; 837 bufpos += remaining;
838
860 /* Clever way to do "if (bufpos != N) break; ... ; bufpos = 0;" */ 839 /* Clever way to do "if (bufpos != N) break; ... ; bufpos = 0;" */
861 bufpos -= 128; 840 bufpos -= 128;
862 if (bufpos != 0) 841 if (bufpos != 0)
863 break; 842 break;
843
864 /* Buffer is filled up, process it */ 844 /* Buffer is filled up, process it */
865 sha512_process_block128(ctx); 845 sha512_process_block128(ctx);
866 /*bufpos = 0; - already is */ 846 /*bufpos = 0; - already is */
867 } 847 }
868# endif
869} 848}
870#endif /* NEED_SHA512 */ 849#endif /* NEED_SHA512 */
871 850
@@ -1398,10 +1377,12 @@ void FAST_FUNC sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len)
1398 bufpos++; 1377 bufpos++;
1399 remaining--; 1378 remaining--;
1400 } 1379 }
1380
1401 /* Clever way to do "if (bufpos != N) break; ... ; bufpos = 0;" */ 1381 /* Clever way to do "if (bufpos != N) break; ... ; bufpos = 0;" */
1402 bufpos -= ctx->input_block_bytes; 1382 bufpos -= ctx->input_block_bytes;
1403 if (bufpos != 0) 1383 if (bufpos != 0)
1404 break; 1384 break;
1385
1405 /* Buffer is filled up, process it */ 1386 /* Buffer is filled up, process it */
1406 sha3_process_block72(ctx->state); 1387 sha3_process_block72(ctx->state);
1407 /*bufpos = 0; - already is */ 1388 /*bufpos = 0; - already is */
diff --git a/libbb/inet_cksum.c b/libbb/inet_cksum.c
index 3d5dc3adf..fee8648f3 100644
--- a/libbb/inet_cksum.c
+++ b/libbb/inet_cksum.c
@@ -6,8 +6,10 @@
6 6
7#include "libbb.h" 7#include "libbb.h"
8 8
9uint16_t FAST_FUNC inet_cksum(uint16_t *addr, int nleft) 9uint16_t FAST_FUNC inet_cksum(const void *ptr, int nleft)
10{ 10{
11 const uint16_t *addr = ptr;
12
11 /* 13 /*
12 * Our algorithm is simple, using a 32 bit accumulator, 14 * Our algorithm is simple, using a 32 bit accumulator,
13 * we add sequential 16 bit words to it, and at the end, fold 15 * we add sequential 16 bit words to it, and at the end, fold
diff --git a/libbb/kernel_version.c b/libbb/kernel_version.c
index 7769a091b..6bb32ce5f 100644
--- a/libbb/kernel_version.c
+++ b/libbb/kernel_version.c
@@ -19,15 +19,17 @@ int FAST_FUNC get_linux_version_code(void)
19{ 19{
20 struct utsname name; 20 struct utsname name;
21 char *t; 21 char *t;
22 int i, r; 22 int r;
23 23
24 uname(&name); /* never fails */ 24 uname(&name); /* never fails */
25 t = name.release; 25 t = name.release - 1;
26 r = 0; 26 r = 1;
27 for (i = 0; i < 3; i++) { 27 do {
28 t = strtok(t, "."); 28 r <<= 8;
29 r = r * 256 + (t ? atoi(t) : 0); 29 if (t) {
30 t = NULL; 30 r += atoi(++t);
31 } 31 t = strchr(t, '.');
32 return r; 32 }
33 } while (r < 0x1000000);
34 return r - 0x1000000;
33} 35}
diff --git a/libbb/loop.c b/libbb/loop.c
index ada0c7638..85b2724e5 100644
--- a/libbb/loop.c
+++ b/libbb/loop.c
@@ -102,7 +102,8 @@ int FAST_FUNC get_free_loop(void)
102 * search will re-use an existing loop device already bound to that 102 * search will re-use an existing loop device already bound to that
103 * file/offset if it finds one. 103 * file/offset if it finds one.
104 */ 104 */
105int FAST_FUNC set_loop(char **device, const char *file, unsigned long long offset, unsigned flags) 105int FAST_FUNC set_loop(char **device, const char *file, unsigned long long offset,
106 unsigned long long sizelimit, unsigned flags)
106{ 107{
107 char dev[LOOP_NAMESIZE]; 108 char dev[LOOP_NAMESIZE];
108 char *try; 109 char *try;
@@ -185,6 +186,7 @@ int FAST_FUNC set_loop(char **device, const char *file, unsigned long long offse
185 memset(&loopinfo, 0, sizeof(loopinfo)); 186 memset(&loopinfo, 0, sizeof(loopinfo));
186 safe_strncpy((char *)loopinfo.lo_file_name, file, LO_NAME_SIZE); 187 safe_strncpy((char *)loopinfo.lo_file_name, file, LO_NAME_SIZE);
187 loopinfo.lo_offset = offset; 188 loopinfo.lo_offset = offset;
189 loopinfo.lo_sizelimit = sizelimit;
188 /* 190 /*
189 * Used by mount to set LO_FLAGS_AUTOCLEAR. 191 * Used by mount to set LO_FLAGS_AUTOCLEAR.
190 * LO_FLAGS_READ_ONLY is not set because RO is controlled by open type of the file. 192 * LO_FLAGS_READ_ONLY is not set because RO is controlled by open type of the file.
diff --git a/libbb/ptr_to_globals.c b/libbb/ptr_to_globals.c
index 8ba9cd154..2232c6864 100644
--- a/libbb/ptr_to_globals.c
+++ b/libbb/ptr_to_globals.c
@@ -14,7 +14,7 @@ struct globals;
14 * but here we make it live in R/W memory */ 14 * but here we make it live in R/W memory */
15struct globals *ptr_to_globals; 15struct globals *ptr_to_globals;
16 16
17#ifdef __GLIBC__ 17#ifdef errno
18int *bb_errno; 18int *bb_errno;
19#endif 19#endif
20 20
@@ -27,7 +27,7 @@ int *bb_errno;
27 * on weird architectures, compilers, linkers and so on */ 27 * on weird architectures, compilers, linkers and so on */
28struct globals *const ptr_to_globals __attribute__ ((section (".data"))); 28struct globals *const ptr_to_globals __attribute__ ((section (".data")));
29 29
30#ifdef __GLIBC__ 30#ifdef errno
31int *const bb_errno __attribute__ ((section (".data"))); 31int *const bb_errno __attribute__ ((section (".data")));
32#endif 32#endif
33 33
diff --git a/libbb/recursive_action.c b/libbb/recursive_action.c
index 0831ecc3a..b1c4bfad7 100644
--- a/libbb/recursive_action.c
+++ b/libbb/recursive_action.c
@@ -21,10 +21,9 @@
21 * is so stinking huge. 21 * is so stinking huge.
22 */ 22 */
23 23
24static int FAST_FUNC true_action(const char *fileName UNUSED_PARAM, 24static int FAST_FUNC true_action(struct recursive_state *state UNUSED_PARAM,
25 struct stat *statbuf UNUSED_PARAM, 25 const char *fileName UNUSED_PARAM,
26 void* userData UNUSED_PARAM, 26 struct stat *statbuf UNUSED_PARAM)
27 int depth UNUSED_PARAM)
28{ 27{
29 return TRUE; 28 return TRUE;
30} 29}
@@ -65,12 +64,7 @@ static int FAST_FUNC true_action(const char *fileName UNUSED_PARAM,
65 * 1: stat(statbuf). Calls dirAction and optionally recurse on link to dir. 64 * 1: stat(statbuf). Calls dirAction and optionally recurse on link to dir.
66 */ 65 */
67 66
68int FAST_FUNC recursive_action(const char *fileName, 67static int recursive_action1(recursive_state_t *state, const char *fileName)
69 unsigned flags,
70 int FAST_FUNC (*fileAction)(const char *fileName, struct stat *statbuf, void* userData, int depth),
71 int FAST_FUNC (*dirAction)(const char *fileName, struct stat *statbuf, void* userData, int depth),
72 void* userData,
73 unsigned depth)
74{ 68{
75 struct stat statbuf; 69 struct stat statbuf;
76 unsigned follow; 70 unsigned follow;
@@ -78,24 +72,21 @@ int FAST_FUNC recursive_action(const char *fileName,
78 DIR *dir; 72 DIR *dir;
79 struct dirent *next; 73 struct dirent *next;
80 74
81 if (!fileAction) fileAction = true_action;
82 if (!dirAction) dirAction = true_action;
83
84 follow = ACTION_FOLLOWLINKS; 75 follow = ACTION_FOLLOWLINKS;
85 if (depth == 0) 76 if (state->depth == 0)
86 follow = ACTION_FOLLOWLINKS | ACTION_FOLLOWLINKS_L0; 77 follow = ACTION_FOLLOWLINKS | ACTION_FOLLOWLINKS_L0;
87 follow &= flags; 78 follow &= state->flags;
88 status = (follow ? stat : lstat)(fileName, &statbuf); 79 status = (follow ? stat : lstat)(fileName, &statbuf);
89 if (status < 0) { 80 if (status < 0) {
90#ifdef DEBUG_RECURS_ACTION 81#ifdef DEBUG_RECURS_ACTION
91 bb_error_msg("status=%d flags=%x", status, flags); 82 bb_error_msg("status=%d flags=%x", status, state->flags);
92#endif 83#endif
93 if ((flags & ACTION_DANGLING_OK) 84 if ((state->flags & ACTION_DANGLING_OK)
94 && errno == ENOENT 85 && errno == ENOENT
95 && lstat(fileName, &statbuf) == 0 86 && lstat(fileName, &statbuf) == 0
96 ) { 87 ) {
97 /* Dangling link */ 88 /* Dangling link */
98 return fileAction(fileName, &statbuf, userData, depth); 89 return state->fileAction(state, fileName, &statbuf);
99 } 90 }
100 goto done_nak_warn; 91 goto done_nak_warn;
101 } 92 }
@@ -103,20 +94,20 @@ int FAST_FUNC recursive_action(const char *fileName,
103 /* If S_ISLNK(m), then we know that !S_ISDIR(m). 94 /* If S_ISLNK(m), then we know that !S_ISDIR(m).
104 * Then we can skip checking first part: if it is true, then 95 * Then we can skip checking first part: if it is true, then
105 * (!dir) is also true! */ 96 * (!dir) is also true! */
106 if ( /* (!(flags & ACTION_FOLLOWLINKS) && S_ISLNK(statbuf.st_mode)) || */ 97 if ( /* (!(state->flags & ACTION_FOLLOWLINKS) && S_ISLNK(statbuf.st_mode)) || */
107 !S_ISDIR(statbuf.st_mode) 98 !S_ISDIR(statbuf.st_mode)
108 ) { 99 ) {
109 return fileAction(fileName, &statbuf, userData, depth); 100 return state->fileAction(state, fileName, &statbuf);
110 } 101 }
111 102
112 /* It's a directory (or a link to one, and followLinks is set) */ 103 /* It's a directory (or a link to one, and followLinks is set) */
113 104
114 if (!(flags & ACTION_RECURSE)) { 105 if (!(state->flags & ACTION_RECURSE)) {
115 return dirAction(fileName, &statbuf, userData, depth); 106 return state->dirAction(state, fileName, &statbuf);
116 } 107 }
117 108
118 if (!(flags & ACTION_DEPTHFIRST)) { 109 if (!(state->flags & ACTION_DEPTHFIRST)) {
119 status = dirAction(fileName, &statbuf, userData, depth); 110 status = state->dirAction(state, fileName, &statbuf);
120 if (status == FALSE) 111 if (status == FALSE)
121 goto done_nak_warn; 112 goto done_nak_warn;
122 if (status == SKIP) 113 if (status == SKIP)
@@ -140,11 +131,13 @@ int FAST_FUNC recursive_action(const char *fileName,
140 continue; 131 continue;
141 132
142 /* process every file (NB: ACTION_RECURSE is set in flags) */ 133 /* process every file (NB: ACTION_RECURSE is set in flags) */
143 s = recursive_action(nextFile, flags, fileAction, dirAction, 134 state->depth++;
144 userData, depth + 1); 135 s = recursive_action1(state, nextFile);
145 if (s == FALSE) 136 if (s == FALSE)
146 status = FALSE; 137 status = FALSE;
147 free(nextFile); 138 free(nextFile);
139 state->depth--;
140
148//#define RECURSE_RESULT_ABORT -1 141//#define RECURSE_RESULT_ABORT -1
149// if (s == RECURSE_RESULT_ABORT) { 142// if (s == RECURSE_RESULT_ABORT) {
150// closedir(dir); 143// closedir(dir);
@@ -153,15 +146,36 @@ int FAST_FUNC recursive_action(const char *fileName,
153 } 146 }
154 closedir(dir); 147 closedir(dir);
155 148
156 if (flags & ACTION_DEPTHFIRST) { 149 if (state->flags & ACTION_DEPTHFIRST) {
157 if (!dirAction(fileName, &statbuf, userData, depth)) 150 if (!state->dirAction(state, fileName, &statbuf))
158 goto done_nak_warn; 151 goto done_nak_warn;
159 } 152 }
160 153
161 return status; 154 return status;
162 155
163 done_nak_warn: 156 done_nak_warn:
164 if (!(flags & ACTION_QUIET)) 157 if (!(state->flags & ACTION_QUIET))
165 bb_simple_perror_msg(fileName); 158 bb_simple_perror_msg(fileName);
166 return FALSE; 159 return FALSE;
167} 160}
161
162int FAST_FUNC recursive_action(const char *fileName,
163 unsigned flags,
164 int FAST_FUNC (*fileAction)(struct recursive_state *state, const char *fileName, struct stat* statbuf),
165 int FAST_FUNC (*dirAction)(struct recursive_state *state, const char *fileName, struct stat* statbuf),
166 void *userData)
167{
168 /* Keeping a part of variables of recusive descent in a "state structure"
169 * instead of passing ALL of them down as parameters of recursive_action1()
170 * relieves register pressure, both in recursive_action1()
171 * and in every file/dirAction().
172 */
173 recursive_state_t state;
174 state.flags = flags;
175 state.depth = 0;
176 state.userData = userData;
177 state.fileAction = fileAction ? fileAction : true_action;
178 state.dirAction = dirAction ? dirAction : true_action;
179
180 return recursive_action1(&state, fileName);
181}
diff --git a/libbb/update_passwd.c b/libbb/update_passwd.c
index c605c4c64..7b67f30cd 100644
--- a/libbb/update_passwd.c
+++ b/libbb/update_passwd.c
@@ -18,17 +18,20 @@
18#if ENABLE_SELINUX 18#if ENABLE_SELINUX
19static void check_selinux_update_passwd(const char *username) 19static void check_selinux_update_passwd(const char *username)
20{ 20{
21 security_context_t context; 21 security_context_t seuser;
22 char *seuser; 22 char *p;
23 23
24 if (getuid() != (uid_t)0 || is_selinux_enabled() == 0) 24 if (getuid() != (uid_t)0 || is_selinux_enabled() == 0)
25 return; /* No need to check */ 25 return; /* No need to check */
26 26
27 if (getprevcon_raw(&context) < 0) 27 if (getprevcon_raw(&seuser) < 0)
28 bb_simple_perror_msg_and_die("getprevcon failed"); 28 bb_simple_perror_msg_and_die("getprevcon failed");
29 seuser = strtok(context, ":"); 29
30 if (!seuser) 30 p = strchr(seuser, ':');
31 bb_error_msg_and_die("invalid context '%s'", context); 31 if (!p)
32 bb_error_msg_and_die("invalid context '%s'", seuser);
33 *p = '\0';
34
32 if (strcmp(seuser, username) != 0) { 35 if (strcmp(seuser, username) != 0) {
33 security_class_t tclass; 36 security_class_t tclass;
34 access_vector_t av; 37 access_vector_t av;
diff --git a/mailutils/reformime.c b/mailutils/reformime.c
index 321729e0a..307656a15 100644
--- a/mailutils/reformime.c
+++ b/mailutils/reformime.c
@@ -115,6 +115,7 @@ static int parse(const char *boundary, char **argv)
115 /* Split to tokens */ 115 /* Split to tokens */
116 { 116 {
117 char *s, *p; 117 char *s, *p;
118 char *tokstate;
118 unsigned ntokens; 119 unsigned ntokens;
119 const char *delims = ";=\" \t\n"; 120 const char *delims = ";=\" \t\n";
120 121
@@ -127,13 +128,13 @@ static int parse(const char *boundary, char **argv)
127 } 128 }
128 dbg_error_msg("L:'%s'", p); 129 dbg_error_msg("L:'%s'", p);
129 ntokens = 0; 130 ntokens = 0;
130 s = strtok(s, delims); 131 s = strtok_r(s, delims, &tokstate);
131 while (s) { 132 while (s) {
132 tokens[ntokens] = s; 133 tokens[ntokens] = s;
133 if (ntokens < ARRAY_SIZE(tokens) - 1) 134 if (ntokens < ARRAY_SIZE(tokens) - 1)
134 ntokens++; 135 ntokens++;
135 dbg_error_msg("L[%d]='%s'", ntokens, s); 136 dbg_error_msg("L[%d]='%s'", ntokens, s);
136 s = strtok(NULL, delims); 137 s = strtok_r(NULL, delims, &tokstate);
137 } 138 }
138 tokens[ntokens] = NULL; 139 tokens[ntokens] = NULL;
139 dbg_error_msg("EMPTYLINE, ntokens:%d", ntokens); 140 dbg_error_msg("EMPTYLINE, ntokens:%d", ntokens);
diff --git a/modutils/depmod.c b/modutils/depmod.c
index 318e7cdc7..bb42bbefe 100644
--- a/modutils/depmod.c
+++ b/modutils/depmod.c
@@ -32,10 +32,11 @@
32 * for each depends, look through our list of full paths and emit if found 32 * for each depends, look through our list of full paths and emit if found
33 */ 33 */
34 34
35static int FAST_FUNC parse_module(const char *fname, struct stat *sb UNUSED_PARAM, 35static int FAST_FUNC parse_module(struct recursive_state *state,
36 void *data, int depth UNUSED_PARAM) 36 const char *fname,
37 struct stat *sb UNUSED_PARAM)
37{ 38{
38 module_db *modules = data; 39 module_db *modules = state->userData;
39 char *image, *ptr; 40 char *image, *ptr;
40 module_entry *e; 41 module_entry *e;
41 42
@@ -201,11 +202,12 @@ int depmod_main(int argc UNUSED_PARAM, char **argv)
201 memset(&modules, 0, sizeof(modules)); 202 memset(&modules, 0, sizeof(modules));
202 if (*argv) { 203 if (*argv) {
203 do { 204 do {
204 parse_module(*argv, /*sb (unused):*/ NULL, &modules, 0); 205 recursive_action(*argv, 0 /* no ACTION_RECURSE! */,
206 parse_module, NULL, &modules);
205 } while (*++argv); 207 } while (*++argv);
206 } else { 208 } else {
207 recursive_action(".", ACTION_RECURSE, 209 recursive_action(".", ACTION_RECURSE,
208 parse_module, NULL, &modules, 0); 210 parse_module, NULL, &modules);
209 } 211 }
210 212
211 /* Generate dependency and alias files */ 213 /* Generate dependency and alias files */
diff --git a/modutils/modprobe-small.c b/modutils/modprobe-small.c
index a94b0b9a6..18cfac481 100644
--- a/modutils/modprobe-small.c
+++ b/modutils/modprobe-small.c
@@ -751,8 +751,7 @@ static int process_module(char *name, const char *cmdline_options)
751 ACTION_RECURSE, /* flags */ 751 ACTION_RECURSE, /* flags */
752 fileAction, /* file action */ 752 fileAction, /* file action */
753 NULL, /* dir action */ 753 NULL, /* dir action */
754 name, /* user data */ 754 name /* user data */
755 0 /* depth */
756 ); 755 );
757 dbg1_error_msg("dirscan complete"); 756 dbg1_error_msg("dirscan complete");
758 /* Module was not found, or load failed, or is_remove */ 757 /* Module was not found, or load failed, or is_remove */
diff --git a/modutils/modprobe.c b/modutils/modprobe.c
index 70c45903a..eeeff7609 100644
--- a/modutils/modprobe.c
+++ b/modutils/modprobe.c
@@ -235,10 +235,9 @@ static void add_probe(const char *name)
235 } 235 }
236} 236}
237 237
238static int FAST_FUNC config_file_action(const char *filename, 238static int FAST_FUNC config_file_action(struct recursive_state *state,
239 struct stat *statbuf UNUSED_PARAM, 239 const char *filename,
240 void *userdata UNUSED_PARAM, 240 struct stat *statbuf UNUSED_PARAM)
241 int depth)
242{ 241{
243 char *tokens[3]; 242 char *tokens[3];
244 parser_t *p; 243 parser_t *p;
@@ -255,7 +254,7 @@ static int FAST_FUNC config_file_action(const char *filename,
255 * that we shouldn't recurse into /etc/modprobe.d/dir/ 254 * that we shouldn't recurse into /etc/modprobe.d/dir/
256 * _subdirectories_: 255 * _subdirectories_:
257 */ 256 */
258 if (depth > 1) 257 if (state->depth > 1)
259 return SKIP; /* stop recursing */ 258 return SKIP; /* stop recursing */
260//TODO: instead, can use dirAction in recursive_action() to SKIP dirs 259//TODO: instead, can use dirAction in recursive_action() to SKIP dirs
261//on depth == 1 level. But that's more code... 260//on depth == 1 level. But that's more code...
@@ -264,7 +263,7 @@ static int FAST_FUNC config_file_action(const char *filename,
264 * depth==0: read_config("modules.{symbols,alias}") must work, 263 * depth==0: read_config("modules.{symbols,alias}") must work,
265 * "include FILE_NOT_ENDING_IN_CONF" must work too. 264 * "include FILE_NOT_ENDING_IN_CONF" must work too.
266 */ 265 */
267 if (depth != 0) { 266 if (state->depth != 0) {
268 if (!is_suffixed_with(base, ".conf")) 267 if (!is_suffixed_with(base, ".conf"))
269 goto error; 268 goto error;
270 } 269 }
@@ -329,8 +328,7 @@ static int FAST_FUNC config_file_action(const char *filename,
329static int read_config(const char *path) 328static int read_config(const char *path)
330{ 329{
331 return recursive_action(path, ACTION_RECURSE | ACTION_QUIET, 330 return recursive_action(path, ACTION_RECURSE | ACTION_QUIET,
332 config_file_action, NULL, NULL, 331 config_file_action, NULL, NULL);
333 /*depth:*/ 0);
334} 332}
335 333
336static const char *humanly_readable_name(struct module_entry *m) 334static const char *humanly_readable_name(struct module_entry *m)
diff --git a/networking/httpd.c b/networking/httpd.c
index 7684ca4e7..daa3ca1d0 100644
--- a/networking/httpd.c
+++ b/networking/httpd.c
@@ -1038,6 +1038,12 @@ static char *encodeString(const char *string)
1038 */ 1038 */
1039static void decodeBase64(char *Data) 1039static void decodeBase64(char *Data)
1040{ 1040{
1041# if ENABLE_BASE64 || ENABLE_UUDECODE
1042 /* Call decode_base64() from uuencode.c */
1043 char *eptr = Data;
1044 decode_base64(&eptr, Data);
1045 *eptr = '\0';
1046# else
1041 const unsigned char *in = (const unsigned char *)Data; 1047 const unsigned char *in = (const unsigned char *)Data;
1042 /* The decoded size will be at most 3/4 the size of the encoded */ 1048 /* The decoded size will be at most 3/4 the size of the encoded */
1043 unsigned ch = 0; 1049 unsigned ch = 0;
@@ -1071,6 +1077,7 @@ static void decodeBase64(char *Data)
1071 } 1077 }
1072 } 1078 }
1073 *Data = '\0'; 1079 *Data = '\0';
1080# endif
1074} 1081}
1075#endif 1082#endif
1076 1083
diff --git a/networking/ip.c b/networking/ip.c
index 7d3faf7f8..33bea5f49 100644
--- a/networking/ip.c
+++ b/networking/ip.c
@@ -252,7 +252,7 @@
252//usage:#define iprule_trivial_usage 252//usage:#define iprule_trivial_usage
253//usage: "[list] | add|del SELECTOR ACTION" 253//usage: "[list] | add|del SELECTOR ACTION"
254//usage:#define iprule_full_usage "\n\n" 254//usage:#define iprule_full_usage "\n\n"
255//usage: " SELECTOR := [from PREFIX] [to PREFIX] [tos TOS] [fwmark FWMARK]\n" 255//usage: " SELECTOR := [from PREFIX] [to PREFIX] [tos TOS] [fwmark FWMARK[/MASK]]\n"
256//usage: " [dev IFACE] [pref NUMBER]\n" 256//usage: " [dev IFACE] [pref NUMBER]\n"
257//usage: " ACTION := [table TABLE_ID] [nat ADDR]\n" 257//usage: " ACTION := [table TABLE_ID] [nat ADDR]\n"
258//usage: " [prohibit|reject|unreachable]\n" 258//usage: " [prohibit|reject|unreachable]\n"
diff --git a/networking/libiproute/iprule.c b/networking/libiproute/iprule.c
index 0ce0dfeef..50acfe4e7 100644
--- a/networking/libiproute/iprule.c
+++ b/networking/libiproute/iprule.c
@@ -17,8 +17,10 @@
17#include <arpa/inet.h> 17#include <arpa/inet.h>
18 18
19/* from <linux/fib_rules.h>: */ 19/* from <linux/fib_rules.h>: */
20#define FRA_SUPPRESS_IFGROUP 13 20#define FRA_FWMARK 10
21#define FRA_SUPPRESS_PREFIXLEN 14 21#define FRA_SUPPRESS_IFGROUP 13
22#define FRA_SUPPRESS_PREFIXLEN 14
23#define FRA_FWMASK 16
22 24
23#include "ip_common.h" /* #include "libbb.h" is inside */ 25#include "ip_common.h" /* #include "libbb.h" is inside */
24#include "rt_names.h" 26#include "rt_names.h"
@@ -117,8 +119,18 @@ static int FAST_FUNC print_rule(const struct sockaddr_nl *who UNUSED_PARAM,
117 if (r->rtm_tos) { 119 if (r->rtm_tos) {
118 printf("tos %s ", rtnl_dsfield_n2a(r->rtm_tos)); 120 printf("tos %s ", rtnl_dsfield_n2a(r->rtm_tos));
119 } 121 }
120 if (tb[RTA_PROTOINFO]) { 122
121 printf("fwmark %#x ", *(uint32_t*)RTA_DATA(tb[RTA_PROTOINFO])); 123 if (tb[FRA_FWMARK] || tb[FRA_FWMASK]) {
124 uint32_t mark = 0, mask = 0;
125
126 if (tb[FRA_FWMARK])
127 mark = *(uint32_t*)RTA_DATA(tb[FRA_FWMARK]);
128 if (tb[FRA_FWMASK]
129 && (mask = *(uint32_t*)RTA_DATA(tb[FRA_FWMASK])) != 0xFFFFFFFF
130 )
131 printf("fwmark %#x/%#x ", mark, mask);
132 else
133 printf("fwmark %#x ", mark);
122 } 134 }
123 135
124 if (tb[RTA_IIF]) { 136 if (tb[RTA_IIF]) {
@@ -257,10 +269,18 @@ static int iprule_modify(int cmd, char **argv)
257 invarg_1_to_2(*argv, "TOS"); 269 invarg_1_to_2(*argv, "TOS");
258 req.r.rtm_tos = tos; 270 req.r.rtm_tos = tos;
259 } else if (key == ARG_fwmark) { 271 } else if (key == ARG_fwmark) {
260 uint32_t fwmark; 272 char *slash;
273 uint32_t fwmark, fwmask;
261 NEXT_ARG(); 274 NEXT_ARG();
275 slash = strchr(*argv, '/');
276 if (slash)
277 *slash = '\0';
262 fwmark = get_u32(*argv, keyword_fwmark); 278 fwmark = get_u32(*argv, keyword_fwmark);
263 addattr32(&req.n, sizeof(req), RTA_PROTOINFO, fwmark); 279 addattr32(&req.n, sizeof(req), FRA_FWMARK, fwmark);
280 if (slash) {
281 fwmask = get_u32(slash + 1, "fwmask");
282 addattr32(&req.n, sizeof(req), FRA_FWMASK, fwmask);
283 }
264 } else if (key == ARG_realms) { 284 } else if (key == ARG_realms) {
265 uint32_t realm; 285 uint32_t realm;
266 NEXT_ARG(); 286 NEXT_ARG();
diff --git a/networking/netstat.c b/networking/netstat.c
index 936610f89..3ab7b0d21 100644
--- a/networking/netstat.c
+++ b/networking/netstat.c
@@ -272,10 +272,9 @@ static long extract_socket_inode(const char *lname)
272 return inode; 272 return inode;
273} 273}
274 274
275static int FAST_FUNC add_to_prg_cache_if_socket(const char *fileName, 275static int FAST_FUNC add_to_prg_cache_if_socket(struct recursive_state *state,
276 struct stat *statbuf UNUSED_PARAM, 276 const char *fileName,
277 void *pid_slash_progname, 277 struct stat *statbuf UNUSED_PARAM)
278 int depth UNUSED_PARAM)
279{ 278{
280 char *linkname; 279 char *linkname;
281 long inode; 280 long inode;
@@ -284,16 +283,17 @@ static int FAST_FUNC add_to_prg_cache_if_socket(const char *fileName,
284 if (linkname != NULL) { 283 if (linkname != NULL) {
285 inode = extract_socket_inode(linkname); 284 inode = extract_socket_inode(linkname);
286 free(linkname); 285 free(linkname);
287 if (inode >= 0) 286 if (inode >= 0) {
288 prg_cache_add(inode, (char *)pid_slash_progname); 287 char *pid_slash_progname = state->userData;
288 prg_cache_add(inode, pid_slash_progname);
289 }
289 } 290 }
290 return TRUE; 291 return TRUE;
291} 292}
292 293
293static int FAST_FUNC dir_act(const char *fileName, 294static int FAST_FUNC dir_act(struct recursive_state *state,
294 struct stat *statbuf UNUSED_PARAM, 295 const char *fileName,
295 void *userData UNUSED_PARAM, 296 struct stat *statbuf UNUSED_PARAM)
296 int depth)
297{ 297{
298 const char *pid; 298 const char *pid;
299 char *pid_slash_progname; 299 char *pid_slash_progname;
@@ -301,7 +301,7 @@ static int FAST_FUNC dir_act(const char *fileName,
301 char cmdline_buf[512]; 301 char cmdline_buf[512];
302 int n, len; 302 int n, len;
303 303
304 if (depth == 0) /* "/proc" itself */ 304 if (state->depth == 0) /* "/proc" itself */
305 return TRUE; /* continue looking one level below /proc */ 305 return TRUE; /* continue looking one level below /proc */
306 306
307 pid = fileName + sizeof("/proc/")-1; /* point after "/proc/" */ 307 pid = fileName + sizeof("/proc/")-1; /* point after "/proc/" */
@@ -321,8 +321,8 @@ static int FAST_FUNC dir_act(const char *fileName,
321 ACTION_RECURSE | ACTION_QUIET, 321 ACTION_RECURSE | ACTION_QUIET,
322 add_to_prg_cache_if_socket, 322 add_to_prg_cache_if_socket,
323 NULL, 323 NULL,
324 (void *)pid_slash_progname, 324 (void *)pid_slash_progname
325 0); 325 );
326 free(pid_slash_progname); 326 free(pid_slash_progname);
327 327
328 if (!n) 328 if (!n)
@@ -337,7 +337,7 @@ static void prg_cache_load(void)
337 337
338 prg_cache_loaded = 1; 338 prg_cache_loaded = 1;
339 load_ok = recursive_action("/proc", ACTION_RECURSE | ACTION_QUIET, 339 load_ok = recursive_action("/proc", ACTION_RECURSE | ACTION_QUIET,
340 NULL, dir_act, NULL, 0); 340 NULL, dir_act, NULL);
341 if (load_ok) 341 if (load_ok)
342 return; 342 return;
343 343
diff --git a/networking/nslookup.c b/networking/nslookup.c
index c43e60558..759de5c83 100644
--- a/networking/nslookup.c
+++ b/networking/nslookup.c
@@ -703,12 +703,13 @@ static void parse_resolvconf(void)
703 703
704 while (fgets(line, sizeof(line), resolv)) { 704 while (fgets(line, sizeof(line), resolv)) {
705 char *p, *arg; 705 char *p, *arg;
706 char *tokstate;
706 707
707 p = strtok(line, " \t\n"); 708 p = strtok_r(line, " \t\n", &tokstate);
708 if (!p) 709 if (!p)
709 continue; 710 continue;
710 dbg("resolv_key:'%s'\n", p); 711 dbg("resolv_key:'%s'\n", p);
711 arg = strtok(NULL, "\n"); 712 arg = strtok_r(NULL, "\n", &tokstate);
712 dbg("resolv_arg:'%s'\n", arg); 713 dbg("resolv_arg:'%s'\n", arg);
713 if (!arg) 714 if (!arg)
714 continue; 715 continue;
diff --git a/networking/ntpd.c b/networking/ntpd.c
index d721fe80c..44e711232 100644
--- a/networking/ntpd.c
+++ b/networking/ntpd.c
@@ -2025,6 +2025,15 @@ recv_and_process_peer_pkt(peer_t *p)
2025 2025
2026 offset = 0; 2026 offset = 0;
2027 2027
2028 /* The below can happen as follows:
2029 * = we receive two peer rsponses at once.
2030 * = recv_and_process_peer_pkt(PEER1) -> update_local_clock()
2031 * -> step_time() and it closes all other fds, sets all ->fd to -1.
2032 * = recv_and_process_peer_pkt(PEER2) sees PEER2->fd == -1
2033 */
2034 if (p->p_fd < 0)
2035 return;
2036
2028 /* We can recvfrom here and check from.IP, but some multihomed 2037 /* We can recvfrom here and check from.IP, but some multihomed
2029 * ntp servers reply from their *other IP*. 2038 * ntp servers reply from their *other IP*.
2030 * TODO: maybe we should check at least what we can: from.port == 123? 2039 * TODO: maybe we should check at least what we can: from.port == 123?
diff --git a/networking/ping.c b/networking/ping.c
index 47b6ab1b2..5f7e5b9b5 100644
--- a/networking/ping.c
+++ b/networking/ping.c
@@ -217,7 +217,7 @@ static void ping4(len_and_sockaddr *lsa)
217 /*memset(pkt, 0, sizeof(G.packet)); already is */ 217 /*memset(pkt, 0, sizeof(G.packet)); already is */
218 pkt->icmp_type = ICMP_ECHO; 218 pkt->icmp_type = ICMP_ECHO;
219 pkt->icmp_id = G.myid; 219 pkt->icmp_id = G.myid;
220 pkt->icmp_cksum = inet_cksum((uint16_t *) pkt, sizeof(G.packet)); 220 pkt->icmp_cksum = inet_cksum(pkt, sizeof(G.packet));
221 221
222 xsendto(pingsock, G.packet, DEFDATALEN + ICMP_MINLEN, &lsa->u.sa, lsa->len); 222 xsendto(pingsock, G.packet, DEFDATALEN + ICMP_MINLEN, &lsa->u.sa, lsa->len);
223 223
@@ -529,7 +529,7 @@ static void sendping4(int junk UNUSED_PARAM)
529 /* No hton: we'll read it back on the same machine */ 529 /* No hton: we'll read it back on the same machine */
530 *(uint32_t*)&pkt->icmp_dun = G.cur_us = monotonic_us(); 530 *(uint32_t*)&pkt->icmp_dun = G.cur_us = monotonic_us();
531 531
532 pkt->icmp_cksum = inet_cksum((uint16_t *) pkt, datalen + ICMP_MINLEN); 532 pkt->icmp_cksum = inet_cksum(pkt, datalen + ICMP_MINLEN);
533 533
534 sendping_tail(sendping4, ICMP_MINLEN); 534 sendping_tail(sendping4, ICMP_MINLEN);
535} 535}
diff --git a/networking/traceroute.c b/networking/traceroute.c
index 5068f654b..1c4dc3e4a 100644
--- a/networking/traceroute.c
+++ b/networking/traceroute.c
@@ -468,7 +468,7 @@ send_probe(int seq, int ttl)
468 /* Always calculate checksum for icmp packets */ 468 /* Always calculate checksum for icmp packets */
469 outicmp->icmp_cksum = 0; 469 outicmp->icmp_cksum = 0;
470 outicmp->icmp_cksum = inet_cksum( 470 outicmp->icmp_cksum = inet_cksum(
471 (uint16_t *)outicmp, 471 outicmp,
472 ((char*)outip + packlen) - (char*)outicmp 472 ((char*)outip + packlen) - (char*)outicmp
473 ); 473 );
474 if (outicmp->icmp_cksum == 0) 474 if (outicmp->icmp_cksum == 0)
diff --git a/networking/udhcp/common.c b/networking/udhcp/common.c
index 20d843bab..4bc719001 100644
--- a/networking/udhcp/common.c
+++ b/networking/udhcp/common.c
@@ -526,7 +526,7 @@ int FAST_FUNC udhcp_str2optset(const char *const_str, void *arg,
526 526
527 /* Cheat, the only *const* str possible is "" */ 527 /* Cheat, the only *const* str possible is "" */
528 str = (char *) const_str; 528 str = (char *) const_str;
529 opt = strtok(str, " \t=:"); 529 opt = strtok_r(str, " \t=:", &str);
530 if (!opt) 530 if (!opt)
531 return 0; 531 return 0;
532 532
@@ -550,10 +550,10 @@ int FAST_FUNC udhcp_str2optset(const char *const_str, void *arg,
550 char *val; 550 char *val;
551 551
552 if (optflag->flags == OPTION_BIN) { 552 if (optflag->flags == OPTION_BIN) {
553 val = strtok(NULL, ""); /* do not split "'q w e'" */ 553 val = strtok_r(NULL, "", &str); /* do not split "'q w e'" */
554 if (val) trim(val); 554 if (val) trim(val);
555 } else 555 } else
556 val = strtok(NULL, ", \t"); 556 val = strtok_r(NULL, ", \t", &str);
557 if (!val) 557 if (!val)
558 break; 558 break;
559 559
@@ -567,7 +567,7 @@ int FAST_FUNC udhcp_str2optset(const char *const_str, void *arg,
567 break; 567 break;
568 case OPTION_IP_PAIR: 568 case OPTION_IP_PAIR:
569 retval = udhcp_str2nip(val, buffer); 569 retval = udhcp_str2nip(val, buffer);
570 val = strtok(NULL, ", \t/-"); 570 val = strtok_r(NULL, ", \t/-", &str);
571 if (!val) 571 if (!val)
572 retval = 0; 572 retval = 0;
573 if (retval) 573 if (retval)
@@ -631,7 +631,7 @@ int FAST_FUNC udhcp_str2optset(const char *const_str, void *arg,
631 *slash = '\0'; 631 *slash = '\0';
632 retval = udhcp_str2nip(val, buffer + 1); 632 retval = udhcp_str2nip(val, buffer + 1);
633 buffer[0] = mask = bb_strtou(slash + 1, NULL, 10); 633 buffer[0] = mask = bb_strtou(slash + 1, NULL, 10);
634 val = strtok(NULL, ", \t/-"); 634 val = strtok_r(NULL, ", \t/-", &str);
635 if (!val || mask > 32 || errno) 635 if (!val || mask > 32 || errno)
636 retval = 0; 636 retval = 0;
637 if (retval) { 637 if (retval) {
diff --git a/networking/udhcp/common.h b/networking/udhcp/common.h
index 81c1dcbdc..3cbd2d3c8 100644
--- a/networking/udhcp/common.h
+++ b/networking/udhcp/common.h
@@ -46,7 +46,7 @@ struct dhcp_packet {
46 uint8_t file[128]; /* boot file name (ASCIZ) */ 46 uint8_t file[128]; /* boot file name (ASCIZ) */
47 uint32_t cookie; /* fixed first four option bytes (99,130,83,99 dec) */ 47 uint32_t cookie; /* fixed first four option bytes (99,130,83,99 dec) */
48 uint8_t options[DHCP_OPTIONS_BUFSIZE + CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS]; 48 uint8_t options[DHCP_OPTIONS_BUFSIZE + CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS];
49} PACKED; 49};
50#define DHCP_PKT_SNAME_LEN 64 50#define DHCP_PKT_SNAME_LEN 64
51#define DHCP_PKT_FILE_LEN 128 51#define DHCP_PKT_FILE_LEN 128
52#define DHCP_PKT_SNAME_LEN_STR "64" 52#define DHCP_PKT_SNAME_LEN_STR "64"
@@ -56,12 +56,12 @@ struct ip_udp_dhcp_packet {
56 struct iphdr ip; 56 struct iphdr ip;
57 struct udphdr udp; 57 struct udphdr udp;
58 struct dhcp_packet data; 58 struct dhcp_packet data;
59} PACKED; 59};
60 60
61struct udp_dhcp_packet { 61struct udp_dhcp_packet {
62 struct udphdr udp; 62 struct udphdr udp;
63 struct dhcp_packet data; 63 struct dhcp_packet data;
64} PACKED; 64};
65 65
66enum { 66enum {
67 IP_UDP_DHCP_SIZE = sizeof(struct ip_udp_dhcp_packet) - CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS, 67 IP_UDP_DHCP_SIZE = sizeof(struct ip_udp_dhcp_packet) - CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS,
diff --git a/networking/udhcp/d6_dhcpc.c b/networking/udhcp/d6_dhcpc.c
index fc2d672b7..ac8af91d3 100644
--- a/networking/udhcp/d6_dhcpc.c
+++ b/networking/udhcp/d6_dhcpc.c
@@ -947,7 +947,7 @@ static NOINLINE int d6_recv_raw_packet(struct in6_addr *peer_ipv6, struct d6_pac
947// packet.ip.tot_len = packet.udp.len; /* yes, this is needed */ 947// packet.ip.tot_len = packet.udp.len; /* yes, this is needed */
948// check = packet.udp.check; 948// check = packet.udp.check;
949// packet.udp.check = 0; 949// packet.udp.check = 0;
950// if (check && check != inet_cksum((uint16_t *)&packet, bytes)) { 950// if (check && check != inet_cksum(&packet, bytes)) {
951// log1("packet with bad UDP checksum received, ignoring"); 951// log1("packet with bad UDP checksum received, ignoring");
952// return -2; 952// return -2;
953// } 953// }
diff --git a/networking/udhcp/d6_packet.c b/networking/udhcp/d6_packet.c
index 446497e15..167a813e3 100644
--- a/networking/udhcp/d6_packet.c
+++ b/networking/udhcp/d6_packet.c
@@ -103,7 +103,7 @@ int FAST_FUNC d6_send_raw_packet(
103 */ 103 */
104 packet.ip6.ip6_hlim = IPPROTO_UDP; 104 packet.ip6.ip6_hlim = IPPROTO_UDP;
105 packet.udp.check = inet_cksum( 105 packet.udp.check = inet_cksum(
106 (uint16_t *)&packet + 2, 106 (uint8_t *)&packet + 4,
107 offsetof(struct ip6_udp_d6_packet, data) - 4 + d6_pkt_size 107 offsetof(struct ip6_udp_d6_packet, data) - 4 + d6_pkt_size
108 ); 108 );
109 /* fix 'hop limit' and 'next header' after UDP checksumming */ 109 /* fix 'hop limit' and 'next header' after UDP checksumming */
diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c
index e13eb3f9f..66aa38c20 100644
--- a/networking/udhcp/dhcpc.c
+++ b/networking/udhcp/dhcpc.c
@@ -935,7 +935,7 @@ static NOINLINE int udhcp_recv_raw_packet(struct dhcp_packet *dhcp_pkt, int fd)
935 /* verify IP checksum */ 935 /* verify IP checksum */
936 check = packet.ip.check; 936 check = packet.ip.check;
937 packet.ip.check = 0; 937 packet.ip.check = 0;
938 if (check != inet_cksum((uint16_t *)&packet.ip, sizeof(packet.ip))) { 938 if (check != inet_cksum(&packet.ip, sizeof(packet.ip))) {
939 log1s("bad IP header checksum, ignoring"); 939 log1s("bad IP header checksum, ignoring");
940 return -2; 940 return -2;
941 } 941 }
@@ -960,7 +960,7 @@ static NOINLINE int udhcp_recv_raw_packet(struct dhcp_packet *dhcp_pkt, int fd)
960 packet.ip.tot_len = packet.udp.len; /* yes, this is needed */ 960 packet.ip.tot_len = packet.udp.len; /* yes, this is needed */
961 check = packet.udp.check; 961 check = packet.udp.check;
962 packet.udp.check = 0; 962 packet.udp.check = 0;
963 if (check && check != inet_cksum((uint16_t *)&packet, bytes)) { 963 if (check && check != inet_cksum(&packet, bytes)) {
964 log1s("packet with bad UDP checksum received, ignoring"); 964 log1s("packet with bad UDP checksum received, ignoring");
965 return -2; 965 return -2;
966 } 966 }
diff --git a/networking/udhcp/packet.c b/networking/udhcp/packet.c
index 6d4375237..51374646d 100644
--- a/networking/udhcp/packet.c
+++ b/networking/udhcp/packet.c
@@ -164,14 +164,14 @@ int FAST_FUNC udhcp_send_raw_packet(struct dhcp_packet *dhcp_pkt,
164 packet.udp.len = htons(UDP_DHCP_SIZE - padding); 164 packet.udp.len = htons(UDP_DHCP_SIZE - padding);
165 /* for UDP checksumming, ip.len is set to UDP packet len */ 165 /* for UDP checksumming, ip.len is set to UDP packet len */
166 packet.ip.tot_len = packet.udp.len; 166 packet.ip.tot_len = packet.udp.len;
167 packet.udp.check = inet_cksum((uint16_t *)&packet, 167 packet.udp.check = inet_cksum(&packet,
168 IP_UDP_DHCP_SIZE - padding); 168 IP_UDP_DHCP_SIZE - padding);
169 /* but for sending, it is set to IP packet len */ 169 /* but for sending, it is set to IP packet len */
170 packet.ip.tot_len = htons(IP_UDP_DHCP_SIZE - padding); 170 packet.ip.tot_len = htons(IP_UDP_DHCP_SIZE - padding);
171 packet.ip.ihl = sizeof(packet.ip) >> 2; 171 packet.ip.ihl = sizeof(packet.ip) >> 2;
172 packet.ip.version = IPVERSION; 172 packet.ip.version = IPVERSION;
173 packet.ip.ttl = IPDEFTTL; 173 packet.ip.ttl = IPDEFTTL;
174 packet.ip.check = inet_cksum((uint16_t *)&packet.ip, sizeof(packet.ip)); 174 packet.ip.check = inet_cksum(&packet.ip, sizeof(packet.ip));
175 175
176 udhcp_dump_packet(dhcp_pkt); 176 udhcp_dump_packet(dhcp_pkt);
177 result = sendto(fd, &packet, IP_UDP_DHCP_SIZE - padding, /*flags:*/ 0, 177 result = sendto(fd, &packet, IP_UDP_DHCP_SIZE - padding, /*flags:*/ 0,
diff --git a/procps/mpstat.c b/procps/mpstat.c
index 3e07edc5c..883c4d52d 100644
--- a/procps/mpstat.c
+++ b/procps/mpstat.c
@@ -923,7 +923,7 @@ int mpstat_main(int argc UNUSED_PARAM, char **argv)
923 char *t; 923 char *t;
924 G.p_option = 1; 924 G.p_option = 1;
925 925
926 for (t = strtok(opt_set_cpu, ","); t; t = strtok(NULL, ",")) { 926 for (t = strtok_r(opt_set_cpu, ",", &opt_set_cpu); t; t = strtok_r(NULL, ",", &opt_set_cpu)) {
927 if (strcmp(t, "ALL") == 0) { 927 if (strcmp(t, "ALL") == 0) {
928 /* Select every CPU */ 928 /* Select every CPU */
929 memset(G.cpu_bitmap, 0xff, G.cpu_bitmap_len); 929 memset(G.cpu_bitmap, 0xff, G.cpu_bitmap_len);
diff --git a/selinux/chcon.c b/selinux/chcon.c
index afe7f713d..9b13679b8 100644
--- a/selinux/chcon.c
+++ b/selinux/chcon.c
@@ -62,11 +62,9 @@ static char *type = NULL;
62static char *range = NULL; 62static char *range = NULL;
63static char *specified_context = NULL; 63static char *specified_context = NULL;
64 64
65static int FAST_FUNC change_filedir_context( 65static int FAST_FUNC change_filedir_context(struct recursive_state *state UNUSED_PARAM,
66 const char *fname, 66 const char *fname,
67 struct stat *stbuf UNUSED_PARAM, 67 struct stat *stbuf UNUSED_PARAM)
68 void *userData UNUSED_PARAM,
69 int depth UNUSED_PARAM)
70{ 68{
71 context_t context = NULL; 69 context_t context = NULL;
72 security_context_t file_context = NULL; 70 security_context_t file_context = NULL;
@@ -207,7 +205,7 @@ int chcon_main(int argc UNUSED_PARAM, char **argv)
207 ((option_mask32 & OPT_RECURSIVE) ? ACTION_RECURSE : 0), 205 ((option_mask32 & OPT_RECURSIVE) ? ACTION_RECURSE : 0),
208 change_filedir_context, 206 change_filedir_context,
209 change_filedir_context, 207 change_filedir_context,
210 NULL, 0) != TRUE) 208 NULL) != TRUE)
211 errors = 1; 209 errors = 1;
212 } 210 }
213 return errors; 211 return errors;
diff --git a/selinux/setfiles.c b/selinux/setfiles.c
index 55bfb4d02..a617b95d8 100644
--- a/selinux/setfiles.c
+++ b/selinux/setfiles.c
@@ -463,11 +463,9 @@ static int restore(const char *file)
463 * This function is called by recursive_action on each file during 463 * This function is called by recursive_action on each file during
464 * the directory traversal. 464 * the directory traversal.
465 */ 465 */
466static int FAST_FUNC apply_spec( 466static int FAST_FUNC apply_spec(struct recursive_state *state UNUSED_PARAM,
467 const char *file, 467 const char *file,
468 struct stat *sb, 468 struct stat *sb)
469 void *userData UNUSED_PARAM,
470 int depth UNUSED_PARAM)
471{ 469{
472 if (!follow_mounts) { 470 if (!follow_mounts) {
473 /* setfiles does not process across different mount points */ 471 /* setfiles does not process across different mount points */
@@ -535,7 +533,7 @@ static int process_one(char *name)
535 ACTION_RECURSE, 533 ACTION_RECURSE,
536 apply_spec, 534 apply_spec,
537 apply_spec, 535 apply_spec,
538 NULL, 0) != TRUE 536 NULL) != TRUE
539 ) { 537 ) {
540 bb_error_msg("error while labeling %s", name); 538 bb_error_msg("error while labeling %s", name);
541 goto err; 539 goto err;
diff --git a/shell/ash.c b/shell/ash.c
index 1f57c2d7e..b8f75af05 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -240,17 +240,17 @@
240#define IF_BASH_SUBSTR IF_ASH_BASH_COMPAT 240#define IF_BASH_SUBSTR IF_ASH_BASH_COMPAT
241/* BASH_TEST2: [[ EXPR ]] 241/* BASH_TEST2: [[ EXPR ]]
242 * Status of [[ support: 242 * Status of [[ support:
243 * We replace && and || with -a and -o 243 * && and || work as they should
244 * = is glob match operator, not equality operator: STR = GLOB
245 * == same as =
246 * =~ is regex match operator: STR =~ REGEX
244 * TODO: 247 * TODO:
245 * singleword+noglob expansion: 248 * singleword+noglob expansion:
246 * v='a b'; [[ $v = 'a b' ]]; echo 0:$? 249 * v='a b'; [[ $v = 'a b' ]]; echo 0:$?
247 * [[ /bin/n* ]]; echo 0:$? 250 * [[ /bin/n* ]]; echo 0:$?
248 * -a/-o are not AND/OR ops! (they are just strings)
249 * quoting needs to be considered (-f is an operator, "-f" and ""-f are not; etc) 251 * quoting needs to be considered (-f is an operator, "-f" and ""-f are not; etc)
250 * = is glob match operator, not equality operator: STR = GLOB 252 * ( ) < > should not have special meaning (IOW: should not require quoting)
251 * (in GLOB, quoting is significant on char-by-char basis: a*cd"*") 253 * in word = GLOB, quoting should be significant on char-by-char basis: a*cd"*"
252 * == same as =
253 * add =~ regex match operator: STR =~ REGEX
254 */ 254 */
255#define BASH_TEST2 (ENABLE_ASH_BASH_COMPAT * ENABLE_ASH_TEST) 255#define BASH_TEST2 (ENABLE_ASH_BASH_COMPAT * ENABLE_ASH_TEST)
256#define BASH_SOURCE ENABLE_ASH_BASH_COMPAT 256#define BASH_SOURCE ENABLE_ASH_BASH_COMPAT
@@ -3173,7 +3173,7 @@ updatepwd(const char *dir)
3173 lim++; 3173 lim++;
3174 } 3174 }
3175 } 3175 }
3176 p = strtok(cdcomppath, "/"); 3176 p = strtok_r(cdcomppath, "/", &cdcomppath);
3177 while (p) { 3177 while (p) {
3178 switch (*p) { 3178 switch (*p) {
3179 case '.': 3179 case '.':
@@ -3192,7 +3192,7 @@ updatepwd(const char *dir)
3192 new = stack_putstr(p, new); 3192 new = stack_putstr(p, new);
3193 USTPUTC('/', new); 3193 USTPUTC('/', new);
3194 } 3194 }
3195 p = strtok(NULL, "/"); 3195 p = strtok_r(NULL, "/", &cdcomppath);
3196 } 3196 }
3197 if (new > lim) 3197 if (new > lim)
3198 STUNPUTC(new); 3198 STUNPUTC(new);
@@ -4780,61 +4780,54 @@ waitpid_child(int *status, int wait_flags)
4780 return pid; 4780 return pid;
4781} 4781}
4782#define waitpid(p, s, f) waitpid_child(s, f) 4782#define waitpid(p, s, f) waitpid_child(s, f)
4783#define wait_block_or_sig(s) waitpid_child(s, 0) 4783#endif
4784 4784
4785#else 4785#define DOWAIT_NONBLOCK 0
4786#define DOWAIT_BLOCK 1
4787#define DOWAIT_BLOCK_OR_SIG 2
4788#if BASH_WAIT_N
4789# define DOWAIT_JOBSTATUS 0x10 /* OR this to get job's exitstatus instead of pid */
4790#endif
4786 4791
4787static int 4792static int
4788wait_block_or_sig(int *status) 4793waitproc(int block, int *status)
4789{ 4794{
4790 int pid; 4795#if !ENABLE_PLATFORM_MINGW32
4796 sigset_t oldmask;
4797 int flags = block == DOWAIT_BLOCK ? 0 : WNOHANG;
4798 int err;
4791 4799
4792 do { 4800#if JOBS
4793 sigset_t mask; 4801 if (doing_jobctl)
4802 flags |= WUNTRACED;
4803#endif
4794 4804
4795 /* Poll all children for changes in their state */ 4805 do {
4796 got_sigchld = 0; 4806 got_sigchld = 0;
4797 /* if job control is active, accept stopped processes too */ 4807 do
4798 pid = waitpid(-1, status, doing_jobctl ? (WNOHANG|WUNTRACED) : WNOHANG); 4808 err = waitpid(-1, status, flags);
4799 if (pid != 0) 4809 while (err < 0 && errno == EINTR);
4800 break; /* Error (e.g. EINTR, ECHILD) or pid */
4801 4810
4802 /* Children exist, but none are ready. Sleep until interesting signal */ 4811 if (err || (err = -!block))
4803#if 1 4812 break;
4804 sigfillset(&mask); 4813
4805 sigprocmask2(SIG_SETMASK, &mask); /* mask is updated */ 4814 sigfillset(&oldmask);
4806 while (!got_sigchld && !pending_sig) { 4815 sigprocmask2(SIG_SETMASK, &oldmask); /* mask is updated */
4807 sigsuspend(&mask);
4808 /* ^^^ add "sigdelset(&mask, SIGCHLD);" before sigsuspend
4809 * to make sure SIGCHLD is not masked off?
4810 * It was reported that this:
4811 * fn() { : | return; }
4812 * shopt -s lastpipe
4813 * fn
4814 * exec ash SCRIPT
4815 * under bash 4.4.23 runs SCRIPT with SIGCHLD masked,
4816 * making "wait" commands in SCRIPT block forever.
4817 */
4818 }
4819 sigprocmask(SIG_SETMASK, &mask, NULL);
4820#else /* unsafe: a signal can set pending_sig after check, but before pause() */
4821 while (!got_sigchld && !pending_sig) 4816 while (!got_sigchld && !pending_sig)
4822 pause(); 4817 sigsuspend(&oldmask);
4823#endif 4818 sigprocmask(SIG_SETMASK, &oldmask, NULL);
4819 //simpler, but unsafe: a signal can set pending_sig after check, but before pause():
4820 //while (!got_sigchld && !pending_sig)
4821 // pause();
4824 4822
4825 /* If it was SIGCHLD, poll children again */
4826 } while (got_sigchld); 4823 } while (got_sigchld);
4827 4824
4828 return pid; 4825 return err;
4829} 4826#else
4830#endif 4827 int flags = block == DOWAIT_BLOCK ? 0 : WNOHANG;
4831 4828 return waitpid(-1, status, flags);
4832#define DOWAIT_NONBLOCK 0
4833#define DOWAIT_BLOCK 1
4834#define DOWAIT_BLOCK_OR_SIG 2
4835#if BASH_WAIT_N
4836# define DOWAIT_JOBSTATUS 0x10 /* OR this to get job's exitstatus instead of pid */
4837#endif 4829#endif
4830}
4838 4831
4839static int 4832static int
4840waitone(int block, struct job *job) 4833waitone(int block, struct job *job)
@@ -4842,7 +4835,7 @@ waitone(int block, struct job *job)
4842 int pid; 4835 int pid;
4843 int status; 4836 int status;
4844 struct job *jp; 4837 struct job *jp;
4845 struct job *thisjob; 4838 struct job *thisjob = NULL;
4846#if BASH_WAIT_N 4839#if BASH_WAIT_N
4847 bool want_jobexitstatus = (block & DOWAIT_JOBSTATUS); 4840 bool want_jobexitstatus = (block & DOWAIT_JOBSTATUS);
4848 block = (block & ~DOWAIT_JOBSTATUS); 4841 block = (block & ~DOWAIT_JOBSTATUS);
@@ -4869,21 +4862,8 @@ waitone(int block, struct job *job)
4869 * SIG_DFL handler does not wake sigsuspend(). 4862 * SIG_DFL handler does not wake sigsuspend().
4870 */ 4863 */
4871 INT_OFF; 4864 INT_OFF;
4872 if (block == DOWAIT_BLOCK_OR_SIG) { 4865 pid = waitproc(block, &status);
4873 pid = wait_block_or_sig(&status); 4866 TRACE(("wait returns pid %d, status=%d\n", pid, status));
4874 } else {
4875 int wait_flags = 0;
4876 if (block == DOWAIT_NONBLOCK)
4877 wait_flags = WNOHANG;
4878 /* if job control is active, accept stopped processes too */
4879 if (doing_jobctl)
4880 wait_flags |= WUNTRACED;
4881 /* NB: _not_ safe_waitpid, we need to detect EINTR */
4882 pid = waitpid(-1, &status, wait_flags);
4883 }
4884 TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n",
4885 pid, status, errno, strerror(errno)));
4886 thisjob = NULL;
4887 if (pid <= 0) 4867 if (pid <= 0)
4888 goto out; 4868 goto out;
4889 4869
@@ -4971,21 +4951,35 @@ static int
4971dowait(int block, struct job *jp) 4951dowait(int block, struct job *jp)
4972{ 4952{
4973#if !ENABLE_PLATFORM_MINGW32 4953#if !ENABLE_PLATFORM_MINGW32
4974 int pid = block == DOWAIT_NONBLOCK ? got_sigchld : 1; 4954 smallint gotchld = *(volatile smallint *)&got_sigchld;
4955 int rpid;
4956 int pid;
4957
4958 if (jp && jp->state != JOBRUNNING)
4959 block = DOWAIT_NONBLOCK;
4960
4961 if (block == DOWAIT_NONBLOCK && !gotchld)
4962 return 1;
4963
4964 rpid = 1;
4975 4965
4976 while (jp ? jp->state == JOBRUNNING : pid > 0) { 4966 do {
4977 if (!jp)
4978 got_sigchld = 0;
4979 pid = waitone(block, jp); 4967 pid = waitone(block, jp);
4980 } 4968 rpid &= !!pid;
4969
4970 if (!pid || (jp && jp->state != JOBRUNNING))
4971 block = DOWAIT_NONBLOCK;
4972 } while (pid >= 0);
4973
4974 return rpid;
4981#else 4975#else
4982 int pid = 1; 4976 int pid = 1;
4983 4977
4984 while (jp ? jp->state == JOBRUNNING : pid > 0) 4978 while (jp ? jp->state == JOBRUNNING : pid > 0)
4985 pid = waitone(block, jp); 4979 pid = waitone(block, jp);
4986#endif
4987 4980
4988 return pid; 4981 return pid;
4982#endif
4989} 4983}
4990 4984
4991#if JOBS 4985#if JOBS
@@ -5232,7 +5226,7 @@ waitcmd(int argc UNUSED_PARAM, char **argv)
5232 job = getjob(*argv, 0); 5226 job = getjob(*argv, 0);
5233 } 5227 }
5234 /* loop until process terminated or stopped */ 5228 /* loop until process terminated or stopped */
5235 dowait(DOWAIT_BLOCK_OR_SIG, NULL); 5229 dowait(DOWAIT_BLOCK_OR_SIG, job);
5236 if (pending_sig) 5230 if (pending_sig)
5237 goto sigout; 5231 goto sigout;
5238 job->waited = 1; 5232 job->waited = 1;
@@ -12622,7 +12616,8 @@ simplecmd(void)
12622 tokpushback = 1; 12616 tokpushback = 1;
12623 goto out; 12617 goto out;
12624 } 12618 }
12625 wordtext = (char *) (t == TAND ? "-a" : "-o"); 12619 /* pass "&&" or "||" to [[ ]] as literal args */
12620 wordtext = (char *) (t == TAND ? "&&" : "||");
12626#endif 12621#endif
12627 case TWORD: 12622 case TWORD:
12628 n = stzalloc(sizeof(struct narg)); 12623 n = stzalloc(sizeof(struct narg));
@@ -13600,7 +13595,7 @@ parsebackq: {
13600 goto done; 13595 goto done;
13601 13596
13602 case '\\': 13597 case '\\':
13603 pc = pgetc(); /* or pgetc_eatbnl()? why (example)? */ 13598 pc = pgetc(); /* not pgetc_eatbnl! */
13604 if (pc != '\\' && pc != '`' && pc != '$' 13599 if (pc != '\\' && pc != '`' && pc != '$'
13605 && (!synstack->dblquote || pc != '"') 13600 && (!synstack->dblquote || pc != '"')
13606 ) { 13601 ) {
diff --git a/shell/ash_test/ash-misc/wait7.right b/shell/ash_test/ash-misc/wait7.right
new file mode 100644
index 000000000..4b6445841
--- /dev/null
+++ b/shell/ash_test/ash-misc/wait7.right
@@ -0,0 +1,2 @@
1Background1
2Ok:0
diff --git a/shell/ash_test/ash-misc/wait7.tests b/shell/ash_test/ash-misc/wait7.tests
new file mode 100755
index 000000000..a54a778e5
--- /dev/null
+++ b/shell/ash_test/ash-misc/wait7.tests
@@ -0,0 +1,7 @@
1sleep 1 && echo "Background1" &
2pid=$!
3sleep 3 && echo "Background2: BUG!" &
4# Shouldn't wait for 2nd bkgd:
5wait $pid
6kill $!
7echo Ok:$?
diff --git a/shell/ash_test/ash-signals/usage.right b/shell/ash_test/ash-signals/usage.right
new file mode 100644
index 000000000..c0dbd6c3c
--- /dev/null
+++ b/shell/ash_test/ash-signals/usage.right
@@ -0,0 +1,14 @@
1___
2___
3___
4trap -- 'a' EXIT
5trap -- 'a' INT
6trap -- 'a' USR1
7trap -- 'a' USR2
8___
9___
10trap -- 'a' USR1
11trap -- 'a' USR2
12___
13___
14trap -- 'a' USR2
diff --git a/shell/ash_test/ash-signals/usage.tests b/shell/ash_test/ash-signals/usage.tests
new file mode 100755
index 000000000..d29c6e74a
--- /dev/null
+++ b/shell/ash_test/ash-signals/usage.tests
@@ -0,0 +1,23 @@
1# no output -- default state
2echo ___
3trap
4
5# assign some traps
6echo ___
7trap "a" EXIT INT USR1 USR2
8
9# show them all
10echo ___
11trap
12
13# clear one
14echo ___
15trap 0 INT
16echo ___
17trap
18
19# clear another
20echo ___
21trap "-" USR1
22echo ___
23trap
diff --git a/shell/hush.c b/shell/hush.c
index e9cec1cc9..ab7263381 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -63,7 +63,6 @@
63 * reserved words: function select 63 * reserved words: function select
64 * advanced test: [[ ]] 64 * advanced test: [[ ]]
65 * process substitution: <(list) and >(list) 65 * process substitution: <(list) and >(list)
66 * =~: regex operator
67 * let EXPR [EXPR...] 66 * let EXPR [EXPR...]
68 * Each EXPR is an arithmetic expression (ARITHMETIC EVALUATION) 67 * Each EXPR is an arithmetic expression (ARITHMETIC EVALUATION)
69 * If the last arg evaluates to 0, let returns 1; 0 otherwise. 68 * If the last arg evaluates to 0, let returns 1; 0 otherwise.
@@ -84,13 +83,12 @@
84 * [[ args ]] are CMD_SINGLEWORD_NOGLOB: 83 * [[ args ]] are CMD_SINGLEWORD_NOGLOB:
85 * v='a b'; [[ $v = 'a b' ]]; echo 0:$? 84 * v='a b'; [[ $v = 'a b' ]]; echo 0:$?
86 * [[ /bin/n* ]]; echo 0:$? 85 * [[ /bin/n* ]]; echo 0:$?
86 * = is glob match operator, not equality operator: STR = GLOB
87 * == same as =
88 * =~ is regex match operator: STR =~ REGEX
87 * TODO: 89 * TODO:
88 * &&/|| are AND/OR ops, -a/-o are not
89 * quoting needs to be considered (-f is an operator, "-f" and ""-f are not; etc) 90 * quoting needs to be considered (-f is an operator, "-f" and ""-f are not; etc)
90 * = is glob match operator, not equality operator: STR = GLOB 91 * in word = GLOB, quoting should be significant on char-by-char basis: a*cd"*"
91 * (in GLOB, quoting is significant on char-by-char basis: a*cd"*")
92 * == same as =
93 * add =~ regex match operator: STR =~ REGEX
94 */ 92 */
95//config:config HUSH 93//config:config HUSH
96//config: bool "hush (68 kb)" 94//config: bool "hush (68 kb)"
@@ -651,14 +649,16 @@ struct command {
651 smallint cmd_type; /* CMD_xxx */ 649 smallint cmd_type; /* CMD_xxx */
652#define CMD_NORMAL 0 650#define CMD_NORMAL 0
653#define CMD_SUBSHELL 1 651#define CMD_SUBSHELL 1
654#if BASH_TEST2 || ENABLE_HUSH_LOCAL || ENABLE_HUSH_EXPORT || ENABLE_HUSH_READONLY 652#if BASH_TEST2
655/* used for "[[ EXPR ]]", and to prevent word splitting and globbing in 653/* used for "[[ EXPR ]]" */
656 * "export v=t*" 654# define CMD_TEST2_SINGLEWORD_NOGLOB 2
657 */ 655#endif
658# define CMD_SINGLEWORD_NOGLOB 2 656#if ENABLE_HUSH_LOCAL || ENABLE_HUSH_EXPORT || ENABLE_HUSH_READONLY
657/* used to prevent word splitting and globbing in "export v=t*" */
658# define CMD_SINGLEWORD_NOGLOB 3
659#endif 659#endif
660#if ENABLE_HUSH_FUNCTIONS 660#if ENABLE_HUSH_FUNCTIONS
661# define CMD_FUNCDEF 3 661# define CMD_FUNCDEF 4
662#endif 662#endif
663 663
664 smalluint cmd_exitcode; 664 smalluint cmd_exitcode;
@@ -4112,6 +4112,14 @@ static int done_word(struct parse_context *ctx)
4112 ctx->ctx_dsemicolon = 0; 4112 ctx->ctx_dsemicolon = 0;
4113 } else 4113 } else
4114# endif 4114# endif
4115# if defined(CMD_TEST2_SINGLEWORD_NOGLOB)
4116 if (command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB
4117 && strcmp(ctx->word.data, "]]") == 0
4118 ) {
4119 /* allow "[[ ]] >file" etc */
4120 command->cmd_type = CMD_SINGLEWORD_NOGLOB;
4121 } else
4122# endif
4115 if (!command->argv /* if it's the first word... */ 4123 if (!command->argv /* if it's the first word... */
4116# if ENABLE_HUSH_LOOPS 4124# if ENABLE_HUSH_LOOPS
4117 && ctx->ctx_res_w != RES_FOR /* ...not after FOR or IN */ 4125 && ctx->ctx_res_w != RES_FOR /* ...not after FOR or IN */
@@ -4146,11 +4154,13 @@ static int done_word(struct parse_context *ctx)
4146 (ctx->ctx_res_w == RES_SNTX)); 4154 (ctx->ctx_res_w == RES_SNTX));
4147 return (ctx->ctx_res_w == RES_SNTX); 4155 return (ctx->ctx_res_w == RES_SNTX);
4148 } 4156 }
4157# if defined(CMD_TEST2_SINGLEWORD_NOGLOB)
4158 if (strcmp(ctx->word.data, "[[") == 0) {
4159 command->cmd_type = CMD_TEST2_SINGLEWORD_NOGLOB;
4160 } else
4161# endif
4149# if defined(CMD_SINGLEWORD_NOGLOB) 4162# if defined(CMD_SINGLEWORD_NOGLOB)
4150 if (0 4163 if (0
4151# if BASH_TEST2
4152 || strcmp(ctx->word.data, "[[") == 0
4153# endif
4154 /* In bash, local/export/readonly are special, args 4164 /* In bash, local/export/readonly are special, args
4155 * are assignments and therefore expansion of them 4165 * are assignments and therefore expansion of them
4156 * should be "one-word" expansion: 4166 * should be "one-word" expansion:
@@ -4172,7 +4182,8 @@ static int done_word(struct parse_context *ctx)
4172 ) { 4182 ) {
4173 command->cmd_type = CMD_SINGLEWORD_NOGLOB; 4183 command->cmd_type = CMD_SINGLEWORD_NOGLOB;
4174 } 4184 }
4175 /* fall through */ 4185# else
4186 { /* empty block to pair "if ... else" */ }
4176# endif 4187# endif
4177 } 4188 }
4178#endif /* HAS_KEYWORDS */ 4189#endif /* HAS_KEYWORDS */
@@ -5354,9 +5365,15 @@ static struct pipe *parse_stream(char **pstring,
5354 if (ch != '\n') 5365 if (ch != '\n')
5355 next = i_peek_and_eat_bkslash_nl(input); 5366 next = i_peek_and_eat_bkslash_nl(input);
5356 5367
5357 is_special = "{}<>;&|()#" /* special outside of "str" */ 5368 is_special = "{}<>&|();#" /* special outside of "str" */
5358 "$\"" IF_HUSH_TICK("`") /* always special */ 5369 "$\"" IF_HUSH_TICK("`") /* always special */
5359 SPECIAL_VAR_SYMBOL_STR; 5370 SPECIAL_VAR_SYMBOL_STR;
5371#if defined(CMD_TEST2_SINGLEWORD_NOGLOB)
5372 if (ctx.command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB) {
5373 /* In [[ ]], {}<>&|() are not special */
5374 is_special += 8;
5375 } else
5376#endif
5360 /* Are { and } special here? */ 5377 /* Are { and } special here? */
5361 if (ctx.command->argv /* word [word]{... - non-special */ 5378 if (ctx.command->argv /* word [word]{... - non-special */
5362 || ctx.word.length /* word{... - non-special */ 5379 || ctx.word.length /* word{... - non-special */
@@ -6953,7 +6970,7 @@ static char **expand_strvec_to_strvec(char **argv)
6953 return expand_variables(argv, EXP_FLAG_GLOB | EXP_FLAG_ESC_GLOB_CHARS); 6970 return expand_variables(argv, EXP_FLAG_GLOB | EXP_FLAG_ESC_GLOB_CHARS);
6954} 6971}
6955 6972
6956#if defined(CMD_SINGLEWORD_NOGLOB) 6973#if defined(CMD_SINGLEWORD_NOGLOB) || defined(CMD_TEST2_SINGLEWORD_NOGLOB)
6957static char **expand_strvec_to_strvec_singleword_noglob(char **argv) 6974static char **expand_strvec_to_strvec_singleword_noglob(char **argv)
6958{ 6975{
6959 return expand_variables(argv, EXP_FLAG_SINGLEWORD); 6976 return expand_variables(argv, EXP_FLAG_SINGLEWORD);
@@ -8698,9 +8715,15 @@ static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status)
8698 */ 8715 */
8699 if (WIFSIGNALED(status)) { 8716 if (WIFSIGNALED(status)) {
8700 int sig = WTERMSIG(status); 8717 int sig = WTERMSIG(status);
8701 if (i == fg_pipe->num_cmds-1) 8718 if (G.run_list_level == 1
8702 /* TODO: use strsignal() instead for bash compat? but that's bloat... */ 8719 /* ^^^^^ Do not print in nested contexts, example:
8703 puts(sig == SIGINT || sig == SIGPIPE ? "" : get_signame(sig)); 8720 * echo `sleep 1; sh -c 'kill -9 $$'` - prints "137", NOT "Killed 137"
8721 */
8722 && i == fg_pipe->num_cmds-1
8723 ) {
8724 /* strsignal() is for bash compat. ~600 bloat versus bbox's get_signame() */
8725 puts(sig == SIGINT || sig == SIGPIPE ? "" : strsignal(sig));
8726 }
8704 /* TODO: if (WCOREDUMP(status)) + " (core dumped)"; */ 8727 /* TODO: if (WCOREDUMP(status)) + " (core dumped)"; */
8705 /* TODO: MIPS has 128 sigs (1..128), what if sig==128 here? 8728 /* TODO: MIPS has 128 sigs (1..128), what if sig==128 here?
8706 * Maybe we need to use sig | 128? */ 8729 * Maybe we need to use sig | 128? */
@@ -9127,6 +9150,11 @@ static NOINLINE int run_pipe(struct pipe *pi)
9127 } 9150 }
9128 9151
9129 /* Expand the rest into (possibly) many strings each */ 9152 /* Expand the rest into (possibly) many strings each */
9153#if defined(CMD_TEST2_SINGLEWORD_NOGLOB)
9154 if (command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB)
9155 argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt);
9156 else
9157#endif
9130#if defined(CMD_SINGLEWORD_NOGLOB) 9158#if defined(CMD_SINGLEWORD_NOGLOB)
9131 if (command->cmd_type == CMD_SINGLEWORD_NOGLOB) 9159 if (command->cmd_type == CMD_SINGLEWORD_NOGLOB)
9132 argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt); 9160 argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt);
diff --git a/shell/hush_test/hush-bugs/strops5_bug.right b/shell/hush_test/hush-bugs/strops5_bug.right
new file mode 100644
index 000000000..aba7bb429
--- /dev/null
+++ b/shell/hush_test/hush-bugs/strops5_bug.right
@@ -0,0 +1,2 @@
14:no:1
25:YES:0
diff --git a/shell/hush_test/hush-bugs/strops5_bug.tests b/shell/hush_test/hush-bugs/strops5_bug.tests
new file mode 100755
index 000000000..3e24df768
--- /dev/null
+++ b/shell/hush_test/hush-bugs/strops5_bug.tests
@@ -0,0 +1,10 @@
1v='*.z'
2
3# Buggy:
4# the problem is that expansion rules of LHS and RHS of ~=
5# should not be the same: in RHS, "$v" and "*" should escape metas
6# (currently "$v" does not),
7# but in LHS, they should _not_ do that
8# (currently "*" does). Thus these cases fail:
9[[ a.z == "$v" ]]; echo 4:no:$? # BUG: "$v" expands to *.z
10[[ "*".z == ?.z ]]; echo 5:YES:$? # BUG: "*" expands to \*
diff --git a/shell/hush_test/hush-misc/sig_exitcode.right b/shell/hush_test/hush-misc/sig_exitcode.right
index d5f000a08..7cbc1072d 100644
--- a/shell/hush_test/hush-misc/sig_exitcode.right
+++ b/shell/hush_test/hush-misc/sig_exitcode.right
@@ -1,5 +1,5 @@
1KILL 1Killed
2137:137 2137:137
3KILL 3Killed
40:0 40:0
5Done 5Done
diff --git a/shell/hush_test/hush-misc/wait7.right b/shell/hush_test/hush-misc/wait7.right
new file mode 100644
index 000000000..4b6445841
--- /dev/null
+++ b/shell/hush_test/hush-misc/wait7.right
@@ -0,0 +1,2 @@
1Background1
2Ok:0
diff --git a/shell/hush_test/hush-misc/wait7.tests b/shell/hush_test/hush-misc/wait7.tests
new file mode 100755
index 000000000..a54a778e5
--- /dev/null
+++ b/shell/hush_test/hush-misc/wait7.tests
@@ -0,0 +1,7 @@
1sleep 1 && echo "Background1" &
2pid=$!
3sleep 3 && echo "Background2: BUG!" &
4# Shouldn't wait for 2nd bkgd:
5wait $pid
6kill $!
7echo Ok:$?
diff --git a/shell/hush_test/hush-psubst/tick6.right b/shell/hush_test/hush-psubst/tick6.right
new file mode 100644
index 000000000..065fd3e79
--- /dev/null
+++ b/shell/hush_test/hush-psubst/tick6.right
@@ -0,0 +1 @@
137
diff --git a/shell/hush_test/hush-psubst/tick6.tests b/shell/hush_test/hush-psubst/tick6.tests
new file mode 100755
index 000000000..33dd3630d
--- /dev/null
+++ b/shell/hush_test/hush-psubst/tick6.tests
@@ -0,0 +1 @@
true; echo `sh -c 'kill -9 $$'` $?
diff --git a/shell/hush_test/hush-signals/catch.right b/shell/hush_test/hush-signals/catch.right
index 80a062c4b..68530c6e7 100644
--- a/shell/hush_test/hush-signals/catch.right
+++ b/shell/hush_test/hush-signals/catch.right
@@ -2,4 +2,4 @@ sending USR2
2caught 2caught
3sending USR2 3sending USR2
4sending USR2 4sending USR2
5USR2 5User defined signal 2
diff --git a/shell/hush_test/hush-signals/signal1.right b/shell/hush_test/hush-signals/signal1.right
new file mode 100644
index 000000000..cf403ac62
--- /dev/null
+++ b/shell/hush_test/hush-signals/signal1.right
@@ -0,0 +1,20 @@
1got signal
2trap -- 'echo got signal' USR1
3sent 1 signal
4got signal
5wait interrupted
6trap -- 'echo got signal' USR1
7sent 2 signal
8got signal
9wait interrupted
10trap -- 'echo got signal' USR1
11sent 3 signal
12got signal
13wait interrupted
14trap -- 'echo got signal' USR1
15sent 4 signal
16got signal
17wait interrupted
18trap -- 'echo got signal' USR1
19sent 5 signal
20sleep completed
diff --git a/shell/hush_test/hush-signals/signal1.tests b/shell/hush_test/hush-signals/signal1.tests
new file mode 100755
index 000000000..61943467a
--- /dev/null
+++ b/shell/hush_test/hush-signals/signal1.tests
@@ -0,0 +1,28 @@
1trap "echo got signal" USR1
2
3for try in 1 2 3 4 5; do
4 kill -USR1 $$
5 sleep 0.2
6 echo "sent $try signal"
7done &
8
9# Ensure "wait" has something to wait for
10sleep 2 &
11
12# Ensure we do not execute "trap" below before "kill -USR1" above
13# (was getting failure on loaded machine without this)
14sleep 0.1
15
16sleeping=true
17while $sleeping; do
18 trap
19 if wait %%; then
20 echo "sleep completed"
21 sleeping=false
22 elif [ $? = 127 ]; then
23 echo "BUG: no processes to wait for?!"
24 sleeping=false
25 else
26 echo "wait interrupted"
27 fi
28done
diff --git a/shell/hush_test/hush-signals/signal_read2.right b/shell/hush_test/hush-signals/signal_read2.right
index 71a6bc16d..87d8da304 100644
--- a/shell/hush_test/hush-signals/signal_read2.right
+++ b/shell/hush_test/hush-signals/signal_read2.right
@@ -1,2 +1,2 @@
1HUP 1Hangup
2Done:129 2Done:129
diff --git a/shell/hush_test/hush-signals/subshell.right b/shell/hush_test/hush-signals/subshell.right
index f865b932b..248fcc41a 100644
--- a/shell/hush_test/hush-signals/subshell.right
+++ b/shell/hush_test/hush-signals/subshell.right
@@ -17,5 +17,5 @@ Ok
17trap -- '' HUP 17trap -- '' HUP
18trap -- '' QUIT 18trap -- '' QUIT
19trap -- '' SYS 19trap -- '' SYS
20TERM 20Terminated
21Done 21Done
diff --git a/shell/hush_test/hush-test2/andor1.right b/shell/hush_test/hush-test2/andor1.right
new file mode 100644
index 000000000..038c7a681
--- /dev/null
+++ b/shell/hush_test/hush-test2/andor1.right
@@ -0,0 +1,6 @@
11:YES
22:no
33:YES
44:YES
55:no
66:no
diff --git a/shell/hush_test/hush-test2/andor1.tests b/shell/hush_test/hush-test2/andor1.tests
new file mode 100755
index 000000000..c449de7e6
--- /dev/null
+++ b/shell/hush_test/hush-test2/andor1.tests
@@ -0,0 +1,7 @@
1e=''
2[[ a && b ]] && echo 1:YES
3[[ a && '' ]] || echo 2:no
4[[ a || b ]] && echo 3:YES
5[[ '' || b ]] && echo 4:YES
6[[ "" || "$e" ]] || echo 5:no
7[[ "" || $e ]] || echo 6:no
diff --git a/shell/hush_test/hush-test2/noglob1.right b/shell/hush_test/hush-test2/noglob1.right
new file mode 100644
index 000000000..d0c3f1d8e
--- /dev/null
+++ b/shell/hush_test/hush-test2/noglob1.right
@@ -0,0 +1,2 @@
11:YES:0
22:YES:0
diff --git a/shell/hush_test/hush-test2/noglob1.tests b/shell/hush_test/hush-test2/noglob1.tests
new file mode 100755
index 000000000..963bacbd3
--- /dev/null
+++ b/shell/hush_test/hush-test2/noglob1.tests
@@ -0,0 +1,3 @@
1v='*.tests'
2[[ *.tests ]]; echo 1:YES:$?
3[[ $v ]]; echo 2:YES:$?
diff --git a/shell/hush_test/hush-test2/strops1.right b/shell/hush_test/hush-test2/strops1.right
new file mode 100644
index 000000000..590496301
--- /dev/null
+++ b/shell/hush_test/hush-test2/strops1.right
@@ -0,0 +1,8 @@
11:YES:0
22:YES:0
33:YES:0
44:YES:0
55:YES:0
66:YES:0
77:YES:0
88:no:1
diff --git a/shell/hush_test/hush-test2/strops1.tests b/shell/hush_test/hush-test2/strops1.tests
new file mode 100755
index 000000000..bb24e2a2f
--- /dev/null
+++ b/shell/hush_test/hush-test2/strops1.tests
@@ -0,0 +1,15 @@
1v='*.z'
2[[ a.z = *.z ]]; echo 1:YES:$?
3[[ a.z == $v ]]; echo 2:YES:$?
4
5# wildcards can match a slash
6[[ a/b = a*b ]]; echo 3:YES:$?
7[[ a/b == a?b ]]; echo 4:YES:$?
8
9# wildcards can match a leading dot
10[[ a/.b = a/*b ]]; echo 5:YES:$?
11[[ a/.b == a/?b ]]; echo 6:YES:$?
12
13# wildcards can be escaped
14[[ abc = a*c ]]; echo 7:YES:$?
15[[ abc == a\*c ]]; echo 8:no:$?
diff --git a/shell/hush_test/hush-test2/strops2.right b/shell/hush_test/hush-test2/strops2.right
new file mode 100644
index 000000000..8ddb4b0f0
--- /dev/null
+++ b/shell/hush_test/hush-test2/strops2.right
@@ -0,0 +1,6 @@
11:ERR2:2
22:YES:0
33:YES:0
44:YES:0
55:no:1
66:YES:0
diff --git a/shell/hush_test/hush-test2/strops2.tests b/shell/hush_test/hush-test2/strops2.tests
new file mode 100755
index 000000000..ab325bc9f
--- /dev/null
+++ b/shell/hush_test/hush-test2/strops2.tests
@@ -0,0 +1,12 @@
1# malformed regex
2[[ a =~ * ]]; echo 1:ERR2:$?
3
4[[ a/b =~ a.b ]]; echo 2:YES:$?
5[[ a/b =~ /*b ]]; echo 3:YES:$?
6
7v='[]b.-]'
8[[ a/.b] =~ $v ]]; echo 4:YES:$?
9
10v=']b.-'
11[[ a/.b] =~ $v ]]; echo 5:no:$?
12[[ a/.b] =~ [$v] ]]; echo 6:YES:$?
diff --git a/shell/hush_test/hush-test2/strops3.right b/shell/hush_test/hush-test2/strops3.right
new file mode 100644
index 000000000..14cc04fdc
--- /dev/null
+++ b/shell/hush_test/hush-test2/strops3.right
@@ -0,0 +1,7 @@
11:YES:0
22:YES:0
33:no:1
44:YES:0
52u:YES:0
63u:YES:0
74u:YES:0
diff --git a/shell/hush_test/hush-test2/strops3.tests b/shell/hush_test/hush-test2/strops3.tests
new file mode 100755
index 000000000..927476671
--- /dev/null
+++ b/shell/hush_test/hush-test2/strops3.tests
@@ -0,0 +1,13 @@
1# regex should accept '+' operator
2[[ abcdef =~ a[b-z]+ ]]; echo 1:YES:$?
3
4# newline matches by "match any" patterns
5v='
6'
7[[ "$v" =~ . ]]; echo 2:YES:$?
8[[ "$v" =~ "[$v]" ]]; echo 3:no:$? # hmm bash does return 1... why?
9[[ "$v" =~ [^a] ]]; echo 4:YES:$?
10# should work even without quotes:
11[[ $v =~ . ]]; echo 2u:YES:$?
12[[ $v =~ [$v] ]]; echo 3u:YES:$?
13[[ $v =~ [^a] ]]; echo 4u:YES:$?
diff --git a/shell/hush_test/hush-test2/strops4.right b/shell/hush_test/hush-test2/strops4.right
new file mode 100644
index 000000000..53a176261
--- /dev/null
+++ b/shell/hush_test/hush-test2/strops4.right
@@ -0,0 +1,2 @@
11:no:1
22:YES:0
diff --git a/shell/hush_test/hush-test2/strops4.tests b/shell/hush_test/hush-test2/strops4.tests
new file mode 100755
index 000000000..bb381426d
--- /dev/null
+++ b/shell/hush_test/hush-test2/strops4.tests
@@ -0,0 +1,3 @@
1# < > are not redirect operators
2[[ a > b ]]; echo 1:no:$?
3[[ a < b ]]; echo 2:YES:$?
diff --git a/shell/hush_test/hush-test2/strops5.right b/shell/hush_test/hush-test2/strops5.right
new file mode 100644
index 000000000..9ed4d6569
--- /dev/null
+++ b/shell/hush_test/hush-test2/strops5.right
@@ -0,0 +1,3 @@
11:YES:0
22:no:1
33:YES:0
diff --git a/shell/hush_test/hush-test2/strops5.tests b/shell/hush_test/hush-test2/strops5.tests
new file mode 100755
index 000000000..ee01e5c30
--- /dev/null
+++ b/shell/hush_test/hush-test2/strops5.tests
@@ -0,0 +1,13 @@
1v='*.z'
2[[ a.z = *.z ]]; echo 1:YES:$?
3[[ a.z = "*".z ]]; echo 2:no:$?
4[[ a.z == $v ]]; echo 3:YES:$?
5
6# Buggy:
7# the problem is that expansion rules of LHS and RHS of ~=
8# should not be the same: in RHS, "$v" and "*" should escape metas
9# (currently "$v" does not),
10# but in LHS, they should _not_ do that
11# (currently "*" does). Thus these cases fail:
12#[[ a.z == "$v" ]]; echo 4:no:$? # BUG: "$v" expands to *.z
13#[[ "*".z == ?.z ]]; echo 5:YES:$? # BUG: "*" expands to \*
diff --git a/shell/math.c b/shell/math.c
index aac5017d0..2942cdd26 100644
--- a/shell/math.c
+++ b/shell/math.c
@@ -251,7 +251,7 @@ typedef struct remembered_name {
251} remembered_name; 251} remembered_name;
252 252
253 253
254static arith_t FAST_FUNC 254static arith_t
255evaluate_string(arith_state_t *math_state, const char *expr); 255evaluate_string(arith_state_t *math_state, const char *expr);
256 256
257static const char* 257static const char*
@@ -582,7 +582,7 @@ static arith_t strto_arith_t(const char *nptr, char **endptr)
582# endif 582# endif
583#endif 583#endif
584 584
585static arith_t FAST_FUNC 585static arith_t
586evaluate_string(arith_state_t *math_state, const char *expr) 586evaluate_string(arith_state_t *math_state, const char *expr)
587{ 587{
588 operator lasttok; 588 operator lasttok;
diff --git a/testsuite/hexdump.tests b/testsuite/hexdump.tests
index 45a0c1300..cfb20187e 100755
--- a/testsuite/hexdump.tests
+++ b/testsuite/hexdump.tests
@@ -15,4 +15,23 @@ testing 'hexdump -C with four NULs' \
15 '' \ 15 '' \
16 '\0\0\0\0' 16 '\0\0\0\0'
17 17
18testing "hexdump does not think last padded block matches any full block" \
19 "hexdump -e '1/1 \"%02x|\"1/1 \"%02x!\\n\"'" \
20 "\
2100|00!
22*
2300| !
24" \
25 '' \
26 '\0\0\0\0\0\0\0\0\0\0\0'
27
28testing "hexdump thinks last full block can match" \
29 "hexdump -e '1/1 \"%02x|\"1/1 \"%02x!\\n\"'" \
30 "\
3100|00!
32*
33" \
34 '' \
35 '\0\0\0\0\0\0\0\0\0\0\0\0'
36
18exit $FAILCOUNT 37exit $FAILCOUNT
diff --git a/testsuite/xargs.tests b/testsuite/xargs.tests
index 159f1ff69..e7c7c4b3d 100755
--- a/testsuite/xargs.tests
+++ b/testsuite/xargs.tests
@@ -61,4 +61,12 @@ testing "xargs -n2" \
61 61
62SKIP= 62SKIP=
63 63
64optional FEATURE_XARGS_SUPPORT_QUOTES
65testing "xargs -I skips empty lines and leading whitespace" \
66 "xargs -I% echo '[%]'" \
67 "[2]\n[4]\n[6 6 ]\n[7]\n" \
68 "" " \n2\n\n4\n\n 6 6 \n \v \t 7\n\t\n\v\n"
69
70SKIP=
71
64exit $FAILCOUNT 72exit $FAILCOUNT
diff --git a/testsuite/xxd.tests b/testsuite/xxd.tests
new file mode 100755
index 000000000..2e80be5fe
--- /dev/null
+++ b/testsuite/xxd.tests
@@ -0,0 +1,34 @@
1#!/bin/sh
2
3# Copyright 2020 by Denys Vlasenko <vda.linux@googlemail.com>
4# Licensed under GPLv2, see file LICENSE in this source tree.
5
6. ./testing.sh
7
8# testing "description" "command" "result" "infile" "stdin"
9testing 'xxd -p with one NUL' \
10 'xxd -p' \
11 "\
1200
13" \
14 '' \
15 '\0'
16
17testing 'xxd -p with 30 NULs' \
18 'xxd -p' \
19 "\
20000000000000000000000000000000000000000000000000000000000000
21" \
22 '' \
23 '\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'
24
25testing 'xxd -p with 31 NULs' \
26 'xxd -p' \
27 "\
28000000000000000000000000000000000000000000000000000000000000
2900
30" \
31 '' \
32 '\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'
33
34exit $FAILCOUNT
diff --git a/util-linux/getopt.c b/util-linux/getopt.c
index db7db6ff8..1fa402429 100644
--- a/util-linux/getopt.c
+++ b/util-linux/getopt.c
@@ -289,12 +289,13 @@ static struct option *add_long_options(struct option *long_options, char *option
289{ 289{
290 int long_nr = 0; 290 int long_nr = 0;
291 int arg_opt, tlen; 291 int arg_opt, tlen;
292 char *tokptr = strtok(options, ", \t\n"); 292 char *tokptr;
293 293
294 if (long_options) 294 if (long_options)
295 while (long_options[long_nr].name) 295 while (long_options[long_nr].name)
296 long_nr++; 296 long_nr++;
297 297
298 tokptr = strtok_r(options, ", \t\n", &options);
298 while (tokptr) { 299 while (tokptr) {
299 arg_opt = no_argument; 300 arg_opt = no_argument;
300 tlen = strlen(tokptr); 301 tlen = strlen(tokptr);
@@ -318,7 +319,7 @@ static struct option *add_long_options(struct option *long_options, char *option
318 long_nr++; 319 long_nr++;
319 /*memset(&long_options[long_nr], 0, sizeof(long_options[0])); - xrealloc_vector did it */ 320 /*memset(&long_options[long_nr], 0, sizeof(long_options[0])); - xrealloc_vector did it */
320 } 321 }
321 tokptr = strtok(NULL, ", \t\n"); 322 tokptr = strtok_r(NULL, ", \t\n", &options);
322 } 323 }
323 return long_options; 324 return long_options;
324} 325}
diff --git a/util-linux/hexdump.c b/util-linux/hexdump.c
index 065b83980..2174c3008 100644
--- a/util-linux/hexdump.c
+++ b/util-linux/hexdump.c
@@ -15,16 +15,6 @@
15//config: The hexdump utility is used to display binary data in a readable 15//config: The hexdump utility is used to display binary data in a readable
16//config: way that is comparable to the output from most hex editors. 16//config: way that is comparable to the output from most hex editors.
17//config: 17//config:
18//config:config FEATURE_HEXDUMP_REVERSE
19//config: bool "Support -R, reverse of 'hexdump -Cv'"
20//config: default y
21//config: depends on HEXDUMP
22//config: help
23//config: The hexdump utility is used to display binary data in an ascii
24//config: readable way. This option creates binary data from an ascii input.
25//config: NB: this option is non-standard. It's unwise to use it in scripts
26//config: aimed to be portable.
27//config:
28//config:config HD 18//config:config HD
29//config: bool "hd (7.8 kb)" 19//config: bool "hd (7.8 kb)"
30//config: default y 20//config: default y
@@ -38,7 +28,7 @@
38//kbuild:lib-$(CONFIG_HD) += hexdump.o 28//kbuild:lib-$(CONFIG_HD) += hexdump.o
39 29
40//usage:#define hexdump_trivial_usage 30//usage:#define hexdump_trivial_usage
41//usage: "[-bcCdefnosvx" IF_FEATURE_HEXDUMP_REVERSE("R") "] [FILE]..." 31//usage: "[-bcCdefnosvx] [FILE]..."
42//usage:#define hexdump_full_usage "\n\n" 32//usage:#define hexdump_full_usage "\n\n"
43//usage: "Display FILEs (or stdin) in a user specified format\n" 33//usage: "Display FILEs (or stdin) in a user specified format\n"
44//usage: "\n -b 1-byte octal display" 34//usage: "\n -b 1-byte octal display"
@@ -53,9 +43,6 @@
53// exactly the same help text lines in hexdump and xxd: 43// exactly the same help text lines in hexdump and xxd:
54//usage: "\n -n LENGTH Show only first LENGTH bytes" 44//usage: "\n -n LENGTH Show only first LENGTH bytes"
55//usage: "\n -s OFFSET Skip OFFSET bytes" 45//usage: "\n -s OFFSET Skip OFFSET bytes"
56//usage: IF_FEATURE_HEXDUMP_REVERSE(
57//usage: "\n -R Reverse of 'hexdump -Cv'")
58// TODO: NONCOMPAT!!! move -R to xxd -r
59//usage: 46//usage:
60//usage:#define hd_trivial_usage 47//usage:#define hd_trivial_usage
61//usage: "FILE..." 48//usage: "FILE..."
@@ -94,7 +81,7 @@ static const char *const add_strings[] = {
94 81
95static const char add_first[] ALIGN1 = "\"%07.7_Ax\n\""; 82static const char add_first[] ALIGN1 = "\"%07.7_Ax\n\"";
96 83
97static const char hexdump_opts[] ALIGN1 = "bcdoxCe:f:n:s:v" IF_FEATURE_HEXDUMP_REVERSE("R"); 84static const char hexdump_opts[] ALIGN1 = "bcdoxCe:f:n:s:v";
98 85
99int hexdump_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 86int hexdump_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
100int hexdump_main(int argc, char **argv) 87int hexdump_main(int argc, char **argv)
@@ -102,10 +89,6 @@ int hexdump_main(int argc, char **argv)
102 dumper_t *dumper = alloc_dumper(); 89 dumper_t *dumper = alloc_dumper();
103 const char *p; 90 const char *p;
104 int ch; 91 int ch;
105#if ENABLE_FEATURE_HEXDUMP_REVERSE
106 FILE *fp;
107 smallint rdump = 0;
108#endif
109 92
110 if (ENABLE_HD 93 if (ENABLE_HD
111 && (!ENABLE_HEXDUMP || !applet_name[2]) 94 && (!ENABLE_HEXDUMP || !applet_name[2])
@@ -153,11 +136,6 @@ int hexdump_main(int argc, char **argv)
153 if (ch == 'v') { 136 if (ch == 'v') {
154 dumper->dump_vflag = ALL; 137 dumper->dump_vflag = ALL;
155 } 138 }
156#if ENABLE_FEATURE_HEXDUMP_REVERSE
157 if (ch == 'R') {
158 rdump = 1;
159 }
160#endif
161 } 139 }
162 140
163 if (!dumper->fshead) { 141 if (!dumper->fshead) {
@@ -167,40 +145,5 @@ int hexdump_main(int argc, char **argv)
167 145
168 argv += optind; 146 argv += optind;
169 147
170#if !ENABLE_FEATURE_HEXDUMP_REVERSE
171 return bb_dump_dump(dumper, argv); 148 return bb_dump_dump(dumper, argv);
172#else
173 if (!rdump) {
174 return bb_dump_dump(dumper, argv);
175 }
176
177 /* -R: reverse of 'hexdump -Cv' */
178 fp = stdin;
179 if (!*argv) {
180 argv--;
181 goto jump_in;
182 }
183
184 do {
185 char *buf;
186 fp = xfopen_for_read(*argv);
187 jump_in:
188 while ((buf = xmalloc_fgetline(fp)) != NULL) {
189 p = buf;
190 while (1) {
191 /* skip address or previous byte */
192 while (isxdigit(*p)) p++;
193 while (*p == ' ') p++;
194 /* '|' char will break the line */
195 if (!isxdigit(*p) || sscanf(p, "%x ", &ch) != 1)
196 break;
197 putchar(ch);
198 }
199 free(buf);
200 }
201 fclose(fp);
202 } while (*++argv);
203
204 fflush_stdout_and_exit(EXIT_SUCCESS);
205#endif
206} 149}
diff --git a/util-linux/hexdump_xxd.c b/util-linux/hexdump_xxd.c
index 6cf6d0297..d2f4b6ed8 100644
--- a/util-linux/hexdump_xxd.c
+++ b/util-linux/hexdump_xxd.c
@@ -50,6 +50,7 @@
50// exactly the same help text lines in hexdump and xxd: 50// exactly the same help text lines in hexdump and xxd:
51//usage: "\n -l LENGTH Show only first LENGTH bytes" 51//usage: "\n -l LENGTH Show only first LENGTH bytes"
52//usage: "\n -s OFFSET Skip OFFSET bytes" 52//usage: "\n -s OFFSET Skip OFFSET bytes"
53//usage: "\n -r Reverse (with -p, assumes no offsets in input)"
53// TODO: implement -r (see hexdump -R) 54// TODO: implement -r (see hexdump -R)
54 55
55#include "libbb.h" 56#include "libbb.h"
@@ -57,6 +58,71 @@
57 58
58/* This is a NOEXEC applet. Be very careful! */ 59/* This is a NOEXEC applet. Be very careful! */
59 60
61#define OPT_l (1 << 0)
62#define OPT_s (1 << 1)
63#define OPT_a (1 << 2)
64#define OPT_p (1 << 3)
65#define OPT_r (1 << 4)
66
67static void reverse(unsigned opt, unsigned cols, const char *filename)
68{
69 FILE *fp;
70 char *buf;
71
72 fp = filename ? xfopen_for_read(filename) : stdin;
73
74 while ((buf = xmalloc_fgetline(fp)) != NULL) {
75 char *p = buf;
76 unsigned cnt = cols;
77
78 if (!(opt & OPT_p)) {
79 /* skip address */
80 while (isxdigit(*p)) p++;
81 /* NB: for xxd -r, first hex portion is address even without colon */
82 /* If it's there, skip it: */
83 if (*p == ':') p++;
84
85//TODO: seek (or zero-pad if unseekable) to the address position
86//NOTE: -s SEEK value should be added to the address before seeking
87 }
88
89 /* Process hex bytes optionally separated by whitespace */
90 do {
91 uint8_t val, c;
92
93 p = skip_whitespace(p);
94
95 c = *p++;
96 if (isdigit(c))
97 val = c - '0';
98 else if ((c|0x20) >= 'a' && (c|0x20) <= 'f')
99 val = (c|0x20) - ('a' - 10);
100 else
101 break;
102 val <<= 4;
103
104 /* Works the same with xxd V1.10:
105 * echo "31 09 32 0a" | xxd -r -p
106 * echo "31 0 9 32 0a" | xxd -r -p
107 * thus allow whitespace even within the byte:
108 */
109 p = skip_whitespace(p);
110
111 c = *p++;
112 if (isdigit(c))
113 val |= c - '0';
114 else if ((c|0x20) >= 'a' && (c|0x20) <= 'f')
115 val |= (c|0x20) - ('a' - 10);
116 else
117 break;
118 putchar(val);
119 } while (!(opt & OPT_p) || --cnt != 0);
120 free(buf);
121 }
122 //fclose(fp);
123 fflush_stdout_and_exit(EXIT_SUCCESS);
124}
125
60int xxd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 126int xxd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
61int xxd_main(int argc UNUSED_PARAM, char **argv) 127int xxd_main(int argc UNUSED_PARAM, char **argv)
62{ 128{
@@ -69,11 +135,7 @@ int xxd_main(int argc UNUSED_PARAM, char **argv)
69 135
70 dumper = alloc_dumper(); 136 dumper = alloc_dumper();
71 137
72#define OPT_l (1 << 0) 138 opt = getopt32(argv, "^" "l:s:aprg:+c:+" "\0" "?1" /* 1 argument max */,
73#define OPT_s (1 << 1)
74#define OPT_a (1 << 2)
75#define OPT_p (1 << 3)
76 opt = getopt32(argv, "^" "l:s:apg:+c:+" "\0" "?1" /* 1 argument max */,
77 &opt_l, &opt_s, &bytes, &cols 139 &opt_l, &opt_s, &bytes, &cols
78 ); 140 );
79 argv += optind; 141 argv += optind;
@@ -107,6 +169,10 @@ int xxd_main(int argc UNUSED_PARAM, char **argv)
107 bb_dump_add(dumper, "\"%08.8_ax: \""); // "address: " 169 bb_dump_add(dumper, "\"%08.8_ax: \""); // "address: "
108 } 170 }
109 171
172 if (opt & OPT_r) {
173 reverse(opt, cols, argv[0]);
174 }
175
110 if (bytes < 1 || bytes >= cols) { 176 if (bytes < 1 || bytes >= cols) {
111 sprintf(buf, "%u/1 \"%%02x\"", cols); // cols * "xx" 177 sprintf(buf, "%u/1 \"%%02x\"", cols); // cols * "xx"
112 bb_dump_add(dumper, buf); 178 bb_dump_add(dumper, buf);
@@ -141,6 +207,7 @@ int xxd_main(int argc UNUSED_PARAM, char **argv)
141 bb_dump_add(dumper, buf); 207 bb_dump_add(dumper, buf);
142 } else { 208 } else {
143 bb_dump_add(dumper, "\"\n\""); 209 bb_dump_add(dumper, "\"\n\"");
210 dumper->eofstring = "\n";
144 } 211 }
145 212
146 return bb_dump_dump(dumper, argv); 213 return bb_dump_dump(dumper, argv);
diff --git a/util-linux/losetup.c b/util-linux/losetup.c
index ac8b79502..24f7a2349 100644
--- a/util-linux/losetup.c
+++ b/util-linux/losetup.c
@@ -150,7 +150,7 @@ int losetup_main(int argc UNUSED_PARAM, char **argv)
150 if (opt & OPT_P) { 150 if (opt & OPT_P) {
151 flags |= BB_LO_FLAGS_PARTSCAN; 151 flags |= BB_LO_FLAGS_PARTSCAN;
152 } 152 }
153 if (set_loop(&d, argv[0], offset, flags) < 0) 153 if (set_loop(&d, argv[0], offset, 0, flags) < 0)
154 bb_simple_perror_msg_and_die(argv[0]); 154 bb_simple_perror_msg_and_die(argv[0]);
155 return EXIT_SUCCESS; 155 return EXIT_SUCCESS;
156 } 156 }
diff --git a/util-linux/lspci.c b/util-linux/lspci.c
index 2f0b5fab9..c22cbcc1e 100644
--- a/util-linux/lspci.c
+++ b/util-linux/lspci.c
@@ -37,11 +37,9 @@ enum {
37/* 37/*
38 * PCI_SLOT_NAME PCI_CLASS: PCI_VID:PCI_DID [PCI_SUBSYS_VID:PCI_SUBSYS_DID] [DRIVER] 38 * PCI_SLOT_NAME PCI_CLASS: PCI_VID:PCI_DID [PCI_SUBSYS_VID:PCI_SUBSYS_DID] [DRIVER]
39 */ 39 */
40static int FAST_FUNC fileAction( 40static int FAST_FUNC fileAction(struct recursive_state *state UNUSED_PARAM,
41 const char *fileName, 41 const char *fileName,
42 struct stat *statbuf UNUSED_PARAM, 42 struct stat *statbuf UNUSED_PARAM)
43 void *userData UNUSED_PARAM,
44 int depth UNUSED_PARAM)
45{ 43{
46 parser_t *parser; 44 parser_t *parser;
47 char *tokens[3]; 45 char *tokens[3];
@@ -117,8 +115,7 @@ int lspci_main(int argc UNUSED_PARAM, char **argv)
117 ACTION_RECURSE, 115 ACTION_RECURSE,
118 fileAction, 116 fileAction,
119 NULL, /* dirAction */ 117 NULL, /* dirAction */
120 NULL, /* userData */ 118 NULL /* userData */
121 0 /* depth */); 119 );
122
123 return EXIT_SUCCESS; 120 return EXIT_SUCCESS;
124} 121}
diff --git a/util-linux/lsusb.c b/util-linux/lsusb.c
index 64a00eee2..9abb748ce 100644
--- a/util-linux/lsusb.c
+++ b/util-linux/lsusb.c
@@ -24,11 +24,9 @@
24 24
25#include "libbb.h" 25#include "libbb.h"
26 26
27static int FAST_FUNC fileAction( 27static int FAST_FUNC fileAction(struct recursive_state *state UNUSED_PARAM,
28 const char *fileName, 28 const char *fileName,
29 struct stat *statbuf UNUSED_PARAM, 29 struct stat *statbuf UNUSED_PARAM)
30 void *userData UNUSED_PARAM,
31 int depth UNUSED_PARAM)
32{ 30{
33 parser_t *parser; 31 parser_t *parser;
34 char *tokens[4]; 32 char *tokens[4];
@@ -80,8 +78,8 @@ int lsusb_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
80 ACTION_RECURSE, 78 ACTION_RECURSE,
81 fileAction, 79 fileAction,
82 NULL, /* dirAction */ 80 NULL, /* dirAction */
83 NULL, /* userData */ 81 NULL /* userData */
84 0 /* depth */); 82 );
85 83
86 return EXIT_SUCCESS; 84 return EXIT_SUCCESS;
87} 85}
diff --git a/util-linux/mdev.c b/util-linux/mdev.c
index f42bebc20..59dbcf0cd 100644
--- a/util-linux/mdev.c
+++ b/util-linux/mdev.c
@@ -845,13 +845,12 @@ static ssize_t readlink2(char *buf, size_t bufsize)
845/* File callback for /sys/ traversal. 845/* File callback for /sys/ traversal.
846 * We act only on "/sys/.../dev" (pseudo)file 846 * We act only on "/sys/.../dev" (pseudo)file
847 */ 847 */
848static int FAST_FUNC fileAction(const char *fileName, 848static int FAST_FUNC fileAction(struct recursive_state *state,
849 struct stat *statbuf UNUSED_PARAM, 849 const char *fileName,
850 void *userData, 850 struct stat *statbuf UNUSED_PARAM)
851 int depth UNUSED_PARAM)
852{ 851{
853 size_t len = strlen(fileName) - 4; /* can't underflow */ 852 size_t len = strlen(fileName) - 4; /* can't underflow */
854 char *path = userData; /* char array[PATH_MAX + SCRATCH_SIZE] */ 853 char *path = state->userData; /* char array[PATH_MAX + SCRATCH_SIZE] */
855 char subsys[PATH_MAX]; 854 char subsys[PATH_MAX];
856 int res; 855 int res;
857 856
@@ -888,12 +887,11 @@ static int FAST_FUNC fileAction(const char *fileName,
888} 887}
889 888
890/* Directory callback for /sys/ traversal */ 889/* Directory callback for /sys/ traversal */
891static int FAST_FUNC dirAction(const char *fileName UNUSED_PARAM, 890static int FAST_FUNC dirAction(struct recursive_state *state,
892 struct stat *statbuf UNUSED_PARAM, 891 const char *fileName UNUSED_PARAM,
893 void *userData UNUSED_PARAM, 892 struct stat *statbuf UNUSED_PARAM)
894 int depth)
895{ 893{
896 return (depth >= MAX_SYSFS_DEPTH ? SKIP : TRUE); 894 return (state->depth >= MAX_SYSFS_DEPTH ? SKIP : TRUE);
897} 895}
898 896
899/* For the full gory details, see linux/Documentation/firmware_class/README 897/* For the full gory details, see linux/Documentation/firmware_class/README
@@ -1149,7 +1147,7 @@ static void initial_scan(char *temp)
1149 /* Create all devices from /sys/dev hierarchy */ 1147 /* Create all devices from /sys/dev hierarchy */
1150 recursive_action("/sys/dev", 1148 recursive_action("/sys/dev",
1151 ACTION_RECURSE | ACTION_FOLLOWLINKS, 1149 ACTION_RECURSE | ACTION_FOLLOWLINKS,
1152 fileAction, dirAction, temp, 0); 1150 fileAction, dirAction, temp);
1153} 1151}
1154 1152
1155#if ENABLE_FEATURE_MDEV_DAEMON 1153#if ENABLE_FEATURE_MDEV_DAEMON
diff --git a/util-linux/mount.c b/util-linux/mount.c
index b92e2c297..fc5161d7f 100644
--- a/util-linux/mount.c
+++ b/util-linux/mount.c
@@ -1230,6 +1230,7 @@ static NOINLINE int nfsmount(struct mntent *mp, unsigned long vfsflags, char *fi
1230 * then data pointer is interpreted as a string. */ 1230 * then data pointer is interpreted as a string. */
1231 struct nfs_mount_data data; 1231 struct nfs_mount_data data;
1232 char *opt; 1232 char *opt;
1233 char *tokstate;
1233 struct hostent *hp; 1234 struct hostent *hp;
1234 struct sockaddr_in server_addr; 1235 struct sockaddr_in server_addr;
1235 struct sockaddr_in mount_server_addr; 1236 struct sockaddr_in mount_server_addr;
@@ -1348,7 +1349,7 @@ static NOINLINE int nfsmount(struct mntent *mp, unsigned long vfsflags, char *fi
1348 nfsvers = 0; 1349 nfsvers = 0;
1349 1350
1350 /* parse options */ 1351 /* parse options */
1351 if (filteropts) for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) { 1352 if (filteropts) for (opt = strtok_r(filteropts, ",", &tokstate); opt; opt = strtok_r(NULL, ",", &tokstate)) {
1352 char *opteq = strchr(opt, '='); 1353 char *opteq = strchr(opt, '=');
1353 if (opteq) { 1354 if (opteq) {
1354 int val, idx; 1355 int val, idx;
@@ -1886,6 +1887,58 @@ static int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts)
1886 1887
1887#endif // !ENABLE_FEATURE_MOUNT_NFS 1888#endif // !ENABLE_FEATURE_MOUNT_NFS
1888 1889
1890// Find "...,NAME=NUM,..." in the option string, remove "NAME=NUM" option
1891// and return NUM.
1892// Return 0 if not found.
1893// All instances must be parsed and removed (for example, since kernel 5.4
1894// squashfs: Unknown parameter 'sizelimit'
1895// will result if loopback mount option "sizelimit=NNN" is not removed
1896// and squashfs sees it in option string).
1897static unsigned long long cut_out_ull_opt(char *opts, const char *name_eq)
1898{
1899 unsigned long long ret = 0;
1900
1901 if (!opts) // allow NULL opts (simplifies callers' work)
1902 return ret;
1903
1904 for (;;) {
1905 char *end;
1906 char *opt;
1907
1908 // Find comma-delimited "NAME="
1909 for (;;) {
1910 opt = strstr(opts, name_eq);
1911 if (!opt)
1912 return ret;
1913 if (opt == opts)
1914 break; // found it (it's first opt)
1915 if (opt[-1] == ',') {
1916 opts = opt - 1;
1917 break; // found it (it's not a first opt)
1918 }
1919 // False positive like "VNAME=", we are at "N".
1920 // - skip it, loop back to searching
1921 opts = opt + 1;
1922 }
1923
1924 ret = bb_strtoull(opt + strlen(name_eq), &end, 0);
1925 if (errno && errno != EINVAL) {
1926 err:
1927 bb_error_msg_and_die("bad option '%s'", opt);
1928 }
1929 if (*end == '\0') {
1930 // It is "[,]NAME=NUM\0" - truncate it and return
1931 *opts = '\0';
1932 return ret;
1933 }
1934 if (*end != ',')
1935 goto err;
1936 // We are at trailing comma
1937 // Remove "NAME=NUM," and loop back to check for duplicate opts
1938 overlapping_strcpy(opt, end + 1);
1939 }
1940}
1941
1889// Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem 1942// Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem
1890// type detection. Returns 0 for success, nonzero for failure. 1943// type detection. Returns 0 for success, nonzero for failure.
1891// NB: mp->xxx fields may be trashed on exit 1944// NB: mp->xxx fields may be trashed on exit
@@ -2029,9 +2082,16 @@ static int singlemount(struct mntent *mp, int ignore_busy)
2029 ) { 2082 ) {
2030 // Do we need to allocate a loopback device for it? 2083 // Do we need to allocate a loopback device for it?
2031 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) { 2084 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
2085 unsigned long long offset;
2086 unsigned long long sizelimit;
2087
2032 loopFile = bb_simplify_path(mp->mnt_fsname); 2088 loopFile = bb_simplify_path(mp->mnt_fsname);
2033 mp->mnt_fsname = NULL; // will receive malloced loop dev name 2089 mp->mnt_fsname = NULL; // will receive malloced loop dev name
2034 2090
2091 // Parse and remove loopback options
2092 offset = cut_out_ull_opt(filteropts, "offset=");
2093 sizelimit = cut_out_ull_opt(filteropts, "sizelimit=");
2094
2035 // mount always creates AUTOCLEARed loopdevs, so that umounting 2095 // mount always creates AUTOCLEARed loopdevs, so that umounting
2036 // drops them without any code in the userspace. 2096 // drops them without any code in the userspace.
2037 // This happens since circa linux-2.6.25: 2097 // This happens since circa linux-2.6.25:
@@ -2040,7 +2100,8 @@ static int singlemount(struct mntent *mp, int ignore_busy)
2040 // Subject: Allow auto-destruction of loop devices 2100 // Subject: Allow auto-destruction of loop devices
2041 loopfd = set_loop(&mp->mnt_fsname, 2101 loopfd = set_loop(&mp->mnt_fsname,
2042 loopFile, 2102 loopFile,
2043 0, 2103 offset,
2104 sizelimit,
2044 ((vfsflags & MS_RDONLY) ? BB_LO_FLAGS_READ_ONLY : 0) 2105 ((vfsflags & MS_RDONLY) ? BB_LO_FLAGS_READ_ONLY : 0)
2045 | BB_LO_FLAGS_AUTOCLEAR 2106 | BB_LO_FLAGS_AUTOCLEAR
2046 ); 2107 );
diff --git a/util-linux/setpriv.c b/util-linux/setpriv.c
index 37e8821a1..1e4b201ed 100644
--- a/util-linux/setpriv.c
+++ b/util-linux/setpriv.c
@@ -144,10 +144,11 @@ static unsigned parse_cap(const char *cap)
144static void set_inh_caps(char *capstring) 144static void set_inh_caps(char *capstring)
145{ 145{
146 struct caps caps; 146 struct caps caps;
147 char *string;
147 148
148 getcaps(&caps); 149 getcaps(&caps);
149 150
150 capstring = strtok(capstring, ","); 151 capstring = strtok_r(capstring, ",", &string);
151 while (capstring) { 152 while (capstring) {
152 unsigned cap; 153 unsigned cap;
153 154
@@ -159,7 +160,7 @@ static void set_inh_caps(char *capstring)
159 caps.data[CAP_TO_INDEX(cap)].inheritable |= CAP_TO_MASK(cap); 160 caps.data[CAP_TO_INDEX(cap)].inheritable |= CAP_TO_MASK(cap);
160 else 161 else
161 caps.data[CAP_TO_INDEX(cap)].inheritable &= ~CAP_TO_MASK(cap); 162 caps.data[CAP_TO_INDEX(cap)].inheritable &= ~CAP_TO_MASK(cap);
162 capstring = strtok(NULL, ","); 163 capstring = strtok_r(NULL, ",", &string);
163 } 164 }
164 165
165 if (capset(&caps.header, caps.data) != 0) 166 if (capset(&caps.header, caps.data) != 0)
@@ -170,7 +171,7 @@ static void set_ambient_caps(char *string)
170{ 171{
171 char *cap; 172 char *cap;
172 173
173 cap = strtok(string, ","); 174 cap = strtok_r(string, ",", &string);
174 while (cap) { 175 while (cap) {
175 unsigned idx; 176 unsigned idx;
176 177
@@ -182,7 +183,7 @@ static void set_ambient_caps(char *string)
182 if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_LOWER, idx, 0, 0) < 0) 183 if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_LOWER, idx, 0, 0) < 0)
183 bb_simple_perror_msg("cap_ambient_lower"); 184 bb_simple_perror_msg("cap_ambient_lower");
184 } 185 }
185 cap = strtok(NULL, ","); 186 cap = strtok_r(NULL, ",", &string);
186 } 187 }
187} 188}
188#endif /* FEATURE_SETPRIV_CAPABILITIES */ 189#endif /* FEATURE_SETPRIV_CAPABILITIES */
diff --git a/util-linux/switch_root.c b/util-linux/switch_root.c
index c65096c27..f2674b5ac 100644
--- a/util-linux/switch_root.c
+++ b/util-linux/switch_root.c
@@ -164,7 +164,7 @@ static void drop_capabilities(char *string)
164{ 164{
165 char *cap; 165 char *cap;
166 166
167 cap = strtok(string, ","); 167 cap = strtok_r(string, ",", &string);
168 while (cap) { 168 while (cap) {
169 unsigned cap_idx; 169 unsigned cap_idx;
170 170
@@ -174,7 +174,7 @@ static void drop_capabilities(char *string)
174 drop_bounding_set(cap_idx); 174 drop_bounding_set(cap_idx);
175 drop_capset(cap_idx); 175 drop_capset(cap_idx);
176 bb_error_msg("dropped capability: %s", cap); 176 bb_error_msg("dropped capability: %s", cap);
177 cap = strtok(NULL, ","); 177 cap = strtok_r(NULL, ",", &string);
178 } 178 }
179} 179}
180#endif 180#endif
diff --git a/util-linux/volume_id/get_devname.c b/util-linux/volume_id/get_devname.c
index 34f5d119f..00cfb2826 100644
--- a/util-linux/volume_id/get_devname.c
+++ b/util-linux/volume_id/get_devname.c
@@ -102,10 +102,9 @@ uuidcache_addentry(char *device, /*int major, int minor,*/ char *label, char *uu
102 * add a cache entry for this device. 102 * add a cache entry for this device.
103 * If device node does not exist, it will be temporarily created. */ 103 * If device node does not exist, it will be temporarily created. */
104static int FAST_FUNC 104static int FAST_FUNC
105uuidcache_check_device(const char *device, 105uuidcache_check_device(struct recursive_state *state UNUSED_PARAM,
106 struct stat *statbuf, 106 const char *device,
107 void *userData UNUSED_PARAM, 107 struct stat *statbuf)
108 int depth UNUSED_PARAM)
109{ 108{
110 /* note: this check rejects links to devices, among other nodes */ 109 /* note: this check rejects links to devices, among other nodes */
111 if (!S_ISBLK(statbuf->st_mode) 110 if (!S_ISBLK(statbuf->st_mode)
@@ -145,12 +144,13 @@ uuidcache_init(int scan_devices)
145 * This is unacceptably complex. Let's just scan /dev. 144 * This is unacceptably complex. Let's just scan /dev.
146 * (Maybe add scanning of /sys/block/XXX/dev for devices 145 * (Maybe add scanning of /sys/block/XXX/dev for devices
147 * somehow not having their /dev/XXX entries created?) */ 146 * somehow not having their /dev/XXX entries created?) */
148 if (scan_devices) 147 if (scan_devices) {
149 recursive_action("/dev", ACTION_RECURSE, 148 recursive_action("/dev", ACTION_RECURSE,
150 uuidcache_check_device, /* file_action */ 149 uuidcache_check_device, /* file_action */
151 NULL, /* dir_action */ 150 NULL, /* dir_action */
152 NULL, /* userData */ 151 NULL /* userData */
153 0 /* depth */); 152 );
153 }
154 154
155 return uuidCache; 155 return uuidCache;
156} 156}