Adding upstream version 3.61+dfsg.
[syslinux-debian/hramrach.git] / com32 / modules / readconfig.c
blob5de9a7da6c75bf4ff676a474719af426e0ef262d
1 /* ----------------------------------------------------------------------- *
3 * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
8 * Boston MA 02111-1307, USA; either version 2 of the License, or
9 * (at your option) any later version; incorporated herein by reference.
11 * ----------------------------------------------------------------------- */
13 #define _GNU_SOURCE /* Needed for asprintf() on Linux */
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <minmax.h>
18 #include <alloca.h>
19 #include <inttypes.h>
20 #include <colortbl.h>
21 #ifdef __COM32__
22 # include <com32.h>
23 #endif
25 #include "menu.h"
27 int nentries = 0;
28 int nhidden = 0;
29 int defentry = 0;
30 int allowedit = 1; /* Allow edits of the command line */
31 int timeout = 0;
32 int shiftkey = 0; /* Only display menu if shift key pressed */
33 int hiddenmenu = 0;
34 long long totaltimeout = 0;
36 char *ontimeout = NULL;
37 char *onerror = NULL;
39 char *menu_master_passwd = NULL;
40 char *menu_background = NULL;
42 struct fkey_help fkeyhelp[12];
44 struct menu_entry menu_entries[MAX_ENTRIES];
45 struct menu_entry hide_entries[MAX_ENTRIES];
46 struct menu_entry *menu_hotkeys[256];
48 struct messages messages[MSG_COUNT] = {
49 [MSG_TITLE] =
50 { "title", "", NULL },
51 [MSG_AUTOBOOT] =
52 { "autoboot", "Automatic boot in # second{,s}...", NULL },
53 [MSG_TAB] =
54 { "tabmsg", "Press [Tab] to edit options", NULL },
55 [MSG_NOTAB] =
56 { "notabmsg", "", NULL },
57 [MSG_PASSPROMPT] =
58 { "passprompt", "Password required", NULL },
61 #define astrdup(x) ({ char *__x = (x); \
62 size_t __n = strlen(__x) + 1; \
63 char *__p = alloca(__n); \
64 if ( __p ) memcpy(__p, __x, __n); \
65 __p; })
67 /* Must match enum kernel_type */
68 const char *kernel_types[] = {
69 "none",
70 "localboot",
71 "kernel",
72 "linux",
73 "boot",
74 "bss",
75 "pxe",
76 "fdimage",
77 "comboot",
78 "com32",
79 "config",
80 NULL
83 const char *ipappends[32];
85 static void
86 get_ipappend(void)
88 #ifdef __COM32__
89 static com32sys_t r;
90 uint16_t *ipp;
91 int i;
92 int nipappends;
94 r.eax.w[0] = 0x000F;
95 __intcall(0x22, &r, &r);
97 nipappends = min(r.ecx.w[0], 32);
98 ipp = MK_PTR(r.es, r.ebx.w[0]);
99 for ( i = 0 ; i < nipappends ; i++ ) {
100 ipappends[i] = MK_PTR(r.es, *ipp++);
102 #else
103 ipappends[0] = "ip=foo:bar:baz:quux";
104 ipappends[1] = "BOOTIF=01-aa-bb-cc-dd-ee-ff";
105 #endif
108 static const char *
109 get_config(void)
111 #ifdef __COM32__
112 static com32sys_t r;
114 r.eax.w[0] = 0x000E;
115 __intcall(0x22, &r, &r);
117 return MK_PTR(r.es, r.ebx.w[0]);
118 #else
119 return "syslinux.cfg"; /* Dummy default name */
120 #endif
123 #define MAX_LINE 512
125 static char *
126 skipspace(char *p)
128 while (*p && my_isspace(*p))
129 p++;
131 return p;
134 /* Check to see if we are at a certain keyword (case insensitive) */
135 /* Returns a pointer to the first character past the keyword */
136 static char *
137 looking_at(char *line, const char *kwd)
139 char *p = line;
140 const char *q = kwd;
142 while ( *p && *q && ((*p^*q) & ~0x20) == 0 ) {
143 p++;
144 q++;
147 if ( *q )
148 return NULL; /* Didn't see the keyword */
150 return my_isspace(*p) ? p : NULL; /* Must be EOL or whitespace */
153 struct labeldata {
154 char *label;
155 char *kernel;
156 enum kernel_type type;
157 char *append;
158 char *menulabel;
159 char *passwd;
160 char *helptext;
161 unsigned int ipappend;
162 unsigned int menuhide;
163 unsigned int menudefault;
164 unsigned int menuseparator;
165 unsigned int menudisabled;
166 unsigned int menuindent;
169 static void
170 record(struct labeldata *ld, char *append)
172 char ipoptions[256], *ipp;
173 int i;
174 struct menu_entry *me = &menu_entries[nentries];
176 if ( ld->label ) {
177 char *a, *s;
178 me->displayname = ld->menulabel ? ld->menulabel : ld->label;
179 me->label = ld->label;
180 me->passwd = ld->passwd;
181 me->helptext = ld->helptext;
182 me->hotkey = 0;
183 me->disabled = 0;
185 if ( ld->menuindent ) {
186 char *n = (char *)malloc(ld->menuindent + strlen(me->displayname) + 1);
187 memset(n, 32, ld->menuindent);
188 strcpy(n + ld->menuindent, me->displayname);
189 me->displayname = n;
192 if ( ld->menulabel ) {
193 unsigned char *p = (unsigned char *)strchr(ld->menulabel, '^');
194 if ( p && p[1] ) {
195 int hotkey = p[1] & ~0x20;
196 if ( !menu_hotkeys[hotkey] ) {
197 me->hotkey = hotkey;
202 ipp = ipoptions;
203 *ipp = '\0';
204 for ( i = 0 ; i < 32 ; i++ ) {
205 if ( (ld->ipappend & (1U << i)) && ipappends[i] )
206 ipp += sprintf(ipp, " %s", ipappends[i]);
209 a = ld->append;
210 if ( !a ) a = append;
211 if ( !a || (a[0] == '-' && !a[1]) ) a = "";
212 s = a[0] ? " " : "";
213 if (ld->type == KT_KERNEL) {
214 asprintf(&me->cmdline, "%s%s%s%s",
215 ld->kernel, s, a, ipoptions);
216 } else {
217 asprintf(&me->cmdline, ".%s %s%s%s%s",
218 kernel_types[ld->type], ld->kernel, s, a, ipoptions);
221 if ( ld->menuseparator )
222 me->displayname = "";
224 if ( ld->menuseparator || ld->menudisabled ) {
225 me->label = NULL;
226 me->passwd = NULL;
227 me->disabled = 1;
229 if ( me->cmdline )
230 free(me->cmdline);
232 me->cmdline = NULL;
235 ld->label = NULL;
236 ld->passwd = NULL;
238 free(ld->kernel);
239 ld->kernel = NULL;
241 if ( ld->append ) {
242 free(ld->append);
243 ld->append = NULL;
246 if ( !ld->menuhide ) {
247 if ( me->hotkey )
248 menu_hotkeys[me->hotkey] = me;
250 if ( ld->menudefault && !ld->menudisabled && !ld->menuseparator )
251 defentry = nentries;
253 nentries++;
255 else {
256 hide_entries[nhidden].displayname = me->displayname;
257 hide_entries[nhidden].label = me->label;
258 hide_entries[nhidden].cmdline = me->cmdline;
259 hide_entries[nhidden].passwd = me->passwd;
261 me->displayname = NULL;
262 me->label = NULL;
263 me->cmdline = NULL;
264 me->passwd = NULL;
266 nhidden++;
271 static char *
272 unlabel(char *str)
274 /* Convert a CLI-style command line to an executable command line */
275 const char *p;
276 char *q;
277 struct menu_entry *me;
278 int i, pos;
280 p = str;
281 while ( *p && !my_isspace(*p) )
282 p++;
284 /* p now points to the first byte beyond the kernel name */
285 pos = p-str;
287 for ( i = 0 ; i < nentries ; i++ ) {
288 me = &menu_entries[i];
290 if ( !strncmp(str, me->label, pos) && !me->label[pos] ) {
291 /* Found matching label */
292 q = malloc(strlen(me->cmdline) + strlen(p) + 1);
293 strcpy(q, me->cmdline);
294 strcat(q, p);
296 free(str);
298 return q;
302 for ( i = 0 ; i < nhidden ; i++ ) {
303 me = &hide_entries[i];
305 if ( !strncmp(str, me->label, pos) && !me->label[pos] ) {
306 /* Found matching label */
307 q = malloc(strlen(me->cmdline) + strlen(p) + 1);
308 strcpy(q, me->cmdline);
309 strcat(q, p);
311 free(str);
313 return q;
317 return str;
320 static char *
321 dup_word(char **p)
323 char *sp = *p;
324 char *ep = sp;
325 char *dp;
326 size_t len;
328 while (*ep && !my_isspace(*ep))
329 ep++;
331 *p = ep;
332 len = ep-sp;
334 dp = malloc(len+1);
335 memcpy(dp, sp, len);
336 dp[len] = '\0';
338 return dp;
341 int my_isxdigit(char c)
343 unsigned int uc = c;
345 return (uc-'0') < 10 ||
346 ((uc|0x20)-'a') < 6;
349 unsigned int hexval(char c)
351 unsigned char uc = c | 0x20;
352 unsigned int v;
354 v = uc-'0';
355 if (v < 10)
356 return v;
358 return uc-'a'+10;
361 unsigned int hexval2(const char *p)
363 return (hexval(p[0]) << 4)+hexval(p[1]);
366 uint32_t parse_argb(char **p)
368 char *sp = *p;
369 char *ep;
370 uint32_t argb;
371 size_t len, dl;
373 if (*sp == '#')
374 sp++;
376 ep = sp;
378 while (my_isxdigit(*ep))
379 ep++;
381 *p = ep;
382 len = ep-sp;
384 switch(len) {
385 case 3: /* #rgb */
386 argb =
387 0xff000000 +
388 (hexval(sp[0])*0x11 << 16) +
389 (hexval(sp[1])*0x11 << 8) +
390 (hexval(sp[2])*0x11);
391 break;
392 case 4: /* #argb */
393 argb =
394 (hexval(sp[0])*0x11 << 24) +
395 (hexval(sp[1])*0x11 << 16) +
396 (hexval(sp[2])*0x11 << 8) +
397 (hexval(sp[3])*0x11);
398 break;
399 case 6: /* #rrggbb */
400 case 9: /* #rrrgggbbb */
401 case 12: /* #rrrrggggbbbb */
402 dl = len/3;
403 argb =
404 0xff000000 +
405 (hexval2(sp+0) << 16) +
406 (hexval2(sp+dl) << 8) +
407 hexval2(sp+dl*2);
408 break;
409 case 8: /* #aarrggbb */
410 /* #aaarrrgggbbb is indistinguishable from #rrrrggggbbbb,
411 assume the latter is a more common format */
412 case 16: /* #aaaarrrrggggbbbb */
413 dl = len/4;
414 argb =
415 (hexval2(sp+0) << 24) +
416 (hexval2(sp+dl) << 16) +
417 (hexval2(sp+dl*2) << 8) +
418 hexval2(sp+dl*3);
419 break;
420 default:
421 argb = 0xffff0000; /* Bright red (error indication) */
422 break;
425 return argb;
429 * Parser state. This is global so that including multiple
430 * files work as expected, which is that everything works the
431 * same way as if the files had been concatenated together.
433 static char *append = NULL;
434 static unsigned int ipappend = 0;
435 static struct labeldata ld;
437 static int parse_one_config(const char *filename);
439 static char *is_kernel_type(char *cmdstr, enum kernel_type *type)
441 const char **p;
442 char *q;
443 enum kernel_type t = KT_NONE;
445 for (p = kernel_types; *p; p++, t++) {
446 if ((q = looking_at(cmdstr, *p))) {
447 *type = t;
448 return q;
452 return NULL;
455 static char *is_message_name(char *cmdstr, struct messages **msgptr)
457 char *q;
458 int i;
460 for (i = 0; i < MSG_COUNT; i++) {
461 if ((q = looking_at(cmdstr, messages[i].name))) {
462 *msgptr = &messages[i];
463 return q;
467 return NULL;
470 static char *is_fkey(char *cmdstr, int *fkeyno)
472 char *q;
473 int no;
475 if ((cmdstr[0]|0x20) != 'f')
476 return NULL;
478 no = strtoul(cmdstr+1, &q, 10);
479 if (!my_isspace(*q))
480 return NULL;
482 if (no < 0 || no > 12)
483 return NULL;
485 *fkeyno = (no == 0) ? 10 : no-1;
486 return q;
489 static void parse_config_file(FILE *f)
491 char line[MAX_LINE], *p, *ep, ch;
492 enum kernel_type type;
493 struct messages *msgptr;
494 int fkeyno;
496 while ( fgets(line, sizeof line, f) ) {
497 p = strchr(line, '\r');
498 if ( p )
499 *p = '\0';
500 p = strchr(line, '\n');
501 if ( p )
502 *p = '\0';
504 p = skipspace(line);
506 if ( looking_at(p, "menu") ) {
507 p = skipspace(p+4);
509 if ( looking_at(p, "label") ) {
510 if ( ld.label )
511 ld.menulabel = strdup(skipspace(p+5));
512 } else if ( looking_at(p, "default") ) {
513 ld.menudefault = 1;
514 } else if ( looking_at(p, "hide") ) {
515 ld.menuhide = 1;
516 } else if ( looking_at(p, "passwd") ) {
517 ld.passwd = strdup(skipspace(p+6));
518 } else if ( looking_at(p, "shiftkey") ) {
519 shiftkey = 1;
520 } else if ( looking_at(p, "onerror") ) {
521 onerror = strdup(skipspace(p+7));
522 } else if ( looking_at(p, "master") ) {
523 p = skipspace(p+6);
524 if ( looking_at(p, "passwd") ) {
525 menu_master_passwd = strdup(skipspace(p+6));
527 } else if ( (ep = looking_at(p, "include")) ) {
528 p = skipspace(ep);
529 parse_one_config(p);
530 } else if ( (ep = looking_at(p, "background")) ) {
531 p = skipspace(ep);
532 if (menu_background)
533 free(menu_background);
534 menu_background = dup_word(&p);
535 } else if ( (ep = looking_at(p, "hidden")) ) {
536 hiddenmenu = 1;
537 } else if ( (ep = is_message_name(p, &msgptr)) ) {
538 free(msgptr->msg);
539 msgptr->msg = strdup(skipspace(ep));
540 } else if ((ep = looking_at(p, "color")) ||
541 (ep = looking_at(p, "colour"))) {
542 int i;
543 struct color_table *cptr;
544 p = skipspace(ep);
545 cptr = console_color_table;
546 for ( i = 0; i < console_color_table_size; i++ ) {
547 if ( (ep = looking_at(p, cptr->name)) ) {
548 p = skipspace(ep);
549 if (*p) {
550 if (looking_at(p, "*")) {
551 p++;
552 } else {
553 free((void *)cptr->ansi);
554 cptr->ansi = dup_word(&p);
557 p = skipspace(p);
558 if (*p) {
559 if (looking_at(p, "*"))
560 p++;
561 else
562 cptr->argb_fg = parse_argb(&p);
564 p = skipspace(p);
565 if (*p) {
566 if (looking_at(p, "*"))
567 p++;
568 else
569 cptr->argb_bg = parse_argb(&p);
571 /* Parse a shadow mode */
572 p = skipspace(p);
573 ch = *p | 0x20;
574 if (ch == 'n') /* none */
575 cptr->shadow = SHADOW_NONE;
576 else if (ch == 's') /* std, standard */
577 cptr->shadow = SHADOW_NORMAL;
578 else if (ch == 'a') /* all */
579 cptr->shadow = SHADOW_ALL;
580 else if (ch == 'r') /* rev, reverse */
581 cptr->shadow = SHADOW_REVERSE;
585 break;
587 cptr++;
589 } else if ((ep = looking_at(p, "msgcolor")) ||
590 (ep = looking_at(p, "msgcolour"))) {
591 unsigned int fg_mask = MSG_COLORS_DEF_FG;
592 unsigned int bg_mask = MSG_COLORS_DEF_BG;
593 enum color_table_shadow shadow = MSG_COLORS_DEF_SHADOW;
595 p = skipspace(ep);
596 if (*p) {
597 if (!looking_at(p, "*"))
598 fg_mask = parse_argb(&p);
600 p = skipspace(p);
601 if (*p) {
602 if (!looking_at(p, "*"))
603 bg_mask = parse_argb(&p);
605 p = skipspace(p);
606 switch (*p | 0x20) {
607 case 'n':
608 shadow = SHADOW_NONE;
609 break;
610 case 's':
611 shadow = SHADOW_NORMAL;
612 break;
613 case 'a':
614 shadow = SHADOW_ALL;
615 break;
616 case 'r':
617 shadow = SHADOW_REVERSE;
618 break;
619 default:
620 /* go with default */
621 break;
625 set_msg_colors_global(fg_mask, bg_mask, shadow);
626 } else if ( looking_at(p, "separator") ) {
627 record(&ld, append);
628 memset(&ld, 0, sizeof(struct labeldata));
629 ld.label = "";
630 ld.menuseparator = 1;
631 record(&ld, append);
632 memset(&ld, 0, sizeof(struct labeldata));
633 } else if ( looking_at(p, "disable") ||
634 looking_at(p, "disabled")) {
635 ld.menudisabled = 1;
636 } else if ( looking_at(p, "indent") ) {
637 ld.menuindent = atoi(skipspace(p+6));
638 } else {
639 /* Unknown, check for layout parameters */
640 struct menu_parameter *pp;
641 for ( pp = mparm ; pp->name ; pp++ ) {
642 if ( (ep = looking_at(p, pp->name)) ) {
643 pp->value = atoi(skipspace(ep));
644 break;
648 } else if ( looking_at(p, "text") ) {
649 enum text_cmd {
650 TEXT_UNKNOWN,
651 TEXT_HELP
652 } cmd = TEXT_UNKNOWN;
653 int len = ld.helptext ? strlen(ld.helptext) : 0;
654 int xlen;
656 p = skipspace(p+4);
658 if (looking_at(p, "help"))
659 cmd = TEXT_HELP;
661 while ( fgets(line, sizeof line, f) ) {
662 p = skipspace(line);
663 if (looking_at(p, "endtext"))
664 break;
666 xlen = strlen(line);
668 switch (cmd) {
669 case TEXT_UNKNOWN:
670 break;
671 case TEXT_HELP:
672 ld.helptext = realloc(ld.helptext, len+xlen+1);
673 memcpy(ld.helptext+len, line, xlen+1);
674 len += xlen;
675 break;
678 } else if ( (ep = is_fkey(p, &fkeyno)) ) {
679 p = skipspace(ep);
680 if (fkeyhelp[fkeyno].textname) {
681 free((void *)fkeyhelp[fkeyno].textname);
682 fkeyhelp[fkeyno].textname = NULL;
684 if (fkeyhelp[fkeyno].background) {
685 free((void *)fkeyhelp[fkeyno].background);
686 fkeyhelp[fkeyno].background = NULL;
689 fkeyhelp[fkeyno].textname = dup_word(&p);
690 if (*p) {
691 p = skipspace(p);
692 fkeyhelp[fkeyno].background = dup_word(&p);
694 } else if ( (ep = looking_at(p, "include")) ) {
695 p = skipspace(ep);
696 parse_one_config(p);
697 } else if ( looking_at(p, "append") ) {
698 char *a = strdup(skipspace(p+6));
699 if ( ld.label )
700 ld.append = a;
701 else
702 append = a;
703 } else if ( looking_at(p, "label") ) {
704 p = skipspace(p+5);
705 record(&ld, append);
706 ld.label = strdup(p);
707 ld.kernel = strdup(p);
708 ld.type = KT_KERNEL;
709 ld.passwd = NULL;
710 ld.append = NULL;
711 ld.menulabel = NULL;
712 ld.helptext = NULL;
713 ld.ipappend = ipappend;
714 ld.menudefault = ld.menuhide = ld.menuseparator =
715 ld.menudisabled = ld.menuindent = 0;
716 } else if ( (ep = is_kernel_type(p, &type)) ) {
717 if ( ld.label ) {
718 free(ld.kernel);
719 ld.kernel = strdup(skipspace(ep));
720 ld.type = type;
722 } else if ( looking_at(p, "timeout") ) {
723 timeout = (atoi(skipspace(p+7))*CLK_TCK+9)/10;
724 } else if ( looking_at(p, "totaltimeout") ) {
725 totaltimeout = (atoll(skipspace(p+13))*CLK_TCK+9)/10;
726 } else if ( looking_at(p, "ontimeout") ) {
727 ontimeout = strdup(skipspace(p+9));
728 } else if ( looking_at(p, "allowoptions") ) {
729 allowedit = atoi(skipspace(p+12));
730 } else if ( looking_at(p, "ipappend") ) {
731 if (ld.label)
732 ld.ipappend = atoi(skipspace(p+8));
733 else
734 ipappend = atoi(skipspace(p+8));
739 static int parse_one_config(const char *filename)
741 FILE *f;
743 if (!strcmp(filename, "~"))
744 filename = get_config();
746 f = fopen(filename, "r");
747 if ( !f )
748 return -1;
750 parse_config_file(f);
751 fclose(f);
753 return 0;
756 void parse_configs(char **argv)
758 const char *filename;
759 int i;
761 /* Initialize defaults */
763 for (i = 0; i < MSG_COUNT; i++) {
764 if (messages[i].msg)
765 free(messages[i].msg);
766 messages[i].msg = strdup(messages[i].defmsg);
769 /* Other initialization */
771 get_ipappend();
772 memset(&ld, 0, sizeof(struct labeldata));
774 /* Actually process the files */
776 if ( !*argv ) {
777 parse_one_config("~");
778 } else {
779 while ( (filename = *argv++) )
780 parse_one_config(filename);
783 /* On final EOF process the last label statement */
785 record(&ld, append);
787 /* Common postprocessing */
789 if ( ontimeout )
790 ontimeout = unlabel(ontimeout);
791 if ( onerror )
792 onerror = unlabel(onerror);