From f56ddf2e4ce42d7914d048f42fd952302027e8a3 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Tue, 27 Jun 2017 17:51:07 +0200 Subject: ash: fix $HOME/.profile reading if !ASH_EXPAND_PRMT Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/ash.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'shell') diff --git a/shell/ash.c b/shell/ash.c index eb51d47cc..9681111cc 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -2458,12 +2458,8 @@ putprompt(const char *s) } #endif -#if ENABLE_ASH_EXPAND_PRMT /* expandstr() needs parsing machinery, so it is far away ahead... */ static const char *expandstr(const char *ps); -#else -#define expandstr(s) s -#endif static void setprompt_if(smallint do_set, int whichprompt) @@ -12449,7 +12445,6 @@ parseheredoc(void) /* * called by editline -- any expansions to the prompt should be added here. */ -#if ENABLE_ASH_EXPAND_PRMT static const char * expandstr(const char *ps) { @@ -12475,7 +12470,6 @@ expandstr(const char *ps) expandarg(&n, NULL, EXP_QUOTED); return stackblock(); } -#endif /* * Execute a command or commands contained in a string. -- cgit v1.2.3-55-g6feb From e9aba3e7ea4936278188555332654796e5b5e873 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Sat, 1 Jul 2017 21:09:27 +0200 Subject: ash: fix 'trap - 65' Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/ash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'shell') diff --git a/shell/ash.c b/shell/ash.c index 9681111cc..22c726043 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -12967,7 +12967,7 @@ trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) exitcode = 0; while (*ap) { signo = get_signum(*ap); - if (signo < 0) { + if (signo < 0 || signo >= NSIG) { /* Mimic bash message exactly */ ash_msg("%s: invalid signal specification", *ap); exitcode = 1; -- cgit v1.2.3-55-g6feb From 48c803a2064d5ae24540760f13a21f092247bd82 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Sat, 1 Jul 2017 23:24:48 +0200 Subject: ash: fix $HOME/.profile reading if !ASH_EXPAND_PRMT, take 2 Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/ash.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'shell') diff --git a/shell/ash.c b/shell/ash.c index 22c726043..6d46e3719 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -2484,10 +2484,10 @@ setprompt_if(smallint do_set, int whichprompt) } #if ENABLE_ASH_EXPAND_PRMT pushstackmark(&smark, stackblocksize()); -#endif putprompt(expandstr(prompt)); -#if ENABLE_ASH_EXPAND_PRMT popstackmark(&smark); +#else + putprompt(prompt); #endif } @@ -11534,9 +11534,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) smallint dblquote; smallint oldstyle; IF_FEATURE_SH_MATH(smallint prevsyntax;) /* syntax before arithmetic */ -#if ENABLE_ASH_EXPAND_PRMT smallint pssyntax; /* we are expanding a prompt string */ -#endif int varnest; /* levels of variables expansion */ IF_FEATURE_SH_MATH(int arinest;) /* levels of arithmetic expansion */ IF_FEATURE_SH_MATH(int parenlevel;) /* levels of parens in arithmetic */ @@ -11548,11 +11546,9 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) bqlist = NULL; quotef = 0; IF_FEATURE_SH_MATH(prevsyntax = 0;) -#if ENABLE_ASH_EXPAND_PRMT pssyntax = (syntax == PSSYNTAX); if (pssyntax) syntax = DQSYNTAX; -#endif dblquote = (syntax == DQSYNTAX); varnest = 0; IF_FEATURE_SH_MATH(arinest = 0;) @@ -11606,12 +11602,10 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) } else if (c == '\n') { nlprompt(); } else { -#if ENABLE_ASH_EXPAND_PRMT if (c == '$' && pssyntax) { USTPUTC(CTLESC, out); USTPUTC('\\', out); } -#endif /* Backslash is retained if we are in "str" and next char isn't special */ if (dblquote && c != '\\' -- cgit v1.2.3-55-g6feb From 4ee824f6ba3f35228f1c48e21681aa532a7dc23f Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Mon, 3 Jul 2017 01:22:13 +0200 Subject: randomconfig fixes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- networking/udhcp/common.h | 2 ++ networking/udhcp/packet.c | 2 ++ shell/hush.c | 8 +++++--- 3 files changed, 9 insertions(+), 3 deletions(-) (limited to 'shell') diff --git a/networking/udhcp/common.h b/networking/udhcp/common.h index 6907e7f60..a9c23a186 100644 --- a/networking/udhcp/common.h +++ b/networking/udhcp/common.h @@ -295,7 +295,9 @@ int FAST_FUNC udhcp_str2optset(const char *str, const struct dhcp_optflag *optflags, const char *option_strings); +#if ENABLE_UDHCPC || ENABLE_UDHCPD void udhcp_init_header(struct dhcp_packet *packet, char type) FAST_FUNC; +#endif int udhcp_recv_kernel_packet(struct dhcp_packet *packet, int fd) FAST_FUNC; diff --git a/networking/udhcp/packet.c b/networking/udhcp/packet.c index 0a31f2643..9e1b46d2f 100644 --- a/networking/udhcp/packet.c +++ b/networking/udhcp/packet.c @@ -12,6 +12,7 @@ #include <netinet/if_ether.h> #include <netpacket/packet.h> +#if ENABLE_UDHCPC || ENABLE_UDHCPD void FAST_FUNC udhcp_init_header(struct dhcp_packet *packet, char type) { memset(packet, 0, sizeof(*packet)); @@ -29,6 +30,7 @@ void FAST_FUNC udhcp_init_header(struct dhcp_packet *packet, char type) packet->options[0] = DHCP_END; udhcp_add_simple_option(packet, DHCP_MESSAGE_TYPE, type); } +#endif #if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 2 void FAST_FUNC udhcp_dump_packet(struct dhcp_packet *packet) diff --git a/shell/hush.c b/shell/hush.c index 125463a56..f5c1e5bc1 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -331,9 +331,9 @@ /* Separate defines document which part of code implements what */ #define BASH_PATTERN_SUBST ENABLE_HUSH_BASH_COMPAT #define BASH_SUBSTR ENABLE_HUSH_BASH_COMPAT -#define BASH_TEST2 ENABLE_HUSH_BASH_COMPAT #define BASH_SOURCE ENABLE_HUSH_BASH_COMPAT #define BASH_HOSTNAME_VAR ENABLE_HUSH_BASH_COMPAT +#define BASH_TEST2 (ENABLE_HUSH_BASH_COMPAT && ENABLE_HUSH_TEST) /* Build knobs */ @@ -1457,7 +1457,7 @@ static void restore_redirected_FILEs(void) fl = fl->next; } } -#if ENABLE_FEATURE_SH_STANDALONE +#if ENABLE_FEATURE_SH_STANDALONE && BB_MMU static void close_all_FILE_list(void) { struct FILE_list *fl = G.FILE_list; @@ -8575,8 +8575,9 @@ int hush_main(int argc, char **argv) optarg++; empty_trap_mask = bb_strtoull(optarg, &optarg, 16); if (empty_trap_mask != 0) { - int sig; + IF_HUSH_TRAP(int sig;) install_special_sighandlers(); +# if ENABLE_HUSH_TRAP G_traps = xzalloc(sizeof(G_traps[0]) * NSIG); for (sig = 1; sig < NSIG; sig++) { if (empty_trap_mask & (1LL << sig)) { @@ -8584,6 +8585,7 @@ int hush_main(int argc, char **argv) install_sighandler(sig, SIG_IGN); } } +# endif } # if ENABLE_HUSH_LOOPS optarg++; -- cgit v1.2.3-55-g6feb From 2e989ef232e35750df573898077dd356003705b2 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Mon, 3 Jul 2017 16:56:37 +0200 Subject: msh: delete this applet It's deprecated since 2009 and interferes with make_single_applets.sh tests. Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- configs/TEST_nommu_defconfig | 1 - configs/TEST_noprintf_defconfig | 1 - configs/TEST_rh9_defconfig | 1 - configs/android2_defconfig | 1 - configs/android_502_defconfig | 1 - configs/android_defconfig | 1 - configs/android_ndk_defconfig | 1 - configs/cygwin_defconfig | 1 - configs/freebsd_defconfig | 1 - shell/ash_test/run-all | 6 +++--- shell/hush.c | 18 ------------------ .../msh/msh-supports-underscores-in-variable-names | 1 - 12 files changed, 3 insertions(+), 31 deletions(-) delete mode 100644 testsuite/msh/msh-supports-underscores-in-variable-names (limited to 'shell') diff --git a/configs/TEST_nommu_defconfig b/configs/TEST_nommu_defconfig index 7fbbbecc7..6ff68a092 100644 --- a/configs/TEST_nommu_defconfig +++ b/configs/TEST_nommu_defconfig @@ -891,7 +891,6 @@ CONFIG_HUSH_FUNCTIONS=y CONFIG_HUSH_LOCAL=y CONFIG_HUSH_EXPORT_N=y CONFIG_HUSH_RANDOM_SUPPORT=y -CONFIG_MSH=y CONFIG_SH_MATH_SUPPORT=y CONFIG_SH_MATH_SUPPORT_64=y CONFIG_FEATURE_SH_EXTRA_QUIET=y diff --git a/configs/TEST_noprintf_defconfig b/configs/TEST_noprintf_defconfig index 3f85ee1df..4b2ef402a 100644 --- a/configs/TEST_noprintf_defconfig +++ b/configs/TEST_noprintf_defconfig @@ -898,7 +898,6 @@ CONFIG_FEATURE_SH_IS_NONE=y # CONFIG_FEATURE_BASH_IS_ASH is not set # CONFIG_FEATURE_BASH_IS_HUSH is not set CONFIG_FEATURE_BASH_IS_NONE=y -# CONFIG_MSH is not set # CONFIG_SH_MATH_SUPPORT is not set # CONFIG_SH_MATH_SUPPORT_64 is not set # CONFIG_FEATURE_SH_EXTRA_QUIET is not set diff --git a/configs/TEST_rh9_defconfig b/configs/TEST_rh9_defconfig index 34d8e31e2..52f3e4670 100644 --- a/configs/TEST_rh9_defconfig +++ b/configs/TEST_rh9_defconfig @@ -905,7 +905,6 @@ CONFIG_HUSH_FUNCTIONS=y CONFIG_HUSH_LOCAL=y CONFIG_HUSH_EXPORT_N=y CONFIG_HUSH_RANDOM_SUPPORT=y -CONFIG_MSH=y CONFIG_SH_MATH_SUPPORT=y CONFIG_SH_MATH_SUPPORT_64=y CONFIG_FEATURE_SH_EXTRA_QUIET=y diff --git a/configs/android2_defconfig b/configs/android2_defconfig index 20866c32b..9202320a4 100644 --- a/configs/android2_defconfig +++ b/configs/android2_defconfig @@ -952,7 +952,6 @@ CONFIG_CTTYHACK=y # CONFIG_HUSH_RANDOM_SUPPORT is not set # CONFIG_HUSH_EXPORT_N is not set # CONFIG_HUSH_MODE_X is not set -# CONFIG_MSH is not set # CONFIG_FEATURE_SH_IS_ASH is not set # CONFIG_FEATURE_SH_IS_HUSH is not set CONFIG_FEATURE_SH_IS_NONE=y diff --git a/configs/android_502_defconfig b/configs/android_502_defconfig index bdca9eebb..1901bdbb0 100644 --- a/configs/android_502_defconfig +++ b/configs/android_502_defconfig @@ -1098,7 +1098,6 @@ CONFIG_CTTYHACK=y # CONFIG_HUSH_RANDOM_SUPPORT is not set # CONFIG_HUSH_EXPORT_N is not set # CONFIG_HUSH_MODE_X is not set -# CONFIG_MSH is not set CONFIG_FEATURE_SH_IS_ASH=y # CONFIG_FEATURE_SH_IS_HUSH is not set # CONFIG_FEATURE_SH_IS_NONE is not set diff --git a/configs/android_defconfig b/configs/android_defconfig index 6ef81750e..ea6e8a79e 100644 --- a/configs/android_defconfig +++ b/configs/android_defconfig @@ -984,7 +984,6 @@ CONFIG_CTTYHACK=y # CONFIG_HUSH_RANDOM_SUPPORT is not set # CONFIG_HUSH_EXPORT_N is not set # CONFIG_HUSH_MODE_X is not set -# CONFIG_MSH is not set # CONFIG_FEATURE_SH_IS_ASH is not set # CONFIG_FEATURE_SH_IS_HUSH is not set CONFIG_FEATURE_SH_IS_NONE=y diff --git a/configs/android_ndk_defconfig b/configs/android_ndk_defconfig index 35d03b42e..61871fcb1 100644 --- a/configs/android_ndk_defconfig +++ b/configs/android_ndk_defconfig @@ -1013,7 +1013,6 @@ CONFIG_CTTYHACK=y # CONFIG_HUSH_RANDOM_SUPPORT is not set # CONFIG_HUSH_EXPORT_N is not set # CONFIG_HUSH_MODE_X is not set -# CONFIG_MSH is not set # CONFIG_FEATURE_SH_IS_ASH is not set # CONFIG_FEATURE_SH_IS_HUSH is not set CONFIG_FEATURE_SH_IS_NONE=y diff --git a/configs/cygwin_defconfig b/configs/cygwin_defconfig index 6bfc973ef..54aa44470 100644 --- a/configs/cygwin_defconfig +++ b/configs/cygwin_defconfig @@ -955,7 +955,6 @@ CONFIG_HUSH_LOCAL=y CONFIG_HUSH_RANDOM_SUPPORT=y CONFIG_HUSH_EXPORT_N=y CONFIG_HUSH_MODE_X=y -# CONFIG_MSH is not set CONFIG_FEATURE_SH_IS_ASH=y # CONFIG_FEATURE_SH_IS_HUSH is not set # CONFIG_FEATURE_SH_IS_NONE is not set diff --git a/configs/freebsd_defconfig b/configs/freebsd_defconfig index e3d04aedc..fadbca13b 100644 --- a/configs/freebsd_defconfig +++ b/configs/freebsd_defconfig @@ -931,7 +931,6 @@ CONFIG_ASH=y # CONFIG_HUSH_RANDOM_SUPPORT is not set # CONFIG_HUSH_EXPORT_N is not set # CONFIG_HUSH_MODE_X is not set -# CONFIG_MSH is not set CONFIG_FEATURE_SH_IS_ASH=y # CONFIG_FEATURE_SH_IS_HUSH is not set # CONFIG_FEATURE_SH_IS_NONE is not set diff --git a/shell/ash_test/run-all b/shell/ash_test/run-all index 354cc1fcf..8dfdddd9f 100755 --- a/shell/ash_test/run-all +++ b/shell/ash_test/run-all @@ -59,9 +59,9 @@ do_test() if [ $# -lt 1 ]; then # All sub directories modules=`ls -d ash-*` - # If you want to test ash against hush and msh testsuites - # (have to copy hush_test and msh_test dirs to current dir first): - #modules=`ls -d ash-* hush_test/hush-* msh_test/msh-*` + # If you want to test ash against hush testsuite + # (have to copy hush_test dir to current dir first): + #modules=`ls -d ash-* hush_test/hush-*` for module in $modules; do do_test $module diff --git a/shell/hush.c b/shell/hush.c index f5c1e5bc1..fc6db316e 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -268,17 +268,9 @@ //config: bool "memleak builtin (debugging)" //config: default n //config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH -//config: -//config:config MSH -//config: bool "msh (deprecated: aliased to hush)" -//config: default n -//config: select HUSH -//config: help -//config: msh is deprecated and will be removed, please migrate to hush. //applet:IF_HUSH(APPLET(hush, BB_DIR_BIN, BB_SUID_DROP)) // APPLET_ODDNAME:name main location suid_type help -//applet:IF_MSH( APPLET_ODDNAME(msh, hush, BB_DIR_BIN, BB_SUID_DROP, hush)) //applet:IF_SH_IS_HUSH( APPLET_ODDNAME(sh, hush, BB_DIR_BIN, BB_SUID_DROP, hush)) //applet:IF_BASH_IS_HUSH(APPLET_ODDNAME(bash, hush, BB_DIR_BIN, BB_SUID_DROP, hush)) @@ -8808,16 +8800,6 @@ int hush_main(int argc, char **argv) } -#if ENABLE_MSH -int msh_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; -int msh_main(int argc, char **argv) -{ - bb_error_msg("msh is deprecated, please use hush instead"); - return hush_main(argc, argv); -} -#endif - - /* * Built-ins */ diff --git a/testsuite/msh/msh-supports-underscores-in-variable-names b/testsuite/msh/msh-supports-underscores-in-variable-names deleted file mode 100644 index 9c7834b37..000000000 --- a/testsuite/msh/msh-supports-underscores-in-variable-names +++ /dev/null @@ -1 +0,0 @@ -test "`busybox msh -c 'FOO_BAR=foo; echo $FOO_BAR'`" = foo -- cgit v1.2.3-55-g6feb From d4e4fdb5ce5ccc067b3d35d877f7a7d978869517 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Mon, 3 Jul 2017 21:31:16 +0200 Subject: fixes for bugs found by make_single_applets.sh Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- archival/bbunzip.c | 11 ++++++++--- include/libbb.h | 2 ++ libbb/appletlib.c | 20 ++++++++++++++++---- libbb/getopt32.c | 4 +--- libbb/vfork_daemon_rexec.c | 3 ++- networking/inetd.c | 17 ++++++++++------- scripts/randomtest.loop | 26 ++++++++++++++++++++++++++ shell/hush.c | 26 ++++++-------------------- shell/shell_common.c | 4 +--- util-linux/fdisk.c | 3 +++ 10 files changed, 75 insertions(+), 41 deletions(-) (limited to 'shell') diff --git a/archival/bbunzip.c b/archival/bbunzip.c index c60f6e6df..f7a7ab354 100644 --- a/archival/bbunzip.c +++ b/archival/bbunzip.c @@ -7,14 +7,19 @@ #include "libbb.h" #include "bb_archive.h" +//kbuild:lib-$(CONFIG_ZCAT) += bbunzip.o +//kbuild:lib-$(CONFIG_GUNZIP) += bbunzip.o +//kbuild:lib-$(CONFIG_BZCAT) += bbunzip.o +//kbuild:lib-$(CONFIG_BUNZIP2) += bbunzip.o + /* lzop_main() uses bbunpack(), need this: */ //kbuild:lib-$(CONFIG_LZOP) += bbunzip.o //kbuild:lib-$(CONFIG_LZOPCAT) += bbunzip.o //kbuild:lib-$(CONFIG_UNLZOP) += bbunzip.o /* bzip2_main() too: */ -//kbuild:lib-$(CONFIG_FEATURE_BZIP2_DECOMPRESS) += bbunzip.o +//kbuild:lib-$(CONFIG_BZIP2) += bbunzip.o /* gzip_main() too: */ -//kbuild:lib-$(CONFIG_FEATURE_GZIP_DECOMPRESS) += bbunzip.o +//kbuild:lib-$(CONFIG_GZIP) += bbunzip.o /* Note: must be kept in sync with archival/lzop.c */ enum { @@ -443,7 +448,7 @@ int gunzip_main(int argc UNUSED_PARAM, char **argv) //applet:IF_BUNZIP2(APPLET(bunzip2, BB_DIR_USR_BIN, BB_SUID_DROP)) // APPLET_ODDNAME:name main location suid_type help //applet:IF_BZCAT(APPLET_ODDNAME(bzcat, bunzip2, BB_DIR_USR_BIN, BB_SUID_DROP, bzcat)) -#if ENABLE_FEATURE_BZIP2_DECOMPRESS +#if ENABLE_FEATURE_BZIP2_DECOMPRESS || ENABLE_BUNZIP2 int bunzip2_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int bunzip2_main(int argc UNUSED_PARAM, char **argv) { diff --git a/include/libbb.h b/include/libbb.h index 9b72c97be..557978e66 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -778,6 +778,8 @@ ssize_t recv_from_to(int fd, void *buf, size_t len, int flags, uint16_t inet_cksum(uint16_t *addr, int len) FAST_FUNC; +/* 0 if argv[0] is NULL: */ +unsigned string_array_len(char **argv) FAST_FUNC; void overlapping_strcpy(char *dst, const char *src) FAST_FUNC; char *safe_strncpy(char *dst, const char *src, size_t size) FAST_FUNC; char *strncpy_IFNAMSIZ(char *dst, const char *src) FAST_FUNC; diff --git a/libbb/appletlib.c b/libbb/appletlib.c index 7f0d62060..2dea2b43a 100644 --- a/libbb/appletlib.c +++ b/libbb/appletlib.c @@ -78,6 +78,17 @@ #endif +unsigned FAST_FUNC string_array_len(char **argv) +{ + char **start = argv; + + while (*argv) + argv++; + + return argv - start; +} + + #if ENABLE_SHOW_USAGE && !ENABLE_FEATURE_COMPRESS_USAGE static const char usage_messages[] ALIGN1 = UNPACKED_USAGE; #else @@ -868,10 +879,7 @@ static int busybox_main(char **argv) # if NUM_APPLETS > 0 void FAST_FUNC run_applet_no_and_exit(int applet_no, char **argv) { - int argc = 1; - - while (argv[argc]) - argc++; + int argc = string_array_len(argv); /* Reinit some shared global data */ xfunc_error_retval = EXIT_FAILURE; @@ -993,7 +1001,11 @@ int main(int argc UNUSED_PARAM, char **argv) } /* applet_names in this case is just "applet\0\0" */ lbb_prepare(applet_names IF_FEATURE_INDIVIDUAL(, argv)); +# if ENABLE_BUILD_LIBBUSYBOX + return SINGLE_APPLET_MAIN(string_array_len(argv), argv); +# else return SINGLE_APPLET_MAIN(argc, argv); +# endif #elif !ENABLE_BUSYBOX && NUM_APPLETS == 0 diff --git a/libbb/getopt32.c b/libbb/getopt32.c index b87b83538..80f4cc060 100644 --- a/libbb/getopt32.c +++ b/libbb/getopt32.c @@ -379,9 +379,7 @@ getopt32(char **argv, const char *applet_opts, ...) int spec_flgs = 0; /* skip 0: some applets cheat: they do not actually HAVE argv[0] */ - argc = 1; - while (argv[argc]) - argc++; + argc = 1 + string_array_len(argv + 1); va_start(p, applet_opts); diff --git a/libbb/vfork_daemon_rexec.c b/libbb/vfork_daemon_rexec.c index fd481bf6e..2695f99ee 100644 --- a/libbb/vfork_daemon_rexec.c +++ b/libbb/vfork_daemon_rexec.c @@ -16,6 +16,7 @@ */ #include "busybox.h" /* uses applet tables */ +#include "NUM_APPLETS.h" /* This does a fork/exec in one call, using vfork(). Returns PID of new child, * -1 for failure. Runs argv[0], searching path if that has no / in it. */ @@ -156,7 +157,7 @@ int FAST_FUNC run_nofork_applet(int applet_no, char **argv) int FAST_FUNC spawn_and_wait(char **argv) { int rc; -#if ENABLE_FEATURE_PREFER_APPLETS +#if ENABLE_FEATURE_PREFER_APPLETS && (NUM_APPLETS > 1) int a = find_applet_by_name(argv[0]); if (a >= 0) { diff --git a/networking/inetd.c b/networking/inetd.c index 01e659f13..39169a935 100644 --- a/networking/inetd.c +++ b/networking/inetd.c @@ -1513,8 +1513,11 @@ int inetd_main(int argc UNUSED_PARAM, char **argv) } /* for (;;) */ } -#if !BB_MMU +#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO \ + || ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD +# if !BB_MMU static const char *const cat_args[] = { "cat", NULL }; +# endif #endif /* @@ -1525,14 +1528,14 @@ static const char *const cat_args[] = { "cat", NULL }; /* ARGSUSED */ static void FAST_FUNC echo_stream(int s, servtab_t *sep UNUSED_PARAM) { -#if BB_MMU +# if BB_MMU while (1) { ssize_t sz = safe_read(s, line, LINE_SIZE); if (sz <= 0) break; xwrite(s, line, sz); } -#else +# else /* We are after vfork here! */ /* move network socket to stdin/stdout */ xmove_fd(s, STDIN_FILENO); @@ -1542,7 +1545,7 @@ static void FAST_FUNC echo_stream(int s, servtab_t *sep UNUSED_PARAM) xopen(bb_dev_null, O_WRONLY); BB_EXECVP("cat", (char**)cat_args); /* on failure we return to main, which does exit(EXIT_FAILURE) */ -#endif +# endif } static void FAST_FUNC echo_dg(int s, servtab_t *sep) { @@ -1566,10 +1569,10 @@ static void FAST_FUNC echo_dg(int s, servtab_t *sep) /* ARGSUSED */ static void FAST_FUNC discard_stream(int s, servtab_t *sep UNUSED_PARAM) { -#if BB_MMU +# if BB_MMU while (safe_read(s, line, LINE_SIZE) > 0) continue; -#else +# else /* We are after vfork here! */ /* move network socket to stdin */ xmove_fd(s, STDIN_FILENO); @@ -1580,7 +1583,7 @@ static void FAST_FUNC discard_stream(int s, servtab_t *sep UNUSED_PARAM) xdup2(STDOUT_FILENO, STDERR_FILENO); BB_EXECVP("cat", (char**)cat_args); /* on failure we return to main, which does exit(EXIT_FAILURE) */ -#endif +# endif } /* ARGSUSED */ static void FAST_FUNC discard_dg(int s, servtab_t *sep UNUSED_PARAM) diff --git a/scripts/randomtest.loop b/scripts/randomtest.loop index 710f5fd05..4d14b652f 100755 --- a/scripts/randomtest.loop +++ b/scripts/randomtest.loop @@ -1,7 +1,11 @@ #!/bin/sh +run_testsuite=false run_testsuite=true +run_single_test=false +run_single_test=true + test -d "$1" || { echo "'$1' is not a directory"; exit 1; } test -x "$1/scripts/randomtest" || { echo "No scripts/randomtest in '$1'"; exit 1; } @@ -40,6 +44,28 @@ while sleep 1; do fi tail -n10 -- "$dir/testsuite/runtest.log" fi + if $run_single_test; then + ( + cd -- "$dir" || exit 1 + echo "Running make_single_applets.sh in $dir..." + + if grep -q '# CONFIG_FEATURE_TFTP_GET is not set' .config \ + && grep -q '# CONFIG_FEATURE_TFTP_PUT is not set' .config \ + ; then + # If both off, tftp[d] is ifdefed out and test fails. + # Enable one: + sed 's/# CONFIG_FEATURE_TFTP_GET is not set/CONFIG_FEATURE_TFTP_GET=y/' -i .config + fi + + ./make_single_applets.sh + ) + if test $? != 0; then + echo "Failed make_single_applets.sh in $dir" + exit 1 # you may comment this out... + let fail++ + continue + fi + fi rm -rf -- "$dir" let cnt++ done diff --git a/shell/hush.c b/shell/hush.c index fc6db316e..30add72f0 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -1478,8 +1478,6 @@ typedef struct save_arg_t { static void save_and_replace_G_args(save_arg_t *sv, char **argv) { - int n; - sv->sv_argv0 = argv[0]; sv->sv_g_argv = G.global_argv; sv->sv_g_argc = G.global_argc; @@ -1489,10 +1487,7 @@ static void save_and_replace_G_args(save_arg_t *sv, char **argv) G.global_argv = argv; IF_HUSH_SET(G.global_args_malloced = 0;) - n = 1; - while (*++argv) - n++; - G.global_argc = n; + G.global_argc = 1 + string_array_len(argv + 1); } static void restore_G_args(save_arg_t *sv, char **argv) @@ -6809,13 +6804,11 @@ static void exec_function(char ***to_free, char **argv) { # if BB_MMU - int n = 1; + int n; argv[0] = G.global_argv[0]; G.global_argv = argv; - while (*++argv) - n++; - G.global_argc = n; + G.global_argc = n = 1 + string_array_len(argv + 1); /* On MMU, funcp->body is always non-NULL */ n = run_list(funcp->body); fflush_all(); @@ -8811,12 +8804,8 @@ static int FAST_FUNC builtin_true(char **argv UNUSED_PARAM) #if ENABLE_HUSH_TEST || ENABLE_HUSH_ECHO || ENABLE_HUSH_PRINTF || ENABLE_HUSH_KILL static int run_applet_main(char **argv, int (*applet_main_func)(int argc, char **argv)) { - int argc = 0; - while (*argv) { - argc++; - argv++; - } - return applet_main_func(argc, argv - argc); + int argc = string_array_len(argv); + return applet_main_func(argc, argv); } #endif #if ENABLE_HUSH_TEST || BASH_TEST2 @@ -9363,10 +9352,7 @@ static int FAST_FUNC builtin_set(char **argv) /* This realloc's G.global_argv */ G.global_argv = pp = add_strings_to_strings(g_argv, argv, /*dup:*/ 1); - n = 1; - while (*++pp) - n++; - G.global_argc = n; + G.global_argc = 1 + string_array_len(pp + 1); return EXIT_SUCCESS; diff --git a/shell/shell_common.c b/shell/shell_common.c index 03b7d0b75..bf56f3d78 100644 --- a/shell/shell_common.c +++ b/shell/shell_common.c @@ -405,9 +405,7 @@ shell_builtin_ulimit(char **argv) */ GETOPT_RESET(); - argc = 1; - while (argv[argc]) - argc++; + argc = string_array_len(argv); opts = 0; while (1) { diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c index 916d4e30e..4467525c7 100644 --- a/util-linux/fdisk.c +++ b/util-linux/fdisk.c @@ -185,6 +185,8 @@ struct hd_geometry { #define HDIO_GETGEO 0x0301 /* get device geometry */ +/* TODO: #if ENABLE_FEATURE_FDISK_WRITABLE */ +/* (currently fdisk_sun/sgi.c do not have proper WRITABLE #ifs) */ static const char msg_building_new_label[] ALIGN1 = "Building a new %s. Changes will remain in memory only,\n" "until you decide to write them. After that the previous content\n" @@ -192,6 +194,7 @@ static const char msg_building_new_label[] ALIGN1 = static const char msg_part_already_defined[] ALIGN1 = "Partition %u is already defined, delete it before re-adding\n"; +/* #endif */ struct partition { -- cgit v1.2.3-55-g6feb From fda9fafe279d9394ad53313320a949c86f646734 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Wed, 5 Jul 2017 19:10:21 +0200 Subject: ash: fix matching of unicode greek letter rho (cf 81) and similar cases Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/ash.c | 23 +++++++++++++++++- shell/ash_test/ash-quoting/unicode_8x_chars.right | 6 +++++ shell/ash_test/ash-quoting/unicode_8x_chars.tests | 28 ++++++++++++++++++++++ .../hush_test/hush-quoting/unicode_8x_chars.right | 6 +++++ .../hush_test/hush-quoting/unicode_8x_chars.tests | 28 ++++++++++++++++++++++ 5 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 shell/ash_test/ash-quoting/unicode_8x_chars.right create mode 100755 shell/ash_test/ash-quoting/unicode_8x_chars.tests create mode 100644 shell/hush_test/hush-quoting/unicode_8x_chars.right create mode 100755 shell/hush_test/hush-quoting/unicode_8x_chars.tests (limited to 'shell') diff --git a/shell/ash.c b/shell/ash.c index 6d46e3719..e5fdd1646 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -5913,6 +5913,7 @@ rmescapes(char *str, int flag) while (*p) { if ((unsigned char)*p == CTLQUOTEMARK) { // Note: both inquotes and protect_against_glob only affect whether +// CTLESC,<ch> gets converted to <ch> or to \<ch> inquotes = ~inquotes; p++; protect_against_glob = globbing; @@ -5925,7 +5926,27 @@ rmescapes(char *str, int flag) ash_msg_and_raise_error("CTLESC at EOL (shouldn't happen)"); #endif if (protect_against_glob) { - *q++ = '\\'; + /* + * We used to trust glob() and fnmatch() to eat + * superfluous escapes (\z where z has no + * special meaning anyway). But this causes + * bugs such as string of one greek letter rho + * (unicode-encoded as two bytes 'cf,81") + * getting encoded as "cf,CTLESC,81" + * and here, converted to "cf,\,81" - + * which does not go well with some flavors + * of fnmatch() in unicode locales. + * + * Lets add "\" only on the chars which need it. + */ + if (*p == '*' + || *p == '?' + || *p == '[' + /* || *p == ']' maybe also this? */ + || *p == '\\' + ) { + *q++ = '\\'; + } } } else if (*p == '\\' && !inquotes) { /* naked back slash */ diff --git a/shell/ash_test/ash-quoting/unicode_8x_chars.right b/shell/ash_test/ash-quoting/unicode_8x_chars.right new file mode 100644 index 000000000..7780b88b4 --- /dev/null +++ b/shell/ash_test/ash-quoting/unicode_8x_chars.right @@ -0,0 +1,6 @@ +ok +ok +ok +ok +ok +ok diff --git a/shell/ash_test/ash-quoting/unicode_8x_chars.tests b/shell/ash_test/ash-quoting/unicode_8x_chars.tests new file mode 100755 index 000000000..1258745ec --- /dev/null +++ b/shell/ash_test/ash-quoting/unicode_8x_chars.tests @@ -0,0 +1,28 @@ +# Unicode: cf 80 +case π in +( "π" ) echo ok ;; +( * ) echo WRONG ;; +esac +# Unicode: cf 81 +case ρ in +( "ρ" ) echo ok ;; +( * ) echo WRONG ;; +esac +# Unicode: cf 82 +case ς in +( "ς" ) echo ok ;; +( * ) echo WRONG ;; +esac + +case "π" in +( π ) echo ok ;; +( * ) echo WRONG ;; +esac +case "ρ" in +( ρ ) echo ok ;; +( * ) echo WRONG ;; +esac +case "ς" in +( ς ) echo ok ;; +( * ) echo WRONG ;; +esac diff --git a/shell/hush_test/hush-quoting/unicode_8x_chars.right b/shell/hush_test/hush-quoting/unicode_8x_chars.right new file mode 100644 index 000000000..7780b88b4 --- /dev/null +++ b/shell/hush_test/hush-quoting/unicode_8x_chars.right @@ -0,0 +1,6 @@ +ok +ok +ok +ok +ok +ok diff --git a/shell/hush_test/hush-quoting/unicode_8x_chars.tests b/shell/hush_test/hush-quoting/unicode_8x_chars.tests new file mode 100755 index 000000000..1258745ec --- /dev/null +++ b/shell/hush_test/hush-quoting/unicode_8x_chars.tests @@ -0,0 +1,28 @@ +# Unicode: cf 80 +case π in +( "π" ) echo ok ;; +( * ) echo WRONG ;; +esac +# Unicode: cf 81 +case ρ in +( "ρ" ) echo ok ;; +( * ) echo WRONG ;; +esac +# Unicode: cf 82 +case ς in +( "ς" ) echo ok ;; +( * ) echo WRONG ;; +esac + +case "π" in +( π ) echo ok ;; +( * ) echo WRONG ;; +esac +case "ρ" in +( ρ ) echo ok ;; +( * ) echo WRONG ;; +esac +case "ς" in +( ς ) echo ok ;; +( * ) echo WRONG ;; +esac -- cgit v1.2.3-55-g6feb From 92b8d9c9faf599cd323c04d4f6853f2de840279c Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Wed, 5 Jul 2017 19:13:44 +0200 Subject: ash: note which versions of glibc exhibit "rho bug" Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/ash.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'shell') diff --git a/shell/ash.c b/shell/ash.c index e5fdd1646..5da23c34b 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -5935,7 +5935,8 @@ rmescapes(char *str, int flag) * getting encoded as "cf,CTLESC,81" * and here, converted to "cf,\,81" - * which does not go well with some flavors - * of fnmatch() in unicode locales. + * of fnmatch() in unicode locales + * (for example, glibc <= 2.22). * * Lets add "\" only on the chars which need it. */ -- cgit v1.2.3-55-g6feb From ed79a636238ec15c562862787dd71cd9de168b7d Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Wed, 5 Jul 2017 19:20:43 +0200 Subject: ash: tweak in comment Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/ash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'shell') diff --git a/shell/ash.c b/shell/ash.c index 5da23c34b..9b1f57949 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -5931,7 +5931,7 @@ rmescapes(char *str, int flag) * superfluous escapes (\z where z has no * special meaning anyway). But this causes * bugs such as string of one greek letter rho - * (unicode-encoded as two bytes 'cf,81") + * (unicode-encoded as two bytes "cf,81") * getting encoded as "cf,CTLESC,81" * and here, converted to "cf,\,81" - * which does not go well with some flavors -- cgit v1.2.3-55-g6feb From 4142f0187dcf8454e8d2a8d16b321dbd573c170e Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Wed, 5 Jul 2017 22:19:28 +0200 Subject: ash: fix escaping of a few characters (broken by last commits) Add a testcase which tests all ASCII punctuation escapes. NB: hush is failing this test! Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/ash.c | 9 ++++-- shell/ash_test/ash-quoting/quoted_punct.right | 35 +++++++++++++++++++++++ shell/ash_test/ash-quoting/quoted_punct.tests | 41 +++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 shell/ash_test/ash-quoting/quoted_punct.right create mode 100755 shell/ash_test/ash-quoting/quoted_punct.tests (limited to 'shell') diff --git a/shell/ash.c b/shell/ash.c index 9b1f57949..946e8726e 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -5939,12 +5939,17 @@ rmescapes(char *str, int flag) * (for example, glibc <= 2.22). * * Lets add "\" only on the chars which need it. + * Testcases for less obvious chars are shown. */ if (*p == '*' || *p == '?' || *p == '[' - /* || *p == ']' maybe also this? */ - || *p == '\\' + || *p == '\\' /* case '\' in \\ ) echo ok;; *) echo WRONG;; esac */ + || *p == ']' /* case ']' in [a\]] ) echo ok;; *) echo WRONG;; esac */ + || *p == '-' /* case '-' in [a\-c]) echo ok;; *) echo WRONG;; esac */ + || *p == '!' /* case '!' in [\!] ) echo ok;; *) echo WRONG;; esac */ + /* Some libc support [^negate], that's why "^" also needs love */ + || *p == '^' /* case '^' in [\^] ) echo ok;; *) echo WRONG;; esac */ ) { *q++ = '\\'; } diff --git a/shell/ash_test/ash-quoting/quoted_punct.right b/shell/ash_test/ash-quoting/quoted_punct.right new file mode 100644 index 000000000..ab66c3ce0 --- /dev/null +++ b/shell/ash_test/ash-quoting/quoted_punct.right @@ -0,0 +1,35 @@ +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok diff --git a/shell/ash_test/ash-quoting/quoted_punct.tests b/shell/ash_test/ash-quoting/quoted_punct.tests new file mode 100755 index 000000000..83ee40bf4 --- /dev/null +++ b/shell/ash_test/ash-quoting/quoted_punct.tests @@ -0,0 +1,41 @@ +# Testing glob-escaping of every ASCII punctuation char +# Some chars have more than one test +# 21..2f +case '!' in [\!] ) echo ok;; *) echo 'WRONG!';; esac +case '"' in [\"] ) echo ok;; *) echo 'WRONG"';; esac +case '#' in [\#] ) echo ok;; *) echo 'WRONG#';; esac +case '$' in [\$] ) echo ok;; *) echo 'WRONG$';; esac +case '%' in [\%] ) echo ok;; *) echo 'WRONG%';; esac +case '&' in [\&] ) echo ok;; *) echo 'WRONG&';; esac +case "'" in [\'] ) echo ok;; *) echo "WRONG'";; esac +case '(' in [\(] ) echo ok;; *) echo 'WRONG(';; esac +case ')' in [\)] ) echo ok;; *) echo 'WRONG)';; esac +case '*' in [\*] ) echo ok;; *) echo 'WRONG*';; esac +case '+' in [\+] ) echo ok;; *) echo 'WRONG+';; esac +case ',' in [\,] ) echo ok;; *) echo 'WRONG,';; esac +case '-' in [\-] ) echo ok;; *) echo 'WRONG-';; esac +case '-' in [a\-c]) echo ok;; *) echo 'WRONGa\-c';; esac +case '.' in [\.] ) echo ok;; *) echo 'WRONG.';; esac +case '/' in [\/] ) echo ok;; *) echo 'WRONG/';; esac +# 3a..40 +case ':' in [\:] ) echo ok;; *) echo 'WRONG:';; esac +case ';' in [\;] ) echo ok;; *) echo 'WRONG;';; esac +case '<' in [\<] ) echo ok;; *) echo 'WRONG<';; esac +case '=' in [\=] ) echo ok;; *) echo 'WRONG=';; esac +case '>' in [\>] ) echo ok;; *) echo 'WRONG>';; esac +case '?' in [\?] ) echo ok;; *) echo 'WRONG?';; esac +case '@' in [\@] ) echo ok;; *) echo 'WRONG@';; esac +# 5b..60 +case '[' in [\[] ) echo ok;; *) echo 'WRONG[';; esac +case '\' in [\\] ) echo ok;; *) echo 'WRONG\';; esac +case '\' in \\ ) echo ok;; *) echo 'WRONG\\';; esac +case ']' in [\]] ) echo ok;; *) echo 'WRONG]';; esac +case ']' in [a\]]) echo ok;; *) echo 'WRONGa]';; esac +case '^' in [\^] ) echo ok;; *) echo 'WRONG^';; esac +case '_' in [\_] ) echo ok;; *) echo 'WRONG_';; esac +case '`' in [\`] ) echo ok;; *) echo 'WRONG`';; esac +# 7b..7e +case '{' in [\{] ) echo ok;; *) echo 'WRONG{';; esac +case '|' in [\|] ) echo ok;; *) echo 'WRONG|';; esac +case '}' in [\}] ) echo ok;; *) echo 'WRONG}';; esac +case '~' in [\~] ) echo ok;; *) echo 'WRONG~';; esac -- cgit v1.2.3-55-g6feb From bd43c6784fb53826806c7cb51a1ed54e95eb4be9 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Wed, 5 Jul 2017 23:12:15 +0200 Subject: hush: fix quoted_punct.tests failure Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/ash.c | 4 ++- shell/hush.c | 6 ++-- shell/hush_test/hush-quoting/quoted_punct.right | 35 +++++++++++++++++++++ shell/hush_test/hush-quoting/quoted_punct.tests | 41 +++++++++++++++++++++++++ 4 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 shell/hush_test/hush-quoting/quoted_punct.right create mode 100755 shell/hush_test/hush-quoting/quoted_punct.tests (limited to 'shell') diff --git a/shell/ash.c b/shell/ash.c index 946e8726e..b7635a823 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -7613,7 +7613,9 @@ expandhere(union node *arg, int fd) static int patmatch(char *pattern, const char *string) { - return pmatch(preglob(pattern, 0), string); + char *p = preglob(pattern, 0); + //bb_error_msg("fnmatch(pattern:'%s',str:'%s')", p, string); + return pmatch(p, string); } /* diff --git a/shell/hush.c b/shell/hush.c index 30add72f0..7574e3918 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -5969,7 +5969,7 @@ static char *expand_string_to_string(const char *str, int do_unbackslash) return (char*)list; } -/* Used for "eval" builtin */ +/* Used for "eval" builtin and case string */ static char* expand_strvec_to_string(char **argv) { char **list; @@ -8053,6 +8053,7 @@ static int run_list(struct pipe *pi) if (rword == RES_CASE) { debug_printf_exec("CASE cond_code:%d\n", cond_code); case_word = expand_strvec_to_string(pi->cmds->argv); + unbackslash(case_word); continue; } if (rword == RES_MATCH) { @@ -8064,9 +8065,10 @@ static int run_list(struct pipe *pi) /* all prev words didn't match, does this one match? */ argv = pi->cmds->argv; while (*argv) { - char *pattern = expand_string_to_string(*argv, /*unbackslash:*/ 1); + char *pattern = expand_string_to_string(*argv, /*unbackslash:*/ 0); /* TODO: which FNM_xxx flags to use? */ cond_code = (fnmatch(pattern, case_word, /*flags:*/ 0) != 0); + debug_printf_exec("fnmatch(pattern:'%s',str:'%s'):%d\n", pattern, case_word, cond_code); free(pattern); if (cond_code == 0) { /* match! we will execute this branch */ free(case_word); diff --git a/shell/hush_test/hush-quoting/quoted_punct.right b/shell/hush_test/hush-quoting/quoted_punct.right new file mode 100644 index 000000000..ab66c3ce0 --- /dev/null +++ b/shell/hush_test/hush-quoting/quoted_punct.right @@ -0,0 +1,35 @@ +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok diff --git a/shell/hush_test/hush-quoting/quoted_punct.tests b/shell/hush_test/hush-quoting/quoted_punct.tests new file mode 100755 index 000000000..83ee40bf4 --- /dev/null +++ b/shell/hush_test/hush-quoting/quoted_punct.tests @@ -0,0 +1,41 @@ +# Testing glob-escaping of every ASCII punctuation char +# Some chars have more than one test +# 21..2f +case '!' in [\!] ) echo ok;; *) echo 'WRONG!';; esac +case '"' in [\"] ) echo ok;; *) echo 'WRONG"';; esac +case '#' in [\#] ) echo ok;; *) echo 'WRONG#';; esac +case '$' in [\$] ) echo ok;; *) echo 'WRONG$';; esac +case '%' in [\%] ) echo ok;; *) echo 'WRONG%';; esac +case '&' in [\&] ) echo ok;; *) echo 'WRONG&';; esac +case "'" in [\'] ) echo ok;; *) echo "WRONG'";; esac +case '(' in [\(] ) echo ok;; *) echo 'WRONG(';; esac +case ')' in [\)] ) echo ok;; *) echo 'WRONG)';; esac +case '*' in [\*] ) echo ok;; *) echo 'WRONG*';; esac +case '+' in [\+] ) echo ok;; *) echo 'WRONG+';; esac +case ',' in [\,] ) echo ok;; *) echo 'WRONG,';; esac +case '-' in [\-] ) echo ok;; *) echo 'WRONG-';; esac +case '-' in [a\-c]) echo ok;; *) echo 'WRONGa\-c';; esac +case '.' in [\.] ) echo ok;; *) echo 'WRONG.';; esac +case '/' in [\/] ) echo ok;; *) echo 'WRONG/';; esac +# 3a..40 +case ':' in [\:] ) echo ok;; *) echo 'WRONG:';; esac +case ';' in [\;] ) echo ok;; *) echo 'WRONG;';; esac +case '<' in [\<] ) echo ok;; *) echo 'WRONG<';; esac +case '=' in [\=] ) echo ok;; *) echo 'WRONG=';; esac +case '>' in [\>] ) echo ok;; *) echo 'WRONG>';; esac +case '?' in [\?] ) echo ok;; *) echo 'WRONG?';; esac +case '@' in [\@] ) echo ok;; *) echo 'WRONG@';; esac +# 5b..60 +case '[' in [\[] ) echo ok;; *) echo 'WRONG[';; esac +case '\' in [\\] ) echo ok;; *) echo 'WRONG\';; esac +case '\' in \\ ) echo ok;; *) echo 'WRONG\\';; esac +case ']' in [\]] ) echo ok;; *) echo 'WRONG]';; esac +case ']' in [a\]]) echo ok;; *) echo 'WRONGa]';; esac +case '^' in [\^] ) echo ok;; *) echo 'WRONG^';; esac +case '_' in [\_] ) echo ok;; *) echo 'WRONG_';; esac +case '`' in [\`] ) echo ok;; *) echo 'WRONG`';; esac +# 7b..7e +case '{' in [\{] ) echo ok;; *) echo 'WRONG{';; esac +case '|' in [\|] ) echo ok;; *) echo 'WRONG|';; esac +case '}' in [\}] ) echo ok;; *) echo 'WRONG}';; esac +case '~' in [\~] ) echo ok;; *) echo 'WRONG~';; esac -- cgit v1.2.3-55-g6feb From 637982f5bbe7a5be4e5409ab0404df2583e7c299 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Thu, 6 Jul 2017 01:52:23 +0200 Subject: hush: correctly handle quoting in "case" even if !BASH_PATTERN_SUBST Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/hush.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'shell') diff --git a/shell/hush.c b/shell/hush.c index 7574e3918..aee77d03b 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -5189,7 +5189,7 @@ static struct pipe *parse_stream(char **pstring, /*** Execution routines ***/ /* Expansion can recurse, need forward decls: */ -#if !BASH_PATTERN_SUBST +#if !BASH_PATTERN_SUBST && !ENABLE_HUSH_CASE /* only ${var/pattern/repl} (its pattern part) needs additional mode */ #define expand_string_to_string(str, do_unbackslash) \ expand_string_to_string(str) @@ -5317,6 +5317,9 @@ static int expand_on_ifs(int *ended_with_ifs, o_string *output, int n, const cha #endif static char *encode_then_expand_string(const char *str, int process_bkslash, int do_unbackslash) { +#if !BASH_PATTERN_SUBST + const int do_unbackslash = 1; +#endif char *exp_str; struct in_str input; o_string dest = NULL_O_STRING; @@ -5936,7 +5939,7 @@ static char **expand_strvec_to_strvec_singleword_noglob(char **argv) */ static char *expand_string_to_string(const char *str, int do_unbackslash) { -#if !BASH_PATTERN_SUBST +#if !BASH_PATTERN_SUBST && !ENABLE_HUSH_CASE const int do_unbackslash = 1; #endif char *argv[2], **list; -- cgit v1.2.3-55-g6feb From 9a8ece51582b83a2d4ed3e1854dca703d5113da2 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Thu, 6 Jul 2017 17:59:25 +0200 Subject: shell: syncronize ash_test/run-all and hush_test/run-all a bit Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/ash_test/run-all | 102 +++++++++++++++++++++++++----------------------- shell/hush_test/run-all | 51 +++++++++++++----------- 2 files changed, 82 insertions(+), 71 deletions(-) (limited to 'shell') diff --git a/shell/ash_test/run-all b/shell/ash_test/run-all index 8dfdddd9f..983e6d184 100755 --- a/shell/ash_test/run-all +++ b/shell/ash_test/run-all @@ -1,6 +1,6 @@ #!/bin/sh -TOPDIR=$PWD +TOPDIR=`pwd` test -x ash || { echo "No ./ash - creating a link to ../../busybox" @@ -10,67 +10,73 @@ test -x printenv || gcc -O2 -o printenv printenv.c || exit $? test -x recho || gcc -O2 -o recho recho.c || exit $? test -x zecho || gcc -O2 -o zecho zecho.c || exit $? -PATH="$PWD:$PATH" # for ash and recho/zecho/printenv +PATH="`pwd`:$PATH" # for ash and recho/zecho/printenv export PATH -THIS_SH="$PWD/ash" +THIS_SH="`pwd`/ash" export THIS_SH do_test() { - test -d "$1" || return 0 -# echo do_test "$1" - # $1 but with / replaced by # so that it can be used as filename part - noslash=`echo "$1" | sed 's:/:#:g'` - ( - cd "$1" || { echo "cannot cd $1!"; exit 1; } - for x in run-*; do - test -f "$x" || continue - case "$x" in - "$0"|run-minimal|run-gprof) ;; - *.orig|*~) ;; - #*) echo $x ; sh $x ;; - *) - echo -n "$1/$x: " - sh "$x" >"$TOPDIR/$noslash-$x.fail" 2>&1 && \ - { echo "ok"; rm "$TOPDIR/$noslash-$x.fail"; } || echo "fail"; - ;; - esac - done - # Many bash run-XXX scripts just do this, - # no point in duplication it all over the place - for x in *.tests; do - test -x "$x" || continue - name="${x%%.tests}" - test -f "$name.right" || continue - echo -n "$1/$x: " - { - "$THIS_SH" "./$x" >"$name.xx" 2>&1 - diff -u "$name.xx" "$name.right" >"$TOPDIR/$noslash-$x.fail" \ - && rm -f "$name.xx" "$TOPDIR/$noslash-$x.fail" - } && echo "ok" || echo "fail" - done - ) + test -d "$1" || return 0 + d=${d%/} +# echo Running tests in directory "$1" + # $1 but with / replaced by # so that it can be used as filename part + noslash=`echo "$1" | sed 's:/:#:g'` + ( + cd "$1" || { echo "cannot cd $1!"; exit 1; } + for x in run-*; do + test -f "$x" || continue + case "$x" in + "$0"|run-minimal|run-gprof) ;; + *.orig|*~) ;; + #*) echo $x ; sh $x ;; + *) + echo -n "$1/$x:" + sh "$x" >"$TOPDIR/$noslash-$x.fail" 2>&1 && \ + { { echo " ok"; rm "$TOPDIR/$noslash-$x.fail"; } || echo " fail"; } + ;; + esac + done + # Many bash run-XXX scripts just do this, + # no point in duplication it all over the place + for x in *.tests; do + test -x "$x" || continue + name="${x%%.tests}" + test -f "$name.right" || continue +# echo Running test: "$x" + echo -n "$1/$x:" + { + "$THIS_SH" "./$x" >"$name.xx" 2>&1 + diff -u "$name.xx" "$name.right" >"$TOPDIR/$noslash-$x.fail" \ + && rm -f "$name.xx" "$TOPDIR/$noslash-$x.fail" + } && echo " ok" || echo " fail" + done + ) } -# main part of this script +# Main part of this script # Usage: run-all [directories] +ret=0 + if [ $# -lt 1 ]; then - # All sub directories - modules=`ls -d ash-*` - # If you want to test ash against hush testsuite - # (have to copy hush_test dir to current dir first): - #modules=`ls -d ash-* hush_test/hush-*` + # All sub directories + modules=`ls -d ash-*` + # If you want to test ash against hush testsuite + # (have to copy hush_test dir to current dir first): + #modules=`ls -d ash-* hush_test/hush-*` - for module in $modules; do - do_test $module - done + for module in $modules; do + do_test $module || ret=1 + done else - while [ $# -ge 1 ]; do + while [ $# -ge 1 ]; do if [ -d $1 ]; then - do_test $1 + do_test $1 || ret=1 fi shift - done + done fi + +exit ${ret} diff --git a/shell/hush_test/run-all b/shell/hush_test/run-all index 837b3f7da..1dd0edc39 100755 --- a/shell/hush_test/run-all +++ b/shell/hush_test/run-all @@ -9,6 +9,8 @@ unset LC_NUMERIC unset LC_TIME unset LC_ALL +TOPDIR=`pwd` + if test ! -x hush; then if test ! -x ../../busybox; then echo "Can't run tests. Put hush binary into this directory (`pwd`)" @@ -38,6 +40,8 @@ do_test() test -d "$1" || return 0 d=${d%/} # echo Running tests in directory "$1" + # $1 but with / replaced by # so that it can be used as filename part + noslash=`echo "$1" | sed 's:/:#:g'` ( tret=0 cd "$1" || { echo "cannot cd $1!"; exit 1; } @@ -49,34 +53,35 @@ do_test() #*) echo $x ; sh $x ;; *) echo -n "$1/$x:" - sh "$x" >"../$1-$x.fail" 2>&1 && \ - { { echo " ok"; rm "../$1-$x.fail"; } || echo " fail"; } + sh "$x" >"$TOPDIR/$noslash-$x.fail" 2>&1 && \ + { { echo " ok"; rm "$TOPDIR/$noslash-$x.fail"; } || echo " fail"; } ;; esac done # Many bash run-XXX scripts just do this, # no point in duplication it all over the place for x in *.tests; do - test -x "$x" || continue - name="${x%%.tests}" - test -f "$name.right" || continue -# echo Running test: "$x" - echo -n "$1/$x:" - ( - "$THIS_SH" "./$x" >"$name.xx" 2>&1 - r=$? - # filter C library differences - sed -i \ - -e "/: invalid option /s:'::g" \ - "$name.xx" - test $r -eq 77 && rm -f "../$1-$x.fail" && exit 77 - diff -u "$name.xx" "$name.right" >"../$1-$x.fail" && rm -f "$name.xx" "../$1-$x.fail" - ) - case $? in - 0) echo " ok";; - 77) echo " skip (feature disabled)";; - *) echo " fail"; tret=1;; - esac + test -x "$x" || continue + name="${x%%.tests}" + test -f "$name.right" || continue +# echo Running test: "$x" + echo -n "$1/$x:" + ( + "$THIS_SH" "./$x" >"$name.xx" 2>&1 + r=$? + # filter C library differences + sed -i \ + -e "/: invalid option /s:'::g" \ + "$name.xx" + test $r -eq 77 && rm -f "$TOPDIR/$noslash-$x.fail" && exit 77 + diff -u "$name.xx" "$name.right" >"$TOPDIR/$noslash-$x.fail" \ + && rm -f "$name.xx" "$TOPDIR/$noslash-$x.fail" + ) + case $? in + 0) echo " ok";; + 77) echo " skip (feature disabled)";; + *) echo " fail"; tret=1;; + esac done exit ${tret} ) @@ -92,7 +97,7 @@ if [ $# -lt 1 ]; then modules=`ls -d hush-*` for module in $modules; do - do_test $module || ret=1 + do_test $module || ret=1 done else while [ $# -ge 1 ]; do -- cgit v1.2.3-55-g6feb From 959cb6742832a3b403a5d0116088a09f33afe927 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Thu, 6 Jul 2017 18:16:18 +0200 Subject: shell: syncronize ash and hush heredoc1.tests Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/ash_test/ash-heredoc/heredoc1.right | 6 +++++- shell/ash_test/ash-heredoc/heredoc1.tests | 12 +++++++++--- shell/ash_test/ash-heredoc/heredoc8.right | 1 + shell/ash_test/ash-heredoc/heredoc8.tests | 3 +++ shell/hush_test/hush-heredoc/heredoc8.right | 1 + shell/hush_test/hush-heredoc/heredoc8.tests | 3 +++ 6 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 shell/ash_test/ash-heredoc/heredoc8.right create mode 100755 shell/ash_test/ash-heredoc/heredoc8.tests create mode 100644 shell/hush_test/hush-heredoc/heredoc8.right create mode 100755 shell/hush_test/hush-heredoc/heredoc8.tests (limited to 'shell') diff --git a/shell/ash_test/ash-heredoc/heredoc1.right b/shell/ash_test/ash-heredoc/heredoc1.right index 40aa5a5fe..7fc68f3e1 100644 --- a/shell/ash_test/ash-heredoc/heredoc1.right +++ b/shell/ash_test/ash-heredoc/heredoc1.right @@ -1 +1,5 @@ -./heredoc1.tests: line 3: syntax error: unexpected "then" +qwe +asd +123 +456 +Ok diff --git a/shell/ash_test/ash-heredoc/heredoc1.tests b/shell/ash_test/ash-heredoc/heredoc1.tests index a912a67c7..2eeb4726b 100755 --- a/shell/ash_test/ash-heredoc/heredoc1.tests +++ b/shell/ash_test/ash-heredoc/heredoc1.tests @@ -1,3 +1,9 @@ -# We used to SEGV on this: - -<<EOF; then <W +cat <<000; cat <<www; cat <<eee +000 +qwe +asd +www +123 +456 +eee +echo Ok diff --git a/shell/ash_test/ash-heredoc/heredoc8.right b/shell/ash_test/ash-heredoc/heredoc8.right new file mode 100644 index 000000000..af396660e --- /dev/null +++ b/shell/ash_test/ash-heredoc/heredoc8.right @@ -0,0 +1 @@ +./heredoc8.tests: line 3: syntax error: unexpected "then" diff --git a/shell/ash_test/ash-heredoc/heredoc8.tests b/shell/ash_test/ash-heredoc/heredoc8.tests new file mode 100755 index 000000000..f7bc0737a --- /dev/null +++ b/shell/ash_test/ash-heredoc/heredoc8.tests @@ -0,0 +1,3 @@ +# ash used to SEGV on this: + +<<EOF; then <W diff --git a/shell/hush_test/hush-heredoc/heredoc8.right b/shell/hush_test/hush-heredoc/heredoc8.right new file mode 100644 index 000000000..558858f47 --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc8.right @@ -0,0 +1 @@ +hush: syntax error at 'then' diff --git a/shell/hush_test/hush-heredoc/heredoc8.tests b/shell/hush_test/hush-heredoc/heredoc8.tests new file mode 100755 index 000000000..f7bc0737a --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc8.tests @@ -0,0 +1,3 @@ +# ash used to SEGV on this: + +<<EOF; then <W -- cgit v1.2.3-55-g6feb From bb963bda62cc8b0965a921df70dfea44c4378163 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Thu, 6 Jul 2017 18:19:35 +0200 Subject: shell: syncronize ash and hush heredoc3.tests Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/ash_test/ash-heredoc/heredoc3.right | 10 +++++++++- shell/ash_test/ash-heredoc/heredoc3.tests | 21 ++++++++++++--------- shell/ash_test/ash-heredoc/heredoc9.right | 1 + shell/ash_test/ash-heredoc/heredoc9.tests | 9 +++++++++ 4 files changed, 31 insertions(+), 10 deletions(-) create mode 100644 shell/ash_test/ash-heredoc/heredoc9.right create mode 100755 shell/ash_test/ash-heredoc/heredoc9.tests (limited to 'shell') diff --git a/shell/ash_test/ash-heredoc/heredoc3.right b/shell/ash_test/ash-heredoc/heredoc3.right index ce0136250..6ed517f74 100644 --- a/shell/ash_test/ash-heredoc/heredoc3.right +++ b/shell/ash_test/ash-heredoc/heredoc3.right @@ -1 +1,9 @@ -hello +exit EOF-f +" +echo $f +echo `echo Hello World` +moo + EOF-f +EOF-f f +EOF-f +Ok diff --git a/shell/ash_test/ash-heredoc/heredoc3.tests b/shell/ash_test/ash-heredoc/heredoc3.tests index 96c227cc1..938577a89 100755 --- a/shell/ash_test/ash-heredoc/heredoc3.tests +++ b/shell/ash_test/ash-heredoc/heredoc3.tests @@ -1,9 +1,12 @@ -echo hello >greeting -cat <<EOF && -$(cat greeting) -EOF -{ - echo $? - cat greeting -} >/dev/null -rm greeting +f=1 + cat <<- EOF-f"" + exit EOF-f +" +echo $f +echo `echo Hello World` + moo + EOF-f +EOF-f f +EOF-f +EOF-f +echo Ok diff --git a/shell/ash_test/ash-heredoc/heredoc9.right b/shell/ash_test/ash-heredoc/heredoc9.right new file mode 100644 index 000000000..ce0136250 --- /dev/null +++ b/shell/ash_test/ash-heredoc/heredoc9.right @@ -0,0 +1 @@ +hello diff --git a/shell/ash_test/ash-heredoc/heredoc9.tests b/shell/ash_test/ash-heredoc/heredoc9.tests new file mode 100755 index 000000000..96c227cc1 --- /dev/null +++ b/shell/ash_test/ash-heredoc/heredoc9.tests @@ -0,0 +1,9 @@ +echo hello >greeting +cat <<EOF && +$(cat greeting) +EOF +{ + echo $? + cat greeting +} >/dev/null +rm greeting -- cgit v1.2.3-55-g6feb From cafb2d195d4d68e4e4c474453409a69643edf5aa Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Thu, 6 Jul 2017 18:31:47 +0200 Subject: hush: add tickquote1.tests from ash testsuite Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/ash_test/ash-misc/tickquote1.tests | 2 +- shell/hush_test/hush-misc/tickquote1.right | 1 + shell/hush_test/hush-misc/tickquote1.tests | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 shell/hush_test/hush-misc/tickquote1.right create mode 100755 shell/hush_test/hush-misc/tickquote1.tests (limited to 'shell') diff --git a/shell/ash_test/ash-misc/tickquote1.tests b/shell/ash_test/ash-misc/tickquote1.tests index 90d5bbc9b..8416ad996 100755 --- a/shell/ash_test/ash-misc/tickquote1.tests +++ b/shell/ash_test/ash-misc/tickquote1.tests @@ -1 +1 @@ -echo `"pwd` +echo _`"pwd`_ diff --git a/shell/hush_test/hush-misc/tickquote1.right b/shell/hush_test/hush-misc/tickquote1.right new file mode 100644 index 000000000..56f8515b7 --- /dev/null +++ b/shell/hush_test/hush-misc/tickquote1.right @@ -0,0 +1 @@ +hush: syntax error: unterminated " diff --git a/shell/hush_test/hush-misc/tickquote1.tests b/shell/hush_test/hush-misc/tickquote1.tests new file mode 100755 index 000000000..02e3904f1 --- /dev/null +++ b/shell/hush_test/hush-misc/tickquote1.tests @@ -0,0 +1,2 @@ +# UNFIXED BUG: hush does not parse embedded `cmd` at embedding document parse time +echo _`"pwd`_ -- cgit v1.2.3-55-g6feb From b18b04c8a832148fb47aec20ce1e74267d2fb438 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Thu, 6 Jul 2017 18:37:30 +0200 Subject: shell: remove duplicate sigint1.tests (another copies are in signals/) Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/ash_test/ash-misc/sigint1.right | 1 - shell/ash_test/ash-misc/sigint1.tests | 41 --------------------------------- shell/hush_test/hush-misc/sigint1.right | 1 - shell/hush_test/hush-misc/sigint1.tests | 41 --------------------------------- 4 files changed, 84 deletions(-) delete mode 100644 shell/ash_test/ash-misc/sigint1.right delete mode 100755 shell/ash_test/ash-misc/sigint1.tests delete mode 100644 shell/hush_test/hush-misc/sigint1.right delete mode 100755 shell/hush_test/hush-misc/sigint1.tests (limited to 'shell') diff --git a/shell/ash_test/ash-misc/sigint1.right b/shell/ash_test/ash-misc/sigint1.right deleted file mode 100644 index a9094b056..000000000 --- a/shell/ash_test/ash-misc/sigint1.right +++ /dev/null @@ -1 +0,0 @@ -Sending SIGINT to main shell PID diff --git a/shell/ash_test/ash-misc/sigint1.tests b/shell/ash_test/ash-misc/sigint1.tests deleted file mode 100755 index 3d483d32a..000000000 --- a/shell/ash_test/ash-misc/sigint1.tests +++ /dev/null @@ -1,41 +0,0 @@ -# What should happen if non-interactive shell gets SIGINT? - -(sleep 1; echo Sending SIGINT to main shell PID; exec kill -INT $$) & - -# We create a child which exits with 0 even on SIGINT -# (The complex command is necessary only if SIGINT is generated by ^C, -# in this testcase even bare "sleep 2" would do because -# in the testcase we don't send SIGINT *to the child*...) -$THIS_SH -c 'trap "exit 0" SIGINT; sleep 2' - -# In one second, we (main shell) get SIGINT here. -# The question is whether we should, or should not, exit. - -# bash will not stop here. It will execute next command(s). - -# The rationale for this is described here: -# http://www.cons.org/cracauer/sigint.html -# -# Basically, bash will not exit on SIGINT immediately if it waits -# for a child. It will wait for the child to exit. -# If child exits NOT by dying on SIGINT, then bash will not exit. -# -# The idea is that the following script: -# | emacs file.txt -# | more cmds -# User may use ^C to interrupt editor's ops like search. But then -# emacs exits normally. User expects that script doesn't stop. -# -# This is a nice idea, but detecting "did process really exit -# with SIGINT?" is racy. Consider: -# | bash -c 'while true; do /bin/true; done' -# When ^C is pressed while bash waits for /bin/true to exit, -# it may happen that /bin/true exits with exitcode 0 before -# ^C is delivered to it as SIGINT. bash will see SIGINT, then -# it will see that child exited with 0, and bash will NOT EXIT. - -# Therefore we do not implement bash behavior. -# I'd say that emacs need to put itself into a separate pgrp -# to isolate shell from getting stray SIGINTs from ^C. - -echo Next command after SIGINT was executed diff --git a/shell/hush_test/hush-misc/sigint1.right b/shell/hush_test/hush-misc/sigint1.right deleted file mode 100644 index a9094b056..000000000 --- a/shell/hush_test/hush-misc/sigint1.right +++ /dev/null @@ -1 +0,0 @@ -Sending SIGINT to main shell PID diff --git a/shell/hush_test/hush-misc/sigint1.tests b/shell/hush_test/hush-misc/sigint1.tests deleted file mode 100755 index 3d483d32a..000000000 --- a/shell/hush_test/hush-misc/sigint1.tests +++ /dev/null @@ -1,41 +0,0 @@ -# What should happen if non-interactive shell gets SIGINT? - -(sleep 1; echo Sending SIGINT to main shell PID; exec kill -INT $$) & - -# We create a child which exits with 0 even on SIGINT -# (The complex command is necessary only if SIGINT is generated by ^C, -# in this testcase even bare "sleep 2" would do because -# in the testcase we don't send SIGINT *to the child*...) -$THIS_SH -c 'trap "exit 0" SIGINT; sleep 2' - -# In one second, we (main shell) get SIGINT here. -# The question is whether we should, or should not, exit. - -# bash will not stop here. It will execute next command(s). - -# The rationale for this is described here: -# http://www.cons.org/cracauer/sigint.html -# -# Basically, bash will not exit on SIGINT immediately if it waits -# for a child. It will wait for the child to exit. -# If child exits NOT by dying on SIGINT, then bash will not exit. -# -# The idea is that the following script: -# | emacs file.txt -# | more cmds -# User may use ^C to interrupt editor's ops like search. But then -# emacs exits normally. User expects that script doesn't stop. -# -# This is a nice idea, but detecting "did process really exit -# with SIGINT?" is racy. Consider: -# | bash -c 'while true; do /bin/true; done' -# When ^C is pressed while bash waits for /bin/true to exit, -# it may happen that /bin/true exits with exitcode 0 before -# ^C is delivered to it as SIGINT. bash will see SIGINT, then -# it will see that child exited with 0, and bash will NOT EXIT. - -# Therefore we do not implement bash behavior. -# I'd say that emacs need to put itself into a separate pgrp -# to isolate shell from getting stray SIGINTs from ^C. - -echo Next command after SIGINT was executed -- cgit v1.2.3-55-g6feb From 74562984720c3f7d0a62ffbfd8ee78391c39c1e8 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Thu, 6 Jul 2017 18:40:45 +0200 Subject: hush: "adopt" ash signal4.tests Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/hush.c | 2 +- shell/hush_test/hush-signals/signal4.right | 4 ++++ shell/hush_test/hush-signals/signal4.tests | 5 +++++ 3 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 shell/hush_test/hush-signals/signal4.right create mode 100755 shell/hush_test/hush-signals/signal4.tests (limited to 'shell') diff --git a/shell/hush.c b/shell/hush.c index aee77d03b..64b33cf1c 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -9486,7 +9486,7 @@ static int FAST_FUNC builtin_trap(char **argv) if (sig < 0 || sig >= NSIG) { ret = EXIT_FAILURE; /* Mimic bash message exactly */ - bb_perror_msg("trap: %s: invalid signal specification", argv[-1]); + bb_error_msg("trap: %s: invalid signal specification", argv[-1]); continue; } diff --git a/shell/hush_test/hush-signals/signal4.right b/shell/hush_test/hush-signals/signal4.right new file mode 100644 index 000000000..2d0624714 --- /dev/null +++ b/shell/hush_test/hush-signals/signal4.right @@ -0,0 +1,4 @@ +hush: trap: BADNAME: invalid signal specification +1 +Trapped +Ok diff --git a/shell/hush_test/hush-signals/signal4.tests b/shell/hush_test/hush-signals/signal4.tests new file mode 100755 index 000000000..6f1c4a950 --- /dev/null +++ b/shell/hush_test/hush-signals/signal4.tests @@ -0,0 +1,5 @@ +#!/bin/sh + +trap "echo Trapped" BADNAME TERM; echo $? +kill $$ +echo Ok -- cgit v1.2.3-55-g6feb From 3234045d07c3fb2a9ef8afd02f821158317adbd3 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Thu, 6 Jul 2017 19:29:23 +0200 Subject: hush: "adopt" ash var-utf8-length.tests Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/ash_test/ash-vars/var-utf8-length.tests | 2 ++ shell/hush_test/hush-vars/var-utf8-length.right | 1 + shell/hush_test/hush-vars/var-utf8-length.tests | 4 ++++ 3 files changed, 7 insertions(+) create mode 100644 shell/hush_test/hush-vars/var-utf8-length.right create mode 100755 shell/hush_test/hush-vars/var-utf8-length.tests (limited to 'shell') diff --git a/shell/ash_test/ash-vars/var-utf8-length.tests b/shell/ash_test/ash-vars/var-utf8-length.tests index d04b2cbb6..b6e87f191 100755 --- a/shell/ash_test/ash-vars/var-utf8-length.tests +++ b/shell/ash_test/ash-vars/var-utf8-length.tests @@ -1,2 +1,4 @@ +LANG=en_US.UTF-8 +LC_ALL=en_US.UTF-8 X=abcdÉfghÍjklmnÓpqrstÚvwcyz echo ${#X} diff --git a/shell/hush_test/hush-vars/var-utf8-length.right b/shell/hush_test/hush-vars/var-utf8-length.right new file mode 100644 index 000000000..6f4247a62 --- /dev/null +++ b/shell/hush_test/hush-vars/var-utf8-length.right @@ -0,0 +1 @@ +26 diff --git a/shell/hush_test/hush-vars/var-utf8-length.tests b/shell/hush_test/hush-vars/var-utf8-length.tests new file mode 100755 index 000000000..b6e87f191 --- /dev/null +++ b/shell/hush_test/hush-vars/var-utf8-length.tests @@ -0,0 +1,4 @@ +LANG=en_US.UTF-8 +LC_ALL=en_US.UTF-8 +X=abcdÉfghÍjklmnÓpqrstÚvwcyz +echo ${#X} -- cgit v1.2.3-55-g6feb From 5dad7bdc3bdad8e9934d45301d5a8c51e843cd7b Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Thu, 6 Jul 2017 19:48:20 +0200 Subject: hush: implement negative start in the ${v: -n[:m]} idiom Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/ash_test/ash-vars/var_bash6.right | 5 +++++ shell/ash_test/ash-vars/var_bash6.tests | 9 +++++++++ shell/hush.c | 8 ++++++-- shell/hush_test/hush-vars/var_bash1a.right | 6 ++++++ shell/hush_test/hush-vars/var_bash1a.tests | 11 +++++++++++ 5 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 shell/ash_test/ash-vars/var_bash6.right create mode 100755 shell/ash_test/ash-vars/var_bash6.tests create mode 100644 shell/hush_test/hush-vars/var_bash1a.right create mode 100755 shell/hush_test/hush-vars/var_bash1a.tests (limited to 'shell') diff --git a/shell/ash_test/ash-vars/var_bash6.right b/shell/ash_test/ash-vars/var_bash6.right new file mode 100644 index 000000000..63fc23df8 --- /dev/null +++ b/shell/ash_test/ash-vars/var_bash6.right @@ -0,0 +1,5 @@ +Expected Actual +a*z : a*z +\z : \z +a1z a2z: a1z a2z +z : z diff --git a/shell/ash_test/ash-vars/var_bash6.tests b/shell/ash_test/ash-vars/var_bash6.tests new file mode 100755 index 000000000..cf2e4f020 --- /dev/null +++ b/shell/ash_test/ash-vars/var_bash6.tests @@ -0,0 +1,9 @@ +# This testcase checks globbing correctness in ${v/a/b} + +>a1z; >a2z; + echo 'Expected' 'Actual' +v='a bz'; echo 'a*z :' "${v/a*z/a*z}" +v='a bz'; echo '\z :' "${v/a*z/\z}" +v='a bz'; echo 'a1z a2z:' ${v/a*z/a*z} +v='a bz'; echo 'z :' ${v/a*z/\z} +rm a1z a2z diff --git a/shell/hush.c b/shell/hush.c index 64b33cf1c..f6b50dec6 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -5619,8 +5619,12 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha goto arith_err; debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len); if (len >= 0) { /* bash compat: len < 0 is illegal */ - if (beg < 0) /* bash compat */ - beg = 0; + if (beg < 0) { + /* negative beg counts from the end */ + beg = (arith_t)strlen(val) + beg; + if (beg < 0) /* ${v: -999999} is "" */ + beg = len = 0; + } debug_printf_varexp("from val:'%s'\n", val); if (len == 0 || !val || beg >= strlen(val)) { arith_err: diff --git a/shell/hush_test/hush-vars/var_bash1a.right b/shell/hush_test/hush-vars/var_bash1a.right new file mode 100644 index 000000000..1965b5c6c --- /dev/null +++ b/shell/hush_test/hush-vars/var_bash1a.right @@ -0,0 +1,6 @@ +parameter 'abcdef' +varoffset2 'cdef' +varoffset-2 'ef' +literal '2' 'cdef' +literal '-2' 'abcdef' +literal ' -2' 'ef' diff --git a/shell/hush_test/hush-vars/var_bash1a.tests b/shell/hush_test/hush-vars/var_bash1a.tests new file mode 100755 index 000000000..551dd9acc --- /dev/null +++ b/shell/hush_test/hush-vars/var_bash1a.tests @@ -0,0 +1,11 @@ +parameter=abcdef +offset=2 +noffset=-2 +echo "parameter '${parameter}'" +echo "varoffset2 '${parameter:${offset}}'" +echo "varoffset-2 '${parameter:${noffset}}'" +echo "literal '2' '${parameter:2}'" +# This is not inrpreted as ${VAR:POS{:LEN}}, +# but as ${VAR:=WORD} - if VAR is unset or null, substitute WORD +echo "literal '-2' '${parameter:-2}'" +echo "literal ' -2' '${parameter: -2}'" -- cgit v1.2.3-55-g6feb From 74d20e637982b412611dbbf32b633857b1c73539 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Thu, 6 Jul 2017 19:50:42 +0200 Subject: typo fix Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/ash_test/ash-vars/var_bash1a.tests | 2 +- shell/hush_test/hush-vars/var_bash1a.tests | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'shell') diff --git a/shell/ash_test/ash-vars/var_bash1a.tests b/shell/ash_test/ash-vars/var_bash1a.tests index 551dd9acc..c75c2dc53 100755 --- a/shell/ash_test/ash-vars/var_bash1a.tests +++ b/shell/ash_test/ash-vars/var_bash1a.tests @@ -5,7 +5,7 @@ echo "parameter '${parameter}'" echo "varoffset2 '${parameter:${offset}}'" echo "varoffset-2 '${parameter:${noffset}}'" echo "literal '2' '${parameter:2}'" -# This is not inrpreted as ${VAR:POS{:LEN}}, +# This is not interpreted as ${VAR:POS{:LEN}}, # but as ${VAR:=WORD} - if VAR is unset or null, substitute WORD echo "literal '-2' '${parameter:-2}'" echo "literal ' -2' '${parameter: -2}'" diff --git a/shell/hush_test/hush-vars/var_bash1a.tests b/shell/hush_test/hush-vars/var_bash1a.tests index 551dd9acc..c75c2dc53 100755 --- a/shell/hush_test/hush-vars/var_bash1a.tests +++ b/shell/hush_test/hush-vars/var_bash1a.tests @@ -5,7 +5,7 @@ echo "parameter '${parameter}'" echo "varoffset2 '${parameter:${offset}}'" echo "varoffset-2 '${parameter:${noffset}}'" echo "literal '2' '${parameter:2}'" -# This is not inrpreted as ${VAR:POS{:LEN}}, +# This is not interpreted as ${VAR:POS{:LEN}}, # but as ${VAR:=WORD} - if VAR is unset or null, substitute WORD echo "literal '-2' '${parameter:-2}'" echo "literal ' -2' '${parameter: -2}'" -- cgit v1.2.3-55-g6feb From e59591a364e43bccb6fd4d373d3ed86fc77dffb7 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Thu, 6 Jul 2017 20:12:44 +0200 Subject: hush: Print error messages on shift -1 Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/ash_test/ash-misc/shift1.tests | 2 +- shell/hush.c | 13 ++++++++++++- shell/hush_test/hush-misc/shift1.right | 10 ++++++++++ shell/hush_test/hush-misc/shift1.tests | 10 ++++++++++ 4 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 shell/hush_test/hush-misc/shift1.right create mode 100755 shell/hush_test/hush-misc/shift1.tests (limited to 'shell') diff --git a/shell/ash_test/ash-misc/shift1.tests b/shell/ash_test/ash-misc/shift1.tests index 0992d9b1b..2774b35ea 100755 --- a/shell/ash_test/ash-misc/shift1.tests +++ b/shell/ash_test/ash-misc/shift1.tests @@ -1,5 +1,5 @@ $THIS_SH -c 'shift; echo "$@"' 0 1 2 3 4 -#We do abort on -1, but then we abort. bash executes echo. +# We do complain on -1, but then we abort. bash executes echo. $THIS_SH -c 'shift -1; echo "$@"' 0 1 2 3 4 $THIS_SH -c 'shift 0; echo "$@"' 0 1 2 3 4 $THIS_SH -c 'shift 1; echo "$@"' 0 1 2 3 4 diff --git a/shell/hush.c b/shell/hush.c index f6b50dec6..0ade2ccca 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -9377,7 +9377,18 @@ static int FAST_FUNC builtin_shift(char **argv) int n = 1; argv = skip_dash_dash(argv); if (argv[0]) { - n = atoi(argv[0]); + n = bb_strtou(argv[0], NULL, 10); + if (errno || n < 0) { + /* shared string with ash.c */ + bb_error_msg("Illegal number: %s", argv[0]); + /* + * ash aborts in this case. + * bash prints error message and set $? to 1. + * Interestingly, for "shift 99999" bash does not + * print error message, but does set $? to 1 + * (and does no shifting at all). + */ + } } if (n >= 0 && n < G.global_argc) { if (G_global_args_malloced) { diff --git a/shell/hush_test/hush-misc/shift1.right b/shell/hush_test/hush-misc/shift1.right new file mode 100644 index 000000000..e3ab61392 --- /dev/null +++ b/shell/hush_test/hush-misc/shift1.right @@ -0,0 +1,10 @@ +2 3 4 +hush: Illegal number: -1 +1 2 3 4 +1 2 3 4 +2 3 4 +3 4 +4 + +1 2 3 4 +1 2 3 4 diff --git a/shell/hush_test/hush-misc/shift1.tests b/shell/hush_test/hush-misc/shift1.tests new file mode 100755 index 000000000..f2a264751 --- /dev/null +++ b/shell/hush_test/hush-misc/shift1.tests @@ -0,0 +1,10 @@ +$THIS_SH -c 'shift; echo "$@"' 0 1 2 3 4 +#We complain on -1 and continue. +$THIS_SH -c 'shift -1; echo "$@"' 0 1 2 3 4 +$THIS_SH -c 'shift 0; echo "$@"' 0 1 2 3 4 +$THIS_SH -c 'shift 1; echo "$@"' 0 1 2 3 4 +$THIS_SH -c 'shift 2; echo "$@"' 0 1 2 3 4 +$THIS_SH -c 'shift 3; echo "$@"' 0 1 2 3 4 +$THIS_SH -c 'shift 4; echo "$@"' 0 1 2 3 4 +$THIS_SH -c 'shift 5; echo "$@"' 0 1 2 3 4 +$THIS_SH -c 'shift 6; echo "$@"' 0 1 2 3 4 -- cgit v1.2.3-55-g6feb From a107ef2a6ace98c51473dc3153564a44b260bc6f Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Thu, 6 Jul 2017 20:36:40 +0200 Subject: hush: rename hush-redir/redir3.tests (ash has redir3.tests which id different) Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/hush_test/hush-redir/redir3.right | 14 -------------- shell/hush_test/hush-redir/redir3.tests | 9 --------- shell/hush_test/hush-redir/redir_errors.right | 14 ++++++++++++++ shell/hush_test/hush-redir/redir_errors.tests | 9 +++++++++ 4 files changed, 23 insertions(+), 23 deletions(-) delete mode 100644 shell/hush_test/hush-redir/redir3.right delete mode 100755 shell/hush_test/hush-redir/redir3.tests create mode 100644 shell/hush_test/hush-redir/redir_errors.right create mode 100755 shell/hush_test/hush-redir/redir_errors.tests (limited to 'shell') diff --git a/shell/hush_test/hush-redir/redir3.right b/shell/hush_test/hush-redir/redir3.right deleted file mode 100644 index 3d20bbf68..000000000 --- a/shell/hush_test/hush-redir/redir3.right +++ /dev/null @@ -1,14 +0,0 @@ -hush: can't open '/does/not/exist': No such file or directory -One:1 -hush: can't open '/cant/be/created': No such file or directory -One:1 -Ok -hush: can't open '/cant/be/created': No such file or directory -Zero:0 -hush: can't open '/cant/be/created': No such file or directory -One:1 -hush: can't open '/cant/be/created': No such file or directory -One:1 -hush: can't open '/cant/be/created': No such file or directory -Zero:0 -Done diff --git a/shell/hush_test/hush-redir/redir3.tests b/shell/hush_test/hush-redir/redir3.tests deleted file mode 100755 index 7c28e4324..000000000 --- a/shell/hush_test/hush-redir/redir3.tests +++ /dev/null @@ -1,9 +0,0 @@ -echo Error >/does/not/exist; echo One:$? -t=BAD -t=Ok >>/cant/be/created; echo One:$? -echo $t -! >/cant/be/created; echo Zero:$? -exec >/cant/be/created; echo One:$? -exec /bin/true >/cant/be/created; echo One:$? -! exec /bin/true >/cant/be/created; echo Zero:$? -echo Done diff --git a/shell/hush_test/hush-redir/redir_errors.right b/shell/hush_test/hush-redir/redir_errors.right new file mode 100644 index 000000000..3d20bbf68 --- /dev/null +++ b/shell/hush_test/hush-redir/redir_errors.right @@ -0,0 +1,14 @@ +hush: can't open '/does/not/exist': No such file or directory +One:1 +hush: can't open '/cant/be/created': No such file or directory +One:1 +Ok +hush: can't open '/cant/be/created': No such file or directory +Zero:0 +hush: can't open '/cant/be/created': No such file or directory +One:1 +hush: can't open '/cant/be/created': No such file or directory +One:1 +hush: can't open '/cant/be/created': No such file or directory +Zero:0 +Done diff --git a/shell/hush_test/hush-redir/redir_errors.tests b/shell/hush_test/hush-redir/redir_errors.tests new file mode 100755 index 000000000..7c28e4324 --- /dev/null +++ b/shell/hush_test/hush-redir/redir_errors.tests @@ -0,0 +1,9 @@ +echo Error >/does/not/exist; echo One:$? +t=BAD +t=Ok >>/cant/be/created; echo One:$? +echo $t +! >/cant/be/created; echo Zero:$? +exec >/cant/be/created; echo One:$? +exec /bin/true >/cant/be/created; echo One:$? +! exec /bin/true >/cant/be/created; echo Zero:$? +echo Done -- cgit v1.2.3-55-g6feb From 50b8b2914b7551b4e36518fcc70aac201d46d7cb Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Thu, 6 Jul 2017 20:57:37 +0200 Subject: hush: add a TODO about redir3.tests failure Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/hush.c | 4 ++++ shell/hush_test/hush-redir/redir3.right | 3 +++ shell/hush_test/hush-redir/redir3.tests | 5 +++++ 3 files changed, 12 insertions(+) create mode 100644 shell/hush_test/hush-redir/redir3.right create mode 100755 shell/hush_test/hush-redir/redir3.tests (limited to 'shell') diff --git a/shell/hush.c b/shell/hush.c index 0ade2ccca..4ba6b3fdd 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -7723,6 +7723,10 @@ static NOINLINE int run_pipe(struct pipe *pi) unset_vars(new_env); add_vars(old_vars); /* clean_up_and_ret0: */ + +//FIXME: this restores stdio fds, but does not close other redirects! +//Example: after "echo TEST 9>/dev/null" fd#9 is not closed! +//The squirreling code needs rework to remember all fds, not just 0,1,2. restore_redirects(squirrel); clean_up_and_ret1: free(argv_expanded); diff --git a/shell/hush_test/hush-redir/redir3.right b/shell/hush_test/hush-redir/redir3.right new file mode 100644 index 000000000..fd641a8ea --- /dev/null +++ b/shell/hush_test/hush-redir/redir3.right @@ -0,0 +1,3 @@ +TEST +./redir3.tests: line 4: 9: Bad file descriptor +Output to fd#9: 1 diff --git a/shell/hush_test/hush-redir/redir3.tests b/shell/hush_test/hush-redir/redir3.tests new file mode 100755 index 000000000..e37d5e45a --- /dev/null +++ b/shell/hush_test/hush-redir/redir3.tests @@ -0,0 +1,5 @@ +# redirects to closed descriptors should not leave these descriptors +# open afterwards +echo TEST 9>/dev/null +echo MUST ERROR OUT >&9 +echo "Output to fd#9: $?" -- cgit v1.2.3-55-g6feb From 1ff1a75710dd0c2d72d1f97f71caebdfeb185f6d Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Thu, 6 Jul 2017 21:00:19 +0200 Subject: ash: rename redir5.tests (hush has redir5.tests which is different) Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/ash_test/ash-redir/redir5.right | 2 -- shell/ash_test/ash-redir/redir5.tests | 3 --- shell/ash_test/ash-redir/redir_to_bad_fd.right | 2 ++ shell/ash_test/ash-redir/redir_to_bad_fd.tests | 3 +++ 4 files changed, 5 insertions(+), 5 deletions(-) delete mode 100644 shell/ash_test/ash-redir/redir5.right delete mode 100755 shell/ash_test/ash-redir/redir5.tests create mode 100644 shell/ash_test/ash-redir/redir_to_bad_fd.right create mode 100755 shell/ash_test/ash-redir/redir_to_bad_fd.tests (limited to 'shell') diff --git a/shell/ash_test/ash-redir/redir5.right b/shell/ash_test/ash-redir/redir5.right deleted file mode 100644 index 9d0877765..000000000 --- a/shell/ash_test/ash-redir/redir5.right +++ /dev/null @@ -1,2 +0,0 @@ -./redir5.tests: line 2: 10: Bad file descriptor -OK diff --git a/shell/ash_test/ash-redir/redir5.tests b/shell/ash_test/ash-redir/redir5.tests deleted file mode 100755 index 91b0c1ff9..000000000 --- a/shell/ash_test/ash-redir/redir5.tests +++ /dev/null @@ -1,3 +0,0 @@ -# ash uses fd 10 (usually) for reading the script -echo LOST >&10 -echo OK diff --git a/shell/ash_test/ash-redir/redir_to_bad_fd.right b/shell/ash_test/ash-redir/redir_to_bad_fd.right new file mode 100644 index 000000000..43b8af293 --- /dev/null +++ b/shell/ash_test/ash-redir/redir_to_bad_fd.right @@ -0,0 +1,2 @@ +./redir_to_bad_fd.tests: line 2: 10: Bad file descriptor +OK diff --git a/shell/ash_test/ash-redir/redir_to_bad_fd.tests b/shell/ash_test/ash-redir/redir_to_bad_fd.tests new file mode 100755 index 000000000..91b0c1ff9 --- /dev/null +++ b/shell/ash_test/ash-redir/redir_to_bad_fd.tests @@ -0,0 +1,3 @@ +# ash uses fd 10 (usually) for reading the script +echo LOST >&10 +echo OK -- cgit v1.2.3-55-g6feb From 111cdcf295b4cab78521480f52b295d9ae719263 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Thu, 6 Jul 2017 21:01:50 +0200 Subject: shell: sync redir/* tests Note: hush-redir/redir_to_bad_fd.tests currently fails Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/ash_test/ash-redir/redir5.right | 4 ++++ shell/ash_test/ash-redir/redir5.tests | 13 +++++++++++++ shell/hush_test/hush-redir/redir_to_bad_fd.right | 2 ++ shell/hush_test/hush-redir/redir_to_bad_fd.tests | 3 +++ 4 files changed, 22 insertions(+) create mode 100644 shell/ash_test/ash-redir/redir5.right create mode 100755 shell/ash_test/ash-redir/redir5.tests create mode 100644 shell/hush_test/hush-redir/redir_to_bad_fd.right create mode 100755 shell/hush_test/hush-redir/redir_to_bad_fd.tests (limited to 'shell') diff --git a/shell/ash_test/ash-redir/redir5.right b/shell/ash_test/ash-redir/redir5.right new file mode 100644 index 000000000..52cce4feb --- /dev/null +++ b/shell/ash_test/ash-redir/redir5.right @@ -0,0 +1,4 @@ +Backgrounded pipes shall have their stdin redirected to /dev/null +Zero:0 +Zero:0 +Done diff --git a/shell/ash_test/ash-redir/redir5.tests b/shell/ash_test/ash-redir/redir5.tests new file mode 100755 index 000000000..957f9c81f --- /dev/null +++ b/shell/ash_test/ash-redir/redir5.tests @@ -0,0 +1,13 @@ +echo "Backgrounded pipes shall have their stdin redirected to /dev/null" + +# 1. bash does not redirect stdin to /dev/null if it is interactive. +# hush does it always (this is allowed by standards). + +# 2. Failure will result in this script hanging + +cat & wait; echo Zero:$? + +# This does not work for bash! bash bug? +cat | cat & wait; echo Zero:$? + +echo Done diff --git a/shell/hush_test/hush-redir/redir_to_bad_fd.right b/shell/hush_test/hush-redir/redir_to_bad_fd.right new file mode 100644 index 000000000..43b8af293 --- /dev/null +++ b/shell/hush_test/hush-redir/redir_to_bad_fd.right @@ -0,0 +1,2 @@ +./redir_to_bad_fd.tests: line 2: 10: Bad file descriptor +OK diff --git a/shell/hush_test/hush-redir/redir_to_bad_fd.tests b/shell/hush_test/hush-redir/redir_to_bad_fd.tests new file mode 100755 index 000000000..91b0c1ff9 --- /dev/null +++ b/shell/hush_test/hush-redir/redir_to_bad_fd.tests @@ -0,0 +1,3 @@ +# ash uses fd 10 (usually) for reading the script +echo LOST >&10 +echo OK -- cgit v1.2.3-55-g6feb From 69a5ec9dccfd183cdf6bee7b994336670755cd47 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Fri, 7 Jul 2017 19:08:56 +0200 Subject: main: fix the case where user has "halt" as login shell. Closes 9986 halt::0:0::/:/sbin/halt function old new delta run_applet_and_exit 748 751 +3 run_applet_no_and_exit 467 459 -8 Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- include/libbb.h | 2 +- libbb/appletlib.c | 10 +++++++--- libbb/vfork_daemon_rexec.c | 2 +- shell/ash.c | 2 +- shell/hush.c | 2 +- 5 files changed, 11 insertions(+), 7 deletions(-) (limited to 'shell') diff --git a/include/libbb.h b/include/libbb.h index 1c9de3af0..0317c7d6a 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -1117,7 +1117,7 @@ int spawn_and_wait(char **argv) FAST_FUNC; int run_nofork_applet(int applet_no, char **argv) FAST_FUNC; #ifndef BUILD_INDIVIDUAL extern int find_applet_by_name(const char *name) FAST_FUNC; -extern void run_applet_no_and_exit(int a, char **argv) NORETURN FAST_FUNC; +extern void run_applet_no_and_exit(int a, const char *name, char **argv) NORETURN FAST_FUNC; #endif /* Helpers for daemonization. diff --git a/libbb/appletlib.c b/libbb/appletlib.c index 2dea2b43a..df6584978 100644 --- a/libbb/appletlib.c +++ b/libbb/appletlib.c @@ -877,13 +877,17 @@ static int busybox_main(char **argv) # endif # if NUM_APPLETS > 0 -void FAST_FUNC run_applet_no_and_exit(int applet_no, char **argv) +void FAST_FUNC run_applet_no_and_exit(int applet_no, const char *name, char **argv) { int argc = string_array_len(argv); /* Reinit some shared global data */ xfunc_error_retval = EXIT_FAILURE; - applet_name = bb_get_last_path_component_nostrip(argv[0]); + /* + * We do not use argv[0]: do not want to repeat massaging of + * "-/sbin/halt" -> "halt", for example. + */ + applet_name = name; /* Special case. POSIX says "test --help" * should be no different from e.g. "test --foo". @@ -927,7 +931,7 @@ static NORETURN void run_applet_and_exit(const char *name, char **argv) { int applet = find_applet_by_name(name); if (applet >= 0) - run_applet_no_and_exit(applet, argv); + run_applet_no_and_exit(applet, name, argv); } # endif diff --git a/libbb/vfork_daemon_rexec.c b/libbb/vfork_daemon_rexec.c index 2695f99ee..576534ee5 100644 --- a/libbb/vfork_daemon_rexec.c +++ b/libbb/vfork_daemon_rexec.c @@ -180,7 +180,7 @@ int FAST_FUNC spawn_and_wait(char **argv) * as of yet (and that should probably always stay true). */ /* xfunc_error_retval and applet_name are init by: */ - run_applet_no_and_exit(a, argv); + run_applet_no_and_exit(a, argv[0], argv); } # endif } diff --git a/shell/ash.c b/shell/ash.c index b7635a823..8c2098dd9 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -7717,7 +7717,7 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char ** clearenv(); while (*envp) putenv(*envp++); - run_applet_no_and_exit(applet_no, argv); + run_applet_no_and_exit(applet_no, cmd, argv); } /* re-exec ourselves with the new arguments */ execve(bb_busybox_exec_path, argv, envp); diff --git a/shell/hush.c b/shell/hush.c index 4ba6b3fdd..cf6d8cd9f 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -7063,7 +7063,7 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save, /* Do not leak open fds from opened script files etc */ close_all_FILE_list(); debug_printf_exec("running applet '%s'\n", argv[0]); - run_applet_no_and_exit(a, argv); + run_applet_no_and_exit(a, argv[0], argv); } # endif /* Re-exec ourselves */ -- cgit v1.2.3-55-g6feb From 2db74610cdf8ffb4f9ed99b62c755377d3cc48ea Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Fri, 7 Jul 2017 22:07:28 +0200 Subject: hush: fix two redirection testcase failures function old new delta save_fds_on_redirect 183 256 +73 fcntl_F_DUPFD - 46 +46 restore_redirects 74 96 +22 xdup_and_close 51 72 +21 setup_redirects 196 200 +4 hush_main 988 983 -5 static.C 12 - -12 run_pipe 1595 1551 -44 ------------------------------------------------------------------------------ (add/remove: 1/1 grow/shrink: 4/2 up/down: 166/-61) Total: 105 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/hush.c | 179 +++++++++++++++-------- shell/hush_test/hush-redir/redir3.right | 3 +- shell/hush_test/hush-redir/redir_to_bad_fd.right | 3 +- 3 files changed, 117 insertions(+), 68 deletions(-) (limited to 'shell') diff --git a/shell/hush.c b/shell/hush.c index cf6d8cd9f..59bddbfff 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -402,6 +402,7 @@ #define debug_printf_expand(...) do {} while (0) #define debug_printf_varexp(...) do {} while (0) #define debug_printf_glob(...) do {} while (0) +#define debug_printf_redir(...) do {} while (0) #define debug_printf_list(...) do {} while (0) #define debug_printf_subst(...) do {} while (0) #define debug_printf_clean(...) do {} while (0) @@ -1146,6 +1147,10 @@ static const struct built_in_command bltins2[] = { # define DEBUG_GLOB 0 #endif +#ifndef debug_printf_redir +# define debug_printf_redir(...) (indent(), fdprintf(2, __VA_ARGS__)) +#endif + #ifndef debug_printf_list # define debug_printf_list(...) (indent(), fdprintf(2, __VA_ARGS__)) #endif @@ -1381,12 +1386,30 @@ static void free_strings(char **strings) free(strings); } +static int fcntl_F_DUPFD(int fd, int avoid_fd) +{ + int newfd; + repeat: + newfd = fcntl(fd, F_DUPFD, avoid_fd + 1); + if (newfd < 0) { + if (errno == EBUSY) + goto repeat; + if (errno == EINTR) + goto repeat; + } + return newfd; +} -static int xdup_and_close(int fd, int F_DUPFD_maybe_CLOEXEC) +static int xdup_and_close(int fd, int F_DUPFD_maybe_CLOEXEC, int avoid_fd) { - /* We avoid taking stdio fds. Mimicking ash: use fds above 9 */ - int newfd = fcntl(fd, F_DUPFD_maybe_CLOEXEC, 10); + int newfd; + repeat: + newfd = fcntl(fd, F_DUPFD_maybe_CLOEXEC, avoid_fd + 1); if (newfd < 0) { + if (errno == EBUSY) + goto repeat; + if (errno == EINTR) + goto repeat; /* fd was not open? */ if (errno == EBADF) return fd; @@ -1424,13 +1447,14 @@ static void fclose_and_forget(FILE *fp) } fclose(fp); } -static int save_FILEs_on_redirect(int fd) +static int save_FILEs_on_redirect(int fd, int avoid_fd) { struct FILE_list *fl = G.FILE_list; while (fl) { if (fd == fl->fd) { /* We use it only on script files, they are all CLOEXEC */ - fl->fd = xdup_and_close(fd, F_DUPFD_CLOEXEC); + fl->fd = xdup_and_close(fd, F_DUPFD_CLOEXEC, avoid_fd); + debug_printf_redir("redirect_fd %d: matches a script fd, moving it to %d\n", fd, fl->fd); return 1; } fl = fl->next; @@ -1443,6 +1467,7 @@ static void restore_redirected_FILEs(void) while (fl) { int should_be = fileno(fl->fp); if (fl->fd != should_be) { + debug_printf_redir("restoring script fd from %d to %d\n", fl->fd, should_be); xmove_fd(fl->fd, should_be); fl->fd = should_be; } @@ -6518,77 +6543,108 @@ static void setup_heredoc(struct redir_struct *redir) wait(NULL); /* wait till child has died */ } -/* fd: redirect wants this fd to be used (e.g. 3>file). - * Move all conflicting internally used fds, - * and remember them so that we can restore them later. - */ -static int save_fds_on_redirect(int fd, int squirrel[3]) +struct squirrel { + int orig_fd; + int moved_to; + /* moved_to = n: fd was moved to n; restore back to orig_fd after redir */ + /* moved_to = -1: fd was opened by redirect; close orig_fd after redir */ +}; + +static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd) { - if (squirrel) { - /* Handle redirects of fds 0,1,2 */ + int i = 0; - /* If we collide with an already moved stdio fd... */ - if (fd == squirrel[0]) { - squirrel[0] = xdup_and_close(squirrel[0], F_DUPFD); - return 1; - } - if (fd == squirrel[1]) { - squirrel[1] = xdup_and_close(squirrel[1], F_DUPFD); - return 1; - } - if (fd == squirrel[2]) { - squirrel[2] = xdup_and_close(squirrel[2], F_DUPFD); - return 1; - } - /* If we are about to redirect stdio fd, and did not yet move it... */ - if (fd <= 2 && squirrel[fd] < 0) { - /* We avoid taking stdio fds */ - squirrel[fd] = fcntl(fd, F_DUPFD, 10); - if (squirrel[fd] < 0 && errno != EBADF) + if (sq) while (sq[i].orig_fd >= 0) { + /* If we collide with an already moved fd... */ + if (fd == sq[i].moved_to) { + sq[i].moved_to = fcntl_F_DUPFD(sq[i].moved_to, avoid_fd); + debug_printf_redir("redirect_fd %d: already busy, moving to %d\n", fd, sq[i].moved_to); + if (sq[i].moved_to < 0) /* what? */ xfunc_die(); - return 0; /* "we did not close fd" */ + return sq; + } + if (fd == sq[i].orig_fd) { + /* Example: echo Hello >/dev/null 1>&2 */ + debug_printf_redir("redirect_fd %d: already moved\n", fd); + return sq; } + i++; } + sq = xrealloc(sq, (i + 2) * sizeof(sq[0])); + sq[i].orig_fd = fd; + /* If this fd is open, we move and remember it; if it's closed, moved_to = -1 */ + sq[i].moved_to = fcntl_F_DUPFD(fd, avoid_fd); + debug_printf_redir("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, sq[i].moved_to); + if (sq[i].moved_to < 0 && errno != EBADF) + xfunc_die(); + sq[i+1].orig_fd = -1; /* end marker */ + return sq; +} + +/* fd: redirect wants this fd to be used (e.g. 3>file). + * Move all conflicting internally used fds, + * and remember them so that we can restore them later. + */ +static int save_fds_on_redirect(int fd, int avoid_fd, struct squirrel **sqp) +{ + if (avoid_fd < 9) /* the important case here is that it can be -1 */ + avoid_fd = 9; + #if ENABLE_HUSH_INTERACTIVE if (fd != 0 && fd == G.interactive_fd) { - G.interactive_fd = xdup_and_close(G.interactive_fd, F_DUPFD_CLOEXEC); - return 1; + G.interactive_fd = xdup_and_close(G.interactive_fd, F_DUPFD_CLOEXEC, avoid_fd); + debug_printf_redir("redirect_fd %d: matches interactive_fd, moving it to %d\n", fd, G.interactive_fd); + return 1; /* "we closed fd" */ } #endif - /* Are we called from setup_redirects(squirrel==NULL)? Two cases: * (1) Redirect in a forked child. No need to save FILEs' fds, * we aren't going to use them anymore, ok to trash. - * (2) "exec 3>FILE". Bummer. We can save FILEs' fds, - * but how are we doing to use them? + * (2) "exec 3>FILE". Bummer. We can save script FILEs' fds, + * but how are we doing to restore them? * "fileno(fd) = new_fd" can't be done. */ - if (!squirrel) + if (!sqp) return 0; - return save_FILEs_on_redirect(fd); + /* If this one of script's fds? */ + if (save_FILEs_on_redirect(fd, avoid_fd)) + return 1; /* yes. "we closed fd" */ + + /* Check whether it collides with any open fds (e.g. stdio), save fds as needed */ + *sqp = add_squirrel(*sqp, fd, avoid_fd); + return 0; /* "we did not close fd" */ } -static void restore_redirects(int squirrel[3]) +static void restore_redirects(struct squirrel *sq) { - int i, fd; - for (i = 0; i <= 2; i++) { - fd = squirrel[i]; - if (fd != -1) { - /* We simply die on error */ - xmove_fd(fd, i); + + if (sq) { + int i = 0; + while (sq[i].orig_fd >= 0) { + if (sq[i].moved_to >= 0) { + /* We simply die on error */ + debug_printf_redir("restoring redirected fd from %d to %d\n", sq[i].moved_to, sq[i].orig_fd); + xmove_fd(sq[i].moved_to, sq[i].orig_fd); + } else { + /* cmd1 9>FILE; cmd2_should_see_fd9_closed */ + debug_printf_redir("restoring redirected fd %d: closing it\n", sq[i].orig_fd); + close(sq[i].orig_fd); + } + i++; } + free(sq); } - /* Moved G.interactive_fd stays on new fd, not doing anything for it */ + /* If moved, G.interactive_fd stays on new fd, not restoring it */ restore_redirected_FILEs(); } /* squirrel != NULL means we squirrel away copies of stdin, stdout, * and stderr if they are redirected. */ -static int setup_redirects(struct command *prog, int squirrel[]) +static int setup_redirects(struct command *prog, struct squirrel **sqp) { int openfd, mode; struct redir_struct *redir; @@ -6596,7 +6652,7 @@ static int setup_redirects(struct command *prog, int squirrel[]) for (redir = prog->redirects; redir; redir = redir->next) { if (redir->rd_type == REDIRECT_HEREDOC2) { /* "rd_fd<<HERE" case */ - save_fds_on_redirect(redir->rd_fd, squirrel); + save_fds_on_redirect(redir->rd_fd, /*avoid:*/ 0, sqp); /* for REDIRECT_HEREDOC2, rd_filename holds _contents_ * of the heredoc */ debug_printf_parse("set heredoc '%s'\n", @@ -6635,7 +6691,7 @@ static int setup_redirects(struct command *prog, int squirrel[]) } if (openfd != redir->rd_fd) { - int closed = save_fds_on_redirect(redir->rd_fd, squirrel); + int closed = save_fds_on_redirect(redir->rd_fd, /*avoid:*/ openfd, sqp); if (openfd == REDIRFD_CLOSE) { /* "rd_fd >&-" means "close me" */ if (!closed) { @@ -7497,14 +7553,14 @@ static int checkjobs_and_fg_shell(struct pipe *fg_pipe) static int redirect_and_varexp_helper(char ***new_env_p, struct variable **old_vars_p, struct command *command, - int squirrel[3], + struct squirrel **sqp, char **argv_expanded) { /* setup_redirects acts on file descriptors, not FILEs. * This is perfect for work that comes after exec(). * Is it really safe for inline use? Experimentally, * things seem to work. */ - int rcode = setup_redirects(command, squirrel); + int rcode = setup_redirects(command, sqp); if (rcode == 0) { char **new_env = expand_assignments(command->argv, command->assignment_cnt); *new_env_p = new_env; @@ -7524,8 +7580,7 @@ static NOINLINE int run_pipe(struct pipe *pi) struct command *command; char **argv_expanded; char **argv; - /* it is not always needed, but we aim to smaller code */ - int squirrel[] = { -1, -1, -1 }; + struct squirrel *squirrel = NULL; int rcode; debug_printf_exec("run_pipe start: members:%d\n", pi->num_cmds); @@ -7582,7 +7637,7 @@ static NOINLINE int run_pipe(struct pipe *pi) /* { list } */ debug_printf("non-subshell group\n"); rcode = 1; /* exitcode if redir failed */ - if (setup_redirects(command, squirrel) == 0) { + if (setup_redirects(command, &squirrel) == 0) { debug_printf_exec(": run_list\n"); rcode = run_list(command->group) & 0xff; } @@ -7609,7 +7664,7 @@ static NOINLINE int run_pipe(struct pipe *pi) /* Ensure redirects take effect (that is, create files). * Try "a=t >file" */ #if 0 /* A few cases in testsuite fail with this code. FIXME */ - rcode = redirect_and_varexp_helper(&new_env, /*old_vars:*/ NULL, command, squirrel, /*argv_expanded:*/ NULL); + rcode = redirect_and_varexp_helper(&new_env, /*old_vars:*/ NULL, command, &squirrel, /*argv_expanded:*/ NULL); /* Set shell variables */ if (new_env) { argv = new_env; @@ -7631,7 +7686,7 @@ static NOINLINE int run_pipe(struct pipe *pi) #else /* Older, bigger, but more correct code */ - rcode = setup_redirects(command, squirrel); + rcode = setup_redirects(command, &squirrel); restore_redirects(squirrel); /* Set shell variables */ if (G_x_mode) @@ -7694,7 +7749,7 @@ static NOINLINE int run_pipe(struct pipe *pi) goto clean_up_and_ret1; } } - rcode = redirect_and_varexp_helper(&new_env, &old_vars, command, squirrel, argv_expanded); + rcode = redirect_and_varexp_helper(&new_env, &old_vars, command, &squirrel, argv_expanded); if (rcode == 0) { if (!funcp) { debug_printf_exec(": builtin '%s' '%s'...\n", @@ -7723,10 +7778,6 @@ static NOINLINE int run_pipe(struct pipe *pi) unset_vars(new_env); add_vars(old_vars); /* clean_up_and_ret0: */ - -//FIXME: this restores stdio fds, but does not close other redirects! -//Example: after "echo TEST 9>/dev/null" fd#9 is not closed! -//The squirreling code needs rework to remember all fds, not just 0,1,2. restore_redirects(squirrel); clean_up_and_ret1: free(argv_expanded); @@ -7739,7 +7790,7 @@ static NOINLINE int run_pipe(struct pipe *pi) if (ENABLE_FEATURE_SH_NOFORK) { int n = find_applet_by_name(argv_expanded[0]); if (n >= 0 && APPLET_IS_NOFORK(n)) { - rcode = redirect_and_varexp_helper(&new_env, &old_vars, command, squirrel, argv_expanded); + rcode = redirect_and_varexp_helper(&new_env, &old_vars, command, &squirrel, argv_expanded); if (rcode == 0) { debug_printf_exec(": run_nofork_applet '%s' '%s'...\n", argv_expanded[0], argv_expanded[1]); @@ -8694,7 +8745,7 @@ int hush_main(int argc, char **argv) G_saved_tty_pgrp = 0; /* try to dup stdin to high fd#, >= 255 */ - G_interactive_fd = fcntl(STDIN_FILENO, F_DUPFD, 255); + G_interactive_fd = fcntl_F_DUPFD(STDIN_FILENO, 254); if (G_interactive_fd < 0) { /* try to dup to any fd */ G_interactive_fd = dup(STDIN_FILENO); @@ -8767,7 +8818,7 @@ int hush_main(int argc, char **argv) #elif ENABLE_HUSH_INTERACTIVE /* No job control compiled in, only prompt/line editing */ if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) { - G_interactive_fd = fcntl(STDIN_FILENO, F_DUPFD, 255); + G_interactive_fd = fcntl_F_DUPFD(STDIN_FILENO, 254); if (G_interactive_fd < 0) { /* try to dup to any fd */ G_interactive_fd = dup(STDIN_FILENO); diff --git a/shell/hush_test/hush-redir/redir3.right b/shell/hush_test/hush-redir/redir3.right index fd641a8ea..e3c878b7d 100644 --- a/shell/hush_test/hush-redir/redir3.right +++ b/shell/hush_test/hush-redir/redir3.right @@ -1,3 +1,2 @@ TEST -./redir3.tests: line 4: 9: Bad file descriptor -Output to fd#9: 1 +hush: can't duplicate file descriptor: Bad file descriptor diff --git a/shell/hush_test/hush-redir/redir_to_bad_fd.right b/shell/hush_test/hush-redir/redir_to_bad_fd.right index 43b8af293..936911ce5 100644 --- a/shell/hush_test/hush-redir/redir_to_bad_fd.right +++ b/shell/hush_test/hush-redir/redir_to_bad_fd.right @@ -1,2 +1 @@ -./redir_to_bad_fd.tests: line 2: 10: Bad file descriptor -OK +hush: can't duplicate file descriptor: Bad file descriptor -- cgit v1.2.3-55-g6feb From 840a4355d035efc360eeefe5da8a11e47b3c80d3 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Fri, 7 Jul 2017 22:56:02 +0200 Subject: hush: fix "(sleep 1; exit 3) & sleep 2; echo $?; wait $!; echo $?" function old new delta process_wait_result 414 426 +12 builtin_wait 283 291 +8 run_list 974 978 +4 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 3/0 up/down: 24/0) Total: 24 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/ash_test/ash-misc/wait6.right | 2 ++ shell/ash_test/ash-misc/wait6.tests | 6 ++++++ shell/hush.c | 15 ++++++++++----- shell/hush_test/hush-misc/wait6.right | 2 ++ shell/hush_test/hush-misc/wait6.tests | 6 ++++++ 5 files changed, 26 insertions(+), 5 deletions(-) create mode 100644 shell/ash_test/ash-misc/wait6.right create mode 100755 shell/ash_test/ash-misc/wait6.tests create mode 100644 shell/hush_test/hush-misc/wait6.right create mode 100755 shell/hush_test/hush-misc/wait6.tests (limited to 'shell') diff --git a/shell/ash_test/ash-misc/wait6.right b/shell/ash_test/ash-misc/wait6.right new file mode 100644 index 000000000..12decc137 --- /dev/null +++ b/shell/ash_test/ash-misc/wait6.right @@ -0,0 +1,2 @@ +0 +3 diff --git a/shell/ash_test/ash-misc/wait6.tests b/shell/ash_test/ash-misc/wait6.tests new file mode 100755 index 000000000..c23713199 --- /dev/null +++ b/shell/ash_test/ash-misc/wait6.tests @@ -0,0 +1,6 @@ +# In bash, "wait $!" extracts correct exitcode even if bg task has already exited +# It prints 0, then 3: +(sleep 0; exit 3) & sleep 1 +echo $? +wait $! +echo $? diff --git a/shell/hush.c b/shell/hush.c index 59bddbfff..df96e6fde 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -832,6 +832,7 @@ struct globals { smallint exiting; /* used to prevent EXIT trap recursion */ /* These four support $?, $#, and $1 */ smalluint last_exitcode; + smalluint last_bg_pid_exitcode; #if ENABLE_HUSH_SET /* are global_argv and global_argv[1..n] malloced? (note: not [0]) */ smalluint global_args_malloced; @@ -7387,10 +7388,13 @@ static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status) found_pi_and_prognum: if (dead) { /* child exited */ - pi->cmds[i].pid = 0; - pi->cmds[i].cmd_exitcode = WEXITSTATUS(status); + int rcode = WEXITSTATUS(status); if (WIFSIGNALED(status)) - pi->cmds[i].cmd_exitcode = 128 + WTERMSIG(status); + rcode = 128 + WTERMSIG(status); + pi->cmds[i].cmd_exitcode = rcode; + if (G.last_bg_pid == pi->cmds[i].pid) + G.last_bg_pid_exitcode = rcode; + pi->cmds[i].pid = 0; pi->alive_cmds--; if (!pi->alive_cmds) { if (G_interactive_fd) @@ -8215,6 +8219,7 @@ static int run_list(struct pipe *pi) #endif /* Last command's pid goes to $! */ G.last_bg_pid = pi->cmds[pi->num_cmds - 1].pid; + G.last_bg_pid_exitcode = 0; debug_printf_exec(": cmd&: exitcode EXIT_SUCCESS\n"); /* Check pi->pi_inverted? "! sleep 1 & echo $?": bash says 1. dash and ash says 0 */ rcode = EXIT_SUCCESS; @@ -9909,6 +9914,7 @@ static int FAST_FUNC builtin_wait(char **argv) ret = waitpid(pid, &status, WNOHANG); if (ret < 0) { /* No */ + ret = 127; if (errno == ECHILD) { if (G.last_bg_pid > 0 && pid == G.last_bg_pid) { /* "wait $!" but last bg task has already exited. Try: @@ -9916,7 +9922,7 @@ static int FAST_FUNC builtin_wait(char **argv) * In bash it prints exitcode 0, then 3. * In dash, it is 127. */ - /* ret = G.last_bg_pid_exitstatus - FIXME */ + ret = G.last_bg_pid_exitcode; } else { /* Example: "wait 1". mimic bash message */ bb_error_msg("wait: pid %d is not a child of this shell", (int)pid); @@ -9925,7 +9931,6 @@ static int FAST_FUNC builtin_wait(char **argv) /* ??? */ bb_perror_msg("wait %s", *argv); } - ret = 127; continue; /* bash checks all argv[] */ } if (ret == 0) { diff --git a/shell/hush_test/hush-misc/wait6.right b/shell/hush_test/hush-misc/wait6.right new file mode 100644 index 000000000..12decc137 --- /dev/null +++ b/shell/hush_test/hush-misc/wait6.right @@ -0,0 +1,2 @@ +0 +3 diff --git a/shell/hush_test/hush-misc/wait6.tests b/shell/hush_test/hush-misc/wait6.tests new file mode 100755 index 000000000..c23713199 --- /dev/null +++ b/shell/hush_test/hush-misc/wait6.tests @@ -0,0 +1,6 @@ +# In bash, "wait $!" extracts correct exitcode even if bg task has already exited +# It prints 0, then 3: +(sleep 0; exit 3) & sleep 1 +echo $? +wait $! +echo $? -- cgit v1.2.3-55-g6feb From 13102634bb38befd80fafb5d2ebd915d7dcf0c5f Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Sat, 8 Jul 2017 00:24:32 +0200 Subject: hush: explain why wait5.tests is failing Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/hush.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'shell') diff --git a/shell/hush.c b/shell/hush.c index df96e6fde..b131f6095 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -7401,6 +7401,10 @@ static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status) printf(JOB_STATUS_FORMAT, pi->jobid, "Done", pi->cmdtext); delete_finished_bg_job(pi); +//bash deletes finished jobs from job table only in interactive mode, after "jobs" cmd, +//or if pid of a new process matches one of the old ones +//(see cleanup_dead_jobs(), delete_old_job(), J_NOTIFIED in bash source). +//Testcase script: "(exit 3) & sleep 1; wait %1; echo $?" prints 3 in bash. } } else { /* child stopped */ @@ -9899,6 +9903,15 @@ static int FAST_FUNC builtin_wait(char **argv) ret = job_exited_or_stopped(wait_pipe); if (ret < 0) ret = wait_for_child_or_signal(wait_pipe, 0); +//bash immediately deletes finished jobs from job table only in interactive mode, +//we _always_ delete them at once. If we'd start doing that, this (and more) +//would be necessary to avoid accumulating dead jobs: +# if 0 + else { + if (!wait_pipe->alive_cmds) + delete_finished_bg_job(wait_pipe); + } +# endif } /* else: parse_jobspec() already emitted error msg */ continue; -- cgit v1.2.3-55-g6feb From 1609629a918f0e5c8813a113447347db61be7b74 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Mon, 10 Jul 2017 10:00:28 +0200 Subject: hush: rename a few functions Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/hush.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'shell') diff --git a/shell/hush.c b/shell/hush.c index b131f6095..3533dfaa4 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -7217,7 +7217,7 @@ static const char *get_cmdtext(struct pipe *pi) return pi->cmdtext; } -static void insert_bg_job(struct pipe *pi) +static void insert_job_into_table(struct pipe *pi) { struct pipe *job, **jobp; int i; @@ -7249,7 +7249,7 @@ static void insert_bg_job(struct pipe *pi) G.last_jobid = job->jobid; } -static void remove_bg_job(struct pipe *pi) +static void remove_job_from_table(struct pipe *pi) { struct pipe *prev_pipe; @@ -7268,9 +7268,9 @@ static void remove_bg_job(struct pipe *pi) } /* Remove a backgrounded job */ -static void delete_finished_bg_job(struct pipe *pi) +static void delete_finished_job(struct pipe *pi) { - remove_bg_job(pi); + remove_job_from_table(pi); free_pipe(pi); } #endif /* JOB */ @@ -7359,7 +7359,7 @@ static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status) if (G_interactive_fd) { #if ENABLE_HUSH_JOB if (fg_pipe->alive_cmds != 0) - insert_bg_job(fg_pipe); + insert_job_into_table(fg_pipe); #endif return rcode; } @@ -7400,7 +7400,7 @@ static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status) if (G_interactive_fd) printf(JOB_STATUS_FORMAT, pi->jobid, "Done", pi->cmdtext); - delete_finished_bg_job(pi); + delete_finished_job(pi); //bash deletes finished jobs from job table only in interactive mode, after "jobs" cmd, //or if pid of a new process matches one of the old ones //(see cleanup_dead_jobs(), delete_old_job(), J_NOTIFIED in bash source). @@ -8219,7 +8219,7 @@ static int run_list(struct pipe *pi) * I'm NOT treating inner &'s as jobs */ #if ENABLE_HUSH_JOB if (G.run_list_level == 1) - insert_bg_job(pi); + insert_job_into_table(pi); #endif /* Last command's pid goes to $! */ G.last_bg_pid = pi->cmds[pi->num_cmds - 1].pid; @@ -9702,14 +9702,14 @@ static int FAST_FUNC builtin_fg_bg(char **argv) i = kill(- pi->pgrp, SIGCONT); if (i < 0) { if (errno == ESRCH) { - delete_finished_bg_job(pi); + delete_finished_job(pi); return EXIT_SUCCESS; } bb_perror_msg("kill (SIGCONT)"); } if (argv[0][0] == 'f') { - remove_bg_job(pi); + remove_job_from_table(pi); /* FG job shouldn't be in job table */ return checkjobs_and_fg_shell(pi); } return EXIT_SUCCESS; @@ -9909,7 +9909,7 @@ static int FAST_FUNC builtin_wait(char **argv) # if 0 else { if (!wait_pipe->alive_cmds) - delete_finished_bg_job(wait_pipe); + delete_finished_job(wait_pipe); } # endif } -- cgit v1.2.3-55-g6feb From 9e55a156f8a07297f620466e8173040336a7c48c Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Mon, 10 Jul 2017 10:01:12 +0200 Subject: hush: simplify insert_job_into_table() a bit function old new delta done_word 767 761 -6 insert_job_into_table 325 264 -61 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 0/2 up/down: 59/-126) Total: -67 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/hush.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) (limited to 'shell') diff --git a/shell/hush.c b/shell/hush.c index 3533dfaa4..8223cdbc5 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -3527,9 +3527,8 @@ static int reserved_word(o_string *word, struct parse_context *ctx) if (r->flag & FLAG_START) { struct parse_context *old; - old = xmalloc(sizeof(*old)); + old = xmemdup(ctx, sizeof(*ctx)); debug_printf_parse("push stack %p\n", old); - *old = *ctx; /* physical copy */ initialize_context(ctx); ctx->stack = old; } else if (/*ctx->ctx_res_w == RES_NONE ||*/ !(ctx->old_flag & (1 << r->res))) { @@ -7222,19 +7221,18 @@ static void insert_job_into_table(struct pipe *pi) struct pipe *job, **jobp; int i; - /* Linear search for the ID of the job to use */ - pi->jobid = 1; - for (job = G.job_list; job; job = job->next) - if (job->jobid >= pi->jobid) - pi->jobid = job->jobid + 1; - - /* Add job to the list of running jobs */ + /* Find the end of the list, and find next job ID to use */ + i = 0; jobp = &G.job_list; - while ((job = *jobp) != NULL) + while ((job = *jobp) != NULL) { + if (job->jobid > i) + i = job->jobid; jobp = &job->next; - job = *jobp = xmalloc(sizeof(*job)); + } + pi->jobid = i + 1; - *job = *pi; /* physical copy */ + /* Create a new job struct at the end */ + job = *jobp = xmemdup(pi, sizeof(*pi)); job->next = NULL; job->cmds = xzalloc(sizeof(pi->cmds[0]) * pi->num_cmds); /* Cannot copy entire pi->cmds[] vector! This causes double frees */ @@ -7267,7 +7265,6 @@ static void remove_job_from_table(struct pipe *pi) G.last_jobid = 0; } -/* Remove a backgrounded job */ static void delete_finished_job(struct pipe *pi) { remove_job_from_table(pi); @@ -9904,8 +9901,8 @@ static int FAST_FUNC builtin_wait(char **argv) if (ret < 0) ret = wait_for_child_or_signal(wait_pipe, 0); //bash immediately deletes finished jobs from job table only in interactive mode, -//we _always_ delete them at once. If we'd start doing that, this (and more) -//would be necessary to avoid accumulating dead jobs: +//we _always_ delete them at once. If we'd start keeping some dead jobs, this +//(and more) would be necessary to avoid accumulating dead jobs: # if 0 else { if (!wait_pipe->alive_cmds) -- cgit v1.2.3-55-g6feb From b057806a6ad7618b10db062bf6c71d09f71ce421 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Mon, 10 Jul 2017 10:33:10 +0200 Subject: hush: add TODO for "set -e" Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/hush.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'shell') diff --git a/shell/hush.c b/shell/hush.c index 8223cdbc5..89cd47d8f 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -49,6 +49,7 @@ * [un]alias, command, fc, getopts, newgrp, readonly, times * make complex ${var%...} constructs support optional * make here documents optional + * set -e (some ash testsuite entries use it, want to adopt those) * * Bash compat TODO: * redirection of stdout+stderr: &> and >& @@ -8421,6 +8422,22 @@ static int set_mode(int state, char mode, const char *o_opt) G.o_opt[idx] = state; break; } +/* TODO: set -e +Exit if pipe, list, or compound command exits with a non-zero status. +Shell does not exit if failed command is part of condition in +if/while, part of && or || list except the last command, any command +in a pipe but the last, or if the command's return value is being +inverted with !. If a compound command other than a subshell returns a +non-zero status because a command failed while -e was being ignored, the +shell does not exit. A trap on ERR, if set, is executed before the shell +exits [ERR is a bashism]. + +If a compound command or function executes in a context where -e is +ignored, none of the commands executed within are affected by the -e +setting. If a compound command or function sets -e while executing in a +context where -e is ignored, that setting does not have any effect until +the compound command or the command containing the function call completes. +*/ default: return EXIT_FAILURE; } -- cgit v1.2.3-55-g6feb From 9fda609a60506f4c1f73f0034cafd2b3434f4df3 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Fri, 14 Jul 2017 13:36:48 +0200 Subject: hush: add support for "set -e" function old new delta run_list 978 1046 +68 o_opt_strings 24 32 +8 reset_traps_to_defaults 136 142 +6 pick_sighandler 57 60 +3 packed_usage 31772 31770 -2 hush_main 983 961 -22 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 4/2 up/down: 85/-24) Total: 61 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/hush.c | 66 ++++++++++++++++++++---------- shell/hush_test/hush-misc/errexit1.right | 1 + shell/hush_test/hush-misc/errexit1.tests | 5 +++ shell/hush_test/hush-signals/signal8.right | 3 ++ shell/hush_test/hush-signals/signal8.tests | 18 ++++++++ shell/hush_test/hush-signals/signal9.right | 3 ++ shell/hush_test/hush-signals/signal9.tests | 21 ++++++++++ 7 files changed, 96 insertions(+), 21 deletions(-) create mode 100644 shell/hush_test/hush-misc/errexit1.right create mode 100755 shell/hush_test/hush-misc/errexit1.tests create mode 100644 shell/hush_test/hush-signals/signal8.right create mode 100755 shell/hush_test/hush-signals/signal8.tests create mode 100644 shell/hush_test/hush-signals/signal9.right create mode 100755 shell/hush_test/hush-signals/signal9.tests (limited to 'shell') diff --git a/shell/hush.c b/shell/hush.c index 89cd47d8f..553c8e64a 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -49,7 +49,6 @@ * [un]alias, command, fc, getopts, newgrp, readonly, times * make complex ${var%...} constructs support optional * make here documents optional - * set -e (some ash testsuite entries use it, want to adopt those) * * Bash compat TODO: * redirection of stdout+stderr: &> and >& @@ -286,7 +285,7 @@ * therefore we don't show them either. */ //usage:#define hush_trivial_usage -//usage: "[-nxl] [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS]]" +//usage: "[-enxl] [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS]]" //usage:#define hush_full_usage "\n\n" //usage: "Unix shell interpreter" @@ -747,6 +746,7 @@ struct function { static const char o_opt_strings[] ALIGN1 = "pipefail\0" "noexec\0" + "errexit\0" #if ENABLE_HUSH_MODE_X "xtrace\0" #endif @@ -754,6 +754,7 @@ static const char o_opt_strings[] ALIGN1 = enum { OPT_O_PIPEFAIL, OPT_O_NOEXEC, + OPT_O_ERREXIT, #if ENABLE_HUSH_MODE_X OPT_O_XTRACE, #endif @@ -810,6 +811,25 @@ struct globals { #else # define G_saved_tty_pgrp 0 #endif + /* How deeply are we in context where "set -e" is ignored */ + int errexit_depth; + /* "set -e" rules (do we follow them correctly?): + * Exit if pipe, list, or compound command exits with a non-zero status. + * Shell does not exit if failed command is part of condition in + * if/while, part of && or || list except the last command, any command + * in a pipe but the last, or if the command's return value is being + * inverted with !. If a compound command other than a subshell returns a + * non-zero status because a command failed while -e was being ignored, the + * shell does not exit. A trap on ERR, if set, is executed before the shell + * exits [ERR is a bashism]. + * + * If a compound command or function executes in a context where -e is + * ignored, none of the commands executed within are affected by the -e + * setting. If a compound command or function sets -e while executing in a + * context where -e is ignored, that setting does not have any effect until + * the compound command or the command containing the function call completes. + */ + char o_opt[NUM_OPT_O]; #if ENABLE_HUSH_MODE_X # define G_x_mode (G.o_opt[OPT_O_XTRACE]) @@ -5159,7 +5179,7 @@ static struct pipe *parse_stream(char **pstring, * and it will match } earlier (not here). */ syntax_error_unexpected_ch(ch); G.last_exitcode = 2; - goto parse_error1; + goto parse_error2; default: if (HUSH_DEBUG) bb_error_msg_and_die("BUG: unexpected %c\n", ch); @@ -5168,7 +5188,7 @@ static struct pipe *parse_stream(char **pstring, parse_error: G.last_exitcode = 1; - parse_error1: + parse_error2: { struct parse_context *pctx; IF_HAS_KEYWORDS(struct parse_context *p2;) @@ -8021,6 +8041,7 @@ static int run_list(struct pipe *pi) /* Go through list of pipes, (maybe) executing them. */ for (; pi; pi = IF_HUSH_LOOPS(rword == RES_DONE ? loop_top : ) pi->next) { int r; + int sv_errexit_depth; if (G.flag_SIGINT) break; @@ -8030,6 +8051,13 @@ static int run_list(struct pipe *pi) IF_HAS_KEYWORDS(rword = pi->res_word;) debug_printf_exec(": rword=%d cond_code=%d last_rword=%d\n", rword, cond_code, last_rword); + + sv_errexit_depth = G.errexit_depth; + if (IF_HAS_KEYWORDS(rword == RES_IF || rword == RES_ELIF ||) + pi->followup != PIPE_SEQ + ) { + G.errexit_depth++; + } #if ENABLE_HUSH_LOOPS if ((rword == RES_WHILE || rword == RES_UNTIL || rword == RES_FOR) && loop_top == NULL /* avoid bumping G.depth_of_loop twice */ @@ -8243,6 +8271,14 @@ static int run_list(struct pipe *pi) check_and_run_traps(); } + /* Handle "set -e" */ + if (rcode != 0 && G.o_opt[OPT_O_ERREXIT]) { + debug_printf_exec("ERREXIT:1 errexit_depth:%d\n", G.errexit_depth); + if (G.errexit_depth == 0) + hush_exit(rcode); + } + G.errexit_depth = sv_errexit_depth; + /* Analyze how result affects subsequent commands */ #if ENABLE_HUSH_IF if (rword == RES_IF || rword == RES_ELIF) @@ -8422,22 +8458,9 @@ static int set_mode(int state, char mode, const char *o_opt) G.o_opt[idx] = state; break; } -/* TODO: set -e -Exit if pipe, list, or compound command exits with a non-zero status. -Shell does not exit if failed command is part of condition in -if/while, part of && or || list except the last command, any command -in a pipe but the last, or if the command's return value is being -inverted with !. If a compound command other than a subshell returns a -non-zero status because a command failed while -e was being ignored, the -shell does not exit. A trap on ERR, if set, is executed before the shell -exits [ERR is a bashism]. - -If a compound command or function executes in a context where -e is -ignored, none of the commands executed within are affected by the -e -setting. If a compound command or function sets -e while executing in a -context where -e is ignored, that setting does not have any effect until -the compound command or the command containing the function call completes. -*/ + case 'e': + G.o_opt[OPT_O_ERREXIT] = state; + break; default: return EXIT_FAILURE; } @@ -8564,7 +8587,7 @@ int hush_main(int argc, char **argv) flags = (argv[0] && argv[0][0] == '-') ? OPT_login : 0; builtin_argc = 0; while (1) { - opt = getopt(argc, argv, "+c:xinsl" + opt = getopt(argc, argv, "+c:exinsl" #if !BB_MMU "<:$:R:V:" # if ENABLE_HUSH_FUNCTIONS @@ -8682,6 +8705,7 @@ int hush_main(int argc, char **argv) #endif case 'n': case 'x': + case 'e': if (set_mode(1, opt, NULL) == 0) /* no error */ break; default: diff --git a/shell/hush_test/hush-misc/errexit1.right b/shell/hush_test/hush-misc/errexit1.right new file mode 100644 index 000000000..d86bac9de --- /dev/null +++ b/shell/hush_test/hush-misc/errexit1.right @@ -0,0 +1 @@ +OK diff --git a/shell/hush_test/hush-misc/errexit1.tests b/shell/hush_test/hush-misc/errexit1.tests new file mode 100755 index 000000000..7b4a15634 --- /dev/null +++ b/shell/hush_test/hush-misc/errexit1.tests @@ -0,0 +1,5 @@ +set -e +(true) +echo OK +(false) +echo FAIL diff --git a/shell/hush_test/hush-signals/signal8.right b/shell/hush_test/hush-signals/signal8.right new file mode 100644 index 000000000..39572f30e --- /dev/null +++ b/shell/hush_test/hush-signals/signal8.right @@ -0,0 +1,3 @@ +Removing traps +End of exit_func +Done: 0 diff --git a/shell/hush_test/hush-signals/signal8.tests b/shell/hush_test/hush-signals/signal8.tests new file mode 100755 index 000000000..731af7477 --- /dev/null +++ b/shell/hush_test/hush-signals/signal8.tests @@ -0,0 +1,18 @@ +"$THIS_SH" -c ' +exit_func() { + echo "Removing traps" + trap - EXIT TERM INT + echo "End of exit_func" +} +set -e +trap exit_func EXIT TERM INT +sleep 2 +exit 77 +' & + +sleep 1 +# BUG: ash kills -PGRP, but in non-interactive shell we do not create pgrps! +# In this case, bash kills by PID, not PGRP. +kill -TERM %1 +wait +echo Done: $? diff --git a/shell/hush_test/hush-signals/signal9.right b/shell/hush_test/hush-signals/signal9.right new file mode 100644 index 000000000..39572f30e --- /dev/null +++ b/shell/hush_test/hush-signals/signal9.right @@ -0,0 +1,3 @@ +Removing traps +End of exit_func +Done: 0 diff --git a/shell/hush_test/hush-signals/signal9.tests b/shell/hush_test/hush-signals/signal9.tests new file mode 100755 index 000000000..18e71012b --- /dev/null +++ b/shell/hush_test/hush-signals/signal9.tests @@ -0,0 +1,21 @@ +# Note: the inner script is a test which checks for a different bug +# (ordering between INT handler and exit on "set -e"), +# but so far I did not figure out how to simulate it non-interactively. + +"$THIS_SH" -c ' +exit_func() { + echo "Removing traps" + trap - EXIT TERM INT + echo "End of exit_func" +} +set -e +trap exit_func EXIT TERM INT +sleep 2 +exit 77 +' & + +child=$! +sleep 1 +kill -TERM $child +wait +echo Done: $? -- cgit v1.2.3-55-g6feb From 0c5657e9119eb52263e83e9b55394a8f43f4e928 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Fri, 14 Jul 2017 19:27:03 +0200 Subject: hush: remove superfluous comparison function old new delta builtin_wait 291 285 -6 Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/hush.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'shell') diff --git a/shell/hush.c b/shell/hush.c index 553c8e64a..af5c26090 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -9967,7 +9967,7 @@ static int FAST_FUNC builtin_wait(char **argv) /* No */ ret = 127; if (errno == ECHILD) { - if (G.last_bg_pid > 0 && pid == G.last_bg_pid) { + if (pid == G.last_bg_pid) { /* "wait $!" but last bg task has already exited. Try: * (sleep 1; exit 3) & sleep 2; echo $?; wait $!; echo $? * In bash it prints exitcode 0, then 3. -- cgit v1.2.3-55-g6feb From 2ed74e25d354e6958dc86a21aa32c2dacb809bf0 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Fri, 14 Jul 2017 19:58:46 +0200 Subject: hush: make "wait %1" work even if the job is dead Example script: sleep 1 | (sleep 1;exit 3) & sleep 2 echo Zero:$? wait %1 echo Three:$? function old new delta clean_up_last_dead_job - 24 +24 process_wait_result 426 447 +21 builtin_wait 285 293 +8 insert_job_into_table 264 269 +5 builtin_jobs 68 73 +5 remove_job_from_table 59 57 -2 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 4/1 up/down: 63/-2) Total: 61 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/hush.c | 91 ++++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 52 insertions(+), 39 deletions(-) (limited to 'shell') diff --git a/shell/hush.c b/shell/hush.c index af5c26090..b76351fde 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -7237,11 +7237,42 @@ static const char *get_cmdtext(struct pipe *pi) return pi->cmdtext; } +static void remove_job_from_table(struct pipe *pi) +{ + struct pipe *prev_pipe; + + if (pi == G.job_list) { + G.job_list = pi->next; + } else { + prev_pipe = G.job_list; + while (prev_pipe->next != pi) + prev_pipe = prev_pipe->next; + prev_pipe->next = pi->next; + } + G.last_jobid = 0; + if (G.job_list) + G.last_jobid = G.job_list->jobid; +} + +static void delete_finished_job(struct pipe *pi) +{ + remove_job_from_table(pi); + free_pipe(pi); +} + +static void clean_up_last_dead_job(void) +{ + if (G.job_list && !G.job_list->alive_cmds) + delete_finished_job(G.job_list); +} + static void insert_job_into_table(struct pipe *pi) { struct pipe *job, **jobp; int i; + clean_up_last_dead_job(); + /* Find the end of the list, and find next job ID to use */ i = 0; jobp = &G.job_list; @@ -7267,30 +7298,6 @@ static void insert_job_into_table(struct pipe *pi) printf("[%u] %u %s\n", job->jobid, (unsigned)job->cmds[0].pid, job->cmdtext); G.last_jobid = job->jobid; } - -static void remove_job_from_table(struct pipe *pi) -{ - struct pipe *prev_pipe; - - if (pi == G.job_list) { - G.job_list = pi->next; - } else { - prev_pipe = G.job_list; - while (prev_pipe->next != pi) - prev_pipe = prev_pipe->next; - prev_pipe->next = pi->next; - } - if (G.job_list) - G.last_jobid = G.job_list->jobid; - else - G.last_jobid = 0; -} - -static void delete_finished_job(struct pipe *pi) -{ - remove_job_from_table(pi); - free_pipe(pi); -} #endif /* JOB */ static int job_exited_or_stopped(struct pipe *pi) @@ -7415,14 +7422,22 @@ static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status) pi->cmds[i].pid = 0; pi->alive_cmds--; if (!pi->alive_cmds) { - if (G_interactive_fd) + if (G_interactive_fd) { printf(JOB_STATUS_FORMAT, pi->jobid, "Done", pi->cmdtext); - delete_finished_job(pi); -//bash deletes finished jobs from job table only in interactive mode, after "jobs" cmd, -//or if pid of a new process matches one of the old ones -//(see cleanup_dead_jobs(), delete_old_job(), J_NOTIFIED in bash source). -//Testcase script: "(exit 3) & sleep 1; wait %1; echo $?" prints 3 in bash. + delete_finished_job(pi); + } else { +/* + * bash deletes finished jobs from job table only in interactive mode, + * after "jobs" cmd, or if pid of a new process matches one of the old ones + * (see cleanup_dead_jobs(), delete_old_job(), J_NOTIFIED in bash source). + * Testcase script: "(exit 3) & sleep 1; wait %1; echo $?" prints 3 in bash. + * We only retain one "dead" job, if it's the single job on the list. + * This covers most of real-world scenarios where this is useful. + */ + if (pi != G.job_list) + delete_finished_job(pi); + } } } else { /* child stopped */ @@ -9696,6 +9711,9 @@ static int FAST_FUNC builtin_jobs(char **argv UNUSED_PARAM) printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->cmdtext); } + + clean_up_last_dead_job(); + return EXIT_SUCCESS; } @@ -9939,17 +9957,12 @@ static int FAST_FUNC builtin_wait(char **argv) wait_pipe = parse_jobspec(*argv); if (wait_pipe) { ret = job_exited_or_stopped(wait_pipe); - if (ret < 0) + if (ret < 0) { ret = wait_for_child_or_signal(wait_pipe, 0); -//bash immediately deletes finished jobs from job table only in interactive mode, -//we _always_ delete them at once. If we'd start keeping some dead jobs, this -//(and more) would be necessary to avoid accumulating dead jobs: -# if 0 - else { - if (!wait_pipe->alive_cmds) - delete_finished_job(wait_pipe); + } else { + /* waiting on "last dead job" removes it */ + clean_up_last_dead_job(); } -# endif } /* else: parse_jobspec() already emitted error msg */ continue; -- cgit v1.2.3-55-g6feb From 9d4dc84a769e3e45e420fbef7c7bb2acbbdccd8e Mon Sep 17 00:00:00 2001 From: Johannes Schindelin <johannes.schindelin@gmx.de> Date: Fri, 14 Jul 2017 22:25:58 +0200 Subject: ash: protect WIFSTOPPED use with #if JOBS This change fixes the build in setups where there are no headers defining WIFSTOPPED and WSTOPSIG (where JOBS has to be set to 0). This partially reverts 4700fb5be (ash: make dowait() a bit more readable. Logic is unchanged, 2015-10-09). Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/ash.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'shell') diff --git a/shell/ash.c b/shell/ash.c index 8c2098dd9..b0c7dac54 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -4022,15 +4022,19 @@ sprint_status48(char *s, int status, int sigonly) col = 0; if (!WIFEXITED(status)) { - if (JOBS && WIFSTOPPED(status)) +#if JOBS + if (WIFSTOPPED(status)) st = WSTOPSIG(status); else +#endif st = WTERMSIG(status); if (sigonly) { if (st == SIGINT || st == SIGPIPE) goto out; - if (JOBS && WIFSTOPPED(status)) +#if JOBS + if (WIFSTOPPED(status)) goto out; +#endif } st &= 0x7f; //TODO: use bbox's get_signame? strsignal adds ~600 bytes to text+rodata @@ -4182,8 +4186,10 @@ dowait(int block, struct job *job) goto out; } /* The process wasn't found in job list */ - if (JOBS && !WIFSTOPPED(status)) +#if JOBS + if (!WIFSTOPPED(status)) jobless--; +#endif out: INT_ON; -- cgit v1.2.3-55-g6feb From ee553b929c81ae0051abfd54984aa0537f767d89 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Sat, 15 Jul 2017 22:51:55 +0200 Subject: hush: fix and_or_and_backgrounding.tests failure function old new delta done_pipe 133 218 +85 Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- .../ash-parsing/and_or_and_backgrounding.right | 4 +++ .../ash-parsing/and_or_and_backgrounding.tests | 31 ++++++++++++++++++ shell/hush.c | 37 +++++++++++++++++++++- .../hush-bugs/and_or_and_backgrounding.right | 4 --- .../hush-bugs/and_or_and_backgrounding.tests | 31 ------------------ .../hush-parsing/and_or_and_backgrounding.right | 4 +++ .../hush-parsing/and_or_and_backgrounding.tests | 31 ++++++++++++++++++ 7 files changed, 106 insertions(+), 36 deletions(-) create mode 100644 shell/ash_test/ash-parsing/and_or_and_backgrounding.right create mode 100755 shell/ash_test/ash-parsing/and_or_and_backgrounding.tests delete mode 100644 shell/hush_test/hush-bugs/and_or_and_backgrounding.right delete mode 100755 shell/hush_test/hush-bugs/and_or_and_backgrounding.tests create mode 100644 shell/hush_test/hush-parsing/and_or_and_backgrounding.right create mode 100755 shell/hush_test/hush-parsing/and_or_and_backgrounding.tests (limited to 'shell') diff --git a/shell/ash_test/ash-parsing/and_or_and_backgrounding.right b/shell/ash_test/ash-parsing/and_or_and_backgrounding.right new file mode 100644 index 000000000..90ce63e01 --- /dev/null +++ b/shell/ash_test/ash-parsing/and_or_and_backgrounding.right @@ -0,0 +1,4 @@ +First +Second +Third +Done diff --git a/shell/ash_test/ash-parsing/and_or_and_backgrounding.tests b/shell/ash_test/ash-parsing/and_or_and_backgrounding.tests new file mode 100755 index 000000000..05acfb863 --- /dev/null +++ b/shell/ash_test/ash-parsing/and_or_and_backgrounding.tests @@ -0,0 +1,31 @@ +# UNFIXED BUG: hush thinks that ; && || & have the same precedence. +# According to this doc, && || have higher precedence than ; &. +# See example below. +# Precedence of ; is not a problem in practice. Precedence of & is. +# +#http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html +# +#2.9.3 Lists +# +#An AND-OR list is a sequence of one or more pipelines separated by +#the operators "&&" and "||" . +# +#A list is a sequence of one or more AND-OR lists separated by the operators +#';' and '&' and optionally terminated by ';', '&', or <newline>. +# +#The operators "&&" and "||" shall have equal precedence and shall be +#evaluated with left associativity. For example, both of the following +#commands write solely bar to standard output: +# +# false && echo foo || echo bar +# true || echo foo && echo bar +# +#A ';' or <newline> terminator shall cause the preceding AND-OR list +#to be executed sequentially; an '&' shall cause asynchronous execution +#of the preceding AND-OR list. + +echo First && sleep 0.2 && echo Third & +sleep 0.1 +echo Second +wait +echo Done diff --git a/shell/hush.c b/shell/hush.c index b76351fde..2a734f3de 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -3373,12 +3373,47 @@ static void done_pipe(struct parse_context *ctx, pipe_style type) debug_printf_parse("done_pipe entered, followup %d\n", type); /* Close previous command */ not_null = done_command(ctx); - ctx->pipe->followup = type; #if HAS_KEYWORDS ctx->pipe->pi_inverted = ctx->ctx_inverted; ctx->ctx_inverted = 0; ctx->pipe->res_word = ctx->ctx_res_w; #endif + if (type != PIPE_BG || ctx->list_head == ctx->pipe) { + no_conv: + ctx->pipe->followup = type; + } else { + /* Necessary since && and || have more precedence than &: + * "cmd1 && cmd2 &" must spawn both cmds, not only cmd2, + * in a backgrounded subshell. + */ + struct pipe *pi; + struct command *command; + + /* Is this actually the case? */ + pi = ctx->list_head; + while (pi != ctx->pipe) { + if (pi->followup != PIPE_AND && pi->followup != PIPE_OR) + goto no_conv; + pi = pi->next; + } + + debug_printf_parse("BG with more than one pipe, converting to { p1 &&...pN; } &\n"); + pi->followup = PIPE_SEQ; /* close pN _not_ with "&"! */ + pi = xzalloc(sizeof(*pi)); + pi->followup = PIPE_BG; + pi->num_cmds = 1; + pi->cmds = xzalloc(sizeof(pi->cmds[0])); + command = &pi->cmds[0]; + if (CMD_NORMAL != 0) /* "if xzalloc didn't do that already" */ + command->cmd_type = CMD_NORMAL; + command->group = ctx->list_head; +#if !BB_MMU +//TODO: is this correct?! + command->group_as_string = xstrdup(ctx->as_string.data); +#endif + /* Replace all pipes in ctx with one newly created */ + ctx->list_head = ctx->pipe = pi; + } /* Without this check, even just <enter> on command line generates * tree of three NOPs (!). Which is harmless but annoying. diff --git a/shell/hush_test/hush-bugs/and_or_and_backgrounding.right b/shell/hush_test/hush-bugs/and_or_and_backgrounding.right deleted file mode 100644 index 90ce63e01..000000000 --- a/shell/hush_test/hush-bugs/and_or_and_backgrounding.right +++ /dev/null @@ -1,4 +0,0 @@ -First -Second -Third -Done diff --git a/shell/hush_test/hush-bugs/and_or_and_backgrounding.tests b/shell/hush_test/hush-bugs/and_or_and_backgrounding.tests deleted file mode 100755 index 05acfb863..000000000 --- a/shell/hush_test/hush-bugs/and_or_and_backgrounding.tests +++ /dev/null @@ -1,31 +0,0 @@ -# UNFIXED BUG: hush thinks that ; && || & have the same precedence. -# According to this doc, && || have higher precedence than ; &. -# See example below. -# Precedence of ; is not a problem in practice. Precedence of & is. -# -#http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html -# -#2.9.3 Lists -# -#An AND-OR list is a sequence of one or more pipelines separated by -#the operators "&&" and "||" . -# -#A list is a sequence of one or more AND-OR lists separated by the operators -#';' and '&' and optionally terminated by ';', '&', or <newline>. -# -#The operators "&&" and "||" shall have equal precedence and shall be -#evaluated with left associativity. For example, both of the following -#commands write solely bar to standard output: -# -# false && echo foo || echo bar -# true || echo foo && echo bar -# -#A ';' or <newline> terminator shall cause the preceding AND-OR list -#to be executed sequentially; an '&' shall cause asynchronous execution -#of the preceding AND-OR list. - -echo First && sleep 0.2 && echo Third & -sleep 0.1 -echo Second -wait -echo Done diff --git a/shell/hush_test/hush-parsing/and_or_and_backgrounding.right b/shell/hush_test/hush-parsing/and_or_and_backgrounding.right new file mode 100644 index 000000000..90ce63e01 --- /dev/null +++ b/shell/hush_test/hush-parsing/and_or_and_backgrounding.right @@ -0,0 +1,4 @@ +First +Second +Third +Done diff --git a/shell/hush_test/hush-parsing/and_or_and_backgrounding.tests b/shell/hush_test/hush-parsing/and_or_and_backgrounding.tests new file mode 100755 index 000000000..05acfb863 --- /dev/null +++ b/shell/hush_test/hush-parsing/and_or_and_backgrounding.tests @@ -0,0 +1,31 @@ +# UNFIXED BUG: hush thinks that ; && || & have the same precedence. +# According to this doc, && || have higher precedence than ; &. +# See example below. +# Precedence of ; is not a problem in practice. Precedence of & is. +# +#http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html +# +#2.9.3 Lists +# +#An AND-OR list is a sequence of one or more pipelines separated by +#the operators "&&" and "||" . +# +#A list is a sequence of one or more AND-OR lists separated by the operators +#';' and '&' and optionally terminated by ';', '&', or <newline>. +# +#The operators "&&" and "||" shall have equal precedence and shall be +#evaluated with left associativity. For example, both of the following +#commands write solely bar to standard output: +# +# false && echo foo || echo bar +# true || echo foo && echo bar +# +#A ';' or <newline> terminator shall cause the preceding AND-OR list +#to be executed sequentially; an '&' shall cause asynchronous execution +#of the preceding AND-OR list. + +echo First && sleep 0.2 && echo Third & +sleep 0.1 +echo Second +wait +echo Done -- cgit v1.2.3-55-g6feb From 9f904a22ffcaa517a0836a635a4a676b6b93c8fe Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Sat, 15 Jul 2017 22:54:46 +0200 Subject: shell: and_or_and_backgrounding.tests is no longer "UNFIXED BUG" Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/ash_test/ash-parsing/and_or_and_backgrounding.tests | 1 - shell/hush_test/hush-parsing/and_or_and_backgrounding.tests | 1 - 2 files changed, 2 deletions(-) (limited to 'shell') diff --git a/shell/ash_test/ash-parsing/and_or_and_backgrounding.tests b/shell/ash_test/ash-parsing/and_or_and_backgrounding.tests index 05acfb863..204d44490 100755 --- a/shell/ash_test/ash-parsing/and_or_and_backgrounding.tests +++ b/shell/ash_test/ash-parsing/and_or_and_backgrounding.tests @@ -1,4 +1,3 @@ -# UNFIXED BUG: hush thinks that ; && || & have the same precedence. # According to this doc, && || have higher precedence than ; &. # See example below. # Precedence of ; is not a problem in practice. Precedence of & is. diff --git a/shell/hush_test/hush-parsing/and_or_and_backgrounding.tests b/shell/hush_test/hush-parsing/and_or_and_backgrounding.tests index 05acfb863..204d44490 100755 --- a/shell/hush_test/hush-parsing/and_or_and_backgrounding.tests +++ b/shell/hush_test/hush-parsing/and_or_and_backgrounding.tests @@ -1,4 +1,3 @@ -# UNFIXED BUG: hush thinks that ; && || & have the same precedence. # According to this doc, && || have higher precedence than ; &. # See example below. # Precedence of ; is not a problem in practice. Precedence of & is. -- cgit v1.2.3-55-g6feb From b24e55da84093dd6bf6dff79627e32459c3da071 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Sun, 16 Jul 2017 20:29:35 +0200 Subject: hush: fix "cmd1 && cmd2 &" handling on NOMMU function old new delta done_pipe 234 238 +4 Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/hush.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) (limited to 'shell') diff --git a/shell/hush.c b/shell/hush.c index 2a734f3de..8d9292ed1 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -3378,18 +3378,15 @@ static void done_pipe(struct parse_context *ctx, pipe_style type) ctx->ctx_inverted = 0; ctx->pipe->res_word = ctx->ctx_res_w; #endif - if (type != PIPE_BG || ctx->list_head == ctx->pipe) { - no_conv: - ctx->pipe->followup = type; - } else { - /* Necessary since && and || have more precedence than &: + if (type == PIPE_BG && ctx->list_head != ctx->pipe) { + /* Necessary since && and || have precedence over &: * "cmd1 && cmd2 &" must spawn both cmds, not only cmd2, * in a backgrounded subshell. */ struct pipe *pi; struct command *command; - /* Is this actually the case? */ + /* Is this actually this construct, all pipes end with && or ||? */ pi = ctx->list_head; while (pi != ctx->pipe) { if (pi->followup != PIPE_AND && pi->followup != PIPE_OR) @@ -3408,11 +3405,16 @@ static void done_pipe(struct parse_context *ctx, pipe_style type) command->cmd_type = CMD_NORMAL; command->group = ctx->list_head; #if !BB_MMU -//TODO: is this correct?! - command->group_as_string = xstrdup(ctx->as_string.data); + command->group_as_string = xstrndup( + ctx->as_string.data, + ctx->as_string.length - 1 /* do not copy last char, "&" */ + ); #endif /* Replace all pipes in ctx with one newly created */ ctx->list_head = ctx->pipe = pi; + } else { + no_conv: + ctx->pipe->followup = type; } /* Without this check, even just <enter> on command line generates @@ -4855,7 +4857,9 @@ static struct pipe *parse_stream(char **pstring, * Really, ask yourself, why * "cmd && <newline>" doesn't start * cmd but waits for more input? - * No reason...) + * The only reason is that it might be + * a "cmd1 && <nl> cmd2 &" construct, + * cmd1 may need to run in BG). */ struct pipe *pi = ctx.list_head; if (pi->num_cmds != 0 /* check #1 */ -- cgit v1.2.3-55-g6feb From 203fd7bc66b869e5022ad33d26065e69eaaaf66b Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Mon, 17 Jul 2017 16:13:35 +0200 Subject: shells: expand TODO comments, no code changes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/ash.c | 1 + shell/hush.c | 31 ++++++++++++++++++++++++++----- 2 files changed, 27 insertions(+), 5 deletions(-) (limited to 'shell') diff --git a/shell/ash.c b/shell/ash.c index b0c7dac54..987d618c6 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -6680,6 +6680,7 @@ subevalvar(char *p, char *varname, int strloc, int subtype, if (*loc++ == ':') { len = number(loc); } +//TODO: number() chokes on "-n". In bash, LEN=-n means strlen()-n } if (pos < 0) { /* ${VAR:$((-n)):l} starts n chars from the end */ diff --git a/shell/hush.c b/shell/hush.c index 8d9292ed1..fd2a3d0f5 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -41,14 +41,29 @@ * * TODOs: * grep for "TODO" and fix (some of them are easy) + * make complex ${var%...} constructs support optional + * make here documents optional * special variables (done: PWD, PPID, RANDOM) + * follow IFS rules more precisely, including update semantics * tilde expansion * aliases - * follow IFS rules more precisely, including update semantics * builtins mandated by standards we don't support: - * [un]alias, command, fc, getopts, newgrp, readonly, times - * make complex ${var%...} constructs support optional - * make here documents optional + * [un]alias, command, fc, getopts, readonly, times: + * command -v CMD: print "/path/to/CMD" + * prints "CMD" for builtins + * prints "alias ALIAS='EXPANSION'" for aliases + * prints nothing and sets $? to 1 if not found + * command -V CMD: print "CMD is /path/CMD|a shell builtin|etc" + * command [-p] CMD: run CMD, even if a function CMD also exists + * (can use this to override standalone shell as well) + * -p: use default $PATH + * readonly VAR[=VAL]...: make VARs readonly + * readonly [-p]: list all such VARs (-p has no effect in bash) + * getopts: getopt() for shells + * times: print getrusage(SELF/CHILDREN).ru_utime/ru_stime + * fc -l[nr] [BEG] [END]: list range of commands in history + * fc [-e EDITOR] [BEG] [END]: edit/rerun range of commands + * fc -s [PAT=REP] [CMD]: rerun CMD, replacing PAT with REP * * Bash compat TODO: * redirection of stdout+stderr: &> and >& @@ -64,8 +79,13 @@ * The EXPR is evaluated according to ARITHMETIC EVALUATION. * This is exactly equivalent to let "EXPR". * $[EXPR]: synonym for $((EXPR)) + * indirect expansion: ${!VAR} + * substring op on @: ${@:n:m} * * Won't do: + * Some builtins mandated by standards: + * newgrp [GRP]: not a builtin in bash but a suid binary + * which spawns a new shell with new group ID * In bash, export builtin is special, its arguments are assignments * and therefore expansion of them should be "one-word" expansion: * $ export i=`echo 'a b'` # export has one arg: "i=a b" @@ -5703,7 +5723,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha if (errmsg) goto arith_err; debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len); - if (len >= 0) { /* bash compat: len < 0 is illegal */ + if (len >= 0) { if (beg < 0) { /* negative beg counts from the end */ beg = (arith_t)strlen(val) + beg; @@ -5723,6 +5743,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha } debug_printf_varexp("val:'%s'\n", val); } else +//TODO: in bash, len=-n means strlen()-n #endif /* HUSH_SUBSTR_EXPANSION && FEATURE_SH_MATH */ { die_if_script("malformed ${%s:...}", var); -- cgit v1.2.3-55-g6feb From e32b6503e75d5bcbf8ffff69cafb09523ff2b482 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Mon, 17 Jul 2017 16:46:57 +0200 Subject: hush: support ${VAR:N:-M} function old new delta expand_one_var 1602 1615 +13 builtin_type 114 116 +2 Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/hush.c | 48 ++++++++++++++++-------------- shell/hush_test/hush-vars/var_bash1b.right | 23 ++++++++++++++ shell/hush_test/hush-vars/var_bash1b.tests | 24 +++++++++++++++ 3 files changed, 72 insertions(+), 23 deletions(-) create mode 100644 shell/hush_test/hush-vars/var_bash1b.right create mode 100755 shell/hush_test/hush-vars/var_bash1b.tests (limited to 'shell') diff --git a/shell/hush.c b/shell/hush.c index fd2a3d0f5..836f3b83c 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -5723,32 +5723,34 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha if (errmsg) goto arith_err; debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len); - if (len >= 0) { - if (beg < 0) { - /* negative beg counts from the end */ - beg = (arith_t)strlen(val) + beg; - if (beg < 0) /* ${v: -999999} is "" */ - beg = len = 0; - } - debug_printf_varexp("from val:'%s'\n", val); - if (len == 0 || !val || beg >= strlen(val)) { + if (beg < 0) { + /* negative beg counts from the end */ + beg = (arith_t)strlen(val) + beg; + if (beg < 0) /* ${v: -999999} is "" */ + beg = len = 0; + } + debug_printf_varexp("from val:'%s'\n", val); + if (len < 0) { + /* in bash, len=-n means strlen()-n */ + len = (arith_t)strlen(val) - beg + len; + if (len < 0) /* bash compat */ + die_if_script("%s: substring expression < 0", var); + } + if (len == 0 || !val || beg >= strlen(val)) { arith_err: - val = NULL; - } else { - /* Paranoia. What if user entered 9999999999999 - * which fits in arith_t but not int? */ - if (len >= INT_MAX) - len = INT_MAX; - val = to_be_freed = xstrndup(val + beg, len); - } - debug_printf_varexp("val:'%s'\n", val); - } else -//TODO: in bash, len=-n means strlen()-n -#endif /* HUSH_SUBSTR_EXPANSION && FEATURE_SH_MATH */ - { - die_if_script("malformed ${%s:...}", var); val = NULL; + } else { + /* Paranoia. What if user entered 9999999999999 + * which fits in arith_t but not int? */ + if (len >= INT_MAX) + len = INT_MAX; + val = to_be_freed = xstrndup(val + beg, len); } + debug_printf_varexp("val:'%s'\n", val); +#else /* not (HUSH_SUBSTR_EXPANSION && FEATURE_SH_MATH) */ + die_if_script("malformed ${%s:...}", var); + val = NULL; +#endif } else { /* one of "-=+?" */ /* Standard-mandated substitution ops: * ${var?word} - indicate error if unset diff --git a/shell/hush_test/hush-vars/var_bash1b.right b/shell/hush_test/hush-vars/var_bash1b.right new file mode 100644 index 000000000..fafc0f07c --- /dev/null +++ b/shell/hush_test/hush-vars/var_bash1b.right @@ -0,0 +1,23 @@ +all |0123456 +4: |456 +4:2 |45 +4:-1 |45 +4:-2 |4 +4:-3 | +-4: |3456 +-4:2 |34 +-4:-1 |345 +-4:-2 |34 +-4:-3 |3 +-4:-4 | +-4:i=2 |34 +-4:i=-2|34 +-4:i=-3|3 +-4:i=-4| +-5: |23456 +-6: |123456 +-7: |0123456 +-8: | +-9: | +-9:-99 | +Ok:0 diff --git a/shell/hush_test/hush-vars/var_bash1b.tests b/shell/hush_test/hush-vars/var_bash1b.tests new file mode 100755 index 000000000..efbdef35c --- /dev/null +++ b/shell/hush_test/hush-vars/var_bash1b.tests @@ -0,0 +1,24 @@ +set -- 0123456 + echo "all |"$1 + echo "4: |"${1:4} + echo "4:2 |"${1:4:2} + echo "4:-1 |"${1:4:-1} + echo "4:-2 |"${1:4:-2} + echo "4:-3 |"${1:4:-3} + echo "-4: |"${1: -4} + echo "-4:2 |"${1: -4:2} + echo "-4:-1 |"${1: -4:-1} + echo "-4:-2 |"${1: -4:-2} + echo "-4:-3 |"${1: -4:-3} + echo "-4:-4 |"${1: -4:-4} +i=2; echo "-4:i=2 |"${1: -4:i} +i=-2; echo "-4:i=-2|"${1: -4:i} +i=-3; echo "-4:i=-3|"${1: -4:i} +i=-4; echo "-4:i=-4|"${1: -4:i} + echo "-5: |"${1: -5} + echo "-6: |"${1: -6} + echo "-7: |"${1: -7} + echo "-8: |"${1: -8} + echo "-9: |"${1: -9} + echo "-9:-99 |"${1: -9:-99} +echo Ok:$? -- cgit v1.2.3-55-g6feb From 0ba80e4fa251a1c753e5feaff4b358a427aa58cb Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Mon, 17 Jul 2017 16:50:20 +0200 Subject: hush: small fix to last commit die_if_script() indeed dies only in scripts! Must handle the case where it continues. Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/hush.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'shell') diff --git a/shell/hush.c b/shell/hush.c index 836f3b83c..c8356f4b8 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -5736,7 +5736,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha if (len < 0) /* bash compat */ die_if_script("%s: substring expression < 0", var); } - if (len == 0 || !val || beg >= strlen(val)) { + if (len <= 0 || !val || beg >= strlen(val)) { arith_err: val = NULL; } else { -- cgit v1.2.3-55-g6feb From 4f8079de877cf2b7206e4cabaf465fb7d8cc4f62 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Mon, 17 Jul 2017 17:11:48 +0200 Subject: ash: "you disabled math" is wrong: user did not disable it, builder of ash did Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/ash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'shell') diff --git a/shell/ash.c b/shell/ash.c index 987d618c6..eb5156bea 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -11926,7 +11926,7 @@ parsesub: { #if ENABLE_FEATURE_SH_MATH PARSEARITH(); #else - raise_error_syntax("you disabled math support for $((arith)) syntax"); + raise_error_syntax("support for $((arith)) is disabled"); #endif } else { pungetc(); -- cgit v1.2.3-55-g6feb From 826360ff238eb23191950dd4c5051195eab9733a Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Mon, 17 Jul 2017 17:49:11 +0200 Subject: ash: more general format ${var:EXPR:EXPR} function old new delta subevalvar 1171 1202 +31 localcmd 364 366 +2 Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/ash.c | 43 +++++++++++++++++++------------- shell/ash_test/ash-vars/var_bash1b.right | 23 +++++++++++++++++ shell/ash_test/ash-vars/var_bash1b.tests | 24 ++++++++++++++++++ 3 files changed, 73 insertions(+), 17 deletions(-) create mode 100644 shell/ash_test/ash-vars/var_bash1b.right create mode 100755 shell/ash_test/ash-vars/var_bash1b.tests (limited to 'shell') diff --git a/shell/ash.c b/shell/ash.c index eb5156bea..aaf0561b8 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -6612,7 +6612,6 @@ subevalvar(char *p, char *varname, int strloc, int subtype, char *loc; char *rmesc, *rmescend; char *str; - IF_BASH_SUBSTR(int pos, len, orig_len;) int amount, resetloc; IF_BASH_PATTERN_SUBST(int workloc;) IF_BASH_PATTERN_SUBST(char *repl = NULL;) @@ -6641,14 +6640,23 @@ subevalvar(char *p, char *varname, int strloc, int subtype, /* NOTREACHED */ #if BASH_SUBSTR - case VSSUBSTR: -//TODO: support more general format ${v:EXPR:EXPR}, -// where EXPR follows $(()) rules + case VSSUBSTR: { + int pos, len, orig_len; + char *colon; + loc = str = stackblock() + strloc; + +# if !ENABLE_FEATURE_SH_MATH +# define ash_arith number +# endif /* Read POS in ${var:POS:LEN} */ - pos = atoi(loc); /* number(loc) errors out on "1:4" */ - len = str - startp - 1; + colon = strchr(loc, ':'); + if (colon) *colon = '\0'; + pos = ash_arith(loc); + if (colon) *colon = ':'; + /* Read LEN in ${var:POS:LEN} */ + len = str - startp - 1; /* *loc != '\0', guaranteed by parser */ if (quotes) { char *ptr; @@ -6662,26 +6670,21 @@ subevalvar(char *p, char *varname, int strloc, int subtype, } } orig_len = len; - if (*loc++ == ':') { /* ${var::LEN} */ - len = number(loc); + len = ash_arith(loc); } else { /* Skip POS in ${var:POS:LEN} */ len = orig_len; while (*loc && *loc != ':') { - /* TODO? - * bash complains on: var=qwe; echo ${var:1a:123} - if (!isdigit(*loc)) - ash_msg_and_raise_error(msg_illnum, str); - */ loc++; } if (*loc++ == ':') { - len = number(loc); + len = ash_arith(loc); } -//TODO: number() chokes on "-n". In bash, LEN=-n means strlen()-n } +# undef ash_arith + if (pos < 0) { /* ${VAR:$((-n)):l} starts n chars from the end */ pos = orig_len + pos; @@ -6689,12 +6692,16 @@ subevalvar(char *p, char *varname, int strloc, int subtype, if ((unsigned)pos >= orig_len) { /* apart from obvious ${VAR:999999:l}, * covers ${VAR:$((-9999999)):l} - result is "" - * (bash-compat) + * (bash compat) */ pos = 0; len = 0; } - if (len > (orig_len - pos)) + if (len < 0) { + /* ${VAR:N:-M} sets LEN to strlen()-M */ + len = (orig_len - pos) + len; + } + if ((unsigned)len > (orig_len - pos)) len = orig_len - pos; for (str = startp; pos; str++, pos--) { @@ -6710,6 +6717,7 @@ subevalvar(char *p, char *varname, int strloc, int subtype, amount = loc - expdest; STADJUST(amount, expdest); return loc; + } #endif /* BASH_SUBSTR */ } @@ -6754,6 +6762,7 @@ subevalvar(char *p, char *varname, int strloc, int subtype, #if BASH_PATTERN_SUBST workloc = expdest - (char *)stackblock(); if (subtype == VSREPLACE || subtype == VSREPLACEALL) { + int len; char *idx, *end; if (!repl) { diff --git a/shell/ash_test/ash-vars/var_bash1b.right b/shell/ash_test/ash-vars/var_bash1b.right new file mode 100644 index 000000000..fafc0f07c --- /dev/null +++ b/shell/ash_test/ash-vars/var_bash1b.right @@ -0,0 +1,23 @@ +all |0123456 +4: |456 +4:2 |45 +4:-1 |45 +4:-2 |4 +4:-3 | +-4: |3456 +-4:2 |34 +-4:-1 |345 +-4:-2 |34 +-4:-3 |3 +-4:-4 | +-4:i=2 |34 +-4:i=-2|34 +-4:i=-3|3 +-4:i=-4| +-5: |23456 +-6: |123456 +-7: |0123456 +-8: | +-9: | +-9:-99 | +Ok:0 diff --git a/shell/ash_test/ash-vars/var_bash1b.tests b/shell/ash_test/ash-vars/var_bash1b.tests new file mode 100755 index 000000000..efbdef35c --- /dev/null +++ b/shell/ash_test/ash-vars/var_bash1b.tests @@ -0,0 +1,24 @@ +set -- 0123456 + echo "all |"$1 + echo "4: |"${1:4} + echo "4:2 |"${1:4:2} + echo "4:-1 |"${1:4:-1} + echo "4:-2 |"${1:4:-2} + echo "4:-3 |"${1:4:-3} + echo "-4: |"${1: -4} + echo "-4:2 |"${1: -4:2} + echo "-4:-1 |"${1: -4:-1} + echo "-4:-2 |"${1: -4:-2} + echo "-4:-3 |"${1: -4:-3} + echo "-4:-4 |"${1: -4:-4} +i=2; echo "-4:i=2 |"${1: -4:i} +i=-2; echo "-4:i=-2|"${1: -4:i} +i=-3; echo "-4:i=-3|"${1: -4:i} +i=-4; echo "-4:i=-4|"${1: -4:i} + echo "-5: |"${1: -5} + echo "-6: |"${1: -6} + echo "-7: |"${1: -7} + echo "-8: |"${1: -8} + echo "-9: |"${1: -9} + echo "-9:-99 |"${1: -9:-99} +echo Ok:$? -- cgit v1.2.3-55-g6feb From 1e660422b16510f8bcdb764d632bb1da391c4a35 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Mon, 17 Jul 2017 21:10:50 +0200 Subject: hush: implement "readonly" builtin function old new delta builtin_readonly - 70 +70 helper_export_local 152 174 +22 bltins1 348 360 +12 expand_one_var 1620 1625 +5 builtin_export 168 173 +5 set_pwd_var 36 40 +4 set_local_var 410 414 +4 set_vars_and_save_old 85 88 +3 set_local_var_from_halves 24 27 +3 run_pipe 1551 1554 +3 run_list 1046 1048 +2 builtin_type 116 114 -2 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 10/1 up/down: 133/-2) Total: 131 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/hush.c | 99 ++++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 63 insertions(+), 36 deletions(-) (limited to 'shell') diff --git a/shell/hush.c b/shell/hush.c index c8356f4b8..a68986329 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -48,7 +48,7 @@ * tilde expansion * aliases * builtins mandated by standards we don't support: - * [un]alias, command, fc, getopts, readonly, times: + * [un]alias, command, fc, getopts, times: * command -v CMD: print "/path/to/CMD" * prints "CMD" for builtins * prints "alias ALIAS='EXPANSION'" for aliases @@ -57,8 +57,7 @@ * command [-p] CMD: run CMD, even if a function CMD also exists * (can use this to override standalone shell as well) * -p: use default $PATH - * readonly VAR[=VAL]...: make VARs readonly - * readonly [-p]: list all such VARs (-p has no effect in bash) + * command BLTIN: disables special-ness (e.g. errors do not abort) * getopts: getopt() for shells * times: print getrusage(SELF/CHILDREN).ru_utime/ru_stime * fc -l[nr] [BEG] [END]: list range of commands in history @@ -239,6 +238,12 @@ //config: help //config: export -n unexports variables. It is a bash extension. //config: +//config:config HUSH_READONLY +//config: bool "readonly builtin" +//config: default y +//config: help +//config: Enable support for read-only variables. +//config: //config:config HUSH_KILL //config: bool "kill builtin (supports kill %jobspec)" //config: default y @@ -964,6 +969,9 @@ static int builtin_exit(char **argv) FAST_FUNC; #if ENABLE_HUSH_EXPORT static int builtin_export(char **argv) FAST_FUNC; #endif +#if ENABLE_HUSH_READONLY +static int builtin_readonly(char **argv) FAST_FUNC; +#endif #if ENABLE_HUSH_JOB static int builtin_fg_bg(char **argv) FAST_FUNC; static int builtin_jobs(char **argv) FAST_FUNC; @@ -1082,6 +1090,9 @@ static const struct built_in_command bltins1[] = { #if ENABLE_HUSH_READ BLTIN("read" , builtin_read , "Input into variable"), #endif +#if ENABLE_HUSH_READONLY + BLTIN("readonly" , builtin_readonly, "Make variables read-only"), +#endif #if ENABLE_HUSH_FUNCTIONS BLTIN("return" , builtin_return , "Return from function"), #endif @@ -2052,19 +2063,10 @@ static const char* FAST_FUNC get_local_var_value(const char *name) * -1: clear export flag and unsetenv the variable * flg_read_only is set only when we handle -R var=val */ -#if !BB_MMU && ENABLE_HUSH_LOCAL -/* all params are used */ -#elif BB_MMU && ENABLE_HUSH_LOCAL -#define set_local_var(str, flg_export, local_lvl, flg_read_only) \ - set_local_var(str, flg_export, local_lvl) -#elif BB_MMU && !ENABLE_HUSH_LOCAL -#define set_local_var(str, flg_export, local_lvl, flg_read_only) \ - set_local_var(str, flg_export) -#elif !BB_MMU && !ENABLE_HUSH_LOCAL -#define set_local_var(str, flg_export, local_lvl, flg_read_only) \ - set_local_var(str, flg_export, flg_read_only) -#endif -static int set_local_var(char *str, int flg_export, int local_lvl, int flg_read_only) +static int set_local_var(char *str, + int flg_export UNUSED_PARAM, + int local_lvl UNUSED_PARAM, + int flg_read_only UNUSED_PARAM) { struct variable **var_pp; struct variable *cur; @@ -2088,9 +2090,7 @@ static int set_local_var(char *str, int flg_export, int local_lvl, int flg_read_ /* We found an existing var with this name */ if (cur->flg_read_only) { -#if !BB_MMU if (!flg_read_only) -#endif bb_error_msg("%s: readonly variable", str); free(str); return -1; @@ -2158,10 +2158,12 @@ static int set_local_var(char *str, int flg_export, int local_lvl, int flg_read_ set_str_and_exp: cur->varstr = str; -#if !BB_MMU - cur->flg_read_only = flg_read_only; -#endif exp: +#if !BB_MMU || ENABLE_HUSH_READONLY + if (flg_read_only != 0) { + cur->flg_read_only = flg_read_only; + } +#endif if (flg_export == 1) cur->flg_export = 1; if (name_len == 4 && cur->varstr[0] == 'P' && cur->varstr[1] == 'S') @@ -9308,12 +9310,11 @@ static void print_escaped(const char *s) } #endif -#if ENABLE_HUSH_EXPORT || ENABLE_HUSH_LOCAL -# if !ENABLE_HUSH_LOCAL -#define helper_export_local(argv, exp, lvl) \ - helper_export_local(argv, exp) -# endif -static void helper_export_local(char **argv, int exp, int lvl) +#if ENABLE_HUSH_EXPORT || ENABLE_HUSH_LOCAL || ENABLE_HUSH_READONLY +static int helper_export_local(char **argv, + int exp UNUSED_PARAM, + int ro UNUSED_PARAM, + int lvl UNUSED_PARAM) { do { char *name = *argv; @@ -9346,7 +9347,7 @@ static void helper_export_local(char **argv, int exp, int lvl) } } # if ENABLE_HUSH_LOCAL - if (exp == 0 /* local? */ + if (exp == 0 && ro == 0 /* local? */ && var && var->func_nest_level == lvl ) { /* "local x=abc; ...; local x" - ignore second local decl */ @@ -9357,16 +9358,23 @@ static void helper_export_local(char **argv, int exp, int lvl) * bash does not put it in environment, * but remembers that it is exported, * and does put it in env when it is set later. - * We just set it to "" and export. */ + * We just set it to "" and export. + */ /* Or, it's "local NAME" (without =VALUE). - * bash sets the value to "". */ + * bash sets the value to "". + */ + /* Or, it's "readonly NAME" (without =VALUE). + * bash remembers NAME and disallows its creation + * in the future. + */ name = xasprintf("%s=", name); } else { /* (Un)exporting/making local NAME=VALUE */ name = xstrdup(name); } - set_local_var(name, /*exp:*/ exp, /*lvl:*/ lvl, /*ro:*/ 0); + set_local_var(name, /*exp:*/ exp, /*lvl:*/ lvl, /*ro:*/ ro); } while (*++argv); + return EXIT_SUCCESS; } #endif @@ -9412,9 +9420,7 @@ static int FAST_FUNC builtin_export(char **argv) return EXIT_SUCCESS; } - helper_export_local(argv, (opt_unexport ? -1 : 1), 0); - - return EXIT_SUCCESS; + return helper_export_local(argv, /*exp:*/ (opt_unexport ? -1 : 1), /*ro:*/ 0, /*lvl:*/ 0); } #endif @@ -9425,11 +9431,32 @@ static int FAST_FUNC builtin_local(char **argv) bb_error_msg("%s: not in a function", argv[0]); return EXIT_FAILURE; /* bash compat */ } - helper_export_local(argv, 0, G.func_nest_level); - return EXIT_SUCCESS; + argv++; + return helper_export_local(argv, /*exp:*/ 0, /*ro:*/ 0, /*lvl:*/ G.func_nest_level); } #endif +#if ENABLE_HUSH_READONLY +static int FAST_FUNC builtin_readonly(char **argv) +{ + if (*++argv == NULL) { + /* bash: readonly [-p]: list all readonly VARs + * (-p has no effect in bash) + */ + struct variable *e; + for (e = G.top_var; e; e = e->next) { + if (e->flg_read_only) { +//TODO: quote value: readonly VAR='VAL' + printf("readonly %s\n", e->varstr); + } + } + return EXIT_SUCCESS; + } + return helper_export_local(argv, /*exp:*/ 0, /*ro:*/ 1, /*lvl:*/ 0); +} +#endif + + #if ENABLE_HUSH_UNSET /* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#unset */ static int FAST_FUNC builtin_unset(char **argv) -- cgit v1.2.3-55-g6feb From b95ee96e7528554fc3ee2c48fbd59d75c59148db Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Mon, 17 Jul 2017 21:19:53 +0200 Subject: hush: smaller code in !READONLY configs Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/hush.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'shell') diff --git a/shell/hush.c b/shell/hush.c index a68986329..2125e757d 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -9347,11 +9347,14 @@ static int helper_export_local(char **argv, } } # if ENABLE_HUSH_LOCAL - if (exp == 0 && ro == 0 /* local? */ - && var && var->func_nest_level == lvl + /* Is this "local" bltin? */ + if (exp == 0 + IF_HUSH_READONLY(&& ro == 0) /* in !READONLY config, always true */ ) { - /* "local x=abc; ...; local x" - ignore second local decl */ - continue; + if (var && var->func_nest_level == lvl) { + /* "local x=abc; ...; local x" - ignore second local decl */ + continue; + } } # endif /* Exporting non-existing variable. -- cgit v1.2.3-55-g6feb From 6b48e1f1212300464c17c8317f5faddf6b414a4c Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Mon, 17 Jul 2017 21:31:17 +0200 Subject: hush: forgot to emit error on (failing) second "readonly VAR=VAL" Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/hush.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'shell') diff --git a/shell/hush.c b/shell/hush.c index 2125e757d..1961c9830 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -2074,6 +2074,7 @@ static int set_local_var(char *str, char *eq_sign; int name_len; + //bb_error_msg("set_local_var('%s',%d,%d,%d)", str, flg_export, local_lvl, flg_read_only); eq_sign = strchr(str, '='); if (!eq_sign) { /* not expected to ever happen? */ free(str); @@ -2090,8 +2091,7 @@ static int set_local_var(char *str, /* We found an existing var with this name */ if (cur->flg_read_only) { - if (!flg_read_only) - bb_error_msg("%s: readonly variable", str); + bb_error_msg("%s: readonly variable", str); free(str); return -1; } @@ -9459,7 +9459,6 @@ static int FAST_FUNC builtin_readonly(char **argv) } #endif - #if ENABLE_HUSH_UNSET /* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#unset */ static int FAST_FUNC builtin_unset(char **argv) -- cgit v1.2.3-55-g6feb From 6b0695bb66dd38af4d4671fb2381fa3e1dbfe90c Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Mon, 17 Jul 2017 21:47:27 +0200 Subject: hush: HUSH_READONLY depends on HUSH Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/hush.c | 1 + 1 file changed, 1 insertion(+) (limited to 'shell') diff --git a/shell/hush.c b/shell/hush.c index 1961c9830..4b71344da 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -241,6 +241,7 @@ //config:config HUSH_READONLY //config: bool "readonly builtin" //config: default y +//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH //config: help //config: Enable support for read-only variables. //config: -- cgit v1.2.3-55-g6feb From 3bab36b18baa0dc254445828f492051450a38d41 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Tue, 18 Jul 2017 01:05:24 +0200 Subject: hush: convert exp/ro/local parameters to bitfields in one flag param function old new delta helper_export_local 174 185 +11 set_local_var 424 420 -4 run_list 1048 1044 -4 set_vars_and_save_old 88 83 -5 set_local_var_from_halves 27 22 -5 run_pipe 1554 1549 -5 builtin_export 173 168 -5 set_pwd_var 40 34 -6 builtin_readonly 70 64 -6 expand_one_var 1625 1618 -7 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/9 up/down: 11/-47) Total: -36 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/hush.c | 81 ++++++++++++++++++++++++++---------------------------------- 1 file changed, 35 insertions(+), 46 deletions(-) (limited to 'shell') diff --git a/shell/hush.c b/shell/hush.c index 4b71344da..7771172b6 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -2057,25 +2057,20 @@ static const char* FAST_FUNC get_local_var_value(const char *name) /* str holds "NAME=VAL" and is expected to be malloced. * We take ownership of it. - * flg_export: - * 0: do not change export flag - * (if creating new variable, flag will be 0) - * 1: set export flag and putenv the variable - * -1: clear export flag and unsetenv the variable - * flg_read_only is set only when we handle -R var=val */ -static int set_local_var(char *str, - int flg_export UNUSED_PARAM, - int local_lvl UNUSED_PARAM, - int flg_read_only UNUSED_PARAM) +#define SETFLAG_EXPORT (1 << 0) +#define SETFLAG_UNEXPORT (1 << 1) +#define SETFLAG_MAKE_RO (1 << 2) +#define SETFLAG_LOCAL_SHIFT 3 +static int set_local_var(char *str, unsigned flags) { struct variable **var_pp; struct variable *cur; char *free_me = NULL; char *eq_sign; int name_len; + IF_HUSH_LOCAL(unsigned local_lvl = (flags >> SETFLAG_LOCAL_SHIFT);) - //bb_error_msg("set_local_var('%s',%d,%d,%d)", str, flg_export, local_lvl, flg_read_only); eq_sign = strchr(str, '='); if (!eq_sign) { /* not expected to ever happen? */ free(str); @@ -2096,7 +2091,7 @@ static int set_local_var(char *str, free(str); return -1; } - if (flg_export == -1) { // "&& cur->flg_export" ? + if (flags & SETFLAG_UNEXPORT) { // && cur->flg_export ? debug_printf_env("%s: unsetenv '%s'\n", __func__, str); *eq_sign = '\0'; unsetenv(str); @@ -2120,7 +2115,7 @@ static int set_local_var(char *str, * z=z */ if (cur->flg_export) - flg_export = 1; + flags |= SETFLAG_EXPORT; break; } #endif @@ -2151,9 +2146,7 @@ static int set_local_var(char *str, /* Not found - create new variable struct */ cur = xzalloc(sizeof(*cur)); -#if ENABLE_HUSH_LOCAL - cur->func_nest_level = local_lvl; -#endif + IF_HUSH_LOCAL(cur->func_nest_level = local_lvl;) cur->next = *var_pp; *var_pp = cur; @@ -2161,16 +2154,16 @@ static int set_local_var(char *str, cur->varstr = str; exp: #if !BB_MMU || ENABLE_HUSH_READONLY - if (flg_read_only != 0) { - cur->flg_read_only = flg_read_only; + if (flags & SETFLAG_MAKE_RO) { + cur->flg_read_only = 1; } #endif - if (flg_export == 1) + if (flags & SETFLAG_EXPORT) cur->flg_export = 1; if (name_len == 4 && cur->varstr[0] == 'P' && cur->varstr[1] == 'S') cmdedit_update_prompt(); if (cur->flg_export) { - if (flg_export == -1) { + if (flags & SETFLAG_UNEXPORT) { cur->flg_export = 0; /* unsetenv was already done */ } else { @@ -2187,10 +2180,9 @@ static int set_local_var(char *str, } /* Used at startup and after each cd */ -static void set_pwd_var(int exp) +static void set_pwd_var(unsigned flag) { - set_local_var(xasprintf("PWD=%s", get_cwd(/*force:*/ 1)), - /*exp:*/ exp, /*lvl:*/ 0, /*ro:*/ 0); + set_local_var(xasprintf("PWD=%s", get_cwd(/*force:*/ 1)), flag); } static int unset_local_var_len(const char *name, int name_len) @@ -2248,7 +2240,7 @@ static void unset_vars(char **strings) static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val) { char *var = xasprintf("%s=%s", name, val); - set_local_var(var, /*flags:*/ 0, /*lvl:*/ 0, /*ro:*/ 0); + set_local_var(var, /*flag:*/ 0); } #endif @@ -2299,7 +2291,7 @@ static struct variable *set_vars_and_save_old(char **strings) var_p->next = old; old = var_p; } - set_local_var(*s, /*exp:*/ 1, /*lvl:*/ 0, /*ro:*/ 0); + set_local_var(*s, SETFLAG_EXPORT); } s++; } @@ -5805,7 +5797,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha val = NULL; } else { char *new_var = xasprintf("%s=%s", var, val); - set_local_var(new_var, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0); + set_local_var(new_var, /*flag:*/ 0); } } } @@ -7775,7 +7767,7 @@ static NOINLINE int run_pipe(struct pipe *pi) if (new_env) { argv = new_env; while (*argv) { - set_local_var(*argv, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0); + set_local_var(*argv, /*flag:*/ 0); /* Do we need to flag set_local_var() errors? * "assignment to readonly var" and "putenv error" */ @@ -7803,7 +7795,7 @@ static NOINLINE int run_pipe(struct pipe *pi) fprintf(stderr, " %s", p); debug_printf_exec("set shell var:'%s'->'%s'\n", *argv, p); - set_local_var(p, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0); + set_local_var(p, /*flag:*/ 0); /* Do we need to flag set_local_var() errors? * "assignment to readonly var" and "putenv error" */ @@ -8215,7 +8207,7 @@ static int run_list(struct pipe *pi) } /* Insert next value from for_lcur */ /* note: *for_lcur already has quotes removed, $var expanded, etc */ - set_local_var(xasprintf("%s=%s", pi->cmds[0].argv[0], *for_lcur++), /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0); + set_local_var(xasprintf("%s=%s", pi->cmds[0].argv[0], *for_lcur++), /*flag:*/ 0); continue; } if (rword == RES_IN) { @@ -8600,7 +8592,7 @@ int hush_main(int argc, char **argv) putenv(shell_ver->varstr); /* Export PWD */ - set_pwd_var(/*exp:*/ 1); + set_pwd_var(SETFLAG_EXPORT); #if BASH_HOSTNAME_VAR /* Set (but not export) HOSTNAME unless already set */ @@ -8770,7 +8762,7 @@ int hush_main(int argc, char **argv) } case 'R': case 'V': - set_local_var(xstrdup(optarg), /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ opt == 'R'); + set_local_var(xstrdup(optarg), opt == 'R' ? SETFLAG_MAKE_RO : 0); break; # if ENABLE_HUSH_FUNCTIONS case 'F': { @@ -9073,7 +9065,7 @@ static int FAST_FUNC builtin_cd(char **argv) * Note: do not enforce exporting. If PWD was unset or unexported, * set it again, but do not export. bash does the same. */ - set_pwd_var(/*exp:*/ 0); + set_pwd_var(/*flag:*/ 0); return EXIT_SUCCESS; } @@ -9312,10 +9304,7 @@ static void print_escaped(const char *s) #endif #if ENABLE_HUSH_EXPORT || ENABLE_HUSH_LOCAL || ENABLE_HUSH_READONLY -static int helper_export_local(char **argv, - int exp UNUSED_PARAM, - int ro UNUSED_PARAM, - int lvl UNUSED_PARAM) +static int helper_export_local(char **argv, unsigned flags) { do { char *name = *argv; @@ -9329,7 +9318,7 @@ static int helper_export_local(char **argv, vpp = get_ptr_to_local_var(name, name_end - name); var = vpp ? *vpp : NULL; - if (exp == -1) { /* unexporting? */ + if (flags & SETFLAG_UNEXPORT) { /* export -n NAME (without =VALUE) */ if (var) { var->flg_export = 0; @@ -9338,7 +9327,7 @@ static int helper_export_local(char **argv, } /* else: export -n NOT_EXISTING_VAR: no-op */ continue; } - if (exp == 1) { /* exporting? */ + if (flags & SETFLAG_EXPORT) { /* export NAME (without =VALUE) */ if (var) { var->flg_export = 1; @@ -9349,9 +9338,8 @@ static int helper_export_local(char **argv, } # if ENABLE_HUSH_LOCAL /* Is this "local" bltin? */ - if (exp == 0 - IF_HUSH_READONLY(&& ro == 0) /* in !READONLY config, always true */ - ) { + if (!(flags & (SETFLAG_EXPORT|SETFLAG_UNEXPORT|SETFLAG_MAKE_RO))) { + unsigned lvl = flags >> SETFLAG_LOCAL_SHIFT; if (var && var->func_nest_level == lvl) { /* "local x=abc; ...; local x" - ignore second local decl */ continue; @@ -9376,7 +9364,7 @@ static int helper_export_local(char **argv, /* (Un)exporting/making local NAME=VALUE */ name = xstrdup(name); } - set_local_var(name, /*exp:*/ exp, /*lvl:*/ lvl, /*ro:*/ ro); + set_local_var(name, flags); } while (*++argv); return EXIT_SUCCESS; } @@ -9424,7 +9412,7 @@ static int FAST_FUNC builtin_export(char **argv) return EXIT_SUCCESS; } - return helper_export_local(argv, /*exp:*/ (opt_unexport ? -1 : 1), /*ro:*/ 0, /*lvl:*/ 0); + return helper_export_local(argv, opt_unexport ? SETFLAG_UNEXPORT : SETFLAG_EXPORT); } #endif @@ -9436,14 +9424,15 @@ static int FAST_FUNC builtin_local(char **argv) return EXIT_FAILURE; /* bash compat */ } argv++; - return helper_export_local(argv, /*exp:*/ 0, /*ro:*/ 0, /*lvl:*/ G.func_nest_level); + return helper_export_local(argv, G.func_nest_level << SETFLAG_LOCAL_SHIFT); } #endif #if ENABLE_HUSH_READONLY static int FAST_FUNC builtin_readonly(char **argv) { - if (*++argv == NULL) { + argv++; + if (*argv == NULL) { /* bash: readonly [-p]: list all readonly VARs * (-p has no effect in bash) */ @@ -9456,7 +9445,7 @@ static int FAST_FUNC builtin_readonly(char **argv) } return EXIT_SUCCESS; } - return helper_export_local(argv, /*exp:*/ 0, /*ro:*/ 1, /*lvl:*/ 0); + return helper_export_local(argv, SETFLAG_MAKE_RO); } #endif -- cgit v1.2.3-55-g6feb From 38ef39a1abd46ca390b0259ebd0b35e9ea9ccb68 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Tue, 18 Jul 2017 01:40:01 +0200 Subject: hush: add readonly testcase, fix fallout function old new delta helper_export_local 185 214 +29 run_pipe 1549 1560 +11 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/0 up/down: 40/0) Total: 40 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/hush.c | 26 +++++++++++++++++--------- shell/hush_test/hush-vars/readonly0.right | 12 ++++++++++++ shell/hush_test/hush-vars/readonly0.tests | 24 ++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 9 deletions(-) create mode 100644 shell/hush_test/hush-vars/readonly0.right create mode 100755 shell/hush_test/hush-vars/readonly0.tests (limited to 'shell') diff --git a/shell/hush.c b/shell/hush.c index 7771172b6..eab1284f6 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -7767,10 +7767,10 @@ static NOINLINE int run_pipe(struct pipe *pi) if (new_env) { argv = new_env; while (*argv) { - set_local_var(*argv, /*flag:*/ 0); - /* Do we need to flag set_local_var() errors? - * "assignment to readonly var" and "putenv error" - */ + if (set_local_var(*argv, /*flag:*/ 0)) { + /* assignment to readonly var / putenv error? */ + rcode = 1; + } argv++; } } @@ -7795,10 +7795,10 @@ static NOINLINE int run_pipe(struct pipe *pi) fprintf(stderr, " %s", p); debug_printf_exec("set shell var:'%s'->'%s'\n", *argv, p); - set_local_var(p, /*flag:*/ 0); - /* Do we need to flag set_local_var() errors? - * "assignment to readonly var" and "putenv error" - */ + if (set_local_var(p, /*flag:*/ 0)) { + /* assignment to readonly var / putenv error? */ + rcode = 1; + } argv++; } if (G_x_mode) @@ -9336,6 +9336,13 @@ static int helper_export_local(char **argv, unsigned flags) continue; } } + if (flags & SETFLAG_MAKE_RO) { + /* readonly NAME (without =VALUE) */ + if (var) { + var->flg_read_only = 1; + continue; + } + } # if ENABLE_HUSH_LOCAL /* Is this "local" bltin? */ if (!(flags & (SETFLAG_EXPORT|SETFLAG_UNEXPORT|SETFLAG_MAKE_RO))) { @@ -9364,7 +9371,8 @@ static int helper_export_local(char **argv, unsigned flags) /* (Un)exporting/making local NAME=VALUE */ name = xstrdup(name); } - set_local_var(name, flags); + if (set_local_var(name, flags)) + return EXIT_FAILURE; } while (*++argv); return EXIT_SUCCESS; } diff --git a/shell/hush_test/hush-vars/readonly0.right b/shell/hush_test/hush-vars/readonly0.right new file mode 100644 index 000000000..9688d2e5f --- /dev/null +++ b/shell/hush_test/hush-vars/readonly0.right @@ -0,0 +1,12 @@ +readonly a=A +readonly b=B +Ok:0 +hush: a=A: readonly variable +Fail:1 +hush: a=A: readonly variable +Fail:1 +hush: a=A: readonly variable +Fail:1 +Visible:0 +hush: a: readonly variable +Fail:1 diff --git a/shell/hush_test/hush-vars/readonly0.tests b/shell/hush_test/hush-vars/readonly0.tests new file mode 100755 index 000000000..3845f76ac --- /dev/null +++ b/shell/hush_test/hush-vars/readonly0.tests @@ -0,0 +1,24 @@ +readonly a=A +b=B +readonly b +# readonly on already readonly var is harmless +readonly b a +readonly | grep '^readonly [ab]=' +# this should work +export a b +export -n a b +echo Ok:$? +env | grep -e^a= -e^b= # shows nothing + +# these should all fail (despite the same value being assigned) +# bash does not abort even in non-interactive more (in script) +true +a=A +echo Fail:$?; true +readonly a=A +echo Fail:$?; true +export a=A +echo Fail:$?; true +a=A echo Visible:$? # command still runs +unset a +echo Fail:$?; true -- cgit v1.2.3-55-g6feb From 5b2cc0aaee6985431d9bab1b49ceea7e1fa1d7af Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Tue, 18 Jul 2017 02:44:06 +0200 Subject: hush: do not assign to readonly VAR in "VAR=VAL CMD" syntax too Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/hush.c | 9 ++++++++- shell/hush_test/hush-vars/readonly0.right | 6 ++++-- shell/hush_test/hush-vars/readonly0.tests | 30 +++++++++++++++++++++--------- 3 files changed, 33 insertions(+), 12 deletions(-) (limited to 'shell') diff --git a/shell/hush.c b/shell/hush.c index eab1284f6..55e581e16 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -2089,6 +2089,8 @@ static int set_local_var(char *str, unsigned flags) if (cur->flg_read_only) { bb_error_msg("%s: readonly variable", str); free(str); +//NOTE: in bash, assignment in "export READONLY_VAR=Z" fails, and sets $?=1, +//but export per se succeeds (does put the var in env). We don't mimic that. return -1; } if (flags & SETFLAG_UNEXPORT) { // && cur->flg_export ? @@ -2283,8 +2285,12 @@ static struct variable *set_vars_and_save_old(char **strings) if (eq) { var_pp = get_ptr_to_local_var(*s, eq - *s); if (var_pp) { - /* Remove variable from global linked list */ var_p = *var_pp; + if (var_p->flg_read_only) { + bb_error_msg("%s: readonly variable", *s); + goto next; + } + /* Remove variable from global linked list */ debug_printf_env("%s: removing '%s'\n", __func__, var_p->varstr); *var_pp = var_p->next; /* Add it to returned list */ @@ -2293,6 +2299,7 @@ static struct variable *set_vars_and_save_old(char **strings) } set_local_var(*s, SETFLAG_EXPORT); } + next: s++; } return old; diff --git a/shell/hush_test/hush-vars/readonly0.right b/shell/hush_test/hush-vars/readonly0.right index 9688d2e5f..07ca6e07f 100644 --- a/shell/hush_test/hush-vars/readonly0.right +++ b/shell/hush_test/hush-vars/readonly0.right @@ -5,8 +5,10 @@ hush: a=A: readonly variable Fail:1 hush: a=A: readonly variable Fail:1 -hush: a=A: readonly variable +hush: a=Z: readonly variable Fail:1 -Visible:0 +hush: a=Z: readonly variable +b=B +^^^a is not exported hush: a: readonly variable Fail:1 diff --git a/shell/hush_test/hush-vars/readonly0.tests b/shell/hush_test/hush-vars/readonly0.tests index 3845f76ac..3ace9b767 100755 --- a/shell/hush_test/hush-vars/readonly0.tests +++ b/shell/hush_test/hush-vars/readonly0.tests @@ -1,10 +1,12 @@ +unset a b + readonly a=A b=B readonly b -# readonly on already readonly var is harmless +# readonly on already readonly var is harmless: readonly b a readonly | grep '^readonly [ab]=' -# this should work +# this should work: export a b export -n a b echo Ok:$? @@ -12,13 +14,23 @@ env | grep -e^a= -e^b= # shows nothing # these should all fail (despite the same value being assigned) # bash does not abort even in non-interactive more (in script) -true -a=A -echo Fail:$?; true -readonly a=A -echo Fail:$?; true -export a=A +true; a=A +echo Fail:$? +true; readonly a=A +echo Fail:$? + +# in bash, assignment in export fails, but export succeeds! :) +# we don't mimic that! +true; export a=Z echo Fail:$?; true -a=A echo Visible:$? # command still runs +#env | grep '^a=' +#echo "^^^a is exported" +export -n a # undo that bashism, if it happens + +export b +# this fails to both set and export a: +a=Z env | grep '^[ab]=' # command still runs +echo "^^^a is not exported" + unset a echo Fail:$?; true -- cgit v1.2.3-55-g6feb From f645e1573c5521c87b972400f9b4abc3636983d4 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Tue, 18 Jul 2017 03:23:07 +0200 Subject: hush: another testcase for "READONLY_VAR=VAL BLTIN ..." Currently fails. Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/hush_test/hush-vars/readonly0.right | 6 ++++++ shell/hush_test/hush-vars/readonly0.tests | 16 +++++++++++----- shell/hush_test/hush-vars/readonly2.right | 4 ++++ shell/hush_test/hush-vars/readonly2.tests | 6 ++++++ 4 files changed, 27 insertions(+), 5 deletions(-) create mode 100644 shell/hush_test/hush-vars/readonly2.right create mode 100755 shell/hush_test/hush-vars/readonly2.tests (limited to 'shell') diff --git a/shell/hush_test/hush-vars/readonly0.right b/shell/hush_test/hush-vars/readonly0.right index 07ca6e07f..8b750eb5f 100644 --- a/shell/hush_test/hush-vars/readonly0.right +++ b/shell/hush_test/hush-vars/readonly0.right @@ -1,14 +1,20 @@ readonly a=A readonly b=B Ok:0 + hush: a=A: readonly variable Fail:1 hush: a=A: readonly variable Fail:1 + hush: a=Z: readonly variable Fail:1 + hush: a=Z: readonly variable b=B ^^^a is not exported +hush: a=Z: readonly variable +Visible:42 + hush: a: readonly variable Fail:1 diff --git a/shell/hush_test/hush-vars/readonly0.tests b/shell/hush_test/hush-vars/readonly0.tests index 3ace9b767..0833ccf29 100755 --- a/shell/hush_test/hush-vars/readonly0.tests +++ b/shell/hush_test/hush-vars/readonly0.tests @@ -1,5 +1,5 @@ unset a b - +# readonly a=A b=B readonly b @@ -12,6 +12,7 @@ export -n a b echo Ok:$? env | grep -e^a= -e^b= # shows nothing +echo # these should all fail (despite the same value being assigned) # bash does not abort even in non-interactive more (in script) true; a=A @@ -19,18 +20,23 @@ echo Fail:$? true; readonly a=A echo Fail:$? +echo # in bash, assignment in export fails, but export succeeds! :) # we don't mimic that! true; export a=Z -echo Fail:$?; true +echo Fail:$? #env | grep '^a=' #echo "^^^a is exported" export -n a # undo that bashism, if it happens +echo export b # this fails to both set and export a: -a=Z env | grep '^[ab]=' # command still runs +a=Z env | grep '^[ab]=' echo "^^^a is not exported" +# but external command does get executed, and $? is not mangled (stays 42): +(exit 42); a=Z env echo Visible:$? -unset a -echo Fail:$?; true +echo +true; unset a +echo Fail:$? diff --git a/shell/hush_test/hush-vars/readonly2.right b/shell/hush_test/hush-vars/readonly2.right new file mode 100644 index 000000000..5b02ddfe8 --- /dev/null +++ b/shell/hush_test/hush-vars/readonly2.right @@ -0,0 +1,4 @@ +hush: a=Z: readonly variable +Visible:42 +hush: a=Z: readonly variable +Visible:42 diff --git a/shell/hush_test/hush-vars/readonly2.tests b/shell/hush_test/hush-vars/readonly2.tests new file mode 100755 index 000000000..d9d178edd --- /dev/null +++ b/shell/hush_test/hush-vars/readonly2.tests @@ -0,0 +1,6 @@ +unset a +readonly a=A + +# external commands and builtins should behave the same: +(exit 42); a=Z echo "Visible:$?" +(exit 42); a=Z env echo "Visible:$?" -- cgit v1.2.3-55-g6feb From cf5110978ba25002ec5cb46aaae2472eb66001ac Mon Sep 17 00:00:00 2001 From: Denys Vlasenko <vda.linux@googlemail.com> Date: Tue, 18 Jul 2017 15:58:02 +0200 Subject: hush: fix readonly2.tests failure Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> --- shell/hush.c | 11 +++++++++++ shell/hush_test/hush-vars/readonly2.right | 1 + shell/hush_test/hush-vars/readonly2.tests | 1 + 3 files changed, 13 insertions(+) (limited to 'shell') diff --git a/shell/hush.c b/shell/hush.c index 55e581e16..f6da826d3 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -2287,7 +2287,18 @@ static struct variable *set_vars_and_save_old(char **strings) if (var_pp) { var_p = *var_pp; if (var_p->flg_read_only) { + char **p; bb_error_msg("%s: readonly variable", *s); + /* + * "VAR=V BLTIN" unsets VARs after BLTIN completes. + * If VAR is readonly, leaving it in the list + * after asssignment error (msg above) + * causes doubled error message later, on unset. + */ + debug_printf_env("removing/freeing '%s' element\n", *s); + free(*s); + p = s; + do { *p = p[1]; p++; } while (*p); goto next; } /* Remove variable from global linked list */ diff --git a/shell/hush_test/hush-vars/readonly2.right b/shell/hush_test/hush-vars/readonly2.right index 5b02ddfe8..38551d4ad 100644 --- a/shell/hush_test/hush-vars/readonly2.right +++ b/shell/hush_test/hush-vars/readonly2.right @@ -1,4 +1,5 @@ hush: a=Z: readonly variable Visible:42 + hush: a=Z: readonly variable Visible:42 diff --git a/shell/hush_test/hush-vars/readonly2.tests b/shell/hush_test/hush-vars/readonly2.tests index d9d178edd..b758d9602 100755 --- a/shell/hush_test/hush-vars/readonly2.tests +++ b/shell/hush_test/hush-vars/readonly2.tests @@ -3,4 +3,5 @@ readonly a=A # external commands and builtins should behave the same: (exit 42); a=Z echo "Visible:$?" +echo (exit 42); a=Z env echo "Visible:$?" -- cgit v1.2.3-55-g6feb