2008-06-09 07:50:25 +00:00
|
|
|
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;
|
2008-06-09 07:58:53 +00:00
|
|
|
+ /* io stream */
|
2008-06-09 07:50:25 +00:00
|
|
|
+ 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++;
|
2008-06-09 07:58:53 +00:00
|
|
|
+ /* do some argument parse and set arguments */
|
2008-06-09 07:50:25 +00:00
|
|
|
+ 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--;
|
2008-06-09 07:58:53 +00:00
|
|
|
+ global_env.iop->argp->afbuf->bufp--;
|
2008-06-09 07:50:25 +00:00
|
|
|
+ */
|
|
|
|
+ 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);
|