hush: implement break and continue
function old new delta bltins 252 276 +24 builtin_continue - 12 +12 builtin_break - 12 +12 static.version_str 18 17 -1 run_list 1984 1948 -36 ------------------------------------------------------------------------------ (add/remove: 2/0 grow/shrink: 1/2 up/down: 48/-27) Total: 11 bytes
This commit is contained in:
parent
cf22c89f9a
commit
bcb25537d0
169
shell/hush.c
169
shell/hush.c
@ -54,7 +54,7 @@
|
|||||||
* to-do:
|
* to-do:
|
||||||
* port selected bugfixes from post-0.49 busybox lash - done?
|
* port selected bugfixes from post-0.49 busybox lash - done?
|
||||||
* change { and } from special chars to reserved words
|
* change { and } from special chars to reserved words
|
||||||
* builtins: break, continue, eval, return, set, trap, ulimit
|
* builtins: return, trap, ulimit
|
||||||
* test magic exec
|
* test magic exec
|
||||||
* check setting of global_argc and global_argv
|
* check setting of global_argc and global_argv
|
||||||
* follow IFS rules more precisely, including update semantics
|
* follow IFS rules more precisely, including update semantics
|
||||||
@ -74,6 +74,7 @@
|
|||||||
#include <fnmatch.h>
|
#include <fnmatch.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define HUSH_VER_STR "0.9"
|
||||||
|
|
||||||
#if !BB_MMU && ENABLE_HUSH_TICK
|
#if !BB_MMU && ENABLE_HUSH_TICK
|
||||||
//#undef ENABLE_HUSH_TICK
|
//#undef ENABLE_HUSH_TICK
|
||||||
@ -230,7 +231,7 @@ void xxfree(void *ptr)
|
|||||||
#define SPECIAL_VAR_SYMBOL 3
|
#define SPECIAL_VAR_SYMBOL 3
|
||||||
#define PARSEFLAG_EXIT_FROM_LOOP 1
|
#define PARSEFLAG_EXIT_FROM_LOOP 1
|
||||||
|
|
||||||
typedef enum {
|
typedef enum redir_type {
|
||||||
REDIRECT_INPUT = 1,
|
REDIRECT_INPUT = 1,
|
||||||
REDIRECT_OVERWRITE = 2,
|
REDIRECT_OVERWRITE = 2,
|
||||||
REDIRECT_APPEND = 3,
|
REDIRECT_APPEND = 3,
|
||||||
@ -253,14 +254,14 @@ static const struct {
|
|||||||
{ O_RDWR, 1, "<>" }
|
{ O_RDWR, 1, "<>" }
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef enum {
|
typedef enum pipe_style {
|
||||||
PIPE_SEQ = 1,
|
PIPE_SEQ = 1,
|
||||||
PIPE_AND = 2,
|
PIPE_AND = 2,
|
||||||
PIPE_OR = 3,
|
PIPE_OR = 3,
|
||||||
PIPE_BG = 4,
|
PIPE_BG = 4,
|
||||||
} pipe_style;
|
} pipe_style;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum reserved_style {
|
||||||
RES_NONE = 0,
|
RES_NONE = 0,
|
||||||
#if ENABLE_HUSH_IF
|
#if ENABLE_HUSH_IF
|
||||||
RES_IF ,
|
RES_IF ,
|
||||||
@ -358,7 +359,7 @@ struct variable {
|
|||||||
smallint flg_read_only;
|
smallint flg_read_only;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct o_string {
|
||||||
char *data;
|
char *data;
|
||||||
int length; /* position where data is appended */
|
int length; /* position where data is appended */
|
||||||
int maxlen;
|
int maxlen;
|
||||||
@ -380,7 +381,7 @@ enum {
|
|||||||
|
|
||||||
/* I can almost use ordinary FILE*. Is open_memstream() universally
|
/* I can almost use ordinary FILE*. Is open_memstream() universally
|
||||||
* available? Where is it documented? */
|
* available? Where is it documented? */
|
||||||
struct in_str {
|
typedef struct in_str {
|
||||||
const char *p;
|
const char *p;
|
||||||
/* eof_flag=1: last char in ->p is really an EOF */
|
/* eof_flag=1: last char in ->p is really an EOF */
|
||||||
char eof_flag; /* meaningless if ->p == NULL */
|
char eof_flag; /* meaningless if ->p == NULL */
|
||||||
@ -392,7 +393,7 @@ struct in_str {
|
|||||||
FILE *file;
|
FILE *file;
|
||||||
int (*get) (struct in_str *);
|
int (*get) (struct in_str *);
|
||||||
int (*peek) (struct in_str *);
|
int (*peek) (struct in_str *);
|
||||||
};
|
} in_str;
|
||||||
#define i_getch(input) ((input)->get(input))
|
#define i_getch(input) ((input)->get(input))
|
||||||
#define i_peek(input) ((input)->peek(input))
|
#define i_peek(input) ((input)->peek(input))
|
||||||
|
|
||||||
@ -403,7 +404,11 @@ enum {
|
|||||||
CHAR_SPECIAL = 3, /* example: $ */
|
CHAR_SPECIAL = 3, /* example: $ */
|
||||||
};
|
};
|
||||||
|
|
||||||
#define HUSH_VER_STR "0.02"
|
enum {
|
||||||
|
BC_BREAK = 1,
|
||||||
|
BC_CONTINUE = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/* "Globals" within this file */
|
/* "Globals" within this file */
|
||||||
|
|
||||||
@ -429,6 +434,7 @@ struct globals {
|
|||||||
struct pipe *toplevel_list;
|
struct pipe *toplevel_list;
|
||||||
smallint ctrl_z_flag;
|
smallint ctrl_z_flag;
|
||||||
#endif
|
#endif
|
||||||
|
smallint flag_break_continue;
|
||||||
smallint fake_mode;
|
smallint fake_mode;
|
||||||
/* these three support $?, $#, and $1 */
|
/* these three support $?, $#, and $1 */
|
||||||
smalluint last_return_code;
|
smalluint last_return_code;
|
||||||
@ -481,6 +487,7 @@ enum { run_list_level = 0 };
|
|||||||
#define global_argc (G.global_argc )
|
#define global_argc (G.global_argc )
|
||||||
#define last_return_code (G.last_return_code)
|
#define last_return_code (G.last_return_code)
|
||||||
#define ifs (G.ifs )
|
#define ifs (G.ifs )
|
||||||
|
#define flag_break_continue (G.flag_break_continue)
|
||||||
#define fake_mode (G.fake_mode )
|
#define fake_mode (G.fake_mode )
|
||||||
#define cwd (G.cwd )
|
#define cwd (G.cwd )
|
||||||
#define last_bg_pid (G.last_bg_pid )
|
#define last_bg_pid (G.last_bg_pid )
|
||||||
@ -713,6 +720,8 @@ static int builtin_shift(char **argv);
|
|||||||
static int builtin_source(char **argv);
|
static int builtin_source(char **argv);
|
||||||
static int builtin_umask(char **argv);
|
static int builtin_umask(char **argv);
|
||||||
static int builtin_unset(char **argv);
|
static int builtin_unset(char **argv);
|
||||||
|
static int builtin_break(char **argv);
|
||||||
|
static int builtin_continue(char **argv);
|
||||||
//static int builtin_not_written(char **argv);
|
//static int builtin_not_written(char **argv);
|
||||||
|
|
||||||
/* Table of built-in functions. They can be forked or not, depending on
|
/* Table of built-in functions. They can be forked or not, depending on
|
||||||
@ -742,10 +751,10 @@ static const struct built_in_command bltins[] = {
|
|||||||
#if ENABLE_HUSH_JOB
|
#if ENABLE_HUSH_JOB
|
||||||
BLTIN("bg" , builtin_fg_bg, "Resume a job in the background"),
|
BLTIN("bg" , builtin_fg_bg, "Resume a job in the background"),
|
||||||
#endif
|
#endif
|
||||||
// BLTIN("break" , builtin_not_written, "Exit for, while or until loop"),
|
BLTIN("break" , builtin_break, "Exit from a loop"),
|
||||||
BLTIN("cd" , builtin_cd, "Change directory"),
|
BLTIN("cd" , builtin_cd, "Change directory"),
|
||||||
// BLTIN("continue", builtin_not_written, "Continue for, while or until loop"),
|
BLTIN("continue", builtin_continue, "Start new loop iteration"),
|
||||||
BLTIN("echo" , builtin_echo, "Write strings to stdout"),
|
BLTIN("echo" , builtin_echo, "Write to stdout"),
|
||||||
BLTIN("eval" , builtin_eval, "Construct and run shell command"),
|
BLTIN("eval" , builtin_eval, "Construct and run shell command"),
|
||||||
BLTIN("exec" , builtin_exec, "Execute command, don't return to shell"),
|
BLTIN("exec" , builtin_exec, "Execute command, don't return to shell"),
|
||||||
BLTIN("exit" , builtin_exit, "Exit"),
|
BLTIN("exit" , builtin_exit, "Exit"),
|
||||||
@ -2016,29 +2025,25 @@ static int run_list(struct pipe *pi)
|
|||||||
char *case_word = NULL;
|
char *case_word = NULL;
|
||||||
#endif
|
#endif
|
||||||
#if ENABLE_HUSH_LOOPS
|
#if ENABLE_HUSH_LOOPS
|
||||||
struct pipe *loop_top = loop_top; /* just for compiler */
|
struct pipe *loop_top = NULL;
|
||||||
char *for_varname = NULL;
|
char *for_varname = NULL;
|
||||||
char **for_lcur = NULL;
|
char **for_lcur = NULL;
|
||||||
char **for_list = NULL;
|
char **for_list = NULL;
|
||||||
smallint flag_run_loop = 0;
|
|
||||||
smallint flag_goto_looptop = 0;
|
|
||||||
#endif
|
#endif
|
||||||
smallint flag_skip = 1;
|
smallint flag_skip = 1;
|
||||||
smalluint rcode = 0; /* probably just for compiler */
|
smalluint rcode = 0; /* probably just for compiler */
|
||||||
#if ENABLE_HUSH_IF
|
#if ENABLE_HUSH_IF
|
||||||
smalluint cond_code = 0;
|
smalluint cond_code = 0;
|
||||||
///experimentally off: last_cond_code seems to be bogus
|
|
||||||
///smalluint last_cond_code = 0; /* need double-buffer to handle "elif" */
|
|
||||||
#else
|
#else
|
||||||
enum { cond_code = 0, /* ///last_cond_code = 0 */ };
|
enum { cond_code = 0, };
|
||||||
#endif
|
#endif
|
||||||
/*reserved_style*/ smallint rword IF_HAS_NO_KEYWORDS(= RES_NONE);
|
/*enum reserved_style*/ smallint rword = RES_NONE;
|
||||||
/*reserved_style*/ smallint skip_more_for_this_rword = RES_XXXX;
|
/*enum reserved_style*/ smallint skip_more_for_this_rword = RES_XXXX;
|
||||||
|
|
||||||
debug_printf_exec("run_list start lvl %d\n", run_list_level + 1);
|
debug_printf_exec("run_list start lvl %d\n", run_list_level + 1);
|
||||||
|
|
||||||
#if ENABLE_HUSH_LOOPS
|
#if ENABLE_HUSH_LOOPS
|
||||||
/* check syntax for "for" */
|
/* Check syntax for "for" */
|
||||||
for (struct pipe *cpipe = pi; cpipe; cpipe = cpipe->next) {
|
for (struct pipe *cpipe = pi; cpipe; cpipe = cpipe->next) {
|
||||||
if (cpipe->res_word != RES_FOR && cpipe->res_word != RES_IN)
|
if (cpipe->res_word != RES_FOR && cpipe->res_word != RES_IN)
|
||||||
continue;
|
continue;
|
||||||
@ -2108,20 +2113,16 @@ static int run_list(struct pipe *pi)
|
|||||||
}
|
}
|
||||||
#endif /* JOB */
|
#endif /* JOB */
|
||||||
|
|
||||||
/* Go through list of pipes, (maybe) executing them */
|
/* Go through list of pipes, (maybe) executing them. */
|
||||||
for (; pi; pi = USE_HUSH_LOOPS( flag_goto_looptop ? loop_top : ) pi->next) {
|
for (; pi; pi = USE_HUSH_LOOPS(rword == RES_DONE ? loop_top : ) pi->next) {
|
||||||
IF_HAS_KEYWORDS(rword = pi->res_word;)
|
IF_HAS_KEYWORDS(rword = pi->res_word;)
|
||||||
IF_HAS_NO_KEYWORDS(rword = RES_NONE;)
|
IF_HAS_NO_KEYWORDS(rword = RES_NONE;)
|
||||||
debug_printf_exec(": rword=%d cond_code=%d last_cond_code=%d skip_more=%d flag_run_loop=%d\n",
|
debug_printf_exec(": rword=%d cond_code=%d skip_more=%d\n",
|
||||||
rword, cond_code, last_cond_code, skip_more_for_this_rword, flag_run_loop);
|
rword, cond_code, skip_more_for_this_rword);
|
||||||
#if ENABLE_HUSH_LOOPS
|
#if ENABLE_HUSH_LOOPS
|
||||||
if (rword == RES_WHILE || rword == RES_UNTIL || rword == RES_FOR) {
|
if (rword == RES_WHILE || rword == RES_UNTIL || rword == RES_FOR) {
|
||||||
/* start of a loop: remember it */
|
/* start of a loop: remember where loop starts */
|
||||||
flag_goto_looptop = 0; /* not yet reached final "done" */
|
loop_top = pi;
|
||||||
// if (!loop_top) { /* hmm why this check is needed? */
|
|
||||||
// flag_run_loop = 0; /* suppose loop condition is false (for now) */
|
|
||||||
loop_top = pi; /* remember where loop starts */
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (rword == skip_more_for_this_rword && flag_skip) {
|
if (rword == skip_more_for_this_rword && flag_skip) {
|
||||||
@ -2134,18 +2135,21 @@ static int run_list(struct pipe *pi)
|
|||||||
flag_skip = 1;
|
flag_skip = 1;
|
||||||
skip_more_for_this_rword = RES_XXXX;
|
skip_more_for_this_rword = RES_XXXX;
|
||||||
#if ENABLE_HUSH_IF
|
#if ENABLE_HUSH_IF
|
||||||
/// if (rword == RES_THEN) // || rword == RES_ELSE)
|
if (cond_code) {
|
||||||
/// cond_code = last_cond_code;
|
if (rword == RES_THEN) {
|
||||||
if (rword == RES_THEN && cond_code)
|
/* "if <false> THEN cmd": skip cmd */
|
||||||
continue; /* "if <false> THEN cmd": skip cmd */
|
continue;
|
||||||
if (rword == RES_ELSE && !cond_code)
|
}
|
||||||
//continue; /* "if <true> then ... ELSE cmd": skip cmd */
|
} else {
|
||||||
break; //TEST
|
if (rword == RES_ELSE || rword == RES_ELIF) {
|
||||||
if (rword == RES_ELIF && !cond_code)
|
/* "if <true> then ... ELSE/ELIF cmd":
|
||||||
break; /* "if <true> then ... ELIF cmd": skip cmd and all following ones */
|
* skip cmd and all following ones */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
#if ENABLE_HUSH_LOOPS
|
#if ENABLE_HUSH_LOOPS
|
||||||
if (rword == RES_FOR && pi->num_progs) { /* hmm why "&& pi->num_progs"? */
|
if (rword == RES_FOR) { /* && pi->num_progs - always == 1 */
|
||||||
if (!for_lcur) {
|
if (!for_lcur) {
|
||||||
/* first loop through for */
|
/* first loop through for */
|
||||||
|
|
||||||
@ -2161,7 +2165,7 @@ static int run_list(struct pipe *pi)
|
|||||||
if (pi->next->res_word == RES_IN) {
|
if (pi->next->res_word == RES_IN) {
|
||||||
/* if no variable values after "in" we skip "for" */
|
/* if no variable values after "in" we skip "for" */
|
||||||
if (!pi->next->progs->argv)
|
if (!pi->next->progs->argv)
|
||||||
continue;
|
break;
|
||||||
vals = pi->next->progs->argv;
|
vals = pi->next->progs->argv;
|
||||||
} /* else: "for var; do..." -> assume "$@" list */
|
} /* else: "for var; do..." -> assume "$@" list */
|
||||||
/* create list of variable values */
|
/* create list of variable values */
|
||||||
@ -2171,7 +2175,6 @@ static int run_list(struct pipe *pi)
|
|||||||
debug_print_strings("for_list", for_list);
|
debug_print_strings("for_list", for_list);
|
||||||
for_varname = pi->progs->argv[0];
|
for_varname = pi->progs->argv[0];
|
||||||
pi->progs->argv[0] = NULL;
|
pi->progs->argv[0] = NULL;
|
||||||
flag_run_loop = 1; /* "for" has no loop condition, loop... */
|
|
||||||
}
|
}
|
||||||
free(pi->progs->argv[0]);
|
free(pi->progs->argv[0]);
|
||||||
if (!*for_lcur) {
|
if (!*for_lcur) {
|
||||||
@ -2179,33 +2182,22 @@ static int run_list(struct pipe *pi)
|
|||||||
free(for_list);
|
free(for_list);
|
||||||
for_list = NULL;
|
for_list = NULL;
|
||||||
for_lcur = NULL;
|
for_lcur = NULL;
|
||||||
flag_run_loop = 0; /* ... until end of value list */
|
|
||||||
pi->progs->argv[0] = for_varname;
|
pi->progs->argv[0] = for_varname;
|
||||||
continue;
|
break;
|
||||||
}
|
}
|
||||||
/* insert next value from for_lcur */
|
/* insert next value from for_lcur */
|
||||||
//TODO: does it need escaping?
|
//TODO: does it need escaping?
|
||||||
pi->progs->argv[0] = xasprintf("%s=%s", for_varname, *for_lcur++);
|
pi->progs->argv[0] = xasprintf("%s=%s", for_varname, *for_lcur++);
|
||||||
}
|
}
|
||||||
if (rword == RES_IN) /* "for v IN list; do ..." - no pipe to execute here */
|
if (rword == RES_IN) /* "for v IN list;..." - "in" has no cmds anyway */
|
||||||
continue;
|
continue;
|
||||||
if (rword == RES_DO) { /* "...; DO cmd; cmd" - this pipe is in loop body */
|
if (rword == RES_DONE) {
|
||||||
if (!flag_run_loop)
|
continue; /* "done" has no cmds too */
|
||||||
continue; /* we are skipping this iteration */
|
|
||||||
}
|
|
||||||
if (rword == RES_DONE) { /* end of loop? */
|
|
||||||
if (flag_run_loop) {
|
|
||||||
flag_goto_looptop = 1;
|
|
||||||
// } else {
|
|
||||||
// loop_top = NULL;
|
|
||||||
}
|
|
||||||
continue; //TEST /* "done" has no cmd anyway */
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#if ENABLE_HUSH_CASE
|
#if ENABLE_HUSH_CASE
|
||||||
if (rword == RES_CASE) {
|
if (rword == RES_CASE) {
|
||||||
case_word = expand_strvec_to_string(pi->progs->argv);
|
case_word = expand_strvec_to_string(pi->progs->argv);
|
||||||
//bb_error_msg("case: arg:'%s' case_word:'%s'", pi->progs->argv[0], case_word);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (rword == RES_MATCH) {
|
if (rword == RES_MATCH) {
|
||||||
@ -2215,35 +2207,53 @@ static int run_list(struct pipe *pi)
|
|||||||
/* all prev words didn't match, does this one match? */
|
/* all prev words didn't match, does this one match? */
|
||||||
pattern = expand_strvec_to_string(pi->progs->argv);
|
pattern = expand_strvec_to_string(pi->progs->argv);
|
||||||
/* TODO: which FNM_xxx flags to use? */
|
/* TODO: which FNM_xxx flags to use? */
|
||||||
/* ///last_ */ cond_code = (fnmatch(pattern, case_word, /*flags:*/ 0) != 0);
|
cond_code = (fnmatch(pattern, case_word, /*flags:*/ 0) != 0);
|
||||||
//bb_error_msg("fnmatch('%s','%s'):%d", pattern, case_word, cond_code);
|
|
||||||
free(pattern);
|
free(pattern);
|
||||||
if (/* ///last_ */ cond_code == 0) { /* match! we will execute this branch */
|
if (cond_code == 0) { /* match! we will execute this branch */
|
||||||
free(case_word); /* make future "word)" stop */
|
free(case_word); /* make future "word)" stop */
|
||||||
case_word = NULL;
|
case_word = NULL;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (rword == RES_CASEI) { /* inside of a case branch */
|
if (rword == RES_CASEI) { /* inside of a case branch */
|
||||||
if (/* ///last_ */ cond_code != 0)
|
if (cond_code != 0)
|
||||||
continue; /* not matched yet, skip this pipe */
|
continue; /* not matched yet, skip this pipe */
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (pi->num_progs == 0)
|
if (pi->num_progs == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* After analyzing all keywrds and conditions, we decided
|
/* After analyzing all keywords and conditions, we decided
|
||||||
* to execute this pipe */
|
* to execute this pipe. NB: has to do checkjobs(NULL)
|
||||||
|
* after run_pipe() to collect any background children,
|
||||||
|
* even if list execution is to be stopped. */
|
||||||
debug_printf_exec(": run_pipe with %d members\n", pi->num_progs);
|
debug_printf_exec(": run_pipe with %d members\n", pi->num_progs);
|
||||||
{
|
{
|
||||||
int r;
|
int r;
|
||||||
|
flag_break_continue = 0;
|
||||||
rcode = r = run_pipe(pi); /* NB: rcode is a smallint */
|
rcode = r = run_pipe(pi); /* NB: rcode is a smallint */
|
||||||
if (r != -1) {
|
if (r != -1) {
|
||||||
/* We only ran a builtin: rcode was set by the return value
|
/* we only ran a builtin: rcode is already known
|
||||||
* of run_pipe(), and we don't need to wait for anything. */
|
* and we don't need to wait for anything. */
|
||||||
|
/* was it "break" or "continue"? */
|
||||||
|
if (flag_break_continue) {
|
||||||
|
smallint fbc = flag_break_continue;
|
||||||
|
/* we might fall into outer *loop*,
|
||||||
|
* don't want to break it too */
|
||||||
|
flag_break_continue = 0;
|
||||||
|
if (loop_top) {
|
||||||
|
if (fbc == BC_BREAK)
|
||||||
|
goto check_jobs_and_break;
|
||||||
|
/* "continue": simulate end of loop */
|
||||||
|
rword = RES_DONE;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
bb_error_msg("break/continue: only meaningful in a loop");
|
||||||
|
/* bash compat: exit code is still 0 */
|
||||||
|
}
|
||||||
} else if (pi->followup == PIPE_BG) {
|
} else if (pi->followup == PIPE_BG) {
|
||||||
/* What does bash do with attempts to background builtins? */
|
/* what does bash do with attempts to background builtins? */
|
||||||
/* Even bash 3.2 doesn't do that well with nested bg:
|
/* even bash 3.2 doesn't do that well with nested bg:
|
||||||
* try "{ { sleep 10; echo DEEP; } & echo HERE; } &".
|
* try "{ { sleep 10; echo DEEP; } & echo HERE; } &".
|
||||||
* I'm NOT treating inner &'s as jobs */
|
* I'm NOT treating inner &'s as jobs */
|
||||||
#if ENABLE_HUSH_JOB
|
#if ENABLE_HUSH_JOB
|
||||||
@ -2271,16 +2281,19 @@ static int run_list(struct pipe *pi)
|
|||||||
/* Analyze how result affects subsequent commands */
|
/* Analyze how result affects subsequent commands */
|
||||||
#if ENABLE_HUSH_IF
|
#if ENABLE_HUSH_IF
|
||||||
if (rword == RES_IF || rword == RES_ELIF)
|
if (rword == RES_IF || rword == RES_ELIF)
|
||||||
/* ///last_cond_code = */ cond_code = rcode;
|
cond_code = rcode;
|
||||||
#endif
|
#endif
|
||||||
#if ENABLE_HUSH_LOOPS
|
#if ENABLE_HUSH_LOOPS
|
||||||
if (rword == RES_WHILE) {
|
if (rword == RES_WHILE) {
|
||||||
flag_run_loop = !rcode;
|
if (rcode)
|
||||||
debug_printf_exec(": setting flag_run_loop=%d\n", flag_run_loop);
|
goto check_jobs_and_break;
|
||||||
}
|
}
|
||||||
if (rword == RES_UNTIL) {
|
if (rword == RES_UNTIL) {
|
||||||
flag_run_loop = rcode;
|
if (!rcode) {
|
||||||
debug_printf_exec(": setting flag_run_loop=%d\n", flag_run_loop);
|
check_jobs_and_break:
|
||||||
|
checkjobs(NULL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if ((rcode == 0 && pi->followup == PIPE_OR)
|
if ((rcode == 0 && pi->followup == PIPE_OR)
|
||||||
@ -4498,3 +4511,15 @@ static int builtin_unset(char **argv)
|
|||||||
unset_local_var(argv[1]);
|
unset_local_var(argv[1]);
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int builtin_break(char **argv UNUSED_PARAM)
|
||||||
|
{
|
||||||
|
flag_break_continue = BC_BREAK;
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int builtin_continue(char **argv UNUSED_PARAM)
|
||||||
|
{
|
||||||
|
flag_break_continue = BC_CONTINUE;
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
@ -2,9 +2,10 @@
|
|||||||
|
|
||||||
Command parsing
|
Command parsing
|
||||||
|
|
||||||
Command parsing results in "pipe" structures. "Pipe" structure
|
Command parsing results in a list of "pipe" structures.
|
||||||
does not always correspond to what sh language calls "pipe",
|
This list correspond not only to usual "pipe1 || pipe2 && pipe3"
|
||||||
it also controls execution of if, while, etc statements.
|
lists, but it also controls execution of if, while, etc statements.
|
||||||
|
Every such statement is a list for hush. List consists of pipes.
|
||||||
|
|
||||||
struct pipe fields:
|
struct pipe fields:
|
||||||
smallint res_word - "none" for normal commands,
|
smallint res_word - "none" for normal commands,
|
||||||
@ -18,7 +19,7 @@ Blocks of commands { pipe; pipe; } and (pipe; pipe) are represented
|
|||||||
as one pipe struct with one progs[0] element which is a "group" -
|
as one pipe struct with one progs[0] element which is a "group" -
|
||||||
struct child_prog can contain a list of pipes. Sometimes these
|
struct child_prog can contain a list of pipes. Sometimes these
|
||||||
"groups" are created implicitly, e.g. every control
|
"groups" are created implicitly, e.g. every control
|
||||||
statement (if, while, etc) sits inside its own "pipe" struct).
|
statement (if, while, etc) sits inside its own group.
|
||||||
|
|
||||||
res_word controls statement execution. Examples:
|
res_word controls statement execution. Examples:
|
||||||
|
|
||||||
@ -41,6 +42,10 @@ res_word=NONE followup=SEQ
|
|||||||
pipe 4 res_word=NONE followup=(null)
|
pipe 4 res_word=NONE followup=(null)
|
||||||
pipe 1 res_word=NONE followup=SEQ
|
pipe 1 res_word=NONE followup=SEQ
|
||||||
|
|
||||||
|
Above you see that if is a list, and it sits in a {} group
|
||||||
|
implicitly created by hush. Also note two THEN res_word's -
|
||||||
|
it is explained below.
|
||||||
|
|
||||||
"if true; then { echo Hello; true; }; fi" -
|
"if true; then { echo Hello; true; }; fi" -
|
||||||
pipe 0 res_word=NONE followup=SEQ
|
pipe 0 res_word=NONE followup=SEQ
|
||||||
prog 0 group {}:
|
prog 0 group {}:
|
||||||
|
Loading…
Reference in New Issue
Block a user