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