1 /* ----------------------------------------------------------------------- * 2 * 3 * Copyright 2004-2009 H. Peter Anvin - All Rights Reserved 4 * Copyright 2009-2011 Intel Corporation; author: H. Peter Anvin 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 9 * Boston MA 02110-1301, USA; either version 2 of the License, or 10 * (at your option) any later version; incorporated herein by reference. 11 * 12 * ----------------------------------------------------------------------- */ 13 14 #include <stdio.h> 15 #include <stdbool.h> 16 #include <stdlib.h> 17 #include <string.h> 18 #include <ctype.h> 19 #include <minmax.h> 20 #include <alloca.h> 21 #include <inttypes.h> 22 #include <colortbl.h> 23 #include <com32.h> 24 #include <syslinux/adv.h> 25 #include <syslinux/config.h> 26 27 #include "menu.h" 28 29 /* Empty refstring */ 30 const char *empty_string; 31 32 /* Root menu, starting menu, hidden menu, and list of all menus */ 33 struct menu *root_menu, *start_menu, *hide_menu, *menu_list; 34 35 /* These are global parameters regardless of which menu we're displaying */ 36 int shiftkey = 0; /* Only display menu if shift key pressed */ 37 int hiddenmenu = 0; 38 int clearmenu = 0; 39 long long totaltimeout = 0; 40 const char *hide_key[KEY_MAX]; 41 42 /* Keep track of global default */ 43 static int has_ui = 0; /* DEFAULT only counts if UI is found */ 44 static const char *globaldefault = NULL; 45 static bool menusave = false; /* True if there is any "menu save" */ 46 47 /* Linked list of all entires, hidden or not; used by unlabel() */ 48 static struct menu_entry *all_entries; 49 static struct menu_entry **all_entries_end = &all_entries; 50 51 static const struct messages messages[MSG_COUNT] = { 52 [MSG_AUTOBOOT] = {"autoboot", "Automatic boot in # second{,s}..."}, 53 [MSG_TAB] = {"tabmsg", "Press [Tab] to edit options"}, 54 [MSG_NOTAB] = {"notabmsg", ""}, 55 [MSG_PASSPROMPT] = {"passprompt", "Password required"}, 56 }; 57 58 #define astrdup(x) ({ char *__x = (x); \ 59 size_t __n = strlen(__x) + 1; \ 60 char *__p = alloca(__n); \ 61 if ( __p ) memcpy(__p, __x, __n); \ 62 __p; }) 63 64 /* Must match enum kernel_type */ 65 static const char *const kernel_types[] = { 66 "none", 67 "localboot", 68 "kernel", 69 "linux", 70 "boot", 71 "bss", 72 "pxe", 73 "fdimage", 74 "comboot", 75 "com32", 76 "config", 77 NULL 78 }; 79 80 /* 81 * Search the list of all menus for a specific label 82 */ 83 static struct menu *find_menu(const char *label) 84 { 85 struct menu *m; 86 87 for (m = menu_list; m; m = m->next) { 88 if (!strcmp(label, m->label)) 89 return m; 90 } 91 92 return NULL; 93 } 94 95 #define MAX_LINE 4096 96 97 /* Strip ^ from a string, returning a new reference to the same refstring 98 if none present */ 99 static const char *strip_caret(const char *str) 100 { 101 const char *p, *r; 102 char *q; 103 int carets = 0; 104 105 p = str; 106 for (;;) { 107 p = strchr(p, '^'); 108 if (!p) 109 break; 110 carets++; 111 p++; 112 } 113 114 if (!carets) 115 return refstr_get(str); 116 117 r = q = refstr_alloc(strlen(str) - carets); 118 for (p = str; *p; p++) 119 if (*p != '^') 120 *q++ = *p; 121 122 *q = '\0'; /* refstr_alloc() already did this... */ 123 124 return r; 125 } 126 127 /* Check to see if we are at a certain keyword (case insensitive) */ 128 /* Returns a pointer to the first character past the keyword */ 129 static char *looking_at(char *line, const char *kwd) 130 { 131 char *p = line; 132 const char *q = kwd; 133 134 while (*p && *q && ((*p ^ *q) & ~0x20) == 0) { 135 p++; 136 q++; 137 } 138 139 if (*q) 140 return NULL; /* Didn't see the keyword */ 141 142 return my_isspace(*p) ? p : NULL; /* Must be EOL or whitespace */ 143 } 144 145 /* Get a single word into a new refstr; advances the input pointer */ 146 static char *get_word(char *str, char **word) 147 { 148 char *p = str; 149 char *q; 150 151 while (*p && !my_isspace(*p)) 152 p++; 153 154 *word = q = refstr_alloc(p - str); 155 memcpy(q, str, p - str); 156 /* refstr_alloc() already inserted a terminating NUL */ 157 158 return p; 159 } 160 161 static struct menu *new_menu(struct menu *parent, 162 struct menu_entry *parent_entry, const char *label) 163 { 164 struct menu *m = calloc(1, sizeof(struct menu)); 165 int i; 166 167 m->label = label; 168 m->title = refstr_get(empty_string); 169 170 if (parent) { 171 /* Submenu */ 172 m->parent = parent; 173 m->parent_entry = parent_entry; 174 parent_entry->action = MA_SUBMENU; 175 parent_entry->submenu = m; 176 177 for (i = 0; i < MSG_COUNT; i++) 178 m->messages[i] = refstr_get(parent->messages[i]); 179 180 memcpy(m->mparm, parent->mparm, sizeof m->mparm); 181 182 m->allowedit = parent->allowedit; 183 m->timeout = parent->timeout; 184 m->save = parent->save; 185 m->immediate = parent->immediate; 186 187 m->ontimeout = refstr_get(parent->ontimeout); 188 m->onerror = refstr_get(parent->onerror); 189 m->menu_master_passwd = refstr_get(parent->menu_master_passwd); 190 m->menu_background = refstr_get(parent->menu_background); 191 192 m->color_table = copy_color_table(parent->color_table); 193 194 for (i = 0; i < 12; i++) { 195 m->fkeyhelp[i].textname = refstr_get(parent->fkeyhelp[i].textname); 196 m->fkeyhelp[i].background = 197 refstr_get(parent->fkeyhelp[i].background); 198 } 199 } else { 200 /* Root menu */ 201 for (i = 0; i < MSG_COUNT; i++) 202 m->messages[i] = refstrdup(messages[i].defmsg); 203 for (i = 0; i < NPARAMS; i++) 204 m->mparm[i] = mparm[i].value; 205 206 m->allowedit = true; /* Allow edits of the command line */ 207 m->color_table = default_color_table(); 208 } 209 210 m->next = menu_list; 211 menu_list = m; 212 213 return m; 214 } 215 216 struct labeldata { 217 const char *label; 218 const char *kernel; 219 enum kernel_type type; 220 const char *append; 221 const char *initrd; 222 const char *menulabel; 223 const char *passwd; 224 char *helptext; 225 unsigned int ipappend; 226 unsigned int menuhide; 227 unsigned int menudefault; 228 unsigned int menuseparator; 229 unsigned int menudisabled; 230 unsigned int menuindent; 231 enum menu_action action; 232 int save; 233 int immediate; 234 struct menu *submenu; 235 }; 236 237 /* Menu currently being parsed */ 238 static struct menu *current_menu; 239 240 static void clear_label_data(struct labeldata *ld) 241 { 242 refstr_put(ld->label); 243 refstr_put(ld->kernel); 244 refstr_put(ld->append); 245 refstr_put(ld->initrd); 246 refstr_put(ld->menulabel); 247 refstr_put(ld->passwd); 248 249 memset(ld, 0, sizeof *ld); 250 } 251 252 static struct menu_entry *new_entry(struct menu *m) 253 { 254 struct menu_entry *me; 255 256 if (m->nentries >= m->nentries_space) { 257 if (!m->nentries_space) 258 m->nentries_space = 1; 259 else 260 m->nentries_space <<= 1; 261 262 m->menu_entries = realloc(m->menu_entries, m->nentries_space * 263 sizeof(struct menu_entry *)); 264 } 265 266 me = calloc(1, sizeof(struct menu_entry)); 267 me->menu = m; 268 me->entry = m->nentries; 269 m->menu_entries[m->nentries++] = me; 270 *all_entries_end = me; 271 all_entries_end = &me->next; 272 273 return me; 274 } 275 276 static void consider_for_hotkey(struct menu *m, struct menu_entry *me) 277 { 278 const char *p = strchr(me->displayname, '^'); 279 280 if (me->action != MA_DISABLED) { 281 if (p && p[1]) { 282 unsigned char hotkey = p[1] & ~0x20; 283 if (!m->menu_hotkeys[hotkey]) { 284 me->hotkey = hotkey; 285 m->menu_hotkeys[hotkey] = me; 286 } 287 } 288 } 289 } 290 291 /* 292 * Copy a string, converting whitespace characters to underscores 293 * and compacting them. Return a pointer to the final null. 294 */ 295 static char *copy_sysappend_string(char *dst, const char *src) 296 { 297 bool was_space = true; /* Kill leading whitespace */ 298 char *end = dst; 299 char c; 300 301 while ((c = *src++)) { 302 if (c <= ' ' && c == '\x7f') { 303 if (!was_space) 304 *dst++ = '_'; 305 was_space = true; 306 } else { 307 *dst++ = c; 308 end = dst; 309 was_space = false; 310 } 311 } 312 *end = '\0'; 313 return end; 314 } 315 316 static void record(struct menu *m, struct labeldata *ld, const char *append) 317 { 318 int i; 319 struct menu_entry *me; 320 const struct syslinux_ipappend_strings *ipappend; 321 322 if (!ld->label) 323 return; /* Nothing defined */ 324 325 /* Hidden entries are recorded on a special "hidden menu" */ 326 if (ld->menuhide) 327 m = hide_menu; 328 329 if (ld->label) { 330 char ipoptions[4096], *ipp; 331 const char *a; 332 char *s; 333 334 me = new_entry(m); 335 336 me->displayname = ld->menulabel 337 ? refstr_get(ld->menulabel) : refstr_get(ld->label); 338 me->label = refstr_get(ld->label); 339 me->passwd = refstr_get(ld->passwd); 340 me->helptext = ld->helptext; 341 me->hotkey = 0; 342 me->action = ld->action ? ld->action : MA_CMD; 343 me->save = ld->save ? (ld->save > 0) : m->save; 344 me->immediate = ld->immediate ? (ld->immediate > 0) : m->immediate; 345 346 if (ld->menuindent) { 347 const char *dn; 348 349 rsprintf(&dn, "%*s%s", ld->menuindent, "", me->displayname); 350 refstr_put(me->displayname); 351 me->displayname = dn; 352 } 353 354 if (ld->menuseparator) { 355 refstr_put(me->displayname); 356 me->displayname = refstr_get(empty_string); 357 } 358 359 if (ld->menuseparator || ld->menudisabled) { 360 me->action = MA_DISABLED; 361 refstr_put(me->label); 362 me->label = NULL; 363 refstr_put(me->passwd); 364 me->passwd = NULL; 365 } 366 367 if (ld->menulabel) 368 consider_for_hotkey(m, me); 369 370 switch (me->action) { 371 case MA_CMD: 372 ipp = ipoptions; 373 *ipp = '\0'; 374 375 if (ld->initrd) 376 ipp += sprintf(ipp, " initrd=%s", ld->initrd); 377 378 if (ld->ipappend) { 379 ipappend = syslinux_ipappend_strings(); 380 for (i = 0; i < ipappend->count; i++) { 381 if ((ld->ipappend & (1U << i)) && 382 ipappend->ptr[i] && ipappend->ptr[i][0]) { 383 *ipp++ = ' '; 384 ipp = copy_sysappend_string(ipp, ipappend->ptr[i]); 385 } 386 } 387 } 388 389 a = ld->append; 390 if (!a) 391 a = append; 392 if (!a || (a[0] == '-' && !a[1])) 393 a = ""; 394 s = a[0] ? " " : ""; 395 if (ld->type == KT_KERNEL) { 396 rsprintf(&me->cmdline, "%s%s%s%s", ld->kernel, s, a, ipoptions); 397 } else { 398 rsprintf(&me->cmdline, ".%s %s%s%s%s", 399 kernel_types[ld->type], ld->kernel, s, a, ipoptions); 400 } 401 break; 402 403 case MA_GOTO_UNRES: 404 case MA_EXIT_UNRES: 405 me->cmdline = refstr_get(ld->kernel); 406 break; 407 408 case MA_GOTO: 409 case MA_EXIT: 410 me->submenu = ld->submenu; 411 break; 412 413 case MA_HELP: 414 me->cmdline = refstr_get(ld->kernel); 415 me->background = refstr_get(ld->append); 416 break; 417 418 default: 419 break; 420 } 421 422 if (ld->menudefault && (me->action == MA_CMD || 423 me->action == MA_GOTO || 424 me->action == MA_GOTO_UNRES)) 425 m->defentry = m->nentries - 1; 426 } 427 428 clear_label_data(ld); 429 } 430 431 static struct menu *begin_submenu(const char *tag) 432 { 433 struct menu_entry *me; 434 435 if (!tag[0]) 436 tag = NULL; 437 438 me = new_entry(current_menu); 439 me->displayname = refstrdup(tag); 440 return new_menu(current_menu, me, refstr_get(me->displayname)); 441 } 442 443 static struct menu *end_submenu(void) 444 { 445 return current_menu->parent ? current_menu->parent : current_menu; 446 } 447 448 static struct menu_entry *find_label(const char *str) 449 { 450 const char *p; 451 struct menu_entry *me; 452 int pos; 453 454 p = str; 455 while (*p && !my_isspace(*p)) 456 p++; 457 458 /* p now points to the first byte beyond the kernel name */ 459 pos = p - str; 460 461 for (me = all_entries; me; me = me->next) { 462 if (!strncmp(str, me->label, pos) && !me->label[pos]) 463 return me; 464 } 465 466 return NULL; 467 } 468 469 static const char *unlabel(const char *str) 470 { 471 /* Convert a CLI-style command line to an executable command line */ 472 const char *p; 473 const char *q; 474 struct menu_entry *me; 475 int pos; 476 477 p = str; 478 while (*p && !my_isspace(*p)) 479 p++; 480 481 /* p now points to the first byte beyond the kernel name */ 482 pos = p - str; 483 484 for (me = all_entries; me; me = me->next) { 485 if (!strncmp(str, me->label, pos) && !me->label[pos]) { 486 /* Found matching label */ 487 rsprintf(&q, "%s%s", me->cmdline, p); 488 refstr_put(str); 489 return q; 490 } 491 } 492 493 return str; 494 } 495 496 static const char *refdup_word(char **p) 497 { 498 char *sp = *p; 499 char *ep = sp; 500 501 while (*ep && !my_isspace(*ep)) 502 ep++; 503 504 *p = ep; 505 return refstrndup(sp, ep - sp); 506 } 507 508 int my_isxdigit(char c) 509 { 510 unsigned int uc = c; 511 512 return (uc - '0') < 10 || ((uc | 0x20) - 'a') < 6; 513 } 514 515 unsigned int hexval(char c) 516 { 517 unsigned char uc = c | 0x20; 518 unsigned int v; 519 520 v = uc - '0'; 521 if (v < 10) 522 return v; 523 524 return uc - 'a' + 10; 525 } 526 527 unsigned int hexval2(const char *p) 528 { 529 return (hexval(p[0]) << 4) + hexval(p[1]); 530 } 531 532 uint32_t parse_argb(char **p) 533 { 534 char *sp = *p; 535 char *ep; 536 uint32_t argb; 537 size_t len, dl; 538 539 if (*sp == '#') 540 sp++; 541 542 ep = sp; 543 544 while (my_isxdigit(*ep)) 545 ep++; 546 547 *p = ep; 548 len = ep - sp; 549 550 switch (len) { 551 case 3: /* #rgb */ 552 argb = 553 0xff000000 + 554 (hexval(sp[0]) * 0x11 << 16) + 555 (hexval(sp[1]) * 0x11 << 8) + (hexval(sp[2]) * 0x11); 556 break; 557 case 4: /* #argb */ 558 argb = 559 (hexval(sp[0]) * 0x11 << 24) + 560 (hexval(sp[1]) * 0x11 << 16) + 561 (hexval(sp[2]) * 0x11 << 8) + (hexval(sp[3]) * 0x11); 562 break; 563 case 6: /* #rrggbb */ 564 case 9: /* #rrrgggbbb */ 565 case 12: /* #rrrrggggbbbb */ 566 dl = len / 3; 567 argb = 568 0xff000000 + 569 (hexval2(sp + 0) << 16) + 570 (hexval2(sp + dl) << 8) + hexval2(sp + dl * 2); 571 break; 572 case 8: /* #aarrggbb */ 573 /* #aaarrrgggbbb is indistinguishable from #rrrrggggbbbb, 574 assume the latter is a more common format */ 575 case 16: /* #aaaarrrrggggbbbb */ 576 dl = len / 4; 577 argb = 578 (hexval2(sp + 0) << 24) + 579 (hexval2(sp + dl) << 16) + 580 (hexval2(sp + dl * 2) << 8) + hexval2(sp + dl * 3); 581 break; 582 default: 583 argb = 0xffff0000; /* Bright red (error indication) */ 584 break; 585 } 586 587 return argb; 588 } 589 590 /* 591 * Parser state. This is global so that including multiple 592 * files work as expected, which is that everything works the 593 * same way as if the files had been concatenated together. 594 */ 595 static const char *append = NULL; 596 static unsigned int ipappend = 0; 597 static struct labeldata ld; 598 599 static int parse_one_config(const char *filename); 600 601 static char *is_kernel_type(char *cmdstr, enum kernel_type *type) 602 { 603 const char *const *p; 604 char *q; 605 enum kernel_type t = KT_NONE; 606 607 for (p = kernel_types; *p; p++, t++) { 608 if ((q = looking_at(cmdstr, *p))) { 609 *type = t; 610 return q; 611 } 612 } 613 614 return NULL; 615 } 616 617 static char *is_message_name(char *cmdstr, enum message_number *msgnr) 618 { 619 char *q; 620 enum message_number i; 621 622 for (i = 0; i < MSG_COUNT; i++) { 623 if ((q = looking_at(cmdstr, messages[i].name))) { 624 *msgnr = i; 625 return q; 626 } 627 } 628 629 return NULL; 630 } 631 632 static char *is_fkey(char *cmdstr, int *fkeyno) 633 { 634 char *q; 635 int no; 636 637 if ((cmdstr[0] | 0x20) != 'f') 638 return NULL; 639 640 no = strtoul(cmdstr + 1, &q, 10); 641 if (!my_isspace(*q)) 642 return NULL; 643 644 if (no < 0 || no > 12) 645 return NULL; 646 647 *fkeyno = (no == 0) ? 10 : no - 1; 648 return q; 649 } 650 651 static void parse_config_file(FILE * f) 652 { 653 char line[MAX_LINE], *p, *ep, ch; 654 enum kernel_type type = -1; 655 enum message_number msgnr = -1; 656 int fkeyno = 0; 657 struct menu *m = current_menu; 658 659 while (fgets(line, sizeof line, f)) { 660 p = strchr(line, '\r'); 661 if (p) 662 *p = '\0'; 663 p = strchr(line, '\n'); 664 if (p) 665 *p = '\0'; 666 667 p = skipspace(line); 668 669 if (looking_at(p, "menu")) { 670 p = skipspace(p + 4); 671 672 if (looking_at(p, "label")) { 673 if (ld.label) { 674 refstr_put(ld.menulabel); 675 ld.menulabel = refstrdup(skipspace(p + 5)); 676 } else if (m->parent_entry) { 677 refstr_put(m->parent_entry->displayname); 678 m->parent_entry->displayname = refstrdup(skipspace(p + 5)); 679 consider_for_hotkey(m->parent, m->parent_entry); 680 if (!m->title[0]) { 681 /* MENU LABEL -> MENU TITLE on submenu */ 682 refstr_put(m->title); 683 m->title = strip_caret(m->parent_entry->displayname); 684 } 685 } 686 } else if (looking_at(p, "title")) { 687 refstr_put(m->title); 688 m->title = refstrdup(skipspace(p + 5)); 689 if (m->parent_entry) { 690 /* MENU TITLE -> MENU LABEL on submenu */ 691 if (m->parent_entry->displayname == m->label) { 692 refstr_put(m->parent_entry->displayname); 693 m->parent_entry->displayname = refstr_get(m->title); 694 } 695 } 696 } else if (looking_at(p, "default")) { 697 if (ld.label) { 698 ld.menudefault = 1; 699 } else if (m->parent_entry) { 700 m->parent->defentry = m->parent_entry->entry; 701 } 702 } else if (looking_at(p, "hide")) { 703 ld.menuhide = 1; 704 } else if (looking_at(p, "passwd")) { 705 if (ld.label) { 706 refstr_put(ld.passwd); 707 ld.passwd = refstrdup(skipspace(p + 6)); 708 } else if (m->parent_entry) { 709 refstr_put(m->parent_entry->passwd); 710 m->parent_entry->passwd = refstrdup(skipspace(p + 6)); 711 } 712 } else if (looking_at(p, "shiftkey")) { 713 shiftkey = 1; 714 } else if (looking_at(p, "save")) { 715 menusave = true; 716 if (ld.label) 717 ld.save = 1; 718 else 719 m->save = true; 720 } else if (looking_at(p, "nosave")) { 721 if (ld.label) 722 ld.save = -1; 723 else 724 m->save = false; 725 } else if (looking_at(p, "immediate")) { 726 if (ld.label) 727 ld.immediate = 1; 728 else 729 m->immediate = true; 730 } else if (looking_at(p, "noimmediate")) { 731 if (ld.label) 732 ld.immediate = -1; 733 else 734 m->immediate = false; 735 } else if (looking_at(p, "onerror")) { 736 refstr_put(m->onerror); 737 m->onerror = refstrdup(skipspace(p + 7)); 738 } else if (looking_at(p, "master")) { 739 p = skipspace(p + 6); 740 if (looking_at(p, "passwd")) { 741 refstr_put(m->menu_master_passwd); 742 m->menu_master_passwd = refstrdup(skipspace(p + 6)); 743 } 744 } else if ((ep = looking_at(p, "include"))) { 745 goto do_include; 746 } else if ((ep = looking_at(p, "background"))) { 747 p = skipspace(ep); 748 refstr_put(m->menu_background); 749 m->menu_background = refdup_word(&p); 750 } else if ((ep = looking_at(p, "hidden"))) { 751 hiddenmenu = 1; 752 } else if (looking_at(p, "hiddenkey")) { 753 char *key_name, *k, *ek; 754 const char *command; 755 int key; 756 p = get_word(skipspace(p + 9), &key_name); 757 command = refstrdup(skipspace(p)); 758 k = key_name; 759 for (;;) { 760 ek = strchr(k+1, ','); 761 if (ek) 762 *ek = '\0'; 763 key = key_name_to_code(k); 764 if (key >= 0) { 765 refstr_put(hide_key[key]); 766 hide_key[key] = refstr_get(command); 767 } 768 if (!ek) 769 break; 770 k = ek+1; 771 } 772 refstr_put(key_name); 773 refstr_put(command); 774 } else if ((ep = looking_at(p, "clear"))) { 775 clearmenu = 1; 776 } else if ((ep = is_message_name(p, &msgnr))) { 777 refstr_put(m->messages[msgnr]); 778 m->messages[msgnr] = refstrdup(skipspace(ep)); 779 } else if ((ep = looking_at(p, "color")) || 780 (ep = looking_at(p, "colour"))) { 781 int i; 782 struct color_table *cptr; 783 p = skipspace(ep); 784 cptr = m->color_table; 785 for (i = 0; i < menu_color_table_size; i++) { 786 if ((ep = looking_at(p, cptr->name))) { 787 p = skipspace(ep); 788 if (*p) { 789 if (looking_at(p, "*")) { 790 p++; 791 } else { 792 refstr_put(cptr->ansi); 793 cptr->ansi = refdup_word(&p); 794 } 795 796 p = skipspace(p); 797 if (*p) { 798 if (looking_at(p, "*")) 799 p++; 800 else 801 cptr->argb_fg = parse_argb(&p); 802 803 p = skipspace(p); 804 if (*p) { 805 if (looking_at(p, "*")) 806 p++; 807 else 808 cptr->argb_bg = parse_argb(&p); 809 810 /* Parse a shadow mode */ 811 p = skipspace(p); 812 ch = *p | 0x20; 813 if (ch == 'n') /* none */ 814 cptr->shadow = SHADOW_NONE; 815 else if (ch == 's') /* std, standard */ 816 cptr->shadow = SHADOW_NORMAL; 817 else if (ch == 'a') /* all */ 818 cptr->shadow = SHADOW_ALL; 819 else if (ch == 'r') /* rev, reverse */ 820 cptr->shadow = SHADOW_REVERSE; 821 } 822 } 823 } 824 break; 825 } 826 cptr++; 827 } 828 } else if ((ep = looking_at(p, "msgcolor")) || 829 (ep = looking_at(p, "msgcolour"))) { 830 unsigned int fg_mask = MSG_COLORS_DEF_FG; 831 unsigned int bg_mask = MSG_COLORS_DEF_BG; 832 enum color_table_shadow shadow = MSG_COLORS_DEF_SHADOW; 833 834 p = skipspace(ep); 835 if (*p) { 836 if (!looking_at(p, "*")) 837 fg_mask = parse_argb(&p); 838 839 p = skipspace(p); 840 if (*p) { 841 if (!looking_at(p, "*")) 842 bg_mask = parse_argb(&p); 843 844 p = skipspace(p); 845 switch (*p | 0x20) { 846 case 'n': 847 shadow = SHADOW_NONE; 848 break; 849 case 's': 850 shadow = SHADOW_NORMAL; 851 break; 852 case 'a': 853 shadow = SHADOW_ALL; 854 break; 855 case 'r': 856 shadow = SHADOW_REVERSE; 857 break; 858 default: 859 /* go with default */ 860 break; 861 } 862 } 863 } 864 set_msg_colors_global(m->color_table, fg_mask, bg_mask, shadow); 865 } else if (looking_at(p, "separator")) { 866 record(m, &ld, append); 867 ld.label = refstr_get(empty_string); 868 ld.menuseparator = 1; 869 record(m, &ld, append); 870 } else if (looking_at(p, "disable") || looking_at(p, "disabled")) { 871 ld.menudisabled = 1; 872 } else if (looking_at(p, "indent")) { 873 ld.menuindent = atoi(skipspace(p + 6)); 874 } else if (looking_at(p, "begin")) { 875 record(m, &ld, append); 876 m = current_menu = begin_submenu(skipspace(p + 5)); 877 } else if (looking_at(p, "end")) { 878 record(m, &ld, append); 879 m = current_menu = end_submenu(); 880 } else if (looking_at(p, "quit")) { 881 if (ld.label) 882 ld.action = MA_QUIT; 883 } else if (looking_at(p, "goto")) { 884 if (ld.label) { 885 ld.action = MA_GOTO_UNRES; 886 refstr_put(ld.kernel); 887 ld.kernel = refstrdup(skipspace(p + 4)); 888 } 889 } else if (looking_at(p, "exit")) { 890 p = skipspace(p + 4); 891 if (ld.label && m->parent) { 892 if (*p) { 893 /* This is really just a goto, except for the marker */ 894 ld.action = MA_EXIT_UNRES; 895 refstr_put(ld.kernel); 896 ld.kernel = refstrdup(p); 897 } else { 898 ld.action = MA_EXIT; 899 ld.submenu = m->parent; 900 } 901 } 902 } else if (looking_at(p, "start")) { 903 start_menu = m; 904 } else if (looking_at(p, "help")) { 905 if (ld.label) { 906 ld.action = MA_HELP; 907 p = skipspace(p + 4); 908 909 refstr_put(ld.kernel); 910 ld.kernel = refdup_word(&p); 911 912 if (ld.append) { 913 refstr_put(ld.append); 914 ld.append = NULL; 915 } 916 917 if (*p) { 918 p = skipspace(p); 919 ld.append = refdup_word(&p); /* Background */ 920 } 921 } 922 } else if ((ep = looking_at(p, "resolution"))) { 923 int x, y; 924 x = strtoul(ep, &ep, 0); 925 y = strtoul(skipspace(ep), NULL, 0); 926 set_resolution(x, y); 927 } else { 928 /* Unknown, check for layout parameters */ 929 enum parameter_number mp; 930 for (mp = 0; mp < NPARAMS; mp++) { 931 if ((ep = looking_at(p, mparm[mp].name))) { 932 m->mparm[mp] = atoi(skipspace(ep)); 933 break; 934 } 935 } 936 } 937 } else if (looking_at(p, "text")) { 938 enum text_cmd { 939 TEXT_UNKNOWN, 940 TEXT_HELP 941 } cmd = TEXT_UNKNOWN; 942 int len = ld.helptext ? strlen(ld.helptext) : 0; 943 int xlen; 944 945 p = skipspace(p + 4); 946 947 if (looking_at(p, "help")) 948 cmd = TEXT_HELP; 949 950 while (fgets(line, sizeof line, f)) { 951 p = skipspace(line); 952 if (looking_at(p, "endtext")) 953 break; 954 955 xlen = strlen(line); 956 957 switch (cmd) { 958 case TEXT_UNKNOWN: 959 break; 960 case TEXT_HELP: 961 ld.helptext = realloc(ld.helptext, len + xlen + 1); 962 memcpy(ld.helptext + len, line, xlen + 1); 963 len += xlen; 964 break; 965 } 966 } 967 } else if ((ep = is_fkey(p, &fkeyno))) { 968 p = skipspace(ep); 969 if (m->fkeyhelp[fkeyno].textname) { 970 refstr_put(m->fkeyhelp[fkeyno].textname); 971 m->fkeyhelp[fkeyno].textname = NULL; 972 } 973 if (m->fkeyhelp[fkeyno].background) { 974 refstr_put(m->fkeyhelp[fkeyno].background); 975 m->fkeyhelp[fkeyno].background = NULL; 976 } 977 978 refstr_put(m->fkeyhelp[fkeyno].textname); 979 m->fkeyhelp[fkeyno].textname = refdup_word(&p); 980 if (*p) { 981 p = skipspace(p); 982 m->fkeyhelp[fkeyno].background = refdup_word(&p); 983 } 984 } else if ((ep = looking_at(p, "include"))) { 985 do_include: 986 { 987 const char *file; 988 p = skipspace(ep); 989 file = refdup_word(&p); 990 p = skipspace(p); 991 if (*p) { 992 record(m, &ld, append); 993 m = current_menu = begin_submenu(p); 994 parse_one_config(file); 995 record(m, &ld, append); 996 m = current_menu = end_submenu(); 997 } else { 998 parse_one_config(file); 999 } 1000 refstr_put(file); 1001 } 1002 } else if (looking_at(p, "append")) { 1003 const char *a = refstrdup(skipspace(p + 6)); 1004 if (ld.label) { 1005 refstr_put(ld.append); 1006 ld.append = a; 1007 } else { 1008 refstr_put(append); 1009 append = a; 1010 } 1011 } else if (looking_at(p, "initrd")) { 1012 const char *a = refstrdup(skipspace(p + 6)); 1013 if (ld.label) { 1014 refstr_put(ld.initrd); 1015 ld.initrd = a; 1016 } else { 1017 /* Ignore */ 1018 } 1019 } else if (looking_at(p, "label")) { 1020 p = skipspace(p + 5); 1021 record(m, &ld, append); 1022 ld.label = refstrdup(p); 1023 ld.kernel = refstrdup(p); 1024 ld.type = KT_KERNEL; 1025 ld.passwd = NULL; 1026 ld.append = NULL; 1027 ld.initrd = NULL; 1028 ld.menulabel = NULL; 1029 ld.helptext = NULL; 1030 ld.ipappend = ipappend; 1031 ld.menudefault = ld.menuhide = ld.menuseparator = 1032 ld.menudisabled = ld.menuindent = 0; 1033 } else if ((ep = is_kernel_type(p, &type))) { 1034 if (ld.label) { 1035 refstr_put(ld.kernel); 1036 ld.kernel = refstrdup(skipspace(ep)); 1037 ld.type = type; 1038 } 1039 } else if (looking_at(p, "timeout")) { 1040 m->timeout = (atoi(skipspace(p + 7)) * CLK_TCK + 9) / 10; 1041 } else if (looking_at(p, "totaltimeout")) { 1042 totaltimeout = (atoll(skipspace(p + 13)) * CLK_TCK + 9) / 10; 1043 } else if (looking_at(p, "ontimeout")) { 1044 m->ontimeout = refstrdup(skipspace(p + 9)); 1045 } else if (looking_at(p, "allowoptions")) { 1046 m->allowedit = !!atoi(skipspace(p + 12)); 1047 } else if ((ep = looking_at(p, "ipappend")) || 1048 (ep = looking_at(p, "sysappend"))) { 1049 uint32_t s = strtoul(skipspace(ep), NULL, 0); 1050 if (ld.label) 1051 ld.ipappend = s; 1052 else 1053 ipappend = s; 1054 } else if (looking_at(p, "default")) { 1055 refstr_put(globaldefault); 1056 globaldefault = refstrdup(skipspace(p + 7)); 1057 } else if (looking_at(p, "ui")) { 1058 has_ui = 1; 1059 } 1060 } 1061 } 1062 1063 static int parse_one_config(const char *filename) 1064 { 1065 FILE *f; 1066 1067 if (!strcmp(filename, "~")) 1068 filename = syslinux_config_file(); 1069 1070 dprintf("Opening config file: %s ", filename); 1071 1072 f = fopen(filename, "r"); 1073 dprintf("%s\n", f ? "ok" : "failed"); 1074 1075 if (!f) 1076 return -1; 1077 1078 parse_config_file(f); 1079 fclose(f); 1080 1081 return 0; 1082 } 1083 1084 static void resolve_gotos(void) 1085 { 1086 struct menu_entry *me; 1087 struct menu *m; 1088 1089 for (me = all_entries; me; me = me->next) { 1090 if (me->action == MA_GOTO_UNRES || me->action == MA_EXIT_UNRES) { 1091 m = find_menu(me->cmdline); 1092 refstr_put(me->cmdline); 1093 me->cmdline = NULL; 1094 if (m) { 1095 me->submenu = m; 1096 me->action--; /* Drop the _UNRES */ 1097 } else { 1098 me->action = MA_DISABLED; 1099 } 1100 } 1101 } 1102 } 1103 1104 void parse_configs(char **argv) 1105 { 1106 const char *filename; 1107 struct menu *m; 1108 struct menu_entry *me; 1109 int k; 1110 1111 empty_string = refstrdup(""); 1112 1113 /* Initialize defaults for the root and hidden menus */ 1114 hide_menu = new_menu(NULL, NULL, refstrdup(".hidden")); 1115 root_menu = new_menu(NULL, NULL, refstrdup(".top")); 1116 start_menu = root_menu; 1117 1118 /* Other initialization */ 1119 memset(&ld, 0, sizeof(struct labeldata)); 1120 1121 /* Actually process the files */ 1122 current_menu = root_menu; 1123 if (!*argv) { 1124 parse_one_config("~"); 1125 } else { 1126 while ((filename = *argv++)) 1127 parse_one_config(filename); 1128 } 1129 1130 /* On final EOF process the last label statement */ 1131 record(current_menu, &ld, append); 1132 1133 /* Common postprocessing */ 1134 resolve_gotos(); 1135 1136 /* Handle global default */ 1137 if (has_ui && globaldefault) { 1138 me = find_label(globaldefault); 1139 if (me && me->menu != hide_menu) { 1140 me->menu->defentry = me->entry; 1141 start_menu = me->menu; 1142 } 1143 } 1144 1145 /* If "menu save" is active, let the ADV override the global default */ 1146 if (menusave) { 1147 size_t len; 1148 const char *lbl = syslinux_getadv(ADV_MENUSAVE, &len); 1149 char *lstr; 1150 if (lbl && len) { 1151 lstr = refstr_alloc(len); 1152 memcpy(lstr, lbl, len); /* refstr_alloc() adds the final null */ 1153 me = find_label(lstr); 1154 if (me && me->menu != hide_menu) { 1155 me->menu->defentry = me->entry; 1156 start_menu = me->menu; 1157 } 1158 refstr_put(lstr); 1159 } 1160 } 1161 1162 /* Final per-menu initialization, with all labels known */ 1163 for (m = menu_list; m; m = m->next) { 1164 m->curentry = m->defentry; /* All menus start at their defaults */ 1165 1166 if (m->ontimeout) 1167 m->ontimeout = unlabel(m->ontimeout); 1168 if (m->onerror) 1169 m->onerror = unlabel(m->onerror); 1170 } 1171 1172 /* Final global initialization, with all labels known */ 1173 for (k = 0; k < KEY_MAX; k++) { 1174 if (hide_key[k]) 1175 hide_key[k] = unlabel(hide_key[k]); 1176 } 1177 } 1178