3995be2602b10371f97b10d8a5be1dcee9a35735
[profile/ivi/syslinux.git] / com32 / menu / readconfig.c
1 /* ----------------------------------------------------------------------- *
2  *
3  *   Copyright 2004-2008 H. Peter Anvin - All Rights Reserved
4  *
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., 51 Franklin St, Fifth Floor,
8  *   Boston MA 02110-1301, USA; either version 2 of the License, or
9  *   (at your option) any later version; incorporated herein by reference.
10  *
11  * ----------------------------------------------------------------------- */
12
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
24
25 #include "menu.h"
26
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;
35
36 char *ontimeout   = NULL;
37 char *onerror     = NULL;
38
39 char *menu_master_passwd = NULL;
40 char *menu_background = NULL;
41
42 struct fkey_help fkeyhelp[12];
43
44 struct menu_entry menu_entries[MAX_ENTRIES];
45 struct menu_entry hide_entries[MAX_ENTRIES];
46 struct menu_entry *menu_hotkeys[256];
47
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 },
59 };
60
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; })
66
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
81 };
82
83 const char *ipappends[32];
84
85 static void
86 get_ipappend(void)
87 {
88 #ifdef __COM32__
89   static com32sys_t r;
90   uint16_t *ipp;
91   int i;
92   int nipappends;
93
94   r.eax.w[0] = 0x000F;
95   __intcall(0x22, &r, &r);
96
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++);
101   }
102 #else
103   ipappends[0] = "ip=foo:bar:baz:quux";
104   ipappends[1] = "BOOTIF=01-aa-bb-cc-dd-ee-ff";
105 #endif
106 }
107
108 static const char *
109 get_config(void)
110 {
111 #ifdef __COM32__
112   static com32sys_t r;
113
114   r.eax.w[0] = 0x000E;
115   __intcall(0x22, &r, &r);
116
117   return MK_PTR(r.es, r.ebx.w[0]);
118 #else
119   return "syslinux.cfg";        /* Dummy default name */
120 #endif
121 }
122
123 #define MAX_LINE 512
124
125 static char *
126 skipspace(char *p)
127 {
128   while (*p && my_isspace(*p))
129     p++;
130
131   return p;
132 }
133
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)
138 {
139   char *p = line;
140   const char *q = kwd;
141
142   while ( *p && *q && ((*p^*q) & ~0x20) == 0 ) {
143     p++;
144     q++;
145   }
146
147   if ( *q )
148     return NULL;                /* Didn't see the keyword */
149
150   return my_isspace(*p) ? p : NULL; /* Must be EOL or whitespace */
151 }
152
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;
167 };
168
169 static void
170 record(struct labeldata *ld, char *append)
171 {
172   char ipoptions[256], *ipp;
173   int i;
174   struct menu_entry *me = &menu_entries[nentries];
175
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;
184
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;
190     }
191
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;
198         }
199       }
200     }
201
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]);
207     }
208
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);
219     }
220
221     if ( ld->menuseparator )
222       me->displayname = "";
223
224     if ( ld->menuseparator || ld->menudisabled ) {
225       me->label    = NULL;
226       me->passwd   = NULL;
227       me->disabled = 1;
228
229       if ( me->cmdline )
230         free(me->cmdline);
231
232       me->cmdline = NULL;
233     }
234
235     ld->label = NULL;
236     ld->passwd = NULL;
237
238     free(ld->kernel);
239     ld->kernel = NULL;
240
241     if ( ld->append ) {
242       free(ld->append);
243       ld->append = NULL;
244     }
245
246     if ( !ld->menuhide ) {
247       if ( me->hotkey )
248         menu_hotkeys[me->hotkey] = me;
249
250       if ( ld->menudefault && !ld->menudisabled && !ld->menuseparator )
251         defentry = nentries;
252
253       nentries++;
254     }
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;
260
261       me->displayname = NULL;
262       me->label       = NULL;
263       me->cmdline     = NULL;
264       me->passwd      = NULL;
265
266       nhidden++;
267     }
268   }
269 }
270
271 static char *
272 unlabel(char *str)
273 {
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;
279
280   p = str;
281   while ( *p && !my_isspace(*p) )
282     p++;
283
284   /* p now points to the first byte beyond the kernel name */
285   pos = p-str;
286
287   for ( i = 0 ; i < nentries ; i++ ) {
288     me = &menu_entries[i];
289
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);
295
296       free(str);
297
298       return q;
299     }
300   }
301
302   for ( i = 0 ; i < nhidden ; i++ ) {
303     me = &hide_entries[i];
304
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);
310
311       free(str);
312
313       return q;
314     }
315   }
316
317   return str;
318 }
319
320 static char *
321 dup_word(char **p)
322 {
323   char *sp = *p;
324   char *ep = sp;
325   char *dp;
326   size_t len;
327
328   while (*ep && !my_isspace(*ep))
329     ep++;
330
331   *p = ep;
332   len = ep-sp;
333
334   dp = malloc(len+1);
335   memcpy(dp, sp, len);
336   dp[len] = '\0';
337
338   return dp;
339 }
340
341 int my_isxdigit(char c)
342 {
343   unsigned int uc = c;
344
345   return (uc-'0') < 10 ||
346     ((uc|0x20)-'a') < 6;
347 }
348
349 unsigned int hexval(char c)
350 {
351   unsigned char uc = c | 0x20;
352   unsigned int v;
353
354   v = uc-'0';
355   if (v < 10)
356     return v;
357
358   return uc-'a'+10;
359 }
360
361 unsigned int hexval2(const char *p)
362 {
363   return (hexval(p[0]) << 4)+hexval(p[1]);
364 }
365
366 uint32_t parse_argb(char **p)
367 {
368   char *sp = *p;
369   char *ep;
370   uint32_t argb;
371   size_t len, dl;
372
373   if (*sp == '#')
374     sp++;
375
376   ep = sp;
377
378   while (my_isxdigit(*ep))
379     ep++;
380
381   *p = ep;
382   len = ep-sp;
383
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;
423   }
424
425   return argb;
426 }
427
428 /*
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.
432  */
433 static char *append = NULL;
434 static unsigned int ipappend = 0;
435 static struct labeldata ld;
436
437 static int parse_one_config(const char *filename);
438
439 static char *is_kernel_type(char *cmdstr, enum kernel_type *type)
440 {
441   const char **p;
442   char *q;
443   enum kernel_type t = KT_NONE;
444
445   for (p = kernel_types; *p; p++, t++) {
446     if ((q = looking_at(cmdstr, *p))) {
447       *type = t;
448       return q;
449     }
450   }
451
452   return NULL;
453 }
454
455 static char *is_message_name(char *cmdstr, struct messages **msgptr)
456 {
457   char *q;
458   int i;
459
460   for (i = 0; i < MSG_COUNT; i++) {
461     if ((q = looking_at(cmdstr, messages[i].name))) {
462       *msgptr = &messages[i];
463       return q;
464     }
465   }
466
467   return NULL;
468 }
469
470 static char *is_fkey(char *cmdstr, int *fkeyno)
471 {
472   char *q;
473   int no;
474
475   if ((cmdstr[0]|0x20) != 'f')
476     return NULL;
477
478   no = strtoul(cmdstr+1, &q, 10);
479   if (!my_isspace(*q))
480     return NULL;
481
482   if (no < 0 || no > 12)
483     return NULL;
484
485   *fkeyno = (no == 0) ? 10 : no-1;
486   return q;
487 }
488
489 static void parse_config_file(FILE *f)
490 {
491   char line[MAX_LINE], *p, *ep, ch;
492   enum kernel_type type;
493   struct messages *msgptr;
494   int fkeyno;
495
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';
503
504     p = skipspace(line);
505
506     if ( looking_at(p, "menu") ) {
507       p = skipspace(p+4);
508
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));
526         }
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);
555               }
556
557               p = skipspace(p);
558               if (*p) {
559                 if (looking_at(p, "*"))
560                   p++;
561                 else
562                   cptr->argb_fg = parse_argb(&p);
563
564                 p = skipspace(p);
565                 if (*p) {
566                   if (looking_at(p, "*"))
567                     p++;
568                   else
569                     cptr->argb_bg = parse_argb(&p);
570
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;
582                 }
583               }
584             }
585             break;
586           }
587           cptr++;
588         }
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;
594
595         p = skipspace(ep);
596         if (*p) {
597           if (!looking_at(p, "*"))
598             fg_mask = parse_argb(&p);
599
600           p = skipspace(p);
601           if (*p) {
602             if (!looking_at(p, "*"))
603               bg_mask = parse_argb(&p);
604
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;
622             }
623           }
624         }
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;
645           }
646         }
647       }
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;
655
656       p = skipspace(p+4);
657
658       if (looking_at(p, "help"))
659         cmd = TEXT_HELP;
660
661       while ( fgets(line, sizeof line, f) ) {
662         p = skipspace(line);
663         if (looking_at(p, "endtext"))
664           break;
665
666         xlen = strlen(line);
667
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;
676         }
677       }
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;
683       }
684       if (fkeyhelp[fkeyno].background) {
685         free((void *)fkeyhelp[fkeyno].background);
686         fkeyhelp[fkeyno].background = NULL;
687       }
688
689       fkeyhelp[fkeyno].textname = dup_word(&p);
690       if (*p) {
691         p = skipspace(p);
692         fkeyhelp[fkeyno].background = dup_word(&p);
693       }
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;
721       }
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));
735     }
736   }
737 }
738
739 static int parse_one_config(const char *filename)
740 {
741   FILE *f;
742
743   if (!strcmp(filename, "~"))
744     filename = get_config();
745
746   f = fopen(filename, "r");
747   if ( !f )
748     return -1;
749
750   parse_config_file(f);
751   fclose(f);
752
753   return 0;
754 }
755
756 void parse_configs(char **argv)
757 {
758   const char *filename;
759   int i;
760
761   /* Initialize defaults */
762
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);
767   }
768
769   /* Other initialization */
770
771   get_ipappend();
772   memset(&ld, 0, sizeof(struct labeldata));
773
774   /* Actually process the files */
775
776   if ( !*argv ) {
777     parse_one_config("~");
778   } else {
779     while ( (filename = *argv++) )
780       parse_one_config(filename);
781   }
782
783   /* On final EOF process the last label statement */
784
785   record(&ld, append);
786
787   /* Common postprocessing */
788
789   if ( ontimeout )
790     ontimeout = unlabel(ontimeout);
791   if ( onerror )
792     onerror = unlabel(onerror);
793 }