Yet another major rework of the BusyBox config system, using the considerably
[oweals/busybox.git] / scripts / config / zconf.y
1 %{
2 /*
3  * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
4  * Released under the terms of the GNU GPL v2.0.
5  */
6
7 #include <ctype.h>
8 #include <stdarg.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <stdbool.h>
13
14 #define printd(mask, fmt...) if (cdebug & (mask)) printf(fmt)
15
16 #define PRINTD          0x0001
17 #define DEBUG_PARSE     0x0002
18
19 int cdebug = PRINTD;
20
21 extern int zconflex(void);
22 static void zconfprint(const char *err, ...);
23 static void zconferror(const char *err);
24 static bool zconf_endtoken(int token, int starttoken, int endtoken);
25
26 struct symbol *symbol_hash[257];
27
28 #define YYERROR_VERBOSE
29 %}
30 %expect 36
31
32 %union
33 {
34         int token;
35         char *string;
36         struct symbol *symbol;
37         struct expr *expr;
38         struct menu *menu;
39 }
40
41 %token T_MAINMENU
42 %token T_MENU
43 %token T_ENDMENU
44 %token T_SOURCE
45 %token T_CHOICE
46 %token T_ENDCHOICE
47 %token T_COMMENT
48 %token T_CONFIG
49 %token T_HELP
50 %token <string> T_HELPTEXT
51 %token T_IF
52 %token T_ENDIF
53 %token T_DEPENDS
54 %token T_REQUIRES
55 %token T_OPTIONAL
56 %token T_PROMPT
57 %token T_DEFAULT
58 %token T_TRISTATE
59 %token T_BOOLEAN
60 %token T_INT
61 %token T_HEX
62 %token <string> T_WORD
63 %token <string> T_STRING
64 %token T_UNEQUAL
65 %token T_EOF
66 %token T_EOL
67 %token T_CLOSE_PAREN
68 %token T_OPEN_PAREN
69 %token T_ON
70
71 %left T_OR
72 %left T_AND
73 %left T_EQUAL T_UNEQUAL
74 %nonassoc T_NOT
75
76 %type <string> prompt
77 %type <string> source
78 %type <symbol> symbol
79 %type <expr> expr
80 %type <expr> if_expr
81 %type <token> end
82
83 %{
84 #define LKC_DIRECT_LINK
85 #include "lkc.h"
86 %}
87 %%
88 input:    /* empty */
89         | input block
90 ;
91
92 block:    common_block
93         | choice_stmt
94         | menu_stmt
95         | T_MAINMENU prompt nl_or_eof
96         | T_ENDMENU             { zconfprint("unexpected 'endmenu' statement"); }
97         | T_ENDIF               { zconfprint("unexpected 'endif' statement"); }
98         | T_ENDCHOICE           { zconfprint("unexpected 'endchoice' statement"); }
99         | error nl_or_eof       { zconfprint("syntax error"); yyerrok; }
100 ;
101
102 common_block:
103           if_stmt
104         | comment_stmt
105         | config_stmt
106         | source_stmt
107         | nl_or_eof
108 ;
109
110
111 /* config entry */
112
113 config_entry_start: T_CONFIG T_WORD
114 {
115         struct symbol *sym = sym_lookup($2, 0);
116         sym->flags |= SYMBOL_OPTIONAL;
117         menu_add_entry(sym);
118         printd(DEBUG_PARSE, "%s:%d:config %s\n", zconf_curname(), zconf_lineno(), $2);
119 };
120
121 config_stmt: config_entry_start T_EOL config_option_list
122 {
123         menu_end_entry();
124         printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno());
125 };
126
127 config_option_list:
128           /* empty */
129         | config_option_list config_option T_EOL
130         | config_option_list depends T_EOL
131         | config_option_list help
132         | config_option_list T_EOL
133 { };
134
135 config_option: T_TRISTATE prompt_stmt_opt
136 {
137         menu_set_type(S_TRISTATE);
138         printd(DEBUG_PARSE, "%s:%d:tristate\n", zconf_curname(), zconf_lineno());
139 };
140
141 config_option: T_BOOLEAN prompt_stmt_opt
142 {
143         menu_set_type(S_BOOLEAN);
144         printd(DEBUG_PARSE, "%s:%d:boolean\n", zconf_curname(), zconf_lineno());
145 };
146
147 config_option: T_INT prompt_stmt_opt
148 {
149         menu_set_type(S_INT);
150         printd(DEBUG_PARSE, "%s:%d:int\n", zconf_curname(), zconf_lineno());
151 };
152
153 config_option: T_HEX prompt_stmt_opt
154 {
155         menu_set_type(S_HEX);
156         printd(DEBUG_PARSE, "%s:%d:hex\n", zconf_curname(), zconf_lineno());
157 };
158
159 config_option: T_STRING prompt_stmt_opt
160 {
161         menu_set_type(S_STRING);
162         printd(DEBUG_PARSE, "%s:%d:string\n", zconf_curname(), zconf_lineno());
163 };
164
165 config_option: T_PROMPT prompt if_expr
166 {
167         menu_add_prop(P_PROMPT, $2, NULL, $3);
168         printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno());
169 };
170
171 config_option: T_DEFAULT symbol if_expr
172 {
173         menu_add_prop(P_DEFAULT, NULL, $2, $3);
174         printd(DEBUG_PARSE, "%s:%d:default\n", zconf_curname(), zconf_lineno());
175 };
176
177 /* choice entry */
178
179 choice: T_CHOICE
180 {
181         struct symbol *sym = sym_lookup(NULL, 0);
182         sym->flags |= SYMBOL_CHOICE;
183         menu_add_entry(sym);
184         menu_add_prop(P_CHOICE, NULL, NULL, NULL);
185         printd(DEBUG_PARSE, "%s:%d:choice\n", zconf_curname(), zconf_lineno());
186 };
187
188 choice_entry: choice T_EOL choice_option_list
189 {
190         menu_end_entry();
191         menu_add_menu();
192 };
193
194 choice_end: end
195 {
196         if (zconf_endtoken($1, T_CHOICE, T_ENDCHOICE)) {
197                 menu_end_menu();
198                 printd(DEBUG_PARSE, "%s:%d:endchoice\n", zconf_curname(), zconf_lineno());
199         }
200 };
201
202 choice_stmt:
203           choice_entry choice_block choice_end T_EOL
204         | choice_entry choice_block
205 {
206         printf("%s:%d: missing 'endchoice' for this 'choice' statement\n", current_menu->file->name, current_menu->lineno);
207         zconfnerrs++;
208 };
209
210 choice_option_list:
211           /* empty */
212         | choice_option_list choice_option T_EOL
213         | choice_option_list depends T_EOL
214         | choice_option_list help
215         | choice_option_list T_EOL
216 ;
217
218 choice_option: T_PROMPT prompt if_expr
219 {
220         menu_add_prop(P_PROMPT, $2, NULL, $3);
221         printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno());
222 };
223
224 choice_option: T_OPTIONAL
225 {
226         current_entry->sym->flags |= SYMBOL_OPTIONAL;
227         printd(DEBUG_PARSE, "%s:%d:optional\n", zconf_curname(), zconf_lineno());
228 };
229
230 choice_option: T_DEFAULT symbol
231 {
232         menu_add_prop(P_DEFAULT, NULL, $2, NULL);
233         //current_choice->prop->def = $2;
234         printd(DEBUG_PARSE, "%s:%d:default\n", zconf_curname(), zconf_lineno());
235 };
236
237 choice_block:
238           /* empty */
239         | choice_block common_block
240 ;
241
242 /* if entry */
243
244 if: T_IF expr
245 {
246         printd(DEBUG_PARSE, "%s:%d:if\n", zconf_curname(), zconf_lineno());
247         menu_add_entry(NULL);
248         //current_entry->prompt = menu_add_prop(T_IF, NULL, NULL, $2);
249         menu_add_dep($2);
250         menu_end_entry();
251         menu_add_menu();
252 };
253
254 if_end: end
255 {
256         if (zconf_endtoken($1, T_IF, T_ENDIF)) {
257                 menu_end_menu();
258                 printd(DEBUG_PARSE, "%s:%d:endif\n", zconf_curname(), zconf_lineno());
259         }
260 };
261
262 if_stmt:
263           if T_EOL if_block if_end T_EOL
264         | if T_EOL if_block
265 {
266         printf("%s:%d: missing 'endif' for this 'if' statement\n", current_menu->file->name, current_menu->lineno);
267         zconfnerrs++;
268 };
269
270 if_block:
271           /* empty */
272         | if_block common_block
273         | if_block menu_stmt
274         | if_block choice_stmt
275 ;
276
277 /* menu entry */
278
279 menu: T_MENU prompt
280 {
281         menu_add_entry(NULL);
282         menu_add_prop(P_MENU, $2, NULL, NULL);
283         printd(DEBUG_PARSE, "%s:%d:menu\n", zconf_curname(), zconf_lineno());
284 };
285
286 menu_entry: menu T_EOL depends_list
287 {
288         menu_end_entry();
289         menu_add_menu();
290 };
291
292 menu_end: end
293 {
294         if (zconf_endtoken($1, T_MENU, T_ENDMENU)) {
295                 menu_end_menu();
296                 printd(DEBUG_PARSE, "%s:%d:endmenu\n", zconf_curname(), zconf_lineno());
297         }
298 };
299
300 menu_stmt:
301           menu_entry menu_block menu_end T_EOL
302         | menu_entry menu_block
303 {
304         printf("%s:%d: missing 'endmenu' for this 'menu' statement\n", current_menu->file->name, current_menu->lineno);
305         zconfnerrs++;
306 };
307
308 menu_block:
309           /* empty */
310         | menu_block common_block
311         | menu_block menu_stmt
312         | menu_block choice_stmt
313         | menu_block error T_EOL                { zconfprint("invalid menu option"); yyerrok; }
314 ;
315
316 source: T_SOURCE prompt
317 {
318         $$ = $2;
319         printd(DEBUG_PARSE, "%s:%d:source %s\n", zconf_curname(), zconf_lineno(), $2);
320 };
321
322 source_stmt: source T_EOL
323 {
324         zconf_nextfile($1);
325 };
326
327 /* comment entry */
328
329 comment: T_COMMENT prompt
330 {
331         menu_add_entry(NULL);
332         menu_add_prop(P_COMMENT, $2, NULL, NULL);
333         printd(DEBUG_PARSE, "%s:%d:comment\n", zconf_curname(), zconf_lineno());
334 };
335
336 comment_stmt: comment T_EOL depends_list
337 {
338         menu_end_entry();
339 };
340
341 /* help option */
342
343 help_start: T_HELP T_EOL
344 {
345         printd(DEBUG_PARSE, "%s:%d:help\n", zconf_curname(), zconf_lineno());
346         zconf_starthelp();
347 };
348
349 help: help_start T_HELPTEXT
350 {
351         current_entry->sym->help = $2;
352 };
353
354 /* depends option */
355
356 depends_list:     /* empty */
357                 | depends_list depends T_EOL
358                 | depends_list T_EOL
359 { };
360
361 depends: T_DEPENDS T_ON expr
362 {
363         menu_add_dep($3);
364         printd(DEBUG_PARSE, "%s:%d:depends on\n", zconf_curname(), zconf_lineno());
365 }
366         | T_DEPENDS expr
367 {
368         menu_add_dep($2);
369         printd(DEBUG_PARSE, "%s:%d:depends\n", zconf_curname(), zconf_lineno());
370 }
371         | T_REQUIRES expr
372 {
373         menu_add_dep($2);
374         printd(DEBUG_PARSE, "%s:%d:requires\n", zconf_curname(), zconf_lineno());
375 };
376
377 /* prompt statement */
378
379 prompt_stmt_opt:
380           /* empty */
381         | prompt
382 {
383         menu_add_prop(P_PROMPT, $1, NULL, NULL);
384 }
385         | prompt T_IF expr
386 {
387         menu_add_prop(P_PROMPT, $1, NULL, $3);
388 };
389
390 prompt:   T_WORD
391         | T_STRING
392 ;
393
394 end:      T_ENDMENU             { $$ = T_ENDMENU; }
395         | T_ENDCHOICE           { $$ = T_ENDCHOICE; }
396         | T_ENDIF               { $$ = T_ENDIF; }
397 ;
398
399 nl_or_eof:
400         T_EOL | T_EOF;
401
402 if_expr:  /* empty */                   { $$ = NULL; }
403         | T_IF expr                     { $$ = $2; }
404 ;
405
406 expr:     symbol                                { $$ = expr_alloc_symbol($1); }
407         | symbol T_EQUAL symbol                 { $$ = expr_alloc_comp(E_EQUAL, $1, $3); }
408         | symbol T_UNEQUAL symbol               { $$ = expr_alloc_comp(E_UNEQUAL, $1, $3); }
409         | T_OPEN_PAREN expr T_CLOSE_PAREN       { $$ = $2; }
410         | T_NOT expr                            { $$ = expr_alloc_one(E_NOT, $2); }
411         | expr T_OR expr                        { $$ = expr_alloc_two(E_OR, $1, $3); }
412         | expr T_AND expr                       { $$ = expr_alloc_two(E_AND, $1, $3); }
413 ;
414
415 symbol:   T_WORD        { $$ = sym_lookup($1, 0); free($1); }
416         | T_STRING      { $$ = sym_lookup($1, 1); free($1); }
417 ;
418
419 %%
420
421 void conf_parse(const char *name)
422 {
423         zconf_initscan(name);
424
425         sym_init();
426         menu_init();
427         rootmenu.prompt = menu_add_prop(P_MENU, "BusyBox Configuration", NULL, NULL);
428
429         //zconfdebug = 1;
430         zconfparse();
431         if (zconfnerrs)
432                 exit(1);
433         menu_finalize(&rootmenu);
434
435         modules_sym = sym_lookup("MODULES", 0);
436
437         sym_change_count = 1;
438 }
439
440 const char *zconf_tokenname(int token)
441 {
442         switch (token) {
443         case T_MENU:            return "menu";
444         case T_ENDMENU:         return "endmenu";
445         case T_CHOICE:          return "choice";
446         case T_ENDCHOICE:       return "endchoice";
447         case T_IF:              return "if";
448         case T_ENDIF:           return "endif";
449         }
450         return "<token>";
451
452
453 static bool zconf_endtoken(int token, int starttoken, int endtoken)
454 {
455         if (token != endtoken) {
456                 zconfprint("unexpected '%s' within %s block", zconf_tokenname(token), zconf_tokenname(starttoken));
457                 zconfnerrs++;
458                 return false;
459         }
460         if (current_menu->file != current_file) {
461                 zconfprint("'%s' in different file than '%s'", zconf_tokenname(token), zconf_tokenname(starttoken));
462                 zconfprint("location of the '%s'", zconf_tokenname(starttoken));
463                 zconfnerrs++;
464                 return false;
465         }
466         return true;
467 }
468
469 static void zconfprint(const char *err, ...)
470 {
471         va_list ap;
472
473         fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno());
474         va_start(ap, err);
475         vfprintf(stderr, err, ap);
476         va_end(ap);
477         fprintf(stderr, "\n");
478 }
479
480 static void zconferror(const char *err)
481 {
482         fprintf(stderr, "%s:%d: %s\n", zconf_curname(), zconf_lineno(), err);
483 }
484
485 void print_quoted_string(FILE *out, const char *str)
486 {
487         const char *p;
488         int len;
489
490         putc('"', out);
491         while ((p = strchr(str, '"'))) {
492                 len = p - str;
493                 if (len)
494                         fprintf(out, "%.*s", len, str);
495                 fputs("\\\"", out);
496                 str = p + 1;
497         }
498         fputs(str, out);
499         putc('"', out);
500 }
501
502 void print_symbol(FILE *out, struct menu *menu)
503 {
504         struct symbol *sym = menu->sym;
505         struct property *prop;
506
507         //sym->flags |= SYMBOL_PRINTED;
508
509         if (sym_is_choice(sym))
510                 fprintf(out, "choice\n");
511         else
512                 fprintf(out, "config %s\n", sym->name);
513         switch (sym->type) {
514         case S_BOOLEAN:
515                 fputs("  boolean\n", out);
516                 break;
517         case S_TRISTATE:
518                 fputs("  tristate\n", out);
519                 break;
520         case S_STRING:
521                 fputs("  string\n", out);
522                 break;
523         case S_INT:
524                 fputs("  integer\n", out);
525                 break;
526         case S_HEX:
527                 fputs("  hex\n", out);
528                 break;
529         default:
530                 fputs("  ???\n", out);
531                 break;
532         }
533 #if 0
534         if (!expr_is_yes(sym->dep)) {
535                 fputs("  depends ", out);
536                 expr_fprint(sym->dep, out);
537                 fputc('\n', out);
538         }
539 #endif
540         for (prop = sym->prop; prop; prop = prop->next) {
541                 if (prop->menu != menu)
542                         continue;
543                 switch (prop->type) {
544                 case P_PROMPT:
545                         fputs("  prompt ", out);
546                         print_quoted_string(out, prop->text);
547                         if (prop->def) {
548                                 fputc(' ', out);
549                                 if (prop->def->flags & SYMBOL_CONST)
550                                         print_quoted_string(out, prop->def->name);
551                                 else
552                                         fputs(prop->def->name, out);
553                         }
554                         if (!expr_is_yes(E_EXPR(prop->visible))) {
555                                 fputs(" if ", out);
556                                 expr_fprint(E_EXPR(prop->visible), out);
557                         }
558                         fputc('\n', out);
559                         break;
560                 case P_DEFAULT:
561                         fputs( "  default ", out);
562                         print_quoted_string(out, prop->def->name);
563                         if (!expr_is_yes(E_EXPR(prop->visible))) {
564                                 fputs(" if ", out);
565                                 expr_fprint(E_EXPR(prop->visible), out);
566                         }
567                         fputc('\n', out);
568                         break;
569                 case P_CHOICE:
570                         fputs("  #choice value\n", out);
571                         break;
572                 default:
573                         fprintf(out, "  unknown prop %d!\n", prop->type);
574                         break;
575                 }
576         }
577         if (sym->help) {
578                 int len = strlen(sym->help);
579                 while (sym->help[--len] == '\n')
580                         sym->help[len] = 0;
581                 fprintf(out, "  help\n%s\n", sym->help);
582         }
583         fputc('\n', out);
584 }
585
586 void zconfdump(FILE *out)
587 {
588         //struct file *file;
589         struct property *prop;
590         struct symbol *sym;
591         struct menu *menu;
592
593         menu = rootmenu.list;
594         while (menu) {
595                 if ((sym = menu->sym))
596                         print_symbol(out, menu);
597                 else if ((prop = menu->prompt)) {
598                         switch (prop->type) {
599                         //case T_MAINMENU:
600                         //      fputs("\nmainmenu ", out);
601                         //      print_quoted_string(out, prop->text);
602                         //      fputs("\n", out);
603                         //      break;
604                         case P_COMMENT:
605                                 fputs("\ncomment ", out);
606                                 print_quoted_string(out, prop->text);
607                                 fputs("\n", out);
608                                 break;
609                         case P_MENU:
610                                 fputs("\nmenu ", out);
611                                 print_quoted_string(out, prop->text);
612                                 fputs("\n", out);
613                                 break;
614                         //case T_SOURCE:
615                         //      fputs("\nsource ", out);
616                         //      print_quoted_string(out, prop->text);
617                         //      fputs("\n", out);
618                         //      break;
619                         //case T_IF:
620                         //      fputs("\nif\n", out);
621                         default:
622                                 ;
623                         }
624                         if (!expr_is_yes(E_EXPR(prop->visible))) {
625                                 fputs("  depends ", out);
626                                 expr_fprint(E_EXPR(prop->visible), out);
627                                 fputc('\n', out);
628                         }
629                         fputs("\n", out);
630                 }
631
632                 if (menu->list)
633                         menu = menu->list;
634                 else if (menu->next)
635                         menu = menu->next;
636                 else while ((menu = menu->parent)) {
637                         if (menu->prompt && menu->prompt->type == P_MENU)
638                                 fputs("\nendmenu\n", out);
639                         if (menu->next) {
640                                 menu = menu->next;
641                                 break;
642                         }
643                 }
644         }
645 }
646
647 #include "lex.zconf.c"
648 #include "confdata.c"
649 #include "expr.c"
650 #include "symbol.c"
651 #include "menu.c"