| 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 argument 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); |