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