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