diff options
author | Ron Yorston <rmy@pobox.com> | 2017-08-22 14:56:12 +0100 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2017-08-22 14:56:12 +0100 |
commit | ce9af1cc5ea23f754587448cf35b5120c77bfeef (patch) | |
tree | 69e5eaba5e75ab909ed92d5045393471b8ff3c13 /shell | |
parent | c170026700eabb10147dd848c45c06995b43a32e (diff) | |
parent | e837a0dbbebf4229306df98fe9ee3b9bb30630c4 (diff) | |
download | busybox-w32-ce9af1cc5ea23f754587448cf35b5120c77bfeef.tar.gz busybox-w32-ce9af1cc5ea23f754587448cf35b5120c77bfeef.tar.bz2 busybox-w32-ce9af1cc5ea23f754587448cf35b5120c77bfeef.zip |
Merge branch 'busybox' into merge
Diffstat (limited to 'shell')
34 files changed, 1422 insertions, 82 deletions
diff --git a/shell/ash.c b/shell/ash.c index 6a572cbc3..ac1ae05fc 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -215,6 +215,9 @@ | |||
215 | #define BASH_PIPEFAIL ENABLE_ASH_BASH_COMPAT | 215 | #define BASH_PIPEFAIL ENABLE_ASH_BASH_COMPAT |
216 | #define BASH_HOSTNAME_VAR ENABLE_ASH_BASH_COMPAT | 216 | #define BASH_HOSTNAME_VAR ENABLE_ASH_BASH_COMPAT |
217 | #define BASH_SHLVL_VAR ENABLE_ASH_BASH_COMPAT | 217 | #define BASH_SHLVL_VAR ENABLE_ASH_BASH_COMPAT |
218 | #define BASH_XTRACEFD ENABLE_ASH_BASH_COMPAT | ||
219 | #define BASH_READ_D ENABLE_ASH_BASH_COMPAT | ||
220 | #define IF_BASH_READ_D IF_ASH_BASH_COMPAT | ||
218 | 221 | ||
219 | #if defined(__ANDROID_API__) && __ANDROID_API__ <= 24 | 222 | #if defined(__ANDROID_API__) && __ANDROID_API__ <= 24 |
220 | /* Bionic at least up to version 24 has no glob() */ | 223 | /* Bionic at least up to version 24 has no glob() */ |
@@ -452,7 +455,7 @@ struct globals_misc { | |||
452 | #define S_DFL 1 /* default signal handling (SIG_DFL) */ | 455 | #define S_DFL 1 /* default signal handling (SIG_DFL) */ |
453 | #define S_CATCH 2 /* signal is caught */ | 456 | #define S_CATCH 2 /* signal is caught */ |
454 | #define S_IGN 3 /* signal is ignored (SIG_IGN) */ | 457 | #define S_IGN 3 /* signal is ignored (SIG_IGN) */ |
455 | #define S_HARD_IGN 4 /* signal is ignored permanently */ | 458 | #define S_HARD_IGN 4 /* signal is ignored permanently (it was SIG_IGN on entry to shell) */ |
456 | 459 | ||
457 | /* indicates specified signal received */ | 460 | /* indicates specified signal received */ |
458 | uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */ | 461 | uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */ |
@@ -1313,6 +1316,10 @@ struct strpush { | |||
1313 | int unget; | 1316 | int unget; |
1314 | }; | 1317 | }; |
1315 | 1318 | ||
1319 | /* | ||
1320 | * The parsefile structure pointed to by the global variable parsefile | ||
1321 | * contains information about the current file being read. | ||
1322 | */ | ||
1316 | struct parsefile { | 1323 | struct parsefile { |
1317 | struct parsefile *prev; /* preceding file on stack */ | 1324 | struct parsefile *prev; /* preceding file on stack */ |
1318 | int linno; /* current line */ | 1325 | int linno; /* current line */ |
@@ -1986,10 +1993,6 @@ nextopt(const char *optstring) | |||
1986 | 1993 | ||
1987 | /* ============ Shell variables */ | 1994 | /* ============ Shell variables */ |
1988 | 1995 | ||
1989 | /* | ||
1990 | * The parsefile structure pointed to by the global variable parsefile | ||
1991 | * contains information about the current file being read. | ||
1992 | */ | ||
1993 | struct shparam { | 1996 | struct shparam { |
1994 | int nparam; /* # of positional parameters (without $0) */ | 1997 | int nparam; /* # of positional parameters (without $0) */ |
1995 | #if ENABLE_ASH_GETOPTS | 1998 | #if ENABLE_ASH_GETOPTS |
@@ -2183,7 +2186,9 @@ extern struct globals_var *const ash_ptr_to_globals_var; | |||
2183 | static void FAST_FUNC | 2186 | static void FAST_FUNC |
2184 | getoptsreset(const char *value) | 2187 | getoptsreset(const char *value) |
2185 | { | 2188 | { |
2186 | shellparam.optind = number(value) ?: 1; | 2189 | shellparam.optind = 1; |
2190 | if (is_number(value)) | ||
2191 | shellparam.optind = number(value) ?: 1; | ||
2187 | shellparam.optoff = -1; | 2192 | shellparam.optoff = -1; |
2188 | } | 2193 | } |
2189 | #endif | 2194 | #endif |
@@ -3751,9 +3756,12 @@ setsignal(int signo) | |||
3751 | #endif | 3756 | #endif |
3752 | } | 3757 | } |
3753 | } | 3758 | } |
3754 | //TODO: if !rootshell, we reset SIGQUIT to DFL, | 3759 | /* if !rootshell, we reset SIGQUIT to DFL, |
3755 | //whereas we have to restore it to what shell got on entry | 3760 | * whereas we have to restore it to what shell got on entry. |
3756 | //from the parent. See comment above | 3761 | * This is handled by the fact that if signal was IGNored on entry, |
3762 | * then cur_act is S_HARD_IGN and we never change its sigaction | ||
3763 | * (see code below). | ||
3764 | */ | ||
3757 | 3765 | ||
3758 | if (signo == SIGCHLD) | 3766 | if (signo == SIGCHLD) |
3759 | new_act = S_CATCH; | 3767 | new_act = S_CATCH; |
@@ -3777,10 +3785,18 @@ setsignal(int signo) | |||
3777 | cur_act = S_IGN; /* don't hard ignore these */ | 3785 | cur_act = S_IGN; /* don't hard ignore these */ |
3778 | } | 3786 | } |
3779 | } | 3787 | } |
3788 | if (act.sa_handler == SIG_DFL && new_act == S_DFL) { | ||
3789 | /* installing SIG_DFL over SIG_DFL is a no-op */ | ||
3790 | /* saves one sigaction call in each "sh -c SCRIPT" invocation */ | ||
3791 | *t = S_DFL; | ||
3792 | return; | ||
3793 | } | ||
3780 | } | 3794 | } |
3781 | if (cur_act == S_HARD_IGN || cur_act == new_act) | 3795 | if (cur_act == S_HARD_IGN || cur_act == new_act) |
3782 | return; | 3796 | return; |
3783 | 3797 | ||
3798 | *t = new_act; | ||
3799 | |||
3784 | act.sa_handler = SIG_DFL; | 3800 | act.sa_handler = SIG_DFL; |
3785 | switch (new_act) { | 3801 | switch (new_act) { |
3786 | case S_CATCH: | 3802 | case S_CATCH: |
@@ -3790,16 +3806,13 @@ setsignal(int signo) | |||
3790 | act.sa_handler = SIG_IGN; | 3806 | act.sa_handler = SIG_IGN; |
3791 | break; | 3807 | break; |
3792 | } | 3808 | } |
3793 | |||
3794 | /* flags and mask matter only if !DFL and !IGN, but we do it | 3809 | /* flags and mask matter only if !DFL and !IGN, but we do it |
3795 | * for all cases for more deterministic behavior: | 3810 | * for all cases for more deterministic behavior: |
3796 | */ | 3811 | */ |
3797 | act.sa_flags = 0; | 3812 | act.sa_flags = 0; //TODO: why not SA_RESTART? |
3798 | sigfillset(&act.sa_mask); | 3813 | sigfillset(&act.sa_mask); |
3799 | 3814 | ||
3800 | sigaction_set(signo, &act); | 3815 | sigaction_set(signo, &act); |
3801 | |||
3802 | *t = new_act; | ||
3803 | } | 3816 | } |
3804 | #else | 3817 | #else |
3805 | #define setsignal(s) | 3818 | #define setsignal(s) |
@@ -5915,12 +5928,10 @@ static void | |||
5915 | redirect(union node *redir, int flags) | 5928 | redirect(union node *redir, int flags) |
5916 | { | 5929 | { |
5917 | struct redirtab *sv; | 5930 | struct redirtab *sv; |
5918 | int sv_pos; | ||
5919 | 5931 | ||
5920 | if (!redir) | 5932 | if (!redir) |
5921 | return; | 5933 | return; |
5922 | 5934 | ||
5923 | sv_pos = 0; | ||
5924 | sv = NULL; | 5935 | sv = NULL; |
5925 | INT_OFF; | 5936 | INT_OFF; |
5926 | if (flags & REDIR_PUSH) | 5937 | if (flags & REDIR_PUSH) |
@@ -8076,8 +8087,9 @@ static int | |||
8076 | patmatch(char *pattern, const char *string) | 8087 | patmatch(char *pattern, const char *string) |
8077 | { | 8088 | { |
8078 | char *p = preglob(pattern, 0); | 8089 | char *p = preglob(pattern, 0); |
8079 | //bb_error_msg("fnmatch(pattern:'%s',str:'%s')", p, string); | 8090 | int r = pmatch(p, string); |
8080 | return pmatch(p, string); | 8091 | //bb_error_msg("!fnmatch(pattern:'%s',str:'%s',0):%d", p, string, r); |
8092 | return r; | ||
8081 | } | 8093 | } |
8082 | 8094 | ||
8083 | /* | 8095 | /* |
@@ -8179,7 +8191,7 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c | |||
8179 | while (*envp) | 8191 | while (*envp) |
8180 | putenv(*envp++); | 8192 | putenv(*envp++); |
8181 | popredir(/*drop:*/ 1); | 8193 | popredir(/*drop:*/ 1); |
8182 | run_applet_no_and_exit(applet_no, cmd, argv); | 8194 | run_noexec_applet_and_exit(applet_no, cmd, argv); |
8183 | } | 8195 | } |
8184 | /* re-exec ourselves with the new arguments */ | 8196 | /* re-exec ourselves with the new arguments */ |
8185 | execve(bb_busybox_exec_path, argv, envp); | 8197 | execve(bb_busybox_exec_path, argv, envp); |
@@ -10250,6 +10262,15 @@ evalcommand(union node *cmd, int flags) | |||
10250 | expredir(cmd->ncmd.redirect); | 10262 | expredir(cmd->ncmd.redirect); |
10251 | redir_stop = pushredir(cmd->ncmd.redirect); | 10263 | redir_stop = pushredir(cmd->ncmd.redirect); |
10252 | preverrout_fd = 2; | 10264 | preverrout_fd = 2; |
10265 | if (BASH_XTRACEFD && xflag) { | ||
10266 | /* NB: bash closes fd == $BASH_XTRACEFD when it is changed. | ||
10267 | * we do not emulate this. We only use its value. | ||
10268 | */ | ||
10269 | const char *xtracefd = lookupvar("BASH_XTRACEFD"); | ||
10270 | if (xtracefd && is_number(xtracefd)) | ||
10271 | preverrout_fd = atoi(xtracefd); | ||
10272 | |||
10273 | } | ||
10253 | status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2); | 10274 | status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2); |
10254 | 10275 | ||
10255 | path = vpath.var_text; | 10276 | path = vpath.var_text; |
@@ -10383,12 +10404,30 @@ evalcommand(union node *cmd, int flags) | |||
10383 | #endif | 10404 | #endif |
10384 | ) { | 10405 | ) { |
10385 | listsetvar(varlist.list, VEXPORT|VSTACK); | 10406 | listsetvar(varlist.list, VEXPORT|VSTACK); |
10386 | /* run <applet>_main() */ | 10407 | /* |
10408 | * Run <applet>_main(). | ||
10409 | * Signals (^C) can't interrupt here. | ||
10410 | * Otherwise we can mangle stdio or malloc internal state. | ||
10411 | * This makes applets which can run for a long time | ||
10412 | * and/or wait for user input ineligible for NOFORK: | ||
10413 | * for example, "yes" or "rm" (rm -i waits for input). | ||
10414 | */ | ||
10415 | INT_OFF; | ||
10387 | status = run_nofork_applet(applet_no, argv); | 10416 | status = run_nofork_applet(applet_no, argv); |
10417 | /* | ||
10418 | * Try enabling NOFORK for "yes" applet. | ||
10419 | * ^C _will_ stop it (write returns EINTR), | ||
10420 | * but this causes stdout FILE to be stuck | ||
10421 | * and needing clearerr(). What if other applets | ||
10422 | * also can get EINTRs? Do we need to switch | ||
10423 | * our signals to SA_RESTART? | ||
10424 | */ | ||
10425 | /*clearerr(stdout);*/ | ||
10426 | INT_ON; | ||
10388 | break; | 10427 | break; |
10389 | } | 10428 | } |
10390 | #endif | 10429 | #endif |
10391 | /* Can we avoid forking off? For example, very last command | 10430 | /* Can we avoid forking? For example, very last command |
10392 | * in a script or a subshell does not need forking, | 10431 | * in a script or a subshell does not need forking, |
10393 | * we can just exec it. | 10432 | * we can just exec it. |
10394 | */ | 10433 | */ |
@@ -10674,8 +10713,8 @@ preadfd(void) | |||
10674 | if (!iflag || g_parsefile->pf_fd != STDIN_FILENO) | 10713 | if (!iflag || g_parsefile->pf_fd != STDIN_FILENO) |
10675 | nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); | 10714 | nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); |
10676 | else { | 10715 | else { |
10677 | int timeout = -1; | ||
10678 | # if ENABLE_ASH_IDLE_TIMEOUT | 10716 | # if ENABLE_ASH_IDLE_TIMEOUT |
10717 | int timeout = -1; | ||
10679 | if (iflag) { | 10718 | if (iflag) { |
10680 | const char *tmout_var = lookupvar("TMOUT"); | 10719 | const char *tmout_var = lookupvar("TMOUT"); |
10681 | if (tmout_var) { | 10720 | if (tmout_var) { |
@@ -10684,12 +10723,13 @@ preadfd(void) | |||
10684 | timeout = -1; | 10723 | timeout = -1; |
10685 | } | 10724 | } |
10686 | } | 10725 | } |
10726 | line_input_state->timeout = timeout; | ||
10687 | # endif | 10727 | # endif |
10688 | # if ENABLE_FEATURE_TAB_COMPLETION | 10728 | # if ENABLE_FEATURE_TAB_COMPLETION |
10689 | line_input_state->path_lookup = pathval(); | 10729 | line_input_state->path_lookup = pathval(); |
10690 | # endif | 10730 | # endif |
10691 | reinit_unicode_for_ash(); | 10731 | reinit_unicode_for_ash(); |
10692 | nr = read_line_input(line_input_state, cmdedit_prompt, buf, IBUFSIZ, timeout); | 10732 | nr = read_line_input(line_input_state, cmdedit_prompt, buf, IBUFSIZ); |
10693 | if (nr == 0) { | 10733 | if (nr == 0) { |
10694 | /* ^C pressed, "convert" to SIGINT */ | 10734 | /* ^C pressed, "convert" to SIGINT */ |
10695 | write(STDOUT_FILENO, "^C", 2); | 10735 | write(STDOUT_FILENO, "^C", 2); |
@@ -11399,6 +11439,7 @@ getopts(char *optstr, char *optvar, char **optfirst) | |||
11399 | p = *optnext; | 11439 | p = *optnext; |
11400 | if (p == NULL || *p != '-' || *++p == '\0') { | 11440 | if (p == NULL || *p != '-' || *++p == '\0') { |
11401 | atend: | 11441 | atend: |
11442 | unsetvar("OPTARG"); | ||
11402 | p = NULL; | 11443 | p = NULL; |
11403 | done = 1; | 11444 | done = 1; |
11404 | goto out; | 11445 | goto out; |
@@ -11411,7 +11452,11 @@ getopts(char *optstr, char *optvar, char **optfirst) | |||
11411 | c = *p++; | 11452 | c = *p++; |
11412 | for (q = optstr; *q != c;) { | 11453 | for (q = optstr; *q != c;) { |
11413 | if (*q == '\0') { | 11454 | if (*q == '\0') { |
11414 | if (optstr[0] == ':') { | 11455 | /* OPTERR is a bashism */ |
11456 | const char *cp = lookupvar("OPTERR"); | ||
11457 | if ((cp && LONE_CHAR(cp, '0')) | ||
11458 | || (optstr[0] == ':') | ||
11459 | ) { | ||
11415 | sbuf[0] = c; | 11460 | sbuf[0] = c; |
11416 | /*sbuf[1] = '\0'; - already is */ | 11461 | /*sbuf[1] = '\0'; - already is */ |
11417 | setvar0("OPTARG", sbuf); | 11462 | setvar0("OPTARG", sbuf); |
@@ -11428,7 +11473,11 @@ getopts(char *optstr, char *optvar, char **optfirst) | |||
11428 | 11473 | ||
11429 | if (*++q == ':') { | 11474 | if (*++q == ':') { |
11430 | if (*p == '\0' && (p = *optnext) == NULL) { | 11475 | if (*p == '\0' && (p = *optnext) == NULL) { |
11431 | if (optstr[0] == ':') { | 11476 | /* OPTERR is a bashism */ |
11477 | const char *cp = lookupvar("OPTERR"); | ||
11478 | if ((cp && LONE_CHAR(cp, '0')) | ||
11479 | || (optstr[0] == ':') | ||
11480 | ) { | ||
11432 | sbuf[0] = c; | 11481 | sbuf[0] = c; |
11433 | /*sbuf[1] = '\0'; - already is */ | 11482 | /*sbuf[1] = '\0'; - already is */ |
11434 | setvar0("OPTARG", sbuf); | 11483 | setvar0("OPTARG", sbuf); |
@@ -12571,7 +12620,7 @@ parsesub: { | |||
12571 | STPUTC(c, out); | 12620 | STPUTC(c, out); |
12572 | c = pgetc_eatbnl(); | 12621 | c = pgetc_eatbnl(); |
12573 | } while (isdigit(c)); | 12622 | } while (isdigit(c)); |
12574 | } else if (is_special(c)) { | 12623 | } else { |
12575 | /* $[{[#]]<specialchar>[}] */ | 12624 | /* $[{[#]]<specialchar>[}] */ |
12576 | int cc = c; | 12625 | int cc = c; |
12577 | 12626 | ||
@@ -12589,10 +12638,16 @@ parsesub: { | |||
12589 | cc = '#'; | 12638 | cc = '#'; |
12590 | } | 12639 | } |
12591 | } | 12640 | } |
12641 | |||
12642 | if (!is_special(cc)) { | ||
12643 | if (subtype == VSLENGTH) | ||
12644 | subtype = 0; | ||
12645 | goto badsub; | ||
12646 | } | ||
12647 | |||
12592 | USTPUTC(cc, out); | 12648 | USTPUTC(cc, out); |
12593 | } else { | ||
12594 | goto badsub; | ||
12595 | } | 12649 | } |
12650 | |||
12596 | if (c != '}' && subtype == VSLENGTH) { | 12651 | if (c != '}' && subtype == VSLENGTH) { |
12597 | /* ${#VAR didn't end with } */ | 12652 | /* ${#VAR didn't end with } */ |
12598 | goto badsub; | 12653 | goto badsub; |
@@ -13850,21 +13905,23 @@ static const unsigned char timescmd_str[] ALIGN1 = { | |||
13850 | static int FAST_FUNC | 13905 | static int FAST_FUNC |
13851 | timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | 13906 | timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) |
13852 | { | 13907 | { |
13853 | unsigned long clk_tck, s, t; | 13908 | unsigned clk_tck; |
13854 | const unsigned char *p; | 13909 | const unsigned char *p; |
13855 | struct tms buf; | 13910 | struct tms buf; |
13856 | 13911 | ||
13857 | clk_tck = bb_clk_tck(); | 13912 | clk_tck = bb_clk_tck(); |
13858 | times(&buf); | ||
13859 | 13913 | ||
13914 | times(&buf); | ||
13860 | p = timescmd_str; | 13915 | p = timescmd_str; |
13861 | do { | 13916 | do { |
13917 | unsigned sec, frac; | ||
13918 | unsigned long t; | ||
13862 | t = *(clock_t *)(((char *) &buf) + p[1]); | 13919 | t = *(clock_t *)(((char *) &buf) + p[1]); |
13863 | s = t / clk_tck; | 13920 | sec = t / clk_tck; |
13864 | t = t % clk_tck; | 13921 | frac = t % clk_tck; |
13865 | out1fmt("%lum%lu.%03lus%c", | 13922 | out1fmt("%um%u.%03us%c", |
13866 | s / 60, s % 60, | 13923 | sec / 60, sec % 60, |
13867 | (t * 1000) / clk_tck, | 13924 | (frac * 1000) / clk_tck, |
13868 | p[0]); | 13925 | p[0]); |
13869 | p += 2; | 13926 | p += 2; |
13870 | } while (*p); | 13927 | } while (*p); |
@@ -13903,10 +13960,10 @@ letcmd(int argc UNUSED_PARAM, char **argv) | |||
13903 | * -p PROMPT Display PROMPT on stderr (if input is from tty) | 13960 | * -p PROMPT Display PROMPT on stderr (if input is from tty) |
13904 | * -t SECONDS Timeout after SECONDS (tty or pipe only) | 13961 | * -t SECONDS Timeout after SECONDS (tty or pipe only) |
13905 | * -u FD Read from given FD instead of fd 0 | 13962 | * -u FD Read from given FD instead of fd 0 |
13963 | * -d DELIM End on DELIM char, not newline | ||
13906 | * This uses unbuffered input, which may be avoidable in some cases. | 13964 | * This uses unbuffered input, which may be avoidable in some cases. |
13907 | * TODO: bash also has: | 13965 | * TODO: bash also has: |
13908 | * -a ARRAY Read into array[0],[1],etc | 13966 | * -a ARRAY Read into array[0],[1],etc |
13909 | * -d DELIM End on DELIM char, not newline | ||
13910 | * -e Use line editing (tty only) | 13967 | * -e Use line editing (tty only) |
13911 | */ | 13968 | */ |
13912 | static int FAST_FUNC | 13969 | static int FAST_FUNC |
@@ -13916,11 +13973,12 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
13916 | char *opt_p = NULL; | 13973 | char *opt_p = NULL; |
13917 | char *opt_t = NULL; | 13974 | char *opt_t = NULL; |
13918 | char *opt_u = NULL; | 13975 | char *opt_u = NULL; |
13976 | char *opt_d = NULL; /* optimized out if !BASH */ | ||
13919 | int read_flags = 0; | 13977 | int read_flags = 0; |
13920 | const char *r; | 13978 | const char *r; |
13921 | int i; | 13979 | int i; |
13922 | 13980 | ||
13923 | while ((i = nextopt("p:u:rt:n:s")) != '\0') { | 13981 | while ((i = nextopt("p:u:rt:n:sd:")) != '\0') { |
13924 | switch (i) { | 13982 | switch (i) { |
13925 | case 'p': | 13983 | case 'p': |
13926 | opt_p = optionarg; | 13984 | opt_p = optionarg; |
@@ -13940,6 +13998,11 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
13940 | case 'u': | 13998 | case 'u': |
13941 | opt_u = optionarg; | 13999 | opt_u = optionarg; |
13942 | break; | 14000 | break; |
14001 | #if BASH_READ_D | ||
14002 | case 'd': | ||
14003 | opt_d = optionarg; | ||
14004 | break; | ||
14005 | #endif | ||
13943 | default: | 14006 | default: |
13944 | break; | 14007 | break; |
13945 | } | 14008 | } |
@@ -13957,12 +14020,15 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
13957 | opt_n, | 14020 | opt_n, |
13958 | opt_p, | 14021 | opt_p, |
13959 | opt_t, | 14022 | opt_t, |
13960 | opt_u | 14023 | opt_u, |
14024 | opt_d | ||
13961 | ); | 14025 | ); |
13962 | INT_ON; | 14026 | INT_ON; |
13963 | 14027 | ||
13964 | if ((uintptr_t)r == 1 && errno == EINTR) { | 14028 | if ((uintptr_t)r == 1 && errno == EINTR) { |
13965 | /* to get SIGCHLD: sleep 1 & read x; echo $x */ | 14029 | /* To get SIGCHLD: sleep 1 & read x; echo $x |
14030 | * Correct behavior is to not exit "read" | ||
14031 | */ | ||
13966 | if (pending_sig == 0) | 14032 | if (pending_sig == 0) |
13967 | goto again; | 14033 | goto again; |
13968 | } | 14034 | } |
@@ -14077,7 +14143,8 @@ exitshell(void) | |||
14077 | /* NOTREACHED */ | 14143 | /* NOTREACHED */ |
14078 | } | 14144 | } |
14079 | 14145 | ||
14080 | static void | 14146 | /* Don't inline: conserve stack of caller from having our locals too */ |
14147 | static NOINLINE void | ||
14081 | #if ENABLE_PLATFORM_MINGW32 | 14148 | #if ENABLE_PLATFORM_MINGW32 |
14082 | init(int xp) | 14149 | init(int xp) |
14083 | #else | 14150 | #else |
@@ -14086,8 +14153,9 @@ init(void) | |||
14086 | { | 14153 | { |
14087 | /* we will never free this */ | 14154 | /* we will never free this */ |
14088 | basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ); | 14155 | basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ); |
14156 | basepf.linno = 1; | ||
14089 | 14157 | ||
14090 | sigmode[SIGCHLD - 1] = S_DFL; | 14158 | sigmode[SIGCHLD - 1] = S_DFL; /* ensure we install handler even if it is SIG_IGNed */ |
14091 | setsignal(SIGCHLD); | 14159 | setsignal(SIGCHLD); |
14092 | 14160 | ||
14093 | /* bash re-enables SIGHUP which is SIG_IGNed on entry. | 14161 | /* bash re-enables SIGHUP which is SIG_IGNed on entry. |
@@ -14098,7 +14166,6 @@ init(void) | |||
14098 | { | 14166 | { |
14099 | char **envp; | 14167 | char **envp; |
14100 | const char *p; | 14168 | const char *p; |
14101 | struct stat st1, st2; | ||
14102 | 14169 | ||
14103 | initvar(); | 14170 | initvar(); |
14104 | 14171 | ||
@@ -14204,6 +14271,7 @@ init(void) | |||
14204 | #endif | 14271 | #endif |
14205 | p = lookupvar("PWD"); | 14272 | p = lookupvar("PWD"); |
14206 | if (p) { | 14273 | if (p) { |
14274 | struct stat st1, st2; | ||
14207 | if (p[0] != '/' || stat(p, &st1) || stat(".", &st2) | 14275 | if (p[0] != '/' || stat(p, &st1) || stat(".", &st2) |
14208 | || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino | 14276 | || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino |
14209 | ) { | 14277 | ) { |
@@ -15161,7 +15229,7 @@ sticky_free(void *base) | |||
15161 | * may be used to endorse or promote products derived from this software | 15229 | * may be used to endorse or promote products derived from this software |
15162 | * without specific prior written permission. | 15230 | * without specific prior written permission. |
15163 | * | 15231 | * |
15164 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | 15232 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND |
15165 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 15233 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
15166 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 15234 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
15167 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | 15235 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
diff --git a/shell/ash_LINENO.patch b/shell/ash_LINENO.patch new file mode 100644 index 000000000..a71549d6a --- /dev/null +++ b/shell/ash_LINENO.patch | |||
@@ -0,0 +1,498 @@ | |||
1 | This patch is a backport from dash of the combination of: | ||
2 | [SHELL] Add preliminary LINENO support | ||
3 | [VAR] Fix varinit ordering that broke fc | ||
4 | [SHELL] Improve LINENO support | ||
5 | |||
6 | Applies cleanly on top of: | ||
7 | commit 9832bbaba966f0e52e183f10cd93fad7f8f643fe | ||
8 | Date: Tue Aug 15 15:44:41 2017 +0200 | ||
9 | |||
10 | Testsuite needs some tweaks (line numbers in some messages change). | ||
11 | |||
12 | Unfortunately, it is somewhat big: | ||
13 | |||
14 | function old new delta | ||
15 | parse_command 1581 1658 +77 | ||
16 | calcsize 203 272 +69 | ||
17 | copynode 195 257 +62 | ||
18 | lookupvar 59 108 +49 | ||
19 | evaltree 494 534 +40 | ||
20 | evalfor 152 187 +35 | ||
21 | evalcase 278 313 +35 | ||
22 | evalcommand 1547 1581 +34 | ||
23 | evalsubshell 169 199 +30 | ||
24 | linenovar - 22 +22 | ||
25 | raise_error_syntax 11 29 +18 | ||
26 | evalfun 266 280 +14 | ||
27 | varinit_data 96 108 +12 | ||
28 | cmdtxt 626 631 +5 | ||
29 | lineno - 4 +4 | ||
30 | funcline - 4 +4 | ||
31 | ash_vmsg 144 141 -3 | ||
32 | startlinno 4 - -4 | ||
33 | funcnest 4 - -4 | ||
34 | xxreadtoken 272 259 -13 | ||
35 | readtoken1 2635 2594 -41 | ||
36 | ------------------------------------------------------------------------------ | ||
37 | (add/remove: 3/2 grow/shrink: 13/3 up/down: 510/-65) Total: 445 bytes | ||
38 | text data bss dec hex filename | ||
39 | 912030 563 5844 918437 e03a5 busybox_old | ||
40 | 912473 587 5844 918904 e0578 busybox_unstripped | ||
41 | |||
42 | diff --git a/shell/ash.c b/shell/ash.c | ||
43 | index 703802f..93a3814 100644 | ||
44 | --- a/shell/ash.c | ||
45 | +++ b/shell/ash.c | ||
46 | @@ -312,6 +312,8 @@ struct globals_misc { | ||
47 | /* shell level: 0 for the main shell, 1 for its children, and so on */ | ||
48 | int shlvl; | ||
49 | #define rootshell (!shlvl) | ||
50 | + int errlinno; | ||
51 | + | ||
52 | char *minusc; /* argument to -c option */ | ||
53 | |||
54 | char *curdir; // = nullstr; /* current working directory */ | ||
55 | @@ -389,6 +391,7 @@ extern struct globals_misc *const ash_ptr_to_globals_misc; | ||
56 | #define job_warning (G_misc.job_warning) | ||
57 | #define rootpid (G_misc.rootpid ) | ||
58 | #define shlvl (G_misc.shlvl ) | ||
59 | +#define errlinno (G_misc.errlinno ) | ||
60 | #define minusc (G_misc.minusc ) | ||
61 | #define curdir (G_misc.curdir ) | ||
62 | #define physdir (G_misc.physdir ) | ||
63 | @@ -723,6 +726,7 @@ union node; | ||
64 | |||
65 | struct ncmd { | ||
66 | smallint type; /* Nxxxx */ | ||
67 | + int linno; | ||
68 | union node *assign; | ||
69 | union node *args; | ||
70 | union node *redirect; | ||
71 | @@ -736,6 +740,7 @@ struct npipe { | ||
72 | |||
73 | struct nredir { | ||
74 | smallint type; | ||
75 | + int linno; | ||
76 | union node *n; | ||
77 | union node *redirect; | ||
78 | }; | ||
79 | @@ -755,6 +760,7 @@ struct nif { | ||
80 | |||
81 | struct nfor { | ||
82 | smallint type; | ||
83 | + int linno; | ||
84 | union node *args; | ||
85 | union node *body; | ||
86 | char *var; | ||
87 | @@ -762,6 +768,7 @@ struct nfor { | ||
88 | |||
89 | struct ncase { | ||
90 | smallint type; | ||
91 | + int linno; | ||
92 | union node *expr; | ||
93 | union node *cases; | ||
94 | }; | ||
95 | @@ -773,6 +780,13 @@ struct nclist { | ||
96 | union node *body; | ||
97 | }; | ||
98 | |||
99 | +struct ndefun { | ||
100 | + smallint type; | ||
101 | + int linno; | ||
102 | + char *text; | ||
103 | + union node *body; | ||
104 | +}; | ||
105 | + | ||
106 | struct narg { | ||
107 | smallint type; | ||
108 | union node *next; | ||
109 | @@ -824,6 +838,7 @@ union node { | ||
110 | struct nfor nfor; | ||
111 | struct ncase ncase; | ||
112 | struct nclist nclist; | ||
113 | + struct ndefun ndefun; | ||
114 | struct narg narg; | ||
115 | struct nfile nfile; | ||
116 | struct ndup ndup; | ||
117 | @@ -1253,7 +1268,6 @@ struct parsefile { | ||
118 | |||
119 | static struct parsefile basepf; /* top level input file */ | ||
120 | static struct parsefile *g_parsefile = &basepf; /* current input file */ | ||
121 | -static int startlinno; /* line # where last token started */ | ||
122 | static char *commandname; /* currently executing command */ | ||
123 | |||
124 | |||
125 | @@ -1267,7 +1281,7 @@ ash_vmsg(const char *msg, va_list ap) | ||
126 | if (strcmp(arg0, commandname)) | ||
127 | fprintf(stderr, "%s: ", commandname); | ||
128 | if (!iflag || g_parsefile->pf_fd > 0) | ||
129 | - fprintf(stderr, "line %d: ", startlinno); | ||
130 | + fprintf(stderr, "line %d: ", errlinno); | ||
131 | } | ||
132 | vfprintf(stderr, msg, ap); | ||
133 | newline_and_flush(stderr); | ||
134 | @@ -1327,6 +1341,7 @@ static void raise_error_syntax(const char *) NORETURN; | ||
135 | static void | ||
136 | raise_error_syntax(const char *msg) | ||
137 | { | ||
138 | + errlinno = g_parsefile->linno; | ||
139 | ash_msg_and_raise_error("syntax error: %s", msg); | ||
140 | /* NOTREACHED */ | ||
141 | } | ||
142 | @@ -1993,6 +2008,9 @@ static void changepath(const char *) FAST_FUNC; | ||
143 | static void change_random(const char *) FAST_FUNC; | ||
144 | #endif | ||
145 | |||
146 | +static int lineno; | ||
147 | +static char linenovar[sizeof("LINENO=%d") + sizeof(int)*3] = "LINENO="; | ||
148 | + | ||
149 | static const struct { | ||
150 | int flags; | ||
151 | const char *var_text; | ||
152 | @@ -2014,6 +2032,7 @@ static const struct { | ||
153 | #if ENABLE_ASH_GETOPTS | ||
154 | { VSTRFIXED|VTEXTFIXED , defoptindvar, getoptsreset }, | ||
155 | #endif | ||
156 | + { VSTRFIXED|VTEXTFIXED , linenovar , NULL }, | ||
157 | #if ENABLE_ASH_RANDOM_SUPPORT | ||
158 | { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM", change_random }, | ||
159 | #endif | ||
160 | @@ -2066,12 +2085,14 @@ extern struct globals_var *const ash_ptr_to_globals_var; | ||
161 | #define vps4 (&vps2)[1] | ||
162 | #if ENABLE_ASH_GETOPTS | ||
163 | # define voptind (&vps4)[1] | ||
164 | +# define vlineno (&voptind)[1] | ||
165 | # if ENABLE_ASH_RANDOM_SUPPORT | ||
166 | -# define vrandom (&voptind)[1] | ||
167 | +# define vrandom (&vlineno)[1] | ||
168 | # endif | ||
169 | #else | ||
170 | +# define vlineno (&vps4)[1] | ||
171 | # if ENABLE_ASH_RANDOM_SUPPORT | ||
172 | -# define vrandom (&vps4)[1] | ||
173 | +# define vrandom (&vlineno)[1] | ||
174 | # endif | ||
175 | #endif | ||
176 | |||
177 | @@ -2209,8 +2230,12 @@ lookupvar(const char *name) | ||
178 | if (v->flags & VDYNAMIC) | ||
179 | v->var_func(NULL); | ||
180 | #endif | ||
181 | - if (!(v->flags & VUNSET)) | ||
182 | + if (!(v->flags & VUNSET)) { | ||
183 | + if (v == &vlineno && v->var_text == linenovar) { | ||
184 | + fmtstr(linenovar+7, sizeof(linenovar)-7, "%d", lineno); | ||
185 | + } | ||
186 | return var_end(v->var_text); | ||
187 | + } | ||
188 | } | ||
189 | return NULL; | ||
190 | } | ||
191 | @@ -4783,7 +4808,7 @@ cmdtxt(union node *n) | ||
192 | p = "; done"; | ||
193 | goto dodo; | ||
194 | case NDEFUN: | ||
195 | - cmdputs(n->narg.text); | ||
196 | + cmdputs(n->ndefun.text); | ||
197 | p = "() { ... }"; | ||
198 | goto dotail2; | ||
199 | case NCMD: | ||
200 | @@ -8551,6 +8576,9 @@ calcsize(int funcblocksize, union node *n) | ||
201 | funcblocksize = calcsize(funcblocksize, n->nclist.next); | ||
202 | break; | ||
203 | case NDEFUN: | ||
204 | + funcblocksize = calcsize(funcblocksize, n->ndefun.body); | ||
205 | + funcblocksize += SHELL_ALIGN(strlen(n->ndefun.text) + 1); | ||
206 | + break; | ||
207 | case NARG: | ||
208 | funcblocksize = sizenodelist(funcblocksize, n->narg.backquote); | ||
209 | funcblocksize += SHELL_ALIGN(strlen(n->narg.text) + 1); /* was funcstringsize += ... */ | ||
210 | @@ -8626,6 +8654,7 @@ copynode(union node *n) | ||
211 | new->ncmd.redirect = copynode(n->ncmd.redirect); | ||
212 | new->ncmd.args = copynode(n->ncmd.args); | ||
213 | new->ncmd.assign = copynode(n->ncmd.assign); | ||
214 | + new->ncmd.linno = n->ncmd.linno; | ||
215 | break; | ||
216 | case NPIPE: | ||
217 | new->npipe.cmdlist = copynodelist(n->npipe.cmdlist); | ||
218 | @@ -8636,6 +8665,7 @@ copynode(union node *n) | ||
219 | case NSUBSHELL: | ||
220 | new->nredir.redirect = copynode(n->nredir.redirect); | ||
221 | new->nredir.n = copynode(n->nredir.n); | ||
222 | + new->nredir.linno = n->nredir.linno; | ||
223 | break; | ||
224 | case NAND: | ||
225 | case NOR: | ||
226 | @@ -8654,10 +8684,12 @@ copynode(union node *n) | ||
227 | new->nfor.var = nodeckstrdup(n->nfor.var); | ||
228 | new->nfor.body = copynode(n->nfor.body); | ||
229 | new->nfor.args = copynode(n->nfor.args); | ||
230 | + new->nfor.linno = n->nfor.linno; | ||
231 | break; | ||
232 | case NCASE: | ||
233 | new->ncase.cases = copynode(n->ncase.cases); | ||
234 | new->ncase.expr = copynode(n->ncase.expr); | ||
235 | + new->ncase.linno = n->ncase.linno; | ||
236 | break; | ||
237 | case NCLIST: | ||
238 | new->nclist.body = copynode(n->nclist.body); | ||
239 | @@ -8665,6 +8697,10 @@ copynode(union node *n) | ||
240 | new->nclist.next = copynode(n->nclist.next); | ||
241 | break; | ||
242 | case NDEFUN: | ||
243 | + new->ndefun.body = copynode(n->ndefun.body); | ||
244 | + new->ndefun.text = nodeckstrdup(n->ndefun.text); | ||
245 | + new->ndefun.linno = n->ndefun.linno; | ||
246 | + break; | ||
247 | case NARG: | ||
248 | new->narg.backquote = copynodelist(n->narg.backquote); | ||
249 | new->narg.text = nodeckstrdup(n->narg.text); | ||
250 | @@ -8733,7 +8769,7 @@ defun(union node *func) | ||
251 | INT_OFF; | ||
252 | entry.cmdtype = CMDFUNCTION; | ||
253 | entry.u.func = copyfunc(func); | ||
254 | - addcmdentry(func->narg.text, &entry); | ||
255 | + addcmdentry(func->ndefun.text, &entry); | ||
256 | INT_ON; | ||
257 | } | ||
258 | |||
259 | @@ -8743,8 +8779,8 @@ defun(union node *func) | ||
260 | #define SKIPFUNC (1 << 2) | ||
261 | static smallint evalskip; /* set to SKIPxxx if we are skipping commands */ | ||
262 | static int skipcount; /* number of levels to skip */ | ||
263 | -static int funcnest; /* depth of function calls */ | ||
264 | static int loopnest; /* current loop nesting level */ | ||
265 | +static int funcline; /* starting line number of current function, or 0 if not in a function */ | ||
266 | |||
267 | /* Forward decl way out to parsing code - dotrap needs it */ | ||
268 | static int evalstring(char *s, int flags); | ||
269 | @@ -8839,6 +8875,9 @@ evaltree(union node *n, int flags) | ||
270 | status = !evaltree(n->nnot.com, EV_TESTED); | ||
271 | goto setstatus; | ||
272 | case NREDIR: | ||
273 | + errlinno = lineno = n->nredir.linno; | ||
274 | + if (funcline) | ||
275 | + lineno -= funcline - 1; | ||
276 | expredir(n->nredir.redirect); | ||
277 | pushredir(n->nredir.redirect); | ||
278 | status = redirectsafe(n->nredir.redirect, REDIR_PUSH); | ||
279 | @@ -8993,6 +9032,10 @@ evalfor(union node *n, int flags) | ||
280 | struct stackmark smark; | ||
281 | int status = 0; | ||
282 | |||
283 | + errlinno = lineno = n->ncase.linno; | ||
284 | + if (funcline) | ||
285 | + lineno -= funcline - 1; | ||
286 | + | ||
287 | setstackmark(&smark); | ||
288 | arglist.list = NULL; | ||
289 | arglist.lastp = &arglist.list; | ||
290 | @@ -9024,6 +9067,10 @@ evalcase(union node *n, int flags) | ||
291 | struct stackmark smark; | ||
292 | int status = 0; | ||
293 | |||
294 | + errlinno = lineno = n->ncase.linno; | ||
295 | + if (funcline) | ||
296 | + lineno -= funcline - 1; | ||
297 | + | ||
298 | setstackmark(&smark); | ||
299 | arglist.list = NULL; | ||
300 | arglist.lastp = &arglist.list; | ||
301 | @@ -9058,6 +9105,10 @@ evalsubshell(union node *n, int flags) | ||
302 | int backgnd = (n->type == NBACKGND); /* FORK_BG(1) if yes, else FORK_FG(0) */ | ||
303 | int status; | ||
304 | |||
305 | + errlinno = lineno = n->nredir.linno; | ||
306 | + if (funcline) | ||
307 | + lineno -= funcline - 1; | ||
308 | + | ||
309 | expredir(n->nredir.redirect); | ||
310 | if (!backgnd && (flags & EV_EXIT) && !may_have_traps) | ||
311 | goto nofork; | ||
312 | @@ -9365,8 +9416,10 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags) | ||
313 | struct jmploc *volatile savehandler; | ||
314 | struct jmploc jmploc; | ||
315 | int e; | ||
316 | + int savefuncline; | ||
317 | |||
318 | saveparam = shellparam; | ||
319 | + savefuncline = funcline; | ||
320 | savehandler = exception_handler; | ||
321 | e = setjmp(jmploc.loc); | ||
322 | if (e) { | ||
323 | @@ -9376,7 +9429,7 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags) | ||
324 | exception_handler = &jmploc; | ||
325 | shellparam.malloced = 0; | ||
326 | func->count++; | ||
327 | - funcnest++; | ||
328 | + funcline = func->n.ndefun.linno; | ||
329 | INT_ON; | ||
330 | shellparam.nparam = argc - 1; | ||
331 | shellparam.p = argv + 1; | ||
332 | @@ -9385,11 +9438,11 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags) | ||
333 | shellparam.optoff = -1; | ||
334 | #endif | ||
335 | pushlocalvars(); | ||
336 | - evaltree(func->n.narg.next, flags & EV_TESTED); | ||
337 | + evaltree(func->n.ndefun.body, flags & EV_TESTED); | ||
338 | poplocalvars(0); | ||
339 | funcdone: | ||
340 | INT_OFF; | ||
341 | - funcnest--; | ||
342 | + funcline = savefuncline; | ||
343 | freefunc(func); | ||
344 | freeparam(&shellparam); | ||
345 | shellparam = saveparam; | ||
346 | @@ -9753,6 +9806,10 @@ evalcommand(union node *cmd, int flags) | ||
347 | char **nargv; | ||
348 | smallint cmd_is_exec; | ||
349 | |||
350 | + errlinno = lineno = cmd->ncmd.linno; | ||
351 | + if (funcline) | ||
352 | + lineno -= funcline - 1; | ||
353 | + | ||
354 | /* First expand the arguments. */ | ||
355 | TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags)); | ||
356 | setstackmark(&smark); | ||
357 | @@ -9798,7 +9855,7 @@ evalcommand(union node *cmd, int flags) | ||
358 | *nargv = NULL; | ||
359 | |||
360 | lastarg = NULL; | ||
361 | - if (iflag && funcnest == 0 && argc > 0) | ||
362 | + if (iflag && funcline == 0 && argc > 0) | ||
363 | lastarg = nargv[-1]; | ||
364 | |||
365 | expredir(cmd->ncmd.redirect); | ||
366 | @@ -11317,6 +11374,7 @@ simplecmd(void) | ||
367 | union node *vars, **vpp; | ||
368 | union node **rpp, *redir; | ||
369 | int savecheckkwd; | ||
370 | + int savelinno; | ||
371 | #if BASH_TEST2 | ||
372 | smallint double_brackets_flag = 0; | ||
373 | #endif | ||
374 | @@ -11330,6 +11388,7 @@ simplecmd(void) | ||
375 | rpp = &redir; | ||
376 | |||
377 | savecheckkwd = CHKALIAS; | ||
378 | + savelinno = g_parsefile->linno; | ||
379 | for (;;) { | ||
380 | int t; | ||
381 | checkkwd = savecheckkwd; | ||
382 | @@ -11419,7 +11478,9 @@ simplecmd(void) | ||
383 | } | ||
384 | n->type = NDEFUN; | ||
385 | checkkwd = CHKNL | CHKKWD | CHKALIAS; | ||
386 | - n->narg.next = parse_command(); | ||
387 | + n->ndefun.text = n->narg.text; | ||
388 | + n->ndefun.linno = g_parsefile->linno; | ||
389 | + n->ndefun.body = parse_command(); | ||
390 | return n; | ||
391 | } | ||
392 | IF_BASH_FUNCTION(function_flag = 0;) | ||
393 | @@ -11435,6 +11496,7 @@ simplecmd(void) | ||
394 | *rpp = NULL; | ||
395 | n = stzalloc(sizeof(struct ncmd)); | ||
396 | n->type = NCMD; | ||
397 | + n->ncmd.linno = savelinno; | ||
398 | n->ncmd.args = args; | ||
399 | n->ncmd.assign = vars; | ||
400 | n->ncmd.redirect = redir; | ||
401 | @@ -11450,10 +11512,13 @@ parse_command(void) | ||
402 | union node *redir, **rpp; | ||
403 | union node **rpp2; | ||
404 | int t; | ||
405 | + int savelinno; | ||
406 | |||
407 | redir = NULL; | ||
408 | rpp2 = &redir; | ||
409 | |||
410 | + savelinno = g_parsefile->linno; | ||
411 | + | ||
412 | switch (readtoken()) { | ||
413 | default: | ||
414 | raise_error_unexpected_syntax(-1); | ||
415 | @@ -11504,6 +11569,7 @@ parse_command(void) | ||
416 | raise_error_syntax("bad for loop variable"); | ||
417 | n1 = stzalloc(sizeof(struct nfor)); | ||
418 | n1->type = NFOR; | ||
419 | + n1->nfor.linno = savelinno; | ||
420 | n1->nfor.var = wordtext; | ||
421 | checkkwd = CHKNL | CHKKWD | CHKALIAS; | ||
422 | if (readtoken() == TIN) { | ||
423 | @@ -11544,6 +11610,7 @@ parse_command(void) | ||
424 | case TCASE: | ||
425 | n1 = stzalloc(sizeof(struct ncase)); | ||
426 | n1->type = NCASE; | ||
427 | + n1->ncase.linno = savelinno; | ||
428 | if (readtoken() != TWORD) | ||
429 | raise_error_unexpected_syntax(TWORD); | ||
430 | n1->ncase.expr = n2 = stzalloc(sizeof(struct narg)); | ||
431 | @@ -11595,6 +11662,7 @@ parse_command(void) | ||
432 | case TLP: | ||
433 | n1 = stzalloc(sizeof(struct nredir)); | ||
434 | n1->type = NSUBSHELL; | ||
435 | + n1->nredir.linno = savelinno; | ||
436 | n1->nredir.n = list(0); | ||
437 | /*n1->nredir.redirect = NULL; - stzalloc did it */ | ||
438 | t = TRP; | ||
439 | @@ -11628,6 +11696,7 @@ parse_command(void) | ||
440 | if (n1->type != NSUBSHELL) { | ||
441 | n2 = stzalloc(sizeof(struct nredir)); | ||
442 | n2->type = NREDIR; | ||
443 | + n2->nredir.linno = savelinno; | ||
444 | n2->nredir.n = n1; | ||
445 | n1 = n2; | ||
446 | } | ||
447 | @@ -11726,10 +11795,8 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) | ||
448 | IF_FEATURE_SH_MATH(int arinest;) /* levels of arithmetic expansion */ | ||
449 | IF_FEATURE_SH_MATH(int parenlevel;) /* levels of parens in arithmetic */ | ||
450 | int dqvarnest; /* levels of variables expansion within double quotes */ | ||
451 | - | ||
452 | IF_BASH_DOLLAR_SQUOTE(smallint bash_dollar_squote = 0;) | ||
453 | |||
454 | - startlinno = g_parsefile->linno; | ||
455 | bqlist = NULL; | ||
456 | quotef = 0; | ||
457 | IF_FEATURE_SH_MATH(prevsyntax = 0;) | ||
458 | @@ -11906,7 +11973,6 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) | ||
459 | if (syntax != BASESYNTAX && eofmark == NULL) | ||
460 | raise_error_syntax("unterminated quoted string"); | ||
461 | if (varnest != 0) { | ||
462 | - startlinno = g_parsefile->linno; | ||
463 | /* { */ | ||
464 | raise_error_syntax("missing '}'"); | ||
465 | } | ||
466 | @@ -12298,7 +12364,6 @@ parsebackq: { | ||
467 | |||
468 | case PEOF: | ||
469 | IF_ASH_ALIAS(case PEOA:) | ||
470 | - startlinno = g_parsefile->linno; | ||
471 | raise_error_syntax("EOF in backquote substitution"); | ||
472 | |||
473 | case '\n': | ||
474 | @@ -12380,8 +12445,6 @@ parsearith: { | ||
475 | * quoted. | ||
476 | * If the token is TREDIR, then we set redirnode to a structure containing | ||
477 | * the redirection. | ||
478 | - * In all cases, the variable startlinno is set to the number of the line | ||
479 | - * on which the token starts. | ||
480 | * | ||
481 | * [Change comment: here documents and internal procedures] | ||
482 | * [Readtoken shouldn't have any arguments. Perhaps we should make the | ||
483 | @@ -12419,7 +12482,6 @@ xxreadtoken(void) | ||
484 | return lasttoken; | ||
485 | } | ||
486 | setprompt_if(needprompt, 2); | ||
487 | - startlinno = g_parsefile->linno; | ||
488 | for (;;) { /* until token or start of word found */ | ||
489 | c = pgetc(); | ||
490 | if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA)) | ||
491 | @@ -12480,7 +12542,6 @@ xxreadtoken(void) | ||
492 | return lasttoken; | ||
493 | } | ||
494 | setprompt_if(needprompt, 2); | ||
495 | - startlinno = g_parsefile->linno; | ||
496 | for (;;) { /* until token or start of word found */ | ||
497 | c = pgetc(); | ||
498 | switch (c) { | ||
diff --git a/shell/ash_test/ash-getopts/getopt_optarg.right b/shell/ash_test/ash-getopts/getopt_optarg.right new file mode 100644 index 000000000..dff28de57 --- /dev/null +++ b/shell/ash_test/ash-getopts/getopt_optarg.right | |||
@@ -0,0 +1,18 @@ | |||
1 | *** no OPTIND, optstring:'w:et' args:-q -w e -r -t -y | ||
2 | Illegal option -q | ||
3 | var:'?' OPTIND:2 OPTARG:'' | ||
4 | var:'w' OPTIND:4 OPTARG:'e' | ||
5 | Illegal option -r | ||
6 | var:'?' OPTIND:5 OPTARG:'' | ||
7 | var:'t' OPTIND:6 OPTARG:'' | ||
8 | Illegal option -y | ||
9 | var:'?' OPTIND:7 OPTARG:'' | ||
10 | exited: var:'?' OPTIND:7 OPTARG:'' | ||
11 | *** OPTIND=0, optstring:'w:et' args:-w 1 -w2 -w -e -e -t -t | ||
12 | var:'w' OPTIND:3 OPTARG:'1' | ||
13 | var:'w' OPTIND:4 OPTARG:'2' | ||
14 | var:'w' OPTIND:6 OPTARG:'-e' | ||
15 | var:'e' OPTIND:7 OPTARG:'' | ||
16 | var:'t' OPTIND:8 OPTARG:'' | ||
17 | var:'t' OPTIND:9 OPTARG:'' | ||
18 | exited: var:'?' OPTIND:9 OPTARG:'' | ||
diff --git a/shell/ash_test/ash-getopts/getopt_optarg.tests b/shell/ash_test/ash-getopts/getopt_optarg.tests new file mode 100755 index 000000000..33682e868 --- /dev/null +++ b/shell/ash_test/ash-getopts/getopt_optarg.tests | |||
@@ -0,0 +1,18 @@ | |||
1 | set -- -q -w e -r -t -y | ||
2 | echo "*** no OPTIND, optstring:'w:et' args:$*" | ||
3 | var=QWERTY | ||
4 | OPTARG=ASDFGH | ||
5 | while getopts "w:et" var; do | ||
6 | echo "var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
7 | OPTARG=ASDFGH | ||
8 | done | ||
9 | echo "exited: var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
10 | |||
11 | set -- -w 1 -w2 -w -e -e -t -t | ||
12 | echo "*** OPTIND=0, optstring:'w:et' args:$*" | ||
13 | OPTIND=0 | ||
14 | while getopts "w:et" var; do | ||
15 | echo "var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
16 | OPTARG=ASDFGH | ||
17 | done | ||
18 | echo "exited: var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
diff --git a/shell/ash_test/ash-getopts/getopt_positional.right b/shell/ash_test/ash-getopts/getopt_positional.right new file mode 100644 index 000000000..37d0ec845 --- /dev/null +++ b/shell/ash_test/ash-getopts/getopt_positional.right | |||
@@ -0,0 +1,6 @@ | |||
1 | *** no OPTIND, optstring:'we' args:-q -w -e r -t -y | ||
2 | Illegal option -q | ||
3 | var:'?' OPTIND:2 | ||
4 | var:'w' OPTIND:3 | ||
5 | var:'e' OPTIND:4 | ||
6 | exited: var:'?' OPTIND:4 | ||
diff --git a/shell/ash_test/ash-getopts/getopt_positional.tests b/shell/ash_test/ash-getopts/getopt_positional.tests new file mode 100755 index 000000000..a5404a2a0 --- /dev/null +++ b/shell/ash_test/ash-getopts/getopt_positional.tests | |||
@@ -0,0 +1,8 @@ | |||
1 | set -- -q -w -e r -t -y | ||
2 | echo "*** no OPTIND, optstring:'we' args:$*" | ||
3 | var=QWERTY | ||
4 | while getopts "we" var; do | ||
5 | echo "var:'$var' OPTIND:$OPTIND" | ||
6 | done | ||
7 | # unfortunately, "rc:0" is shown since while's overall exitcode is "success" | ||
8 | echo "exited: var:'$var' OPTIND:$OPTIND" | ||
diff --git a/shell/ash_test/ash-getopts/getopt_silent.right b/shell/ash_test/ash-getopts/getopt_silent.right new file mode 100644 index 000000000..03d4eb149 --- /dev/null +++ b/shell/ash_test/ash-getopts/getopt_silent.right | |||
@@ -0,0 +1,6 @@ | |||
1 | *** optstring:':ac' args:-a -b -c | ||
2 | 1 rc:0 var:'a' OPTIND:2 OPTARG:'' | ||
3 | 2 rc:0 var:'?' OPTIND:3 OPTARG:'b' | ||
4 | 3 rc:0 var:'c' OPTIND:4 OPTARG:'' | ||
5 | 4 rc:1 var:'?' OPTIND:4 OPTARG:'' | ||
6 | 5 rc:1 var:'?' OPTIND:4 OPTARG:'' | ||
diff --git a/shell/ash_test/ash-getopts/getopt_silent.tests b/shell/ash_test/ash-getopts/getopt_silent.tests new file mode 100755 index 000000000..097d7ba85 --- /dev/null +++ b/shell/ash_test/ash-getopts/getopt_silent.tests | |||
@@ -0,0 +1,23 @@ | |||
1 | # Open Group Base Specifications Issue 7: | ||
2 | # """ | ||
3 | # If an unknown option is met, VAR shall be set to "?". In this case, | ||
4 | # if the first character in optstring is ":", OPTARG shall be set | ||
5 | # to the option character found, but no output shall be written | ||
6 | # to standard error; otherwise, the shell variable OPTARG shall be | ||
7 | # unset and a diagnostic message shall be written to standard error." | ||
8 | # ... | ||
9 | # If an option-argument is missing: | ||
10 | # If the first character of optstring is ":", VAR shall be set to ":" | ||
11 | # and OPTARG shall be set to the option character found. | ||
12 | # """ | ||
13 | |||
14 | echo "*** optstring:':ac' args:-a -b -c" | ||
15 | getopts ":ac" var -a -b -c; echo "1 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
16 | getopts ":ac" var -a -b -c; echo "2 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
17 | getopts ":ac" var -a -b -c; echo "3 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
18 | getopts ":ac" var -a -b -c; echo "4 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
19 | # Previous line should result in "rc:1", which is normally treated | ||
20 | # in getopts loops as exit condition. | ||
21 | # Nevertheless, let's verify that calling it yet another time doesn't do | ||
22 | # anything weird: | ||
23 | getopts ":ac" var -a -b -c; echo "5 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
diff --git a/shell/ash_test/ash-getopts/getopt_simple.right b/shell/ash_test/ash-getopts/getopt_simple.right new file mode 100644 index 000000000..07e3c57f5 --- /dev/null +++ b/shell/ash_test/ash-getopts/getopt_simple.right | |||
@@ -0,0 +1,34 @@ | |||
1 | *** no OPTIND, optstring:'ab' args:-a -b c | ||
2 | var:'a' OPTIND:2 | ||
3 | var:'b' OPTIND:3 | ||
4 | exited: rc:0 var:'?' OPTIND:3 | ||
5 | *** OPTIND=1, optstring:'ab' args:-a -b c | ||
6 | var:'a' OPTIND:2 | ||
7 | var:'b' OPTIND:3 | ||
8 | exited: rc:0 var:'?' OPTIND:3 | ||
9 | *** OPTIND=0, optstring:'ab' args:-a -b c | ||
10 | var:'a' OPTIND:2 | ||
11 | var:'b' OPTIND:3 | ||
12 | exited: rc:0 var:'?' OPTIND:3 | ||
13 | *** unset OPTIND, optstring:'ab' args:-a -b c | ||
14 | var:'a' OPTIND:2 | ||
15 | var:'b' OPTIND:3 | ||
16 | exited: rc:0 var:'?' OPTIND:3 | ||
17 | *** optstring:'ab' args:-a -b c | ||
18 | 1 rc:0 var:'a' OPTIND:2 | ||
19 | 2 rc:0 var:'b' OPTIND:3 | ||
20 | 3 rc:1 var:'?' OPTIND:3 | ||
21 | *** unset OPTIND, optstring:'ab' args:-a c -c -b d | ||
22 | var:'a' OPTIND:2 | ||
23 | exited: rc:0 var:'?' OPTIND:2 | ||
24 | *** unset OPTIND, optstring:'ab' args:-a -c -b d | ||
25 | var:'a' OPTIND:2 | ||
26 | Illegal option -c | ||
27 | var:'?' OPTIND:3 | ||
28 | var:'b' OPTIND:4 | ||
29 | exited: rc:0 var:'?' OPTIND:4 | ||
30 | *** unset OPTIND, OPTERR=0, optstring:'ab' args:-a -c -b d | ||
31 | var:'a' OPTIND:2 | ||
32 | var:'?' OPTIND:3 | ||
33 | var:'b' OPTIND:4 | ||
34 | exited: rc:0 var:'?' OPTIND:4 | ||
diff --git a/shell/ash_test/ash-getopts/getopt_simple.tests b/shell/ash_test/ash-getopts/getopt_simple.tests new file mode 100755 index 000000000..8615ae366 --- /dev/null +++ b/shell/ash_test/ash-getopts/getopt_simple.tests | |||
@@ -0,0 +1,75 @@ | |||
1 | # Simple usage cases for getopts. | ||
2 | # | ||
3 | # OPTIND is either not touched at all (first loop with getopts, | ||
4 | # relying on shell startup init), or getopts state is reset | ||
5 | # before new loop with "unset OPTIND", "OPTIND=1" or "OPTIND=0". | ||
6 | # | ||
7 | # Each option is a separate argument (no "-abc"). This conceptually | ||
8 | # needs only $OPTIND to hold getopts state. | ||
9 | # | ||
10 | # We check that loop does not stop on unknown option (sets "?"), | ||
11 | # stops on _first_ non-option argument. | ||
12 | |||
13 | echo "*** no OPTIND, optstring:'ab' args:-a -b c" | ||
14 | var=QWERTY | ||
15 | while getopts "ab" var -a -b c; do | ||
16 | echo "var:'$var' OPTIND:$OPTIND" | ||
17 | done | ||
18 | # unfortunately, "rc:0" is shown since while's overall exitcode is "success" | ||
19 | echo "exited: rc:$? var:'$var' OPTIND:$OPTIND" | ||
20 | |||
21 | # Resetting behavior =1 | ||
22 | echo "*** OPTIND=1, optstring:'ab' args:-a -b c" | ||
23 | OPTIND=1 | ||
24 | while getopts "ab" var -a -b c; do | ||
25 | echo "var:'$var' OPTIND:$OPTIND" | ||
26 | done | ||
27 | echo "exited: rc:$? var:'$var' OPTIND:$OPTIND" | ||
28 | |||
29 | # Resetting behavior =0 | ||
30 | echo "*** OPTIND=0, optstring:'ab' args:-a -b c" | ||
31 | OPTIND=0 | ||
32 | while getopts "ab" var -a -b c; do | ||
33 | echo "var:'$var' OPTIND:$OPTIND" | ||
34 | done | ||
35 | echo "exited: rc:$? var:'$var' OPTIND:$OPTIND" | ||
36 | |||
37 | # Resetting behavior "unset" | ||
38 | echo "*** unset OPTIND, optstring:'ab' args:-a -b c" | ||
39 | unset OPTIND | ||
40 | while getopts "ab" var -a -b c; do | ||
41 | echo "var:'$var' OPTIND:$OPTIND" | ||
42 | done | ||
43 | echo "exited: rc:$? var:'$var' OPTIND:$OPTIND" | ||
44 | |||
45 | # What is the final exitcode? | ||
46 | echo "*** optstring:'ab' args:-a -b c" | ||
47 | unset OPTIND | ||
48 | getopts "ab" var -a -b c; echo "1 rc:$? var:'$var' OPTIND:$OPTIND" | ||
49 | getopts "ab" var -a -b c; echo "2 rc:$? var:'$var' OPTIND:$OPTIND" | ||
50 | getopts "ab" var -a -b c; echo "3 rc:$? var:'$var' OPTIND:$OPTIND" | ||
51 | |||
52 | # Where would it stop? c or -c? | ||
53 | echo "*** unset OPTIND, optstring:'ab' args:-a c -c -b d" | ||
54 | unset OPTIND | ||
55 | while getopts "ab" var -a c -c -b d; do | ||
56 | echo "var:'$var' OPTIND:$OPTIND" | ||
57 | done | ||
58 | echo "exited: rc:$? var:'$var' OPTIND:$OPTIND" | ||
59 | |||
60 | # What happens on unknown option? | ||
61 | echo "*** unset OPTIND, optstring:'ab' args:-a -c -b d" | ||
62 | unset OPTIND | ||
63 | while getopts "ab" var -a -c -b d; do | ||
64 | echo "var:'$var' OPTIND:$OPTIND" | ||
65 | done | ||
66 | echo "exited: rc:$? var:'$var' OPTIND:$OPTIND" | ||
67 | |||
68 | # ORTERR=0 suppresses error message? | ||
69 | echo "*** unset OPTIND, OPTERR=0, optstring:'ab' args:-a -c -b d" | ||
70 | unset OPTIND | ||
71 | OPTERR=0 | ||
72 | while getopts "ab" var -a -c -b d; do | ||
73 | echo "var:'$var' OPTIND:$OPTIND" | ||
74 | done | ||
75 | echo "exited: rc:$? var:'$var' OPTIND:$OPTIND" | ||
diff --git a/shell/ash_test/ash-getopts/getopt_test_libc_bug.right b/shell/ash_test/ash-getopts/getopt_test_libc_bug.right new file mode 100644 index 000000000..f6ad4602d --- /dev/null +++ b/shell/ash_test/ash-getopts/getopt_test_libc_bug.right | |||
@@ -0,0 +1,26 @@ | |||
1 | *** optstring:'ac' args:-a -b -c -d e | ||
2 | 1 rc:0 var:'a' OPTIND:2 OPTARG:'' | ||
3 | Illegal option -b | ||
4 | 2 rc:0 var:'?' OPTIND:3 OPTARG:'' | ||
5 | 3 rc:0 var:'c' OPTIND:4 OPTARG:'' | ||
6 | Illegal option -d | ||
7 | 4 rc:0 var:'?' OPTIND:5 OPTARG:'' | ||
8 | 5 rc:1 var:'?' OPTIND:5 OPTARG:'' | ||
9 | |||
10 | *** optstring:'ac' args:-a -b -c -d e | ||
11 | 1 rc:0 var:'a' OPTIND:2 OPTARG:'' | ||
12 | Illegal option -b | ||
13 | 2 rc:0 var:'?' OPTIND:3 OPTARG:'' | ||
14 | 3 rc:0 var:'c' OPTIND:4 OPTARG:'' | ||
15 | Illegal option -d | ||
16 | 4 rc:0 var:'?' OPTIND:5 OPTARG:'' | ||
17 | 5 rc:1 var:'?' OPTIND:5 OPTARG:'' | ||
18 | |||
19 | *** optstring:'ac' args:-a -b -c -d e | ||
20 | 1 rc:0 var:'a' OPTIND:2 OPTARG:'' | ||
21 | Illegal option -b | ||
22 | 2 rc:0 var:'?' OPTIND:3 OPTARG:'' | ||
23 | 3 rc:0 var:'c' OPTIND:4 OPTARG:'' | ||
24 | Illegal option -d | ||
25 | 4 rc:0 var:'?' OPTIND:5 OPTARG:'' | ||
26 | 5 rc:1 var:'?' OPTIND:5 OPTARG:'' | ||
diff --git a/shell/ash_test/ash-getopts/getopt_test_libc_bug.tests b/shell/ash_test/ash-getopts/getopt_test_libc_bug.tests new file mode 100755 index 000000000..fcaac81a2 --- /dev/null +++ b/shell/ash_test/ash-getopts/getopt_test_libc_bug.tests | |||
@@ -0,0 +1,38 @@ | |||
1 | # This test can fail with libc with buggy getopt() implementation. | ||
2 | # If getopt() wants to parse multi-option args (-abc), | ||
3 | # it needs to remember a position within current arg. | ||
4 | # | ||
5 | # If this position is kept as a POINTER, not an offset, | ||
6 | # and if argv[] ADDRESSES (not contents!) change, it blows up. | ||
7 | |||
8 | echo "*** optstring:'ac' args:-a -b -c -d e" | ||
9 | getopts "ac" var -a -b -c -d e; echo "1 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
10 | getopts "ac" var -a -b -c -d e; echo "2 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
11 | getopts "ac" var -a -b -c -d e; echo "3 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
12 | getopts "ac" var -a -b -c -d e; echo "4 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
13 | getopts "ac" var -a -b -c -d e; echo "5 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
14 | |||
15 | # Above: args are (usually) in the same locations in memory. | ||
16 | # Below: variable allocations change the location. | ||
17 | |||
18 | echo | ||
19 | echo "*** optstring:'ac' args:-a -b -c -d e" | ||
20 | unset OPTIND | ||
21 | OPTARG=QWERTY; getopts "ac" var -a -b -c -d e; echo "1 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
22 | NEWVAR=NEWVAL; getopts "ac" var -a -b -c -d e; echo "2 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
23 | VAR111=NEWVAL; getopts "ac" var -a -b -c -d e; echo "3 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
24 | VAR222=NEWVAL; getopts "ac" var -a -b -c -d e; echo "4 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
25 | VAR333=NEWVAL; getopts "ac" var -a -b -c -d e; echo "5 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
26 | |||
27 | # Slightly different attempts to force reallocations | ||
28 | |||
29 | echo | ||
30 | echo "*** optstring:'ac' args:-a -b -c -d e" | ||
31 | unset OPTIND | ||
32 | export OPTARG; getopts "ac" var -a -b -c -d e; echo "1 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
33 | export NEWVAR; getopts "ac" var -a -b -c -d e; echo "2 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
34 | export VAR111; getopts "ac" var -a -b -c -d e; echo "3 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
35 | export VAR222; getopts "ac" var -a -b -c -d e; echo "4 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
36 | export VAR333; getopts "ac" var -a -b -c -d e; echo "5 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
37 | |||
38 | # All copies of code above should generate identical output | ||
diff --git a/shell/ash_test/ash-parsing/groups_and_keywords2.right b/shell/ash_test/ash-parsing/groups_and_keywords2.right new file mode 100644 index 000000000..3fcbeb662 --- /dev/null +++ b/shell/ash_test/ash-parsing/groups_and_keywords2.right | |||
@@ -0,0 +1,3 @@ | |||
1 | ./groups_and_keywords2.tests: eval: line 1: syntax error: unexpected ")" | ||
2 | Fail:2 | ||
3 | ./groups_and_keywords2.tests: line 8: syntax error: unexpected ")" | ||
diff --git a/shell/ash_test/ash-parsing/groups_and_keywords2.tests b/shell/ash_test/ash-parsing/groups_and_keywords2.tests new file mode 100755 index 000000000..ab33b909f --- /dev/null +++ b/shell/ash_test/ash-parsing/groups_and_keywords2.tests | |||
@@ -0,0 +1,9 @@ | |||
1 | # This is an error | ||
2 | (eval 'if() { echo; }') | ||
3 | echo Fail:$? | ||
4 | # ^^^^^^ bash prints 1, but interactively it sets $? = 2 | ||
5 | # we print 2 | ||
6 | |||
7 | # This is an error, and it aborts in script | ||
8 | if() { echo; } | ||
9 | echo Not reached | ||
diff --git a/shell/ash_test/ash-vars/param_expand_assign.right b/shell/ash_test/ash-vars/param_expand_assign.right index 9b07d8cd4..6e9ea1379 100644 --- a/shell/ash_test/ash-vars/param_expand_assign.right +++ b/shell/ash_test/ash-vars/param_expand_assign.right | |||
@@ -1,6 +1,6 @@ | |||
1 | SHELL: line 1: syntax error: bad substitution | 1 | SHELL: line 1: syntax error: bad substitution |
2 | SHELL: line 1: syntax error: bad substitution | 2 | SHELL: line 1: syntax error: bad substitution |
3 | 0 | 3 | SHELL: line 1: syntax error: bad substitution |
4 | 0 | 4 | 0 |
5 | SHELL: line 1: 1: bad variable name | 5 | SHELL: line 1: 1: bad variable name |
6 | SHELL: line 1: 1: bad variable name | 6 | SHELL: line 1: 1: bad variable name |
diff --git a/shell/ash_test/printenv.c b/shell/ash_test/printenv.c index c0c5e197c..c86308d3b 100644 --- a/shell/ash_test/printenv.c +++ b/shell/ash_test/printenv.c | |||
@@ -56,7 +56,7 @@ main (argc, argv) | |||
56 | if (**argv == **envp && strncmp (*envp, *argv, len) == 0) | 56 | if (**argv == **envp && strncmp (*envp, *argv, len) == 0) |
57 | { | 57 | { |
58 | eval = *envp + len; | 58 | eval = *envp + len; |
59 | /* If the environment variable doesn't have an `=', ignore it. */ | 59 | /* If the environment variable doesn't have an '=', ignore it. */ |
60 | if (*eval == '=') | 60 | if (*eval == '=') |
61 | { | 61 | { |
62 | puts (eval + 1); | 62 | puts (eval + 1); |
diff --git a/shell/cttyhack.c b/shell/cttyhack.c index 9004b4763..849fe9e48 100644 --- a/shell/cttyhack.c +++ b/shell/cttyhack.c | |||
@@ -6,7 +6,7 @@ | |||
6 | */ | 6 | */ |
7 | #include "libbb.h" | 7 | #include "libbb.h" |
8 | 8 | ||
9 | //applet:IF_CTTYHACK(APPLET(cttyhack, BB_DIR_BIN, BB_SUID_DROP)) | 9 | //applet:IF_CTTYHACK(APPLET_NOEXEC(cttyhack, cttyhack, BB_DIR_BIN, BB_SUID_DROP, cttyhack)) |
10 | 10 | ||
11 | //kbuild:lib-$(CONFIG_CTTYHACK) += cttyhack.o | 11 | //kbuild:lib-$(CONFIG_CTTYHACK) += cttyhack.o |
12 | 12 | ||
diff --git a/shell/hush.c b/shell/hush.c index 9f946d82f..cdc3a8618 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -48,7 +48,7 @@ | |||
48 | * tilde expansion | 48 | * tilde expansion |
49 | * aliases | 49 | * aliases |
50 | * builtins mandated by standards we don't support: | 50 | * builtins mandated by standards we don't support: |
51 | * [un]alias, command, fc, getopts, times: | 51 | * [un]alias, command, fc: |
52 | * command -v CMD: print "/path/to/CMD" | 52 | * command -v CMD: print "/path/to/CMD" |
53 | * prints "CMD" for builtins | 53 | * prints "CMD" for builtins |
54 | * prints "alias ALIAS='EXPANSION'" for aliases | 54 | * prints "alias ALIAS='EXPANSION'" for aliases |
@@ -58,8 +58,6 @@ | |||
58 | * (can use this to override standalone shell as well) | 58 | * (can use this to override standalone shell as well) |
59 | * -p: use default $PATH | 59 | * -p: use default $PATH |
60 | * command BLTIN: disables special-ness (e.g. errors do not abort) | 60 | * command BLTIN: disables special-ness (e.g. errors do not abort) |
61 | * getopts: getopt() for shells | ||
62 | * times: print getrusage(SELF/CHILDREN).ru_utime/ru_stime | ||
63 | * fc -l[nr] [BEG] [END]: list range of commands in history | 61 | * fc -l[nr] [BEG] [END]: list range of commands in history |
64 | * fc [-e EDITOR] [BEG] [END]: edit/rerun range of commands | 62 | * fc [-e EDITOR] [BEG] [END]: edit/rerun range of commands |
65 | * fc -s [PAT=REP] [CMD]: rerun CMD, replacing PAT with REP | 63 | * fc -s [PAT=REP] [CMD]: rerun CMD, replacing PAT with REP |
@@ -265,6 +263,11 @@ | |||
265 | //config: default y | 263 | //config: default y |
266 | //config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH | 264 | //config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH |
267 | //config: | 265 | //config: |
266 | //config:config HUSH_TIMES | ||
267 | //config: bool "times builtin" | ||
268 | //config: default y | ||
269 | //config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH | ||
270 | //config: | ||
268 | //config:config HUSH_READ | 271 | //config:config HUSH_READ |
269 | //config: bool "read builtin" | 272 | //config: bool "read builtin" |
270 | //config: default y | 273 | //config: default y |
@@ -290,6 +293,11 @@ | |||
290 | //config: default y | 293 | //config: default y |
291 | //config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH | 294 | //config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH |
292 | //config: | 295 | //config: |
296 | //config:config HUSH_GETOPTS | ||
297 | //config: bool "getopts builtin" | ||
298 | //config: default y | ||
299 | //config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH | ||
300 | //config: | ||
293 | //config:config HUSH_MEMLEAK | 301 | //config:config HUSH_MEMLEAK |
294 | //config: bool "memleak builtin (debugging)" | 302 | //config: bool "memleak builtin (debugging)" |
295 | //config: default n | 303 | //config: default n |
@@ -325,6 +333,7 @@ | |||
325 | #if ENABLE_HUSH_CASE | 333 | #if ENABLE_HUSH_CASE |
326 | # include <fnmatch.h> | 334 | # include <fnmatch.h> |
327 | #endif | 335 | #endif |
336 | #include <sys/times.h> | ||
328 | #include <sys/utsname.h> /* for setting $HOSTNAME */ | 337 | #include <sys/utsname.h> /* for setting $HOSTNAME */ |
329 | 338 | ||
330 | #include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */ | 339 | #include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */ |
@@ -352,6 +361,7 @@ | |||
352 | #define BASH_SOURCE ENABLE_HUSH_BASH_COMPAT | 361 | #define BASH_SOURCE ENABLE_HUSH_BASH_COMPAT |
353 | #define BASH_HOSTNAME_VAR ENABLE_HUSH_BASH_COMPAT | 362 | #define BASH_HOSTNAME_VAR ENABLE_HUSH_BASH_COMPAT |
354 | #define BASH_TEST2 (ENABLE_HUSH_BASH_COMPAT && ENABLE_HUSH_TEST) | 363 | #define BASH_TEST2 (ENABLE_HUSH_BASH_COMPAT && ENABLE_HUSH_TEST) |
364 | #define BASH_READ_D ENABLE_HUSH_BASH_COMPAT | ||
355 | 365 | ||
356 | 366 | ||
357 | /* Build knobs */ | 367 | /* Build knobs */ |
@@ -977,6 +987,9 @@ static int builtin_readonly(char **argv) FAST_FUNC; | |||
977 | static int builtin_fg_bg(char **argv) FAST_FUNC; | 987 | static int builtin_fg_bg(char **argv) FAST_FUNC; |
978 | static int builtin_jobs(char **argv) FAST_FUNC; | 988 | static int builtin_jobs(char **argv) FAST_FUNC; |
979 | #endif | 989 | #endif |
990 | #if ENABLE_HUSH_GETOPTS | ||
991 | static int builtin_getopts(char **argv) FAST_FUNC; | ||
992 | #endif | ||
980 | #if ENABLE_HUSH_HELP | 993 | #if ENABLE_HUSH_HELP |
981 | static int builtin_help(char **argv) FAST_FUNC; | 994 | static int builtin_help(char **argv) FAST_FUNC; |
982 | #endif | 995 | #endif |
@@ -1010,6 +1023,9 @@ static int builtin_trap(char **argv) FAST_FUNC; | |||
1010 | #if ENABLE_HUSH_TYPE | 1023 | #if ENABLE_HUSH_TYPE |
1011 | static int builtin_type(char **argv) FAST_FUNC; | 1024 | static int builtin_type(char **argv) FAST_FUNC; |
1012 | #endif | 1025 | #endif |
1026 | #if ENABLE_HUSH_TIMES | ||
1027 | static int builtin_times(char **argv) FAST_FUNC; | ||
1028 | #endif | ||
1013 | static int builtin_true(char **argv) FAST_FUNC; | 1029 | static int builtin_true(char **argv) FAST_FUNC; |
1014 | #if ENABLE_HUSH_UMASK | 1030 | #if ENABLE_HUSH_UMASK |
1015 | static int builtin_umask(char **argv) FAST_FUNC; | 1031 | static int builtin_umask(char **argv) FAST_FUNC; |
@@ -1070,6 +1086,9 @@ static const struct built_in_command bltins1[] = { | |||
1070 | #if ENABLE_HUSH_JOB | 1086 | #if ENABLE_HUSH_JOB |
1071 | BLTIN("fg" , builtin_fg_bg , "Bring job to foreground"), | 1087 | BLTIN("fg" , builtin_fg_bg , "Bring job to foreground"), |
1072 | #endif | 1088 | #endif |
1089 | #if ENABLE_HUSH_GETOPTS | ||
1090 | BLTIN("getopts" , builtin_getopts , NULL), | ||
1091 | #endif | ||
1073 | #if ENABLE_HUSH_HELP | 1092 | #if ENABLE_HUSH_HELP |
1074 | BLTIN("help" , builtin_help , NULL), | 1093 | BLTIN("help" , builtin_help , NULL), |
1075 | #endif | 1094 | #endif |
@@ -1104,6 +1123,9 @@ static const struct built_in_command bltins1[] = { | |||
1104 | #if BASH_SOURCE | 1123 | #if BASH_SOURCE |
1105 | BLTIN("source" , builtin_source , NULL), | 1124 | BLTIN("source" , builtin_source , NULL), |
1106 | #endif | 1125 | #endif |
1126 | #if ENABLE_HUSH_TIMES | ||
1127 | BLTIN("times" , builtin_times , NULL), | ||
1128 | #endif | ||
1107 | #if ENABLE_HUSH_TRAP | 1129 | #if ENABLE_HUSH_TRAP |
1108 | BLTIN("trap" , builtin_trap , "Trap signals"), | 1130 | BLTIN("trap" , builtin_trap , "Trap signals"), |
1109 | #endif | 1131 | #endif |
@@ -1272,7 +1294,7 @@ static void xxfree(void *ptr) | |||
1272 | * HUSH_DEBUG >= 2 prints line number in this file where it was detected. | 1294 | * HUSH_DEBUG >= 2 prints line number in this file where it was detected. |
1273 | */ | 1295 | */ |
1274 | #if HUSH_DEBUG < 2 | 1296 | #if HUSH_DEBUG < 2 |
1275 | # define die_if_script(lineno, ...) die_if_script(__VA_ARGS__) | 1297 | # define msg_and_die_if_script(lineno, ...) msg_and_die_if_script(__VA_ARGS__) |
1276 | # define syntax_error(lineno, msg) syntax_error(msg) | 1298 | # define syntax_error(lineno, msg) syntax_error(msg) |
1277 | # define syntax_error_at(lineno, msg) syntax_error_at(msg) | 1299 | # define syntax_error_at(lineno, msg) syntax_error_at(msg) |
1278 | # define syntax_error_unterm_ch(lineno, ch) syntax_error_unterm_ch(ch) | 1300 | # define syntax_error_unterm_ch(lineno, ch) syntax_error_unterm_ch(ch) |
@@ -1280,7 +1302,16 @@ static void xxfree(void *ptr) | |||
1280 | # define syntax_error_unexpected_ch(lineno, ch) syntax_error_unexpected_ch(ch) | 1302 | # define syntax_error_unexpected_ch(lineno, ch) syntax_error_unexpected_ch(ch) |
1281 | #endif | 1303 | #endif |
1282 | 1304 | ||
1283 | static void die_if_script(unsigned lineno, const char *fmt, ...) | 1305 | static void die_if_script(void) |
1306 | { | ||
1307 | if (!G_interactive_fd) { | ||
1308 | if (G.last_exitcode) /* sometines it's 2, not 1 (bash compat) */ | ||
1309 | xfunc_error_retval = G.last_exitcode; | ||
1310 | xfunc_die(); | ||
1311 | } | ||
1312 | } | ||
1313 | |||
1314 | static void msg_and_die_if_script(unsigned lineno, const char *fmt, ...) | ||
1284 | { | 1315 | { |
1285 | va_list p; | 1316 | va_list p; |
1286 | 1317 | ||
@@ -1290,8 +1321,7 @@ static void die_if_script(unsigned lineno, const char *fmt, ...) | |||
1290 | va_start(p, fmt); | 1321 | va_start(p, fmt); |
1291 | bb_verror_msg(fmt, p, NULL); | 1322 | bb_verror_msg(fmt, p, NULL); |
1292 | va_end(p); | 1323 | va_end(p); |
1293 | if (!G_interactive_fd) | 1324 | die_if_script(); |
1294 | xfunc_die(); | ||
1295 | } | 1325 | } |
1296 | 1326 | ||
1297 | static void syntax_error(unsigned lineno UNUSED_PARAM, const char *msg) | 1327 | static void syntax_error(unsigned lineno UNUSED_PARAM, const char *msg) |
@@ -1300,16 +1330,20 @@ static void syntax_error(unsigned lineno UNUSED_PARAM, const char *msg) | |||
1300 | bb_error_msg("syntax error: %s", msg); | 1330 | bb_error_msg("syntax error: %s", msg); |
1301 | else | 1331 | else |
1302 | bb_error_msg("syntax error"); | 1332 | bb_error_msg("syntax error"); |
1333 | die_if_script(); | ||
1303 | } | 1334 | } |
1304 | 1335 | ||
1305 | static void syntax_error_at(unsigned lineno UNUSED_PARAM, const char *msg) | 1336 | static void syntax_error_at(unsigned lineno UNUSED_PARAM, const char *msg) |
1306 | { | 1337 | { |
1307 | bb_error_msg("syntax error at '%s'", msg); | 1338 | bb_error_msg("syntax error at '%s'", msg); |
1339 | die_if_script(); | ||
1308 | } | 1340 | } |
1309 | 1341 | ||
1310 | static void syntax_error_unterm_str(unsigned lineno UNUSED_PARAM, const char *s) | 1342 | static void syntax_error_unterm_str(unsigned lineno UNUSED_PARAM, const char *s) |
1311 | { | 1343 | { |
1312 | bb_error_msg("syntax error: unterminated %s", s); | 1344 | bb_error_msg("syntax error: unterminated %s", s); |
1345 | //? source4.tests fails: in bash, echo ${^} in script does not terminate the script | ||
1346 | // die_if_script(); | ||
1313 | } | 1347 | } |
1314 | 1348 | ||
1315 | static void syntax_error_unterm_ch(unsigned lineno, char ch) | 1349 | static void syntax_error_unterm_ch(unsigned lineno, char ch) |
@@ -1327,17 +1361,18 @@ static void syntax_error_unexpected_ch(unsigned lineno UNUSED_PARAM, int ch) | |||
1327 | bb_error_msg("hush.c:%u", lineno); | 1361 | bb_error_msg("hush.c:%u", lineno); |
1328 | #endif | 1362 | #endif |
1329 | bb_error_msg("syntax error: unexpected %s", ch == EOF ? "EOF" : msg); | 1363 | bb_error_msg("syntax error: unexpected %s", ch == EOF ? "EOF" : msg); |
1364 | die_if_script(); | ||
1330 | } | 1365 | } |
1331 | 1366 | ||
1332 | #if HUSH_DEBUG < 2 | 1367 | #if HUSH_DEBUG < 2 |
1333 | # undef die_if_script | 1368 | # undef msg_and_die_if_script |
1334 | # undef syntax_error | 1369 | # undef syntax_error |
1335 | # undef syntax_error_at | 1370 | # undef syntax_error_at |
1336 | # undef syntax_error_unterm_ch | 1371 | # undef syntax_error_unterm_ch |
1337 | # undef syntax_error_unterm_str | 1372 | # undef syntax_error_unterm_str |
1338 | # undef syntax_error_unexpected_ch | 1373 | # undef syntax_error_unexpected_ch |
1339 | #else | 1374 | #else |
1340 | # define die_if_script(...) die_if_script(__LINE__, __VA_ARGS__) | 1375 | # define msg_and_die_if_script(...) msg_and_die_if_script(__LINE__, __VA_ARGS__) |
1341 | # define syntax_error(msg) syntax_error(__LINE__, msg) | 1376 | # define syntax_error(msg) syntax_error(__LINE__, msg) |
1342 | # define syntax_error_at(msg) syntax_error_at(__LINE__, msg) | 1377 | # define syntax_error_at(msg) syntax_error_at(__LINE__, msg) |
1343 | # define syntax_error_unterm_ch(ch) syntax_error_unterm_ch(__LINE__, ch) | 1378 | # define syntax_error_unterm_ch(ch) syntax_error_unterm_ch(__LINE__, ch) |
@@ -1800,7 +1835,7 @@ static void restore_ttypgrp_and__exit(void) | |||
1800 | * echo END_OF_SCRIPT | 1835 | * echo END_OF_SCRIPT |
1801 | * lseeks fd in input FILE object from EOF to "e" in "echo END_OF_SCRIPT". | 1836 | * lseeks fd in input FILE object from EOF to "e" in "echo END_OF_SCRIPT". |
1802 | * This makes "echo END_OF_SCRIPT" executed twice. | 1837 | * This makes "echo END_OF_SCRIPT" executed twice. |
1803 | * Similar problems can be seen with die_if_script() -> xfunc_die() | 1838 | * Similar problems can be seen with msg_and_die_if_script() -> xfunc_die() |
1804 | * and in `cmd` handling. | 1839 | * and in `cmd` handling. |
1805 | * If set as die_func(), this makes xfunc_die() exit via _exit(), not exit(): | 1840 | * If set as die_func(), this makes xfunc_die() exit via _exit(), not exit(): |
1806 | */ | 1841 | */ |
@@ -1966,6 +2001,9 @@ static int check_and_run_traps(void) | |||
1966 | break; | 2001 | break; |
1967 | #if ENABLE_HUSH_JOB | 2002 | #if ENABLE_HUSH_JOB |
1968 | case SIGHUP: { | 2003 | case SIGHUP: { |
2004 | //TODO: why are we doing this? ash and dash don't do this, | ||
2005 | //they have no handler for SIGHUP at all, | ||
2006 | //they rely on kernel to send SIGHUP+SIGCONT to orphaned process groups | ||
1969 | struct pipe *job; | 2007 | struct pipe *job; |
1970 | debug_printf_exec("%s: sig:%d default SIGHUP handler\n", __func__, sig); | 2008 | debug_printf_exec("%s: sig:%d default SIGHUP handler\n", __func__, sig); |
1971 | /* bash is observed to signal whole process groups, | 2009 | /* bash is observed to signal whole process groups, |
@@ -2411,18 +2449,17 @@ static int get_user_input(struct in_str *i) | |||
2411 | /* buglet: SIGINT will not make new prompt to appear _at once_, | 2449 | /* buglet: SIGINT will not make new prompt to appear _at once_, |
2412 | * only after <Enter>. (^C works immediately) */ | 2450 | * only after <Enter>. (^C works immediately) */ |
2413 | r = read_line_input(G.line_input_state, prompt_str, | 2451 | r = read_line_input(G.line_input_state, prompt_str, |
2414 | G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1, | 2452 | G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1 |
2415 | /*timeout*/ -1 | ||
2416 | ); | 2453 | ); |
2417 | /* read_line_input intercepts ^C, "convert" it to SIGINT */ | 2454 | /* read_line_input intercepts ^C, "convert" it to SIGINT */ |
2418 | if (r == 0) { | 2455 | if (r == 0) |
2419 | write(STDOUT_FILENO, "^C", 2); | ||
2420 | raise(SIGINT); | 2456 | raise(SIGINT); |
2421 | } | ||
2422 | check_and_run_traps(); | 2457 | check_and_run_traps(); |
2423 | if (r != 0 && !G.flag_SIGINT) | 2458 | if (r != 0 && !G.flag_SIGINT) |
2424 | break; | 2459 | break; |
2425 | /* ^C or SIGINT: repeat */ | 2460 | /* ^C or SIGINT: repeat */ |
2461 | /* bash prints ^C even on real SIGINT (non-kbd generated) */ | ||
2462 | write(STDOUT_FILENO, "^C", 2); | ||
2426 | G.last_exitcode = 128 + SIGINT; | 2463 | G.last_exitcode = 128 + SIGINT; |
2427 | } | 2464 | } |
2428 | if (r < 0) { | 2465 | if (r < 0) { |
@@ -3384,7 +3421,7 @@ static int done_command(struct parse_context *ctx) | |||
3384 | #if 0 /* Instead we emit error message at run time */ | 3421 | #if 0 /* Instead we emit error message at run time */ |
3385 | if (ctx->pending_redirect) { | 3422 | if (ctx->pending_redirect) { |
3386 | /* For example, "cmd >" (no filename to redirect to) */ | 3423 | /* For example, "cmd >" (no filename to redirect to) */ |
3387 | die_if_script("syntax error: %s", "invalid redirect"); | 3424 | syntax_error("invalid redirect"); |
3388 | ctx->pending_redirect = NULL; | 3425 | ctx->pending_redirect = NULL; |
3389 | } | 3426 | } |
3390 | #endif | 3427 | #endif |
@@ -3950,7 +3987,7 @@ static int parse_redirect(struct parse_context *ctx, | |||
3950 | #if 0 /* Instead we emit error message at run time */ | 3987 | #if 0 /* Instead we emit error message at run time */ |
3951 | if (ctx->pending_redirect) { | 3988 | if (ctx->pending_redirect) { |
3952 | /* For example, "cmd > <file" */ | 3989 | /* For example, "cmd > <file" */ |
3953 | die_if_script("syntax error: %s", "invalid redirect"); | 3990 | syntax_error("invalid redirect"); |
3954 | } | 3991 | } |
3955 | #endif | 3992 | #endif |
3956 | /* Set ctx->pending_redirect, so we know what to do at the | 3993 | /* Set ctx->pending_redirect, so we know what to do at the |
@@ -5022,10 +5059,16 @@ static struct pipe *parse_stream(char **pstring, | |||
5022 | else | 5059 | else |
5023 | o_free_unsafe(&ctx.as_string); | 5060 | o_free_unsafe(&ctx.as_string); |
5024 | #endif | 5061 | #endif |
5025 | debug_leave(); | 5062 | if (ch != ';' && IS_NULL_PIPE(ctx.list_head)) { |
5063 | /* Example: bare "{ }", "()" */ | ||
5064 | G.last_exitcode = 2; /* bash compat */ | ||
5065 | syntax_error_unexpected_ch(ch); | ||
5066 | goto parse_error2; | ||
5067 | } | ||
5026 | debug_printf_parse("parse_stream return %p: " | 5068 | debug_printf_parse("parse_stream return %p: " |
5027 | "end_trigger char found\n", | 5069 | "end_trigger char found\n", |
5028 | ctx.list_head); | 5070 | ctx.list_head); |
5071 | debug_leave(); | ||
5029 | return ctx.list_head; | 5072 | return ctx.list_head; |
5030 | } | 5073 | } |
5031 | } | 5074 | } |
@@ -5283,8 +5326,8 @@ static struct pipe *parse_stream(char **pstring, | |||
5283 | /* proper use of this character is caught by end_trigger: | 5326 | /* proper use of this character is caught by end_trigger: |
5284 | * if we see {, we call parse_group(..., end_trigger='}') | 5327 | * if we see {, we call parse_group(..., end_trigger='}') |
5285 | * and it will match } earlier (not here). */ | 5328 | * and it will match } earlier (not here). */ |
5286 | syntax_error_unexpected_ch(ch); | ||
5287 | G.last_exitcode = 2; | 5329 | G.last_exitcode = 2; |
5330 | syntax_error_unexpected_ch(ch); | ||
5288 | goto parse_error2; | 5331 | goto parse_error2; |
5289 | default: | 5332 | default: |
5290 | if (HUSH_DEBUG) | 5333 | if (HUSH_DEBUG) |
@@ -5514,7 +5557,7 @@ static arith_t expand_and_evaluate_arith(const char *arg, const char **errmsg_p) | |||
5514 | if (errmsg_p) | 5557 | if (errmsg_p) |
5515 | *errmsg_p = math_state.errmsg; | 5558 | *errmsg_p = math_state.errmsg; |
5516 | if (math_state.errmsg) | 5559 | if (math_state.errmsg) |
5517 | die_if_script(math_state.errmsg); | 5560 | msg_and_die_if_script(math_state.errmsg); |
5518 | return res; | 5561 | return res; |
5519 | } | 5562 | } |
5520 | #endif | 5563 | #endif |
@@ -5781,7 +5824,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha | |||
5781 | /* in bash, len=-n means strlen()-n */ | 5824 | /* in bash, len=-n means strlen()-n */ |
5782 | len = (arith_t)strlen(val) - beg + len; | 5825 | len = (arith_t)strlen(val) - beg + len; |
5783 | if (len < 0) /* bash compat */ | 5826 | if (len < 0) /* bash compat */ |
5784 | die_if_script("%s: substring expression < 0", var); | 5827 | msg_and_die_if_script("%s: substring expression < 0", var); |
5785 | } | 5828 | } |
5786 | if (len <= 0 || !val || beg >= strlen(val)) { | 5829 | if (len <= 0 || !val || beg >= strlen(val)) { |
5787 | arith_err: | 5830 | arith_err: |
@@ -5795,7 +5838,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha | |||
5795 | } | 5838 | } |
5796 | debug_printf_varexp("val:'%s'\n", val); | 5839 | debug_printf_varexp("val:'%s'\n", val); |
5797 | #else /* not (HUSH_SUBSTR_EXPANSION && FEATURE_SH_MATH) */ | 5840 | #else /* not (HUSH_SUBSTR_EXPANSION && FEATURE_SH_MATH) */ |
5798 | die_if_script("malformed ${%s:...}", var); | 5841 | msg_and_die_if_script("malformed ${%s:...}", var); |
5799 | val = NULL; | 5842 | val = NULL; |
5800 | #endif | 5843 | #endif |
5801 | } else { /* one of "-=+?" */ | 5844 | } else { /* one of "-=+?" */ |
@@ -5832,7 +5875,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha | |||
5832 | exp_word = to_be_freed; | 5875 | exp_word = to_be_freed; |
5833 | if (exp_op == '?') { | 5876 | if (exp_op == '?') { |
5834 | /* mimic bash message */ | 5877 | /* mimic bash message */ |
5835 | die_if_script("%s: %s", | 5878 | msg_and_die_if_script("%s: %s", |
5836 | var, | 5879 | var, |
5837 | exp_word[0] | 5880 | exp_word[0] |
5838 | ? exp_word | 5881 | ? exp_word |
@@ -5849,7 +5892,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha | |||
5849 | /* ${var=[word]} or ${var:=[word]} */ | 5892 | /* ${var=[word]} or ${var:=[word]} */ |
5850 | if (isdigit(var[0]) || var[0] == '#') { | 5893 | if (isdigit(var[0]) || var[0] == '#') { |
5851 | /* mimic bash message */ | 5894 | /* mimic bash message */ |
5852 | die_if_script("$%s: cannot assign in this way", var); | 5895 | msg_and_die_if_script("$%s: cannot assign in this way", var); |
5853 | val = NULL; | 5896 | val = NULL; |
5854 | } else { | 5897 | } else { |
5855 | char *new_var = xasprintf("%s=%s", var, val); | 5898 | char *new_var = xasprintf("%s=%s", var, val); |
@@ -6698,7 +6741,8 @@ static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd) | |||
6698 | int moved_to; | 6741 | int moved_to; |
6699 | int i; | 6742 | int i; |
6700 | 6743 | ||
6701 | if (sq) for (i = 0; sq[i].orig_fd >= 0; i++) { | 6744 | i = 0; |
6745 | if (sq) for (; sq[i].orig_fd >= 0; i++) { | ||
6702 | /* If we collide with an already moved fd... */ | 6746 | /* If we collide with an already moved fd... */ |
6703 | if (fd == sq[i].moved_to) { | 6747 | if (fd == sq[i].moved_to) { |
6704 | sq[i].moved_to = fcntl_F_DUPFD(sq[i].moved_to, avoid_fd); | 6748 | sq[i].moved_to = fcntl_F_DUPFD(sq[i].moved_to, avoid_fd); |
@@ -6726,7 +6770,8 @@ static struct squirrel *add_squirrel_closed(struct squirrel *sq, int fd) | |||
6726 | { | 6770 | { |
6727 | int i; | 6771 | int i; |
6728 | 6772 | ||
6729 | if (sq) for (i = 0; sq[i].orig_fd >= 0; i++) { | 6773 | i = 0; |
6774 | if (sq) for (; sq[i].orig_fd >= 0; i++) { | ||
6730 | /* If we collide with an already moved fd... */ | 6775 | /* If we collide with an already moved fd... */ |
6731 | if (fd == sq[i].orig_fd) { | 6776 | if (fd == sq[i].orig_fd) { |
6732 | /* Examples: | 6777 | /* Examples: |
@@ -6863,7 +6908,7 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp) | |||
6863 | * "cmd >" (no filename) | 6908 | * "cmd >" (no filename) |
6864 | * "cmd > <file" (2nd redirect starts too early) | 6909 | * "cmd > <file" (2nd redirect starts too early) |
6865 | */ | 6910 | */ |
6866 | die_if_script("syntax error: %s", "invalid redirect"); | 6911 | syntax_error("invalid redirect"); |
6867 | continue; | 6912 | continue; |
6868 | } | 6913 | } |
6869 | mode = redir_table[redir->rd_type].mode; | 6914 | mode = redir_table[redir->rd_type].mode; |
@@ -7363,8 +7408,10 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save, | |||
7363 | */ | 7408 | */ |
7364 | close_saved_fds_and_FILE_fds(); | 7409 | close_saved_fds_and_FILE_fds(); |
7365 | //FIXME: should also close saved redir fds | 7410 | //FIXME: should also close saved redir fds |
7411 | /* Without this, "rm -i FILE" can't be ^C'ed: */ | ||
7412 | switch_off_special_sigs(G.special_sig_mask); | ||
7366 | debug_printf_exec("running applet '%s'\n", argv[0]); | 7413 | debug_printf_exec("running applet '%s'\n", argv[0]); |
7367 | run_applet_no_and_exit(a, argv[0], argv); | 7414 | run_noexec_applet_and_exit(a, argv[0], argv); |
7368 | } | 7415 | } |
7369 | # endif | 7416 | # endif |
7370 | /* Re-exec ourselves */ | 7417 | /* Re-exec ourselves */ |
@@ -8045,6 +8092,24 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
8045 | add_vars(old_vars); | 8092 | add_vars(old_vars); |
8046 | /* clean_up_and_ret0: */ | 8093 | /* clean_up_and_ret0: */ |
8047 | restore_redirects(squirrel); | 8094 | restore_redirects(squirrel); |
8095 | /* | ||
8096 | * Try "usleep 99999999" + ^C + "echo $?" | ||
8097 | * with FEATURE_SH_NOFORK=y. | ||
8098 | */ | ||
8099 | if (!funcp) { | ||
8100 | /* It was builtin or nofork. | ||
8101 | * if this would be a real fork/execed program, | ||
8102 | * it should have died if a fatal sig was received. | ||
8103 | * But OTOH, there was no separate process, | ||
8104 | * the sig was sent to _shell_, not to non-existing | ||
8105 | * child. | ||
8106 | * Let's just handle ^C only, this one is obvious: | ||
8107 | * we aren't ok with exitcode 0 when ^C was pressed | ||
8108 | * during builtin/nofork. | ||
8109 | */ | ||
8110 | if (sigismember(&G.pending_set, SIGINT)) | ||
8111 | rcode = 128 + SIGINT; | ||
8112 | } | ||
8048 | clean_up_and_ret1: | 8113 | clean_up_and_ret1: |
8049 | free(argv_expanded); | 8114 | free(argv_expanded); |
8050 | IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;) | 8115 | IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;) |
@@ -8060,6 +8125,14 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
8060 | if (rcode == 0) { | 8125 | if (rcode == 0) { |
8061 | debug_printf_exec(": run_nofork_applet '%s' '%s'...\n", | 8126 | debug_printf_exec(": run_nofork_applet '%s' '%s'...\n", |
8062 | argv_expanded[0], argv_expanded[1]); | 8127 | argv_expanded[0], argv_expanded[1]); |
8128 | /* | ||
8129 | * Note: signals (^C) can't interrupt here. | ||
8130 | * We remember them and they will be acted upon | ||
8131 | * after applet returns. | ||
8132 | * This makes applets which can run for a long time | ||
8133 | * and/or wait for user input ineligible for NOFORK: | ||
8134 | * for example, "yes" or "rm" (rm -i waits for input). | ||
8135 | */ | ||
8063 | rcode = run_nofork_applet(n, argv_expanded); | 8136 | rcode = run_nofork_applet(n, argv_expanded); |
8064 | } | 8137 | } |
8065 | goto clean_up_and_ret; | 8138 | goto clean_up_and_ret; |
@@ -8491,7 +8564,7 @@ static int run_list(struct pipe *pi) | |||
8491 | G.last_bg_pid = pi->cmds[pi->num_cmds - 1].pid; | 8564 | G.last_bg_pid = pi->cmds[pi->num_cmds - 1].pid; |
8492 | G.last_bg_pid_exitcode = 0; | 8565 | G.last_bg_pid_exitcode = 0; |
8493 | debug_printf_exec(": cmd&: exitcode EXIT_SUCCESS\n"); | 8566 | debug_printf_exec(": cmd&: exitcode EXIT_SUCCESS\n"); |
8494 | /* Check pi->pi_inverted? "! sleep 1 & echo $?": bash says 1. dash and ash says 0 */ | 8567 | /* Check pi->pi_inverted? "! sleep 1 & echo $?": bash says 1. dash and ash say 0 */ |
8495 | rcode = EXIT_SUCCESS; | 8568 | rcode = EXIT_SUCCESS; |
8496 | goto check_traps; | 8569 | goto check_traps; |
8497 | } else { | 8570 | } else { |
@@ -8600,6 +8673,10 @@ static void install_sighandlers(unsigned mask) | |||
8600 | */ | 8673 | */ |
8601 | if (sig == SIGCHLD) | 8674 | if (sig == SIGCHLD) |
8602 | continue; | 8675 | continue; |
8676 | /* bash re-enables SIGHUP which is SIG_IGNed on entry. | ||
8677 | * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$" | ||
8678 | */ | ||
8679 | //if (sig == SIGHUP) continue; - TODO? | ||
8603 | if (old_handler == SIG_IGN) { | 8680 | if (old_handler == SIG_IGN) { |
8604 | /* oops... restore back to IGN, and record this fact */ | 8681 | /* oops... restore back to IGN, and record this fact */ |
8605 | install_sighandler(sig, old_handler); | 8682 | install_sighandler(sig, old_handler); |
@@ -9381,13 +9458,20 @@ static int FAST_FUNC builtin_read(char **argv) | |||
9381 | char *opt_p = NULL; | 9458 | char *opt_p = NULL; |
9382 | char *opt_t = NULL; | 9459 | char *opt_t = NULL; |
9383 | char *opt_u = NULL; | 9460 | char *opt_u = NULL; |
9461 | char *opt_d = NULL; /* optimized out if !BASH */ | ||
9384 | const char *ifs; | 9462 | const char *ifs; |
9385 | int read_flags; | 9463 | int read_flags; |
9386 | 9464 | ||
9387 | /* "!": do not abort on errors. | 9465 | /* "!": do not abort on errors. |
9388 | * Option string must start with "sr" to match BUILTIN_READ_xxx | 9466 | * Option string must start with "sr" to match BUILTIN_READ_xxx |
9389 | */ | 9467 | */ |
9390 | read_flags = getopt32(argv, "!srn:p:t:u:", &opt_n, &opt_p, &opt_t, &opt_u); | 9468 | read_flags = getopt32(argv, |
9469 | #if BASH_READ_D | ||
9470 | "!srn:p:t:u:d:", &opt_n, &opt_p, &opt_t, &opt_u, &opt_d | ||
9471 | #else | ||
9472 | "!srn:p:t:u:", &opt_n, &opt_p, &opt_t, &opt_u | ||
9473 | #endif | ||
9474 | ); | ||
9391 | if (read_flags == (uint32_t)-1) | 9475 | if (read_flags == (uint32_t)-1) |
9392 | return EXIT_FAILURE; | 9476 | return EXIT_FAILURE; |
9393 | argv += optind; | 9477 | argv += optind; |
@@ -9401,7 +9485,8 @@ static int FAST_FUNC builtin_read(char **argv) | |||
9401 | opt_n, | 9485 | opt_n, |
9402 | opt_p, | 9486 | opt_p, |
9403 | opt_t, | 9487 | opt_t, |
9404 | opt_u | 9488 | opt_u, |
9489 | opt_d | ||
9405 | ); | 9490 | ); |
9406 | 9491 | ||
9407 | if ((uintptr_t)r == 1 && errno == EINTR) { | 9492 | if ((uintptr_t)r == 1 && errno == EINTR) { |
@@ -9786,6 +9871,93 @@ static int FAST_FUNC builtin_shift(char **argv) | |||
9786 | return EXIT_FAILURE; | 9871 | return EXIT_FAILURE; |
9787 | } | 9872 | } |
9788 | 9873 | ||
9874 | #if ENABLE_HUSH_GETOPTS | ||
9875 | static int FAST_FUNC builtin_getopts(char **argv) | ||
9876 | { | ||
9877 | /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/getopts.html | ||
9878 | |||
9879 | TODO: | ||
9880 | If a required argument is not found, and getopts is not silent, | ||
9881 | a question mark (?) is placed in VAR, OPTARG is unset, and a | ||
9882 | diagnostic message is printed. If getopts is silent, then a | ||
9883 | colon (:) is placed in VAR and OPTARG is set to the option | ||
9884 | character found. | ||
9885 | |||
9886 | Test that VAR is a valid variable name? | ||
9887 | |||
9888 | "Whenever the shell is invoked, OPTIND shall be initialized to 1" | ||
9889 | */ | ||
9890 | char cbuf[2]; | ||
9891 | const char *cp, *optstring, *var; | ||
9892 | int c, exitcode; | ||
9893 | |||
9894 | optstring = *++argv; | ||
9895 | if (!optstring || !(var = *++argv)) { | ||
9896 | bb_error_msg("usage: getopts OPTSTRING VAR [ARGS]"); | ||
9897 | return EXIT_FAILURE; | ||
9898 | } | ||
9899 | |||
9900 | c = 0; | ||
9901 | if (optstring[0] != ':') { | ||
9902 | cp = get_local_var_value("OPTERR"); | ||
9903 | /* 0 if "OPTERR=0", 1 otherwise */ | ||
9904 | c = (!cp || NOT_LONE_CHAR(cp, '0')); | ||
9905 | } | ||
9906 | opterr = c; | ||
9907 | cp = get_local_var_value("OPTIND"); | ||
9908 | optind = cp ? atoi(cp) : 0; | ||
9909 | optarg = NULL; | ||
9910 | cbuf[1] = '\0'; | ||
9911 | |||
9912 | /* getopts stops on first non-option. Add "+" to force that */ | ||
9913 | /*if (optstring[0] != '+')*/ { | ||
9914 | char *s = alloca(strlen(optstring) + 2); | ||
9915 | sprintf(s, "+%s", optstring); | ||
9916 | optstring = s; | ||
9917 | } | ||
9918 | |||
9919 | if (argv[1]) | ||
9920 | argv[0] = G.global_argv[0]; /* for error messages */ | ||
9921 | else | ||
9922 | argv = G.global_argv; | ||
9923 | c = getopt(string_array_len(argv), argv, optstring); | ||
9924 | |||
9925 | /* Set OPTARG */ | ||
9926 | /* Always set or unset, never left as-is, even on exit/error: | ||
9927 | * "If no option was found, or if the option that was found | ||
9928 | * does not have an option-argument, OPTARG shall be unset." | ||
9929 | */ | ||
9930 | cp = optarg; | ||
9931 | if (c == '?') { | ||
9932 | /* If ":optstring" and unknown option is seen, | ||
9933 | * it is stored to OPTARG. | ||
9934 | */ | ||
9935 | if (optstring[1] == ':') { | ||
9936 | cbuf[0] = optopt; | ||
9937 | cp = cbuf; | ||
9938 | } | ||
9939 | } | ||
9940 | if (cp) | ||
9941 | set_local_var_from_halves("OPTARG", cp); | ||
9942 | else | ||
9943 | unset_local_var("OPTARG"); | ||
9944 | |||
9945 | /* Convert -1 to "?" */ | ||
9946 | exitcode = EXIT_SUCCESS; | ||
9947 | if (c < 0) { /* -1: end of options */ | ||
9948 | exitcode = EXIT_FAILURE; | ||
9949 | c = '?'; | ||
9950 | } | ||
9951 | |||
9952 | /* Set VAR and OPTIND */ | ||
9953 | cbuf[0] = c; | ||
9954 | set_local_var_from_halves(var, cbuf); | ||
9955 | set_local_var_from_halves("OPTIND", utoa(optind)); | ||
9956 | |||
9957 | return exitcode; | ||
9958 | } | ||
9959 | #endif | ||
9960 | |||
9789 | static int FAST_FUNC builtin_source(char **argv) | 9961 | static int FAST_FUNC builtin_source(char **argv) |
9790 | { | 9962 | { |
9791 | char *arg_path, *filename; | 9963 | char *arg_path, *filename; |
@@ -10178,6 +10350,7 @@ static int wait_for_child_or_signal(struct pipe *waitfor_pipe, pid_t waitfor_pid | |||
10178 | /* So, did we get a signal? */ | 10350 | /* So, did we get a signal? */ |
10179 | sig = check_and_run_traps(); | 10351 | sig = check_and_run_traps(); |
10180 | if (sig /*&& sig != SIGCHLD - always true */) { | 10352 | if (sig /*&& sig != SIGCHLD - always true */) { |
10353 | /* Do this for any (non-ignored) signal, not only for ^C */ | ||
10181 | ret = 128 + sig; | 10354 | ret = 128 + sig; |
10182 | break; | 10355 | break; |
10183 | } | 10356 | } |
@@ -10344,6 +10517,41 @@ static int FAST_FUNC builtin_return(char **argv) | |||
10344 | } | 10517 | } |
10345 | #endif | 10518 | #endif |
10346 | 10519 | ||
10520 | #if ENABLE_HUSH_TIMES | ||
10521 | static int FAST_FUNC builtin_times(char **argv UNUSED_PARAM) | ||
10522 | { | ||
10523 | static const uint8_t times_tbl[] ALIGN1 = { | ||
10524 | ' ', offsetof(struct tms, tms_utime), | ||
10525 | '\n', offsetof(struct tms, tms_stime), | ||
10526 | ' ', offsetof(struct tms, tms_cutime), | ||
10527 | '\n', offsetof(struct tms, tms_cstime), | ||
10528 | 0 | ||
10529 | }; | ||
10530 | const uint8_t *p; | ||
10531 | unsigned clk_tck; | ||
10532 | struct tms buf; | ||
10533 | |||
10534 | clk_tck = bb_clk_tck(); | ||
10535 | |||
10536 | times(&buf); | ||
10537 | p = times_tbl; | ||
10538 | do { | ||
10539 | unsigned sec, frac; | ||
10540 | unsigned long t; | ||
10541 | t = *(clock_t *)(((char *) &buf) + p[1]); | ||
10542 | sec = t / clk_tck; | ||
10543 | frac = t % clk_tck; | ||
10544 | printf("%um%u.%03us%c", | ||
10545 | sec / 60, sec % 60, | ||
10546 | (frac * 1000) / clk_tck, | ||
10547 | p[0]); | ||
10548 | p += 2; | ||
10549 | } while (*p); | ||
10550 | |||
10551 | return EXIT_SUCCESS; | ||
10552 | } | ||
10553 | #endif | ||
10554 | |||
10347 | #if ENABLE_HUSH_MEMLEAK | 10555 | #if ENABLE_HUSH_MEMLEAK |
10348 | static int FAST_FUNC builtin_memleak(char **argv UNUSED_PARAM) | 10556 | static int FAST_FUNC builtin_memleak(char **argv UNUSED_PARAM) |
10349 | { | 10557 | { |
diff --git a/shell/hush_test/hush-getopts/getopt_optarg.right b/shell/hush_test/hush-getopts/getopt_optarg.right new file mode 100644 index 000000000..9dbd8460e --- /dev/null +++ b/shell/hush_test/hush-getopts/getopt_optarg.right | |||
@@ -0,0 +1,18 @@ | |||
1 | *** no OPTIND, optstring:'w:et' args:-q -w e -r -t -y | ||
2 | ./getopt_optarg.tests: invalid option -- q | ||
3 | var:'?' OPTIND:2 OPTARG:'' | ||
4 | var:'w' OPTIND:4 OPTARG:'e' | ||
5 | ./getopt_optarg.tests: invalid option -- r | ||
6 | var:'?' OPTIND:5 OPTARG:'' | ||
7 | var:'t' OPTIND:6 OPTARG:'' | ||
8 | ./getopt_optarg.tests: invalid option -- y | ||
9 | var:'?' OPTIND:7 OPTARG:'' | ||
10 | exited: var:'?' OPTIND:7 OPTARG:'' | ||
11 | *** OPTIND=0, optstring:'w:et' args:-w 1 -w2 -w -e -e -t -t | ||
12 | var:'w' OPTIND:3 OPTARG:'1' | ||
13 | var:'w' OPTIND:4 OPTARG:'2' | ||
14 | var:'w' OPTIND:6 OPTARG:'-e' | ||
15 | var:'e' OPTIND:7 OPTARG:'' | ||
16 | var:'t' OPTIND:8 OPTARG:'' | ||
17 | var:'t' OPTIND:9 OPTARG:'' | ||
18 | exited: var:'?' OPTIND:9 OPTARG:'' | ||
diff --git a/shell/hush_test/hush-getopts/getopt_optarg.tests b/shell/hush_test/hush-getopts/getopt_optarg.tests new file mode 100755 index 000000000..881cc7884 --- /dev/null +++ b/shell/hush_test/hush-getopts/getopt_optarg.tests | |||
@@ -0,0 +1,24 @@ | |||
1 | ( | ||
2 | |||
3 | set -- -q -w e -r -t -y | ||
4 | echo "*** no OPTIND, optstring:'w:et' args:$*" | ||
5 | var=QWERTY | ||
6 | OPTARG=ASDFGH | ||
7 | while getopts "w:et" var; do | ||
8 | echo "var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
9 | OPTARG=ASDFGH | ||
10 | done | ||
11 | echo "exited: var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
12 | |||
13 | set -- -w 1 -w2 -w -e -e -t -t | ||
14 | echo "*** OPTIND=0, optstring:'w:et' args:$*" | ||
15 | OPTIND=0 | ||
16 | while getopts "w:et" var; do | ||
17 | echo "var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
18 | OPTARG=ASDFGH | ||
19 | done | ||
20 | echo "exited: var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
21 | |||
22 | ) 2>&1 \ | ||
23 | | sed -e 's/ unrecognized option: / invalid option -- /' \ | ||
24 | -e 's/ illegal option -- / invalid option -- /' \ | ||
diff --git a/shell/hush_test/hush-getopts/getopt_positional.right b/shell/hush_test/hush-getopts/getopt_positional.right new file mode 100644 index 000000000..f1c942476 --- /dev/null +++ b/shell/hush_test/hush-getopts/getopt_positional.right | |||
@@ -0,0 +1,6 @@ | |||
1 | *** no OPTIND, optstring:'we' args:-q -w -e r -t -y | ||
2 | ./getopt_positional.tests: invalid option -- q | ||
3 | var:'?' OPTIND:2 | ||
4 | var:'w' OPTIND:3 | ||
5 | var:'e' OPTIND:4 | ||
6 | exited: var:'?' OPTIND:4 | ||
diff --git a/shell/hush_test/hush-getopts/getopt_positional.tests b/shell/hush_test/hush-getopts/getopt_positional.tests new file mode 100755 index 000000000..20716bb0c --- /dev/null +++ b/shell/hush_test/hush-getopts/getopt_positional.tests | |||
@@ -0,0 +1,13 @@ | |||
1 | ( | ||
2 | |||
3 | set -- -q -w -e r -t -y | ||
4 | echo "*** no OPTIND, optstring:'we' args:$*" | ||
5 | var=QWERTY | ||
6 | while getopts "we" var; do | ||
7 | echo "var:'$var' OPTIND:$OPTIND" | ||
8 | done | ||
9 | echo "exited: var:'$var' OPTIND:$OPTIND" | ||
10 | |||
11 | ) 2>&1 \ | ||
12 | | sed -e 's/ unrecognized option: / invalid option -- /' \ | ||
13 | -e 's/ illegal option -- / invalid option -- /' \ | ||
diff --git a/shell/hush_test/hush-getopts/getopt_silent.right b/shell/hush_test/hush-getopts/getopt_silent.right new file mode 100644 index 000000000..03d4eb149 --- /dev/null +++ b/shell/hush_test/hush-getopts/getopt_silent.right | |||
@@ -0,0 +1,6 @@ | |||
1 | *** optstring:':ac' args:-a -b -c | ||
2 | 1 rc:0 var:'a' OPTIND:2 OPTARG:'' | ||
3 | 2 rc:0 var:'?' OPTIND:3 OPTARG:'b' | ||
4 | 3 rc:0 var:'c' OPTIND:4 OPTARG:'' | ||
5 | 4 rc:1 var:'?' OPTIND:4 OPTARG:'' | ||
6 | 5 rc:1 var:'?' OPTIND:4 OPTARG:'' | ||
diff --git a/shell/hush_test/hush-getopts/getopt_silent.tests b/shell/hush_test/hush-getopts/getopt_silent.tests new file mode 100755 index 000000000..5f255db7f --- /dev/null +++ b/shell/hush_test/hush-getopts/getopt_silent.tests | |||
@@ -0,0 +1,29 @@ | |||
1 | # Open Group Base Specifications Issue 7: | ||
2 | # """ | ||
3 | # If an unknown option is met, VAR shall be set to "?". In this case, | ||
4 | # if the first character in optstring is ":", OPTARG shall be set | ||
5 | # to the option character found, but no output shall be written | ||
6 | # to standard error; otherwise, the shell variable OPTARG shall be | ||
7 | # unset and a diagnostic message shall be written to standard error." | ||
8 | # ... | ||
9 | # If an option-argument is missing: | ||
10 | # If the first character of optstring is ":", VAR shall be set to ":" | ||
11 | # and OPTARG shall be set to the option character found. | ||
12 | # """ | ||
13 | |||
14 | ( | ||
15 | |||
16 | echo "*** optstring:':ac' args:-a -b -c" | ||
17 | getopts ":ac" var -a -b -c; echo "1 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
18 | getopts ":ac" var -a -b -c; echo "2 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
19 | getopts ":ac" var -a -b -c; echo "3 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
20 | getopts ":ac" var -a -b -c; echo "4 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
21 | # Previous line should result in "rc:1", which is normally treated | ||
22 | # in getopts loops as exit condition. | ||
23 | # Nevertheless, let's verify that calling it yet another time doesn't do | ||
24 | # anything weird: | ||
25 | getopts ":ac" var -a -b -c; echo "5 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
26 | |||
27 | ) 2>&1 \ | ||
28 | | sed -e 's/ unrecognized option: / invalid option -- /' \ | ||
29 | -e 's/ illegal option -- / invalid option -- /' \ | ||
diff --git a/shell/hush_test/hush-getopts/getopt_simple.right b/shell/hush_test/hush-getopts/getopt_simple.right new file mode 100644 index 000000000..b4855fa1a --- /dev/null +++ b/shell/hush_test/hush-getopts/getopt_simple.right | |||
@@ -0,0 +1,34 @@ | |||
1 | *** no OPTIND, optstring:'ab' args:-a -b c | ||
2 | var:'a' OPTIND:2 | ||
3 | var:'b' OPTIND:3 | ||
4 | exited: rc:0 var:'?' OPTIND:3 | ||
5 | *** OPTIND=1, optstring:'ab' args:-a -b c | ||
6 | var:'a' OPTIND:2 | ||
7 | var:'b' OPTIND:3 | ||
8 | exited: rc:0 var:'?' OPTIND:3 | ||
9 | *** OPTIND=0, optstring:'ab' args:-a -b c | ||
10 | var:'a' OPTIND:2 | ||
11 | var:'b' OPTIND:3 | ||
12 | exited: rc:0 var:'?' OPTIND:3 | ||
13 | *** unset OPTIND, optstring:'ab' args:-a -b c | ||
14 | var:'a' OPTIND:2 | ||
15 | var:'b' OPTIND:3 | ||
16 | exited: rc:0 var:'?' OPTIND:3 | ||
17 | *** optstring:'ab' args:-a -b c | ||
18 | 1 rc:0 var:'a' OPTIND:2 | ||
19 | 2 rc:0 var:'b' OPTIND:3 | ||
20 | 3 rc:1 var:'?' OPTIND:3 | ||
21 | *** unset OPTIND, optstring:'ab' args:-a c -c -b d | ||
22 | var:'a' OPTIND:2 | ||
23 | exited: rc:0 var:'?' OPTIND:2 | ||
24 | *** unset OPTIND, optstring:'ab' args:-a -c -b d | ||
25 | var:'a' OPTIND:2 | ||
26 | ./getopt_simple.tests: invalid option -- c | ||
27 | var:'?' OPTIND:3 | ||
28 | var:'b' OPTIND:4 | ||
29 | exited: rc:0 var:'?' OPTIND:4 | ||
30 | *** unset OPTIND, OPTERR=0, optstring:'ab' args:-a -c -b d | ||
31 | var:'a' OPTIND:2 | ||
32 | var:'?' OPTIND:3 | ||
33 | var:'b' OPTIND:4 | ||
34 | exited: rc:0 var:'?' OPTIND:4 | ||
diff --git a/shell/hush_test/hush-getopts/getopt_simple.tests b/shell/hush_test/hush-getopts/getopt_simple.tests new file mode 100755 index 000000000..50718cc98 --- /dev/null +++ b/shell/hush_test/hush-getopts/getopt_simple.tests | |||
@@ -0,0 +1,81 @@ | |||
1 | # Simple usage cases for getopts. | ||
2 | # | ||
3 | # OPTIND is either not touched at all (first loop with getopts, | ||
4 | # relying on shell startup init), or getopts state is reset | ||
5 | # before new loop with "unset OPTIND", "OPTIND=1" or "OPTIND=0". | ||
6 | # | ||
7 | # Each option is a separate argument (no "-abc"). This conceptually | ||
8 | # needs only $OPTIND to hold getopts state. | ||
9 | # | ||
10 | # We check that loop does not stop on unknown option (sets "?"), | ||
11 | # stops on _first_ non-option argument. | ||
12 | |||
13 | ( | ||
14 | |||
15 | echo "*** no OPTIND, optstring:'ab' args:-a -b c" | ||
16 | var=QWERTY | ||
17 | while getopts "ab" var -a -b c; do | ||
18 | echo "var:'$var' OPTIND:$OPTIND" | ||
19 | done | ||
20 | # unfortunately, "rc:0" is shown since while's overall exitcode is "success" | ||
21 | echo "exited: rc:$? var:'$var' OPTIND:$OPTIND" | ||
22 | |||
23 | # Resetting behavior =1 | ||
24 | echo "*** OPTIND=1, optstring:'ab' args:-a -b c" | ||
25 | OPTIND=1 | ||
26 | while getopts "ab" var -a -b c; do | ||
27 | echo "var:'$var' OPTIND:$OPTIND" | ||
28 | done | ||
29 | echo "exited: rc:$? var:'$var' OPTIND:$OPTIND" | ||
30 | |||
31 | # Resetting behavior =0 | ||
32 | echo "*** OPTIND=0, optstring:'ab' args:-a -b c" | ||
33 | OPTIND=0 | ||
34 | while getopts "ab" var -a -b c; do | ||
35 | echo "var:'$var' OPTIND:$OPTIND" | ||
36 | done | ||
37 | echo "exited: rc:$? var:'$var' OPTIND:$OPTIND" | ||
38 | |||
39 | # Resetting behavior "unset" | ||
40 | echo "*** unset OPTIND, optstring:'ab' args:-a -b c" | ||
41 | unset OPTIND | ||
42 | while getopts "ab" var -a -b c; do | ||
43 | echo "var:'$var' OPTIND:$OPTIND" | ||
44 | done | ||
45 | echo "exited: rc:$? var:'$var' OPTIND:$OPTIND" | ||
46 | |||
47 | # What is the final exitcode? | ||
48 | echo "*** optstring:'ab' args:-a -b c" | ||
49 | unset OPTIND | ||
50 | getopts "ab" var -a -b c; echo "1 rc:$? var:'$var' OPTIND:$OPTIND" | ||
51 | getopts "ab" var -a -b c; echo "2 rc:$? var:'$var' OPTIND:$OPTIND" | ||
52 | getopts "ab" var -a -b c; echo "3 rc:$? var:'$var' OPTIND:$OPTIND" | ||
53 | |||
54 | # Where would it stop? c or -c? | ||
55 | echo "*** unset OPTIND, optstring:'ab' args:-a c -c -b d" | ||
56 | unset OPTIND | ||
57 | while getopts "ab" var -a c -c -b d; do | ||
58 | echo "var:'$var' OPTIND:$OPTIND" | ||
59 | done | ||
60 | echo "exited: rc:$? var:'$var' OPTIND:$OPTIND" | ||
61 | |||
62 | # What happens on unknown option? | ||
63 | echo "*** unset OPTIND, optstring:'ab' args:-a -c -b d" | ||
64 | unset OPTIND | ||
65 | while getopts "ab" var -a -c -b d; do | ||
66 | echo "var:'$var' OPTIND:$OPTIND" | ||
67 | done | ||
68 | echo "exited: rc:$? var:'$var' OPTIND:$OPTIND" | ||
69 | |||
70 | # ORTERR=0 suppresses error message? | ||
71 | echo "*** unset OPTIND, OPTERR=0, optstring:'ab' args:-a -c -b d" | ||
72 | unset OPTIND | ||
73 | OPTERR=0 | ||
74 | while getopts "ab" var -a -c -b d; do | ||
75 | echo "var:'$var' OPTIND:$OPTIND" | ||
76 | done | ||
77 | echo "exited: rc:$? var:'$var' OPTIND:$OPTIND" | ||
78 | |||
79 | ) 2>&1 \ | ||
80 | | sed -e 's/ unrecognized option: / invalid option -- /' \ | ||
81 | -e 's/ illegal option -- / invalid option -- /' \ | ||
diff --git a/shell/hush_test/hush-getopts/getopt_test_libc_bug.right b/shell/hush_test/hush-getopts/getopt_test_libc_bug.right new file mode 100644 index 000000000..6694e8f0c --- /dev/null +++ b/shell/hush_test/hush-getopts/getopt_test_libc_bug.right | |||
@@ -0,0 +1,26 @@ | |||
1 | *** optstring:'ac' args:-a -b -c -d e | ||
2 | 1 rc:0 var:'a' OPTIND:2 OPTARG:'' | ||
3 | ./getopt_test_libc_bug.tests: invalid option -- b | ||
4 | 2 rc:0 var:'?' OPTIND:3 OPTARG:'' | ||
5 | 3 rc:0 var:'c' OPTIND:4 OPTARG:'' | ||
6 | ./getopt_test_libc_bug.tests: invalid option -- d | ||
7 | 4 rc:0 var:'?' OPTIND:5 OPTARG:'' | ||
8 | 5 rc:1 var:'?' OPTIND:5 OPTARG:'' | ||
9 | |||
10 | *** optstring:'ac' args:-a -b -c -d e | ||
11 | 1 rc:0 var:'a' OPTIND:2 OPTARG:'' | ||
12 | ./getopt_test_libc_bug.tests: invalid option -- b | ||
13 | 2 rc:0 var:'?' OPTIND:3 OPTARG:'' | ||
14 | 3 rc:0 var:'c' OPTIND:4 OPTARG:'' | ||
15 | ./getopt_test_libc_bug.tests: invalid option -- d | ||
16 | 4 rc:0 var:'?' OPTIND:5 OPTARG:'' | ||
17 | 5 rc:1 var:'?' OPTIND:5 OPTARG:'' | ||
18 | |||
19 | *** optstring:'ac' args:-a -b -c -d e | ||
20 | 1 rc:0 var:'a' OPTIND:2 OPTARG:'' | ||
21 | ./getopt_test_libc_bug.tests: invalid option -- b | ||
22 | 2 rc:0 var:'?' OPTIND:3 OPTARG:'' | ||
23 | 3 rc:0 var:'c' OPTIND:4 OPTARG:'' | ||
24 | ./getopt_test_libc_bug.tests: invalid option -- d | ||
25 | 4 rc:0 var:'?' OPTIND:5 OPTARG:'' | ||
26 | 5 rc:1 var:'?' OPTIND:5 OPTARG:'' | ||
diff --git a/shell/hush_test/hush-getopts/getopt_test_libc_bug.tests b/shell/hush_test/hush-getopts/getopt_test_libc_bug.tests new file mode 100755 index 000000000..ab7bc3b09 --- /dev/null +++ b/shell/hush_test/hush-getopts/getopt_test_libc_bug.tests | |||
@@ -0,0 +1,44 @@ | |||
1 | # This test can fail with libc with buggy getopt() implementation. | ||
2 | # If getopt() wants to parse multi-option args (-abc), | ||
3 | # it needs to remember a position within current arg. | ||
4 | # | ||
5 | # If this position is kept as a POINTER, not an offset, | ||
6 | # and if argv[] ADDRESSES (not contents!) change, it blows up. | ||
7 | |||
8 | ( | ||
9 | |||
10 | echo "*** optstring:'ac' args:-a -b -c -d e" | ||
11 | getopts "ac" var -a -b -c -d e; echo "1 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
12 | getopts "ac" var -a -b -c -d e; echo "2 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
13 | getopts "ac" var -a -b -c -d e; echo "3 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
14 | getopts "ac" var -a -b -c -d e; echo "4 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
15 | getopts "ac" var -a -b -c -d e; echo "5 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
16 | |||
17 | # Above: args are (usually) in the same locations in memory. | ||
18 | # Below: variable allocations change the location. | ||
19 | |||
20 | echo | ||
21 | echo "*** optstring:'ac' args:-a -b -c -d e" | ||
22 | unset OPTIND | ||
23 | OPTARG=QWERTY; getopts "ac" var -a -b -c -d e; echo "1 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
24 | NEWVAR=NEWVAL; getopts "ac" var -a -b -c -d e; echo "2 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
25 | VAR111=NEWVAL; getopts "ac" var -a -b -c -d e; echo "3 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
26 | VAR222=NEWVAL; getopts "ac" var -a -b -c -d e; echo "4 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
27 | VAR333=NEWVAL; getopts "ac" var -a -b -c -d e; echo "5 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
28 | |||
29 | # Slightly different attempts to force reallocations | ||
30 | |||
31 | echo | ||
32 | echo "*** optstring:'ac' args:-a -b -c -d e" | ||
33 | unset OPTIND | ||
34 | export OPTARG; getopts "ac" var -a -b -c -d e; echo "1 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
35 | export NEWVAR; getopts "ac" var -a -b -c -d e; echo "2 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
36 | export VAR111; getopts "ac" var -a -b -c -d e; echo "3 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
37 | export VAR222; getopts "ac" var -a -b -c -d e; echo "4 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
38 | export VAR333; getopts "ac" var -a -b -c -d e; echo "5 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'" | ||
39 | |||
40 | # All copies of code above should generate identical output | ||
41 | |||
42 | ) 2>&1 \ | ||
43 | | sed -e 's/ unrecognized option: / invalid option -- /' \ | ||
44 | -e 's/ illegal option -- / invalid option -- /' \ | ||
diff --git a/shell/hush_test/hush-parsing/groups_and_keywords2.right b/shell/hush_test/hush-parsing/groups_and_keywords2.right new file mode 100644 index 000000000..ae74a5db9 --- /dev/null +++ b/shell/hush_test/hush-parsing/groups_and_keywords2.right | |||
@@ -0,0 +1,3 @@ | |||
1 | hush: syntax error: unexpected ) | ||
2 | Fail:2 | ||
3 | hush: syntax error: unexpected ) | ||
diff --git a/shell/hush_test/hush-parsing/groups_and_keywords2.tests b/shell/hush_test/hush-parsing/groups_and_keywords2.tests new file mode 100755 index 000000000..ab33b909f --- /dev/null +++ b/shell/hush_test/hush-parsing/groups_and_keywords2.tests | |||
@@ -0,0 +1,9 @@ | |||
1 | # This is an error | ||
2 | (eval 'if() { echo; }') | ||
3 | echo Fail:$? | ||
4 | # ^^^^^^ bash prints 1, but interactively it sets $? = 2 | ||
5 | # we print 2 | ||
6 | |||
7 | # This is an error, and it aborts in script | ||
8 | if() { echo; } | ||
9 | echo Not reached | ||
diff --git a/shell/hush_test/run-all b/shell/hush_test/run-all index 1dd0edc39..3fbc7c531 100755 --- a/shell/hush_test/run-all +++ b/shell/hush_test/run-all | |||
@@ -80,7 +80,7 @@ do_test() | |||
80 | case $? in | 80 | case $? in |
81 | 0) echo " ok";; | 81 | 0) echo " ok";; |
82 | 77) echo " skip (feature disabled)";; | 82 | 77) echo " skip (feature disabled)";; |
83 | *) echo " fail"; tret=1;; | 83 | *) echo " fail ($?)"; tret=1;; |
84 | esac | 84 | esac |
85 | done | 85 | done |
86 | exit ${tret} | 86 | exit ${tret} |
diff --git a/shell/math.c b/shell/math.c index 006221b6a..f01f24362 100644 --- a/shell/math.c +++ b/shell/math.c | |||
@@ -743,7 +743,7 @@ arith(arith_state_t *math_state, const char *expr) | |||
743 | * may be used to endorse or promote products derived from this software | 743 | * may be used to endorse or promote products derived from this software |
744 | * without specific prior written permission. | 744 | * without specific prior written permission. |
745 | * | 745 | * |
746 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | 746 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND |
747 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 747 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
748 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 748 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
749 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | 749 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
diff --git a/shell/shell_common.c b/shell/shell_common.c index 750adc5d8..bc34de404 100644 --- a/shell/shell_common.c +++ b/shell/shell_common.c | |||
@@ -58,7 +58,8 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), | |||
58 | const char *opt_n, | 58 | const char *opt_n, |
59 | const char *opt_p, | 59 | const char *opt_p, |
60 | const char *opt_t, | 60 | const char *opt_t, |
61 | const char *opt_u | 61 | const char *opt_u, |
62 | const char *opt_d | ||
62 | ) | 63 | ) |
63 | { | 64 | { |
64 | struct pollfd pfd[1]; | 65 | struct pollfd pfd[1]; |
@@ -68,6 +69,7 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), | |||
68 | int nchars; /* -n NUM */ | 69 | int nchars; /* -n NUM */ |
69 | char **pp; | 70 | char **pp; |
70 | char *buffer; | 71 | char *buffer; |
72 | char delim; | ||
71 | struct termios tty, old_tty; | 73 | struct termios tty, old_tty; |
72 | const char *retval; | 74 | const char *retval; |
73 | int bufpos; /* need to be able to hold -1 */ | 75 | int bufpos; /* need to be able to hold -1 */ |
@@ -188,6 +190,7 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), | |||
188 | end_ms += (unsigned)monotonic_ms(); | 190 | end_ms += (unsigned)monotonic_ms(); |
189 | buffer = NULL; | 191 | buffer = NULL; |
190 | bufpos = 0; | 192 | bufpos = 0; |
193 | delim = opt_d ? *opt_d : '\n'; | ||
191 | do { | 194 | do { |
192 | char c; | 195 | char c; |
193 | int timeout; | 196 | int timeout; |
@@ -243,14 +246,14 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), | |||
243 | continue; | 246 | continue; |
244 | } | 247 | } |
245 | } | 248 | } |
246 | if (c == '\n') | 249 | if (c == delim) /* '\n' or -d CHAR */ |
247 | break; | 250 | break; |
248 | 251 | ||
249 | /* $IFS splitting. NOT done if we run "read" | 252 | /* $IFS splitting. NOT done if we run "read" |
250 | * without variable names (bash compat). | 253 | * without variable names (bash compat). |
251 | * Thus, "read" and "read REPLY" are not the same. | 254 | * Thus, "read" and "read REPLY" are not the same. |
252 | */ | 255 | */ |
253 | if (argv[0]) { | 256 | if (!opt_d && argv[0]) { |
254 | /* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 */ | 257 | /* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 */ |
255 | const char *is_ifs = strchr(ifs, c); | 258 | const char *is_ifs = strchr(ifs, c); |
256 | if (startword && is_ifs) { | 259 | if (startword && is_ifs) { |
diff --git a/shell/shell_common.h b/shell/shell_common.h index a82535c86..875fd9ea7 100644 --- a/shell/shell_common.h +++ b/shell/shell_common.h | |||
@@ -34,6 +34,11 @@ enum { | |||
34 | BUILTIN_READ_SILENT = 1 << 0, | 34 | BUILTIN_READ_SILENT = 1 << 0, |
35 | BUILTIN_READ_RAW = 1 << 1, | 35 | BUILTIN_READ_RAW = 1 << 1, |
36 | }; | 36 | }; |
37 | //TODO? do not provide bashisms if not asked for: | ||
38 | //#if !ENABLE_HUSH_BASH_COMPAT && !ENABLE_ASH_BASH_COMPAT | ||
39 | //#define shell_builtin_read(setvar,argv,ifs,read_flags,n,p,t,u,d) | ||
40 | // shell_builtin_read(setvar,argv,ifs,read_flags) | ||
41 | //#endif | ||
37 | const char* FAST_FUNC | 42 | const char* FAST_FUNC |
38 | shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), | 43 | shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), |
39 | char **argv, | 44 | char **argv, |
@@ -42,7 +47,8 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), | |||
42 | const char *opt_n, | 47 | const char *opt_n, |
43 | const char *opt_p, | 48 | const char *opt_p, |
44 | const char *opt_t, | 49 | const char *opt_t, |
45 | const char *opt_u | 50 | const char *opt_u, |
51 | const char *opt_d | ||
46 | ); | 52 | ); |
47 | 53 | ||
48 | int FAST_FUNC | 54 | int FAST_FUNC |