From afb5d8b2db432b8433ba80a1206ae41ef4f4ea8b Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 29 Sep 2020 14:19:17 +0200 Subject: ntpd: fix the case where two replies received at once and first one causes a step function old new delta recv_and_process_peer_pkt 2476 2486 +10 Signed-off-by: Denys Vlasenko --- networking/ntpd.c | 9 +++++++++ 1 file changed, 9 insertions(+) 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) offset = 0; + /* The below can happen as follows: + * = we receive two peer rsponses at once. + * = recv_and_process_peer_pkt(PEER1) -> update_local_clock() + * -> step_time() and it closes all other fds, sets all ->fd to -1. + * = recv_and_process_peer_pkt(PEER2) sees PEER2->fd == -1 + */ + if (p->p_fd < 0) + return; + /* We can recvfrom here and check from.IP, but some multihomed * ntp servers reply from their *other IP*. * TODO: maybe we should check at least what we can: from.port == 123? -- cgit v1.2.3-55-g6feb From 777a6357c09e1e91acfde060144dd019e3baaf3c Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 29 Sep 2020 16:25:32 +0200 Subject: ash: remove a tentative TODO, it's a wrong idea Upstream tried it, and then reverted by: From: Herbert Xu Date: Tue, 26 May 2020 23:19:05 +1000 parser: Fix double-backslash nl in old-style command sub Signed-off-by: Denys Vlasenko --- shell/ash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/ash.c b/shell/ash.c index ecb9b132b..ac25866ec 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -12811,7 +12811,7 @@ parsebackq: { goto done; case '\\': - pc = pgetc(); /* or pgetc_eatbnl()? why (example)? */ + pc = pgetc(); /* not pgetc_eatbnl! */ if (pc != '\\' && pc != '`' && pc != '$' && (!synstack->dblquote || pc != '"') ) { -- cgit v1.2.3-55-g6feb From 8d5f465a206e307bc47d8157b6c90152ddd2ab3c Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 29 Sep 2020 16:44:46 +0200 Subject: ash: jobs: Fix infinite loop in waitproc Upstream commit: Date: Fri, 10 Apr 2020 21:03:09 +1000 jobs: Fix infinite loop in waitproc After we changed the resetting of gotsigchld so that it is only done if jp is NULL, we can now get an infinite loop in waitproc if gotsigchld is set but there is no outstanding child because everything had been waited for previously without gotsigchld being zeroed. This patch fixes it by always zeroing gotsigchld as we did before. The bug that the previous patch was trying to fix is now resolved by switching the blocking mode to DOWAIT_NORMAL after the specified job has been completed so that we really do wait for all outstanding dead children. Reported-by: Harald van Dijk Fixes: 6c691b3e5099 ("jobs: Only clear gotsigchld when waiting...") Signed-off-by: Herbert Xu function old new delta dowait 553 631 +78 Signed-off-by: Denys Vlasenko --- shell/ash.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index ac25866ec..13470b2fa 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -4453,15 +4453,28 @@ waitone(int block, struct job *job) static int dowait(int block, struct job *jp) { - int pid = block == DOWAIT_NONBLOCK ? got_sigchld : 1; + smallint gotchld = *(volatile smallint *)&got_sigchld; + int rpid; + int pid; + + if (jp && jp->state != JOBRUNNING) + block = DOWAIT_NONBLOCK; + + if (block == DOWAIT_NONBLOCK && !gotchld) + return 1; + + rpid = 1; - while (jp ? jp->state == JOBRUNNING : pid > 0) { - if (!jp) - got_sigchld = 0; + do { + got_sigchld = 0; pid = waitone(block, jp); - } + rpid &= !!pid; - return pid; + if (!pid || (jp && jp->state != JOBRUNNING)) + block = DOWAIT_NONBLOCK; + } while (pid >= 0); + + return rpid; } #if JOBS -- cgit v1.2.3-55-g6feb From 91e11eba6e3ee21f70905bca857b9b216fb764f6 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 29 Sep 2020 20:35:55 +0200 Subject: ash: jobs: Fix waitcmd busy loop Upstream commit: Date: Tue, 2 Jun 2020 23:46:48 +1000 jobs: Fix waitcmd busy loop We need to clear gotsigchld in waitproc because it is used as a loop conditional for the waitcmd case. Without it waitcmd may busy loop after a SIGCHLD. This patch also changes gotsigchld into a volatile sig_atomic_t to prevent compilers from optimising its accesses away. Fixes: 6c691b3e5099 ("jobs: Only clear gotsigchld when waiting...") Signed-off-by: Herbert Xu This change also incorporates other changes to bring us closer to upstream. function old new delta dowait 553 636 +83 Signed-off-by: Denys Vlasenko --- shell/ash.c | 91 +++++++++++++++++++++++-------------------------------------- 1 file changed, 34 insertions(+), 57 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index 13470b2fa..07aa2da2e 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -4273,64 +4273,55 @@ sprint_status48(char *os, int status, int sigonly) return s - os; } +#define DOWAIT_NONBLOCK 0 +#define DOWAIT_BLOCK 1 +#define DOWAIT_BLOCK_OR_SIG 2 +#if BASH_WAIT_N +# define DOWAIT_JOBSTATUS 0x10 /* OR this to get job's exitstatus instead of pid */ +#endif + static int -wait_block_or_sig(int *status) +waitproc(int block, int *status) { - int pid; + sigset_t oldmask; + int flags = block == DOWAIT_BLOCK ? 0 : WNOHANG; + int err; - do { - sigset_t mask; +#if JOBS + if (doing_jobctl) + flags |= WUNTRACED; +#endif - /* Poll all children for changes in their state */ + do { got_sigchld = 0; - /* if job control is active, accept stopped processes too */ - pid = waitpid(-1, status, doing_jobctl ? (WNOHANG|WUNTRACED) : WNOHANG); - if (pid != 0) - break; /* Error (e.g. EINTR, ECHILD) or pid */ + do + err = waitpid(-1, status, flags); + while (err < 0 && errno == EINTR); - /* Children exist, but none are ready. Sleep until interesting signal */ -#if 1 - sigfillset(&mask); - sigprocmask2(SIG_SETMASK, &mask); /* mask is updated */ - while (!got_sigchld && !pending_sig) { - sigsuspend(&mask); - /* ^^^ add "sigdelset(&mask, SIGCHLD);" before sigsuspend - * to make sure SIGCHLD is not masked off? - * It was reported that this: - * fn() { : | return; } - * shopt -s lastpipe - * fn - * exec ash SCRIPT - * under bash 4.4.23 runs SCRIPT with SIGCHLD masked, - * making "wait" commands in SCRIPT block forever. - */ - } - sigprocmask(SIG_SETMASK, &mask, NULL); -#else /* unsafe: a signal can set pending_sig after check, but before pause() */ + if (err || (err = -!block)) + break; + + sigfillset(&oldmask); + sigprocmask2(SIG_SETMASK, &oldmask); /* mask is updated */ while (!got_sigchld && !pending_sig) - pause(); -#endif + sigsuspend(&oldmask); + sigprocmask(SIG_SETMASK, &oldmask, NULL); + //simpler, but unsafe: a signal can set pending_sig after check, but before pause(): + //while (!got_sigchld && !pending_sig) + // pause(); - /* If it was SIGCHLD, poll children again */ } while (got_sigchld); - return pid; + return err; } -#define DOWAIT_NONBLOCK 0 -#define DOWAIT_BLOCK 1 -#define DOWAIT_BLOCK_OR_SIG 2 -#if BASH_WAIT_N -# define DOWAIT_JOBSTATUS 0x10 /* OR this to get job's exitstatus instead of pid */ -#endif - static int waitone(int block, struct job *job) { int pid; int status; struct job *jp; - struct job *thisjob; + struct job *thisjob = NULL; #if BASH_WAIT_N bool want_jobexitstatus = (block & DOWAIT_JOBSTATUS); block = (block & ~DOWAIT_JOBSTATUS); @@ -4357,21 +4348,8 @@ waitone(int block, struct job *job) * SIG_DFL handler does not wake sigsuspend(). */ INT_OFF; - if (block == DOWAIT_BLOCK_OR_SIG) { - pid = wait_block_or_sig(&status); - } else { - int wait_flags = 0; - if (block == DOWAIT_NONBLOCK) - wait_flags = WNOHANG; - /* if job control is active, accept stopped processes too */ - if (doing_jobctl) - wait_flags |= WUNTRACED; - /* NB: _not_ safe_waitpid, we need to detect EINTR */ - pid = waitpid(-1, &status, wait_flags); - } - TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n", - pid, status, errno, strerror(errno))); - thisjob = NULL; + pid = waitproc(block, &status); + TRACE(("wait returns pid %d, status=%d\n", pid, status)); if (pid <= 0) goto out; @@ -4466,7 +4444,6 @@ dowait(int block, struct job *jp) rpid = 1; do { - got_sigchld = 0; pid = waitone(block, jp); rpid &= !!pid; @@ -4721,7 +4698,7 @@ waitcmd(int argc UNUSED_PARAM, char **argv) job = getjob(*argv, 0); } /* loop until process terminated or stopped */ - dowait(DOWAIT_BLOCK_OR_SIG, NULL); + dowait(DOWAIT_BLOCK_OR_SIG, job); if (pending_sig) goto sigout; job->waited = 1; -- cgit v1.2.3-55-g6feb From d4dd48f2948b006f4ccb7cc2b603fb3d00f90685 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 29 Sep 2020 22:36:36 +0200 Subject: shell: add testsuite for "wait $pid" waiting for other tasks Signed-off-by: Denys Vlasenko --- shell/ash_test/ash-misc/wait7.right | 2 ++ shell/ash_test/ash-misc/wait7.tests | 7 +++++++ shell/hush_test/hush-misc/wait7.right | 2 ++ shell/hush_test/hush-misc/wait7.tests | 7 +++++++ 4 files changed, 18 insertions(+) create mode 100644 shell/ash_test/ash-misc/wait7.right create mode 100755 shell/ash_test/ash-misc/wait7.tests create mode 100644 shell/hush_test/hush-misc/wait7.right create mode 100755 shell/hush_test/hush-misc/wait7.tests 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 @@ +Background1 +Ok: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 @@ +sleep 1 && echo "Background1" & +pid=$! +sleep 3 && echo "Background2: BUG!" & +# Shouldn't wait for 2nd bkgd: +wait $pid +kill $! +echo Ok:$? 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 @@ +Background1 +Ok: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 @@ +sleep 1 && echo "Background1" & +pid=$! +sleep 3 && echo "Background2: BUG!" & +# Shouldn't wait for 2nd bkgd: +wait $pid +kill $! +echo Ok:$? -- cgit v1.2.3-55-g6feb From bd202a5ec15b82ba9562cdd81157e703349d8459 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 30 Sep 2020 00:00:43 +0200 Subject: xargs: fix -I SUBSTR behaviour function old new delta process_stdin_with_replace 165 204 +39 Signed-off-by: Denys Vlasenko --- findutils/xargs.c | 21 ++++++++++++++++----- testsuite/xargs.tests | 8 ++++++++ 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/findutils/xargs.c b/findutils/xargs.c index ff04bfe7c..e2b3527f3 100644 --- a/findutils/xargs.c +++ b/findutils/xargs.c @@ -80,9 +80,11 @@ /* This is a NOEXEC applet. Be very careful! */ -//#define dbg_msg(...) bb_error_msg(__VA_ARGS__) -#define dbg_msg(...) ((void)0) - +#if 0 +# define dbg_msg(...) bb_error_msg(__VA_ARGS__) +#else +# define dbg_msg(...) ((void)0) +#endif #ifdef TEST # ifndef ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION @@ -466,9 +468,18 @@ static char* FAST_FUNC process_stdin_with_replace(int n_max_chars, int n_max_arg while (1) { int c = getchar(); + if (p == buf) { + if (c == EOF) + goto ret; /* last line is empty, return "" */ + if (c == G.eol_ch) + continue; /* empty line, ignore */ + /* Skip leading whitespace of each line: try + * echo -e ' \t\v1 2 3 ' | xargs -I% echo '[%]' + */ + if (ISSPACE(c)) + continue; + } if (c == EOF || c == G.eol_ch) { - if (p == buf) - goto ret; /* empty line */ c = '\0'; } *p++ = c; 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" \ SKIP= +optional FEATURE_XARGS_SUPPORT_QUOTES +testing "xargs -I skips empty lines and leading whitespace" \ + "xargs -I% echo '[%]'" \ + "[2]\n[4]\n[6 6 ]\n[7]\n" \ + "" " \n2\n\n4\n\n 6 6 \n \v \t 7\n\t\n\v\n" + +SKIP= + exit $FAILCOUNT -- cgit v1.2.3-55-g6feb From abefc3c276ac2450f756c5232745a145bd14dbdf Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 30 Sep 2020 22:22:04 +0200 Subject: libbb: fold common64_hash() into its only user Signed-off-by: Denys Vlasenko --- libbb/hash_md5_sha.c | 75 ++++++++++++++++++++-------------------------------- 1 file changed, 28 insertions(+), 47 deletions(-) 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) return (x << n) | (x >> (64 - n)); } -/* Feed data through a temporary buffer. - * The internal buffer remembers previous data until it has 64 - * bytes worth to pass on. - */ -static void FAST_FUNC common64_hash(md5_ctx_t *ctx, const void *buffer, size_t len) -{ - unsigned bufpos = ctx->total64 & 63; - - ctx->total64 += len; - - while (1) { - unsigned remaining = 64 - bufpos; - if (remaining > len) - remaining = len; - /* Copy data into aligned buffer */ - memcpy(ctx->wbuffer + bufpos, buffer, remaining); - len -= remaining; - buffer = (const char *)buffer + remaining; - bufpos += remaining; - /* Clever way to do "if (bufpos != N) break; ... ; bufpos = 0;" */ - bufpos -= 64; - if (bufpos != 0) - break; - /* Buffer is filled up, process it */ - ctx->process_block(ctx); - /*bufpos = 0; - already is */ - } -} - /* Process the remaining bytes in the buffer */ static void FAST_FUNC common64_end(md5_ctx_t *ctx, int swap_needed) { @@ -449,7 +420,29 @@ void FAST_FUNC md5_begin(md5_ctx_t *ctx) /* Used also for sha1 and sha256 */ void FAST_FUNC md5_hash(md5_ctx_t *ctx, const void *buffer, size_t len) { - common64_hash(ctx, buffer, len); + unsigned bufpos = ctx->total64 & 63; + + ctx->total64 += len; + + while (1) { + unsigned remaining = 64 - bufpos; + if (remaining > len) + remaining = len; + /* Copy data into aligned buffer */ + memcpy(ctx->wbuffer + bufpos, buffer, remaining); + len -= remaining; + buffer = (const char *)buffer + remaining; + bufpos += remaining; + + /* Clever way to do "if (bufpos != N) break; ... ; bufpos = 0;" */ + bufpos -= 64; + if (bufpos != 0) + break; + + /* Buffer is filled up, process it */ + ctx->process_block(ctx); + /*bufpos = 0; - already is */ + } } /* 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) int i; /* Two extra iterations zero out ctx->total64[2] */ uint64_t *tp = ctx->total64; - for (i = 0; i < 2+8; i++) + for (i = 0; i < 8 + 2; i++) tp[i] = ((uint64_t)(init256[i]) << 32) + init512_lo[i]; /*ctx->total64[0] = ctx->total64[1] = 0; - already done */ } @@ -832,22 +825,7 @@ void FAST_FUNC sha512_hash(sha512_ctx_t *ctx, const void *buffer, size_t len) ctx->total64[0] += len; if (ctx->total64[0] < len) ctx->total64[1]++; -# if 0 - remaining = 128 - bufpos; - /* Hash whole blocks */ - while (len >= remaining) { - memcpy(ctx->wbuffer + bufpos, buffer, remaining); - buffer = (const char *)buffer + remaining; - len -= remaining; - remaining = 128; - bufpos = 0; - sha512_process_block128(ctx); - } - - /* Save last, partial blosk */ - memcpy(ctx->wbuffer + bufpos, buffer, len); -# else while (1) { remaining = 128 - bufpos; if (remaining > len) @@ -857,15 +835,16 @@ void FAST_FUNC sha512_hash(sha512_ctx_t *ctx, const void *buffer, size_t len) len -= remaining; buffer = (const char *)buffer + remaining; bufpos += remaining; + /* Clever way to do "if (bufpos != N) break; ... ; bufpos = 0;" */ bufpos -= 128; if (bufpos != 0) break; + /* Buffer is filled up, process it */ sha512_process_block128(ctx); /*bufpos = 0; - already is */ } -# endif } #endif /* NEED_SHA512 */ @@ -1398,10 +1377,12 @@ void FAST_FUNC sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len) bufpos++; remaining--; } + /* Clever way to do "if (bufpos != N) break; ... ; bufpos = 0;" */ bufpos -= ctx->input_block_bytes; if (bufpos != 0) break; + /* Buffer is filled up, process it */ sha3_process_block72(ctx->state); /*bufpos = 0; - already is */ -- cgit v1.2.3-55-g6feb From 6561e07460acc5b38af99aa5e80283dc04953eca Mon Sep 17 00:00:00 2001 From: Steffen Trumtrar Date: Wed, 29 Jul 2020 10:43:53 +0200 Subject: mount: support the sizelimit and offset option for loop devices Starting with linux kernel v5.4 squashfs has a more strict parameter checking implemented. Unlike util-linux mount, busybox never supported the sizelimit option but simply forwards it to the kernel. Since v5.4 mounting will fail with squashfs: Unknown parameter 'sizelimit' Support the sizelimit parameter by setting it in the LOOP_SET_STATUS64 structure before handing it to the kernel. While at it also add support for the offset option, which currently will always be set to 0. function old new delta cut_out_ull_opt - 167 +167 singlemount 1230 1266 +36 set_loop 834 862 +28 losetup_main 479 483 +4 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 3/0 up/down: 235/0) Total: 235 bytes Signed-off-by: Steffen Trumtrar Signed-off-by: Denys Vlasenko --- include/libbb.h | 3 ++- libbb/loop.c | 4 +++- util-linux/losetup.c | 2 +- util-linux/mount.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 67 insertions(+), 4 deletions(-) diff --git a/include/libbb.h b/include/libbb.h index 8c7978456..83ecca47f 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -1515,7 +1515,8 @@ int del_loop(const char *device) FAST_FUNC; * malloc and return it in *devname. * return value is the opened fd to the loop device, or < on error */ -int set_loop(char **devname, const char *file, unsigned long long offset, unsigned flags) FAST_FUNC; +int set_loop(char **devname, const char *file, unsigned long long offset, + unsigned long long sizelimit, unsigned flags) FAST_FUNC; /* These constants match linux/loop.h (without BB_ prefix): */ #define BB_LO_FLAGS_READ_ONLY 1 #define BB_LO_FLAGS_AUTOCLEAR 4 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) * search will re-use an existing loop device already bound to that * file/offset if it finds one. */ -int FAST_FUNC set_loop(char **device, const char *file, unsigned long long offset, unsigned flags) +int FAST_FUNC set_loop(char **device, const char *file, unsigned long long offset, + unsigned long long sizelimit, unsigned flags) { char dev[LOOP_NAMESIZE]; char *try; @@ -185,6 +186,7 @@ int FAST_FUNC set_loop(char **device, const char *file, unsigned long long offse memset(&loopinfo, 0, sizeof(loopinfo)); safe_strncpy((char *)loopinfo.lo_file_name, file, LO_NAME_SIZE); loopinfo.lo_offset = offset; + loopinfo.lo_sizelimit = sizelimit; /* * Used by mount to set LO_FLAGS_AUTOCLEAR. * LO_FLAGS_READ_ONLY is not set because RO is controlled by open type of the file. 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) if (opt & OPT_P) { flags |= BB_LO_FLAGS_PARTSCAN; } - if (set_loop(&d, argv[0], offset, flags) < 0) + if (set_loop(&d, argv[0], offset, 0, flags) < 0) bb_simple_perror_msg_and_die(argv[0]); return EXIT_SUCCESS; } diff --git a/util-linux/mount.c b/util-linux/mount.c index b92e2c297..19ac13930 100644 --- a/util-linux/mount.c +++ b/util-linux/mount.c @@ -1886,6 +1886,58 @@ static int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts) #endif // !ENABLE_FEATURE_MOUNT_NFS +// Find "...,NAME=NUM,..." in the option string, remove "NAME=NUM" option +// and return NUM. +// Return 0 if not found. +// All instances must be parsed and removed (for example, since kernel 5.4 +// squashfs: Unknown parameter 'sizelimit' +// will result if loopback mount option "sizelimit=NNN" is not removed +// and squashfs sees it in option string). +static unsigned long long cut_out_ull_opt(char *opts, const char *name_eq) +{ + unsigned long long ret = 0; + + if (!opts) // allow NULL opts (simplifies callers' work) + return ret; + + for (;;) { + char *end; + char *opt; + + // Find comma-delimited "NAME=" + for (;;) { + opt = strstr(opts, name_eq); + if (!opt) + return ret; + if (opt == opts) + break; // found it (it's first opt) + if (opt[-1] == ',') { + opts = opt - 1; + break; // found it (it's not a first opt) + } + // False positive like "VNAME=", we are at "N". + // - skip it, loop back to searching + opts = opt + 1; + } + + ret = bb_strtoull(opt + strlen(name_eq), &end, 0); + if (errno && errno != EINVAL) { + err: + bb_error_msg_and_die("bad option '%s'", opt); + } + if (*end == '\0') { + // It is "[,]NAME=NUM\0" - truncate it and return + *opts = '\0'; + return ret; + } + if (*end != ',') + goto err; + // We are at trailing comma + // Remove "NAME=NUM," and loop back to check for duplicate opts + overlapping_strcpy(opt, end + 1); + } +} + // Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem // type detection. Returns 0 for success, nonzero for failure. // NB: mp->xxx fields may be trashed on exit @@ -2029,9 +2081,16 @@ static int singlemount(struct mntent *mp, int ignore_busy) ) { // Do we need to allocate a loopback device for it? if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) { + unsigned long long offset; + unsigned long long sizelimit; + loopFile = bb_simplify_path(mp->mnt_fsname); mp->mnt_fsname = NULL; // will receive malloced loop dev name + // Parse and remove loopback options + offset = cut_out_ull_opt(filteropts, "offset="); + sizelimit = cut_out_ull_opt(filteropts, "sizelimit="); + // mount always creates AUTOCLEARed loopdevs, so that umounting // drops them without any code in the userspace. // This happens since circa linux-2.6.25: @@ -2040,7 +2099,8 @@ static int singlemount(struct mntent *mp, int ignore_busy) // Subject: Allow auto-destruction of loop devices loopfd = set_loop(&mp->mnt_fsname, loopFile, - 0, + offset, + sizelimit, ((vfsflags & MS_RDONLY) ? BB_LO_FLAGS_READ_ONLY : 0) | BB_LO_FLAGS_AUTOCLEAR ); -- cgit v1.2.3-55-g6feb From b1c7a66ca6ff913afba17cfe6f16a78a66915c07 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 1 Oct 2020 00:34:44 +0200 Subject: libbb: extend "errno pointer" trick to other than __GLIBC__ Savings on musl: function old new delta resume_main 582 614 +32 lbb_prepare - 20 +20 seq_main 432 449 +17 fgetsetversion 74 88 +14 ... script_main 1207 1180 -27 close_silently 28 - -28 shell_builtin_ulimit 655 626 -29 lineedit_read_key 280 247 -33 ------------------------------------------------------------------------------ (add/remove: 2/1 grow/shrink: 25/123 up/down: 182/-882) Total: -700 bytes text data bss dec hex filename 1005858 551 5676 1012085 f7175 busybox_old 1005136 551 5680 1011367 f6ea7 busybox_unstripped Signed-off-by: Denys Vlasenko --- include/libbb.h | 7 ++++--- libbb/appletlib.c | 2 +- libbb/ptr_to_globals.c | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/include/libbb.h b/include/libbb.h index 83ecca47f..df7e45404 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -339,12 +339,13 @@ struct BUG_off_t_size_is_misdetected { #endif #endif -#if defined(__GLIBC__) -/* glibc uses __errno_location() to get a ptr to errno */ -/* We can just memorize it once - no multithreading in busybox :) */ +#if defined(errno) +/* If errno is a define, assume it's "define errno (*__errno_location())" + * and we will cache it's result in this variable */ extern int *const bb_errno; #undef errno #define errno (*bb_errno) +#define bb_cached_errno_ptr 1 #endif #if !(ULONG_MAX > 0xffffffff) diff --git a/libbb/appletlib.c b/libbb/appletlib.c index a515c3fe3..717c63649 100644 --- a/libbb/appletlib.c +++ b/libbb/appletlib.c @@ -303,7 +303,7 @@ void lbb_prepare(const char *applet void lbb_prepare(const char *applet IF_FEATURE_INDIVIDUAL(, char **argv)) { -#ifdef __GLIBC__ +#ifdef bb_cached_errno_ptr (*(int **)not_const_pp(&bb_errno)) = __errno_location(); barrier(); #endif 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; * but here we make it live in R/W memory */ struct globals *ptr_to_globals; -#ifdef __GLIBC__ +#ifdef errno int *bb_errno; #endif @@ -27,7 +27,7 @@ int *bb_errno; * on weird architectures, compilers, linkers and so on */ struct globals *const ptr_to_globals __attribute__ ((section (".data"))); -#ifdef __GLIBC__ +#ifdef errno int *const bb_errno __attribute__ ((section (".data"))); #endif -- cgit v1.2.3-55-g6feb From 841878e7eeb8861aa1b6f94436b8dcf3b064d1e4 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 1 Oct 2020 00:57:05 +0200 Subject: shell: remove FAST_FUNC from a static function Signed-off-by: Denys Vlasenko --- shell/math.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 { } remembered_name; -static arith_t FAST_FUNC +static arith_t evaluate_string(arith_state_t *math_state, const char *expr); static const char* @@ -582,7 +582,7 @@ static arith_t strto_arith_t(const char *nptr, char **endptr) # endif #endif -static arith_t FAST_FUNC +static arith_t evaluate_string(arith_state_t *math_state, const char *expr) { operator lasttok; -- cgit v1.2.3-55-g6feb From aaa0709e7b39d0dc22ac92443a86c84eaff58679 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 1 Oct 2020 01:44:26 +0200 Subject: libbb: do not open-code __errno_location() call Thanks dalias! Signed-off-by: Denys Vlasenko --- libbb/appletlib.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libbb/appletlib.c b/libbb/appletlib.c index 717c63649..5f59f1273 100644 --- a/libbb/appletlib.c +++ b/libbb/appletlib.c @@ -25,6 +25,11 @@ * * FEATURE_INSTALLER or FEATURE_SUID will still link printf routines in. :( */ + +/* Define this accessor before we #define "errno" our way */ +#include +static inline int *get_perrno(void) { return &errno; } + #include "busybox.h" #if !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \ @@ -304,7 +309,7 @@ void lbb_prepare(const char *applet IF_FEATURE_INDIVIDUAL(, char **argv)) { #ifdef bb_cached_errno_ptr - (*(int **)not_const_pp(&bb_errno)) = __errno_location(); + (*(int **)not_const_pp(&bb_errno)) = get_perrno(); barrier(); #endif applet_name = applet; -- cgit v1.2.3-55-g6feb From 4a0eb0370c4df8ee01973b50bb460560532b79f1 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 1 Oct 2020 03:07:22 +0200 Subject: gcc-9.x warning fixes Signed-off-by: Denys Vlasenko --- archival/tar.c | 3 ++- include/libbb.h | 2 +- libbb/inet_cksum.c | 4 +++- networking/ping.c | 4 ++-- networking/traceroute.c | 2 +- networking/udhcp/d6_dhcpc.c | 2 +- networking/udhcp/d6_packet.c | 2 +- networking/udhcp/dhcpc.c | 4 ++-- networking/udhcp/packet.c | 4 ++-- 9 files changed, 15 insertions(+), 12 deletions(-) diff --git a/archival/tar.c b/archival/tar.c index 4ab38db29..4f2564813 100644 --- a/archival/tar.c +++ b/archival/tar.c @@ -297,7 +297,8 @@ static void writeLongname(int fd, int type, const char *name, int dir) header.typeflag = type; strcpy(header.name, "././@LongLink"); /* This sets mode/uid/gid/mtime to "00...00" strings */ - memset(header.mode, '0', sizeof(struct prefilled)); + memset((char*)&header + offsetof(struct tar_header_t, mode), /* make gcc-9.x happy */ + '0', sizeof(struct prefilled)); header.mode [sizeof(header.mode ) - 1] = '\0'; header.uid [sizeof(header.uid ) - 1] = '\0'; header.gid [sizeof(header.gid ) - 1] = '\0'; diff --git a/include/libbb.h b/include/libbb.h index df7e45404..3e23b5bbd 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -818,7 +818,7 @@ ssize_t recv_from_to(int fd, void *buf, size_t len, int flags, struct sockaddr *to, socklen_t sa_size) FAST_FUNC; -uint16_t inet_cksum(uint16_t *addr, int len) FAST_FUNC; +uint16_t inet_cksum(const void *addr, int len) FAST_FUNC; int parse_pasv_epsv(char *buf) FAST_FUNC; /* 0 if argv[0] is NULL: */ 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 @@ #include "libbb.h" -uint16_t FAST_FUNC inet_cksum(uint16_t *addr, int nleft) +uint16_t FAST_FUNC inet_cksum(const void *ptr, int nleft) { + const uint16_t *addr = ptr; + /* * Our algorithm is simple, using a 32 bit accumulator, * we add sequential 16 bit words to it, and at the end, fold 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) /*memset(pkt, 0, sizeof(G.packet)); already is */ pkt->icmp_type = ICMP_ECHO; pkt->icmp_id = G.myid; - pkt->icmp_cksum = inet_cksum((uint16_t *) pkt, sizeof(G.packet)); + pkt->icmp_cksum = inet_cksum(pkt, sizeof(G.packet)); xsendto(pingsock, G.packet, DEFDATALEN + ICMP_MINLEN, &lsa->u.sa, lsa->len); @@ -529,7 +529,7 @@ static void sendping4(int junk UNUSED_PARAM) /* No hton: we'll read it back on the same machine */ *(uint32_t*)&pkt->icmp_dun = G.cur_us = monotonic_us(); - pkt->icmp_cksum = inet_cksum((uint16_t *) pkt, datalen + ICMP_MINLEN); + pkt->icmp_cksum = inet_cksum(pkt, datalen + ICMP_MINLEN); sendping_tail(sendping4, ICMP_MINLEN); } 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) /* Always calculate checksum for icmp packets */ outicmp->icmp_cksum = 0; outicmp->icmp_cksum = inet_cksum( - (uint16_t *)outicmp, + outicmp, ((char*)outip + packlen) - (char*)outicmp ); if (outicmp->icmp_cksum == 0) 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 // packet.ip.tot_len = packet.udp.len; /* yes, this is needed */ // check = packet.udp.check; // packet.udp.check = 0; -// if (check && check != inet_cksum((uint16_t *)&packet, bytes)) { +// if (check && check != inet_cksum(&packet, bytes)) { // log1("packet with bad UDP checksum received, ignoring"); // return -2; // } 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( */ packet.ip6.ip6_hlim = IPPROTO_UDP; packet.udp.check = inet_cksum( - (uint16_t *)&packet + 2, + (uint8_t *)&packet + 4, offsetof(struct ip6_udp_d6_packet, data) - 4 + d6_pkt_size ); /* 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) /* verify IP checksum */ check = packet.ip.check; packet.ip.check = 0; - if (check != inet_cksum((uint16_t *)&packet.ip, sizeof(packet.ip))) { + if (check != inet_cksum(&packet.ip, sizeof(packet.ip))) { log1s("bad IP header checksum, ignoring"); return -2; } @@ -960,7 +960,7 @@ static NOINLINE int udhcp_recv_raw_packet(struct dhcp_packet *dhcp_pkt, int fd) packet.ip.tot_len = packet.udp.len; /* yes, this is needed */ check = packet.udp.check; packet.udp.check = 0; - if (check && check != inet_cksum((uint16_t *)&packet, bytes)) { + if (check && check != inet_cksum(&packet, bytes)) { log1s("packet with bad UDP checksum received, ignoring"); return -2; } 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, packet.udp.len = htons(UDP_DHCP_SIZE - padding); /* for UDP checksumming, ip.len is set to UDP packet len */ packet.ip.tot_len = packet.udp.len; - packet.udp.check = inet_cksum((uint16_t *)&packet, + packet.udp.check = inet_cksum(&packet, IP_UDP_DHCP_SIZE - padding); /* but for sending, it is set to IP packet len */ packet.ip.tot_len = htons(IP_UDP_DHCP_SIZE - padding); packet.ip.ihl = sizeof(packet.ip) >> 2; packet.ip.version = IPVERSION; packet.ip.ttl = IPDEFTTL; - packet.ip.check = inet_cksum((uint16_t *)&packet.ip, sizeof(packet.ip)); + packet.ip.check = inet_cksum(&packet.ip, sizeof(packet.ip)); udhcp_dump_packet(dhcp_pkt); result = sendto(fd, &packet, IP_UDP_DHCP_SIZE - padding, /*flags:*/ 0, -- cgit v1.2.3-55-g6feb From 3c3928fc658bddacd4e540589868bc28a8e4ca0a Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 1 Oct 2020 20:27:28 +0200 Subject: tydy up a few uses of recursive_action(), no logic changes Signed-off-by: Denys Vlasenko --- debianutils/run_parts.c | 8 ++++---- findutils/grep.c | 13 +++++++------ include/libbb.h | 6 +++--- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/debianutils/run_parts.c b/debianutils/run_parts.c index c80cc863f..196e0640d 100644 --- a/debianutils/run_parts.c +++ b/debianutils/run_parts.c @@ -133,10 +133,10 @@ static int bb_alphasort(const void *p1, const void *p2) static int FAST_FUNC act(const char *file, struct stat *statbuf, void *args UNUSED_PARAM, int depth) { - if (depth == 1) + if (depth == 0) return TRUE; - if (depth == 2 + if (depth == 1 && ( !(statbuf->st_mode & (S_IFREG | S_IFLNK)) || invalid_name(file) || (!(option_mask32 & OPT_l) && access(file, X_OK) != 0)) @@ -200,8 +200,8 @@ int run_parts_main(int argc UNUSED_PARAM, char **argv) act, /* file action */ act, /* dir action */ NULL, /* user data */ - 1 /* depth */ - ); + 0 /* depth */ + ); if (!names) return 0; diff --git a/findutils/grep.c b/findutils/grep.c index b456ed467..0892a713a 100644 --- a/findutils/grep.c +++ b/findutils/grep.c @@ -694,15 +694,16 @@ static int FAST_FUNC file_action_grep(const char *filename, static int grep_dir(const char *dir) { int matched = 0; - recursive_action(dir, - /* recurse=yes */ ACTION_RECURSE | - /* followLinks=always */ ((option_mask32 & OPT_R) ? ACTION_FOLLOWLINKS : 0) | - /* followLinks=command line only */ ACTION_FOLLOWLINKS_L0 | - /* depthFirst=yes */ ACTION_DEPTHFIRST, + recursive_action(dir, 0 + | ACTION_RECURSE + | ((option_mask32 & OPT_R) ? ACTION_FOLLOWLINKS : 0) + | ACTION_FOLLOWLINKS_L0 /* grep -r ... SYMLINK follows it */ + | ACTION_DEPTHFIRST + | 0, /* fileAction= */ file_action_grep, /* dirAction= */ NULL, /* userData= */ &matched, - /* depth= */ 0); + 0); return matched; } diff --git a/include/libbb.h b/include/libbb.h index 3e23b5bbd..8b84d13a0 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -437,15 +437,15 @@ enum { ACTION_FOLLOWLINKS = (1 << 1), ACTION_FOLLOWLINKS_L0 = (1 << 2), ACTION_DEPTHFIRST = (1 << 3), - /*ACTION_REVERSE = (1 << 4), - unused */ - ACTION_QUIET = (1 << 5), - ACTION_DANGLING_OK = (1 << 6), + ACTION_QUIET = (1 << 4), + ACTION_DANGLING_OK = (1 << 5), }; typedef uint8_t recurse_flags_t; extern int recursive_action(const char *fileName, unsigned flags, int FAST_FUNC (*fileAction)(const char *fileName, struct stat* statbuf, void* userData, int depth), int FAST_FUNC (*dirAction)(const char *fileName, struct stat* statbuf, void* userData, int depth), void* userData, unsigned depth) FAST_FUNC; + extern int device_open(const char *device, int mode) FAST_FUNC; enum { GETPTY_BUFSIZE = 16 }; /* more than enough for "/dev/ttyXXX" */ extern int xgetpty(char *line) FAST_FUNC; -- cgit v1.2.3-55-g6feb From 689d0650ab63425adaea26afe347015a204958ee Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 1 Oct 2020 21:52:16 +0200 Subject: libbb: shrink recursive_action() by reducing memory pressure function old new delta recursive_action1 - 316 +316 file_action_grep 161 164 +3 add_to_prg_cache_if_socket 202 205 +3 depmod_main 509 511 +2 writeFileToTarball 488 489 +1 parse_module 281 282 +1 fileAction 207 208 +1 act 189 190 +1 add_to_dirlist 65 64 -1 writeTarFile 196 194 -2 uuidcache_init 47 45 -2 uuidcache_check_device 109 107 -2 true_action 8 6 -2 run_parts_main 310 308 -2 netstat_main 534 532 -2 lsusb_main 29 27 -2 lspci_main 45 43 -2 initial_scan 138 136 -2 grep_main 845 843 -2 find_main 482 480 -2 config_file_action 437 435 -2 chmod_main 142 140 -2 dirAction 14 10 -4 diff_main 1544 1540 -4 chown_main 154 148 -6 skip_dir 136 129 -7 dir_act 191 184 -7 recursive_action 453 69 -384 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 7/20 up/down: 328/-439) Total: -111 bytes Signed-off-by: Denys Vlasenko --- archival/tar.c | 9 ++--- coreutils/chmod.c | 13 +++---- coreutils/chown.c | 9 +++-- debianutils/run_parts.c | 10 +++--- editors/diff.c | 22 ++++++------ findutils/find.c | 17 +++++---- findutils/grep.c | 13 ++++--- include/libbb.h | 16 ++++++--- libbb/recursive_action.c | 72 +++++++++++++++++++++++--------------- modutils/depmod.c | 12 ++++--- modutils/modprobe-small.c | 3 +- modutils/modprobe.c | 14 ++++---- networking/netstat.c | 28 +++++++-------- selinux/chcon.c | 8 ++--- selinux/setfiles.c | 8 ++--- util-linux/lspci.c | 11 +++--- util-linux/lsusb.c | 10 +++--- util-linux/mdev.c | 20 +++++------ util-linux/volume_id/get_devname.c | 14 ++++---- 19 files changed, 159 insertions(+), 150 deletions(-) diff --git a/archival/tar.c b/archival/tar.c index 4f2564813..93184cc2a 100644 --- a/archival/tar.c +++ b/archival/tar.c @@ -491,10 +491,11 @@ static int exclude_file(const llist_t *excluded_files, const char *file) # define exclude_file(excluded_files, file) 0 # endif -static int FAST_FUNC writeFileToTarball(const char *fileName, struct stat *statbuf, - void *userData, int depth UNUSED_PARAM) +static int FAST_FUNC writeFileToTarball(struct recursive_state *state, + const char *fileName, + struct stat *statbuf) { - struct TarBallInfo *tbInfo = (struct TarBallInfo *) userData; + struct TarBallInfo *tbInfo = (struct TarBallInfo *) state->userData; const char *header_name; int inputFileFd = -1; @@ -700,7 +701,7 @@ static NOINLINE int writeTarFile( /* Read the directory/files and iterate over them one at a time */ while (filelist) { if (!recursive_action(filelist->data, recurseFlags, - writeFileToTarball, writeFileToTarball, tbInfo, 0) + writeFileToTarball, writeFileToTarball, tbInfo) ) { errorFlag = TRUE; } 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 @@ * symbolic links encountered during recursive directory traversals. */ -static int FAST_FUNC fileAction(const char *fileName, struct stat *statbuf, void* param, int depth) +static int FAST_FUNC fileAction(struct recursive_state *state, + const char *fileName, + struct stat *statbuf) { mode_t newmode; /* match coreutils behavior */ - if (depth == 0) { + if (state->depth == 0) { /* statbuf holds lstat result, but we need stat (follow link) */ if (stat(fileName, statbuf)) goto err; @@ -79,9 +81,9 @@ static int FAST_FUNC fileAction(const char *fileName, struct stat *statbuf, void return TRUE; } - newmode = bb_parse_mode((char *)param, statbuf->st_mode); + newmode = bb_parse_mode((char *)state->userData, statbuf->st_mode); if (newmode == (mode_t)-1) - bb_error_msg_and_die("invalid mode '%s'", (char *)param); + bb_error_msg_and_die("invalid mode '%s'", (char *)state->userData); if (chmod(fileName, newmode) == 0) { if (OPT_VERBOSE @@ -136,8 +138,7 @@ int chmod_main(int argc UNUSED_PARAM, char **argv) OPT_RECURSE, // recurse fileAction, // file action fileAction, // dir action - smode, // user data - 0) // depth + smode) // user data ) { retval = EXIT_FAILURE; } 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 { chown_fptr chown_func; }; -static int FAST_FUNC fileAction(const char *fileName, struct stat *statbuf, - void *vparam, int depth UNUSED_PARAM) +static int FAST_FUNC fileAction(struct recursive_state *state UNUSED_PARAM, + const char *fileName, struct stat *statbuf) { -#define param (*(struct param_t*)vparam) +#define param (*(struct param_t*)state->userData) #define opt option_mask32 uid_t u = (param.ugid.uid == (uid_t)-1L) ? statbuf->st_uid : param.ugid.uid; 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) flags, /* flags */ fileAction, /* file action */ fileAction, /* dir action */ - ¶m, /* user data */ - 0) /* depth */ + ¶m) /* user data */ ) { retval = EXIT_FAILURE; } diff --git a/debianutils/run_parts.c b/debianutils/run_parts.c index 196e0640d..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) return (option_mask32 & OPT_r) ? -r : r; } -static int FAST_FUNC act(const char *file, struct stat *statbuf, void *args UNUSED_PARAM, int depth) +static int FAST_FUNC act(struct recursive_state *state, + const char *file, struct stat *statbuf) { - if (depth == 0) + if (state->depth == 0) return TRUE; - if (depth == 1 + if (state->depth == 1 && ( !(statbuf->st_mode & (S_IFREG | S_IFLNK)) || invalid_name(file) || (!(option_mask32 & OPT_l) && access(file, X_OK) != 0)) @@ -199,8 +200,7 @@ int run_parts_main(int argc UNUSED_PARAM, char **argv) ACTION_RECURSE|ACTION_FOLLOWLINKS, act, /* file action */ act, /* dir action */ - NULL, /* user data */ - 0 /* depth */ + NULL /* user data */ ); if (!names) diff --git a/editors/diff.c b/editors/diff.c index dc40ab4f1..280091756 100644 --- a/editors/diff.c +++ b/editors/diff.c @@ -803,11 +803,11 @@ struct dlist { }; /* This function adds a filename to dl, the directory listing. */ -static int FAST_FUNC add_to_dirlist(const char *filename, - struct stat *sb UNUSED_PARAM, - void *userdata, int depth UNUSED_PARAM) +static int FAST_FUNC add_to_dirlist(struct recursive_state *state, + const char *filename, + struct stat *sb UNUSED_PARAM) { - struct dlist *const l = userdata; + struct dlist *const l = state->userData; const char *file = filename + l->len; while (*file == '/') file++; @@ -820,12 +820,12 @@ static int FAST_FUNC add_to_dirlist(const char *filename, /* If recursion is not set, this function adds the directory * to the list and prevents recursive_action from recursing into it. */ -static int FAST_FUNC skip_dir(const char *filename, - struct stat *sb, void *userdata, - int depth) +static int FAST_FUNC skip_dir(struct recursive_state *state, + const char *filename, + struct stat *sb) { - if (!(option_mask32 & FLAG(r)) && depth) { - add_to_dirlist(filename, sb, userdata, depth); + if (!(option_mask32 & FLAG(r)) && state->depth) { + add_to_dirlist(state, filename, sb); return SKIP; } if (!(option_mask32 & FLAG(N))) { @@ -833,7 +833,7 @@ static int FAST_FUNC skip_dir(const char *filename, * which do not exist on the "other side". * Testcase: diff -r /tmp / * (it would recurse deep into /proc without this code) */ - struct dlist *const l = userdata; + struct dlist *const l = state->userData; filename += l->len; if (filename[0]) { struct stat osb; @@ -868,7 +868,7 @@ static void diffdir(char *p[2], const char *s_start) * add_to_dirlist will remove it. */ list[i].len = strlen(p[i]); recursive_action(p[i], ACTION_RECURSE | ACTION_FOLLOWLINKS, - add_to_dirlist, skip_dir, &list[i], 0); + add_to_dirlist, skip_dir, &list[i]); /* Sort dl alphabetically. * GNU diff does this ignoring any number of trailing dots. * We don't, so for us dotted files almost always are diff --git a/findutils/find.c b/findutils/find.c index 9a2c61b75..e2947afb4 100644 --- a/findutils/find.c +++ b/findutils/find.c @@ -889,10 +889,10 @@ ACTF(links) } #endif -static int FAST_FUNC fileAction(const char *fileName, - struct stat *statbuf, - void *userData UNUSED_PARAM, - int depth IF_NOT_FEATURE_FIND_MAXDEPTH(UNUSED_PARAM)) +static int FAST_FUNC fileAction( + struct recursive_state *state IF_NOT_FEATURE_FIND_MAXDEPTH(UNUSED_PARAM), + const char *fileName, + struct stat *statbuf) { int r; int same_fs = 1; @@ -911,12 +911,12 @@ static int FAST_FUNC fileAction(const char *fileName, #endif #if ENABLE_FEATURE_FIND_MAXDEPTH - if (depth < G.minmaxdepth[0]) { + if (state->depth < G.minmaxdepth[0]) { if (same_fs) return TRUE; /* skip this, continue recursing */ return SKIP; /* stop recursing */ } - if (depth > G.minmaxdepth[1]) + if (state->depth > G.minmaxdepth[1]) return SKIP; /* stop recursing */ #endif @@ -927,7 +927,7 @@ static int FAST_FUNC fileAction(const char *fileName, #if ENABLE_FEATURE_FIND_MAXDEPTH if (S_ISDIR(statbuf->st_mode)) { - if (depth == G.minmaxdepth[1]) + if (state->depth == G.minmaxdepth[1]) return SKIP; } #endif @@ -1570,8 +1570,7 @@ int find_main(int argc UNUSED_PARAM, char **argv) G.recurse_flags,/* flags */ fileAction, /* file action */ fileAction, /* dir action */ - NULL, /* user data */ - 0) /* depth */ + NULL) /* user data */ ) { G.exitstatus |= EXIT_FAILURE; } diff --git a/findutils/grep.c b/findutils/grep.c index 0892a713a..10cca83e7 100644 --- a/findutils/grep.c +++ b/findutils/grep.c @@ -656,10 +656,9 @@ static void load_pattern_list(llist_t **lst, char *pattern) llist_add_to(lst, new_grep_list_data(p, 0)); } -static int FAST_FUNC file_action_grep(const char *filename, - struct stat *statbuf, - void* matched, - int depth UNUSED_PARAM) +static int FAST_FUNC file_action_grep(struct recursive_state *state UNUSED_PARAM, + const char *filename, + struct stat *statbuf) { FILE *file; @@ -686,7 +685,7 @@ static int FAST_FUNC file_action_grep(const char *filename, return 0; } cur_file = filename; - *(int*)matched |= grep_file(file); + *(int*)state->userData |= grep_file(file); fclose(file); return 1; } @@ -702,8 +701,8 @@ static int grep_dir(const char *dir) | 0, /* fileAction= */ file_action_grep, /* dirAction= */ NULL, - /* userData= */ &matched, - 0); + /* userData= */ &matched + ); return matched; } diff --git a/include/libbb.h b/include/libbb.h index 8b84d13a0..1b7c0b83a 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -441,10 +441,18 @@ enum { ACTION_DANGLING_OK = (1 << 5), }; typedef uint8_t recurse_flags_t; -extern int recursive_action(const char *fileName, unsigned flags, - int FAST_FUNC (*fileAction)(const char *fileName, struct stat* statbuf, void* userData, int depth), - int FAST_FUNC (*dirAction)(const char *fileName, struct stat* statbuf, void* userData, int depth), - void* userData, unsigned depth) FAST_FUNC; +typedef struct recursive_state { + unsigned flags; + unsigned depth; + void *userData; + int FAST_FUNC (*fileAction)(struct recursive_state *state, const char *fileName, struct stat* statbuf); + int FAST_FUNC (*dirAction)(struct recursive_state *state, const char *fileName, struct stat* statbuf); +} recursive_state_t; +int recursive_action(const char *fileName, unsigned flags, + int FAST_FUNC (*fileAction)(struct recursive_state *state, const char *fileName, struct stat* statbuf), + int FAST_FUNC (*dirAction)(struct recursive_state *state, const char *fileName, struct stat* statbuf), + void *userData +) FAST_FUNC; extern int device_open(const char *device, int mode) FAST_FUNC; enum { GETPTY_BUFSIZE = 16 }; /* more than enough for "/dev/ttyXXX" */ 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 @@ * is so stinking huge. */ -static int FAST_FUNC true_action(const char *fileName UNUSED_PARAM, - struct stat *statbuf UNUSED_PARAM, - void* userData UNUSED_PARAM, - int depth UNUSED_PARAM) +static int FAST_FUNC true_action(struct recursive_state *state UNUSED_PARAM, + const char *fileName UNUSED_PARAM, + struct stat *statbuf UNUSED_PARAM) { return TRUE; } @@ -65,12 +64,7 @@ static int FAST_FUNC true_action(const char *fileName UNUSED_PARAM, * 1: stat(statbuf). Calls dirAction and optionally recurse on link to dir. */ -int FAST_FUNC recursive_action(const char *fileName, - unsigned flags, - int FAST_FUNC (*fileAction)(const char *fileName, struct stat *statbuf, void* userData, int depth), - int FAST_FUNC (*dirAction)(const char *fileName, struct stat *statbuf, void* userData, int depth), - void* userData, - unsigned depth) +static int recursive_action1(recursive_state_t *state, const char *fileName) { struct stat statbuf; unsigned follow; @@ -78,24 +72,21 @@ int FAST_FUNC recursive_action(const char *fileName, DIR *dir; struct dirent *next; - if (!fileAction) fileAction = true_action; - if (!dirAction) dirAction = true_action; - follow = ACTION_FOLLOWLINKS; - if (depth == 0) + if (state->depth == 0) follow = ACTION_FOLLOWLINKS | ACTION_FOLLOWLINKS_L0; - follow &= flags; + follow &= state->flags; status = (follow ? stat : lstat)(fileName, &statbuf); if (status < 0) { #ifdef DEBUG_RECURS_ACTION - bb_error_msg("status=%d flags=%x", status, flags); + bb_error_msg("status=%d flags=%x", status, state->flags); #endif - if ((flags & ACTION_DANGLING_OK) + if ((state->flags & ACTION_DANGLING_OK) && errno == ENOENT && lstat(fileName, &statbuf) == 0 ) { /* Dangling link */ - return fileAction(fileName, &statbuf, userData, depth); + return state->fileAction(state, fileName, &statbuf); } goto done_nak_warn; } @@ -103,20 +94,20 @@ int FAST_FUNC recursive_action(const char *fileName, /* If S_ISLNK(m), then we know that !S_ISDIR(m). * Then we can skip checking first part: if it is true, then * (!dir) is also true! */ - if ( /* (!(flags & ACTION_FOLLOWLINKS) && S_ISLNK(statbuf.st_mode)) || */ + if ( /* (!(state->flags & ACTION_FOLLOWLINKS) && S_ISLNK(statbuf.st_mode)) || */ !S_ISDIR(statbuf.st_mode) ) { - return fileAction(fileName, &statbuf, userData, depth); + return state->fileAction(state, fileName, &statbuf); } /* It's a directory (or a link to one, and followLinks is set) */ - if (!(flags & ACTION_RECURSE)) { - return dirAction(fileName, &statbuf, userData, depth); + if (!(state->flags & ACTION_RECURSE)) { + return state->dirAction(state, fileName, &statbuf); } - if (!(flags & ACTION_DEPTHFIRST)) { - status = dirAction(fileName, &statbuf, userData, depth); + if (!(state->flags & ACTION_DEPTHFIRST)) { + status = state->dirAction(state, fileName, &statbuf); if (status == FALSE) goto done_nak_warn; if (status == SKIP) @@ -140,11 +131,13 @@ int FAST_FUNC recursive_action(const char *fileName, continue; /* process every file (NB: ACTION_RECURSE is set in flags) */ - s = recursive_action(nextFile, flags, fileAction, dirAction, - userData, depth + 1); + state->depth++; + s = recursive_action1(state, nextFile); if (s == FALSE) status = FALSE; free(nextFile); + state->depth--; + //#define RECURSE_RESULT_ABORT -1 // if (s == RECURSE_RESULT_ABORT) { // closedir(dir); @@ -153,15 +146,36 @@ int FAST_FUNC recursive_action(const char *fileName, } closedir(dir); - if (flags & ACTION_DEPTHFIRST) { - if (!dirAction(fileName, &statbuf, userData, depth)) + if (state->flags & ACTION_DEPTHFIRST) { + if (!state->dirAction(state, fileName, &statbuf)) goto done_nak_warn; } return status; done_nak_warn: - if (!(flags & ACTION_QUIET)) + if (!(state->flags & ACTION_QUIET)) bb_simple_perror_msg(fileName); return FALSE; } + +int FAST_FUNC recursive_action(const char *fileName, + unsigned flags, + int FAST_FUNC (*fileAction)(struct recursive_state *state, const char *fileName, struct stat* statbuf), + int FAST_FUNC (*dirAction)(struct recursive_state *state, const char *fileName, struct stat* statbuf), + void *userData) +{ + /* Keeping a part of variables of recusive descent in a "state structure" + * instead of passing ALL of them down as parameters of recursive_action1() + * relieves register pressure, both in recursive_action1() + * and in every file/dirAction(). + */ + recursive_state_t state; + state.flags = flags; + state.depth = 0; + state.userData = userData; + state.fileAction = fileAction ? fileAction : true_action; + state.dirAction = dirAction ? dirAction : true_action; + + return recursive_action1(&state, fileName); +} 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 @@ * for each depends, look through our list of full paths and emit if found */ -static int FAST_FUNC parse_module(const char *fname, struct stat *sb UNUSED_PARAM, - void *data, int depth UNUSED_PARAM) +static int FAST_FUNC parse_module(struct recursive_state *state, + const char *fname, + struct stat *sb UNUSED_PARAM) { - module_db *modules = data; + module_db *modules = state->userData; char *image, *ptr; module_entry *e; @@ -201,11 +202,12 @@ int depmod_main(int argc UNUSED_PARAM, char **argv) memset(&modules, 0, sizeof(modules)); if (*argv) { do { - parse_module(*argv, /*sb (unused):*/ NULL, &modules, 0); + recursive_action(*argv, 0 /* no ACTION_RECURSE! */, + parse_module, NULL, &modules); } while (*++argv); } else { recursive_action(".", ACTION_RECURSE, - parse_module, NULL, &modules, 0); + parse_module, NULL, &modules); } /* 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) ACTION_RECURSE, /* flags */ fileAction, /* file action */ NULL, /* dir action */ - name, /* user data */ - 0 /* depth */ + name /* user data */ ); dbg1_error_msg("dirscan complete"); /* 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) } } -static int FAST_FUNC config_file_action(const char *filename, - struct stat *statbuf UNUSED_PARAM, - void *userdata UNUSED_PARAM, - int depth) +static int FAST_FUNC config_file_action(struct recursive_state *state, + const char *filename, + struct stat *statbuf UNUSED_PARAM) { char *tokens[3]; parser_t *p; @@ -255,7 +254,7 @@ static int FAST_FUNC config_file_action(const char *filename, * that we shouldn't recurse into /etc/modprobe.d/dir/ * _subdirectories_: */ - if (depth > 1) + if (state->depth > 1) return SKIP; /* stop recursing */ //TODO: instead, can use dirAction in recursive_action() to SKIP dirs //on depth == 1 level. But that's more code... @@ -264,7 +263,7 @@ static int FAST_FUNC config_file_action(const char *filename, * depth==0: read_config("modules.{symbols,alias}") must work, * "include FILE_NOT_ENDING_IN_CONF" must work too. */ - if (depth != 0) { + if (state->depth != 0) { if (!is_suffixed_with(base, ".conf")) goto error; } @@ -329,8 +328,7 @@ static int FAST_FUNC config_file_action(const char *filename, static int read_config(const char *path) { return recursive_action(path, ACTION_RECURSE | ACTION_QUIET, - config_file_action, NULL, NULL, - /*depth:*/ 0); + config_file_action, NULL, NULL); } static const char *humanly_readable_name(struct module_entry *m) 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) return inode; } -static int FAST_FUNC add_to_prg_cache_if_socket(const char *fileName, - struct stat *statbuf UNUSED_PARAM, - void *pid_slash_progname, - int depth UNUSED_PARAM) +static int FAST_FUNC add_to_prg_cache_if_socket(struct recursive_state *state, + const char *fileName, + struct stat *statbuf UNUSED_PARAM) { char *linkname; long inode; @@ -284,16 +283,17 @@ static int FAST_FUNC add_to_prg_cache_if_socket(const char *fileName, if (linkname != NULL) { inode = extract_socket_inode(linkname); free(linkname); - if (inode >= 0) - prg_cache_add(inode, (char *)pid_slash_progname); + if (inode >= 0) { + char *pid_slash_progname = state->userData; + prg_cache_add(inode, pid_slash_progname); + } } return TRUE; } -static int FAST_FUNC dir_act(const char *fileName, - struct stat *statbuf UNUSED_PARAM, - void *userData UNUSED_PARAM, - int depth) +static int FAST_FUNC dir_act(struct recursive_state *state, + const char *fileName, + struct stat *statbuf UNUSED_PARAM) { const char *pid; char *pid_slash_progname; @@ -301,7 +301,7 @@ static int FAST_FUNC dir_act(const char *fileName, char cmdline_buf[512]; int n, len; - if (depth == 0) /* "/proc" itself */ + if (state->depth == 0) /* "/proc" itself */ return TRUE; /* continue looking one level below /proc */ pid = fileName + sizeof("/proc/")-1; /* point after "/proc/" */ @@ -321,8 +321,8 @@ static int FAST_FUNC dir_act(const char *fileName, ACTION_RECURSE | ACTION_QUIET, add_to_prg_cache_if_socket, NULL, - (void *)pid_slash_progname, - 0); + (void *)pid_slash_progname + ); free(pid_slash_progname); if (!n) @@ -337,7 +337,7 @@ static void prg_cache_load(void) prg_cache_loaded = 1; load_ok = recursive_action("/proc", ACTION_RECURSE | ACTION_QUIET, - NULL, dir_act, NULL, 0); + NULL, dir_act, NULL); if (load_ok) return; 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; static char *range = NULL; static char *specified_context = NULL; -static int FAST_FUNC change_filedir_context( +static int FAST_FUNC change_filedir_context(struct recursive_state *state UNUSED_PARAM, const char *fname, - struct stat *stbuf UNUSED_PARAM, - void *userData UNUSED_PARAM, - int depth UNUSED_PARAM) + struct stat *stbuf UNUSED_PARAM) { context_t context = NULL; security_context_t file_context = NULL; @@ -207,7 +205,7 @@ int chcon_main(int argc UNUSED_PARAM, char **argv) ((option_mask32 & OPT_RECURSIVE) ? ACTION_RECURSE : 0), change_filedir_context, change_filedir_context, - NULL, 0) != TRUE) + NULL) != TRUE) errors = 1; } 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) * This function is called by recursive_action on each file during * the directory traversal. */ -static int FAST_FUNC apply_spec( +static int FAST_FUNC apply_spec(struct recursive_state *state UNUSED_PARAM, const char *file, - struct stat *sb, - void *userData UNUSED_PARAM, - int depth UNUSED_PARAM) + struct stat *sb) { if (!follow_mounts) { /* setfiles does not process across different mount points */ @@ -535,7 +533,7 @@ static int process_one(char *name) ACTION_RECURSE, apply_spec, apply_spec, - NULL, 0) != TRUE + NULL) != TRUE ) { bb_error_msg("error while labeling %s", name); goto err; 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 { /* * PCI_SLOT_NAME PCI_CLASS: PCI_VID:PCI_DID [PCI_SUBSYS_VID:PCI_SUBSYS_DID] [DRIVER] */ -static int FAST_FUNC fileAction( +static int FAST_FUNC fileAction(struct recursive_state *state UNUSED_PARAM, const char *fileName, - struct stat *statbuf UNUSED_PARAM, - void *userData UNUSED_PARAM, - int depth UNUSED_PARAM) + struct stat *statbuf UNUSED_PARAM) { parser_t *parser; char *tokens[3]; @@ -117,8 +115,7 @@ int lspci_main(int argc UNUSED_PARAM, char **argv) ACTION_RECURSE, fileAction, NULL, /* dirAction */ - NULL, /* userData */ - 0 /* depth */); - + NULL /* userData */ + ); return EXIT_SUCCESS; } 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 @@ #include "libbb.h" -static int FAST_FUNC fileAction( +static int FAST_FUNC fileAction(struct recursive_state *state UNUSED_PARAM, const char *fileName, - struct stat *statbuf UNUSED_PARAM, - void *userData UNUSED_PARAM, - int depth UNUSED_PARAM) + struct stat *statbuf UNUSED_PARAM) { parser_t *parser; char *tokens[4]; @@ -80,8 +78,8 @@ int lsusb_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) ACTION_RECURSE, fileAction, NULL, /* dirAction */ - NULL, /* userData */ - 0 /* depth */); + NULL /* userData */ + ); return EXIT_SUCCESS; } 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) /* File callback for /sys/ traversal. * We act only on "/sys/.../dev" (pseudo)file */ -static int FAST_FUNC fileAction(const char *fileName, - struct stat *statbuf UNUSED_PARAM, - void *userData, - int depth UNUSED_PARAM) +static int FAST_FUNC fileAction(struct recursive_state *state, + const char *fileName, + struct stat *statbuf UNUSED_PARAM) { size_t len = strlen(fileName) - 4; /* can't underflow */ - char *path = userData; /* char array[PATH_MAX + SCRATCH_SIZE] */ + char *path = state->userData; /* char array[PATH_MAX + SCRATCH_SIZE] */ char subsys[PATH_MAX]; int res; @@ -888,12 +887,11 @@ static int FAST_FUNC fileAction(const char *fileName, } /* Directory callback for /sys/ traversal */ -static int FAST_FUNC dirAction(const char *fileName UNUSED_PARAM, - struct stat *statbuf UNUSED_PARAM, - void *userData UNUSED_PARAM, - int depth) +static int FAST_FUNC dirAction(struct recursive_state *state, + const char *fileName UNUSED_PARAM, + struct stat *statbuf UNUSED_PARAM) { - return (depth >= MAX_SYSFS_DEPTH ? SKIP : TRUE); + return (state->depth >= MAX_SYSFS_DEPTH ? SKIP : TRUE); } /* For the full gory details, see linux/Documentation/firmware_class/README @@ -1149,7 +1147,7 @@ static void initial_scan(char *temp) /* Create all devices from /sys/dev hierarchy */ recursive_action("/sys/dev", ACTION_RECURSE | ACTION_FOLLOWLINKS, - fileAction, dirAction, temp, 0); + fileAction, dirAction, temp); } #if ENABLE_FEATURE_MDEV_DAEMON 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 * add a cache entry for this device. * If device node does not exist, it will be temporarily created. */ static int FAST_FUNC -uuidcache_check_device(const char *device, - struct stat *statbuf, - void *userData UNUSED_PARAM, - int depth UNUSED_PARAM) +uuidcache_check_device(struct recursive_state *state UNUSED_PARAM, + const char *device, + struct stat *statbuf) { /* note: this check rejects links to devices, among other nodes */ if (!S_ISBLK(statbuf->st_mode) @@ -145,12 +144,13 @@ uuidcache_init(int scan_devices) * This is unacceptably complex. Let's just scan /dev. * (Maybe add scanning of /sys/block/XXX/dev for devices * somehow not having their /dev/XXX entries created?) */ - if (scan_devices) + if (scan_devices) { recursive_action("/dev", ACTION_RECURSE, uuidcache_check_device, /* file_action */ NULL, /* dir_action */ - NULL, /* userData */ - 0 /* depth */); + NULL /* userData */ + ); + } return uuidCache; } -- cgit v1.2.3-55-g6feb From a90a54aead2a082f9bc4d44fd48d1042bcd2c3fb Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 1 Oct 2020 23:49:16 +0200 Subject: udhcp: remove struct packing attribute, eliminates gcc-9.x "error: taking address of packed member of 'struct ip_udp_dhcp_packet' may result in an unaligned pointer value" here: udhcp_dump_packet(&packet.data); and in other places. Signed-off-by: Denys Vlasenko --- networking/udhcp/common.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 { uint8_t file[128]; /* boot file name (ASCIZ) */ uint32_t cookie; /* fixed first four option bytes (99,130,83,99 dec) */ uint8_t options[DHCP_OPTIONS_BUFSIZE + CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS]; -} PACKED; +}; #define DHCP_PKT_SNAME_LEN 64 #define DHCP_PKT_FILE_LEN 128 #define DHCP_PKT_SNAME_LEN_STR "64" @@ -56,12 +56,12 @@ struct ip_udp_dhcp_packet { struct iphdr ip; struct udphdr udp; struct dhcp_packet data; -} PACKED; +}; struct udp_dhcp_packet { struct udphdr udp; struct dhcp_packet data; -} PACKED; +}; enum { IP_UDP_DHCP_SIZE = sizeof(struct ip_udp_dhcp_packet) - CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS, -- cgit v1.2.3-55-g6feb From 16e82c61d4db91e2e888600cdee7cf656ba4774c Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 2 Oct 2020 18:30:53 +0200 Subject: tar: fix fallout of last_char_is(NULL) no longer being allowed Signed-off-by: Denys Vlasenko --- archival/libarchive/get_header_tar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) /* case 0: */ case '0': #if ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY - if (last_char_is(file_header->name, '/')) { + if (file_header->name && last_char_is(file_header->name, '/')) { goto set_dir; } #endif -- cgit v1.2.3-55-g6feb From eecd6f7a6c44af48f1c70d383ac87e3200dc1233 Mon Sep 17 00:00:00 2001 From: Rui Salvaterra Date: Fri, 31 Jul 2020 10:10:36 +0100 Subject: ip rule: add support for fwmark/fwmask for policy routing This adds support for fwmark/fwmask in ip rule which is needed, for example, in OpenWrt's mwan3. Masks are supported since Linux 2.6.19. Fixes: https://bugs.busybox.net/show_bug.cgi?id=11621 Signed-off-by: Rui Salvaterra Signed-off-by: Denys Vlasenko --- networking/ip.c | 2 +- networking/libiproute/iprule.c | 32 ++++++++++++++++++++++++++------ 2 files changed, 27 insertions(+), 7 deletions(-) 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 @@ //usage:#define iprule_trivial_usage //usage: "[list] | add|del SELECTOR ACTION" //usage:#define iprule_full_usage "\n\n" -//usage: " SELECTOR := [from PREFIX] [to PREFIX] [tos TOS] [fwmark FWMARK]\n" +//usage: " SELECTOR := [from PREFIX] [to PREFIX] [tos TOS] [fwmark FWMARK[/MASK]]\n" //usage: " [dev IFACE] [pref NUMBER]\n" //usage: " ACTION := [table TABLE_ID] [nat ADDR]\n" //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 @@ #include /* from : */ -#define FRA_SUPPRESS_IFGROUP 13 -#define FRA_SUPPRESS_PREFIXLEN 14 +#define FRA_FWMARK 10 +#define FRA_SUPPRESS_IFGROUP 13 +#define FRA_SUPPRESS_PREFIXLEN 14 +#define FRA_FWMASK 16 #include "ip_common.h" /* #include "libbb.h" is inside */ #include "rt_names.h" @@ -117,8 +119,18 @@ static int FAST_FUNC print_rule(const struct sockaddr_nl *who UNUSED_PARAM, if (r->rtm_tos) { printf("tos %s ", rtnl_dsfield_n2a(r->rtm_tos)); } - if (tb[RTA_PROTOINFO]) { - printf("fwmark %#x ", *(uint32_t*)RTA_DATA(tb[RTA_PROTOINFO])); + + if (tb[FRA_FWMARK] || tb[FRA_FWMASK]) { + uint32_t mark = 0, mask = 0; + + if (tb[FRA_FWMARK]) + mark = *(uint32_t*)RTA_DATA(tb[FRA_FWMARK]); + if (tb[FRA_FWMASK] + && (mask = *(uint32_t*)RTA_DATA(tb[FRA_FWMASK])) != 0xFFFFFFFF + ) + printf("fwmark %#x/%#x ", mark, mask); + else + printf("fwmark %#x ", mark); } if (tb[RTA_IIF]) { @@ -257,10 +269,18 @@ static int iprule_modify(int cmd, char **argv) invarg_1_to_2(*argv, "TOS"); req.r.rtm_tos = tos; } else if (key == ARG_fwmark) { - uint32_t fwmark; + char *slash; + uint32_t fwmark, fwmask; NEXT_ARG(); + slash = strchr(*argv, '/'); + if (slash) + *slash = '\0'; fwmark = get_u32(*argv, keyword_fwmark); - addattr32(&req.n, sizeof(req), RTA_PROTOINFO, fwmark); + addattr32(&req.n, sizeof(req), FRA_FWMARK, fwmark); + if (slash) { + fwmask = get_u32(slash + 1, "fwmask"); + addattr32(&req.n, sizeof(req), FRA_FWMASK, fwmask); + } } else if (key == ARG_realms) { uint32_t realm; NEXT_ARG(); -- cgit v1.2.3-55-g6feb From 535a509846be5087ddd0d6e8fc6399f919942639 Mon Sep 17 00:00:00 2001 From: Xabier Oneca Date: Tue, 6 Oct 2020 02:32:12 +0200 Subject: httpd: code shrink Use decode_base64() from uuencode.c when uudecode/base64 applets are included. That function is bigger than httpd's decodeBase64(), so we use the old one when those applets are disabled. Bloat-o-meter when one of those is enabled: function old new delta handle_incoming_and_exit 2371 2265 -106 Signed-off-by: Xabier Oneca Signed-off-by: Denys Vlasenko --- networking/httpd.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/networking/httpd.c b/networking/httpd.c index 2946b2a54..961f8cab4 100644 --- a/networking/httpd.c +++ b/networking/httpd.c @@ -1017,6 +1017,12 @@ static char *encodeString(const char *string) */ static void decodeBase64(char *Data) { +# if ENABLE_BASE64 || ENABLE_UUDECODE + /* Call decode_base64() from uuencode.c */ + char *eptr = Data; + decode_base64(&eptr, Data); + *eptr = '\0'; +# else const unsigned char *in = (const unsigned char *)Data; /* The decoded size will be at most 3/4 the size of the encoded */ unsigned ch = 0; @@ -1050,6 +1056,7 @@ static void decodeBase64(char *Data) } } *Data = '\0'; +# endif } #endif -- cgit v1.2.3-55-g6feb From 2496616b0a8d1c80cd1416b73a4847b59b9f969a Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 6 Oct 2020 02:36:47 +0200 Subject: avoid using strok - eliminates use of hidden global variable function old new delta udhcp_str2optset 616 650 +34 setpriv_main 950 975 +25 switch_root_main 688 706 +18 parse 958 970 +12 getopt_main 622 628 +6 parse_resolvconf 302 306 +4 mpstat_main 1139 1142 +3 static.p 4 - -4 cdcmd 717 702 -15 strtok 148 - -148 ------------------------------------------------------------------------------ (add/remove: 0/3 grow/shrink: 7/1 up/down: 102/-167) Total: -65 bytes Signed-off-by: Denys Vlasenko --- libbb/kernel_version.c | 20 +++++++++++--------- libbb/update_passwd.c | 15 +++++++++------ mailutils/reformime.c | 5 +++-- networking/nslookup.c | 5 +++-- networking/udhcp/common.c | 10 +++++----- procps/mpstat.c | 2 +- shell/ash.c | 4 ++-- util-linux/getopt.c | 5 +++-- util-linux/mount.c | 3 ++- util-linux/setpriv.c | 9 +++++---- util-linux/switch_root.c | 4 ++-- 11 files changed, 46 insertions(+), 36 deletions(-) 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) { struct utsname name; char *t; - int i, r; + int r; uname(&name); /* never fails */ - t = name.release; - r = 0; - for (i = 0; i < 3; i++) { - t = strtok(t, "."); - r = r * 256 + (t ? atoi(t) : 0); - t = NULL; - } - return r; + t = name.release - 1; + r = 1; + do { + r <<= 8; + if (t) { + r += atoi(++t); + t = strchr(t, '.'); + } + } while (r < 0x1000000); + return r - 0x1000000; } 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 @@ #if ENABLE_SELINUX static void check_selinux_update_passwd(const char *username) { - security_context_t context; - char *seuser; + security_context_t seuser; + char *p; if (getuid() != (uid_t)0 || is_selinux_enabled() == 0) return; /* No need to check */ - if (getprevcon_raw(&context) < 0) + if (getprevcon_raw(&seuser) < 0) bb_simple_perror_msg_and_die("getprevcon failed"); - seuser = strtok(context, ":"); - if (!seuser) - bb_error_msg_and_die("invalid context '%s'", context); + + p = strchr(seuser, ':'); + if (!p) + bb_error_msg_and_die("invalid context '%s'", seuser); + *p = '\0'; + if (strcmp(seuser, username) != 0) { security_class_t tclass; 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) /* Split to tokens */ { char *s, *p; + char *tokstate; unsigned ntokens; const char *delims = ";=\" \t\n"; @@ -127,13 +128,13 @@ static int parse(const char *boundary, char **argv) } dbg_error_msg("L:'%s'", p); ntokens = 0; - s = strtok(s, delims); + s = strtok_r(s, delims, &tokstate); while (s) { tokens[ntokens] = s; if (ntokens < ARRAY_SIZE(tokens) - 1) ntokens++; dbg_error_msg("L[%d]='%s'", ntokens, s); - s = strtok(NULL, delims); + s = strtok_r(NULL, delims, &tokstate); } tokens[ntokens] = NULL; dbg_error_msg("EMPTYLINE, ntokens:%d", ntokens); 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) while (fgets(line, sizeof(line), resolv)) { char *p, *arg; + char *tokstate; - p = strtok(line, " \t\n"); + p = strtok_r(line, " \t\n", &tokstate); if (!p) continue; dbg("resolv_key:'%s'\n", p); - arg = strtok(NULL, "\n"); + arg = strtok_r(NULL, "\n", &tokstate); dbg("resolv_arg:'%s'\n", arg); if (!arg) continue; 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, /* Cheat, the only *const* str possible is "" */ str = (char *) const_str; - opt = strtok(str, " \t=:"); + opt = strtok_r(str, " \t=:", &str); if (!opt) return 0; @@ -550,10 +550,10 @@ int FAST_FUNC udhcp_str2optset(const char *const_str, void *arg, char *val; if (optflag->flags == OPTION_BIN) { - val = strtok(NULL, ""); /* do not split "'q w e'" */ + val = strtok_r(NULL, "", &str); /* do not split "'q w e'" */ if (val) trim(val); } else - val = strtok(NULL, ", \t"); + val = strtok_r(NULL, ", \t", &str); if (!val) break; @@ -567,7 +567,7 @@ int FAST_FUNC udhcp_str2optset(const char *const_str, void *arg, break; case OPTION_IP_PAIR: retval = udhcp_str2nip(val, buffer); - val = strtok(NULL, ", \t/-"); + val = strtok_r(NULL, ", \t/-", &str); if (!val) retval = 0; if (retval) @@ -631,7 +631,7 @@ int FAST_FUNC udhcp_str2optset(const char *const_str, void *arg, *slash = '\0'; retval = udhcp_str2nip(val, buffer + 1); buffer[0] = mask = bb_strtou(slash + 1, NULL, 10); - val = strtok(NULL, ", \t/-"); + val = strtok_r(NULL, ", \t/-", &str); if (!val || mask > 32 || errno) retval = 0; if (retval) { diff --git a/procps/mpstat.c b/procps/mpstat.c index 52a436a55..c78c1f0a0 100644 --- a/procps/mpstat.c +++ b/procps/mpstat.c @@ -923,7 +923,7 @@ int mpstat_main(int argc UNUSED_PARAM, char **argv) char *t; G.p_option = 1; - for (t = strtok(opt_set_cpu, ","); t; t = strtok(NULL, ",")) { + for (t = strtok_r(opt_set_cpu, ",", &opt_set_cpu); t; t = strtok_r(NULL, ",", &opt_set_cpu)) { if (strcmp(t, "ALL") == 0) { /* Select every CPU */ memset(G.cpu_bitmap, 0xff, G.cpu_bitmap_len); diff --git a/shell/ash.c b/shell/ash.c index 07aa2da2e..58da0a2a0 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -2770,7 +2770,7 @@ updatepwd(const char *dir) lim++; } } - p = strtok(cdcomppath, "/"); + p = strtok_r(cdcomppath, "/", &cdcomppath); while (p) { switch (*p) { case '.': @@ -2789,7 +2789,7 @@ updatepwd(const char *dir) new = stack_putstr(p, new); USTPUTC('/', new); } - p = strtok(NULL, "/"); + p = strtok_r(NULL, "/", &cdcomppath); } if (new > lim) STUNPUTC(new); 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 { int long_nr = 0; int arg_opt, tlen; - char *tokptr = strtok(options, ", \t\n"); + char *tokptr; if (long_options) while (long_options[long_nr].name) long_nr++; + tokptr = strtok_r(options, ", \t\n", &options); while (tokptr) { arg_opt = no_argument; tlen = strlen(tokptr); @@ -318,7 +319,7 @@ static struct option *add_long_options(struct option *long_options, char *option long_nr++; /*memset(&long_options[long_nr], 0, sizeof(long_options[0])); - xrealloc_vector did it */ } - tokptr = strtok(NULL, ", \t\n"); + tokptr = strtok_r(NULL, ", \t\n", &options); } return long_options; } diff --git a/util-linux/mount.c b/util-linux/mount.c index 19ac13930..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 * then data pointer is interpreted as a string. */ struct nfs_mount_data data; char *opt; + char *tokstate; struct hostent *hp; struct sockaddr_in server_addr; struct sockaddr_in mount_server_addr; @@ -1348,7 +1349,7 @@ static NOINLINE int nfsmount(struct mntent *mp, unsigned long vfsflags, char *fi nfsvers = 0; /* parse options */ - if (filteropts) for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) { + if (filteropts) for (opt = strtok_r(filteropts, ",", &tokstate); opt; opt = strtok_r(NULL, ",", &tokstate)) { char *opteq = strchr(opt, '='); if (opteq) { int val, idx; 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) static void set_inh_caps(char *capstring) { struct caps caps; + char *string; getcaps(&caps); - capstring = strtok(capstring, ","); + capstring = strtok_r(capstring, ",", &string); while (capstring) { unsigned cap; @@ -159,7 +160,7 @@ static void set_inh_caps(char *capstring) caps.data[CAP_TO_INDEX(cap)].inheritable |= CAP_TO_MASK(cap); else caps.data[CAP_TO_INDEX(cap)].inheritable &= ~CAP_TO_MASK(cap); - capstring = strtok(NULL, ","); + capstring = strtok_r(NULL, ",", &string); } if (capset(&caps.header, caps.data) != 0) @@ -170,7 +171,7 @@ static void set_ambient_caps(char *string) { char *cap; - cap = strtok(string, ","); + cap = strtok_r(string, ",", &string); while (cap) { unsigned idx; @@ -182,7 +183,7 @@ static void set_ambient_caps(char *string) if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_LOWER, idx, 0, 0) < 0) bb_simple_perror_msg("cap_ambient_lower"); } - cap = strtok(NULL, ","); + cap = strtok_r(NULL, ",", &string); } } #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) { char *cap; - cap = strtok(string, ","); + cap = strtok_r(string, ",", &string); while (cap) { unsigned cap_idx; @@ -174,7 +174,7 @@ static void drop_capabilities(char *string) drop_bounding_set(cap_idx); drop_capset(cap_idx); bb_error_msg("dropped capability: %s", cap); - cap = strtok(NULL, ","); + cap = strtok_r(NULL, ",", &string); } } #endif -- cgit v1.2.3-55-g6feb From 085f19cdffd653013b1483c08851ecc35cdd818e Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 7 Oct 2020 17:02:46 +0200 Subject: uudecode: fix possible signed char bug Signed-off-by: Denys Vlasenko --- coreutils/uudecode.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) 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 } bb_simple_error_msg_and_die("short file"); } -#endif -#if ENABLE_UUDECODE int uudecode_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int uudecode_main(int argc UNUSED_PARAM, char **argv) { @@ -202,10 +200,10 @@ int base64_main(int argc UNUSED_PARAM, char **argv) *--argv = (char*)"-"; src_stream = xfopen_stdin(argv[0]); if (opts) { - read_base64(src_stream, stdout, /*flags:*/ (char)EOF); + read_base64(src_stream, stdout, /*flags:*/ (unsigned char)EOF); } else { enum { - SRC_BUF_SIZE = 76/4*3, /* This *MUST* be a multiple of 3 */ + SRC_BUF_SIZE = 76 / 4 * 3, /* this *MUST* be a multiple of 3 */ DST_BUF_SIZE = 4 * ((SRC_BUF_SIZE + 2) / 3), }; char src_buf[SRC_BUF_SIZE]; -- cgit v1.2.3-55-g6feb From dac5b8314236338963877903cba3d7edfbfc9c58 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 20 Oct 2020 18:54:36 +0200 Subject: xxd: fix printing of trailing spaces function old new delta bb_dump_dump 1497 1523 +26 xxd_main 459 466 +7 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/0 up/down: 33/0) Total: 33 bytes Signed-off-by: Denys Vlasenko --- include/dump.h | 39 ++------------------ libbb/dump.c | 93 ++++++++++++++++++++++++++++++++++++------------ testsuite/xxd.tests | 34 ++++++++++++++++++ util-linux/hexdump_xxd.c | 1 + 4 files changed, 108 insertions(+), 59 deletions(-) create mode 100755 testsuite/xxd.tests diff --git a/include/dump.h b/include/dump.h index 4c237ef05..f4759c193 100644 --- a/include/dump.h +++ b/include/dump.h @@ -2,50 +2,15 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN -#define F_IGNORE 0x01 /* %_A */ -#define F_SETREP 0x02 /* rep count set, not default */ -#define F_ADDRESS 0x001 /* print offset */ -#define F_BPAD 0x002 /* blank pad */ -#define F_C 0x004 /* %_c */ -#define F_CHAR 0x008 /* %c */ -#define F_DBL 0x010 /* %[EefGf] */ -#define F_INT 0x020 /* %[di] */ -#define F_P 0x040 /* %_p */ -#define F_STR 0x080 /* %s */ -#define F_U 0x100 /* %_u */ -#define F_UINT 0x200 /* %[ouXx] */ -#define F_TEXT 0x400 /* no conversions */ - enum dump_vflag_t { ALL, DUP, FIRST, WAIT }; /* -v values */ -typedef struct PR { - struct PR *nextpr; /* next print unit */ - unsigned flags; /* flag values */ - int bcnt; /* byte count */ - char *cchar; /* conversion character */ - char *fmt; /* printf format */ - char *nospace; /* no whitespace version */ -} PR; - -typedef struct FU { - struct FU *nextfu; /* next format unit */ - struct PR *nextpr; /* next print unit */ - unsigned flags; /* flag values */ - int reps; /* repetition count */ - int bcnt; /* byte count */ - char *fmt; /* format string */ -} FU; - -typedef struct FS { /* format strings */ - struct FS *nextfs; /* linked list of format strings */ - struct FU *nextfu; /* linked list of format units */ - int bcnt; -} FS; +typedef struct FS FS; typedef struct dumper_t { off_t dump_skip; /* bytes to skip */ int dump_length; /* max bytes to read */ smallint dump_vflag; /*enum dump_vflag_t*/ + const char *eofstring; FS *fshead; } dumper_t; diff --git a/libbb/dump.c b/libbb/dump.c index 8029cca0e..920f003ef 100644 --- a/libbb/dump.c +++ b/libbb/dump.c @@ -13,13 +13,43 @@ #include "libbb.h" #include "dump.h" -static const char dot_flags_width_chars[] ALIGN1 = ".#-+ 0123456789"; - -static const char size_conv_str[] ALIGN1 = -"\x1\x4\x4\x4\x4\x4\x4\x8\x8\x8\x8\010cdiouxXeEfgG"; - -static const char int_convs[] ALIGN1 = "diouxX"; - +#define F_IGNORE 0x01 /* %_A */ +#define F_SETREP 0x02 /* rep count set, not default */ +#define F_ADDRESS 0x001 /* print offset */ +#define F_BPAD 0x002 /* blank pad */ +#define F_C 0x004 /* %_c */ +#define F_CHAR 0x008 /* %c */ +#define F_DBL 0x010 /* %[EefGf] */ +#define F_INT 0x020 /* %[di] */ +#define F_P 0x040 /* %_p */ +#define F_STR 0x080 /* %s */ +#define F_U 0x100 /* %_u */ +#define F_UINT 0x200 /* %[ouXx] */ +#define F_TEXT 0x400 /* no conversions */ + +typedef struct PR { + struct PR *nextpr; /* next print unit */ + unsigned flags; /* flag values */ + int bcnt; /* byte count */ + char *cchar; /* conversion character */ + char *fmt; /* printf format */ + char *nospace; /* no whitespace version */ +} PR; + +typedef struct FU { + struct FU *nextfu; /* next format unit */ + struct PR *nextpr; /* next print unit */ + unsigned flags; /* flag values */ + int reps; /* repetition count */ + int bcnt; /* byte count */ + char *fmt; /* format string */ +} FU; + +typedef struct FS { /* format strings */ + struct FS *nextfs; /* linked list of format strings */ + struct FU *nextfu; /* linked list of format units */ + int bcnt; +} FS; typedef struct priv_dumper_t { dumper_t pub; @@ -39,6 +69,13 @@ typedef struct priv_dumper_t { unsigned char *get__savp; } priv_dumper_t; +static const char dot_flags_width_chars[] ALIGN1 = ".#-+ 0123456789"; + +static const char size_conv_str[] ALIGN1 = +"\x1\x4\x4\x4\x4\x4\x4\x8\x8\x8\x8\010cdiouxXeEfgG"; + +static const char int_convs[] ALIGN1 = "diouxX"; + dumper_t* FAST_FUNC alloc_dumper(void) { priv_dumper_t *dumper = xzalloc(sizeof(*dumper)); @@ -48,7 +85,6 @@ dumper_t* FAST_FUNC alloc_dumper(void) return &dumper->pub; } - static NOINLINE int bb_dump_size(FS *fs) { FU *fu; @@ -284,7 +320,9 @@ static NOINLINE void rewrite(priv_dumper_t *dumper, FS *fs) * repeat it as necessary. * * if rep count is greater than 1, no trailing whitespace - * gets output from the last iteration of the format unit. + * gets output from the last iteration of the format unit: + * 2/1 "%02x " prints "XX XX", not "XX XX " + * 2/1 "%02x\n" prints "XX\nXX", not "XX\nXX\n" */ for (fu = fs->nextfu; fu; fu = fu->nextfu) { if (!fu->nextfu @@ -453,7 +491,7 @@ static void bpad(PR *pr) for (p2 = ++p1; *p1 && strchr(" -0+#", *p1); ++p1) if (pr->nospace) pr->nospace--; - while ((*p2++ = *p1++) != 0) + while ((*p2++ = *p1++) != '\0') continue; } @@ -520,36 +558,43 @@ static void conv_u(PR *pr, unsigned char *p) static void display(priv_dumper_t* dumper) { - FS *fs; - FU *fu; - PR *pr; - int cnt; - unsigned char *bp, *savebp; - off_t saveaddress; + unsigned char *bp; unsigned char savech = '\0'; while ((bp = get(dumper)) != NULL) { + FS *fs; + unsigned char *savebp; + off_t saveaddress; + fs = dumper->pub.fshead; savebp = bp; saveaddress = dumper->address; for (; fs; fs = fs->nextfs, bp = savebp, dumper->address = saveaddress) { + FU *fu; for (fu = fs->nextfu; fu; fu = fu->nextfu) { + int cnt; if (fu->flags & F_IGNORE) { break; } for (cnt = fu->reps; cnt; --cnt) { + PR *pr; for (pr = fu->nextpr; pr; dumper->address += pr->bcnt, bp += pr->bcnt, pr = pr->nextpr) { - if (dumper->eaddress && dumper->address >= dumper->eaddress - && !(pr->flags & (F_TEXT | F_BPAD)) + if (dumper->eaddress + && dumper->address >= dumper->eaddress ) { - bpad(pr); + if (dumper->pub.eofstring) { + /* xxd support: requested to not pad incomplete blocks */ + fputs(dumper->pub.eofstring, stdout); + return; + } + if (!(pr->flags & (F_TEXT | F_BPAD))) + bpad(pr); } if (cnt == 1 && pr->nospace) { savech = *pr->nospace; *pr->nospace = '\0'; } -/* PRINT; */ switch (pr->flags) { case F_ADDRESS: printf(pr->fmt, (unsigned) dumper->address); @@ -638,7 +683,9 @@ static void display(priv_dumper_t* dumper) } } } + if (dumper->endfu) { + PR *pr; /* * if eaddress not set, error or file size was multiple * of blocksize, and no partial block ever found. @@ -695,8 +742,7 @@ void FAST_FUNC bb_dump_add(dumper_t* pub_dumper, const char *fmt) { const char *p; FS *tfs; - FU *tfu, **nextfupp; - const char *savep; + FU **nextfupp; /* start new linked list of format units */ tfs = xzalloc(sizeof(FS)); /*DBU:[dave@cray.com] start out NULL */ @@ -713,6 +759,9 @@ void FAST_FUNC bb_dump_add(dumper_t* pub_dumper, const char *fmt) /* take the format string and break it up into format units */ p = fmt; for (;;) { + FU *tfu; + const char *savep; + p = skip_whitespace(p); if (*p == '\0') { break; 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 @@ +#!/bin/sh + +# Copyright 2020 by Denys Vlasenko +# Licensed under GPLv2, see file LICENSE in this source tree. + +. ./testing.sh + +# testing "description" "command" "result" "infile" "stdin" +testing 'xxd -p with one NUL' \ + 'xxd -p' \ + "\ +00 +" \ + '' \ + '\0' + +testing 'xxd -p with 30 NULs' \ + 'xxd -p' \ + "\ +000000000000000000000000000000000000000000000000000000000000 +" \ + '' \ + '\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' + +testing 'xxd -p with 31 NULs' \ + 'xxd -p' \ + "\ +000000000000000000000000000000000000000000000000000000000000 +00 +" \ + '' \ + '\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' + +exit $FAILCOUNT diff --git a/util-linux/hexdump_xxd.c b/util-linux/hexdump_xxd.c index 6cf6d0297..f2d1ecb2c 100644 --- a/util-linux/hexdump_xxd.c +++ b/util-linux/hexdump_xxd.c @@ -141,6 +141,7 @@ int xxd_main(int argc UNUSED_PARAM, char **argv) bb_dump_add(dumper, buf); } else { bb_dump_add(dumper, "\"\n\""); + dumper->eofstring = "\n"; } return bb_dump_dump(dumper, argv); -- cgit v1.2.3-55-g6feb From 079487b48724d936b31a6696ae0f929b0251eaa3 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 20 Oct 2020 19:26:52 +0200 Subject: hexdump: fix dup block comparison function old new delta bb_dump_dump 1523 1520 -3 Signed-off-by: Denys Vlasenko --- libbb/dump.c | 1 - testsuite/hexdump.tests | 19 +++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/libbb/dump.c b/libbb/dump.c index 920f003ef..7a07d6605 100644 --- a/libbb/dump.c +++ b/libbb/dump.c @@ -432,7 +432,6 @@ static unsigned char *get(priv_dumper_t *dumper) if (dumper->pub.dump_vflag != DUP) { puts("*"); } - return NULL; } memset(dumper->get__curp + nread, 0, need); dumper->eaddress = dumper->address + nread; 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' \ '' \ '\0\0\0\0' +testing "hexdump does not think last padded block matches any full block" \ + "hexdump -e '1/1 \"%02x|\"1/1 \"%02x!\\n\"'" \ + "\ +00|00! +* +00| ! +" \ + '' \ + '\0\0\0\0\0\0\0\0\0\0\0' + +testing "hexdump thinks last full block can match" \ + "hexdump -e '1/1 \"%02x|\"1/1 \"%02x!\\n\"'" \ + "\ +00|00! +* +" \ + '' \ + '\0\0\0\0\0\0\0\0\0\0\0\0' + exit $FAILCOUNT -- cgit v1.2.3-55-g6feb From b65d6cb00fa0ea51bac4c4e62b576b43ae2c996b Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 24 Oct 2020 03:33:32 +0200 Subject: hush: do not print killing signal name in `cmd_whihc_dies_on_signal` function old new delta process_wait_result 438 449 +11 Signed-off-by: Denys Vlasenko --- shell/hush.c | 8 +++++++- shell/hush_test/hush-psubst/tick6.right | 1 + shell/hush_test/hush-psubst/tick6.tests | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 shell/hush_test/hush-psubst/tick6.right create mode 100755 shell/hush_test/hush-psubst/tick6.tests diff --git a/shell/hush.c b/shell/hush.c index e9cec1cc9..2b34b7da5 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -8698,9 +8698,15 @@ static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status) */ if (WIFSIGNALED(status)) { int sig = WTERMSIG(status); - if (i == fg_pipe->num_cmds-1) + if (G.run_list_level == 1 + /* ^^^^^ Do not print in nested contexts, example: + * echo `sleep 1; sh -c 'kill -9 $$'` - prints "137", NOT "Killed 137" + */ + && i == fg_pipe->num_cmds-1 + ) { /* TODO: use strsignal() instead for bash compat? but that's bloat... */ puts(sig == SIGINT || sig == SIGPIPE ? "" : get_signame(sig)); + } /* TODO: if (WCOREDUMP(status)) + " (core dumped)"; */ /* TODO: MIPS has 128 sigs (1..128), what if sig==128 here? * Maybe we need to use sig | 128? */ 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 $$'` $? -- cgit v1.2.3-55-g6feb From e16f7eb5967b9a960f4600c20690af63fb830b60 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 24 Oct 2020 04:26:43 +0200 Subject: hush: output bash-compat killing signal names This significantly syncronises ash-signals and hush-signals tests. function old new delta process_wait_result 449 450 +1 Signed-off-by: Denys Vlasenko --- shell/ash_test/ash-signals/usage.right | 14 +++++++++++++ shell/ash_test/ash-signals/usage.tests | 23 ++++++++++++++++++++ shell/hush.c | 4 ++-- shell/hush_test/hush-misc/sig_exitcode.right | 4 ++-- shell/hush_test/hush-signals/catch.right | 2 +- shell/hush_test/hush-signals/signal1.right | 20 ++++++++++++++++++ shell/hush_test/hush-signals/signal1.tests | 28 +++++++++++++++++++++++++ shell/hush_test/hush-signals/signal_read2.right | 2 +- shell/hush_test/hush-signals/subshell.right | 2 +- 9 files changed, 92 insertions(+), 7 deletions(-) create mode 100644 shell/ash_test/ash-signals/usage.right create mode 100755 shell/ash_test/ash-signals/usage.tests create mode 100644 shell/hush_test/hush-signals/signal1.right create mode 100755 shell/hush_test/hush-signals/signal1.tests 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 @@ +___ +___ +___ +trap -- 'a' EXIT +trap -- 'a' INT +trap -- 'a' USR1 +trap -- 'a' USR2 +___ +___ +trap -- 'a' USR1 +trap -- 'a' USR2 +___ +___ +trap -- '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 @@ +# no output -- default state +echo ___ +trap + +# assign some traps +echo ___ +trap "a" EXIT INT USR1 USR2 + +# show them all +echo ___ +trap + +# clear one +echo ___ +trap 0 INT +echo ___ +trap + +# clear another +echo ___ +trap "-" USR1 +echo ___ +trap diff --git a/shell/hush.c b/shell/hush.c index 2b34b7da5..bc6e6014f 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -8704,8 +8704,8 @@ static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status) */ && i == fg_pipe->num_cmds-1 ) { - /* TODO: use strsignal() instead for bash compat? but that's bloat... */ - puts(sig == SIGINT || sig == SIGPIPE ? "" : get_signame(sig)); + /* strsignal() is for bash compat. ~600 bloat versus bbox's get_signame() */ + puts(sig == SIGINT || sig == SIGPIPE ? "" : strsignal(sig)); } /* TODO: if (WCOREDUMP(status)) + " (core dumped)"; */ /* TODO: MIPS has 128 sigs (1..128), what if sig==128 here? 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 @@ -KILL +Killed 137:137 -KILL +Killed 0:0 Done 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 caught sending USR2 sending USR2 -USR2 +User 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 @@ +got signal +trap -- 'echo got signal' USR1 +sent 1 signal +got signal +wait interrupted +trap -- 'echo got signal' USR1 +sent 2 signal +got signal +wait interrupted +trap -- 'echo got signal' USR1 +sent 3 signal +got signal +wait interrupted +trap -- 'echo got signal' USR1 +sent 4 signal +got signal +wait interrupted +trap -- 'echo got signal' USR1 +sent 5 signal +sleep 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 @@ +trap "echo got signal" USR1 + +for try in 1 2 3 4 5; do + kill -USR1 $$ + sleep 0.2 + echo "sent $try signal" +done & + +# Ensure "wait" has something to wait for +sleep 2 & + +# Ensure we do not execute "trap" below before "kill -USR1" above +# (was getting failure on loaded machine without this) +sleep 0.1 + +sleeping=true +while $sleeping; do + trap + if wait %%; then + echo "sleep completed" + sleeping=false + elif [ $? = 127 ]; then + echo "BUG: no processes to wait for?!" + sleeping=false + else + echo "wait interrupted" + fi +done 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 @@ -HUP +Hangup Done: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 trap -- '' HUP trap -- '' QUIT trap -- '' SYS -TERM +Terminated Done -- cgit v1.2.3-55-g6feb From 32e1f69ae05c0b02495509caad77269e0fecd395 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 25 Oct 2020 16:06:45 +0100 Subject: compat: move hexdump -R functionality to xxd -r function old new delta xxd_main 466 680 +214 packed_usage 33474 33483 +9 hexdump_opts 17 16 -1 hexdump_main 565 401 -164 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/2 up/down: 223/-165) Total: 58 bytes Signed-off-by: Denys Vlasenko --- util-linux/hexdump.c | 61 ++------------------------------------ util-linux/hexdump_xxd.c | 76 ++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 73 insertions(+), 64 deletions(-) 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 @@ //config: The hexdump utility is used to display binary data in a readable //config: way that is comparable to the output from most hex editors. //config: -//config:config FEATURE_HEXDUMP_REVERSE -//config: bool "Support -R, reverse of 'hexdump -Cv'" -//config: default y -//config: depends on HEXDUMP -//config: help -//config: The hexdump utility is used to display binary data in an ascii -//config: readable way. This option creates binary data from an ascii input. -//config: NB: this option is non-standard. It's unwise to use it in scripts -//config: aimed to be portable. -//config: //config:config HD //config: bool "hd (7.8 kb)" //config: default y @@ -38,7 +28,7 @@ //kbuild:lib-$(CONFIG_HD) += hexdump.o //usage:#define hexdump_trivial_usage -//usage: "[-bcCdefnosvx" IF_FEATURE_HEXDUMP_REVERSE("R") "] [FILE]..." +//usage: "[-bcCdefnosvx] [FILE]..." //usage:#define hexdump_full_usage "\n\n" //usage: "Display FILEs (or stdin) in a user specified format\n" //usage: "\n -b 1-byte octal display" @@ -53,9 +43,6 @@ // exactly the same help text lines in hexdump and xxd: //usage: "\n -n LENGTH Show only first LENGTH bytes" //usage: "\n -s OFFSET Skip OFFSET bytes" -//usage: IF_FEATURE_HEXDUMP_REVERSE( -//usage: "\n -R Reverse of 'hexdump -Cv'") -// TODO: NONCOMPAT!!! move -R to xxd -r //usage: //usage:#define hd_trivial_usage //usage: "FILE..." @@ -94,7 +81,7 @@ static const char *const add_strings[] = { static const char add_first[] ALIGN1 = "\"%07.7_Ax\n\""; -static const char hexdump_opts[] ALIGN1 = "bcdoxCe:f:n:s:v" IF_FEATURE_HEXDUMP_REVERSE("R"); +static const char hexdump_opts[] ALIGN1 = "bcdoxCe:f:n:s:v"; int hexdump_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int hexdump_main(int argc, char **argv) @@ -102,10 +89,6 @@ int hexdump_main(int argc, char **argv) dumper_t *dumper = alloc_dumper(); const char *p; int ch; -#if ENABLE_FEATURE_HEXDUMP_REVERSE - FILE *fp; - smallint rdump = 0; -#endif if (ENABLE_HD && (!ENABLE_HEXDUMP || !applet_name[2]) @@ -153,11 +136,6 @@ int hexdump_main(int argc, char **argv) if (ch == 'v') { dumper->dump_vflag = ALL; } -#if ENABLE_FEATURE_HEXDUMP_REVERSE - if (ch == 'R') { - rdump = 1; - } -#endif } if (!dumper->fshead) { @@ -167,40 +145,5 @@ int hexdump_main(int argc, char **argv) argv += optind; -#if !ENABLE_FEATURE_HEXDUMP_REVERSE return bb_dump_dump(dumper, argv); -#else - if (!rdump) { - return bb_dump_dump(dumper, argv); - } - - /* -R: reverse of 'hexdump -Cv' */ - fp = stdin; - if (!*argv) { - argv--; - goto jump_in; - } - - do { - char *buf; - fp = xfopen_for_read(*argv); - jump_in: - while ((buf = xmalloc_fgetline(fp)) != NULL) { - p = buf; - while (1) { - /* skip address or previous byte */ - while (isxdigit(*p)) p++; - while (*p == ' ') p++; - /* '|' char will break the line */ - if (!isxdigit(*p) || sscanf(p, "%x ", &ch) != 1) - break; - putchar(ch); - } - free(buf); - } - fclose(fp); - } while (*++argv); - - fflush_stdout_and_exit(EXIT_SUCCESS); -#endif } diff --git a/util-linux/hexdump_xxd.c b/util-linux/hexdump_xxd.c index f2d1ecb2c..d2f4b6ed8 100644 --- a/util-linux/hexdump_xxd.c +++ b/util-linux/hexdump_xxd.c @@ -50,6 +50,7 @@ // exactly the same help text lines in hexdump and xxd: //usage: "\n -l LENGTH Show only first LENGTH bytes" //usage: "\n -s OFFSET Skip OFFSET bytes" +//usage: "\n -r Reverse (with -p, assumes no offsets in input)" // TODO: implement -r (see hexdump -R) #include "libbb.h" @@ -57,6 +58,71 @@ /* This is a NOEXEC applet. Be very careful! */ +#define OPT_l (1 << 0) +#define OPT_s (1 << 1) +#define OPT_a (1 << 2) +#define OPT_p (1 << 3) +#define OPT_r (1 << 4) + +static void reverse(unsigned opt, unsigned cols, const char *filename) +{ + FILE *fp; + char *buf; + + fp = filename ? xfopen_for_read(filename) : stdin; + + while ((buf = xmalloc_fgetline(fp)) != NULL) { + char *p = buf; + unsigned cnt = cols; + + if (!(opt & OPT_p)) { + /* skip address */ + while (isxdigit(*p)) p++; + /* NB: for xxd -r, first hex portion is address even without colon */ + /* If it's there, skip it: */ + if (*p == ':') p++; + +//TODO: seek (or zero-pad if unseekable) to the address position +//NOTE: -s SEEK value should be added to the address before seeking + } + + /* Process hex bytes optionally separated by whitespace */ + do { + uint8_t val, c; + + p = skip_whitespace(p); + + c = *p++; + if (isdigit(c)) + val = c - '0'; + else if ((c|0x20) >= 'a' && (c|0x20) <= 'f') + val = (c|0x20) - ('a' - 10); + else + break; + val <<= 4; + + /* Works the same with xxd V1.10: + * echo "31 09 32 0a" | xxd -r -p + * echo "31 0 9 32 0a" | xxd -r -p + * thus allow whitespace even within the byte: + */ + p = skip_whitespace(p); + + c = *p++; + if (isdigit(c)) + val |= c - '0'; + else if ((c|0x20) >= 'a' && (c|0x20) <= 'f') + val |= (c|0x20) - ('a' - 10); + else + break; + putchar(val); + } while (!(opt & OPT_p) || --cnt != 0); + free(buf); + } + //fclose(fp); + fflush_stdout_and_exit(EXIT_SUCCESS); +} + int xxd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int xxd_main(int argc UNUSED_PARAM, char **argv) { @@ -69,11 +135,7 @@ int xxd_main(int argc UNUSED_PARAM, char **argv) dumper = alloc_dumper(); -#define OPT_l (1 << 0) -#define OPT_s (1 << 1) -#define OPT_a (1 << 2) -#define OPT_p (1 << 3) - opt = getopt32(argv, "^" "l:s:apg:+c:+" "\0" "?1" /* 1 argument max */, + opt = getopt32(argv, "^" "l:s:aprg:+c:+" "\0" "?1" /* 1 argument max */, &opt_l, &opt_s, &bytes, &cols ); argv += optind; @@ -107,6 +169,10 @@ int xxd_main(int argc UNUSED_PARAM, char **argv) bb_dump_add(dumper, "\"%08.8_ax: \""); // "address: " } + if (opt & OPT_r) { + reverse(opt, cols, argv[0]); + } + if (bytes < 1 || bytes >= cols) { sprintf(buf, "%u/1 \"%%02x\"", cols); // cols * "xx" bb_dump_add(dumper, buf); -- cgit v1.2.3-55-g6feb From 112453acf24520b4655f9f36da41d8ac591b1a60 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 25 Oct 2020 23:44:22 +0100 Subject: od: unbreak it Signed-off-by: Denys Vlasenko --- include/dump.h | 24 +++++++++++++++++++++++- libbb/dump.c | 24 ------------------------ 2 files changed, 23 insertions(+), 25 deletions(-) diff --git a/include/dump.h b/include/dump.h index f4759c193..9193a6925 100644 --- a/include/dump.h +++ b/include/dump.h @@ -4,7 +4,29 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN enum dump_vflag_t { ALL, DUP, FIRST, WAIT }; /* -v values */ -typedef struct FS FS; +typedef struct PR { + struct PR *nextpr; /* next print unit */ + unsigned flags; /* flag values */ + int bcnt; /* byte count */ + char *cchar; /* conversion character */ + char *fmt; /* printf format */ + char *nospace; /* no whitespace version */ +} PR; + +typedef struct FU { + struct FU *nextfu; /* next format unit */ + struct PR *nextpr; /* next print unit */ + unsigned flags; /* flag values */ + int reps; /* repetition count */ + int bcnt; /* byte count */ + char *fmt; /* format string */ +} FU; + +typedef struct FS { /* format strings */ + struct FS *nextfs; /* linked list of format strings */ + struct FU *nextfu; /* linked list of format units */ + int bcnt; +} FS; typedef struct dumper_t { off_t dump_skip; /* bytes to skip */ diff --git a/libbb/dump.c b/libbb/dump.c index 7a07d6605..1ba1132b3 100644 --- a/libbb/dump.c +++ b/libbb/dump.c @@ -27,30 +27,6 @@ #define F_UINT 0x200 /* %[ouXx] */ #define F_TEXT 0x400 /* no conversions */ -typedef struct PR { - struct PR *nextpr; /* next print unit */ - unsigned flags; /* flag values */ - int bcnt; /* byte count */ - char *cchar; /* conversion character */ - char *fmt; /* printf format */ - char *nospace; /* no whitespace version */ -} PR; - -typedef struct FU { - struct FU *nextfu; /* next format unit */ - struct PR *nextpr; /* next print unit */ - unsigned flags; /* flag values */ - int reps; /* repetition count */ - int bcnt; /* byte count */ - char *fmt; /* format string */ -} FU; - -typedef struct FS { /* format strings */ - struct FS *nextfs; /* linked list of format strings */ - struct FU *nextfu; /* linked list of format units */ - int bcnt; -} FS; - typedef struct priv_dumper_t { dumper_t pub; -- cgit v1.2.3-55-g6feb From d2241f59022c38d4b171e56eea42e216ecccfdd9 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 31 Oct 2020 03:34:07 +0100 Subject: shell: better support of [[ ]] bashism Still rather rudimentary for ash function old new delta binop 433 589 +156 check_operator 65 101 +36 done_word 736 769 +33 test_main 405 418 +13 parse_stream 2227 2238 +11 ops_texts 124 133 +9 ops_table 80 86 +6 run_pipe 1557 1562 +5 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 8/0 up/down: 269/0) Total: 269 bytes Signed-off-by: Denys Vlasenko --- coreutils/test.c | 82 ++++++++++++++++++++++++++++++++ shell/ash.c | 15 +++--- shell/hush.c | 57 +++++++++++++++------- shell/hush_test/hush-test2/andor1.right | 6 +++ shell/hush_test/hush-test2/andor1.tests | 7 +++ shell/hush_test/hush-test2/noglob1.right | 2 + shell/hush_test/hush-test2/noglob1.tests | 3 ++ shell/hush_test/hush-test2/strops1.right | 8 ++++ shell/hush_test/hush-test2/strops1.tests | 15 ++++++ shell/hush_test/hush-test2/strops2.right | 6 +++ shell/hush_test/hush-test2/strops2.tests | 12 +++++ shell/hush_test/hush-test2/strops3.right | 7 +++ shell/hush_test/hush-test2/strops3.tests | 13 +++++ 13 files changed, 209 insertions(+), 24 deletions(-) create mode 100644 shell/hush_test/hush-test2/andor1.right create mode 100755 shell/hush_test/hush-test2/andor1.tests create mode 100644 shell/hush_test/hush-test2/noglob1.right create mode 100755 shell/hush_test/hush-test2/noglob1.tests create mode 100644 shell/hush_test/hush-test2/strops1.right create mode 100755 shell/hush_test/hush-test2/strops1.tests create mode 100644 shell/hush_test/hush-test2/strops2.right create mode 100755 shell/hush_test/hush-test2/strops2.tests create mode 100644 shell/hush_test/hush-test2/strops3.right create mode 100755 shell/hush_test/hush-test2/strops3.tests 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 @@ //usage: "1\n" #include "libbb.h" +#include +#include /* This is a NOFORK applet. Be very careful! */ @@ -146,6 +148,14 @@ #define TEST_DEBUG 0 +#if ENABLE_TEST2 \ + || (ENABLE_ASH_BASH_COMPAT && ENABLE_ASH_TEST) \ + || (ENABLE_HUSH_BASH_COMPAT && ENABLE_HUSH_TEST) +# define BASH_TEST2 1 +#else +# define BASH_TEST2 0 +#endif + enum token { EOI, @@ -184,6 +194,10 @@ enum token { STRLT, STRGT, +#if BASH_TEST2 + REGEX, +#endif + INTEQ, /* int ops */ INTNE, INTGE, @@ -257,6 +271,9 @@ static const char *const TOKSTR[] = { "STRNE", "STRLT", "STRGT", +#if BASH_TEST2 + "REGEX", +#endif "INTEQ", "INTNE", "INTGE", @@ -320,6 +337,9 @@ static const struct operator_t ops_table[] = { { /* "!=" */ STRNE , BINOP }, { /* "<" */ STRLT , BINOP }, { /* ">" */ STRGT , BINOP }, +#if BASH_TEST2 + { /* "=~" */ REGEX , BINOP }, +#endif { /* "-eq"*/ INTEQ , BINOP }, { /* "-ne"*/ INTNE , BINOP }, { /* "-ge"*/ INTGE , BINOP }, @@ -332,6 +352,10 @@ static const struct operator_t ops_table[] = { { /* "!" */ UNOT , BUNOP }, { /* "-a" */ BAND , BBINOP }, { /* "-o" */ BOR , BBINOP }, +#if BASH_TEST2 + { /* "&&" */ BAND , BBINOP }, + { /* "||" */ BOR , BBINOP }, +#endif { /* "(" */ LPAREN , PAREN }, { /* ")" */ RPAREN , PAREN }, }; @@ -365,6 +389,9 @@ static const char ops_texts[] ALIGN1 = "!=" "\0" "<" "\0" ">" "\0" +#if BASH_TEST2 + "=~" "\0" +#endif "-eq" "\0" "-ne" "\0" "-ge" "\0" @@ -377,6 +404,10 @@ static const char ops_texts[] ALIGN1 = "!" "\0" "-a" "\0" "-o" "\0" +#if BASH_TEST2 + "&&" "\0" + "||" "\0" +#endif "(" "\0" ")" "\0" ; @@ -397,6 +428,9 @@ struct test_statics { const struct operator_t *last_operator; gid_t *group_array; int ngroups; +#if BASH_TEST2 + bool bash_test2; +#endif jmp_buf leaving; }; @@ -408,6 +442,7 @@ extern struct test_statics *const test_ptr_to_statics; #define last_operator (S.last_operator) #define group_array (S.group_array ) #define ngroups (S.ngroups ) +#define bash_test2 (S.bash_test2 ) #define leaving (S.leaving ) #define INIT_S() do { \ @@ -501,6 +536,20 @@ static enum token check_operator(const char *s) n = index_in_strings(ops_texts, s); if (n < 0) return OPERAND; + +#if BASH_TEST2 + if (ops_table[n].op_num == REGEX && !bash_test2) { + /* =~ is only for [[ ]] */ + return OPERAND; + } + if (ops_table[n].op_num == BAND || ops_table[n].op_num == BOR) { + /* [ ] accepts -a and -o but not && and || */ + /* [[ ]] accepts && and || but not -a and -o */ + if (bash_test2 == (s[0] == '-')) + return OPERAND; + } +#endif + last_operator = &ops_table[n]; return ops_table[n].op_num; } @@ -536,6 +585,29 @@ static int binop(void) /*if (op->op_num == INTLT)*/ return val1 < val2; } +#if BASH_TEST2 + if (bash_test2) { + if (op->op_num == STREQ) { + val1 = fnmatch(opnd2, opnd1, 0); + return val1 == 0; + } + if (op->op_num == STRNE) { + val1 = fnmatch(opnd2, opnd1, 0); + return val1 != 0; + } + if (op->op_num == REGEX) { + regex_t re_buffer; + memset(&re_buffer, 0, sizeof(re_buffer)); + if (regcomp(&re_buffer, opnd2, REG_EXTENDED)) { // REG_NEWLINE? + /* Bad regex */ + longjmp(leaving, 2); /* [[ a =~ * ]]; echo $? - prints 2 (silently, no error msg) */ + } + val1 = regexec(&re_buffer, opnd1, 0, NULL, 0); + regfree(&re_buffer); + return val1 == 0; + } + } +#endif if (is_str_op(op->op_num)) { val1 = strcmp(opnd1, opnd2); if (op->op_num == STREQ) @@ -824,6 +896,9 @@ int test_main(int argc, char **argv) { int res; const char *arg0; +#if BASH_TEST2 + bool bt2 = 0; +#endif arg0 = bb_basename(argv[0]); if ((ENABLE_TEST1 || ENABLE_TEST2 || ENABLE_ASH_TEST || ENABLE_HUSH_TEST) @@ -840,6 +915,9 @@ int test_main(int argc, char **argv) bb_simple_error_msg("missing ]]"); return 2; } +#if BASH_TEST2 + bt2 = 1; +#endif } argv[argc] = NULL; } @@ -848,6 +926,10 @@ int test_main(int argc, char **argv) /* We must do DEINIT_S() prior to returning */ INIT_S(); +#if BASH_TEST2 + bash_test2 = bt2; +#endif + res = setjmp(leaving); if (res) goto ret; diff --git a/shell/ash.c b/shell/ash.c index 58da0a2a0..cfcc0b818 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -207,17 +207,17 @@ #define IF_BASH_SUBSTR IF_ASH_BASH_COMPAT /* BASH_TEST2: [[ EXPR ]] * Status of [[ support: - * We replace && and || with -a and -o + * && and || work as they should + * = is glob match operator, not equality operator: STR = GLOB + * (in GLOB, quoting is significant on char-by-char basis: a*cd"*") + * == same as = + * add =~ regex match operator: STR =~ REGEX * TODO: * singleword+noglob expansion: * v='a b'; [[ $v = 'a b' ]]; echo 0:$? * [[ /bin/n* ]]; echo 0:$? - * -a/-o are not AND/OR ops! (they are just strings) * quoting needs to be considered (-f is an operator, "-f" and ""-f are not; etc) - * = is glob match operator, not equality operator: STR = GLOB - * (in GLOB, quoting is significant on char-by-char basis: a*cd"*") - * == same as = - * add =~ regex match operator: STR =~ REGEX + * ( ) < > should not have special meaning */ #define BASH_TEST2 (ENABLE_ASH_BASH_COMPAT * ENABLE_ASH_TEST) #define BASH_SOURCE ENABLE_ASH_BASH_COMPAT @@ -11823,7 +11823,8 @@ simplecmd(void) tokpushback = 1; goto out; } - wordtext = (char *) (t == TAND ? "-a" : "-o"); + /* pass "&&" or "||" to [[ ]] as literal args */ + wordtext = (char *) (t == TAND ? "&&" : "||"); #endif case TWORD: n = stzalloc(sizeof(struct narg)); diff --git a/shell/hush.c b/shell/hush.c index bc6e6014f..7c1e1d748 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -84,13 +84,12 @@ * [[ args ]] are CMD_SINGLEWORD_NOGLOB: * v='a b'; [[ $v = 'a b' ]]; echo 0:$? * [[ /bin/n* ]]; echo 0:$? + * = is glob match operator, not equality operator: STR = GLOB + * (in GLOB, quoting is significant on char-by-char basis: a*cd"*") + * == same as = + * =~ is regex match operator: STR =~ REGEX * TODO: - * &&/|| are AND/OR ops, -a/-o are not * quoting needs to be considered (-f is an operator, "-f" and ""-f are not; etc) - * = is glob match operator, not equality operator: STR = GLOB - * (in GLOB, quoting is significant on char-by-char basis: a*cd"*") - * == same as = - * add =~ regex match operator: STR =~ REGEX */ //config:config HUSH //config: bool "hush (68 kb)" @@ -651,14 +650,16 @@ struct command { smallint cmd_type; /* CMD_xxx */ #define CMD_NORMAL 0 #define CMD_SUBSHELL 1 -#if BASH_TEST2 || ENABLE_HUSH_LOCAL || ENABLE_HUSH_EXPORT || ENABLE_HUSH_READONLY -/* used for "[[ EXPR ]]", and to prevent word splitting and globbing in - * "export v=t*" - */ -# define CMD_SINGLEWORD_NOGLOB 2 +#if BASH_TEST2 +/* used for "[[ EXPR ]]" */ +# define CMD_TEST2_SINGLEWORD_NOGLOB 2 +#endif +#if ENABLE_HUSH_LOCAL || ENABLE_HUSH_EXPORT || ENABLE_HUSH_READONLY +/* used to prevent word splitting and globbing in "export v=t*" */ +# define CMD_SINGLEWORD_NOGLOB 3 #endif #if ENABLE_HUSH_FUNCTIONS -# define CMD_FUNCDEF 3 +# define CMD_FUNCDEF 4 #endif smalluint cmd_exitcode; @@ -4111,6 +4112,14 @@ static int done_word(struct parse_context *ctx) /* ctx->ctx_res_w = RES_MATCH; */ ctx->ctx_dsemicolon = 0; } else +# endif +# if defined(CMD_TEST2_SINGLEWORD_NOGLOB) + if (command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB + && strcmp(ctx->word.data, "]]") == 0 + ) { + /* allow "[[ ]] >file" etc */ + command->cmd_type = CMD_SINGLEWORD_NOGLOB; + } else # endif if (!command->argv /* if it's the first word... */ # if ENABLE_HUSH_LOOPS @@ -4146,11 +4155,13 @@ static int done_word(struct parse_context *ctx) (ctx->ctx_res_w == RES_SNTX)); return (ctx->ctx_res_w == RES_SNTX); } +# if defined(CMD_TEST2_SINGLEWORD_NOGLOB) + if (strcmp(ctx->word.data, "[[") == 0) { + command->cmd_type = CMD_TEST2_SINGLEWORD_NOGLOB; + } else +# endif # if defined(CMD_SINGLEWORD_NOGLOB) if (0 -# if BASH_TEST2 - || strcmp(ctx->word.data, "[[") == 0 -# endif /* In bash, local/export/readonly are special, args * are assignments and therefore expansion of them * should be "one-word" expansion: @@ -4172,7 +4183,8 @@ static int done_word(struct parse_context *ctx) ) { command->cmd_type = CMD_SINGLEWORD_NOGLOB; } - /* fall through */ +# else + { /* empty block to pair "if ... else" */ } # endif } #endif /* HAS_KEYWORDS */ @@ -5354,9 +5366,15 @@ static struct pipe *parse_stream(char **pstring, if (ch != '\n') next = i_peek_and_eat_bkslash_nl(input); - is_special = "{}<>;&|()#" /* special outside of "str" */ + is_special = "{}<>&|();#" /* special outside of "str" */ "$\"" IF_HUSH_TICK("`") /* always special */ SPECIAL_VAR_SYMBOL_STR; +#if defined(CMD_TEST2_SINGLEWORD_NOGLOB) + if (ctx.command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB) { + /* In [[ ]], {}<>&|() are not special */ + is_special += 8; + } else +#endif /* Are { and } special here? */ if (ctx.command->argv /* word [word]{... - non-special */ || ctx.word.length /* word{... - non-special */ @@ -6953,7 +6971,7 @@ static char **expand_strvec_to_strvec(char **argv) return expand_variables(argv, EXP_FLAG_GLOB | EXP_FLAG_ESC_GLOB_CHARS); } -#if defined(CMD_SINGLEWORD_NOGLOB) +#if defined(CMD_SINGLEWORD_NOGLOB) || defined(CMD_TEST2_SINGLEWORD_NOGLOB) static char **expand_strvec_to_strvec_singleword_noglob(char **argv) { return expand_variables(argv, EXP_FLAG_SINGLEWORD); @@ -9133,6 +9151,11 @@ static NOINLINE int run_pipe(struct pipe *pi) } /* Expand the rest into (possibly) many strings each */ +#if defined(CMD_TEST2_SINGLEWORD_NOGLOB) + if (command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB) + argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt); + else +#endif #if defined(CMD_SINGLEWORD_NOGLOB) if (command->cmd_type == CMD_SINGLEWORD_NOGLOB) argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt); 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 @@ +1:YES +2:no +3:YES +4:YES +5:no +6: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 @@ +e='' +[[ a && b ]] && echo 1:YES +[[ a && '' ]] || echo 2:no +[[ a || b ]] && echo 3:YES +[[ '' || b ]] && echo 4:YES +[[ "" || "$e" ]] || echo 5:no +[[ "" || $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 @@ +1:YES:0 +2: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 @@ +v='*.tests' +[[ *.tests ]]; echo 1:YES:$? +[[ $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 @@ +1:YES:0 +2:YES:0 +3:YES:0 +4:YES:0 +5:YES:0 +6:YES:0 +7:YES:0 +8: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 @@ +v='*.z' +[[ a.z = *.z ]]; echo 1:YES:$? +[[ a.z == $v ]]; echo 2:YES:$? + +# wildcards can match a slash +[[ a/b = a*b ]]; echo 3:YES:$? +[[ a/b == a?b ]]; echo 4:YES:$? + +# wildcards can match a leading dot +[[ a/.b = a/*b ]]; echo 5:YES:$? +[[ a/.b == a/?b ]]; echo 6:YES:$? + +# wildcards can be escaped +[[ abc = a*c ]]; echo 7:YES:$? +[[ 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 @@ +1:ERR2:2 +2:YES:0 +3:YES:0 +4:YES:0 +5:no:1 +6: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 @@ +# malformed regex +[[ a =~ * ]]; echo 1:ERR2:$? + +[[ a/b =~ a.b ]]; echo 2:YES:$? +[[ a/b =~ /*b ]]; echo 3:YES:$? + +v='[]b.-]' +[[ a/.b] =~ $v ]]; echo 4:YES:$? + +v=']b.-' +[[ a/.b] =~ $v ]]; echo 5:no:$? +[[ 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 @@ +1:YES:0 +2:YES:0 +3:no:1 +4:YES:0 +2u:YES:0 +3u:YES:0 +4u: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 @@ +# regex should accept '+' operator +[[ abcdef =~ a[b-z]+ ]]; echo 1:YES:$? + +# newline matches by "match any" patterns +v=' +' +[[ "$v" =~ . ]]; echo 2:YES:$? +[[ "$v" =~ "[$v]" ]]; echo 3:no:$? # hmm bash does return 1... why? +[[ "$v" =~ [^a] ]]; echo 4:YES:$? +# should work even without quotes: +[[ $v =~ . ]]; echo 2u:YES:$? +[[ $v =~ [$v] ]]; echo 3u:YES:$? +[[ $v =~ [^a] ]]; echo 4u:YES:$? -- cgit v1.2.3-55-g6feb From b259e97d47de9a3f6b3dc4538159e57544c04a01 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 31 Oct 2020 03:51:37 +0100 Subject: hush: add tests for unquoted < > in [[ ]] They pass. Signed-off-by: Denys Vlasenko --- shell/hush_test/hush-test2/strops4.right | 2 ++ shell/hush_test/hush-test2/strops4.tests | 3 +++ 2 files changed, 5 insertions(+) create mode 100644 shell/hush_test/hush-test2/strops4.right create mode 100755 shell/hush_test/hush-test2/strops4.tests 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 @@ +1:no:1 +2: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 @@ +# < > are not redirect operators +[[ a > b ]]; echo 1:no:$? +[[ a < b ]]; echo 2:YES:$? -- cgit v1.2.3-55-g6feb From a7c065354f820560621aa5f2e90498d6636f41c3 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 31 Oct 2020 04:32:34 +0100 Subject: shell: update comments on the status of [[ ]] compat Signed-off-by: Denys Vlasenko --- shell/ash.c | 6 +++--- shell/hush.c | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index cfcc0b818..674a41bd3 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -209,15 +209,15 @@ * Status of [[ support: * && and || work as they should * = is glob match operator, not equality operator: STR = GLOB - * (in GLOB, quoting is significant on char-by-char basis: a*cd"*") * == same as = - * add =~ regex match operator: STR =~ REGEX + * =~ is regex match operator: STR =~ REGEX * TODO: * singleword+noglob expansion: * v='a b'; [[ $v = 'a b' ]]; echo 0:$? * [[ /bin/n* ]]; echo 0:$? * quoting needs to be considered (-f is an operator, "-f" and ""-f are not; etc) - * ( ) < > should not have special meaning + * ( ) < > should not have special meaning (IOW: should not require quoting) + * in word = GLOB, quoting should be significant on char-by-char basis: a*cd"*" */ #define BASH_TEST2 (ENABLE_ASH_BASH_COMPAT * ENABLE_ASH_TEST) #define BASH_SOURCE ENABLE_ASH_BASH_COMPAT diff --git a/shell/hush.c b/shell/hush.c index 7c1e1d748..ab7263381 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -63,7 +63,6 @@ * reserved words: function select * advanced test: [[ ]] * process substitution: <(list) and >(list) - * =~: regex operator * let EXPR [EXPR...] * Each EXPR is an arithmetic expression (ARITHMETIC EVALUATION) * If the last arg evaluates to 0, let returns 1; 0 otherwise. @@ -85,11 +84,11 @@ * v='a b'; [[ $v = 'a b' ]]; echo 0:$? * [[ /bin/n* ]]; echo 0:$? * = is glob match operator, not equality operator: STR = GLOB - * (in GLOB, quoting is significant on char-by-char basis: a*cd"*") * == same as = * =~ is regex match operator: STR =~ REGEX * TODO: * quoting needs to be considered (-f is an operator, "-f" and ""-f are not; etc) + * in word = GLOB, quoting should be significant on char-by-char basis: a*cd"*" */ //config:config HUSH //config: bool "hush (68 kb)" -- cgit v1.2.3-55-g6feb From 64981b4c8e88812c322bee3832f1d421ff670ed5 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 1 Nov 2020 18:47:24 +0100 Subject: hush: document bugs in [[ =~ ]] comparison Signed-off-by: Denys Vlasenko --- shell/hush_test/hush-bugs/strops5_bug.right | 2 ++ shell/hush_test/hush-bugs/strops5_bug.tests | 10 ++++++++++ shell/hush_test/hush-test2/strops5.right | 3 +++ shell/hush_test/hush-test2/strops5.tests | 13 +++++++++++++ 4 files changed, 28 insertions(+) create mode 100644 shell/hush_test/hush-bugs/strops5_bug.right create mode 100755 shell/hush_test/hush-bugs/strops5_bug.tests create mode 100644 shell/hush_test/hush-test2/strops5.right create mode 100755 shell/hush_test/hush-test2/strops5.tests 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 @@ +4:no:1 +5: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 @@ +v='*.z' + +# Buggy: +# the problem is that expansion rules of LHS and RHS of ~= +# should not be the same: in RHS, "$v" and "*" should escape metas +# (currently "$v" does not), +# but in LHS, they should _not_ do that +# (currently "*" does). Thus these cases fail: +[[ a.z == "$v" ]]; echo 4:no:$? # BUG: "$v" expands to *.z +[[ "*".z == ?.z ]]; echo 5:YES:$? # BUG: "*" expands to \* 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 @@ +1:YES:0 +2:no:1 +3: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 @@ +v='*.z' +[[ a.z = *.z ]]; echo 1:YES:$? +[[ a.z = "*".z ]]; echo 2:no:$? +[[ a.z == $v ]]; echo 3:YES:$? + +# Buggy: +# the problem is that expansion rules of LHS and RHS of ~= +# should not be the same: in RHS, "$v" and "*" should escape metas +# (currently "$v" does not), +# but in LHS, they should _not_ do that +# (currently "*" does). Thus these cases fail: +#[[ a.z == "$v" ]]; echo 4:no:$? # BUG: "$v" expands to *.z +#[[ "*".z == ?.z ]]; echo 5:YES:$? # BUG: "*" expands to \* -- cgit v1.2.3-55-g6feb