msh_function.patch: picked it up in the wild. Fixed allocation bugs (it was allocating one too small vectors) but it still is very buggy, thus not applied.
No binary (build failed)
—
(log yok)
| @@ -0,0 +1,350 @@ | |
| + | This is a "function" patch for msh which is in use by some busybox |
| + | users. Unfortunately it is far too buggy to be applied, but maybe |
| + | it's a useful starting point for future work. |
| + | |
| + | Function-related code is delimited by comments of the form |
| + | //funccode:start |
| + | ... |
| + | //funccode:end |
| + | for ease of grepping |
| + | |
| + | An example of buggy behavior: |
| + | |
| + | #f() { |
| + | # echo foo |
| + | # echo test`echo bar >&2` |
| + | # echo END f |
| + | #} |
| + | |
| + | function g { |
| + | # echo 2 foo |
| + | # echo 2 test`echo 2 bar >&2` |
| + | # f |
| + | echo END g |
| + | # echo "1:'$1' 2:'$2'" |
| + | } |
| + | |
| + | # Even this first block fails - it does not even call functions! |
| + | # (replacing "echo END g" above with "echo END" makes it run ok) |
| + | echo DRY RUN |
| + | echo 2 foo |
| + | echo 2 test`echo 2 bar >&2` |
| + | echo END g |
| + | echo "1:'$1' 2:'$2'" |
| + | echo foo |
| + | echo test`echo bar >&2` |
| + | echo END f |
| + | echo END DRY RUN |
| + | |
| + | exit |
| + | |
| + | # This would fail too |
| + | g "$1-one" "two$2" |
| + | echo DONE |
| + | |
| + | |
| + | |
| + | diff -d -urpN busybox.7/shell/msh.c busybox.8/shell/msh.c |
| + | --- busybox.7/shell/msh.c 2008-06-09 09:34:45.000000000 +0200 |
| + | +++ busybox.8/shell/msh.c 2008-06-09 09:38:17.000000000 +0200 |
| + | @@ -89,6 +89,14 @@ static char *itoa(int n) |
| + | |
| + | //#define MSHDEBUG 4 |
| + | |
| + | +/* Used only in "function" support code */ |
| + | +#ifdef KSDBG //funccode:start |
| + | + #define KSDBG_PRINT_FUNCNAME fprintf(stderr, "in %s\n", __FUNCTION__) |
| + | +#else |
| + | + #define KSDBG_PRINT_FUNCNAME ((void)0) |
| + | +#endif |
| + | +//funccode:end |
| + | + |
| + | #ifdef MSHDEBUG |
| + | static int mshdbg = MSHDEBUG; |
| + | |
| + | @@ -220,6 +228,9 @@ struct op { |
| + | #define TASYNC 16 /* c & */ |
| + | /* Added to support "." file expansion */ |
| + | #define TDOT 17 |
| + | +#define TFUNC 18 //funccode:start |
| + | +#define TRETURN 19 |
| + | + //funccode:end |
| + | |
| + | /* Strings for names to make debug easier */ |
| + | #ifdef MSHDEBUG |
| + | @@ -319,6 +330,27 @@ struct region { |
| + | int area; |
| + | }; |
| + | |
| + | +static int func_finished; //funccode:start |
| + | +struct func { |
| + | + char* name; |
| + | + int begin_addr; /* pos in buffer of function */ |
| + | + int end_addr; |
| + | +}; |
| + | +#define MAX_FUNCS 100 |
| + | + |
| + | +static struct func funcs[MAX_FUNCS]; |
| + | + |
| + | +/* the max DEPTH of function call */ |
| + | +#define MAX_DEPTH 100 |
| + | +static struct _frame_s { |
| + | + int argc; |
| + | + char **argv; |
| + | + int saved_return_addr; |
| + | +} frame[MAX_DEPTH]; |
| + | + |
| + | +static void register_func(int begin, int end); |
| + | +static struct func* find_func(char* name); |
| + | +static void exec_func(struct func* f); //funccode:end |
| + | |
| + | /* -------- grammar stuff -------- */ |
| + | typedef union { |
| + | @@ -347,6 +379,8 @@ typedef union { |
| + | #define IN 272 |
| + | /* Added for "." file expansion */ |
| + | #define DOT 273 |
| + | +#define FUNC 274 //funccode:start |
| + | +#define RETURN 275 //funccode:end |
| + | |
| + | #define YYERRCODE 300 |
| + | |
| + | @@ -1722,6 +1756,40 @@ static struct op *simple(void) |
| + | (void) synio(0); |
| + | break; |
| + | |
| + | + case FUNC: { //funccode:start |
| + | + int stop_flag; |
| + | + int number_brace; |
| + | + int func_begin; |
| + | + int func_end; |
| + | + int c; |
| + | + while ((c = my_getc(0)) == ' ' || c == '\t'|| c == '\n') /* skip whitespace */ |
| + | + continue; |
| + | + stop_flag = 1; |
| + | + number_brace = 0; |
| + | + func_begin = global_env.iobase->argp->afpos; |
| + | + while (stop_flag) { |
| + | + if (c == '{') |
| + | + number_brace++; |
| + | + if (c == '}') |
| + | + number_brace--; |
| + | + if (!number_brace) /* if we reach the brace of most outsite */ |
| + | + stop_flag = 0; |
| + | + c = my_getc(0); |
| + | + } |
| + | + unget(c); |
| + | + unget(c); |
| + | + func_end = global_env.iobase->argp->afpos; |
| + | + register_func(func_begin, func_end); |
| + | + peeksym = 0; |
| + | + t = NULL; |
| + | + return t; |
| + | + } |
| + | + case RETURN: |
| + | + func_finished = 1; |
| + | + peeksym = 0; |
| + | + t = NULL; |
| + | + return t; //funccode:end |
| + | + |
| + | case WORD: |
| + | if (t == NULL) { |
| + | t = newtp(); |
| + | @@ -2265,6 +2333,13 @@ static int yylex(int cf) |
| + | case ')': |
| + | startl = 1; |
| + | return c; |
| + | + case '{': //funccode:start |
| + | + c = collect(c, '}'); |
| + | + if (c != '\0') |
| + | + return c; |
| + | + break; |
| + | + case '}': |
| + | + return RETURN; //funccode:end |
| + | } |
| + | |
| + | unget(c); |
| + | @@ -2293,9 +2368,172 @@ static int yylex(int cf) |
| + | } |
| + | |
| + | yylval.cp = strsave(line, areanum); |
| + | + /* To identify a subroutine */ //funccode:start |
| + | + c = my_getc(0); |
| + | + if (c && any(c, "(")) { |
| + | + c = my_getc(0); |
| + | + if (c && any(c, ")")) |
| + | + return FUNC; |
| + | + zzerr(); |
| + | + } else |
| + | + unget(c); |
| + | + /* read the first char */ |
| + | + /* To identify a function */ |
| + | + if (strcmp(yylval.cp, "function") == 0) { |
| + | + int ret = yylex(0); |
| + | + /* read the function name after "function" */ |
| + | + if (ret == WORD) |
| + | + return (FUNC); |
| + | + zzerr(); |
| + | + } |
| + | + { |
| + | + struct func* f = find_func(yylval.cp); |
| + | + if (f != NULL) { |
| + | + exec_func(f); |
| + | + return RETURN; |
| + | + } |
| + | + } |
| + | + if (yylval.cp != NULL && strcmp(yylval.cp, "return") == 0) { |
| + | + return RETURN; |
| + | + } //funccode:end |
| + | return WORD; |
| + | } |
| + | |
| + | +static void register_func(int begin, int end) //funccode:start |
| + | +{ |
| + | + struct func *p; |
| + | + int i; |
| + | + for (i = 0; i < MAX_FUNCS; i++) { |
| + | + if (funcs[i].name == NULL) { |
| + | + p = &funcs[i]; |
| + | + break; |
| + | + } |
| + | + } |
| + | + if (i == MAX_FUNCS) { |
| + | + fprintf(stderr, "Too much functions beyond limit\n"); |
| + | + leave(); |
| + | + } |
| + | + p->name = xstrdup(yylval.cp); |
| + | + //fprintf(stderr, "register function,%d,%d,%s\n", begin, end, p->name); |
| + | + KSDBG_PRINT_FUNCNAME; |
| + | + /* io stream */ |
| + | + p->begin_addr = begin; |
| + | + p->end_addr = end; |
| + | +} |
| + | + |
| + | +static struct func* find_func(char* name) |
| + | +{ |
| + | + int i; |
| + | + for (i = 0; i < MAX_FUNCS; i++) { |
| + | + if (funcs[i].name == NULL) |
| + | + continue; |
| + | + if (!strcmp(funcs[i].name, name)) |
| + | + return &funcs[i]; |
| + | + } |
| + | + KSDBG_PRINT_FUNCNAME; |
| + | + //fprintf(stderr, "not found the function %s\n", name); |
| + | + return NULL; |
| + | + //zzerr(); |
| + | +} |
| + | + |
| + | +/* Begin to execute the function */ |
| + | +static int cur_frame = 0; |
| + | + |
| + | +static void exec_func(struct func* f) |
| + | +{ |
| + | + int c; |
| + | + int temp_argc; |
| + | + char** temp_argv; |
| + | + struct iobuf *bp; |
| + | + |
| + | + /* create a new frame, save the argument and return address to this frame */ |
| + | + frame[cur_frame].argc = dolc; |
| + | + frame[cur_frame].argv = dolv; |
| + | + |
| + | + cur_frame++; |
| + | + /* do some argment parse and set arguments */ |
| + | + temp_argv = xmalloc(sizeof(char *)); |
| + | + temp_argv[0] = xstrdup(f->name); |
| + | + temp_argc = 0; |
| + | + global_env.iop->argp->afpos--; |
| + | + global_env.iop->argp->afbuf->bufp--; |
| + | +// unget(c); |
| + | + while (((c = yylex(0)) != '\n') && (yylval.cp != NULL)) { |
| + | + temp_argc++; |
| + | + temp_argv = xrealloc(temp_argv, sizeof(char *) * (temp_argc+1)); |
| + | + /* parse $ var if passed argument is a variable */ |
| + | + if (yylval.cp[0] == '$') { |
| + | + struct var *arg = lookup(&yylval.cp[1]); |
| + | + temp_argv[temp_argc] = xstrdup(arg->value); |
| + | + //fprintf(stderr, "arg->value=%s\n", arg->value); |
| + | + } else { |
| + | + temp_argv[temp_argc] = xstrdup(yylval.cp); |
| + | + //fprintf(stderr, "ARG:%s\n", yylval.cp); |
| + | + } |
| + | + } |
| + | + /* |
| + | + global_env.iop->argp->afpos--; |
| + | + global_env.iop->argp->afbuf->bufp--; |
| + | + */ |
| + | + dolc = temp_argc; |
| + | + dolv = temp_argv; |
| + | + //unget(c); |
| + | + //while ((c = my_getc(0)) == ' ' || c == '\t') /* Skip whitespace */ |
| + | + // continue; |
| + | + //unget(c); |
| + | + frame[cur_frame].saved_return_addr = global_env.iop->argp->afpos; |
| + | + |
| + | + /* get function begin address and execute this function */ |
| + | + |
| + | + bp = global_env.iop->argp->afbuf; |
| + | + bp->bufp = &(bp->buf[f->begin_addr]); |
| + | + global_env.iop->argp->afpos = f->begin_addr; |
| + | + |
| + | + /* func_finished=0 means we are in a function and func_finished=1 means we are executing a function */ |
| + | + func_finished = 0; |
| + | + |
| + | + //fprintf(stderr, "exec function %s\n", f->name); |
| + | + KSDBG_PRINT_FUNCNAME; |
| + | + for (;;) { |
| + | + //fprintf(stderr, "afpos=%d,%s\n", global_env.iop->argp->afpos, yylval.cp); |
| + | + if (global_env.iop->argp->afpos == f->end_addr) |
| + | + break; |
| + | + onecommand(); |
| + | + /* we return from a function, when func_finished = 1 */ |
| + | + if (func_finished) |
| + | + break; |
| + | + } |
| + | + |
| + | + { |
| + | + //fprintf(stderr, "%s is finished @%d!\n", f->name, global_env.iop->argp->afpos); |
| + | + int ret = frame[cur_frame].saved_return_addr; |
| + | + /* workaround code for \n */ |
| + | + if (dolc) |
| + | + ret--; |
| + | + /* get return address from current frame and jump to */ |
| + | + global_env.iop->argp->afpos = ret; |
| + | + global_env.iop->argp->afbuf->bufp = &(global_env.iop->argp->afbuf->buf[ret]); |
| + | + } |
| + | + /* |
| + | + fprintf(stderr, "******** after execution ********************\n"); |
| + | + fprintf(stderr, " %s \n############# %d\n", global_env.iop->argp->afbuf->bufp, ret); |
| + | + fprintf(stderr, "*******************************\n"); |
| + | + */ |
| + | + /* we return to previous frame */ |
| + | + cur_frame--; |
| + | + /* free some space occupied by argument */ |
| + | + while (dolc--) |
| + | + free(dolv[dolc]); |
| + | + free(dolv); |
| + | + |
| + | + /* recover argument for last function */ |
| + | + dolv = frame[cur_frame].argv; |
| + | + dolc = frame[cur_frame].argc; |
| + | + /* If we are not in the outest frame, we should set |
| + | + * func_finished to 0 that means we still in some function */ |
| + | + if (cur_frame != 0) |
| + | + func_finished = 0; |
| + | +} //funccode:end |
| + | |
| + | static int collect(int c, int c1) |
| + | { |
| + | @@ -2601,6 +2839,10 @@ static int execute(struct op *t, int *pi |
| + | execute(t->right->right, pin, pout, /* no_fork: */ 0); |
| + | } |
| + | break; |
| + | + case TFUNC: //funccode:start |
| + | + break; |
| + | + case TRETURN: |
| + | + break; //funccode:end |
| + | |
| + | case TCASE: |
| + | cp = evalstr(t->str, DOSUB | DOTRIM); |