blob: 270b9eeffb664332071b6720210470871b445e77 [file] [log] [blame]
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);