aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
authorNguyễn Thái Ngọc Duy <pclouds@gmail.com>2011-01-04 19:56:15 +0700
committerNguyễn Thái Ngọc Duy <pclouds@gmail.com>2011-01-04 19:56:15 +0700
commit5f6f2162512106adf120d4b528bb125e93e34429 (patch)
tree7d7449f755633c263be7125ad58d21cc3ca5b8a7 /shell
parent9db69882bee2d528d706d61d34ef7741122330be (diff)
parenta116552869db5e7793ae10968eb3c962c69b3d8c (diff)
downloadbusybox-w32-5f6f2162512106adf120d4b528bb125e93e34429.tar.gz
busybox-w32-5f6f2162512106adf120d4b528bb125e93e34429.tar.bz2
busybox-w32-5f6f2162512106adf120d4b528bb125e93e34429.zip
Merge remote-tracking branch 'upstream/master'
Diffstat (limited to 'shell')
-rw-r--r--shell/ash.c11
-rw-r--r--shell/ash_test/recho.c13
-rw-r--r--shell/ash_test/zecho.c5
-rw-r--r--shell/hush.c559
-rw-r--r--shell/hush_test/hush-misc/assignment3.right2
-rwxr-xr-xshell/hush_test/hush-misc/assignment3.tests5
-rw-r--r--shell/hush_test/hush-misc/pipefail.right40
-rwxr-xr-xshell/hush_test/hush-misc/pipefail.tests45
-rw-r--r--shell/hush_test/hush-parsing/comment1.right2
-rwxr-xr-xshell/hush_test/hush-parsing/comment1.tests2
-rw-r--r--shell/hush_test/hush-parsing/eol1.right1
-rwxr-xr-xshell/hush_test/hush-parsing/eol1.tests18
12 files changed, 465 insertions, 238 deletions
diff --git a/shell/ash.c b/shell/ash.c
index ccf4288a9..f7baf2b3b 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -57,7 +57,9 @@
57#include <sys/times.h> 57#include <sys/times.h>
58 58
59#include "shell_common.h" 59#include "shell_common.h"
60#include "math.h" 60#if ENABLE_SH_MATH_SUPPORT
61# include "math.h"
62#endif
61#if ENABLE_ASH_RANDOM_SUPPORT 63#if ENABLE_ASH_RANDOM_SUPPORT
62# include "random.h" 64# include "random.h"
63#else 65#else
@@ -5834,6 +5836,11 @@ static struct arglist exparg;
5834/* 5836/*
5835 * Our own itoa(). 5837 * Our own itoa().
5836 */ 5838 */
5839#if !ENABLE_SH_MATH_SUPPORT
5840/* cvtnum() is used even if math support is off (to prepare $? values and such) */
5841typedef long arith_t;
5842# define ARITH_FMT "%ld"
5843#endif
5837static int 5844static int
5838cvtnum(arith_t num) 5845cvtnum(arith_t num)
5839{ 5846{
@@ -13648,7 +13655,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
13648 } 13655 }
13649 } 13656 }
13650#endif 13657#endif
13651 if (/* argv[0] && */ argv[0][0] == '-') 13658 if (argv[0] && argv[0][0] == '-')
13652 isloginsh = 1; 13659 isloginsh = 1;
13653 if (isloginsh) { 13660 if (isloginsh) {
13654 state = 1; 13661 state = 1;
diff --git a/shell/ash_test/recho.c b/shell/ash_test/recho.c
index fb48d9c48..42a5feafd 100644
--- a/shell/ash_test/recho.c
+++ b/shell/ash_test/recho.c
@@ -29,12 +29,9 @@
29 29
30void strprint(); 30void strprint();
31 31
32int 32int main(int argc, char **argv)
33main(argc, argv)
34int argc;
35char **argv;
36{ 33{
37 register int i; 34 int i;
38 35
39 for (i = 1; i < argc; i++) { 36 for (i = 1; i < argc; i++) {
40 printf("argv[%d] = <", i); 37 printf("argv[%d] = <", i);
@@ -44,11 +41,9 @@ char **argv;
44 exit(EXIT_SUCCESS); 41 exit(EXIT_SUCCESS);
45} 42}
46 43
47void 44void strprint(char *str)
48strprint(str)
49char *str;
50{ 45{
51 register unsigned char *s; 46 unsigned char *s;
52 47
53 for (s = (unsigned char *)str; s && *s; s++) { 48 for (s = (unsigned char *)str; s && *s; s++) {
54 if (*s < ' ') { 49 if (*s < ' ') {
diff --git a/shell/ash_test/zecho.c b/shell/ash_test/zecho.c
index bf876f641..cbaa59b81 100644
--- a/shell/ash_test/zecho.c
+++ b/shell/ash_test/zecho.c
@@ -21,10 +21,7 @@
21#include <stdio.h> 21#include <stdio.h>
22#include <stdlib.h> 22#include <stdlib.h>
23 23
24int 24int main(int argc, char **argv)
25main(argc, argv)
26int argc;
27char **argv;
28{ 25{
29 argv++; 26 argv++;
30 27
diff --git a/shell/hush.c b/shell/hush.c
index 9dd30c436..a771e9cd9 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -82,7 +82,11 @@
82 * aaa 82 * aaa
83 */ 83 */
84#include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */ 84#include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */
85#include <malloc.h> /* for malloc_trim */ 85#if !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \
86 || defined(__APPLE__) \
87 )
88# include <malloc.h> /* for malloc_trim */
89#endif
86#include <glob.h> 90#include <glob.h>
87/* #include <dmalloc.h> */ 91/* #include <dmalloc.h> */
88#if ENABLE_HUSH_CASE 92#if ENABLE_HUSH_CASE
@@ -245,9 +249,15 @@
245//config: msh is deprecated and will be removed, please migrate to hush. 249//config: msh is deprecated and will be removed, please migrate to hush.
246//config: 250//config:
247 251
248//usage:#define hush_trivial_usage NOUSAGE_STR 252/* -i (interactive) and -s (read stdin) are also accepted,
253 * but currently do nothing, therefore aren't shown in help.
254 * NOMMU-specific options are not meant to be used by users,
255 * therefore we don't show them either.
256 */
257//usage:#define hush_trivial_usage
258//usage: "[-nx] [-c SCRIPT]"
249//usage:#define hush_full_usage "" 259//usage:#define hush_full_usage ""
250//usage:#define msh_trivial_usage NOUSAGE_STR 260//usage:#define msh_trivial_usage hush_trivial_usage
251//usage:#define msh_full_usage "" 261//usage:#define msh_full_usage ""
252//usage:#define sh_trivial_usage NOUSAGE_STR 262//usage:#define sh_trivial_usage NOUSAGE_STR
253//usage:#define sh_full_usage "" 263//usage:#define sh_full_usage ""
@@ -507,6 +517,7 @@ struct command {
507# define CMD_FUNCDEF 3 517# define CMD_FUNCDEF 3
508#endif 518#endif
509 519
520 smalluint cmd_exitcode;
510 /* if non-NULL, this "command" is { list }, ( list ), or a compound statement */ 521 /* if non-NULL, this "command" is { list }, ( list ), or a compound statement */
511 struct pipe *group; 522 struct pipe *group;
512#if !BB_MMU 523#if !BB_MMU
@@ -542,7 +553,6 @@ struct command {
542#define IS_NULL_CMD(cmd) \ 553#define IS_NULL_CMD(cmd) \
543 (!(cmd)->group && !(cmd)->argv && !(cmd)->redirects) 554 (!(cmd)->group && !(cmd)->argv && !(cmd)->redirects)
544 555
545
546struct pipe { 556struct pipe {
547 struct pipe *next; 557 struct pipe *next;
548 int num_cmds; /* total number of commands in pipe */ 558 int num_cmds; /* total number of commands in pipe */
@@ -637,6 +647,53 @@ struct function {
637#endif 647#endif
638 648
639 649
650/* set -/+o OPT support. (TODO: make it optional)
651 * bash supports the following opts:
652 * allexport off
653 * braceexpand on
654 * emacs on
655 * errexit off
656 * errtrace off
657 * functrace off
658 * hashall on
659 * histexpand off
660 * history on
661 * ignoreeof off
662 * interactive-comments on
663 * keyword off
664 * monitor on
665 * noclobber off
666 * noexec off
667 * noglob off
668 * nolog off
669 * notify off
670 * nounset off
671 * onecmd off
672 * physical off
673 * pipefail off
674 * posix off
675 * privileged off
676 * verbose off
677 * vi off
678 * xtrace off
679 */
680static const char o_opt_strings[] ALIGN1 =
681 "pipefail\0"
682 "noexec\0"
683#if ENABLE_HUSH_MODE_X
684 "xtrace\0"
685#endif
686 ;
687enum {
688 OPT_O_PIPEFAIL,
689 OPT_O_NOEXEC,
690#if ENABLE_HUSH_MODE_X
691 OPT_O_XTRACE,
692#endif
693 NUM_OPT_O
694};
695
696
640/* "Globals" within this file */ 697/* "Globals" within this file */
641/* Sorted roughly by size (smaller offsets == smaller code) */ 698/* Sorted roughly by size (smaller offsets == smaller code) */
642struct globals { 699struct globals {
@@ -679,6 +736,12 @@ struct globals {
679#else 736#else
680# define G_saved_tty_pgrp 0 737# define G_saved_tty_pgrp 0
681#endif 738#endif
739 char o_opt[NUM_OPT_O];
740#if ENABLE_HUSH_MODE_X
741# define G_x_mode (G.o_opt[OPT_O_XTRACE])
742#else
743# define G_x_mode 0
744#endif
682 smallint flag_SIGINT; 745 smallint flag_SIGINT;
683#if ENABLE_HUSH_LOOPS 746#if ENABLE_HUSH_LOOPS
684 smallint flag_break_continue; 747 smallint flag_break_continue;
@@ -690,13 +753,6 @@ struct globals {
690 */ 753 */
691 smallint flag_return_in_progress; 754 smallint flag_return_in_progress;
692#endif 755#endif
693 smallint n_mode;
694#if ENABLE_HUSH_MODE_X
695 smallint x_mode;
696# define G_x_mode (G.x_mode)
697#else
698# define G_x_mode 0
699#endif
700 smallint exiting; /* used to prevent EXIT trap recursion */ 756 smallint exiting; /* used to prevent EXIT trap recursion */
701 /* These four support $?, $#, and $1 */ 757 /* These four support $?, $#, and $1 */
702 smalluint last_exitcode; 758 smalluint last_exitcode;
@@ -875,7 +931,7 @@ static const struct built_in_command bltins2[] = {
875 */ 931 */
876#if HUSH_DEBUG 932#if HUSH_DEBUG
877/* prevent disasters with G.debug_indent < 0 */ 933/* prevent disasters with G.debug_indent < 0 */
878# define indent() fprintf(stderr, "%*s", (G.debug_indent * 2) & 0xff, "") 934# define indent() fdprintf(2, "%*s", (G.debug_indent * 2) & 0xff, "")
879# define debug_enter() (G.debug_indent++) 935# define debug_enter() (G.debug_indent++)
880# define debug_leave() (G.debug_indent--) 936# define debug_leave() (G.debug_indent--)
881#else 937#else
@@ -885,56 +941,56 @@ static const struct built_in_command bltins2[] = {
885#endif 941#endif
886 942
887#ifndef debug_printf 943#ifndef debug_printf
888# define debug_printf(...) (indent(), fprintf(stderr, __VA_ARGS__)) 944# define debug_printf(...) (indent(), fdprintf(2, __VA_ARGS__))
889#endif 945#endif
890 946
891#ifndef debug_printf_parse 947#ifndef debug_printf_parse
892# define debug_printf_parse(...) (indent(), fprintf(stderr, __VA_ARGS__)) 948# define debug_printf_parse(...) (indent(), fdprintf(2, __VA_ARGS__))
893#endif 949#endif
894 950
895#ifndef debug_printf_exec 951#ifndef debug_printf_exec
896#define debug_printf_exec(...) (indent(), fprintf(stderr, __VA_ARGS__)) 952#define debug_printf_exec(...) (indent(), fdprintf(2, __VA_ARGS__))
897#endif 953#endif
898 954
899#ifndef debug_printf_env 955#ifndef debug_printf_env
900# define debug_printf_env(...) (indent(), fprintf(stderr, __VA_ARGS__)) 956# define debug_printf_env(...) (indent(), fdprintf(2, __VA_ARGS__))
901#endif 957#endif
902 958
903#ifndef debug_printf_jobs 959#ifndef debug_printf_jobs
904# define debug_printf_jobs(...) (indent(), fprintf(stderr, __VA_ARGS__)) 960# define debug_printf_jobs(...) (indent(), fdprintf(2, __VA_ARGS__))
905# define DEBUG_JOBS 1 961# define DEBUG_JOBS 1
906#else 962#else
907# define DEBUG_JOBS 0 963# define DEBUG_JOBS 0
908#endif 964#endif
909 965
910#ifndef debug_printf_expand 966#ifndef debug_printf_expand
911# define debug_printf_expand(...) (indent(), fprintf(stderr, __VA_ARGS__)) 967# define debug_printf_expand(...) (indent(), fdprintf(2, __VA_ARGS__))
912# define DEBUG_EXPAND 1 968# define DEBUG_EXPAND 1
913#else 969#else
914# define DEBUG_EXPAND 0 970# define DEBUG_EXPAND 0
915#endif 971#endif
916 972
917#ifndef debug_printf_varexp 973#ifndef debug_printf_varexp
918# define debug_printf_varexp(...) (indent(), fprintf(stderr, __VA_ARGS__)) 974# define debug_printf_varexp(...) (indent(), fdprintf(2, __VA_ARGS__))
919#endif 975#endif
920 976
921#ifndef debug_printf_glob 977#ifndef debug_printf_glob
922# define debug_printf_glob(...) (indent(), fprintf(stderr, __VA_ARGS__)) 978# define debug_printf_glob(...) (indent(), fdprintf(2, __VA_ARGS__))
923# define DEBUG_GLOB 1 979# define DEBUG_GLOB 1
924#else 980#else
925# define DEBUG_GLOB 0 981# define DEBUG_GLOB 0
926#endif 982#endif
927 983
928#ifndef debug_printf_list 984#ifndef debug_printf_list
929# define debug_printf_list(...) (indent(), fprintf(stderr, __VA_ARGS__)) 985# define debug_printf_list(...) (indent(), fdprintf(2, __VA_ARGS__))
930#endif 986#endif
931 987
932#ifndef debug_printf_subst 988#ifndef debug_printf_subst
933# define debug_printf_subst(...) (indent(), fprintf(stderr, __VA_ARGS__)) 989# define debug_printf_subst(...) (indent(), fdprintf(2, __VA_ARGS__))
934#endif 990#endif
935 991
936#ifndef debug_printf_clean 992#ifndef debug_printf_clean
937# define debug_printf_clean(...) (indent(), fprintf(stderr, __VA_ARGS__)) 993# define debug_printf_clean(...) (indent(), fdprintf(2, __VA_ARGS__))
938# define DEBUG_CLEAN 1 994# define DEBUG_CLEAN 1
939#else 995#else
940# define DEBUG_CLEAN 0 996# define DEBUG_CLEAN 0
@@ -944,9 +1000,9 @@ static const struct built_in_command bltins2[] = {
944static void debug_print_strings(const char *prefix, char **vv) 1000static void debug_print_strings(const char *prefix, char **vv)
945{ 1001{
946 indent(); 1002 indent();
947 fprintf(stderr, "%s:\n", prefix); 1003 fdprintf(2, "%s:\n", prefix);
948 while (*vv) 1004 while (*vv)
949 fprintf(stderr, " '%s'\n", *vv++); 1005 fdprintf(2, " '%s'\n", *vv++);
950} 1006}
951#else 1007#else
952# define debug_print_strings(prefix, vv) ((void)0) 1008# define debug_print_strings(prefix, vv) ((void)0)
@@ -1378,6 +1434,22 @@ static void hush_exit(int exitcode)
1378 builtin_eval(argv); 1434 builtin_eval(argv);
1379 } 1435 }
1380 1436
1437#if ENABLE_FEATURE_CLEAN_UP
1438 {
1439 struct variable *cur_var;
1440 if (G.cwd != bb_msg_unknown)
1441 free((char*)G.cwd);
1442 cur_var = G.top_var;
1443 while (cur_var) {
1444 struct variable *tmp = cur_var;
1445 if (!cur_var->max_len)
1446 free(cur_var->varstr);
1447 cur_var = cur_var->next;
1448 free(tmp);
1449 }
1450 }
1451#endif
1452
1381#if ENABLE_HUSH_JOB 1453#if ENABLE_HUSH_JOB
1382 fflush_all(); 1454 fflush_all();
1383 sigexit(- (exitcode & 0xff)); 1455 sigexit(- (exitcode & 0xff));
@@ -2120,22 +2192,22 @@ static void debug_print_list(const char *prefix, o_string *o, int n)
2120 int i = 0; 2192 int i = 0;
2121 2193
2122 indent(); 2194 indent();
2123 fprintf(stderr, "%s: list:%p n:%d string_start:%d length:%d maxlen:%d glob:%d quoted:%d escape:%d\n", 2195 fdprintf(2, "%s: list:%p n:%d string_start:%d length:%d maxlen:%d glob:%d quoted:%d escape:%d\n",
2124 prefix, list, n, string_start, o->length, o->maxlen, 2196 prefix, list, n, string_start, o->length, o->maxlen,
2125 !!(o->o_expflags & EXP_FLAG_GLOB), 2197 !!(o->o_expflags & EXP_FLAG_GLOB),
2126 o->has_quoted_part, 2198 o->has_quoted_part,
2127 !!(o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)); 2199 !!(o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
2128 while (i < n) { 2200 while (i < n) {
2129 indent(); 2201 indent();
2130 fprintf(stderr, " list[%d]=%d '%s' %p\n", i, (int)list[i], 2202 fdprintf(2, " list[%d]=%d '%s' %p\n", i, (int)(uintptr_t)list[i],
2131 o->data + (int)list[i] + string_start, 2203 o->data + (int)(uintptr_t)list[i] + string_start,
2132 o->data + (int)list[i] + string_start); 2204 o->data + (int)(uintptr_t)list[i] + string_start);
2133 i++; 2205 i++;
2134 } 2206 }
2135 if (n) { 2207 if (n) {
2136 const char *p = o->data + (int)list[n - 1] + string_start; 2208 const char *p = o->data + (int)(uintptr_t)list[n - 1] + string_start;
2137 indent(); 2209 indent();
2138 fprintf(stderr, " total_sz:%ld\n", (long)((p + strlen(p) + 1) - o->data)); 2210 fdprintf(2, " total_sz:%ld\n", (long)((p + strlen(p) + 1) - o->data));
2139 } 2211 }
2140} 2212}
2141#else 2213#else
@@ -2583,6 +2655,94 @@ static void free_pipe_list(struct pipe *pi)
2583 2655
2584/*** Parsing routines ***/ 2656/*** Parsing routines ***/
2585 2657
2658#ifndef debug_print_tree
2659static void debug_print_tree(struct pipe *pi, int lvl)
2660{
2661 static const char *const PIPE[] = {
2662 [PIPE_SEQ] = "SEQ",
2663 [PIPE_AND] = "AND",
2664 [PIPE_OR ] = "OR" ,
2665 [PIPE_BG ] = "BG" ,
2666 };
2667 static const char *RES[] = {
2668 [RES_NONE ] = "NONE" ,
2669# if ENABLE_HUSH_IF
2670 [RES_IF ] = "IF" ,
2671 [RES_THEN ] = "THEN" ,
2672 [RES_ELIF ] = "ELIF" ,
2673 [RES_ELSE ] = "ELSE" ,
2674 [RES_FI ] = "FI" ,
2675# endif
2676# if ENABLE_HUSH_LOOPS
2677 [RES_FOR ] = "FOR" ,
2678 [RES_WHILE] = "WHILE",
2679 [RES_UNTIL] = "UNTIL",
2680 [RES_DO ] = "DO" ,
2681 [RES_DONE ] = "DONE" ,
2682# endif
2683# if ENABLE_HUSH_LOOPS || ENABLE_HUSH_CASE
2684 [RES_IN ] = "IN" ,
2685# endif
2686# if ENABLE_HUSH_CASE
2687 [RES_CASE ] = "CASE" ,
2688 [RES_CASE_IN ] = "CASE_IN" ,
2689 [RES_MATCH] = "MATCH",
2690 [RES_CASE_BODY] = "CASE_BODY",
2691 [RES_ESAC ] = "ESAC" ,
2692# endif
2693 [RES_XXXX ] = "XXXX" ,
2694 [RES_SNTX ] = "SNTX" ,
2695 };
2696 static const char *const CMDTYPE[] = {
2697 "{}",
2698 "()",
2699 "[noglob]",
2700# if ENABLE_HUSH_FUNCTIONS
2701 "func()",
2702# endif
2703 };
2704
2705 int pin, prn;
2706
2707 pin = 0;
2708 while (pi) {
2709 fdprintf(2, "%*spipe %d res_word=%s followup=%d %s\n", lvl*2, "",
2710 pin, RES[pi->res_word], pi->followup, PIPE[pi->followup]);
2711 prn = 0;
2712 while (prn < pi->num_cmds) {
2713 struct command *command = &pi->cmds[prn];
2714 char **argv = command->argv;
2715
2716 fdprintf(2, "%*s cmd %d assignment_cnt:%d",
2717 lvl*2, "", prn,
2718 command->assignment_cnt);
2719 if (command->group) {
2720 fdprintf(2, " group %s: (argv=%p)%s%s\n",
2721 CMDTYPE[command->cmd_type],
2722 argv
2723# if !BB_MMU
2724 , " group_as_string:", command->group_as_string
2725# else
2726 , "", ""
2727# endif
2728 );
2729 debug_print_tree(command->group, lvl+1);
2730 prn++;
2731 continue;
2732 }
2733 if (argv) while (*argv) {
2734 fdprintf(2, " '%s'", *argv);
2735 argv++;
2736 }
2737 fdprintf(2, "\n");
2738 prn++;
2739 }
2740 pi = pi->next;
2741 pin++;
2742 }
2743}
2744#endif /* debug_print_tree */
2745
2586static struct pipe *new_pipe(void) 2746static struct pipe *new_pipe(void)
2587{ 2747{
2588 struct pipe *pi; 2748 struct pipe *pi;
@@ -3972,15 +4132,16 @@ static struct pipe *parse_stream(char **pstring,
3972 goto parse_error; 4132 goto parse_error;
3973 } 4133 }
3974 if (ch == '\n') { 4134 if (ch == '\n') {
3975#if ENABLE_HUSH_CASE 4135 /* Is this a case when newline is simply ignored?
3976 /* "case ... in <newline> word) ..." - 4136 * Some examples:
3977 * newlines are ignored (but ';' wouldn't be) */ 4137 * "cmd | <newline> cmd ..."
3978 if (ctx.command->argv == NULL 4138 * "case ... in <newline> word) ..."
3979 && ctx.ctx_res_w == RES_MATCH 4139 */
4140 if (IS_NULL_CMD(ctx.command)
4141 && dest.length == 0 && !dest.has_quoted_part
3980 ) { 4142 ) {
3981 continue; 4143 continue;
3982 } 4144 }
3983#endif
3984 /* Treat newline as a command separator. */ 4145 /* Treat newline as a command separator. */
3985 done_pipe(&ctx, PIPE_SEQ); 4146 done_pipe(&ctx, PIPE_SEQ);
3986 debug_printf_parse("heredoc_cnt:%d\n", heredoc_cnt); 4147 debug_printf_parse("heredoc_cnt:%d\n", heredoc_cnt);
@@ -4112,6 +4273,31 @@ static struct pipe *parse_stream(char **pstring,
4112 if (parse_redirect(&ctx, redir_fd, redir_style, input)) 4273 if (parse_redirect(&ctx, redir_fd, redir_style, input))
4113 goto parse_error; 4274 goto parse_error;
4114 continue; /* back to top of while (1) */ 4275 continue; /* back to top of while (1) */
4276 case '#':
4277 if (dest.length == 0 && !dest.has_quoted_part) {
4278 /* skip "#comment" */
4279 while (1) {
4280 ch = i_peek(input);
4281 if (ch == EOF || ch == '\n')
4282 break;
4283 i_getch(input);
4284 /* note: we do not add it to &ctx.as_string */
4285 }
4286 nommu_addchr(&ctx.as_string, '\n');
4287 continue; /* back to top of while (1) */
4288 }
4289 break;
4290 case '\\':
4291 if (next == '\n') {
4292 /* It's "\<newline>" */
4293#if !BB_MMU
4294 /* Remove trailing '\' from ctx.as_string */
4295 ctx.as_string.data[--ctx.as_string.length] = '\0';
4296#endif
4297 ch = i_getch(input); /* eat it */
4298 continue; /* back to top of while (1) */
4299 }
4300 break;
4115 } 4301 }
4116 4302
4117 if (dest.o_assignment == MAYBE_ASSIGNMENT 4303 if (dest.o_assignment == MAYBE_ASSIGNMENT
@@ -4126,19 +4312,8 @@ static struct pipe *parse_stream(char **pstring,
4126 /* Note: nommu_addchr(&ctx.as_string, ch) is already done */ 4312 /* Note: nommu_addchr(&ctx.as_string, ch) is already done */
4127 4313
4128 switch (ch) { 4314 switch (ch) {
4129 case '#': 4315 case '#': /* non-comment #: "echo a#b" etc */
4130 if (dest.length == 0) { 4316 o_addQchr(&dest, ch);
4131 while (1) {
4132 ch = i_peek(input);
4133 if (ch == EOF || ch == '\n')
4134 break;
4135 i_getch(input);
4136 /* note: we do not add it to &ctx.as_string */
4137 }
4138 nommu_addchr(&ctx.as_string, '\n');
4139 } else {
4140 o_addQchr(&dest, ch);
4141 }
4142 break; 4317 break;
4143 case '\\': 4318 case '\\':
4144 if (next == EOF) { 4319 if (next == EOF) {
@@ -4146,21 +4321,14 @@ static struct pipe *parse_stream(char **pstring,
4146 xfunc_die(); 4321 xfunc_die();
4147 } 4322 }
4148 ch = i_getch(input); 4323 ch = i_getch(input);
4149 if (ch != '\n') { 4324 /* note: ch != '\n' (that case does not reach this place) */
4150 o_addchr(&dest, '\\'); 4325 o_addchr(&dest, '\\');
4151 /*nommu_addchr(&ctx.as_string, '\\'); - already done */ 4326 /*nommu_addchr(&ctx.as_string, '\\'); - already done */
4152 o_addchr(&dest, ch); 4327 o_addchr(&dest, ch);
4153 nommu_addchr(&ctx.as_string, ch); 4328 nommu_addchr(&ctx.as_string, ch);
4154 /* Example: echo Hello \2>file 4329 /* Example: echo Hello \2>file
4155 * we need to know that word 2 is quoted */ 4330 * we need to know that word 2 is quoted */
4156 dest.has_quoted_part = 1; 4331 dest.has_quoted_part = 1;
4157 }
4158#if !BB_MMU
4159 else {
4160 /* It's "\<newline>". Remove trailing '\' from ctx.as_string */
4161 ctx.as_string.data[--ctx.as_string.length] = '\0';
4162 }
4163#endif
4164 break; 4332 break;
4165 case '$': 4333 case '$':
4166 if (parse_dollar(&ctx.as_string, &dest, input, /*quote_mask:*/ 0) != 0) { 4334 if (parse_dollar(&ctx.as_string, &dest, input, /*quote_mask:*/ 0) != 0) {
@@ -4366,7 +4534,9 @@ static struct pipe *parse_stream(char **pstring,
4366 expand_string_to_string(str) 4534 expand_string_to_string(str)
4367#endif 4535#endif
4368static char *expand_string_to_string(const char *str, int do_unbackslash); 4536static char *expand_string_to_string(const char *str, int do_unbackslash);
4537#if ENABLE_HUSH_TICK
4369static int process_command_subs(o_string *dest, const char *s); 4538static int process_command_subs(o_string *dest, const char *s);
4539#endif
4370 4540
4371/* expand_strvec_to_strvec() takes a list of strings, expands 4541/* expand_strvec_to_strvec() takes a list of strings, expands
4372 * all variable references within and returns a pointer to 4542 * all variable references within and returns a pointer to
@@ -6309,48 +6479,57 @@ static int checkjobs(struct pipe *fg_pipe)
6309#endif 6479#endif
6310 /* Were we asked to wait for fg pipe? */ 6480 /* Were we asked to wait for fg pipe? */
6311 if (fg_pipe) { 6481 if (fg_pipe) {
6312 for (i = 0; i < fg_pipe->num_cmds; i++) { 6482 i = fg_pipe->num_cmds;
6483 while (--i >= 0) {
6313 debug_printf_jobs("check pid %d\n", fg_pipe->cmds[i].pid); 6484 debug_printf_jobs("check pid %d\n", fg_pipe->cmds[i].pid);
6314 if (fg_pipe->cmds[i].pid != childpid) 6485 if (fg_pipe->cmds[i].pid != childpid)
6315 continue; 6486 continue;
6316 if (dead) { 6487 if (dead) {
6488 int ex;
6317 fg_pipe->cmds[i].pid = 0; 6489 fg_pipe->cmds[i].pid = 0;
6318 fg_pipe->alive_cmds--; 6490 fg_pipe->alive_cmds--;
6319 if (i == fg_pipe->num_cmds - 1) { 6491 ex = WEXITSTATUS(status);
6320 /* last process gives overall exitstatus */ 6492 /* bash prints killer signal's name for *last*
6321 rcode = WEXITSTATUS(status); 6493 * process in pipe (prints just newline for SIGINT).
6322 /* bash prints killer signal's name for *last* 6494 * Mimic this. Example: "sleep 5" + (^\ or kill -QUIT)
6323 * process in pipe (prints just newline for SIGINT). 6495 */
6324 * Mimic this. Example: "sleep 5" + (^\ or kill -QUIT) 6496 if (WIFSIGNALED(status)) {
6325 */ 6497 int sig = WTERMSIG(status);
6326 if (WIFSIGNALED(status)) { 6498 if (i == fg_pipe->num_cmds-1)
6327 int sig = WTERMSIG(status);
6328 printf("%s\n", sig == SIGINT ? "" : get_signame(sig)); 6499 printf("%s\n", sig == SIGINT ? "" : get_signame(sig));
6329 /* TODO: MIPS has 128 sigs (1..128), what if sig==128 here? 6500 /* TODO: MIPS has 128 sigs (1..128), what if sig==128 here?
6330 * Maybe we need to use sig | 128? */ 6501 * Maybe we need to use sig | 128? */
6331 rcode = sig + 128; 6502 ex = sig + 128;
6332 }
6333 IF_HAS_KEYWORDS(if (fg_pipe->pi_inverted) rcode = !rcode;)
6334 } 6503 }
6504 fg_pipe->cmds[i].cmd_exitcode = ex;
6335 } else { 6505 } else {
6336 fg_pipe->cmds[i].is_stopped = 1; 6506 fg_pipe->cmds[i].is_stopped = 1;
6337 fg_pipe->stopped_cmds++; 6507 fg_pipe->stopped_cmds++;
6338 } 6508 }
6339 debug_printf_jobs("fg_pipe: alive_cmds %d stopped_cmds %d\n", 6509 debug_printf_jobs("fg_pipe: alive_cmds %d stopped_cmds %d\n",
6340 fg_pipe->alive_cmds, fg_pipe->stopped_cmds); 6510 fg_pipe->alive_cmds, fg_pipe->stopped_cmds);
6341 if (fg_pipe->alive_cmds - fg_pipe->stopped_cmds <= 0) { 6511 if (fg_pipe->alive_cmds == fg_pipe->stopped_cmds) {
6342 /* All processes in fg pipe have exited or stopped */ 6512 /* All processes in fg pipe have exited or stopped */
6513 i = fg_pipe->num_cmds;
6514 while (--i >= 0) {
6515 rcode = fg_pipe->cmds[i].cmd_exitcode;
6516 /* usually last process gives overall exitstatus,
6517 * but with "set -o pipefail", last *failed* process does */
6518 if (G.o_opt[OPT_O_PIPEFAIL] == 0 || rcode != 0)
6519 break;
6520 }
6521 IF_HAS_KEYWORDS(if (fg_pipe->pi_inverted) rcode = !rcode;)
6343/* Note: *non-interactive* bash does not continue if all processes in fg pipe 6522/* Note: *non-interactive* bash does not continue if all processes in fg pipe
6344 * are stopped. Testcase: "cat | cat" in a script (not on command line!) 6523 * are stopped. Testcase: "cat | cat" in a script (not on command line!)
6345 * and "killall -STOP cat" */ 6524 * and "killall -STOP cat" */
6346 if (G_interactive_fd) { 6525 if (G_interactive_fd) {
6347#if ENABLE_HUSH_JOB 6526#if ENABLE_HUSH_JOB
6348 if (fg_pipe->alive_cmds) 6527 if (fg_pipe->alive_cmds != 0)
6349 insert_bg_job(fg_pipe); 6528 insert_bg_job(fg_pipe);
6350#endif 6529#endif
6351 return rcode; 6530 return rcode;
6352 } 6531 }
6353 if (!fg_pipe->alive_cmds) 6532 if (fg_pipe->alive_cmds == 0)
6354 return rcode; 6533 return rcode;
6355 } 6534 }
6356 /* There are still running processes in the fg pipe */ 6535 /* There are still running processes in the fg pipe */
@@ -6436,7 +6615,7 @@ static int checkjobs_and_fg_shell(struct pipe *fg_pipe)
6436 * subshell: ( list ) [&] 6615 * subshell: ( list ) [&]
6437 */ 6616 */
6438#if !ENABLE_HUSH_MODE_X 6617#if !ENABLE_HUSH_MODE_X
6439#define redirect_and_varexp_helper(new_env_p, old_vars_p, command, squirrel, char argv_expanded) \ 6618#define redirect_and_varexp_helper(new_env_p, old_vars_p, command, squirrel, argv_expanded) \
6440 redirect_and_varexp_helper(new_env_p, old_vars_p, command, squirrel) 6619 redirect_and_varexp_helper(new_env_p, old_vars_p, command, squirrel)
6441#endif 6620#endif
6442static int redirect_and_varexp_helper(char ***new_env_p, 6621static int redirect_and_varexp_helper(char ***new_env_p,
@@ -6821,94 +7000,6 @@ static NOINLINE int run_pipe(struct pipe *pi)
6821 return -1; 7000 return -1;
6822} 7001}
6823 7002
6824#ifndef debug_print_tree
6825static void debug_print_tree(struct pipe *pi, int lvl)
6826{
6827 static const char *const PIPE[] = {
6828 [PIPE_SEQ] = "SEQ",
6829 [PIPE_AND] = "AND",
6830 [PIPE_OR ] = "OR" ,
6831 [PIPE_BG ] = "BG" ,
6832 };
6833 static const char *RES[] = {
6834 [RES_NONE ] = "NONE" ,
6835# if ENABLE_HUSH_IF
6836 [RES_IF ] = "IF" ,
6837 [RES_THEN ] = "THEN" ,
6838 [RES_ELIF ] = "ELIF" ,
6839 [RES_ELSE ] = "ELSE" ,
6840 [RES_FI ] = "FI" ,
6841# endif
6842# if ENABLE_HUSH_LOOPS
6843 [RES_FOR ] = "FOR" ,
6844 [RES_WHILE] = "WHILE",
6845 [RES_UNTIL] = "UNTIL",
6846 [RES_DO ] = "DO" ,
6847 [RES_DONE ] = "DONE" ,
6848# endif
6849# if ENABLE_HUSH_LOOPS || ENABLE_HUSH_CASE
6850 [RES_IN ] = "IN" ,
6851# endif
6852# if ENABLE_HUSH_CASE
6853 [RES_CASE ] = "CASE" ,
6854 [RES_CASE_IN ] = "CASE_IN" ,
6855 [RES_MATCH] = "MATCH",
6856 [RES_CASE_BODY] = "CASE_BODY",
6857 [RES_ESAC ] = "ESAC" ,
6858# endif
6859 [RES_XXXX ] = "XXXX" ,
6860 [RES_SNTX ] = "SNTX" ,
6861 };
6862 static const char *const CMDTYPE[] = {
6863 "{}",
6864 "()",
6865 "[noglob]",
6866# if ENABLE_HUSH_FUNCTIONS
6867 "func()",
6868# endif
6869 };
6870
6871 int pin, prn;
6872
6873 pin = 0;
6874 while (pi) {
6875 fprintf(stderr, "%*spipe %d res_word=%s followup=%d %s\n", lvl*2, "",
6876 pin, RES[pi->res_word], pi->followup, PIPE[pi->followup]);
6877 prn = 0;
6878 while (prn < pi->num_cmds) {
6879 struct command *command = &pi->cmds[prn];
6880 char **argv = command->argv;
6881
6882 fprintf(stderr, "%*s cmd %d assignment_cnt:%d",
6883 lvl*2, "", prn,
6884 command->assignment_cnt);
6885 if (command->group) {
6886 fprintf(stderr, " group %s: (argv=%p)%s%s\n",
6887 CMDTYPE[command->cmd_type],
6888 argv
6889# if !BB_MMU
6890 , " group_as_string:", command->group_as_string
6891# else
6892 , "", ""
6893# endif
6894 );
6895 debug_print_tree(command->group, lvl+1);
6896 prn++;
6897 continue;
6898 }
6899 if (argv) while (*argv) {
6900 fprintf(stderr, " '%s'", *argv);
6901 argv++;
6902 }
6903 fprintf(stderr, "\n");
6904 prn++;
6905 }
6906 pi = pi->next;
6907 pin++;
6908 }
6909}
6910#endif /* debug_print_tree */
6911
6912/* NB: called by pseudo_exec, and therefore must not modify any 7003/* NB: called by pseudo_exec, and therefore must not modify any
6913 * global data until exec/_exit (we can be a child after vfork!) */ 7004 * global data until exec/_exit (we can be a child after vfork!) */
6914static int run_list(struct pipe *pi) 7005static int run_list(struct pipe *pi)
@@ -6938,27 +7029,30 @@ static int run_list(struct pipe *pi)
6938 7029
6939#if ENABLE_HUSH_LOOPS 7030#if ENABLE_HUSH_LOOPS
6940 /* Check syntax for "for" */ 7031 /* Check syntax for "for" */
6941 for (struct pipe *cpipe = pi; cpipe; cpipe = cpipe->next) { 7032 {
6942 if (cpipe->res_word != RES_FOR && cpipe->res_word != RES_IN) 7033 struct pipe *cpipe;
6943 continue; 7034 for (cpipe = pi; cpipe; cpipe = cpipe->next) {
6944 /* current word is FOR or IN (BOLD in comments below) */ 7035 if (cpipe->res_word != RES_FOR && cpipe->res_word != RES_IN)
6945 if (cpipe->next == NULL) { 7036 continue;
6946 syntax_error("malformed for"); 7037 /* current word is FOR or IN (BOLD in comments below) */
6947 debug_leave(); 7038 if (cpipe->next == NULL) {
6948 debug_printf_exec("run_list lvl %d return 1\n", G.run_list_level); 7039 syntax_error("malformed for");
6949 return 1; 7040 debug_leave();
6950 } 7041 debug_printf_exec("run_list lvl %d return 1\n", G.run_list_level);
6951 /* "FOR v; do ..." and "for v IN a b; do..." are ok */ 7042 return 1;
6952 if (cpipe->next->res_word == RES_DO) 7043 }
6953 continue; 7044 /* "FOR v; do ..." and "for v IN a b; do..." are ok */
6954 /* next word is not "do". It must be "in" then ("FOR v in ...") */ 7045 if (cpipe->next->res_word == RES_DO)
6955 if (cpipe->res_word == RES_IN /* "for v IN a b; not_do..."? */ 7046 continue;
6956 || cpipe->next->res_word != RES_IN /* FOR v not_do_and_not_in..."? */ 7047 /* next word is not "do". It must be "in" then ("FOR v in ...") */
6957 ) { 7048 if (cpipe->res_word == RES_IN /* "for v IN a b; not_do..."? */
6958 syntax_error("malformed for"); 7049 || cpipe->next->res_word != RES_IN /* FOR v not_do_and_not_in..."? */
6959 debug_leave(); 7050 ) {
6960 debug_printf_exec("run_list lvl %d return 1\n", G.run_list_level); 7051 syntax_error("malformed for");
6961 return 1; 7052 debug_leave();
7053 debug_printf_exec("run_list lvl %d return 1\n", G.run_list_level);
7054 return 1;
7055 }
6962 } 7056 }
6963 } 7057 }
6964#endif 7058#endif
@@ -7241,7 +7335,7 @@ static int run_and_free_list(struct pipe *pi)
7241{ 7335{
7242 int rcode = 0; 7336 int rcode = 0;
7243 debug_printf_exec("run_and_free_list entered\n"); 7337 debug_printf_exec("run_and_free_list entered\n");
7244 if (!G.n_mode) { 7338 if (!G.o_opt[OPT_O_NOEXEC]) {
7245 debug_printf_exec(": run_list: 1st pipe with %d cmds\n", pi->num_cmds); 7339 debug_printf_exec(": run_list: 1st pipe with %d cmds\n", pi->num_cmds);
7246 rcode = run_list(pi); 7340 rcode = run_list(pi);
7247 } 7341 }
@@ -7339,13 +7433,41 @@ static void set_fatal_handlers(void)
7339} 7433}
7340#endif 7434#endif
7341 7435
7342static int set_mode(const char cstate, const char mode) 7436static int set_mode(int state, char mode, const char *o_opt)
7343{ 7437{
7344 int state = (cstate == '-' ? 1 : 0); 7438 int idx;
7345 switch (mode) { 7439 switch (mode) {
7346 case 'n': G.n_mode = state; break; 7440 case 'n':
7347 case 'x': IF_HUSH_MODE_X(G_x_mode = state;) break; 7441 G.o_opt[OPT_O_NOEXEC] = state;
7348 default: return EXIT_FAILURE; 7442 break;
7443 case 'x':
7444 IF_HUSH_MODE_X(G_x_mode = state;)
7445 break;
7446 case 'o':
7447 if (!o_opt) {
7448 /* "set -+o" without parameter.
7449 * in bash, set -o produces this output:
7450 * pipefail off
7451 * and set +o:
7452 * set +o pipefail
7453 * We always use the second form.
7454 */
7455 const char *p = o_opt_strings;
7456 idx = 0;
7457 while (*p) {
7458 printf("set %co %s\n", (G.o_opt[idx] ? '-' : '+'), p);
7459 idx++;
7460 p += strlen(p) + 1;
7461 }
7462 break;
7463 }
7464 idx = index_in_strings(o_opt_strings, o_opt);
7465 if (idx >= 0) {
7466 G.o_opt[idx] = state;
7467 break;
7468 }
7469 default:
7470 return EXIT_FAILURE;
7349 } 7471 }
7350 return EXIT_SUCCESS; 7472 return EXIT_SUCCESS;
7351} 7473}
@@ -7357,7 +7479,7 @@ int hush_main(int argc, char **argv)
7357 unsigned builtin_argc; 7479 unsigned builtin_argc;
7358 char **e; 7480 char **e;
7359 struct variable *cur_var; 7481 struct variable *cur_var;
7360 struct variable shell_ver; 7482 struct variable *shell_ver;
7361 7483
7362 INIT_G(); 7484 INIT_G();
7363 if (EXIT_SUCCESS) /* if EXIT_SUCCESS == 0, it is already done */ 7485 if (EXIT_SUCCESS) /* if EXIT_SUCCESS == 0, it is already done */
@@ -7366,17 +7488,17 @@ int hush_main(int argc, char **argv)
7366 G.argv0_for_re_execing = argv[0]; 7488 G.argv0_for_re_execing = argv[0];
7367#endif 7489#endif
7368 /* Deal with HUSH_VERSION */ 7490 /* Deal with HUSH_VERSION */
7369 memset(&shell_ver, 0, sizeof(shell_ver)); 7491 shell_ver = xzalloc(sizeof(*shell_ver));
7370 shell_ver.flg_export = 1; 7492 shell_ver->flg_export = 1;
7371 shell_ver.flg_read_only = 1; 7493 shell_ver->flg_read_only = 1;
7372 /* Code which handles ${var<op>...} needs writable values for all variables, 7494 /* Code which handles ${var<op>...} needs writable values for all variables,
7373 * therefore we xstrdup: */ 7495 * therefore we xstrdup: */
7374 shell_ver.varstr = xstrdup(hush_version_str), 7496 shell_ver->varstr = xstrdup(hush_version_str);
7375 G.top_var = &shell_ver;
7376 /* Create shell local variables from the values 7497 /* Create shell local variables from the values
7377 * currently living in the environment */ 7498 * currently living in the environment */
7378 debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION"); 7499 debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION");
7379 unsetenv("HUSH_VERSION"); /* in case it exists in initial env */ 7500 unsetenv("HUSH_VERSION"); /* in case it exists in initial env */
7501 G.top_var = shell_ver;
7380 cur_var = G.top_var; 7502 cur_var = G.top_var;
7381 e = environ; 7503 e = environ;
7382 if (e) while (*e) { 7504 if (e) while (*e) {
@@ -7391,8 +7513,8 @@ int hush_main(int argc, char **argv)
7391 e++; 7513 e++;
7392 } 7514 }
7393 /* (Re)insert HUSH_VERSION into env (AFTER we scanned the env!) */ 7515 /* (Re)insert HUSH_VERSION into env (AFTER we scanned the env!) */
7394 debug_printf_env("putenv '%s'\n", shell_ver.varstr); 7516 debug_printf_env("putenv '%s'\n", shell_ver->varstr);
7395 putenv(shell_ver.varstr); 7517 putenv(shell_ver->varstr);
7396 7518
7397 /* Export PWD */ 7519 /* Export PWD */
7398 set_pwd_var(/*exp:*/ 1); 7520 set_pwd_var(/*exp:*/ 1);
@@ -7585,7 +7707,7 @@ int hush_main(int argc, char **argv)
7585#endif 7707#endif
7586 case 'n': 7708 case 'n':
7587 case 'x': 7709 case 'x':
7588 if (set_mode('-', opt) == 0) /* no error */ 7710 if (set_mode(1, opt, NULL) == 0) /* no error */
7589 break; 7711 break;
7590 default: 7712 default:
7591#ifndef BB_VER 7713#ifndef BB_VER
@@ -7752,18 +7874,6 @@ int hush_main(int argc, char **argv)
7752 parse_and_run_file(stdin); 7874 parse_and_run_file(stdin);
7753 7875
7754 final_return: 7876 final_return:
7755#if ENABLE_FEATURE_CLEAN_UP
7756 if (G.cwd != bb_msg_unknown)
7757 free((char*)G.cwd);
7758 cur_var = G.top_var->next;
7759 while (cur_var) {
7760 struct variable *tmp = cur_var;
7761 if (!cur_var->max_len)
7762 free(cur_var->varstr);
7763 cur_var = cur_var->next;
7764 free(tmp);
7765 }
7766#endif
7767 hush_exit(G.last_exitcode); 7877 hush_exit(G.last_exitcode);
7768} 7878}
7769 7879
@@ -8375,15 +8485,18 @@ static int FAST_FUNC builtin_set(char **argv)
8375 } 8485 }
8376 8486
8377 do { 8487 do {
8378 if (!strcmp(arg, "--")) { 8488 if (strcmp(arg, "--") == 0) {
8379 ++argv; 8489 ++argv;
8380 goto set_argv; 8490 goto set_argv;
8381 } 8491 }
8382 if (arg[0] != '+' && arg[0] != '-') 8492 if (arg[0] != '+' && arg[0] != '-')
8383 break; 8493 break;
8384 for (n = 1; arg[n]; ++n) 8494 for (n = 1; arg[n]; ++n) {
8385 if (set_mode(arg[0], arg[n])) 8495 if (set_mode((arg[0] == '-'), arg[n], argv[1]))
8386 goto error; 8496 goto error;
8497 if (arg[n] == 'o' && argv[1])
8498 argv++;
8499 }
8387 } while ((arg = *++argv) != NULL); 8500 } while ((arg = *++argv) != NULL);
8388 /* Now argv[0] is 1st argument */ 8501 /* Now argv[0] is 1st argument */
8389 8502
diff --git a/shell/hush_test/hush-misc/assignment3.right b/shell/hush_test/hush-misc/assignment3.right
new file mode 100644
index 000000000..0f02d7cbc
--- /dev/null
+++ b/shell/hush_test/hush-misc/assignment3.right
@@ -0,0 +1,2 @@
1Done:0
2abc=123
diff --git a/shell/hush_test/hush-misc/assignment3.tests b/shell/hush_test/hush-misc/assignment3.tests
new file mode 100755
index 000000000..790129be1
--- /dev/null
+++ b/shell/hush_test/hush-misc/assignment3.tests
@@ -0,0 +1,5 @@
1# This must be interpreted as assignments
2a=1 b\
3=2 c=3
4echo Done:$?
5echo abc=$a$b$c
diff --git a/shell/hush_test/hush-misc/pipefail.right b/shell/hush_test/hush-misc/pipefail.right
new file mode 100644
index 000000000..5845d8939
--- /dev/null
+++ b/shell/hush_test/hush-misc/pipefail.right
@@ -0,0 +1,40 @@
1Default:
2true | true:
30
41
5true | false:
61
70
8false | true:
90
101
11exit 2 | exit 3 | exit 4:
124
130
14Pipefail on:
15true | true:
160
171
18true | false:
191
200
21false | true:
221
230
24exit 2 | exit 3 | exit 4:
254
260
27Pipefail off:
28true | true:
290
301
31true | false:
321
330
34false | true:
350
361
37exit 2 | exit 3 | exit 4:
384
390
40Done
diff --git a/shell/hush_test/hush-misc/pipefail.tests b/shell/hush_test/hush-misc/pipefail.tests
new file mode 100755
index 000000000..9df841861
--- /dev/null
+++ b/shell/hush_test/hush-misc/pipefail.tests
@@ -0,0 +1,45 @@
1echo Default:
2echo "true | true:"
3 true | true; echo $?
4! true | true; echo $?
5echo "true | false:"
6 true | false; echo $?
7! true | false; echo $?
8echo "false | true:"
9 false | true; echo $?
10! false | true; echo $?
11echo "exit 2 | exit 3 | exit 4:"
12 exit 2 | exit 3 | exit 4; echo $?
13! exit 2 | exit 3 | exit 4; echo $?
14
15echo Pipefail on:
16set -o pipefail
17echo "true | true:"
18 true | true; echo $?
19! true | true; echo $?
20echo "true | false:"
21 true | false; echo $?
22! true | false; echo $?
23echo "false | true:"
24 false | true; echo $?
25! false | true; echo $?
26echo "exit 2 | exit 3 | exit 4:"
27 exit 2 | exit 3 | exit 4; echo $?
28! exit 2 | exit 3 | exit 4; echo $?
29
30echo Pipefail off:
31set +o pipefail
32echo "true | true:"
33 true | true; echo $?
34! true | true; echo $?
35echo "true | false:"
36 true | false; echo $?
37! true | false; echo $?
38echo "false | true:"
39 false | true; echo $?
40! false | true; echo $?
41echo "exit 2 | exit 3 | exit 4:"
42 exit 2 | exit 3 | exit 4; echo $?
43! exit 2 | exit 3 | exit 4; echo $?
44
45echo Done
diff --git a/shell/hush_test/hush-parsing/comment1.right b/shell/hush_test/hush-parsing/comment1.right
new file mode 100644
index 000000000..a102b1d4e
--- /dev/null
+++ b/shell/hush_test/hush-parsing/comment1.right
@@ -0,0 +1,2 @@
1Nothing:
2String: #should-be-echoed
diff --git a/shell/hush_test/hush-parsing/comment1.tests b/shell/hush_test/hush-parsing/comment1.tests
new file mode 100755
index 000000000..d268860ff
--- /dev/null
+++ b/shell/hush_test/hush-parsing/comment1.tests
@@ -0,0 +1,2 @@
1echo Nothing: #should-not-be-echoed
2echo String: ""#should-be-echoed
diff --git a/shell/hush_test/hush-parsing/eol1.right b/shell/hush_test/hush-parsing/eol1.right
new file mode 100644
index 000000000..31c896f62
--- /dev/null
+++ b/shell/hush_test/hush-parsing/eol1.right
@@ -0,0 +1 @@
Done:0
diff --git a/shell/hush_test/hush-parsing/eol1.tests b/shell/hush_test/hush-parsing/eol1.tests
new file mode 100755
index 000000000..f1b55e8b8
--- /dev/null
+++ b/shell/hush_test/hush-parsing/eol1.tests
@@ -0,0 +1,18 @@
1# bug was that we treated <newline> as ';' in this line:
2true || echo foo |
3echo BAD1 | cat
4
5# variation on the same theme
6true || echo foo |
7# comment
8echo BAD2 | cat
9
10# variation on the same theme
11true || echo foo |
12
13echo BAD3 | cat
14
15# this should error out, but currently works in hush:
16#true || echo foo |;
17
18echo Done:$?