Nice patch from Wolfgang Denk <wd@denx.de> to provide hush with

shell loops (for, while, until) and control operators (||, &&)
This commit is contained in:
Eric Andersen 2002-04-13 12:33:41 +00:00
parent a66a43e8ef
commit 4c9b68f0e0

View File

@ -116,6 +116,10 @@
#undef CONFIG_FEATURE_SH_FANCY_PROMPT
#define BB_BANNER
#endif
#define SPECIAL_VAR_SYMBOL 03
#define FLAG_EXIT_FROM_LOOP 1
#define FLAG_PARSE_SEMICOLON (1 << 1) /* symbol ';' is special for parser */
#define FLAG_REPARSING (1 << 2) /* >=2nd pass */
typedef enum {
REDIRECT_INPUT = 1,
@ -157,7 +161,8 @@ typedef enum {
RES_DO = 9,
RES_DONE = 10,
RES_XXXX = 11,
RES_SNTX = 12
RES_IN = 12,
RES_SNTX = 13
} reserved_style;
#define FLAG_END (1<<RES_NONE)
#define FLAG_IF (1<<RES_IF)
@ -170,6 +175,7 @@ typedef enum {
#define FLAG_UNTIL (1<<RES_UNTIL)
#define FLAG_DO (1<<RES_DO)
#define FLAG_DONE (1<<RES_DONE)
#define FLAG_IN (1<<RES_IN)
#define FLAG_START (1<<RES_XXXX)
/* This holds pointers to the various results of parsing */
@ -181,6 +187,7 @@ struct p_context {
reserved_style w;
int old_flag; /* for figuring out valid reserved words */
struct p_context *stack;
int type; /* define type of parser : ";$" common or special symbol */
/* How about quoting status? */
};
@ -201,6 +208,8 @@ struct child_prog {
glob_t glob_result; /* result of parameter globbing */
int is_stopped; /* is the program currently running? */
struct pipe *family; /* pointer back to the child's parent pipe */
int sp; /* number of SPECIAL_VAR_SYMBOL */
int type;
};
struct pipe {
@ -319,6 +328,7 @@ static void __syntax(char *file, int line) {
/* function prototypes for builtins */
static int builtin_cd(struct child_prog *child);
static int builtin_env(struct child_prog *child);
static int builtin_eval(struct child_prog *child);
static int builtin_exec(struct child_prog *child);
static int builtin_exit(struct child_prog *child);
static int builtin_export(struct child_prog *child);
@ -376,19 +386,22 @@ static int redirect_dup_num(struct in_str *input);
static int redirect_opt_num(o_string *o);
static int process_command_subs(o_string *dest, struct p_context *ctx, struct in_str *input, int subst_end);
static int parse_group(o_string *dest, struct p_context *ctx, struct in_str *input, int ch);
static void lookup_param(o_string *dest, struct p_context *ctx, o_string *src);
static char *lookup_param(char *src);
static char *make_string(char **inp);
static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input);
static int parse_string(o_string *dest, struct p_context *ctx, const char *src);
static int parse_stream(o_string *dest, struct p_context *ctx, struct in_str *input0, int end_trigger);
/* setup: */
static int parse_stream_outer(struct in_str *inp);
static int parse_string_outer(const char *s);
static int parse_stream_outer(struct in_str *inp, int flag);
static int parse_string_outer(const char *s, int flag);
static int parse_file_outer(FILE *f);
/* job management: */
static int checkjobs(struct pipe* fg_pipe);
static void insert_bg_job(struct pipe *pi);
static void remove_bg_job(struct pipe *pi);
/* local variable support */
static char **make_list_in(char **inp, char *name);
static char *insert_var_value(char *inp);
static char *get_local_var(const char *var);
static void unset_local_var(const char *name);
static int set_local_var(const char *s, int flg_export);
@ -405,7 +418,7 @@ static struct built_in_command bltins[] = {
{"cd", "Change working directory", builtin_cd},
{"continue", "Continue for, while or until loop", builtin_not_written},
{"env", "Print all environment variables", builtin_env},
{"eval", "Construct and run shell command", builtin_not_written},
{"eval", "Construct and run shell command", builtin_eval},
{"exec", "Exec command, replacing this shell with the exec'd process",
builtin_exec},
{"exit", "Exit from shell()", builtin_exit},
@ -436,6 +449,21 @@ static const char *set_cwd(void)
return cwd;
}
/* built-in 'eval' handler */
static int builtin_eval(struct child_prog *child)
{
char *str = NULL;
int rcode = EXIT_SUCCESS;
if (child->argv[1]) {
str = make_string(child->argv + 1);
parse_string_outer(str, FLAG_EXIT_FROM_LOOP |
FLAG_PARSE_SEMICOLON);
free(str);
rcode = last_return_code;
}
return rcode;
}
/* built-in 'cd <path>' handler */
static int builtin_cd(struct child_prog *child)
@ -1046,11 +1074,14 @@ static void restore_redirects(int squirrel[])
static void pseudo_exec(struct child_prog *child)
{
int i, rcode;
char *p;
struct built_in_command *x;
if (child->argv) {
for (i=0; is_assignment(child->argv[i]); i++) {
debug_printf("pid %d environment modification: %s\n",getpid(),child->argv[i]);
putenv(strdup(child->argv[i]));
p = insert_var_value(child->argv[i]);
putenv(strdup(p));
if (p != child->argv[i]) free(p);
}
child->argv+=i; /* XXX this hack isn't so horrible, since we are about
to exit, and therefore don't need to keep data
@ -1317,6 +1348,7 @@ static int run_pipe_real(struct pipe *pi)
int pipefds[2]; /* pipefds[0] is for reading */
struct child_prog *child;
struct built_in_command *x;
char *p;
nextin = 0;
pi->pgrp = -1;
@ -1359,10 +1391,28 @@ static int run_pipe_real(struct pipe *pi)
export_me=1;
}
free(name);
set_local_var(child->argv[i], export_me);
p = insert_var_value(child->argv[i]);
set_local_var(p, export_me);
if (p != child->argv[i]) free(p);
}
return EXIT_SUCCESS; /* don't worry about errors in set_local_var() yet */
}
for (i = 0; is_assignment(child->argv[i]); i++) {
p = insert_var_value(child->argv[i]);
putenv(strdup(p));
if (p != child->argv[i]) {
child->sp--;
free(p);
}
}
if (child->sp) {
char * str = NULL;
str = make_string((child->argv + i));
parse_string_outer(str, FLAG_EXIT_FROM_LOOP | FLAG_REPARSING);
free(str);
return last_return_code;
}
for (x = bltins; x->cmd; x++) {
if (strcmp(child->argv[i], x->cmd) == 0 ) {
int squirrel[] = {-1, -1, -1};
@ -1378,9 +1428,6 @@ static int run_pipe_real(struct pipe *pi)
* Is it really safe for inline use? Experimentally,
* things seem to work with glibc. */
setup_redirects(child, squirrel);
for (i=0; is_assignment(child->argv[i]); i++) {
putenv(strdup(child->argv[i]));
}
child->argv+=i; /* XXX horrible hack */
rcode = x->function(child);
child->argv-=i; /* XXX restore hack so free() can work right */
@ -1474,19 +1521,97 @@ static int run_pipe_real(struct pipe *pi)
static int run_list_real(struct pipe *pi)
{
int rcode=0;
char *save_name = NULL;
char **list = NULL;
char **save_list = NULL;
struct pipe *rpipe;
int flag_rep = 0;
int save_num_progs;
int rcode=0, flag_skip=1;
int flag_restore = 0;
int if_code=0, next_if_code=0; /* need double-buffer to handle elif */
reserved_style rmode, skip_more_in_this_rmode=RES_XXXX;
for (;pi;pi=pi->next) {
/* check syntax for "for" */
for (rpipe = pi; rpipe; rpipe = rpipe->next) {
if ((rpipe->r_mode == RES_IN ||
rpipe->r_mode == RES_FOR) &&
(rpipe->next == NULL)) {
syntax();
return 1;
}
if ((rpipe->r_mode == RES_IN &&
(rpipe->next->r_mode == RES_IN &&
rpipe->next->progs->argv != NULL))||
(rpipe->r_mode == RES_FOR &&
rpipe->next->r_mode != RES_IN)) {
syntax();
return 1;
}
}
for (; pi; pi = (flag_restore != 0) ? rpipe : pi->next) {
if (pi->r_mode == RES_WHILE || pi->r_mode == RES_UNTIL ||
pi->r_mode == RES_FOR) {
flag_restore = 0;
if (!rpipe) {
flag_rep = 0;
rpipe = pi;
}
}
rmode = pi->r_mode;
debug_printf("rmode=%d if_code=%d next_if_code=%d skip_more=%d\n", rmode, if_code, next_if_code, skip_more_in_this_rmode);
if (rmode == skip_more_in_this_rmode) continue;
if (rmode == skip_more_in_this_rmode && flag_skip) {
if (pi->followup == PIPE_SEQ) flag_skip=0;
continue;
}
flag_skip = 1;
skip_more_in_this_rmode = RES_XXXX;
if (rmode == RES_THEN || rmode == RES_ELSE) if_code = next_if_code;
if (rmode == RES_THEN && if_code) continue;
if (rmode == RES_ELSE && !if_code) continue;
if (rmode == RES_ELIF && !if_code) continue;
if (rmode == RES_FOR && pi->num_progs) {
if (!list) {
/* if no variable values after "in" we skip "for" */
if (!pi->next->progs->argv) continue;
/* create list of variable values */
list = make_list_in(pi->next->progs->argv,
pi->progs->argv[0]);
save_list = list;
save_name = pi->progs->argv[0];
pi->progs->argv[0] = NULL;
flag_rep = 1;
}
if (!(*list)) {
free(pi->progs->argv[0]);
free(save_list);
list = NULL;
flag_rep = 0;
pi->progs->argv[0] = save_name;
pi->progs->glob_result.gl_pathv[0] =
pi->progs->argv[0];
continue;
} else {
/* insert new value from list for variable */
if (pi->progs->argv[0])
free(pi->progs->argv[0]);
pi->progs->argv[0] = *list++;
pi->progs->glob_result.gl_pathv[0] =
pi->progs->argv[0];
}
}
if (rmode == RES_IN) continue;
if (rmode == RES_DO) {
if (!flag_rep) continue;
}
if ((rmode == RES_DONE)) {
if (flag_rep) {
flag_restore = 1;
} else {
rpipe = NULL;
}
}
if (pi->num_progs == 0) continue;
save_num_progs = pi->num_progs; /* save number of programs */
rcode = run_pipe_real(pi);
debug_printf("run_pipe_real returned %d\n",rcode);
if (rcode!=-1) {
@ -1513,8 +1638,13 @@ static int run_list_real(struct pipe *pi)
debug_printf("checkjobs returned %d\n",rcode);
}
last_return_code=rcode;
pi->num_progs = save_num_progs; /* restore number of programs */
if ( rmode == RES_IF || rmode == RES_ELIF )
next_if_code=rcode; /* can be overwritten a number of times */
if (rmode == RES_WHILE)
flag_rep = !last_return_code;
if (rmode == RES_UNTIL)
flag_rep = last_return_code;
if ( (rcode==EXIT_SUCCESS && pi->followup==PIPE_OR) ||
(rcode!=EXIT_SUCCESS && pi->followup==PIPE_AND) )
skip_more_in_this_rmode=rmode;
@ -1898,6 +2028,7 @@ static void initialize_context(struct p_context *ctx)
ctx->pipe=ctx->list_head;
ctx->w=RES_NONE;
ctx->stack=NULL;
ctx->old_flag=0;
done_command(ctx); /* creates the memory for working child */
}
@ -1924,9 +2055,10 @@ int reserved_word(o_string *dest, struct p_context *ctx)
{ "elif", RES_ELIF, FLAG_THEN },
{ "else", RES_ELSE, FLAG_FI },
{ "fi", RES_FI, FLAG_END },
{ "for", RES_FOR, FLAG_DO | FLAG_START },
{ "for", RES_FOR, FLAG_IN | FLAG_START },
{ "while", RES_WHILE, FLAG_DO | FLAG_START },
{ "until", RES_UNTIL, FLAG_DO | FLAG_START },
{ "in", RES_IN, FLAG_DO },
{ "do", RES_DO, FLAG_DONE },
{ "done", RES_DONE, FLAG_END }
};
@ -1939,13 +2071,20 @@ int reserved_word(o_string *dest, struct p_context *ctx)
if (r->flag & FLAG_START) {
struct p_context *new = xmalloc(sizeof(struct p_context));
debug_printf("push stack\n");
if (ctx->w == RES_IN || ctx->w == RES_FOR) {
syntax();
free(new);
ctx->w = RES_SNTX;
b_reset(dest);
return 1;
}
*new = *ctx; /* physical copy */
initialize_context(ctx);
ctx->stack=new;
} else if ( ctx->w == RES_NONE || ! (ctx->old_flag & (1<<r->code))) {
syntax();
ctx->w = RES_SNTX;
b_reset (dest);
b_reset(dest);
return 1;
}
ctx->w=r->code;
@ -1953,6 +2092,7 @@ int reserved_word(o_string *dest, struct p_context *ctx)
if (ctx->old_flag & FLAG_END) {
struct p_context *old;
debug_printf("pop stack\n");
done_pipe(ctx,PIPE_SEQ);
old = ctx->stack;
old->child->group = ctx->list_head;
old->child->subshell = 0;
@ -1986,7 +2126,7 @@ static int done_word(o_string *dest, struct p_context *ctx)
syntax();
return 1; /* syntax error, groups and arglists don't mix */
}
if (!child->argv) {
if (!child->argv && (ctx->type & FLAG_PARSE_SEMICOLON)) {
debug_printf("checking %s for reserved-ness\n",dest->data);
if (reserved_word(dest,ctx)) return ctx->w==RES_SNTX;
}
@ -2006,6 +2146,10 @@ static int done_word(o_string *dest, struct p_context *ctx)
} else {
child->argv = glob_target->gl_pathv;
}
if (ctx->w == RES_FOR) {
done_word(dest,ctx);
done_pipe(ctx,PIPE_SEQ);
}
return 0;
}
@ -2041,8 +2185,10 @@ static int done_command(struct p_context *ctx)
prog->group = NULL;
prog->glob_result.gl_pathv = NULL;
prog->family = pi;
prog->sp = 0;
ctx->child = prog;
prog->type = ctx->type;
ctx->child=prog;
/* but ctx->pipe and ctx->list_head remain unchanged */
return 0;
}
@ -2227,32 +2373,32 @@ static int parse_group(o_string *dest, struct p_context *ctx,
/* basically useful version until someone wants to get fancier,
* see the bash man page under "Parameter Expansion" */
static void lookup_param(o_string *dest, struct p_context *ctx, o_string *src)
static char *lookup_param(char *src)
{
const char *p=NULL;
if (src->data) {
p = getenv(src->data);
char *p=NULL;
if (src) {
p = getenv(src);
if (!p)
p = get_local_var(src->data);
p = get_local_var(src);
}
if (p) parse_string(dest, ctx, p); /* recursion */
b_free(src);
return p;
}
/* return code: 0 for OK, 1 for syntax error */
static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input)
{
int i, advance=0;
o_string alt=NULL_O_STRING;
char sep[]=" ";
int ch = input->peek(input); /* first character after the $ */
debug_printf("handle_dollar: ch=%c\n",ch);
if (isalpha(ch)) {
b_addchr(dest, SPECIAL_VAR_SYMBOL);
ctx->child->sp++;
while(ch=b_peek(input),isalnum(ch) || ch=='_') {
b_getch(input);
b_addchr(&alt,ch);
b_addchr(dest,ch);
}
lookup_param(dest, ctx, &alt);
b_addchr(dest, SPECIAL_VAR_SYMBOL);
} else if (isdigit(ch)) {
i = ch-'0'; /* XXX is $0 special? */
if (i<global_argc) {
@ -2277,16 +2423,18 @@ static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *i
advance = 1;
break;
case '{':
b_addchr(dest, SPECIAL_VAR_SYMBOL);
ctx->child->sp++;
b_getch(input);
/* XXX maybe someone will try to escape the '}' */
while(ch=b_getch(input),ch!=EOF && ch!='}') {
b_addchr(&alt,ch);
b_addchr(dest,ch);
}
if (ch != '}') {
syntax();
return 1;
}
lookup_param(dest, ctx, &alt);
b_addchr(dest, SPECIAL_VAR_SYMBOL);
break;
case '(':
b_getch(input);
@ -2348,7 +2496,9 @@ int parse_stream(o_string *dest, struct p_context *ctx,
b_addqchr(dest, ch, dest->quote);
} else {
if (m==2) { /* unquoted IFS */
done_word(dest, ctx);
if (done_word(dest, ctx)) {
return 1;
}
/* If we aren't performing a substitution, treat a newline as a
* command separator. */
if (end_trigger != '\0' && ch=='\n')
@ -2509,30 +2659,46 @@ void update_ifs_map(void)
/* most recursion does not come through here, the exeception is
* from builtin_source() */
int parse_stream_outer(struct in_str *inp)
int parse_stream_outer(struct in_str *inp, int flag)
{
struct p_context ctx;
o_string temp=NULL_O_STRING;
int rcode;
do {
ctx.type = flag;
initialize_context(&ctx);
update_ifs_map();
if (!(flag & FLAG_PARSE_SEMICOLON) || (flag & FLAG_REPARSING)) mapset(";$&|", 0);
inp->promptmode=1;
rcode = parse_stream(&temp, &ctx, inp, '\n');
done_word(&temp, &ctx);
done_pipe(&ctx,PIPE_SEQ);
run_list(ctx.list_head);
if (rcode != 1 && ctx.old_flag != 0) {
syntax();
}
if (rcode != 1 && ctx.old_flag == 0) {
done_word(&temp, &ctx);
done_pipe(&ctx,PIPE_SEQ);
run_list(ctx.list_head);
} else {
if (ctx.old_flag != 0) {
free(ctx.stack);
b_reset(&temp);
}
temp.nonnull = 0;
temp.quote = 0;
inp->p = NULL;
free_pipe_list(ctx.list_head,0);
}
b_free(&temp);
} while (rcode != -1); /* loop on syntax errors, return on EOF */
} while (rcode != -1 && !(flag & FLAG_EXIT_FROM_LOOP)); /* loop on syntax errors, return on EOF */
return 0;
}
static int parse_string_outer(const char *s)
static int parse_string_outer(const char *s, int flag)
{
struct in_str input;
setup_string_in_str(&input, s);
return parse_stream_outer(&input);
return parse_stream_outer(&input, flag);
}
static int parse_file_outer(FILE *f)
@ -2540,7 +2706,7 @@ static int parse_file_outer(FILE *f)
int rcode;
struct in_str input;
setup_file_in_str(&input, f);
rcode = parse_stream_outer(&input);
rcode = parse_stream_outer(&input, FLAG_PARSE_SEMICOLON);
return rcode;
}
@ -2630,7 +2796,7 @@ int hush_main(int argc, char **argv)
{
global_argv = argv+optind;
global_argc = argc-optind;
opt = parse_string_outer(optarg);
opt = parse_string_outer(optarg, FLAG_PARSE_SEMICOLON);
goto final_return;
}
break;
@ -2703,3 +2869,106 @@ int hush_main(int argc, char **argv)
final_return:
return(opt?opt:last_return_code);
}
static char *insert_var_value(char *inp)
{
int res_str_len = 0;
int len;
int done = 0;
char *p, *p1, *res_str = NULL;
while ((p = strchr(inp, SPECIAL_VAR_SYMBOL))) {
if (p != inp) {
len = p - inp;
res_str = xrealloc(res_str, (res_str_len + len));
strncpy((res_str + res_str_len), inp, len);
res_str_len += len;
}
inp = ++p;
p = strchr(inp, SPECIAL_VAR_SYMBOL);
*p = '\0';
if ((p1 = lookup_param(inp))) {
len = res_str_len + strlen(p1);
res_str = xrealloc(res_str, (1 + len));
strcpy((res_str + res_str_len), p1);
res_str_len = len;
}
*p = SPECIAL_VAR_SYMBOL;
inp = ++p;
done = 1;
}
if (done) {
res_str = xrealloc(res_str, (1 + res_str_len + strlen(inp)));
strcpy((res_str + res_str_len), inp);
while ((p = strchr(res_str, '\n'))) {
*p = ' ';
}
}
return (res_str == NULL) ? inp : res_str;
}
static char **make_list_in(char **inp, char *name)
{
int len, i;
int name_len = strlen(name);
int n = 0;
char **list;
char *p1, *p2, *p3;
/* create list of variable values */
list = xmalloc(sizeof(*list));
for (i = 0; inp[i]; i++) {
p3 = insert_var_value(inp[i]);
p1 = p3;
while (*p1) {
if ((*p1 == ' ')) {
p1++;
continue;
}
if ((p2 = strchr(p1, ' '))) {
len = p2 - p1;
} else {
len = strlen(p1);
p2 = p1 + len;
}
/* we use n + 2 in realloc for list,because we add
* new element and then we will add NULL element */
list = xrealloc(list, sizeof(*list) * (n + 2));
list[n] = xmalloc(2 + name_len + len);
strcpy(list[n], name);
strcat(list[n], "=");
strncat(list[n], p1, len);
list[n++][name_len + len + 1] = '\0';
p1 = p2;
}
if (p3 != inp[i]) free(p3);
}
list[n] = NULL;
return list;
}
/* Make new string for parser */
static char * make_string(char ** inp)
{
char *p;
char *str = NULL;
int n;
int len = 2;
for (n = 0; inp[n]; n++) {
p = insert_var_value(inp[n]);
str = xrealloc(str, (len + strlen(p)));
if (n) {
strcat(str, " ");
} else {
*str = '\0';
}
strcat(str, p);
len = strlen(str) + 3;
if (p != inp[n]) free(p);
}
len = strlen(str);
*(str + len) = '\n';
*(str + len + 1) = '\0';
return str;
}