hush: fix a few thinkos in function support; make it work on NOMMU;

functions in child shells now even have $n passed to them! :)
 (in main shell it still doesn't work)
This commit is contained in:
Denis Vlasenko 2009-04-10 21:22:02 +00:00
parent b7d8c0dbbd
commit c0ea329297

View File

@ -452,11 +452,16 @@ enum {
BC_CONTINUE = 2, BC_CONTINUE = 2,
}; };
#if ENABLE_HUSH_FUNCTIONS
struct function { struct function {
struct function *next; struct function *next;
char *name; char *name;
struct pipe *body; struct pipe *body;
#if !BB_MMU
char *body_as_string;
#endif
}; };
#endif
/* "Globals" within this file */ /* "Globals" within this file */
@ -510,7 +515,9 @@ struct globals {
const char *cwd; const char *cwd;
struct variable *top_var; /* = &G.shell_ver (set in main()) */ struct variable *top_var; /* = &G.shell_ver (set in main()) */
struct variable shell_ver; struct variable shell_ver;
#if ENABLE_HUSH_FUNCTIONS
struct function *top_func; struct function *top_func;
#endif
/* Signal and trap handling */ /* Signal and trap handling */
// unsigned count_SIGCHLD; // unsigned count_SIGCHLD;
// unsigned handled_SIGCHLD; // unsigned handled_SIGCHLD;
@ -675,11 +682,12 @@ static void xxfree(void *ptr)
* HUSH_DEBUG >= 2 prints line number in this file where it was detected. * HUSH_DEBUG >= 2 prints line number in this file where it was detected.
*/ */
#if HUSH_DEBUG < 2 #if HUSH_DEBUG < 2
# define die_if_script(lineno, fmt...) die_if_script(fmt) # define die_if_script(lineno, fmt...) die_if_script(fmt)
# define syntax_error(lineno, msg) syntax_error(msg) # define syntax_error(lineno, msg) syntax_error(msg)
# define syntax_error_at(lineno, msg) syntax_error_at(msg) # define syntax_error_at(lineno, msg) syntax_error_at(msg)
# define syntax_error_unterm_ch(lineno, ch) syntax_error_unterm_ch(ch) # define syntax_error_unterm_ch(lineno, ch) syntax_error_unterm_ch(ch)
# define syntax_error_unterm_str(lineno, s) syntax_error_unterm_str(s) # define syntax_error_unterm_str(lineno, s) syntax_error_unterm_str(s)
# define syntax_error_unexpected_ch(lineno, ch) syntax_error_unexpected_ch(ch)
#endif #endif
static void die_if_script(unsigned lineno, const char *fmt, ...) static void die_if_script(unsigned lineno, const char *fmt, ...)
@ -729,18 +737,29 @@ static void syntax_error_unterm_str(unsigned lineno, const char *s)
die_if_script(lineno, "syntax error: unterminated %s", s); die_if_script(lineno, "syntax error: unterminated %s", s);
} }
static void syntax_error_unexpected_ch(unsigned lineno, char ch)
{
char msg[2];
msg[0] = ch;
msg[1] = '\0';
die_if_script(lineno, "syntax error: unexpected %s", msg);
xfunc_die();
}
#if HUSH_DEBUG < 2 #if HUSH_DEBUG < 2
# undef die_if_script # undef die_if_script
# undef syntax_error # undef syntax_error
# undef syntax_error_at # undef syntax_error_at
# undef syntax_error_unterm_ch # undef syntax_error_unterm_ch
# undef syntax_error_unterm_str # undef syntax_error_unterm_str
# undef syntax_error_unexpected_ch
#else #else
# define die_if_script(fmt...) die_if_script(__LINE__, fmt) # define die_if_script(fmt...) die_if_script(__LINE__, fmt)
# define syntax_error(msg) syntax_error(__LINE__, msg) # define syntax_error(msg) syntax_error(__LINE__, msg)
# define syntax_error_at(msg) syntax_error_at(__LINE__, msg) # define syntax_error_at(msg) syntax_error_at(__LINE__, msg)
# define syntax_error_unterm_ch(ch) syntax_error_unterm_ch(__LINE__, ch) # define syntax_error_unterm_ch(ch) syntax_error_unterm_ch(__LINE__, ch)
# define syntax_error_unterm_str(s) syntax_error_unterm_str(__LINE__, s) # define syntax_error_unterm_str(s) syntax_error_unterm_str(__LINE__, s)
# define syntax_error_unexpected_ch(ch) syntax_error_unexpected_ch(__LINE__, ch)
#endif #endif
@ -2234,8 +2253,11 @@ static char **expand_assignments(char **argv, int count)
#if BB_MMU #if BB_MMU
void re_execute_shell(const char *s, int is_heredoc); /* never called */ /* never called */
void re_execute_shell(const char *s, char *argv0, char **argv);
#define clean_up_after_re_execute() ((void)0) #define clean_up_after_re_execute() ((void)0)
static void reset_traps_to_defaults(void) static void reset_traps_to_defaults(void)
{ {
unsigned sig; unsigned sig;
@ -2266,16 +2288,16 @@ static void reset_traps_to_defaults(void)
#else /* !BB_MMU */ #else /* !BB_MMU */
static void re_execute_shell(const char *s, int is_heredoc) NORETURN; static void re_execute_shell(const char *s, char *g_argv0, char **g_argv) NORETURN;
static void re_execute_shell(const char *s, int is_heredoc) static void re_execute_shell(const char *s, char *g_argv0, char **g_argv)
{ {
char param_buf[sizeof("-$%x:%x:%x:%x") + sizeof(unsigned) * 4]; char param_buf[sizeof("-$%x:%x:%x:%x") + sizeof(unsigned) * 4];
char *heredoc_argv[4]; char *heredoc_argv[4];
struct variable *cur; struct variable *cur;
char **argv, **pp, **pp2; char **argv, **pp;
unsigned cnt; unsigned cnt;
if (is_heredoc) { if (!g_argv0) { /* heredoc */
argv = heredoc_argv; argv = heredoc_argv;
argv[0] = (char *) G.argv0_for_re_execing; argv[0] = (char *) G.argv0_for_re_execing;
argv[1] = (char *) "-<"; argv[1] = (char *) "-<";
@ -2292,13 +2314,16 @@ static void re_execute_shell(const char *s, int is_heredoc)
USE_HUSH_LOOPS(, G.depth_of_loop) USE_HUSH_LOOPS(, G.depth_of_loop)
); );
/* 1:hush 2:-$<pid>:<pid>:<exitcode>:<depth> <vars...> /* 1:hush 2:-$<pid>:<pid>:<exitcode>:<depth> <vars...>
* 3:-c 4:<cmd> <argN...> 5:NULL * 3:-c 4:<cmd> 5:<arg0> <argN...> 6:NULL
*/ */
cnt = 5 + G.global_argc; cnt = 6;
for (cur = G.top_var; cur; cur = cur->next) { for (cur = G.top_var; cur; cur = cur->next) {
if (!cur->flg_export || cur->flg_read_only) if (!cur->flg_export || cur->flg_read_only)
cnt += 2; cnt += 2;
} }
pp = g_argv;
while (*pp++)
cnt++;
G.argv_from_re_execing = argv = pp = xzalloc(sizeof(argv[0]) * cnt); G.argv_from_re_execing = argv = pp = xzalloc(sizeof(argv[0]) * cnt);
*pp++ = (char *) G.argv0_for_re_execing; *pp++ = (char *) G.argv0_for_re_execing;
*pp++ = param_buf; *pp++ = param_buf;
@ -2335,9 +2360,9 @@ static void re_execute_shell(const char *s, int is_heredoc)
*/ */
*pp++ = (char *) "-c"; *pp++ = (char *) "-c";
*pp++ = (char *) s; *pp++ = (char *) s;
pp2 = G.global_argv; *pp++ = g_argv0;
while (*pp2) while (*g_argv)
*pp++ = *pp2++; *pp++ = *g_argv++;
/* *pp = NULL; - is already there */ /* *pp = NULL; - is already there */
pp = environ; pp = environ;
@ -2426,7 +2451,7 @@ static void setup_heredoc(struct redir_struct *redir)
/* Delegate blocking writes to another process */ /* Delegate blocking writes to another process */
disable_restore_tty_pgrp_on_exit(); disable_restore_tty_pgrp_on_exit();
xmove_fd(pair.wr, STDOUT_FILENO); xmove_fd(pair.wr, STDOUT_FILENO);
re_execute_shell(heredoc, 1); re_execute_shell(heredoc, NULL, NULL);
#endif #endif
} }
/* parent */ /* parent */
@ -2592,6 +2617,8 @@ static void free_pipe_list(struct pipe *head, int indent)
} }
static int run_list(struct pipe *pi);
static const struct built_in_command* find_builtin(const char *name) static const struct built_in_command* find_builtin(const char *name)
{ {
const struct built_in_command *x; const struct built_in_command *x;
@ -2604,23 +2631,40 @@ static const struct built_in_command* find_builtin(const char *name)
return NULL; return NULL;
} }
# if ENABLE_HUSH_FUNCTIONS #if ENABLE_HUSH_FUNCTIONS
static const struct function *find_function(const char *name) static const struct function *find_function(const char *name)
{ {
const struct function *funcp = G.top_func; const struct function *funcp = G.top_func;
while (funcp) { while (funcp) {
if (strcmp(name, funcp->name) != 0) if (strcmp(name, funcp->name) == 0) {
continue; break;
return funcp; }
debug_printf_exec("found function '%s'\n", name); funcp = funcp->next;
} }
return NULL; debug_printf_exec("found function '%s'\n", name);
return funcp;
}
static void exec_function(const struct function *funcp, char **argv) NORETURN;
static void exec_function(const struct function *funcp, char **argv)
{
# if BB_MMU
int n = 1;
argv[0] = G.global_argv[0];
G.global_argv = argv;
while (*++argv)
n++;
G.global_argc = n;
n = run_list(funcp->body);
fflush(NULL);
_exit(n);
# else
re_execute_shell(funcp->body_as_string, G.global_argv[0], argv + 1);
#endif
} }
#endif #endif
static int run_list(struct pipe *pi);
#if BB_MMU #if BB_MMU
#define pseudo_exec_argv(nommu_save, argv, assignment_cnt, argv_expanded) \ #define pseudo_exec_argv(nommu_save, argv, assignment_cnt, argv_expanded) \
pseudo_exec_argv(argv, assignment_cnt, argv_expanded) pseudo_exec_argv(argv, assignment_cnt, argv_expanded)
@ -2688,18 +2732,15 @@ static void pseudo_exec_argv(nommu_save_t *nommu_save,
_exit(rcode); _exit(rcode);
} }
} }
# if ENABLE_HUSH_FUNCTIONS #endif
#if ENABLE_HUSH_FUNCTIONS
/* Check if the command matches any functions */ /* Check if the command matches any functions */
{ {
int rcode;
const struct function *funcp = find_function(argv[0]); const struct function *funcp = find_function(argv[0]);
if (funcp) { if (funcp) {
rcode = run_list(funcp->body); exec_function(funcp, argv);
fflush(NULL);
_exit(rcode);
} }
} }
# endif
#endif #endif
#if ENABLE_FEATURE_SH_STANDALONE #if ENABLE_FEATURE_SH_STANDALONE
@ -2723,7 +2764,9 @@ static void pseudo_exec_argv(nommu_save_t *nommu_save,
} }
#endif #endif
#if ENABLE_FEATURE_SH_STANDALONE || BB_MMU
skip: skip:
#endif
debug_printf_exec("execing '%s'\n", argv[0]); debug_printf_exec("execing '%s'\n", argv[0]);
sigprocmask(SIG_SETMASK, &G.inherited_set, NULL); sigprocmask(SIG_SETMASK, &G.inherited_set, NULL);
execvp(argv[0], argv); execvp(argv[0], argv);
@ -2761,7 +2804,9 @@ static void pseudo_exec(nommu_save_t *nommu_save,
* since this process is about to exit */ * since this process is about to exit */
_exit(rcode); _exit(rcode);
#else #else
re_execute_shell(command->group_as_string, 0); re_execute_shell(command->group_as_string,
G.global_argv[0],
G.global_argv + 1);
#endif #endif
} }
@ -3085,19 +3130,23 @@ static int run_pipe(struct pipe *pi)
while ((funcp = *funcpp) != NULL) { while ((funcp = *funcpp) != NULL) {
if (strcmp(funcp->name, command->argv[0]) == 0) { if (strcmp(funcp->name, command->argv[0]) == 0) {
debug_printf_exec("replacing function '%s'", funcp->name); debug_printf_exec("replacing function '%s'\n", funcp->name);
free(funcp->name); free(funcp->name);
free_pipe_list(funcp->body, /* indent: */ 0); free_pipe_list(funcp->body, /* indent: */ 0);
goto skip; goto skip;
} }
funcpp = &funcp->next; funcpp = &funcp->next;
} }
debug_printf_exec("remembering new function '%s'", funcp->name); debug_printf_exec("remembering new function '%s'\n", command->argv[0]);
funcp = *funcpp = xzalloc(sizeof(*funcp)); funcp = *funcpp = xzalloc(sizeof(*funcp));
/*funcp->next = NULL;*/ /*funcp->next = NULL;*/
skip: skip:
funcp->name = command->argv[0]; funcp->name = command->argv[0];
funcp->body = command->group; funcp->body = command->group;
#if !BB_MMU
funcp->body_as_string = command->group_as_string;
command->group_as_string = NULL;
#endif
command->group = NULL; command->group = NULL;
command->argv[0] = NULL; command->argv[0] = NULL;
free_strings(command->argv); free_strings(command->argv);
@ -3160,6 +3209,7 @@ static int run_pipe(struct pipe *pi)
x = find_builtin(argv_expanded[0]); x = find_builtin(argv_expanded[0]);
#if ENABLE_HUSH_FUNCTIONS #if ENABLE_HUSH_FUNCTIONS
funcp = NULL;
if (!x) if (!x)
funcp = find_function(argv_expanded[0]); funcp = find_function(argv_expanded[0]);
#endif #endif
@ -3171,7 +3221,6 @@ static int run_pipe(struct pipe *pi)
goto clean_up_and_ret1; goto clean_up_and_ret1;
} }
} }
debug_printf("builtin inline %s\n", argv_expanded[0]);
/* XXX setup_redirects acts on file descriptors, not FILEs. /* XXX setup_redirects acts on file descriptors, not FILEs.
* This is perfect for work that comes after exec(). * This is perfect for work that comes after exec().
* Is it really safe for inline use? Experimentally, * Is it really safe for inline use? Experimentally,
@ -3681,7 +3730,7 @@ static int run_list(struct pipe *pi)
/* We only ran a builtin: rcode is already known /* We only ran a builtin: rcode is already known
* and we don't need to wait for anything. */ * and we don't need to wait for anything. */
G.last_exitcode = rcode; G.last_exitcode = rcode;
debug_printf_exec(": builtin exitcode %d\n", rcode); debug_printf_exec(": builtin/func exitcode %d\n", rcode);
check_and_run_traps(0); check_and_run_traps(0);
#if ENABLE_HUSH_LOOPS #if ENABLE_HUSH_LOOPS
/* Was it "break" or "continue"? */ /* Was it "break" or "continue"? */
@ -4497,7 +4546,9 @@ static FILE *generate_stream_from_string(const char *s)
* huge=`cat BIG` # was blocking here forever * huge=`cat BIG` # was blocking here forever
* echo OK * echo OK
*/ */
re_execute_shell(s, 0); re_execute_shell(s,
G.global_argv[0],
G.global_argv + 1);
#endif #endif
} }
@ -4561,34 +4612,34 @@ static int parse_group(o_string *dest, struct parse_context *ctx,
debug_printf_parse("parse_group entered\n"); debug_printf_parse("parse_group entered\n");
#if ENABLE_HUSH_FUNCTIONS #if ENABLE_HUSH_FUNCTIONS
if (ch == '(') { if (ch == '(' && !dest->o_quoted) {
if (!dest->o_quoted) { if (dest->length)
if (dest->length) done_word(dest, ctx);
done_word(dest, ctx); if (!command->argv)
if (!command->argv) goto skip; /* (... */
goto skip; /* (... */ if (command->argv[1]) { /* word word ... (... */
if (command->argv[1]) { /* word word ... (... */ syntax_error_unexpected_ch('(');
syntax_error("unexpected character ("); return 1;
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;
} }
/* it is "word(..." or "word (..." */
do
ch = i_getch(input);
while (ch == ' ' || ch == '\t');
if (ch != ')') {
syntax_error_unexpected_ch(ch);
return 1;
}
nommu_addchr(&ctx->as_string, ch);
do
ch = i_getch(input);
while (ch == ' ' || ch == '\t' || ch == '\n');
if (ch != '{') {
syntax_error_unexpected_ch(ch);
return 1;
}
nommu_addchr(&ctx->as_string, ch);
command->grp_type = GRP_FUNCTION;
goto skip;
} }
#endif #endif
if (command->argv /* word [word]{... */ if (command->argv /* word [word]{... */
@ -5452,7 +5503,7 @@ static struct pipe *parse_stream(char **pstring,
/* proper use of this character is caught by end_trigger: /* proper use of this character is caught by end_trigger:
* if we see {, we call parse_group(..., end_trigger='}') * if we see {, we call parse_group(..., end_trigger='}')
* and it will match } earlier (not here). */ * and it will match } earlier (not here). */
syntax_error("unexpected } or )"); syntax_error_unexpected_ch(ch);
goto parse_error; goto parse_error;
default: default:
if (HUSH_DEBUG) if (HUSH_DEBUG)