c9c653bb69c23656dba7bc428821d621e57d76d9
[openwrt/svn-archive/archive.git] / package / busybox / patches / 920-awx.patch
1 diff -purN bb.old/editors/awk.c bb.dev/editors/awk.c
2 --- bb.old/editors/awk.c 2007-03-06 19:38:07.278092000 +0100
3 +++ bb.dev/editors/awk.c 2007-03-11 05:14:11.776304544 +0100
4 @@ -30,6 +30,11 @@
5 /* these flags are static, don't change them when value is changed */
6 #define VF_DONTTOUCH (VF_ARRAY | VF_SPECIAL | VF_WALK | VF_CHILD | VF_DIRTY)
7
8 +#ifdef CONFIG_AWX
9 +#define fputs(s, stream) fputs_hook(s, stream)
10 +static inline int fputs_hook (__const char *__restrict __s, FILE *__restrict __stream);
11 +#endif
12 +
13 /* Variable */
14 typedef struct var_s {
15 unsigned short type; /* flags */
16 @@ -50,10 +55,15 @@ typedef struct chain_s {
17 char *programname;
18 } chain;
19
20 +typedef var *(*awk_cfunc)(var *res, var *args, int nargs);
21 /* Function */
22 typedef struct func_s {
23 unsigned short nargs;
24 - struct chain_s body;
25 + enum { AWKFUNC, CFUNC } type;
26 + union {
27 + awk_cfunc cfunc;
28 + struct chain_s body;
29 + } x;
30 } func;
31
32 /* I/O stream */
33 @@ -1312,7 +1322,8 @@ static void parse_program(char *p)
34 next_token(TC_FUNCTION);
35 pos++;
36 f = newfunc(t.string);
37 - f->body.first = NULL;
38 + f->type = AWKFUNC;
39 + f->x.body.first = NULL;
40 f->nargs = 0;
41 while (next_token(TC_VARIABLE | TC_SEQTERM) & TC_VARIABLE) {
42 v = findvar(ahash, t.string);
43 @@ -1321,7 +1332,7 @@ static void parse_program(char *p)
44 if (next_token(TC_COMMA | TC_SEQTERM) & TC_SEQTERM)
45 break;
46 }
47 - seq = &(f->body);
48 + seq = &(f->x.body);
49 chain_group();
50 clear_array(ahash);
51
52 @@ -2260,7 +2271,8 @@ static var *evaluate(node *op, var *res)
53 break;
54
55 case XC( OC_FUNC ):
56 - if (! op->r.f->body.first)
57 + if ((op->r.f->type == AWKFUNC) &&
58 + !op->r.f->x.body.first)
59 runtime_error(EMSG_UNDEF_FUNC);
60
61 X.v = R.v = nvalloc(op->r.f->nargs+1);
62 @@ -2277,7 +2289,11 @@ static var *evaluate(node *op, var *res)
63 fnargs = X.v;
64
65 L.s = programname;
66 - res = evaluate(op->r.f->body.first, res);
67 + if (op->r.f->type == AWKFUNC)
68 + res = evaluate(op->r.f->x.body.first, res);
69 + else if (op->r.f->type == CFUNC)
70 + res = op->r.f->x.cfunc(res, fnargs, op->r.f->nargs);
71 +
72 programname = L.s;
73
74 nvfree(fnargs);
75 @@ -2637,6 +2653,11 @@ static rstream *next_input_file(void)
76 return &rsm;
77 }
78
79 +#ifdef CONFIG_AWX
80 +static int is_awx = 0;
81 +#include "awx.c"
82 +#endif
83 +
84 int awk_main(int argc, char **argv)
85 {
86 int i, j, flen;
87 @@ -2693,6 +2714,10 @@ int awk_main(int argc, char **argv)
88 free(s);
89 }
90
91 +#ifdef CONFIG_AWX
92 + do_awx(argc, argv);
93 +#endif
94 +
95 programname = NULL;
96 while((c = getopt(argc, argv, "F:v:f:W:")) != EOF) {
97 switch (c) {
98 diff -purN bb.old/editors/awx.c bb.dev/editors/awx.c
99 --- bb.old/editors/awx.c 1970-01-01 01:00:00.000000000 +0100
100 +++ bb.dev/editors/awx.c 2007-03-11 06:25:48.552095336 +0100
101 @@ -0,0 +1,521 @@
102 +/*
103 + * awk web extension
104 + *
105 + * Copyright (C) 2007 by Felix Fietkau <nbd@openwrt.org>
106 + *
107 + * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
108 + */
109 +
110 +#include <cgi.h>
111 +#include <glob.h>
112 +
113 +#define LINE_BUF 2048
114 +#define HASH_MAX 1536
115 +#define TR_START "@TR<<"
116 +#define TR_END ">>"
117 +#define MAX_TR 32
118 +#define SSI_START "<%"
119 +#define SSI_END "%>"
120 +
121 +#undef fputs
122 +
123 +static xhash *lstr = NULL;
124 +static int lang_inuse = 0;
125 +
126 +/* look up a translation symbol from the hash */
127 +static inline char *translate_lookup(char *str)
128 +{
129 + char *name, *def, *p;
130 + hash_item *hi;
131 + var *v;
132 +
133 + def = name = str;
134 + if (((p = strchr(str, '|')) != NULL)
135 + || ((p = strchr(str, '#')) != NULL)) {
136 + def = p + 1;
137 + *p = 0;
138 + }
139 +
140 + hi = hash_search(lstr, name);
141 + if (!hi)
142 + return def;
143 +
144 + v = &hi->data.v;
145 +
146 + return getvar_s(v);
147 +}
148 +
149 +/* look for translation markers in the line and return the translated string */
150 +static char *translate_line(char *line)
151 +{
152 + char *tok[MAX_TR * 3];
153 + char *l, *p, *p2, *res;
154 + int len = 0, _pos = 0, i;
155 +
156 + l = line;
157 + while (l != NULL) {
158 + if ((p = strstr(l, TR_START)) == NULL) {
159 + len += strlen((tok[_pos++] = l));
160 + break;
161 + }
162 +
163 + p2 = strstr(p, TR_END);
164 + if (p2 == NULL)
165 + break;
166 +
167 + *p = 0;
168 + *p2 = 0;
169 + len += strlen((tok[_pos++] = l));
170 + len += strlen((tok[_pos++] = translate_lookup(p + strlen(TR_START))));
171 +
172 + l = p2;
173 + l += strlen(TR_END);
174 + }
175 + len++;
176 +
177 + p = xmalloc(len + 1);
178 + *p = 0;
179 + res = p;
180 + for (i = 0; i < _pos; i++) {
181 + strcat(p, tok[i]);
182 + p += strlen(tok[i]);
183 + }
184 +
185 + return res;
186 +}
187 +
188 +/* hook for intercepting awk's use of puts. used for running all printed strings
189 + * through the translation system */
190 +static inline int fputs_hook (__const char *__restrict __s, FILE *__restrict __stream)
191 +{
192 + if (lang_inuse && (__stream == stdout)) {
193 + int ret;
194 + char *str;
195 +
196 + str = translate_line((char *) __s);
197 + ret = fputs(str, __stream);
198 + free(str);
199 +
200 + return ret;
201 + }
202 +
203 + return fputs(__s, __stream);
204 +}
205 +
206 +static var *init_lang(var *res, var *args, int nargs)
207 +{
208 + if (!lstr)
209 + lstr = hash_init();
210 +
211 + lang_inuse = 1;
212 + return res;
213 +}
214 +
215 +
216 +/* load and parse language file */
217 +static void load_lang_file(char *file)
218 +{
219 + FILE *f;
220 + char *b, *name, *value;
221 + char buf1[LINE_BUF];
222 +
223 + if ((f = fopen(file, "r")) == NULL)
224 + return;
225 +
226 + while (!feof(f) && (fgets(buf1, LINE_BUF - 1, f) != NULL)) {
227 + b = buf1;
228 + if (*b == '#')
229 + continue; /* skip comments */
230 +
231 + while (isspace(*b))
232 + b++; /* skip leading spaces */
233 + if (!*b)
234 + continue;
235 +
236 + name = b;
237 + if ((b = strstr(name, "=>")) == NULL)
238 + continue; /* separator not found */
239 +
240 + value = b + 2;
241 + if (!*value)
242 + continue;
243 +
244 + *b = 0;
245 + for (b--; isspace(*b); b--)
246 + *b = 0; /* remove trailing spaces */
247 +
248 + while (isspace(*value))
249 + value++; /* skip leading spaces */
250 +
251 + for (b = value + strlen(value) - 1; isspace(*b); b--)
252 + *b = 0; /* remove trailing spaces */
253 +
254 + if (!*value)
255 + continue;
256 +
257 + setvar_s(findvar(lstr,name), value);
258 + }
259 +
260 + fclose(f);
261 +}
262 +
263 +static var *load_lang(var *res, var *args, int nargs)
264 +{
265 + const char *langfmt = "/usr/lib/webif/lang/%s.txt";
266 + char lbuf[LINE_BUF];
267 + char *lang;
268 +
269 + if (!lang_inuse)
270 + init_lang(res, args, nargs);
271 +
272 + lang = getvar_s(args);
273 + if (!lang || !strcmp(lang, ""))
274 + return res;
275 +
276 + sprintf(lbuf, langfmt, lang);
277 + load_lang_file(lbuf);
278 +
279 + return res;
280 +}
281 +
282 +/* read the contents of an entire file */
283 +static char *get_file(char *fname)
284 +{
285 + FILE *F;
286 + char *s = NULL;
287 + int i, j, flen;
288 +
289 + F = fopen(fname, "r");
290 + if (!F) {
291 + return NULL;
292 + }
293 +
294 + if (fseek(F, 0, SEEK_END) == 0) {
295 + flen = ftell(F);
296 + s = (char *)xmalloc(flen+4);
297 + fseek(F, 0, SEEK_SET);
298 + i = 1 + fread(s+1, 1, flen, F);
299 + } else {
300 + for (i=j=1; j>0; i+=j) {
301 + s = (char *)xrealloc(s, i+4096);
302 + j = fread(s+i, 1, 4094, F);
303 + }
304 + }
305 +
306 + s[i] = '\0';
307 + fclose(F);
308 + return s;
309 +}
310 +
311 +
312 +/* parse_include():
313 + *
314 + * taken from parse_program from awk.c
315 + * END{} is not parsed here, and BEGIN{} is executed immediately
316 + */
317 +static void parse_include(char *p)
318 +{
319 + uint32_t tclass;
320 + chain initseq;
321 + func *f;
322 + var *v, tv;
323 + int has_init = 0;
324 +
325 + pos = p;
326 + t.lineno = 1;
327 + memset(&initseq, 0, sizeof(initseq));
328 + while ((tclass = next_token(TC_EOF | TC_OPSEQ |
329 + TC_OPTERM | TC_BEGIN | TC_FUNCDECL)) != TC_EOF) {
330 + if (tclass & TC_OPTERM)
331 + continue;
332 +
333 + seq = &initseq;
334 + if (tclass & TC_BEGIN) {
335 + has_init = 1;
336 + chain_group();
337 + } else if (tclass & TC_FUNCDECL) {
338 + next_token(TC_FUNCTION);
339 + pos++;
340 + f = newfunc(t.string);
341 + f->type = AWKFUNC;
342 + f->x.body.first = NULL;
343 + f->nargs = 0;
344 + while (next_token(TC_VARIABLE | TC_SEQTERM) & TC_VARIABLE) {
345 + v = findvar(ahash, t.string);
346 + v->x.aidx = (f->nargs)++;
347 +
348 + if (next_token(TC_COMMA | TC_SEQTERM) & TC_SEQTERM)
349 + break;
350 + }
351 + seq = &(f->x.body);
352 + chain_group();
353 + clear_array(ahash);
354 + }
355 + }
356 + if (has_init)
357 + evaluate(initseq.first, &tv);
358 +}
359 +
360 +/* include an awk file and run its BEGIN{} section */
361 +static var *include(var *res, var *args, int nargs)
362 +{
363 + static xhash *includes = NULL;
364 + char *s;
365 + var *v;
366 +
367 + s = getvar_s(args);
368 + if (!s)
369 + return res;
370 +
371 + if (!includes)
372 + includes = hash_init();
373 +
374 + /* find out if the file has been included already */
375 + v = findvar(includes, s);
376 + if (istrue(v))
377 + return res;
378 + setvar_s(v, "1");
379 +
380 + /* read include file */
381 + s = get_file(s);
382 + if (!s) {
383 + fprintf(stderr, "Could not open file.\n");
384 + return res;
385 + }
386 + parse_include(s+1);
387 + free(s);
388 +
389 + return res;
390 +}
391 +
392 +
393 +/* parse and evaluate an awk expression and return the result as string */
394 +static char *render_lookup(char *fname, int lnr, char *str)
395 +{
396 + chain body;
397 + var tv;
398 +
399 + memset(&body, 0, sizeof(body));
400 + zero_out_var(&tv);
401 + pos = str;
402 + seq = &body;
403 +
404 + /* end of expression, assume that there's going to be a free byte
405 + * at the end of the string that can be used for the ')' */
406 + strcat(str + strlen(str), ")");
407 + return getvar_s(evaluate(parse_expr(TC_SEQTERM), &tv));
408 +}
409 +
410 +static inline void print_translate(char *s)
411 +{
412 + char *str = s;
413 + if (lang_inuse)
414 + str = translate_line(s);
415 + fputs(str, stdout);
416 + fflush(stdout);
417 + if (lang_inuse)
418 + free(str);
419 +}
420 +
421 +/* process awk calls in a template line and print the output to stdout */
422 +static void render_line(char *fname, int lnr, char *line)
423 +{
424 + char *tok[MAX_TR * 3];
425 + char *l, *p, *p2, *res;
426 + int len = 0, _pos = 0, i;
427 +
428 + l = line;
429 + while (l != NULL) {
430 + if ((p = strstr(l, SSI_START)) == NULL) {
431 + len += strlen((tok[_pos++] = l));
432 + break;
433 + }
434 +
435 + p2 = strstr(p, SSI_END);
436 + if (p2 == NULL) {
437 + fprintf(stderr, "Parse error in '%s', line '%d', unmatched %s\n", fname, lnr, SSI_END);
438 + break;
439 + }
440 +
441 + *p = 0;
442 + *p2 = 0;
443 +
444 + len += strlen((tok[_pos++] = l));
445 + len += strlen((tok[_pos++] = render_lookup(fname, lnr, p + strlen(SSI_START))));
446 +
447 + l = p2;
448 + l += strlen(SSI_END);
449 + }
450 + len++;
451 +
452 + p = xmalloc(len + 1);
453 + *p = 0;
454 + res = p;
455 + for (i = 0; i < _pos; i++) {
456 + strcat(p, tok[i]);
457 + p += strlen(tok[i]);
458 + }
459 + print_translate(res);
460 + free(res);
461 +}
462 +
463 +/* awk method render(), which opens a template file and processes all awk ssi calls */
464 +static var *render(var *res, var *args, int nargs)
465 +{
466 + char *s;
467 + int lnr = 0;
468 + FILE *f;
469 + char *buf1;
470 +
471 + buf1 = xmalloc(LINE_BUF);
472 + s = getvar_s(args);
473 + if (!s)
474 + goto done;
475 +
476 + f = fopen(s, "r");
477 + if (!f)
478 + goto done;
479 +
480 + while (!feof(f) && (fgets(buf1, LINE_BUF - 1, f) != NULL)) {
481 + render_line(s, ++lnr, buf1);
482 + }
483 +
484 +done:
485 + free(buf1);
486 + return res;
487 +}
488 +
489 +/* Call render, but only if this function hasn't been called already */
490 +static int layout_rendered = 0;
491 +static var *render_layout(var *res, var *args, int nargs)
492 +{
493 + if (layout_rendered)
494 + return res;
495 + layout_rendered = 1;
496 + return render(res, args, nargs);
497 +}
498 +
499 +/* registers a global c function for the awk interpreter */
500 +static void register_cfunc(char *name, awk_cfunc cfunc, int nargs)
501 +{
502 + func *f;
503 +
504 + f = newfunc(name);
505 + f->type = CFUNC;
506 + f->x.cfunc = cfunc;
507 + f->nargs = nargs;
508 +}
509 +
510 +/* function call for accessing cgi form variables */
511 +static var *getvar(var *res, var *args, int nargs)
512 +{
513 + char *s;
514 +
515 + s = getvar_s(args);
516 + if (s)
517 + setvar_s(res, cgi_param(s) ?: "");
518 +
519 + return res;
520 +}
521 +
522 +/* call an awk function without arguments by string reference */
523 +static var *call(var *res, var *args, int nargs)
524 +{
525 + char *s = getvar_s(args);
526 + func *f;
527 +
528 + if (!s)
529 + goto done;
530 +
531 + f = newfunc(s);
532 + if (f && f->type == AWKFUNC && f->x.body.first)
533 + return evaluate(f->x.body.first, res);
534 +
535 +done:
536 + return res;
537 +}
538 +
539 +
540 +/* main awx processing function. called from awk_main() */
541 +static int do_awx(int argc, char **argv)
542 +{
543 + int ret = -1;
544 + var tv;
545 + char *s = NULL;
546 + var *layout;
547 + var *action;
548 + char *tmp;
549 + int i;
550 +
551 + zero_out_var(&tv);
552 +
553 + /* register awk C callbacks */
554 + register_cfunc("getvar", getvar, 1);
555 + register_cfunc("render", render, 1);
556 + register_cfunc("render_layout", render_layout, 1);
557 + register_cfunc("call", call, 1);
558 + register_cfunc("include", include, 1);
559 + register_cfunc("init_lang", init_lang, 1);
560 + register_cfunc("load_lang", load_lang, 1);
561 +
562 + if (!is_awx)
563 + return 0;
564 +
565 + /* fill in ARGV array */
566 + programname = argv[1];
567 + setvar_i(V[ARGC], argc + 1);
568 + setari_u(V[ARGV], 0, "awx");
569 + i = 0;
570 + while (*argv)
571 + setari_u(V[ARGV], ++i, *argv++);
572 +
573 + cgi_init();
574 + if (argc < 2) {
575 + fprintf(stderr, "Invalid argument.\n");
576 + goto done;
577 + }
578 +
579 + /* read the main controller source */
580 + s = get_file(programname);
581 + if (!s) {
582 + fprintf(stderr, "Could not open file\n");
583 + goto done;
584 + }
585 + parse_program(s+1);
586 + free(s);
587 +
588 + /* set some defaults for ACTION and LAYOUT, which will have special meaning */
589 + cgi_process_form();
590 +
591 + action = newvar("ACTION");
592 + setvar_s(action, cgi_param("action") ?: "default");
593 + layout = newvar("LAYOUT");
594 + setvar_s(layout, "views/layout.ahtml");
595 +
596 + /* run the BEGIN {} block */
597 + evaluate(beginseq.first, &tv);
598 +
599 + /* call the action (precedence: begin block override > cgi parameter > "default") */
600 + tmp = xmalloc(strlen(getvar_s(action)) + 7);
601 + sprintf(tmp, "handle_%s", getvar_s(action));
602 + setvar_s(action, tmp);
603 + call(&tv, action, 1);
604 + free(tmp);
605 +
606 + /* render the selected layout, will do nothing if render_layout has been called from awk */
607 + render_layout(&tv, layout, 1);
608 +
609 + ret = 0;
610 +done:
611 + cgi_end();
612 +
613 + exit(0);
614 +}
615 +
616 +/* entry point for awx applet */
617 +int awx_main(int argc, char **argv)
618 +{
619 + is_awx = 1;
620 + return awk_main(argc, argv);
621 +}
622 +
623 diff -purN bb.old/editors/Config.in bb.dev/editors/Config.in
624 --- bb.old/editors/Config.in 2007-01-24 22:34:50.000000000 +0100
625 +++ bb.dev/editors/Config.in 2007-03-11 06:19:51.469380160 +0100
626 @@ -12,6 +12,13 @@ config AWK
627 Awk is used as a pattern scanning and processing language. This is
628 the BusyBox implementation of that programming language.
629
630 +config AWX
631 + bool "Enable awx (awk web extension)"
632 + default n
633 + depends on AWK
634 + help
635 + awx - awk web extension
636 +
637 config FEATURE_AWK_MATH
638 bool "Enable math functions (requires libm)"
639 default y
640 diff -purN bb.old/include/applets.h bb.dev/include/applets.h
641 --- bb.old/include/applets.h 2007-03-06 19:38:07.355081000 +0100
642 +++ bb.dev/include/applets.h 2007-03-07 02:12:24.280681880 +0100
643 @@ -60,6 +60,7 @@ USE_ARP(APPLET(arp, _BB_DIR_SBIN, _BB_SU
644 USE_ARPING(APPLET(arping, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
645 USE_ASH(APPLET_NOUSAGE(ash, ash, _BB_DIR_BIN, _BB_SUID_NEVER))
646 USE_AWK(APPLET(awk, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
647 +USE_AWX(APPLET_NOUSAGE(awx, awx, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
648 USE_BASENAME(APPLET(basename, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
649 USE_BBCONFIG(APPLET(bbconfig, _BB_DIR_BIN, _BB_SUID_NEVER))
650 //USE_BBSH(APPLET(bbsh, _BB_DIR_BIN, _BB_SUID_NEVER))
651 diff -purN bb.old/Makefile bb.dev/Makefile
652 --- bb.old/Makefile 2007-03-06 19:38:07.358080000 +0100
653 +++ bb.dev/Makefile 2007-03-07 02:28:22.844957960 +0100
654 @@ -565,7 +565,7 @@ quiet_cmd_busybox__ ?= LINK $@
655 cmd_busybox__ ?= $(srctree)/scripts/trylink $(CC) $(LDFLAGS) \
656 -o $@ \
657 -Wl,--warn-common -Wl,--sort-common -Wl,--gc-sections \
658 - -Wl,--start-group $(busybox-all) -Wl,--end-group
659 + -Wl,--start-group $(busybox-all) $(EXTRA_LIBS) -Wl,--end-group
660
661 # Generate System.map
662 quiet_cmd_sysmap = SYSMAP