351 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			351 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
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);
 |