Adding upstream version 3.50~pre5.
[syslinux-debian/hramrach.git] / com32 / modules / readconfig.c
blob12ea76cdba433dc123a6b2e33a8ad12e97d721a3
1 /* ----------------------------------------------------------------------- *
3 * Copyright 2004-2007 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 long long totaltimeout = 0;
35 char *ontimeout = NULL;
36 char *onerror = NULL;
38 char *menu_master_passwd = NULL;
39 char *menu_background = NULL;
41 struct menu_entry menu_entries[MAX_ENTRIES];
42 struct menu_entry hide_entries[MAX_ENTRIES];
43 struct menu_entry *menu_hotkeys[256];
45 struct messages messages[MSG_COUNT] = {
46 [MSG_TITLE] =
47 { "title", "", NULL },
48 [MSG_AUTOBOOT] =
49 { "autoboot", "Automatic boot in # seconds", NULL },
50 [MSG_TAB] =
51 { "tabmsg", "Press [Tab] to edit options", NULL },
52 [MSG_NOTAB] =
53 { "notabmsg", "", NULL },
54 [MSG_PASSPROMPT] =
55 { "passprompt", "Password required", NULL },
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; })
64 /* Must match enum kernel_type */
65 const char *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
80 const char *ipappends[32];
82 static void
83 get_ipappend(void)
85 #ifdef __COM32__
86 static com32sys_t r;
87 uint16_t *ipp;
88 int i;
89 int nipappends;
91 r.eax.w[0] = 0x000F;
92 __intcall(0x22, &r, &r);
94 nipappends = min(r.ecx.w[0], 32);
95 ipp = MK_PTR(r.es, r.ebx.w[0]);
96 for ( i = 0 ; i < nipappends ; i++ ) {
97 ipappends[i] = MK_PTR(r.es, *ipp++);
99 #else
100 ipappends[0] = "ip=foo:bar:baz:quux";
101 ipappends[1] = "BOOTIF=01-aa-bb-cc-dd-ee-ff";
102 #endif
105 static const char *
106 get_config(void)
108 #ifdef __COM32__
109 static com32sys_t r;
111 r.eax.w[0] = 0x000E;
112 __intcall(0x22, &r, &r);
114 return MK_PTR(r.es, r.ebx.w[0]);
115 #else
116 return "syslinux.cfg"; /* Dummy default name */
117 #endif
120 #define MAX_LINE 512
122 static char *
123 skipspace(char *p)
125 while (*p && my_isspace(*p))
126 p++;
128 return p;
131 /* Check to see if we are at a certain keyword (case insensitive) */
132 /* Returns a pointer to the first character past the keyword */
133 static char *
134 looking_at(char *line, const char *kwd)
136 char *p = line;
137 const char *q = kwd;
139 while ( *p && *q && ((*p^*q) & ~0x20) == 0 ) {
140 p++;
141 q++;
144 if ( *q )
145 return NULL; /* Didn't see the keyword */
147 return my_isspace(*p) ? p : NULL; /* Must be EOL or whitespace */
150 struct labeldata {
151 char *label;
152 char *kernel;
153 enum kernel_type type;
154 char *append;
155 char *menulabel;
156 char *passwd;
157 char *helptext;
158 unsigned int ipappend;
159 unsigned int menuhide;
160 unsigned int menudefault;
163 static void
164 record(struct labeldata *ld, char *append)
166 char ipoptions[256], *ipp;
167 int i;
168 struct menu_entry *me = &menu_entries[nentries];
170 if ( ld->label ) {
171 char *a, *s;
172 me->displayname = ld->menulabel ? ld->menulabel : ld->label;
173 me->label = ld->label;
174 me->passwd = ld->passwd;
175 me->helptext = ld->helptext;
176 me->hotkey = 0;
178 if ( ld->menulabel ) {
179 unsigned char *p = (unsigned char *)strchr(ld->menulabel, '^');
180 if ( p && p[1] ) {
181 int hotkey = p[1] & ~0x20;
182 if ( !menu_hotkeys[hotkey] ) {
183 me->hotkey = hotkey;
188 ipp = ipoptions;
189 *ipp = '\0';
190 for ( i = 0 ; i < 32 ; i++ ) {
191 if ( (ld->ipappend & (1U << i)) && ipappends[i] )
192 ipp += sprintf(ipp, " %s", ipappends[i]);
195 a = ld->append;
196 if ( !a ) a = append;
197 if ( !a || (a[0] == '-' && !a[1]) ) a = "";
198 s = a[0] ? " " : "";
199 if (ld->type == KT_KERNEL) {
200 asprintf(&me->cmdline, "%s%s%s%s",
201 ld->kernel, s, a, ipoptions);
202 } else {
203 asprintf(&me->cmdline, ".%s %s%s%s%s",
204 kernel_types[ld->type], ld->kernel, s, a, ipoptions);
207 ld->label = NULL;
208 ld->passwd = NULL;
210 free(ld->kernel);
211 ld->kernel = NULL;
213 if ( ld->append ) {
214 free(ld->append);
215 ld->append = NULL;
218 if ( !ld->menuhide ) {
219 if ( me->hotkey )
220 menu_hotkeys[me->hotkey] = me;
222 if ( ld->menudefault )
223 defentry = nentries;
225 nentries++;
227 else {
228 hide_entries[nhidden].displayname = me->displayname;
229 hide_entries[nhidden].label = me->label;
230 hide_entries[nhidden].cmdline = me->cmdline;
231 hide_entries[nhidden].passwd = me->passwd;
233 me->displayname = NULL;
234 me->label = NULL;
235 me->cmdline = NULL;
236 me->passwd = NULL;
238 nhidden++;
243 static char *
244 unlabel(char *str)
246 /* Convert a CLI-style command line to an executable command line */
247 const char *p;
248 char *q;
249 struct menu_entry *me;
250 int i, pos;
252 p = str;
253 while ( *p && !my_isspace(*p) )
254 p++;
256 /* p now points to the first byte beyond the kernel name */
257 pos = p-str;
259 for ( i = 0 ; i < nentries ; i++ ) {
260 me = &menu_entries[i];
262 if ( !strncmp(str, me->label, pos) && !me->label[pos] ) {
263 /* Found matching label */
264 q = malloc(strlen(me->cmdline) + strlen(p) + 1);
265 strcpy(q, me->cmdline);
266 strcat(q, p);
268 free(str);
270 return q;
274 for ( i = 0 ; i < nhidden ; i++ ) {
275 me = &hide_entries[i];
277 if ( !strncmp(str, me->label, pos) && !me->label[pos] ) {
278 /* Found matching label */
279 q = malloc(strlen(me->cmdline) + strlen(p) + 1);
280 strcpy(q, me->cmdline);
281 strcat(q, p);
283 free(str);
285 return q;
289 return str;
292 static char *
293 dup_word(char **p)
295 char *sp = *p;
296 char *ep = sp;
297 char *dp;
298 size_t len;
300 while (*ep && !my_isspace(*ep))
301 ep++;
303 *p = ep;
304 len = ep-sp;
306 dp = malloc(len+1);
307 memcpy(dp, sp, len);
308 dp[len] = '\0';
310 return dp;
313 static int my_isxdigit(char c)
315 unsigned int uc = c;
317 return (uc-'0') < 10 ||
318 ((uc|0x20)-'a') < 6;
321 static unsigned int hexval(char c)
323 unsigned char uc = c | 0x20;
324 unsigned int v;
326 v = uc-'0';
327 if (v < 10)
328 return v;
330 return uc-'a'+10;
333 static unsigned int hexval2(const char *p)
335 return (hexval(p[0]) << 4)+hexval(p[1]);
338 static uint32_t parse_argb(char **p)
340 char *sp = *p;
341 char *ep;
342 uint32_t argb;
343 size_t len, dl;
345 if (*sp == '#')
346 sp++;
348 ep = sp;
350 while (my_isxdigit(*ep))
351 ep++;
353 *p = ep;
354 len = ep-sp;
356 switch(len) {
357 case 3: /* #rgb */
358 argb =
359 0xff000000 +
360 (hexval(sp[0])*0x11 << 16) +
361 (hexval(sp[1])*0x11 << 8) +
362 (hexval(sp[2])*0x11);
363 break;
364 case 4: /* #argb */
365 argb =
366 (hexval(sp[0])*0x11 << 24) +
367 (hexval(sp[1])*0x11 << 16) +
368 (hexval(sp[2])*0x11 << 8) +
369 (hexval(sp[3])*0x11);
370 break;
371 case 6: /* #rrggbb */
372 case 9: /* #rrrgggbbb */
373 case 12: /* #rrrrggggbbbb */
374 dl = len/3;
375 argb =
376 0xff000000 +
377 (hexval2(sp+0) << 16) +
378 (hexval2(sp+dl) << 8) +
379 hexval2(sp+dl*2);
380 break;
381 case 8: /* #aarrggbb */
382 /* 12 is indistinguishable from #rrrrggggbbbb,
383 assume that is a more common format */
384 case 16: /* #aaaarrrrggggbbbb */
385 dl = len/4;
386 argb =
387 (hexval2(sp+0) << 24) +
388 (hexval2(sp+dl) << 16) +
389 (hexval2(sp+dl*2) << 8) +
390 hexval2(sp+dl*3);
391 break;
392 default:
393 argb = 0xffff0000; /* Bright red (error indication) */
394 break;
397 return argb;
401 * Parser state. This is global so that including multiple
402 * files work as expected, which is that everything works the
403 * same way as if the files had been concatenated together.
405 static char *append = NULL;
406 static unsigned int ipappend = 0;
407 static struct labeldata ld;
409 static int parse_one_config(const char *filename);
411 static char *is_kernel_type(char *cmdstr, enum kernel_type *type)
413 const char **p;
414 char *q;
415 enum kernel_type t = KT_NONE;
417 for (p = kernel_types; *p; p++, t++) {
418 if ((q = looking_at(cmdstr, *p))) {
419 *type = t;
420 return q;
424 return NULL;
427 static char *is_message_name(char *cmdstr, struct messages **msgptr)
429 char *q;
430 int i;
432 for (i = 0; i < MSG_COUNT; i++) {
433 if ((q = looking_at(cmdstr, messages[i].name))) {
434 *msgptr = &messages[i];
435 return q;
439 return NULL;
442 static void parse_config_file(FILE *f)
444 char line[MAX_LINE], *p, *ep, ch;
445 enum kernel_type type;
446 struct messages *msgptr;
448 while ( fgets(line, sizeof line, f) ) {
449 p = strchr(line, '\r');
450 if ( p )
451 *p = '\0';
452 p = strchr(line, '\n');
453 if ( p )
454 *p = '\0';
456 p = skipspace(line);
458 if ( looking_at(p, "menu") ) {
459 p = skipspace(p+4);
461 if ( looking_at(p, "label") ) {
462 if ( ld.label )
463 ld.menulabel = strdup(skipspace(p+5));
464 } else if ( looking_at(p, "default") ) {
465 ld.menudefault = 1;
466 } else if ( looking_at(p, "hide") ) {
467 ld.menuhide = 1;
468 } else if ( looking_at(p, "passwd") ) {
469 ld.passwd = strdup(skipspace(p+6));
470 } else if ( looking_at(p, "shiftkey") ) {
471 shiftkey = 1;
472 } else if ( looking_at(p, "onerror") ) {
473 onerror = strdup(skipspace(p+7));
474 } else if ( looking_at(p, "master") ) {
475 p = skipspace(p+6);
476 if ( looking_at(p, "passwd") ) {
477 menu_master_passwd = strdup(skipspace(p+6));
479 } else if ( (ep = looking_at(p, "include")) ) {
480 p = skipspace(ep);
481 parse_one_config(p);
482 } else if ( (ep = looking_at(p, "background")) ) {
483 p = skipspace(ep);
484 if (menu_background)
485 free(menu_background);
486 menu_background = dup_word(&p);
487 } else if ( (ep = is_message_name(p, &msgptr)) ) {
488 free(msgptr->msg);
489 msgptr->msg = strdup(skipspace(ep));
490 } else if ((ep = looking_at(p, "color")) ||
491 (ep = looking_at(p, "colour"))) {
492 int i;
493 struct color_table *cptr;
494 p = skipspace(ep);
495 cptr = console_color_table;
496 for ( i = 0; i < console_color_table_size; i++ ) {
497 if ( (ep = looking_at(p, cptr->name)) ) {
498 p = skipspace(ep);
499 if (*p) {
500 if (looking_at(p, "*")) {
501 p++;
502 } else {
503 free((void *)cptr->ansi);
504 cptr->ansi = dup_word(&p);
507 p = skipspace(p);
508 if (*p) {
509 if (looking_at(p, "*"))
510 p++;
511 else
512 cptr->argb_fg = parse_argb(&p);
514 p = skipspace(p);
515 if (*p) {
516 if (looking_at(p, "*"))
517 p++;
518 else
519 cptr->argb_bg = parse_argb(&p);
521 /* Parse a shadow mode */
522 p = skipspace(p);
523 ch = *p | 0x20;
524 if (ch == 'n') /* none */
525 cptr->shadow = SHADOW_NONE;
526 else if (ch == 's') /* std, standard */
527 cptr->shadow = SHADOW_NORMAL;
528 else if (ch == 'a') /* all */
529 cptr->shadow = SHADOW_ALL;
530 else if (ch == 'r') /* rev, reverse */
531 cptr->shadow = SHADOW_REVERSE;
535 break;
537 cptr++;
539 } else {
540 /* Unknown, check for layout parameters */
541 struct menu_parameter *pp;
542 for ( pp = mparm ; pp->name ; pp++ ) {
543 if ( (ep = looking_at(p, pp->name)) ) {
544 pp->value = atoi(skipspace(ep));
545 break;
549 } else if ( looking_at(p, "text") ) {
550 enum text_cmd {
551 TEXT_UNKNOWN,
552 TEXT_HELP
553 } cmd = TEXT_UNKNOWN;
554 int len = ld.helptext ? strlen(ld.helptext) : 0;
555 int xlen;
557 p = skipspace(p+4);
559 if (looking_at(p, "help"))
560 cmd = TEXT_HELP;
562 while ( fgets(line, sizeof line, f) ) {
563 p = skipspace(line);
564 if (looking_at(p, "endtext"))
565 break;
567 xlen = strlen(line);
569 switch (cmd) {
570 case TEXT_UNKNOWN:
571 break;
572 case TEXT_HELP:
573 ld.helptext = realloc(ld.helptext, len+xlen+1);
574 memcpy(ld.helptext+len, line, xlen+1);
575 len += xlen;
576 break;
579 } else if ( looking_at(p, "append") ) {
580 char *a = strdup(skipspace(p+6));
581 if ( ld.label )
582 ld.append = a;
583 else
584 append = a;
585 } else if ( looking_at(p, "label") ) {
586 p = skipspace(p+5);
587 record(&ld, append);
588 ld.label = strdup(p);
589 ld.kernel = strdup(p);
590 ld.type = KT_KERNEL;
591 ld.passwd = NULL;
592 ld.append = NULL;
593 ld.menulabel = NULL;
594 ld.helptext = NULL;
595 ld.ipappend = ipappend;
596 ld.menudefault = ld.menuhide = 0;
597 } else if ( (ep = is_kernel_type(p, &type)) ) {
598 if ( ld.label ) {
599 free(ld.kernel);
600 ld.kernel = strdup(skipspace(ep));
601 ld.type = type;
603 } else if ( looking_at(p, "timeout") ) {
604 timeout = (atoi(skipspace(p+7))*CLK_TCK+9)/10;
605 } else if ( looking_at(p, "totaltimeout") ) {
606 totaltimeout = (atoll(skipspace(p+13))*CLK_TCK+9)/10;
607 } else if ( looking_at(p, "ontimeout") ) {
608 ontimeout = strdup(skipspace(p+9));
609 } else if ( looking_at(p, "allowoptions") ) {
610 allowedit = atoi(skipspace(p+12));
611 } else if ( looking_at(p, "ipappend") ) {
612 if (ld.label)
613 ld.ipappend = atoi(skipspace(p+8));
614 else
615 ipappend = atoi(skipspace(p+8));
620 static int parse_one_config(const char *filename)
622 FILE *f;
624 if (!strcmp(filename, "~"))
625 filename = get_config();
627 f = fopen(filename, "r");
628 if ( !f )
629 return -1;
631 parse_config_file(f);
632 fclose(f);
634 return 0;
637 void parse_configs(char **argv)
639 const char *filename;
640 int i;
642 /* Initialize defaults */
644 for (i = 0; i < MSG_COUNT; i++) {
645 if (messages[i].msg)
646 free(messages[i].msg);
647 messages[i].msg = strdup(messages[i].defmsg);
650 /* Other initialization */
652 get_ipappend();
654 /* Actually process the files */
656 if ( !*argv ) {
657 parse_one_config("~");
658 } else {
659 while ( (filename = *argv++) )
660 parse_one_config(filename);
663 /* On final EOF process the last label statement */
665 record(&ld, append);
667 /* Common postprocessing */
669 if ( ontimeout )
670 ontimeout = unlabel(ontimeout);
671 if ( onerror )
672 onerror = unlabel(onerror);