add awx (awk web extension) - experimental core for a new web interface framework
[librecmc/librecmc.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