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