hush: first stab at function support. argv passing is not coded yet.
Only very rudimentary testing was done. With function support off, code growth is zero, with it on: function old new delta run_list 2158 2339 +181 parse_stream 1929 2044 +115 find_builtin 24 67 +43 find_function - 36 +36 file_get 244 264 +20 pseudo_exec_argv 145 160 +15 free_strings - 7 +7 free_pipe 183 181 -2 done_word 735 728 -7 expand_variables 2227 2204 -23 ------------------------------------------------------------------------------ (add/remove: 2/0 grow/shrink: 5/3 up/down: 417/-32) Total: 385 bytes
This commit is contained in:
parent
835fcfd33d
commit
b7d8c0dbbd
233
shell/hush.c
233
shell/hush.c
@ -86,7 +86,7 @@
|
||||
*/
|
||||
#define HUSH_DEBUG 1
|
||||
/* In progress... */
|
||||
#define ENABLE_HUSH_FUNCTIONS 0
|
||||
#define ENABLE_HUSH_FUNCTIONS 1
|
||||
|
||||
|
||||
#if BUILD_AS_NOMMU
|
||||
@ -374,12 +374,12 @@ struct command {
|
||||
#define GRP_NORMAL 0
|
||||
#define GRP_SUBSHELL 1
|
||||
#if ENABLE_HUSH_FUNCTIONS
|
||||
#define GRP_FUNCTION 2
|
||||
# define GRP_FUNCTION 2
|
||||
#endif
|
||||
|
||||
struct pipe {
|
||||
struct pipe *next;
|
||||
int num_cmds; /* total number of commands in job */
|
||||
int num_cmds; /* total number of commands in pipe */
|
||||
int alive_cmds; /* number of commands running (not exited) */
|
||||
int stopped_cmds; /* number of commands alive, but stopped */
|
||||
#if ENABLE_HUSH_JOB
|
||||
@ -452,6 +452,12 @@ enum {
|
||||
BC_CONTINUE = 2,
|
||||
};
|
||||
|
||||
struct function {
|
||||
struct function *next;
|
||||
char *name;
|
||||
struct pipe *body;
|
||||
};
|
||||
|
||||
|
||||
/* "Globals" within this file */
|
||||
/* Sorted roughly by size (smaller offsets == smaller code) */
|
||||
@ -504,6 +510,7 @@ struct globals {
|
||||
const char *cwd;
|
||||
struct variable *top_var; /* = &G.shell_ver (set in main()) */
|
||||
struct variable shell_ver;
|
||||
struct function *top_func;
|
||||
/* Signal and trap handling */
|
||||
// unsigned count_SIGCHLD;
|
||||
// unsigned handled_SIGCHLD;
|
||||
@ -2585,6 +2592,35 @@ static void free_pipe_list(struct pipe *head, int indent)
|
||||
}
|
||||
|
||||
|
||||
static const struct built_in_command* find_builtin(const char *name)
|
||||
{
|
||||
const struct built_in_command *x;
|
||||
for (x = bltins; x != &bltins[ARRAY_SIZE(bltins)]; x++) {
|
||||
if (strcmp(name, x->cmd) != 0)
|
||||
continue;
|
||||
debug_printf_exec("found builtin '%s'\n", name);
|
||||
return x;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
# if ENABLE_HUSH_FUNCTIONS
|
||||
static const struct function *find_function(const char *name)
|
||||
{
|
||||
const struct function *funcp = G.top_func;
|
||||
while (funcp) {
|
||||
if (strcmp(name, funcp->name) != 0)
|
||||
continue;
|
||||
return funcp;
|
||||
debug_printf_exec("found function '%s'\n", name);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static int run_list(struct pipe *pi);
|
||||
|
||||
#if BB_MMU
|
||||
#define pseudo_exec_argv(nommu_save, argv, assignment_cnt, argv_expanded) \
|
||||
pseudo_exec_argv(argv, assignment_cnt, argv_expanded)
|
||||
@ -2627,6 +2663,11 @@ static void pseudo_exec_argv(nommu_save_t *nommu_save,
|
||||
#endif
|
||||
}
|
||||
|
||||
#if ENABLE_FEATURE_SH_STANDALONE || BB_MMU
|
||||
if (strchr(argv[0], '/') != NULL)
|
||||
goto skip;
|
||||
#endif
|
||||
|
||||
/* On NOMMU, we must never block!
|
||||
* Example: { sleep 99999 | read line } & echo Ok
|
||||
* read builtin will block on read syscall, leaving parent blocked
|
||||
@ -2640,30 +2681,38 @@ static void pseudo_exec_argv(nommu_save_t *nommu_save,
|
||||
*/
|
||||
{
|
||||
int rcode;
|
||||
const struct built_in_command *x;
|
||||
for (x = bltins; x != &bltins[ARRAY_SIZE(bltins)]; x++) {
|
||||
if (strcmp(argv[0], x->cmd) == 0) {
|
||||
debug_printf_exec("running builtin '%s'\n",
|
||||
argv[0]);
|
||||
rcode = x->function(argv);
|
||||
fflush(NULL);
|
||||
_exit(rcode);
|
||||
}
|
||||
const struct built_in_command *x = find_builtin(argv[0]);
|
||||
if (x) {
|
||||
rcode = x->function(argv);
|
||||
fflush(NULL);
|
||||
_exit(rcode);
|
||||
}
|
||||
}
|
||||
# if ENABLE_HUSH_FUNCTIONS
|
||||
/* Check if the command matches any functions */
|
||||
{
|
||||
int rcode;
|
||||
const struct function *funcp = find_function(argv[0]);
|
||||
if (funcp) {
|
||||
rcode = run_list(funcp->body);
|
||||
fflush(NULL);
|
||||
_exit(rcode);
|
||||
}
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if ENABLE_FEATURE_SH_STANDALONE
|
||||
/* Check if the command matches any busybox applets */
|
||||
if (strchr(argv[0], '/') == NULL) {
|
||||
{
|
||||
int a = find_applet_by_name(argv[0]);
|
||||
if (a >= 0) {
|
||||
#if BB_MMU /* see above why on NOMMU it is not allowed */
|
||||
# if BB_MMU /* see above why on NOMMU it is not allowed */
|
||||
if (APPLET_IS_NOEXEC(a)) {
|
||||
debug_printf_exec("running applet '%s'\n", argv[0]);
|
||||
run_applet_no_and_exit(a, argv);
|
||||
}
|
||||
#endif
|
||||
# endif
|
||||
/* Re-exec ourselves */
|
||||
debug_printf_exec("re-execing applet '%s'\n", argv[0]);
|
||||
sigprocmask(SIG_SETMASK, &G.inherited_set, NULL);
|
||||
@ -2674,6 +2723,7 @@ static void pseudo_exec_argv(nommu_save_t *nommu_save,
|
||||
}
|
||||
#endif
|
||||
|
||||
skip:
|
||||
debug_printf_exec("execing '%s'\n", argv[0]);
|
||||
sigprocmask(SIG_SETMASK, &G.inherited_set, NULL);
|
||||
execvp(argv[0], argv);
|
||||
@ -2681,8 +2731,6 @@ static void pseudo_exec_argv(nommu_save_t *nommu_save,
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static int run_list(struct pipe *pi);
|
||||
|
||||
/* Called after [v]fork() in run_pipe
|
||||
*/
|
||||
static void pseudo_exec(nommu_save_t *nommu_save,
|
||||
@ -3031,8 +3079,35 @@ static int run_pipe(struct pipe *pi)
|
||||
if (command->group) {
|
||||
#if ENABLE_HUSH_FUNCTIONS
|
||||
if (command->grp_type == GRP_FUNCTION) {
|
||||
/* func () { list } */
|
||||
bb_error_msg("here we ought to remember function definition, and go on");
|
||||
/* "executing" func () { list } */
|
||||
struct function *funcp;
|
||||
struct function **funcpp = &G.top_func;
|
||||
|
||||
while ((funcp = *funcpp) != NULL) {
|
||||
if (strcmp(funcp->name, command->argv[0]) == 0) {
|
||||
debug_printf_exec("replacing function '%s'", funcp->name);
|
||||
free(funcp->name);
|
||||
free_pipe_list(funcp->body, /* indent: */ 0);
|
||||
goto skip;
|
||||
}
|
||||
funcpp = &funcp->next;
|
||||
}
|
||||
debug_printf_exec("remembering new function '%s'", funcp->name);
|
||||
funcp = *funcpp = xzalloc(sizeof(*funcp));
|
||||
/*funcp->next = NULL;*/
|
||||
skip:
|
||||
funcp->name = command->argv[0];
|
||||
funcp->body = command->group;
|
||||
command->group = NULL;
|
||||
command->argv[0] = NULL;
|
||||
free_strings(command->argv);
|
||||
command->argv = NULL;
|
||||
/* note: if we are in a loop, future "executions"
|
||||
* of func def will see it as null command since
|
||||
* command->group == NULL and command->argv == NULL */
|
||||
//this isn't exactly right: while...do f1() {a;}; f1; f1 {b;}; done
|
||||
//second loop will execute b!
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
@ -3052,6 +3127,11 @@ static int run_pipe(struct pipe *pi)
|
||||
argv = command->argv ? command->argv : (char **) &null_ptr;
|
||||
{
|
||||
const struct built_in_command *x;
|
||||
#if ENABLE_HUSH_FUNCTIONS
|
||||
const struct function *funcp;
|
||||
#else
|
||||
enum { funcp = 0 };
|
||||
#endif
|
||||
char **new_env = NULL;
|
||||
char **old_env = NULL;
|
||||
|
||||
@ -3078,13 +3158,18 @@ static int run_pipe(struct pipe *pi)
|
||||
/* Expand the rest into (possibly) many strings each */
|
||||
argv_expanded = expand_strvec_to_strvec(argv + command->assignment_cnt);
|
||||
|
||||
for (x = bltins; x != &bltins[ARRAY_SIZE(bltins)]; x++) {
|
||||
if (strcmp(argv_expanded[0], x->cmd) != 0)
|
||||
continue;
|
||||
if (x->function == builtin_exec && argv_expanded[1] == NULL) {
|
||||
debug_printf("exec with redirects only\n");
|
||||
rcode = setup_redirects(command, NULL);
|
||||
goto clean_up_and_ret1;
|
||||
x = find_builtin(argv_expanded[0]);
|
||||
#if ENABLE_HUSH_FUNCTIONS
|
||||
if (!x)
|
||||
funcp = find_function(argv_expanded[0]);
|
||||
#endif
|
||||
if (x || funcp) {
|
||||
if (!funcp) {
|
||||
if (x->function == builtin_exec && argv_expanded[1] == NULL) {
|
||||
debug_printf("exec with redirects only\n");
|
||||
rcode = setup_redirects(command, NULL);
|
||||
goto clean_up_and_ret1;
|
||||
}
|
||||
}
|
||||
debug_printf("builtin inline %s\n", argv_expanded[0]);
|
||||
/* XXX setup_redirects acts on file descriptors, not FILEs.
|
||||
@ -3095,9 +3180,18 @@ static int run_pipe(struct pipe *pi)
|
||||
if (rcode == 0) {
|
||||
new_env = expand_assignments(argv, command->assignment_cnt);
|
||||
old_env = putenv_all_and_save_old(new_env);
|
||||
debug_printf_exec(": builtin '%s' '%s'...\n",
|
||||
if (!funcp) {
|
||||
debug_printf_exec(": builtin '%s' '%s'...\n",
|
||||
x->cmd, argv_expanded[1]);
|
||||
rcode = x->function(argv_expanded) & 0xff;
|
||||
rcode = x->function(argv_expanded) & 0xff;
|
||||
}
|
||||
#if ENABLE_HUSH_FUNCTIONS
|
||||
else {
|
||||
debug_printf_exec(": function '%s' '%s'...\n",
|
||||
funcp->name, argv_expanded[1]);
|
||||
rcode = run_list(funcp->body) & 0xff;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#if ENABLE_FEATURE_SH_STANDALONE
|
||||
clean_up_and_ret:
|
||||
@ -3114,6 +3208,7 @@ static int run_pipe(struct pipe *pi)
|
||||
debug_printf_exec("run_pipe return %d\n", rcode);
|
||||
return rcode;
|
||||
}
|
||||
|
||||
#if ENABLE_FEATURE_SH_STANDALONE
|
||||
i = find_applet_by_name(argv_expanded[0]);
|
||||
if (i >= 0 && APPLET_IS_NOFORK(i)) {
|
||||
@ -4081,6 +4176,10 @@ static int done_word(o_string *word, struct parse_context *ctx)
|
||||
}
|
||||
}
|
||||
command->argv = add_string_to_strings(command->argv, xstrdup(word->data));
|
||||
//SEGV, but good idea.
|
||||
// command->argv = add_string_to_strings(command->argv, word->data);
|
||||
// word->data = NULL;
|
||||
// word->length = 0;
|
||||
debug_print_strings("word appended to argv", command->argv);
|
||||
}
|
||||
|
||||
@ -4089,7 +4188,7 @@ static int done_word(o_string *word, struct parse_context *ctx)
|
||||
if (word->o_quoted
|
||||
|| !is_well_formed_var_name(command->argv[0], '\0')
|
||||
) {
|
||||
/* bash says "not a valid identifier" */
|
||||
/* bash says just "not a valid identifier" */
|
||||
syntax_error("not a valid identifier in for");
|
||||
return 1;
|
||||
}
|
||||
@ -4462,27 +4561,55 @@ static int parse_group(o_string *dest, struct parse_context *ctx,
|
||||
|
||||
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;
|
||||
memset(dest, 0, sizeof(*dest));
|
||||
if (ch == '(') {
|
||||
if (!dest->o_quoted) {
|
||||
if (dest->length)
|
||||
done_word(dest, ctx);
|
||||
if (!command->argv)
|
||||
goto skip; /* (... */
|
||||
if (command->argv[1]) { /* word word ... (... */
|
||||
syntax_error("unexpected character (");
|
||||
return 1;
|
||||
}
|
||||
/* it is "word(..." or "word (..." */
|
||||
do
|
||||
ch = i_getch(input);
|
||||
while (ch == ' ' || ch == '\t');
|
||||
if (ch != ')') {
|
||||
syntax_error("unexpected character X");
|
||||
return 1;
|
||||
}
|
||||
do
|
||||
ch = i_getch(input);
|
||||
while (ch == ' ' || ch == '\t' || ch == '\n');
|
||||
if (ch != '{') {
|
||||
syntax_error("unexpected character X");
|
||||
return 1;
|
||||
}
|
||||
command->grp_type = GRP_FUNCTION;
|
||||
goto skip;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (command->argv /* word [word](... */
|
||||
|| dest->length /* word(... */
|
||||
|| dest->o_quoted /* ""(... */
|
||||
if (command->argv /* word [word]{... */
|
||||
|| dest->length /* word{... */
|
||||
|| dest->o_quoted /* ""{... */
|
||||
) {
|
||||
syntax_error(NULL);
|
||||
debug_printf_parse("parse_group return 1: "
|
||||
"syntax error, groups and arglists don't mix\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if ENABLE_HUSH_FUNCTIONS
|
||||
skip:
|
||||
#endif
|
||||
endch = '}';
|
||||
if (ch == '(') {
|
||||
endch = ')';
|
||||
command->grp_type = GRP_SUBSHELL;
|
||||
}
|
||||
|
||||
{
|
||||
#if !BB_MMU
|
||||
char *as_string = NULL;
|
||||
@ -5310,28 +5437,6 @@ static struct pipe *parse_stream(char **pstring,
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
#if ENABLE_HUSH_FUNCTIONS
|
||||
if (dest.length != 0 /* not just () but word() */
|
||||
&& dest.o_quoted == 0 /* not a"b"c() */
|
||||
&& ctx.command->argv == NULL /* it's the first word */
|
||||
//TODO: "func ( ) {...}" - note spaces - is valid format too in bash
|
||||
&& i_peek(input) == ')'
|
||||
&& !match_reserved_word(&dest)
|
||||
) {
|
||||
bb_error_msg("seems like a function definition");
|
||||
i_getch(input);
|
||||
//if !BB_MMU o_addchr(&ctx.as_string...
|
||||
do {
|
||||
//TODO: do it properly.
|
||||
ch = i_getch(input);
|
||||
} while (ch == ' ' || ch == '\n');
|
||||
if (ch != '{') {
|
||||
syntax_error("was expecting {");
|
||||
goto parse_error;
|
||||
}
|
||||
ch = 'F'; /* magic value */
|
||||
}
|
||||
#endif
|
||||
case '{':
|
||||
if (parse_group(&dest, &ctx, input, ch) != 0) {
|
||||
@ -6427,11 +6532,11 @@ static int builtin_unset(char **argv)
|
||||
ret = EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
#if ENABLE_HUSH_FUNCTIONS
|
||||
else {
|
||||
unset_local_func(*argv);
|
||||
}
|
||||
#endif
|
||||
//#if ENABLE_HUSH_FUNCTIONS
|
||||
// else {
|
||||
// unset_local_func(*argv);
|
||||
// }
|
||||
//#endif
|
||||
argv++;
|
||||
}
|
||||
return ret;
|
||||
|
Loading…
Reference in New Issue
Block a user