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