blob: 270b9eeffb664332071b6720210470871b445e77 [file] [log] [blame]
Denis Vlasenko2d0529c2008-06-09 07:50:25 +00001This is a "function" patch for msh which is in use by some busybox
2users. Unfortunately it is far too buggy to be applied, but maybe
3it's a useful starting point for future work.
4
5Function-related code is delimited by comments of the form
6 //funccode:start
7 ...
8 //funccode:end
9for ease of grepping
10
11An example of buggy behavior:
12
13#f() {
14# echo foo
15# echo test`echo bar >&2`
16# echo END f
17#}
18
19function g {
20# echo 2 foo
21# echo 2 test`echo 2 bar >&2`
22# f
23 echo END g
24# echo "1:'$1' 2:'$2'"
25}
26
27# Even this first block fails - it does not even call functions!
28# (replacing "echo END g" above with "echo END" makes it run ok)
29echo DRY RUN
30 echo 2 foo
31 echo 2 test`echo 2 bar >&2`
32 echo END g
33 echo "1:'$1' 2:'$2'"
34 echo foo
35 echo test`echo bar >&2`
36 echo END f
37echo END DRY RUN
38
39exit
40
41# This would fail too
42g "$1-one" "two$2"
43echo DONE
44
45
46
47diff -d -urpN busybox.7/shell/msh.c busybox.8/shell/msh.c
48--- busybox.7/shell/msh.c 2008-06-09 09:34:45.000000000 +0200
49+++ busybox.8/shell/msh.c 2008-06-09 09:38:17.000000000 +0200
50@@ -89,6 +89,14 @@ static char *itoa(int n)
51
52 //#define MSHDEBUG 4
53
54+/* Used only in "function" support code */
55+#ifdef KSDBG //funccode:start
56+ #define KSDBG_PRINT_FUNCNAME fprintf(stderr, "in %s\n", __FUNCTION__)
57+#else
58+ #define KSDBG_PRINT_FUNCNAME ((void)0)
59+#endif
60+//funccode:end
61+
62 #ifdef MSHDEBUG
63 static int mshdbg = MSHDEBUG;
64
65@@ -220,6 +228,9 @@ struct op {
66 #define TASYNC 16 /* c & */
67 /* Added to support "." file expansion */
68 #define TDOT 17
69+#define TFUNC 18 //funccode:start
70+#define TRETURN 19
71+ //funccode:end
72
73 /* Strings for names to make debug easier */
74 #ifdef MSHDEBUG
75@@ -319,6 +330,27 @@ struct region {
76 int area;
77 };
78
79+static int func_finished; //funccode:start
80+struct func {
81+ char* name;
82+ int begin_addr; /* pos in buffer of function */
83+ int end_addr;
84+};
85+#define MAX_FUNCS 100
86+
87+static struct func funcs[MAX_FUNCS];
88+
89+/* the max DEPTH of function call */
90+#define MAX_DEPTH 100
91+static struct _frame_s {
92+ int argc;
93+ char **argv;
94+ int saved_return_addr;
95+} frame[MAX_DEPTH];
96+
97+static void register_func(int begin, int end);
98+static struct func* find_func(char* name);
99+static void exec_func(struct func* f); //funccode:end
100
101 /* -------- grammar stuff -------- */
102 typedef union {
103@@ -347,6 +379,8 @@ typedef union {
104 #define IN 272
105 /* Added for "." file expansion */
106 #define DOT 273
107+#define FUNC 274 //funccode:start
108+#define RETURN 275 //funccode:end
109
110 #define YYERRCODE 300
111
112@@ -1722,6 +1756,40 @@ static struct op *simple(void)
113 (void) synio(0);
114 break;
115
116+ case FUNC: { //funccode:start
117+ int stop_flag;
118+ int number_brace;
119+ int func_begin;
120+ int func_end;
121+ int c;
122+ while ((c = my_getc(0)) == ' ' || c == '\t'|| c == '\n') /* skip whitespace */
123+ continue;
124+ stop_flag = 1;
125+ number_brace = 0;
126+ func_begin = global_env.iobase->argp->afpos;
127+ while (stop_flag) {
128+ if (c == '{')
129+ number_brace++;
130+ if (c == '}')
131+ number_brace--;
132+ if (!number_brace) /* if we reach the brace of most outsite */
133+ stop_flag = 0;
134+ c = my_getc(0);
135+ }
136+ unget(c);
137+ unget(c);
138+ func_end = global_env.iobase->argp->afpos;
139+ register_func(func_begin, func_end);
140+ peeksym = 0;
141+ t = NULL;
142+ return t;
143+ }
144+ case RETURN:
145+ func_finished = 1;
146+ peeksym = 0;
147+ t = NULL;
148+ return t; //funccode:end
149+
150 case WORD:
151 if (t == NULL) {
152 t = newtp();
153@@ -2265,6 +2333,13 @@ static int yylex(int cf)
154 case ')':
155 startl = 1;
156 return c;
157+ case '{': //funccode:start
158+ c = collect(c, '}');
159+ if (c != '\0')
160+ return c;
161+ break;
162+ case '}':
163+ return RETURN; //funccode:end
164 }
165
166 unget(c);
167@@ -2293,9 +2368,172 @@ static int yylex(int cf)
168 }
169
170 yylval.cp = strsave(line, areanum);
171+ /* To identify a subroutine */ //funccode:start
172+ c = my_getc(0);
173+ if (c && any(c, "(")) {
174+ c = my_getc(0);
175+ if (c && any(c, ")"))
176+ return FUNC;
177+ zzerr();
178+ } else
179+ unget(c);
180+ /* read the first char */
181+ /* To identify a function */
182+ if (strcmp(yylval.cp, "function") == 0) {
183+ int ret = yylex(0);
184+ /* read the function name after "function" */
185+ if (ret == WORD)
186+ return (FUNC);
187+ zzerr();
188+ }
189+ {
190+ struct func* f = find_func(yylval.cp);
191+ if (f != NULL) {
192+ exec_func(f);
193+ return RETURN;
194+ }
195+ }
196+ if (yylval.cp != NULL && strcmp(yylval.cp, "return") == 0) {
197+ return RETURN;
198+ } //funccode:end
199 return WORD;
200 }
201
202+static void register_func(int begin, int end) //funccode:start
203+{
204+ struct func *p;
205+ int i;
206+ for (i = 0; i < MAX_FUNCS; i++) {
207+ if (funcs[i].name == NULL) {
208+ p = &funcs[i];
209+ break;
210+ }
211+ }
212+ if (i == MAX_FUNCS) {
213+ fprintf(stderr, "Too much functions beyond limit\n");
214+ leave();
215+ }
216+ p->name = xstrdup(yylval.cp);
217+ //fprintf(stderr, "register function,%d,%d,%s\n", begin, end, p->name);
218+ KSDBG_PRINT_FUNCNAME;
Denis Vlasenko598bb732008-06-09 07:58:53 +0000219+ /* io stream */
Denis Vlasenko2d0529c2008-06-09 07:50:25 +0000220+ p->begin_addr = begin;
221+ p->end_addr = end;
222+}
223+
224+static struct func* find_func(char* name)
225+{
226+ int i;
227+ for (i = 0; i < MAX_FUNCS; i++) {
228+ if (funcs[i].name == NULL)
229+ continue;
230+ if (!strcmp(funcs[i].name, name))
231+ return &funcs[i];
232+ }
233+ KSDBG_PRINT_FUNCNAME;
234+ //fprintf(stderr, "not found the function %s\n", name);
235+ return NULL;
236+ //zzerr();
237+}
238+
239+/* Begin to execute the function */
240+static int cur_frame = 0;
241+
242+static void exec_func(struct func* f)
243+{
244+ int c;
245+ int temp_argc;
246+ char** temp_argv;
247+ struct iobuf *bp;
248+
249+ /* create a new frame, save the argument and return address to this frame */
250+ frame[cur_frame].argc = dolc;
251+ frame[cur_frame].argv = dolv;
252+
253+ cur_frame++;
Denis Vlasenko598bb732008-06-09 07:58:53 +0000254+ /* do some argument parse and set arguments */
Denis Vlasenko2d0529c2008-06-09 07:50:25 +0000255+ temp_argv = xmalloc(sizeof(char *));
256+ temp_argv[0] = xstrdup(f->name);
257+ temp_argc = 0;
258+ global_env.iop->argp->afpos--;
259+ global_env.iop->argp->afbuf->bufp--;
260+// unget(c);
261+ while (((c = yylex(0)) != '\n') && (yylval.cp != NULL)) {
262+ temp_argc++;
263+ temp_argv = xrealloc(temp_argv, sizeof(char *) * (temp_argc+1));
264+ /* parse $ var if passed argument is a variable */
265+ if (yylval.cp[0] == '$') {
266+ struct var *arg = lookup(&yylval.cp[1]);
267+ temp_argv[temp_argc] = xstrdup(arg->value);
268+ //fprintf(stderr, "arg->value=%s\n", arg->value);
269+ } else {
270+ temp_argv[temp_argc] = xstrdup(yylval.cp);
271+ //fprintf(stderr, "ARG:%s\n", yylval.cp);
272+ }
273+ }
274+ /*
275+ global_env.iop->argp->afpos--;
Denis Vlasenko598bb732008-06-09 07:58:53 +0000276+ global_env.iop->argp->afbuf->bufp--;
Denis Vlasenko2d0529c2008-06-09 07:50:25 +0000277+ */
278+ dolc = temp_argc;
279+ dolv = temp_argv;
280+ //unget(c);
281+ //while ((c = my_getc(0)) == ' ' || c == '\t') /* Skip whitespace */
282+ // continue;
283+ //unget(c);
284+ frame[cur_frame].saved_return_addr = global_env.iop->argp->afpos;
285+
286+ /* get function begin address and execute this function */
287+
288+ bp = global_env.iop->argp->afbuf;
289+ bp->bufp = &(bp->buf[f->begin_addr]);
290+ global_env.iop->argp->afpos = f->begin_addr;
291+
292+ /* func_finished=0 means we are in a function and func_finished=1 means we are executing a function */
293+ func_finished = 0;
294+
295+ //fprintf(stderr, "exec function %s\n", f->name);
296+ KSDBG_PRINT_FUNCNAME;
297+ for (;;) {
298+ //fprintf(stderr, "afpos=%d,%s\n", global_env.iop->argp->afpos, yylval.cp);
299+ if (global_env.iop->argp->afpos == f->end_addr)
300+ break;
301+ onecommand();
302+ /* we return from a function, when func_finished = 1 */
303+ if (func_finished)
304+ break;
305+ }
306+
307+ {
308+ //fprintf(stderr, "%s is finished @%d!\n", f->name, global_env.iop->argp->afpos);
309+ int ret = frame[cur_frame].saved_return_addr;
310+ /* workaround code for \n */
311+ if (dolc)
312+ ret--;
313+ /* get return address from current frame and jump to */
314+ global_env.iop->argp->afpos = ret;
315+ global_env.iop->argp->afbuf->bufp = &(global_env.iop->argp->afbuf->buf[ret]);
316+ }
317+ /*
318+ fprintf(stderr, "******** after execution ********************\n");
319+ fprintf(stderr, " %s \n############# %d\n", global_env.iop->argp->afbuf->bufp, ret);
320+ fprintf(stderr, "*******************************\n");
321+ */
322+ /* we return to previous frame */
323+ cur_frame--;
324+ /* free some space occupied by argument */
325+ while (dolc--)
326+ free(dolv[dolc]);
327+ free(dolv);
328+
329+ /* recover argument for last function */
330+ dolv = frame[cur_frame].argv;
331+ dolc = frame[cur_frame].argc;
332+ /* If we are not in the outest frame, we should set
333+ * func_finished to 0 that means we still in some function */
334+ if (cur_frame != 0)
335+ func_finished = 0;
336+} //funccode:end
337
338 static int collect(int c, int c1)
339 {
340@@ -2601,6 +2839,10 @@ static int execute(struct op *t, int *pi
341 execute(t->right->right, pin, pout, /* no_fork: */ 0);
342 }
343 break;
344+ case TFUNC: //funccode:start
345+ break;
346+ case TRETURN:
347+ break; //funccode:end
348
349 case TCASE:
350 cp = evalstr(t->str, DOSUB | DOTRIM);