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:
Denis Vlasenko 2009-04-10 19:05:43 +00:00
parent 835fcfd33d
commit b7d8c0dbbd

View File

@ -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;