aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2017-09-27 10:08:12 +0100
committerRon Yorston <rmy@pobox.com>2017-09-27 10:11:19 +0100
commitd9383e984da8de72e61e5094a3cf6404c5707ddc (patch)
treedd42825854fc42aea40d4f7a95548d53721d1733 /shell
parent166b3e4e82799f87d3b002c7177891111eff079e (diff)
parent0c4dbd481aedb5d22c1048e7f7eb547a3b5e50a5 (diff)
downloadbusybox-w32-d9383e984da8de72e61e5094a3cf6404c5707ddc.tar.gz
busybox-w32-d9383e984da8de72e61e5094a3cf6404c5707ddc.tar.bz2
busybox-w32-d9383e984da8de72e61e5094a3cf6404c5707ddc.zip
Merge branch 'busybox' into merge
Diffstat (limited to 'shell')
-rw-r--r--shell/ash.c14
-rw-r--r--shell/ash_test/ash-getopts/getopt_nested.right14
-rwxr-xr-xshell/ash_test/ash-getopts/getopt_nested.tests21
-rw-r--r--shell/cttyhack.c12
-rw-r--r--shell/hush.c80
-rw-r--r--shell/hush_test/hush-getopts/getopt_nested.right14
-rwxr-xr-xshell/hush_test/hush-getopts/getopt_nested.tests21
-rwxr-xr-xshell/hush_test/hush-vars/unset.tests6
-rw-r--r--shell/shell_common.c4
9 files changed, 156 insertions, 30 deletions
diff --git a/shell/ash.c b/shell/ash.c
index 639f5396a..0d65a225b 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -2957,7 +2957,7 @@ cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2957 goto docd; 2957 goto docd;
2958 2958
2959 err: 2959 err:
2960 ash_msg_and_raise_error("can't cd to %s", dest); 2960 ash_msg_and_raise_perror("can't cd to %s", dest);
2961 /* NOTREACHED */ 2961 /* NOTREACHED */
2962 out: 2962 out:
2963 if (flags & CD_PRINT) 2963 if (flags & CD_PRINT)
@@ -5313,7 +5313,7 @@ forkchild(struct job *jp, union node *n, int mode)
5313 if (jp->nprocs == 0) { 5313 if (jp->nprocs == 0) {
5314 close(0); 5314 close(0);
5315 if (open(bb_dev_null, O_RDONLY) != 0) 5315 if (open(bb_dev_null, O_RDONLY) != 0)
5316 ash_msg_and_raise_error("can't open '%s'", bb_dev_null); 5316 ash_msg_and_raise_perror("can't open '%s'", bb_dev_null);
5317 } 5317 }
5318 } 5318 }
5319 if (oldlvl == 0) { 5319 if (oldlvl == 0) {
@@ -5414,7 +5414,7 @@ forkshell(struct job *jp, union node *n, int mode)
5414 TRACE(("Fork failed, errno=%d", errno)); 5414 TRACE(("Fork failed, errno=%d", errno));
5415 if (jp) 5415 if (jp)
5416 freejob(jp); 5416 freejob(jp);
5417 ash_msg_and_raise_error("can't fork"); 5417 ash_msg_and_raise_perror("can't fork");
5418 } 5418 }
5419 if (pid == 0) { 5419 if (pid == 0) {
5420 CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */ 5420 CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */
@@ -5559,7 +5559,7 @@ openhere(union node *redir)
5559 IF_PLATFORM_MINGW32(struct forkshell fs); 5559 IF_PLATFORM_MINGW32(struct forkshell fs);
5560 5560
5561 if (pipe(pip) < 0) 5561 if (pipe(pip) < 0)
5562 ash_msg_and_raise_error("pipe call failed"); 5562 ash_msg_and_raise_perror("can't create pipe");
5563 if (redir->type == NHERE) { 5563 if (redir->type == NHERE) {
5564 len = strlen(redir->nhere.doc->narg.text); 5564 len = strlen(redir->nhere.doc->narg.text);
5565 if (len <= PIPE_BUF) { 5565 if (len <= PIPE_BUF) {
@@ -6661,7 +6661,7 @@ evalbackcmd(union node *n, struct backcmd *result)
6661 } 6661 }
6662 6662
6663 if (pipe(pip) < 0) 6663 if (pipe(pip) < 0)
6664 ash_msg_and_raise_error("pipe call failed"); 6664 ash_msg_and_raise_perror("can't create pipe");
6665 jp = makejob(/*n,*/ 1); 6665 jp = makejob(/*n,*/ 1);
6666#if ENABLE_PLATFORM_MINGW32 6666#if ENABLE_PLATFORM_MINGW32
6667 result->fs.fpid = FS_EVALBACKCMD; 6667 result->fs.fpid = FS_EVALBACKCMD;
@@ -9625,7 +9625,7 @@ evalpipe(union node *n, int flags)
9625 if (lp->next) { 9625 if (lp->next) {
9626 if (pipe(pip) < 0) { 9626 if (pipe(pip) < 0) {
9627 close(prevfd); 9627 close(prevfd);
9628 ash_msg_and_raise_error("pipe call failed"); 9628 ash_msg_and_raise_perror("can't create pipe");
9629 } 9629 }
9630 } 9630 }
9631#if ENABLE_PLATFORM_MINGW32 9631#if ENABLE_PLATFORM_MINGW32
@@ -11058,7 +11058,7 @@ setinputfile(const char *fname, int flags)
11058 if (flags & INPUT_NOFILE_OK) 11058 if (flags & INPUT_NOFILE_OK)
11059 goto out; 11059 goto out;
11060 exitstatus = 127; 11060 exitstatus = 127;
11061 ash_msg_and_raise_error("can't open '%s'", fname); 11061 ash_msg_and_raise_perror("can't open '%s'", fname);
11062 } 11062 }
11063 if (fd < 10) 11063 if (fd < 10)
11064 fd = savefd(fd); 11064 fd = savefd(fd);
diff --git a/shell/ash_test/ash-getopts/getopt_nested.right b/shell/ash_test/ash-getopts/getopt_nested.right
new file mode 100644
index 000000000..b0c339db1
--- /dev/null
+++ b/shell/ash_test/ash-getopts/getopt_nested.right
@@ -0,0 +1,14 @@
1var:a
2var:b
3var:c
4var:a
5var:b
6var:c
7Illegal option -d
8var:?
9Illegal option -e
10var:?
11Illegal option -f
12var:?
13var:a
14End: var:? OPTIND:6
diff --git a/shell/ash_test/ash-getopts/getopt_nested.tests b/shell/ash_test/ash-getopts/getopt_nested.tests
new file mode 100755
index 000000000..1b48b4075
--- /dev/null
+++ b/shell/ash_test/ash-getopts/getopt_nested.tests
@@ -0,0 +1,21 @@
1# Test that there is no interference of getopt()
2# in getopts and unset.
3# It's unclear what "correct" OPTIND values should be
4# for "b" and "c" results from "-bc": 2? 3?
5# What we focus on here is that all options are reported
6# correct number of times and in correct sequence.
7
8(
9
10loop=99
11while getopts "abc" var -a -bc -abc -def -a; do
12 echo "var:$var" #OPTIND:$OPTIND
13 # this may use getopt():
14 unset -ff func
15 test $((--loop)) = 0 && break # BUG if this triggers
16done
17echo "End: var:$var OPTIND:$OPTIND"
18
19) 2>&1 \
20| sed -e 's/ unrecognized option: / invalid option -- /' \
21 -e 's/ illegal option -- / invalid option -- /' \
diff --git a/shell/cttyhack.c b/shell/cttyhack.c
index 849fe9e48..ec1b6c429 100644
--- a/shell/cttyhack.c
+++ b/shell/cttyhack.c
@@ -4,12 +4,6 @@
4 * 4 *
5 * Licensed under GPLv2, see file LICENSE in this source tree. 5 * Licensed under GPLv2, see file LICENSE in this source tree.
6 */ 6 */
7#include "libbb.h"
8
9//applet:IF_CTTYHACK(APPLET_NOEXEC(cttyhack, cttyhack, BB_DIR_BIN, BB_SUID_DROP, cttyhack))
10
11//kbuild:lib-$(CONFIG_CTTYHACK) += cttyhack.o
12
13//config:config CTTYHACK 7//config:config CTTYHACK
14//config: bool "cttyhack (2.5 kb)" 8//config: bool "cttyhack (2.5 kb)"
15//config: default y 9//config: default y
@@ -54,6 +48,10 @@
54//config: 48//config:
55//config: # getty 115200 $(cttyhack) 49//config: # getty 115200 $(cttyhack)
56 50
51//applet:IF_CTTYHACK(APPLET_NOEXEC(cttyhack, cttyhack, BB_DIR_BIN, BB_SUID_DROP, cttyhack))
52
53//kbuild:lib-$(CONFIG_CTTYHACK) += cttyhack.o
54
57//usage:#define cttyhack_trivial_usage 55//usage:#define cttyhack_trivial_usage
58//usage: "[PROG ARGS]" 56//usage: "[PROG ARGS]"
59//usage:#define cttyhack_full_usage "\n\n" 57//usage:#define cttyhack_full_usage "\n\n"
@@ -65,6 +63,8 @@
65//usage: "\nStarting interactive shell from boot shell script:" 63//usage: "\nStarting interactive shell from boot shell script:"
66//usage: "\n setsid cttyhack sh" 64//usage: "\n setsid cttyhack sh"
67 65
66#include "libbb.h"
67
68#if !defined(__linux__) && !defined(TIOCGSERIAL) && !ENABLE_WERROR 68#if !defined(__linux__) && !defined(TIOCGSERIAL) && !ENABLE_WERROR
69# warning cttyhack will not be able to detect a controlling tty on this system 69# warning cttyhack will not be able to detect a controlling tty on this system
70#endif 70#endif
diff --git a/shell/hush.c b/shell/hush.c
index cdc3a8618..d27550ba0 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -907,6 +907,9 @@ struct globals {
907 unsigned depth_break_continue; 907 unsigned depth_break_continue;
908 unsigned depth_of_loop; 908 unsigned depth_of_loop;
909#endif 909#endif
910#if ENABLE_HUSH_GETOPTS
911 unsigned getopt_count;
912#endif
910 const char *ifs; 913 const char *ifs;
911 const char *cwd; 914 const char *cwd;
912 struct variable *top_var; 915 struct variable *top_var;
@@ -2214,6 +2217,11 @@ static int set_local_var(char *str, unsigned flags)
2214 cur->flg_export = 1; 2217 cur->flg_export = 1;
2215 if (name_len == 4 && cur->varstr[0] == 'P' && cur->varstr[1] == 'S') 2218 if (name_len == 4 && cur->varstr[0] == 'P' && cur->varstr[1] == 'S')
2216 cmdedit_update_prompt(); 2219 cmdedit_update_prompt();
2220#if ENABLE_HUSH_GETOPTS
2221 /* defoptindvar is a "OPTIND=..." constant string */
2222 if (strncmp(cur->varstr, defoptindvar, 7) == 0)
2223 G.getopt_count = 0;
2224#endif
2217 if (cur->flg_export) { 2225 if (cur->flg_export) {
2218 if (flags & SETFLAG_UNEXPORT) { 2226 if (flags & SETFLAG_UNEXPORT) {
2219 cur->flg_export = 0; 2227 cur->flg_export = 0;
@@ -2244,6 +2252,10 @@ static int unset_local_var_len(const char *name, int name_len)
2244 2252
2245 if (!name) 2253 if (!name)
2246 return EXIT_SUCCESS; 2254 return EXIT_SUCCESS;
2255#if ENABLE_HUSH_GETOPTS
2256 if (name_len == 6 && strncmp(name, "OPTIND", 6) == 0)
2257 G.getopt_count = 0;
2258#endif
2247 var_pp = &G.top_var; 2259 var_pp = &G.top_var;
2248 while ((cur = *var_pp) != NULL) { 2260 while ((cur = *var_pp) != NULL) {
2249 if (strncmp(cur->varstr, name, name_len) == 0 && cur->varstr[name_len] == '=') { 2261 if (strncmp(cur->varstr, name, name_len) == 0 && cur->varstr[name_len] == '=') {
@@ -9889,7 +9901,8 @@ Test that VAR is a valid variable name?
9889*/ 9901*/
9890 char cbuf[2]; 9902 char cbuf[2];
9891 const char *cp, *optstring, *var; 9903 const char *cp, *optstring, *var;
9892 int c, exitcode; 9904 int c, n, exitcode, my_opterr;
9905 unsigned count;
9893 9906
9894 optstring = *++argv; 9907 optstring = *++argv;
9895 if (!optstring || !(var = *++argv)) { 9908 if (!optstring || !(var = *++argv)) {
@@ -9897,17 +9910,18 @@ Test that VAR is a valid variable name?
9897 return EXIT_FAILURE; 9910 return EXIT_FAILURE;
9898 } 9911 }
9899 9912
9900 c = 0; 9913 if (argv[1])
9914 argv[0] = G.global_argv[0]; /* for error messages in getopt() */
9915 else
9916 argv = G.global_argv;
9917 cbuf[1] = '\0';
9918
9919 my_opterr = 0;
9901 if (optstring[0] != ':') { 9920 if (optstring[0] != ':') {
9902 cp = get_local_var_value("OPTERR"); 9921 cp = get_local_var_value("OPTERR");
9903 /* 0 if "OPTERR=0", 1 otherwise */ 9922 /* 0 if "OPTERR=0", 1 otherwise */
9904 c = (!cp || NOT_LONE_CHAR(cp, '0')); 9923 my_opterr = (!cp || NOT_LONE_CHAR(cp, '0'));
9905 } 9924 }
9906 opterr = c;
9907 cp = get_local_var_value("OPTIND");
9908 optind = cp ? atoi(cp) : 0;
9909 optarg = NULL;
9910 cbuf[1] = '\0';
9911 9925
9912 /* getopts stops on first non-option. Add "+" to force that */ 9926 /* getopts stops on first non-option. Add "+" to force that */
9913 /*if (optstring[0] != '+')*/ { 9927 /*if (optstring[0] != '+')*/ {
@@ -9916,11 +9930,48 @@ Test that VAR is a valid variable name?
9916 optstring = s; 9930 optstring = s;
9917 } 9931 }
9918 9932
9919 if (argv[1]) 9933 /* Naively, now we should just
9920 argv[0] = G.global_argv[0]; /* for error messages */ 9934 * cp = get_local_var_value("OPTIND");
9921 else 9935 * optind = cp ? atoi(cp) : 0;
9922 argv = G.global_argv; 9936 * optarg = NULL;
9923 c = getopt(string_array_len(argv), argv, optstring); 9937 * opterr = my_opterr;
9938 * c = getopt(string_array_len(argv), argv, optstring);
9939 * and be done? Not so fast...
9940 * Unlike normal getopt() usage in C programs, here
9941 * each successive call will (usually) have the same argv[] CONTENTS,
9942 * but not the ADDRESSES. Worse yet, it's possible that between
9943 * invocations of "getopts", there will be calls to shell builtins
9944 * which use getopt() internally. Example:
9945 * while getopts "abc" RES -a -bc -abc de; do
9946 * unset -ff func
9947 * done
9948 * This would not work correctly: getopt() call inside "unset"
9949 * modifies internal libc state which is tracking position in
9950 * multi-option strings ("-abc"). At best, it can skip options
9951 * or return the same option infinitely. With glibc implementation
9952 * of getopt(), it would use outright invalid pointers and return
9953 * garbage even _without_ "unset" mangling internal state.
9954 *
9955 * We resort to resetting getopt() state and calling it N times,
9956 * until we get Nth result (or failure).
9957 * (N == G.getopt_count is reset to 0 whenever OPTIND is [un]set).
9958 */
9959 GETOPT_RESET();
9960 count = 0;
9961 n = string_array_len(argv);
9962 do {
9963 optarg = NULL;
9964 opterr = (count < G.getopt_count) ? 0 : my_opterr;
9965 c = getopt(n, argv, optstring);
9966 if (c < 0)
9967 break;
9968 count++;
9969 } while (count <= G.getopt_count);
9970
9971 /* Set OPTIND. Prevent resetting of the magic counter! */
9972 set_local_var_from_halves("OPTIND", utoa(optind));
9973 G.getopt_count = count; /* "next time, give me N+1'th result" */
9974 GETOPT_RESET(); /* just in case */
9924 9975
9925 /* Set OPTARG */ 9976 /* Set OPTARG */
9926 /* Always set or unset, never left as-is, even on exit/error: 9977 /* Always set or unset, never left as-is, even on exit/error:
@@ -9949,10 +10000,9 @@ Test that VAR is a valid variable name?
9949 c = '?'; 10000 c = '?';
9950 } 10001 }
9951 10002
9952 /* Set VAR and OPTIND */ 10003 /* Set VAR */
9953 cbuf[0] = c; 10004 cbuf[0] = c;
9954 set_local_var_from_halves(var, cbuf); 10005 set_local_var_from_halves(var, cbuf);
9955 set_local_var_from_halves("OPTIND", utoa(optind));
9956 10006
9957 return exitcode; 10007 return exitcode;
9958} 10008}
diff --git a/shell/hush_test/hush-getopts/getopt_nested.right b/shell/hush_test/hush-getopts/getopt_nested.right
new file mode 100644
index 000000000..0218dba56
--- /dev/null
+++ b/shell/hush_test/hush-getopts/getopt_nested.right
@@ -0,0 +1,14 @@
1var:a
2var:b
3var:c
4var:a
5var:b
6var:c
7./getopt_nested.tests: invalid option -- d
8var:?
9./getopt_nested.tests: invalid option -- e
10var:?
11./getopt_nested.tests: invalid option -- f
12var:?
13var:a
14End: var:? OPTIND:6
diff --git a/shell/hush_test/hush-getopts/getopt_nested.tests b/shell/hush_test/hush-getopts/getopt_nested.tests
new file mode 100755
index 000000000..1b48b4075
--- /dev/null
+++ b/shell/hush_test/hush-getopts/getopt_nested.tests
@@ -0,0 +1,21 @@
1# Test that there is no interference of getopt()
2# in getopts and unset.
3# It's unclear what "correct" OPTIND values should be
4# for "b" and "c" results from "-bc": 2? 3?
5# What we focus on here is that all options are reported
6# correct number of times and in correct sequence.
7
8(
9
10loop=99
11while getopts "abc" var -a -bc -abc -def -a; do
12 echo "var:$var" #OPTIND:$OPTIND
13 # this may use getopt():
14 unset -ff func
15 test $((--loop)) = 0 && break # BUG if this triggers
16done
17echo "End: var:$var OPTIND:$OPTIND"
18
19) 2>&1 \
20| sed -e 's/ unrecognized option: / invalid option -- /' \
21 -e 's/ illegal option -- / invalid option -- /' \
diff --git a/shell/hush_test/hush-vars/unset.tests b/shell/hush_test/hush-vars/unset.tests
index 81243fbf9..268323a6d 100755
--- a/shell/hush_test/hush-vars/unset.tests
+++ b/shell/hush_test/hush-vars/unset.tests
@@ -1,3 +1,5 @@
1(
2
1# check invalid options are rejected 3# check invalid options are rejected
2# bash: in posix mode, aborts if non-interactive 4# bash: in posix mode, aborts if non-interactive
3unset - 5unset -
@@ -37,3 +39,7 @@ unset VAR_RO
37echo $? $f $g 39echo $? $f $g
38unset f VAR_RO g 40unset f VAR_RO g
39echo $? $f $g 41echo $? $f $g
42
43) 2>&1 \
44| sed -e 's/ unrecognized option: / invalid option -- /' \
45 -e 's/ illegal option -- / invalid option -- /' \
diff --git a/shell/shell_common.c b/shell/shell_common.c
index bc34de404..ad9048d89 100644
--- a/shell/shell_common.c
+++ b/shell/shell_common.c
@@ -432,8 +432,8 @@ shell_builtin_ulimit(char **argv)
432 * ulimit 123 -c2 -l 456 432 * ulimit 123 -c2 -l 456
433 */ 433 */
434 434
435 /* In case getopt was already called: 435 /* In case getopt() was already called:
436 * reset the libc getopt() function, which keeps internal state. 436 * reset libc getopt() internal state.
437 */ 437 */
438 GETOPT_RESET(); 438 GETOPT_RESET();
439 439