Fix partition numbering handling with bplan firmware
[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 200
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_strg, "sysmap", NULL},
96      {cft_end, NULL, NULL}};
97
98 static char flag_set;
99 static char *last_token = NULL, *last_item = NULL, *last_value = NULL;
100 static int line_num;
101 static int back = 0;            /* can go back by one char */
102 static char *currp = NULL;
103 static char *endp = NULL;
104 static char *file_name = NULL;
105 static CONFIG *curr_table = cf_options;
106 static int ignore_entry;
107 static jmp_buf env;
108
109 static struct IMAGES {
110      CONFIG table[sizeof (cf_image) / sizeof (cf_image[0])];
111      int obsolete;
112      struct IMAGES *next;
113 } *images = NULL;
114
115 void cfg_error (char *msg,...)
116 {
117      va_list ap;
118
119      va_start (ap, msg);
120      prom_printf ("Config file error: ");
121      prom_vprintf (msg, ap);
122      va_end (ap);
123      prom_printf (" near line %d in file %s\n", line_num, file_name);
124      longjmp (env, 1);
125 }
126
127 void cfg_warn (char *msg,...)
128 {
129      va_list ap;
130
131      va_start (ap, msg);
132      prom_printf ("Config file warning: ");
133      prom_vprintf (msg, ap);
134      va_end (ap);
135      prom_printf (" near line %d in file %s\n", line_num, file_name);
136 }
137
138 inline int getc ()
139 {
140      if (currp == endp)
141           return EOF;
142      return *currp++;
143 }
144
145 #define next_raw next
146 static int next (void)
147 {
148      int ch;
149
150      if (!back)
151           return getc ();
152      ch = back;
153      back = 0;
154      return ch;
155 }
156
157 static void again (int ch)
158 {
159      back = ch;
160 }
161
162 static char *cfg_get_token (void)
163 {
164      char buf[MAX_TOKEN + 1];
165      char *here;
166      int ch, escaped;
167
168      if (last_token) {
169           here = last_token;
170           last_token = NULL;
171           return here;
172      }
173      while (1) {
174           while (ch = next (), ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')
175                if (ch == '\n' || ch == '\r')
176                     line_num++;
177           if (ch == EOF || ch == (int)NULL)
178                return NULL;
179           if (ch != '#')
180                break;
181           while (ch = next_raw (), (ch != '\n' && ch != '\r'))
182                if (ch == EOF)
183                     return NULL;
184           line_num++;
185      }
186      if (ch == '=')
187           return strdup ("=");
188      if (ch == '"') {
189           here = buf;
190           while (here - buf < MAX_TOKEN) {
191                if ((ch = next ()) == EOF)
192                     cfg_error ("EOF in quoted string");
193                if (ch == '"') {
194                     *here = 0;
195                     return strdup (buf);
196                }
197                if (ch == '\\') {
198                     ch = next ();
199                     switch (ch) {
200                     case '"':
201                     case '\\':
202                          break;
203                     case '\n':
204                     case '\r':
205                          while ((ch = next ()), ch == ' ' || ch == '\t');
206                          if (!ch)
207                               continue;
208                          again (ch);
209                          ch = ' ';
210                          break;
211                     case 'n':
212                          ch = '\n';
213                          break;
214                     default:
215                          cfg_error ("Bad use of \\ in quoted string");
216                     }
217                } else if ((ch == '\n') || (ch == '\r'))
218                     cfg_error ("newline is not allowed in quoted strings");
219                *here++ = ch;
220           }
221           cfg_error ("Quoted string is too long");
222           return 0;             /* not reached */
223      }
224      here = buf;
225      escaped = 0;
226      while (here - buf < MAX_TOKEN) {
227           if (escaped) {
228                if (ch == EOF)
229                     cfg_error ("\\ precedes EOF");
230                if (ch == '\n')
231                     line_num++;
232                else
233                     *here++ = ch == '\t' ? ' ' : ch;
234                escaped = 0;
235           } else {
236                if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == '#' ||
237                    ch == '=' || ch == EOF) {
238                     again (ch);
239                     *here = 0;
240                     return strdup (buf);
241                }
242                if (!(escaped = (ch == '\\')))
243                     *here++ = ch;
244           }
245           ch = next ();
246      }
247      cfg_error ("Token is too long");
248      return 0;                  /* not reached */
249 }
250
251 static void cfg_return_token (char *token)
252 {
253      last_token = token;
254 }
255
256 static int cfg_next (char **item, char **value)
257 {
258      char *this;
259
260      if (last_item) {
261           *item = last_item;
262           *value = last_value;
263           last_item = NULL;
264           return 1;
265      }
266      *value = NULL;
267      if (!(*item = cfg_get_token ()))
268           return 0;
269      if (!strcmp (*item, "="))
270           cfg_error ("Syntax error");
271      if (!(this = cfg_get_token ()))
272           return 1;
273      if (strcmp (this, "=")) {
274           cfg_return_token (this);
275           return 1;
276      }
277      if (!(*value = cfg_get_token ()))
278           cfg_error ("Value expected at EOF");
279      if (!strcmp (*value, "="))
280           cfg_error ("Syntax error after %s", *item);
281      return 1;
282 }
283
284 static char *cfg_get_strg_i (CONFIG * table, char *item)
285 {
286      CONFIG *walk;
287
288      for (walk = table; walk->type != cft_end; walk++)
289           if (walk->name && !strcasecmp (walk->name, item))
290                return walk->data;
291      return 0;
292 }
293
294 #if 0
295 // The one and only call to this procedure is commented out
296 // below, so we don't need this unless we decide to use it again.
297 static void cfg_return (char *item, char *value)
298 {
299      last_item = item;
300      last_value = value;
301 }
302 #endif
303
304 static int match_arch(const char *name)
305 {
306         static prom_handle root;
307         static char model[256], *p;
308
309         if (!root) {
310                 if (!(root = prom_finddevice("/")))
311                         return 0;
312         }
313
314         if (!model[0]) {
315                 if (!prom_getprop(root, "compatible", model, sizeof(model)))
316                         return 0;
317         }
318
319         for (p = model; *p; p += strlen(p) + 1) {
320                 if (!strcasecmp(p, name))
321                         return 1;
322         }
323
324         return 0;
325 }
326
327 static void check_for_obsolete(const char *label)
328 {
329         struct IMAGES *p;
330         char *cur_label;
331
332         /* Make sure our current entry isn't obsolete (ignored) */
333         for (p = images; p; p = p->next) {
334                 if (curr_table == p->table && p->obsolete)
335                         return;
336         }
337
338         for (p = images; p; p = p->next) {
339                 if (curr_table == p->table)
340                         continue;
341
342                 cur_label = cfg_get_strg_i (p->table, "label");
343                 if (!cur_label)
344                         cur_label = cfg_get_strg_i (p->table, "image");
345
346                 if (!cur_label)
347                         continue;
348
349                 if (!strcasecmp(cur_label, label))
350                         p->obsolete = 1;
351         }
352 }
353
354 static int cfg_set (char *item, char *value)
355 {
356      CONFIG *walk;
357
358      if (!strncasecmp (item, "image", 5)) {
359           struct IMAGES **p = &images;
360           int ignore = 0;
361
362           if (item[5] == '[' && item[strlen(item) - 1] == ']') {
363                 char *s, *q = item;
364
365                 /* Get rid of braces */
366                 item[strlen(item) - 1] = 0;
367                 item[5] = 0;
368
369                 for (s = item + 6; q; s = q) {
370                         q = strchr(s, '|');
371                         if (q)
372                                 *q++ = 0;
373
374                         if (match_arch(s))
375                                 goto cfg_set_cont;
376                 }
377                 /* This just creates an unused table. It will get ignored */
378                 ignore = 1;
379           } else if (item[5])
380                 goto cfg_set_redo;
381
382 cfg_set_cont:
383           while (*p)
384                p = &((*p)->next);
385           *p = (struct IMAGES *)malloc (sizeof (struct IMAGES));
386           if (*p == NULL) {
387                prom_printf("malloc error in cfg_set\n");
388                return -1;
389           }
390           (*p)->next = 0;
391           (*p)->obsolete = ignore;
392           curr_table = ((*p)->table);
393           memcpy (curr_table, cf_image, sizeof (cf_image));
394      }
395
396 cfg_set_redo:
397      for (walk = curr_table; walk->type != cft_end; walk++) {
398           if (walk->name && !strcasecmp (walk->name, item)) {
399                if (value && walk->type != cft_strg)
400                     cfg_warn ("'%s' doesn't have a value", walk->name);
401                else if (!value && walk->type == cft_strg)
402                     cfg_warn ("Value expected for '%s'", walk->name);
403                else {
404                     if (!strcasecmp (item, "label"))
405                          check_for_obsolete(value);
406                     if (walk->data)
407                          cfg_warn ("Duplicate entry '%s'", walk->name);
408                     if (walk->type == cft_flag)
409                          walk->data = &flag_set;
410                     else if (walk->type == cft_strg)
411                          walk->data = value;
412                }
413                break;
414           }
415      }
416
417      if (walk->type != cft_end)
418           return 1;
419
420      //cfg_return (item, value);
421
422      return 0;
423 }
424
425 static int cfg_reset ()
426 {
427     CONFIG *walk;
428 #if DEBUG
429     prom_printf("Resetting image table\n");
430 #endif
431     line_num = 0;
432     images = NULL;
433     curr_table = NULL;
434     curr_table = cf_options;
435     for (walk = curr_table; walk->type != cft_end; walk++) {
436 #if DEBUG
437         prom_printf("ItemA %s = %s\n", walk->name, walk->data);
438 #endif
439         if (walk->data != NULL)
440             walk->data = NULL;
441 #if DEBUG
442         prom_printf("ItemB %s = %s\n\n", walk->name, walk->data);
443 #endif
444     }
445
446     return 0;
447 }
448
449 int cfg_parse (char *cfg_file, char *buff, int len)
450 {
451      char *item, *value;
452
453      file_name = cfg_file;
454      currp = buff;
455      endp = currp + len;
456
457      cfg_reset();
458
459      if (setjmp (env))
460           return -1;
461      while (1) {
462           if (!cfg_next (&item, &value))
463                return 0;
464           if (!cfg_set (item, value)) {
465 #if DEBUG
466                prom_printf("Can't set item %s to value %s\n", item, value);
467 #endif
468           }
469           free (item);
470      }
471 }
472
473 char *cfg_get_strg (char *image, char *item)
474 {
475      struct IMAGES *p;
476      char *label, *alias;
477      char *ret;
478
479      if (!image)
480           return cfg_get_strg_i (cf_options, item);
481      for (p = images; p; p = p->next) {
482           if (p->obsolete)
483                   continue;
484
485           label = cfg_get_strg_i (p->table, "label");
486           if (!label) {
487                label = cfg_get_strg_i (p->table, "image");
488                alias = strrchr (label, '/');
489                if (alias)
490                     label = alias + 1;
491           }
492           alias = cfg_get_strg_i (p->table, "alias");
493           if (!strcmp (label, image) || (alias && !strcmp (alias, image))) {
494                ret = cfg_get_strg_i (p->table, item);
495                if (!ret)
496                     ret = cfg_get_strg_i (cf_options, item);
497                return ret;
498           }
499      }
500      return 0;
501 }
502
503 int cfg_get_flag (char *image, char *item)
504 {
505      return !!cfg_get_strg (image, item);
506 }
507
508 static int printl_count = 0;
509 static void printlabel (char *label, int defflag)
510 {
511      int len = strlen (label);
512      char a = ' ';
513
514      if (!printl_count)
515           prom_printf ("\n");
516      switch (defflag) {
517           case 1:  a='*'; break;
518           case 2:  a='&'; break;
519           default: a=' '; break;
520      }
521      prom_printf ("%c %s", a, label);
522      while (len++ < 25)
523           prom_putchar (' ');
524      printl_count++;
525      if (printl_count == 3)
526           printl_count = 0;
527 }
528
529 void cfg_print_images (void)
530 {
531      struct IMAGES *p;
532      char *label, *alias;
533
534      char *ret = cfg_get_strg_i (cf_options, "default");
535      int defflag=0;
536
537      printl_count = 0;
538      for (p = images; p; p = p->next) {
539           if (p->obsolete)
540                   continue;
541
542           label = cfg_get_strg_i (p->table, "label");
543           if (!label) {
544                label = cfg_get_strg_i (p->table, "image");
545                alias = strrchr (label, '/');
546                if (alias)
547                     label = alias + 1;
548           }
549           if(!strcmp(bootoncelabel,label))
550                defflag=2;
551           else if(!strcmp(ret,label))
552                defflag=1;
553           else
554                defflag=0;
555           alias = cfg_get_strg_i (p->table, "alias");
556           printlabel (label, defflag);
557           if (alias)
558                printlabel (alias, 0);
559      }
560      prom_printf("\n");
561 }
562
563 char *cfg_get_default (void)
564 {
565      char *label;
566      struct IMAGES *p;
567      char *ret = cfg_get_strg_i (cf_options, "default");
568
569      if (ret)
570           return ret;
571      if (!images)
572           return 0;
573
574      for (p = images; p && p->obsolete; p = p->next);
575      if (!p)
576              return 0;
577
578      ret = cfg_get_strg_i (p->table, "label");
579      if (!ret) {
580           ret = cfg_get_strg_i (p->table, "image");
581           label = strrchr (ret, '/');
582           if (label)
583                ret = label + 1;
584      }
585      return ret;
586 }
587
588 /*
589  * cfg_set_default_by_mac ()
590  * return 1 if the default cf_option was changed to label with the MAC addr
591  * return 0 if not changed
592  */
593 int cfg_set_default_by_mac (char *mac_addr)
594 {
595      CONFIG *walk;
596      struct IMAGES *tmp;
597      char * label = NULL;
598      int haslabel = 0;
599
600      /* check if there is an image label equal to mac_addr */
601      for (tmp = images; tmp; tmp = tmp->next) {
602         label = cfg_get_strg_i (tmp->table, "label");
603         if (!strcmp(label,mac_addr)){
604             haslabel = 1;
605         }
606      }
607
608      if (!haslabel)
609          return 0;
610      else {
611          /*
612           * if there is an image label equal to mac_addr, change the default
613           * cf_options to this image label
614           */
615          for (walk = cf_options; walk->type != cft_end; walk++) {
616              if (!strcasecmp(walk->name,"default")) {
617                  walk->data = mac_addr;
618                  return 1;
619              }
620          }
621          return 0;
622      }
623 }
624
625 /*
626  * Local variables:
627  * c-file-style: "k&r"
628  * c-basic-offset: 5
629  * End:
630  */