Denis Vlasenko | 2d0529c | 2008-06-09 07:50:25 +0000 | [diff] [blame] | 1 | This is a "function" patch for msh which is in use by some busybox |
| 2 | users. Unfortunately it is far too buggy to be applied, but maybe |
| 3 | it's a useful starting point for future work. |
| 4 | |
| 5 | Function-related code is delimited by comments of the form |
| 6 | //funccode:start |
| 7 | ... |
| 8 | //funccode:end |
| 9 | for ease of grepping |
| 10 | |
| 11 | An example of buggy behavior: |
| 12 | |
| 13 | #f() { |
| 14 | # echo foo |
| 15 | # echo test`echo bar >&2` |
| 16 | # echo END f |
| 17 | #} |
| 18 | |
| 19 | function 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) |
| 29 | echo 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 |
| 37 | echo END DRY RUN |
| 38 | |
| 39 | exit |
| 40 | |
| 41 | # This would fail too |
| 42 | g "$1-one" "two$2" |
| 43 | echo DONE |
| 44 | |
| 45 | |
| 46 | |
| 47 | diff -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 Vlasenko | 598bb73 | 2008-06-09 07:58:53 +0000 | [diff] [blame] | 219 | + /* io stream */ |
Denis Vlasenko | 2d0529c | 2008-06-09 07:50:25 +0000 | [diff] [blame] | 220 | + 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 Vlasenko | 598bb73 | 2008-06-09 07:58:53 +0000 | [diff] [blame] | 254 | + /* do some argument parse and set arguments */ |
Denis Vlasenko | 2d0529c | 2008-06-09 07:50:25 +0000 | [diff] [blame] | 255 | + 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 Vlasenko | 598bb73 | 2008-06-09 07:58:53 +0000 | [diff] [blame] | 276 | + global_env.iop->argp->afbuf->bufp--; |
Denis Vlasenko | 2d0529c | 2008-06-09 07:50:25 +0000 | [diff] [blame] | 277 | + */ |
| 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); |