Remove sysmap support
[yaboot.git] / second / cfg.c
1 /*
2  *  cfg.c - Handling and parsing of yaboot.conf
3  *
4  *  Copyright (C) 1995 Werner Almesberger
5  *                1996 Jakub Jelinek
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20  */
21
22 #include "setjmp.h"
23 #include "stdarg.h"
24 #include "stdlib.h"
25 #include "string.h"
26 #include "types.h"
27 #include "prom.h"
28
29 /* Imported functions */
30 extern int strcasecmp(const char *s1, const char *s2);
31 extern int strncasecmp(const char *cs, const char *ct, size_t n);
32 extern char bootoncelabel[1024];
33
34 typedef enum {
35      cft_strg, cft_flag, cft_end
36 } CONFIG_TYPE;
37
38 typedef struct {
39      CONFIG_TYPE type;
40      char *name;
41      void *data;
42 } CONFIG;
43
44 #define MAX_TOKEN 511
45 #define MAX_VAR_NAME MAX_TOKEN
46 #define EOF -1
47
48 CONFIG cf_options[] =
49 {
50      {cft_strg, "device", NULL},
51      {cft_strg, "partition", NULL},
52      {cft_strg, "default", NULL},
53      {cft_strg, "timeout", NULL},
54      {cft_strg, "password", NULL},
55      {cft_flag, "restricted", NULL},
56      {cft_strg, "message", NULL},
57      {cft_strg, "root", NULL},
58      {cft_strg, "ramdisk", NULL},
59      {cft_flag, "read-only", NULL},
60      {cft_flag, "read-write", NULL},
61      {cft_strg, "append", NULL},
62      {cft_strg, "initrd", NULL},
63      {cft_flag, "initrd-prompt", NULL},
64      {cft_strg, "initrd-size", NULL},
65      {cft_flag, "pause-after", NULL},
66      {cft_strg, "pause-message", NULL},
67      {cft_strg, "init-code", NULL},
68      {cft_strg, "init-message", NULL},
69      {cft_strg, "fgcolor", NULL},
70      {cft_strg, "bgcolor", NULL},
71      {cft_strg, "ptypewarning", NULL},
72      {cft_end, NULL, NULL}};
73
74 CONFIG cf_image[] =
75 {
76      {cft_strg, "image", NULL},
77      {cft_strg, "label", NULL},
78      {cft_strg, "alias", NULL},
79      {cft_flag, "single-key", NULL},
80      {cft_flag, "restricted", NULL},
81      {cft_strg, "device", NULL},
82      {cft_strg, "partition", NULL},
83      {cft_strg, "root", NULL},
84      {cft_strg, "ramdisk", NULL},
85      {cft_flag, "read-only", NULL},
86      {cft_flag, "read-write", NULL},
87      {cft_strg, "append", NULL},
88      {cft_strg, "literal", NULL},
89      {cft_strg, "initrd", NULL},
90      {cft_flag, "initrd-prompt", NULL},
91      {cft_strg, "initrd-size", NULL},
92      {cft_flag, "pause-after", NULL},
93      {cft_strg, "pause-message", NULL},
94      {cft_flag, "novideo", NULL},
95      {cft_end, NULL, NULL}};
96
97 static char flag_set;
98 static char *last_token = NULL, *last_item = NULL, *last_value = NULL;
99 static int line_num;
100 static int back = 0;            /* can go back by one char */
101 static char *currp = NULL;
102 static char *endp = NULL;
103 static char *file_name = NULL;
104 static CONFIG *curr_table = cf_options;
105 static int ignore_entry;
106 static jmp_buf env;
107
108 static struct IMAGES {
109      CONFIG table[sizeof (cf_image) / sizeof (cf_image[0])];
110      int obsolete;
111      struct IMAGES *next;
112 } *images = NULL;
113
114 void cfg_error (char *msg,...)
115 {
116      va_list ap;
117
118      va_start (ap, msg);
119      prom_printf ("Config file error: ");
120      prom_vprintf (msg, ap);
121      va_end (ap);
122      prom_printf (" near line %d in file %s\n", line_num, file_name);
123      longjmp (env, 1);
124 }
125
126 void cfg_warn (char *msg,...)
127 {
128      va_list ap;
129
130      va_start (ap, msg);
131      prom_printf ("Config file warning: ");
132      prom_vprintf (msg, ap);
133      va_end (ap);
134      prom_printf (" near line %d in file %s\n", line_num, file_name);
135 }
136
137 inline int getc ()
138 {
139      if (currp == endp)
140           return EOF;
141      return *currp++;
142 }
143
144 #define next_raw next
145 static int next (void)
146 {
147      int ch;
148
149      if (!back)
150           return getc ();
151      ch = back;
152      back = 0;
153      return ch;
154 }
155
156 static void again (int ch)
157 {
158      back = ch;
159 }
160
161 static char *cfg_get_token (void)
162 {
163      char buf[MAX_TOKEN + 1];
164      char *here;
165      int ch, escaped;
166
167      if (last_token) {
168           here = last_token;
169           last_token = NULL;
170           return here;
171      }
172      while (1) {
173           while (ch = next (), ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')
174                if (ch == '\n' || ch == '\r')
175                     line_num++;
176           if (ch == EOF || ch == (int)NULL)
177                return NULL;
178           if (ch != '#')
179                break;
180           while (ch = next_raw (), (ch != '\n' && ch != '\r'))
181                if (ch == EOF)
182                     return NULL;
183           line_num++;
184      }
185      if (ch == '=')
186           return strdup ("=");
187      if (ch == '"') {
188           here = buf;
189           while (here - buf < MAX_TOKEN) {
190                if ((ch = next ()) == EOF)
191                     cfg_error ("EOF in quoted string");
192                if (ch == '"') {
193                     *here = 0;
194                     return strdup (buf);
195                }
196                if (ch == '\\') {
197                     ch = next ();
198                     switch (ch) {
199                     case '"':
200                     case '\\':
201                          break;
202                     case '\n':
203                     case '\r':
204                          while ((ch = next ()), ch == ' ' || ch == '\t');
205                          if (!ch)
206                               continue;
207                          again (ch);
208                          ch = ' ';
209                          break;
210                     case 'n':
211                          ch = '\n';
212                          break;
213                     default:
214                          cfg_error ("Bad use of \\ in quoted string");
215                     }
216                } else if ((ch == '\n') || (ch == '\r'))
217                     cfg_error ("newline is not allowed in quoted strings");
218                *here++ = ch;
219           }
220           cfg_error ("Quoted string is too long");
221           return 0;             /* not reached */
222      }
223      here = buf;
224      escaped = 0;
225      while (here - buf < MAX_TOKEN) {
226           if (escaped) {
227                if (ch == EOF)
228                     cfg_error ("\\ precedes EOF");
229                if (ch == '\n')
230                     line_num++;
231                else
232                     *here++ = ch == '\t' ? ' ' : ch;
233                escaped = 0;
234           } else {
235                if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == '#' ||
236                    ch == '=' || ch == EOF) {
237                     again (ch);
238                     *here = 0;
239                     return strdup (buf);
240                }
241                if (!(escaped = (ch == '\\')))
242                     *here++ = ch;
243           }
244           ch = next ();
245      }
246      cfg_error ("Token is too long");
247      return 0;                  /* not reached */
248 }
249
250 static void cfg_return_token (char *token)
251 {
252      last_token = token;
253 }
254
255 static int cfg_next (char **item, char **value)
256 {
257      char *this;
258
259      if (last_item) {
260           *item = last_item;
261           *value = last_value;
262           last_item = NULL;
263           return 1;
264      }
265      *value = NULL;
266      if (!(*item = cfg_get_token ()))
267           return 0;
268      if (!strcmp (*item, "="))
269           cfg_error ("Syntax error");
270      if (!(this = cfg_get_token ()))
271           return 1;
272      if (strcmp (this, "=")) {
273           cfg_return_token (this);
274           return 1;
275      }
276      if (!(*value = cfg_get_token ()))
277           cfg_error ("Value expected at EOF");
278      if (!strcmp (*value, "="))
279           cfg_error ("Syntax error after %s", *item);
280      return 1;
281 }
282
283 static char *cfg_get_strg_i (CONFIG * table, char *item)
284 {
285      CONFIG *walk;
286
287      for (walk = table; walk->type != cft_end; walk++)
288           if (walk->name && !strcasecmp (walk->name, item))
289                return walk->data;
290      return 0;
291 }
292
293 #if 0
294 // The one and only call to this procedure is commented out
295 // below, so we don't need this unless we decide to use it again.
296 static void cfg_return (char *item, char *value)
297 {
298      last_item = item;
299      last_value = value;
300 }
301 #endif
302
303 static int match_arch(const char *name)
304 {
305         static prom_handle root;
306         static char model[256], *p;
307
308         if (!root) {
309                 if (!(root = prom_finddevice("/")))
310                         return 0;
311         }
312
313         if (!model[0]) {
314                 if (!prom_getprop(root, "compatible", model, sizeof(model)))
315                         return 0;
316         }
317
318         for (p = model; *p; p += strlen(p) + 1) {
319                 if (!strcasecmp(p, name))
320                         return 1;
321         }
322
323         return 0;
324 }
325
326 static void check_for_obsolete(const char *label)
327 {
328         struct IMAGES *p;
329         char *cur_label;
330
331         /* Make sure our current entry isn't obsolete (ignored) */
332         for (p = images; p; p = p->next) {
333                 if (curr_table == p->table && p->obsolete)
334                         return;
335         }
336
337         for (p = images; p; p = p->next) {
338                 if (curr_table == p->table)
339                         continue;
340
341                 cur_label = cfg_get_strg_i (p->table, "label");
342                 if (!cur_label)
343                         cur_label = cfg_get_strg_i (p->table, "image");
344
345                 if (!cur_label)
346                         continue;
347
348                 if (!strcasecmp(cur_label, label))
349                         p->obsolete = 1;
350         }
351 }
352
353 static int cfg_set (char *item, char *value)
354 {
355      CONFIG *walk;
356
357      if (!strncasecmp (item, "image", 5)) {
358           struct IMAGES **p = &images;
359           int ignore = 0;
360
361           if (item[5] == '[' && item[strlen(item) - 1] == ']') {
362                 char *s, *q = item;
363
364                 /* Get rid of braces */
365                 item[strlen(item) - 1] = 0;
366                 item[5] = 0;
367
368                 for (s = item + 6; q; s = q) {
369                         q = strchr(s, '|');
370                         if (q)
371                                 *q++ = 0;
372
373                         if (match_arch(s))
374                                 goto cfg_set_cont;
375                 }
376                 /* This just creates an unused table. It will get ignored */
377                 ignore = 1;
378           } else if (item[5])
379                 goto cfg_set_redo;
380
381 cfg_set_cont:
382           while (*p)
383                p = &((*p)->next);
384           *p = (struct IMAGES *)malloc (sizeof (struct IMAGES));
385           if (*p == NULL) {
386                prom_printf("malloc error in cfg_set\n");
387                return -1;
388           }
389           (*p)->next = 0;
390           (*p)->obsolete = ignore;
391           curr_table = ((*p)->table);
392           memcpy (curr_table, cf_image, sizeof (cf_image));
393      }
394
395 cfg_set_redo:
396      for (walk = curr_table; walk->type != cft_end; walk++) {
397           if (walk->name && !strcasecmp (walk->name, item)) {
398                if (value && walk->type != cft_strg)
399                     cfg_warn ("'%s' doesn't have a value", walk->name);
400                else if (!value && walk->type == cft_strg)
401                     cfg_warn ("Value expected for '%s'", walk->name);
402                else {
403                     if (!strcasecmp (item, "label"))
404                          check_for_obsolete(value);
405                     if (walk->data)
406                          cfg_warn ("Duplicate entry '%s'", walk->name);
407                     if (walk->type == cft_flag)
408                          walk->data = &flag_set;
409                     else if (walk->type == cft_strg)
410                          walk->data = value;
411                }
412                break;
413           }
414      }
415
416      if (walk->type != cft_end)
417           return 1;
418
419      //cfg_return (item, value);
420
421      return 0;
422 }
423
424 static int cfg_reset ()
425 {
426     CONFIG *walk;
427 #if DEBUG
428     prom_printf("Resetting image table\n");
429 #endif
430     line_num = 0;
431     images = NULL;
432     curr_table = NULL;
433     curr_table = cf_options;
434     for (walk = curr_table; walk->type != cft_end; walk++) {
435 #if DEBUG
436         prom_printf("ItemA %s = %s\n", walk->name, walk->data);
437 #endif
438         if (walk->data != NULL)
439             walk->data = NULL;
440 #if DEBUG
441         prom_printf("ItemB %s = %s\n\n", walk->name, walk->data);
442 #endif
443     }
444
445     return 0;
446 }
447
448 int cfg_parse (char *cfg_file, char *buff, int len)
449 {
450      char *item, *value;
451
452      file_name = cfg_file;
453      currp = buff;
454      endp = currp + len;
455
456      cfg_reset();
457
458      if (setjmp (env))
459           return -1;
460      while (1) {
461           if (!cfg_next (&item, &value))
462                return 0;
463           if (!cfg_set (item, value)) {
464 #if DEBUG
465                prom_printf("Can't set item %s to value %s\n", item, value);
466 #endif
467           }
468           free (item);
469      }
470 }
471
472 char *cfg_get_strg (char *image, char *item)
473 {
474      struct IMAGES *p;
475      char *label, *alias;
476      char *ret;
477
478      if (!image)
479           return cfg_get_strg_i (cf_options, item);
480      for (p = images; p; p = p->next) {
481           if (p->obsolete)
482                   continue;
483
484           label = cfg_get_strg_i (p->table, "label");
485           if (!label) {
486                label = cfg_get_strg_i (p->table, "image");
487                alias = strrchr (label, '/');
488                if (alias)
489                     label = alias + 1;
490           }
491           alias = cfg_get_strg_i (p->table, "alias");
492           if (!strcmp (label, image) || (alias && !strcmp (alias, image))) {
493                ret = cfg_get_strg_i (p->table, item);
494                if (!ret)
495                     ret = cfg_get_strg_i (cf_options, item);
496                return ret;
497           }
498      }
499      return 0;
500 }
501
502 int cfg_get_flag (char *image, char *item)
503 {
504      return !!cfg_get_strg (image, item);
505 }
506
507 static int printl_count = 0;
508 static void printlabel (char *label, int defflag)
509 {
510      int len = strlen (label);
511      char a = ' ';
512
513      if (!printl_count)
514           prom_printf ("\n");
515      switch (defflag) {
516           case 1:  a='*'; break;
517           case 2:  a='&'; break;
518           default: a=' '; break;
519      }
520      prom_printf ("%c %s", a, label);
521      while (len++ < 25)
522           prom_putchar (' ');
523      printl_count++;
524      if (printl_count == 3)
525           printl_count = 0;
526 }
527
528 void cfg_print_images (void)
529 {
530      struct IMAGES *p;
531      char *label, *alias;
532
533      char *ret = cfg_get_strg_i (cf_options, "default");
534      int defflag=0;
535
536      printl_count = 0;
537      for (p = images; p; p = p->next) {
538           if (p->obsolete)
539                   continue;
540
541           label = cfg_get_strg_i (p->table, "label");
542           if (!label) {
543                label = cfg_get_strg_i (p->table, "image");
544                alias = strrchr (label, '/');
545                if (alias)
546                     label = alias + 1;
547           }
548           if(!strcmp(bootoncelabel,label))
549                defflag=2;
550           else if(!strcmp(ret,label))
551                defflag=1;
552           else
553                defflag=0;
554           alias = cfg_get_strg_i (p->table, "alias");
555           printlabel (label, defflag);
556           if (alias)
557                printlabel (alias, 0);
558      }
559      prom_printf("\n");
560 }
561
562 char *cfg_get_default (void)
563 {
564      char *label;
565      struct IMAGES *p;
566      char *ret = cfg_get_strg_i (cf_options, "default");
567
568      if (ret)
569           return ret;
570      if (!images)
571           return 0;
572
573      for (p = images; p && p->obsolete; p = p->next);
574      if (!p)
575              return 0;
576
577      ret = cfg_get_strg_i (p->table, "label");
578      if (!ret) {
579           ret = cfg_get_strg_i (p->table, "image");
580           label = strrchr (ret, '/');
581           if (label)
582                ret = label + 1;
583      }
584      return ret;
585 }
586
587 /*
588  * cfg_set_default_by_mac ()
589  * return 1 if the default cf_option was changed to label with the MAC addr
590  * return 0 if not changed
591  */
592 int cfg_set_default_by_mac (char *mac_addr)
593 {
594      CONFIG *walk;
595      struct IMAGES *tmp;
596      char * label = NULL;
597      int haslabel = 0;
598
599      /* check if there is an image label equal to mac_addr */
600      for (tmp = images; tmp; tmp = tmp->next) {
601         label = cfg_get_strg_i (tmp->table, "label");
602         if (!strcmp(label,mac_addr)){
603             haslabel = 1;
604         }
605      }
606
607      if (!haslabel)
608          return 0;
609      else {
610          /*
611           * if there is an image label equal to mac_addr, change the default
612           * cf_options to this image label
613           */
614          for (walk = cf_options; walk->type != cft_end; walk++) {
615              if (!strcasecmp(walk->name,"default")) {
616                  walk->data = mac_addr;
617                  return 1;
618              }
619          }
620          return 0;
621      }
622 }
623
624 /*
625  * Local variables:
626  * c-file-style: "k&r"
627  * c-basic-offset: 5
628  * End:
629  */