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