hush: next small step towards functions
This commit is contained in:
parent
ded6ad34ed
commit
371de4acf7
72
shell/hush.c
72
shell/hush.c
@ -96,6 +96,12 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* Keep unconditionally on for now */
|
||||||
|
#define HUSH_DEBUG 1
|
||||||
|
/* In progress... */
|
||||||
|
#define ENABLE_HUSH_FUNCTIONS 0
|
||||||
|
|
||||||
|
|
||||||
/* If you comment out one of these below, it will be #defined later
|
/* If you comment out one of these below, it will be #defined later
|
||||||
* to perform debug printfs to stderr: */
|
* to perform debug printfs to stderr: */
|
||||||
#define debug_printf(...) do {} while (0)
|
#define debug_printf(...) do {} while (0)
|
||||||
@ -218,8 +224,6 @@ void xxfree(void *ptr)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/* Keep unconditionally on for now */
|
|
||||||
#define HUSH_DEBUG 1
|
|
||||||
/* Do we support ANY keywords? */
|
/* Do we support ANY keywords? */
|
||||||
#if ENABLE_HUSH_IF || ENABLE_HUSH_LOOPS || ENABLE_HUSH_CASE
|
#if ENABLE_HUSH_IF || ENABLE_HUSH_LOOPS || ENABLE_HUSH_CASE
|
||||||
#define HAS_KEYWORDS 1
|
#define HAS_KEYWORDS 1
|
||||||
@ -307,7 +311,7 @@ struct command {
|
|||||||
pid_t pid; /* 0 if exited */
|
pid_t pid; /* 0 if exited */
|
||||||
int assignment_cnt; /* how many argv[i] are assignments? */
|
int assignment_cnt; /* how many argv[i] are assignments? */
|
||||||
smallint is_stopped; /* is the command currently running? */
|
smallint is_stopped; /* is the command currently running? */
|
||||||
smallint subshell; /* flag, non-zero if group must be forked */
|
smallint grp_type;
|
||||||
struct pipe *group; /* if non-NULL, this "prog" is {} group,
|
struct pipe *group; /* if non-NULL, this "prog" is {} group,
|
||||||
* subshell, or a compound statement */
|
* subshell, or a compound statement */
|
||||||
char **argv; /* command name and arguments */
|
char **argv; /* command name and arguments */
|
||||||
@ -319,6 +323,11 @@ struct command {
|
|||||||
* Example: argv[0]=='.^C*^C.' here: echo .$*.
|
* Example: argv[0]=='.^C*^C.' here: echo .$*.
|
||||||
* References of the form ^C`cmd arg^C are `cmd arg` substitutions.
|
* References of the form ^C`cmd arg^C are `cmd arg` substitutions.
|
||||||
*/
|
*/
|
||||||
|
#define GRP_NORMAL 0
|
||||||
|
#define GRP_SUBSHELL 1
|
||||||
|
#if ENABLE_HUSH_FUNCTIONS
|
||||||
|
#define GRP_FUNCTION 2
|
||||||
|
#endif
|
||||||
|
|
||||||
struct pipe {
|
struct pipe {
|
||||||
struct pipe *next;
|
struct pipe *next;
|
||||||
@ -1016,12 +1025,14 @@ static void o_addstr_duplicate_backslash(o_string *o, const char *str, int len)
|
|||||||
static void o_addqchr(o_string *o, int ch)
|
static void o_addqchr(o_string *o, int ch)
|
||||||
{
|
{
|
||||||
int sz = 1;
|
int sz = 1;
|
||||||
if (strchr("*?[\\", ch)) {
|
char *found = strchr("*?[\\", ch);
|
||||||
|
if (found)
|
||||||
sz++;
|
sz++;
|
||||||
|
o_grow_by(o, sz);
|
||||||
|
if (found) {
|
||||||
o->data[o->length] = '\\';
|
o->data[o->length] = '\\';
|
||||||
o->length++;
|
o->length++;
|
||||||
}
|
}
|
||||||
o_grow_by(o, sz);
|
|
||||||
o->data[o->length] = ch;
|
o->data[o->length] = ch;
|
||||||
o->length++;
|
o->length++;
|
||||||
o->data[o->length] = '\0';
|
o->data[o->length] = '\0';
|
||||||
@ -1834,7 +1845,15 @@ static int run_pipe(struct pipe *pi)
|
|||||||
*/
|
*/
|
||||||
command = &(pi->cmds[0]);
|
command = &(pi->cmds[0]);
|
||||||
|
|
||||||
if (single_and_fg && command->group && command->subshell == 0) {
|
#if ENABLE_HUSH_FUNCTIONS
|
||||||
|
if (single_and_fg && command->group && command->grp_type == GRP_FUNCTION) {
|
||||||
|
/* We "execute" function definition */
|
||||||
|
bb_error_msg("here we ought to remember function definition, and go on");
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (single_and_fg && command->group && command->grp_type == GRP_NORMAL) {
|
||||||
debug_printf("non-subshell grouping\n");
|
debug_printf("non-subshell grouping\n");
|
||||||
setup_redirects(command, squirrel);
|
setup_redirects(command, squirrel);
|
||||||
debug_printf_exec(": run_list\n");
|
debug_printf_exec(": run_list\n");
|
||||||
@ -2023,7 +2042,7 @@ static int run_pipe(struct pipe *pi)
|
|||||||
#ifndef debug_print_tree
|
#ifndef debug_print_tree
|
||||||
static void debug_print_tree(struct pipe *pi, int lvl)
|
static void debug_print_tree(struct pipe *pi, int lvl)
|
||||||
{
|
{
|
||||||
static const char *PIPE[] = {
|
static const char *const PIPE[] = {
|
||||||
[PIPE_SEQ] = "SEQ",
|
[PIPE_SEQ] = "SEQ",
|
||||||
[PIPE_AND] = "AND",
|
[PIPE_AND] = "AND",
|
||||||
[PIPE_OR ] = "OR" ,
|
[PIPE_OR ] = "OR" ,
|
||||||
@ -2057,6 +2076,13 @@ static void debug_print_tree(struct pipe *pi, int lvl)
|
|||||||
[RES_XXXX ] = "XXXX" ,
|
[RES_XXXX ] = "XXXX" ,
|
||||||
[RES_SNTX ] = "SNTX" ,
|
[RES_SNTX ] = "SNTX" ,
|
||||||
};
|
};
|
||||||
|
static const char *const GRPTYPE[] = {
|
||||||
|
"()",
|
||||||
|
"{}",
|
||||||
|
#if ENABLE_HUSH_FUNCTIONS
|
||||||
|
"func()",
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
int pin, prn;
|
int pin, prn;
|
||||||
|
|
||||||
@ -2072,7 +2098,7 @@ static void debug_print_tree(struct pipe *pi, int lvl)
|
|||||||
fprintf(stderr, "%*s prog %d assignment_cnt:%d", lvl*2, "", prn, command->assignment_cnt);
|
fprintf(stderr, "%*s prog %d assignment_cnt:%d", lvl*2, "", prn, command->assignment_cnt);
|
||||||
if (command->group) {
|
if (command->group) {
|
||||||
fprintf(stderr, " group %s: (argv=%p)\n",
|
fprintf(stderr, " group %s: (argv=%p)\n",
|
||||||
(command->subshell ? "()" : "{}"),
|
GRPTYPE[command->grp_type],
|
||||||
argv);
|
argv);
|
||||||
debug_print_tree(command->group, lvl+1);
|
debug_print_tree(command->group, lvl+1);
|
||||||
prn++;
|
prn++;
|
||||||
@ -2443,7 +2469,7 @@ static int free_pipe(struct pipe *pi, int indent)
|
|||||||
free_strings(command->argv);
|
free_strings(command->argv);
|
||||||
command->argv = NULL;
|
command->argv = NULL;
|
||||||
} else if (command->group) {
|
} else if (command->group) {
|
||||||
debug_printf_clean("%s begin group (subshell:%d)\n", indenter(indent), command->subshell);
|
debug_printf_clean("%s begin group (grp_type:%d)\n", indenter(indent), command->grp_type);
|
||||||
ret_code = free_pipe_list(command->group, indent+3);
|
ret_code = free_pipe_list(command->group, indent+3);
|
||||||
debug_printf_clean("%s end group\n", indenter(indent));
|
debug_printf_clean("%s end group\n", indenter(indent));
|
||||||
} else {
|
} else {
|
||||||
@ -2572,7 +2598,6 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask)
|
|||||||
#if ENABLE_HUSH_TICK
|
#if ENABLE_HUSH_TICK
|
||||||
o_string subst_result = NULL_O_STRING;
|
o_string subst_result = NULL_O_STRING;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
o_addstr(output, arg, p - arg);
|
o_addstr(output, arg, p - arg);
|
||||||
debug_print_list("expand_vars_to_list[1]", output, n);
|
debug_print_list("expand_vars_to_list[1]", output, n);
|
||||||
arg = ++p;
|
arg = ++p;
|
||||||
@ -3100,7 +3125,7 @@ static int reserved_word(o_string *word, struct parse_context *ctx)
|
|||||||
done_pipe(ctx, PIPE_SEQ);
|
done_pipe(ctx, PIPE_SEQ);
|
||||||
old = ctx->stack;
|
old = ctx->stack;
|
||||||
old->command->group = ctx->list_head;
|
old->command->group = ctx->list_head;
|
||||||
old->command->subshell = 0;
|
old->command->grp_type = GRP_NORMAL;
|
||||||
*ctx = *old; /* physical copy */
|
*ctx = *old; /* physical copy */
|
||||||
free(old);
|
free(old);
|
||||||
}
|
}
|
||||||
@ -3461,15 +3486,24 @@ static int process_command_subs(o_string *dest,
|
|||||||
static int parse_group(o_string *dest, struct parse_context *ctx,
|
static int parse_group(o_string *dest, struct parse_context *ctx,
|
||||||
struct in_str *input, int ch)
|
struct in_str *input, int ch)
|
||||||
{
|
{
|
||||||
/* NB: parse_group may create and use its own o_string,
|
/* dest contains characters seen prior to ( or {.
|
||||||
* without any code changes. It just so happens that code is smaller
|
* Typically it's empty, but for functions defs,
|
||||||
* if we (ab)use caller's one. */
|
* it contains function name (without '()'). */
|
||||||
int rcode;
|
int rcode;
|
||||||
const char *endch = NULL;
|
const char *endch = NULL;
|
||||||
struct parse_context sub;
|
struct parse_context sub;
|
||||||
struct command *command = ctx->command;
|
struct command *command = ctx->command;
|
||||||
|
|
||||||
debug_printf_parse("parse_group entered\n");
|
debug_printf_parse("parse_group entered\n");
|
||||||
|
#if ENABLE_HUSH_FUNCTIONS
|
||||||
|
if (ch == 'F') { /* function definition? */
|
||||||
|
bb_error_msg("aha '%s' is a function, parsing it...", dest->data);
|
||||||
|
//command->fname = dest->data;
|
||||||
|
command->grp_type = GRP_FUNCTION;
|
||||||
|
//TODO: review every o_reset() location... do they handle all o_string fields correctly?
|
||||||
|
memset(dest, 0, sizeof(*dest));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if (command->argv /* word [word](... */
|
if (command->argv /* word [word](... */
|
||||||
|| dest->length /* word(... */
|
|| dest->length /* word(... */
|
||||||
|| dest->nonnull /* ""(... */
|
|| dest->nonnull /* ""(... */
|
||||||
@ -3482,11 +3516,8 @@ static int parse_group(o_string *dest, struct parse_context *ctx,
|
|||||||
endch = "}";
|
endch = "}";
|
||||||
if (ch == '(') {
|
if (ch == '(') {
|
||||||
endch = ")";
|
endch = ")";
|
||||||
command->subshell = 1;
|
command->grp_type = GRP_SUBSHELL;
|
||||||
}
|
}
|
||||||
//TODO if (ch == 'F') { /* function definition */
|
|
||||||
// command->subshell = 2;
|
|
||||||
// }
|
|
||||||
rcode = parse_stream(dest, &sub, input, endch);
|
rcode = parse_stream(dest, &sub, input, endch);
|
||||||
if (rcode == 0) {
|
if (rcode == 0) {
|
||||||
done_word(dest, &sub); /* finish off the final word in the subcontext */
|
done_word(dest, &sub); /* finish off the final word in the subcontext */
|
||||||
@ -3988,16 +4019,18 @@ static int parse_stream(o_string *dest, struct parse_context *ctx,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#if 0 /* TODO: implement functions */
|
#if ENABLE_HUSH_FUNCTIONS
|
||||||
if (dest->length != 0 /* not just () but word() */
|
if (dest->length != 0 /* not just () but word() */
|
||||||
&& dest->nonnull == 0 /* not a"b"c() */
|
&& dest->nonnull == 0 /* not a"b"c() */
|
||||||
&& ctx->command->argv == NULL /* it's the first word */
|
&& ctx->command->argv == NULL /* it's the first word */
|
||||||
|
//TODO: "func ( ) {...}" - note spaces - is valid format too in bash
|
||||||
&& i_peek(input) == ')'
|
&& i_peek(input) == ')'
|
||||||
&& !match_reserved_word(dest)
|
&& !match_reserved_word(dest)
|
||||||
) {
|
) {
|
||||||
bb_error_msg("seems like a function definition");
|
bb_error_msg("seems like a function definition");
|
||||||
i_getch(input);
|
i_getch(input);
|
||||||
do {
|
do {
|
||||||
|
//TODO: do it properly.
|
||||||
ch = i_getch(input);
|
ch = i_getch(input);
|
||||||
} while (ch == ' ' || ch == '\n');
|
} while (ch == ' ' || ch == '\n');
|
||||||
if (ch != '{') {
|
if (ch != '{') {
|
||||||
@ -4005,6 +4038,7 @@ static int parse_stream(o_string *dest, struct parse_context *ctx,
|
|||||||
debug_printf_parse("parse_stream return 1\n");
|
debug_printf_parse("parse_stream return 1\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
ch = 'F'; /* magic value */
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
case '{':
|
case '{':
|
||||||
|
Loading…
x
Reference in New Issue
Block a user