Commit yaboot 1.3.4
[yaboot.git] / second / cfg.c
1 /* Handling and parsing of silo.conf
2    
3    Copyright (C) 1995 Werner Almesberger
4                  1996 Jakub Jelinek
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
19
20 #include "setjmp.h"
21 #include "stdarg.h"
22 #include "stdlib.h"
23 #include "string.h"
24 #include "types.h"
25 #include "prom.h"
26
27 /* Imported functions */
28 extern int strcasecmp(const char *s1, const char *s2);
29
30 typedef enum {
31      cft_strg, cft_flag, cft_end
32 } CONFIG_TYPE;
33
34 typedef struct {
35      CONFIG_TYPE type;
36      char *name;
37      void *data;
38 } CONFIG;
39
40 #define MAX_TOKEN 200
41 #define MAX_VAR_NAME MAX_TOKEN
42 #define EOF -1
43
44 CONFIG cf_options[] =
45 {
46      {cft_strg, "device", NULL},
47      {cft_strg, "partition", NULL},
48      {cft_strg, "default", NULL},
49      {cft_strg, "timeout", NULL},
50      {cft_strg, "password", NULL},
51      {cft_flag, "restricted", NULL},
52      {cft_strg, "message", NULL},
53      {cft_strg, "root", NULL},
54      {cft_strg, "ramdisk", NULL},
55      {cft_flag, "read-only", NULL},
56      {cft_flag, "read-write", NULL},
57      {cft_strg, "append", NULL},
58      {cft_strg, "initrd", NULL},
59      {cft_flag, "initrd-prompt", NULL},
60      {cft_strg, "initrd-size", NULL},
61      {cft_flag, "pause-after", NULL},
62      {cft_strg, "pause-message", NULL},
63      {cft_strg, "init-code", NULL},
64      {cft_strg, "init-message", NULL},
65      {cft_strg, "fgcolor", NULL},
66      {cft_strg, "bgcolor", NULL},
67      {cft_strg, "ptypewarning", NULL},
68      {cft_end, NULL, NULL}};
69
70 CONFIG cf_image[] =
71 {
72      {cft_strg, "image", NULL},
73      {cft_strg, "label", NULL},
74      {cft_strg, "alias", NULL},
75      {cft_flag, "single-key", NULL},
76      {cft_flag, "restricted", NULL},
77      {cft_strg, "device", NULL},
78      {cft_strg, "partition", NULL},
79      {cft_strg, "root", NULL},
80      {cft_strg, "ramdisk", NULL},
81      {cft_flag, "read-only", NULL},
82      {cft_flag, "read-write", NULL},
83      {cft_strg, "append", NULL},
84      {cft_strg, "literal", NULL},
85      {cft_strg, "initrd", NULL},
86      {cft_flag, "initrd-prompt", NULL},
87      {cft_strg, "initrd-size", NULL},
88      {cft_flag, "pause-after", NULL},
89      {cft_strg, "pause-message", NULL},
90      {cft_flag, "novideo", NULL},
91      {cft_strg, "sysmap", NULL},
92      {cft_end, NULL, NULL}};
93
94 static char flag_set;
95 static char *last_token = NULL, *last_item = NULL, *last_value = NULL;
96 static int line_num;
97 static int back = 0;            /* can go back by one char */
98 static char *currp = NULL;
99 static char *endp = NULL;
100 static char *file_name = NULL;
101 static CONFIG *curr_table = cf_options;
102 static jmp_buf env;
103
104 static struct IMAGES {
105      CONFIG table[sizeof (cf_image) / sizeof (cf_image[0])];
106      struct IMAGES *next;
107 } *images = NULL;
108
109 void cfg_error (char *msg,...)
110 {
111      va_list ap;
112
113      va_start (ap, msg);
114      prom_printf ("Config file error: ");
115      prom_vprintf (msg, ap);
116      va_end (ap);
117      prom_printf (" near line %d in file %s\n", line_num, file_name);
118      longjmp (env, 1);
119 }
120
121 void cfg_warn (char *msg,...)
122 {
123      va_list ap;
124
125      va_start (ap, msg);
126      prom_printf ("Config file warning: ");
127      prom_vprintf (msg, ap);
128      va_end (ap);
129      prom_printf (" near line %d in file %s\n", line_num, file_name);
130 }
131
132 inline int getc ()
133 {
134      if (currp == endp)
135           return EOF;
136      return *currp++;
137 }
138
139 #define next_raw next
140 static int next (void)
141 {
142      int ch;
143
144      if (!back)
145           return getc ();
146      ch = back;
147      back = 0;
148      return ch;
149 }
150
151 static void again (int ch)
152 {
153      back = ch;
154 }
155
156 static char *cfg_get_token (void)
157 {
158      char buf[MAX_TOKEN + 1];
159      char *here;
160      int ch, escaped;
161
162      if (last_token) {
163           here = last_token;
164           last_token = NULL;
165           return here;
166      }
167      while (1) {
168           while (ch = next (), ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')
169                if (ch == '\n' || ch == '\r')
170                     line_num++;
171           if (ch == EOF || ch == (int)NULL)
172                return NULL;
173           if (ch != '#')
174                break;
175           while (ch = next_raw (), (ch != '\n' && ch != '\r'))
176                if (ch == EOF)
177                     return NULL;
178           line_num++;
179      }
180      if (ch == '=')
181           return strdup ("=");
182      if (ch == '"') {
183           here = buf;
184           while (here - buf < MAX_TOKEN) {
185                if ((ch = next ()) == EOF)
186                     cfg_error ("EOF in quoted string");
187                if (ch == '"') {
188                     *here = 0;
189                     return strdup (buf);
190                }
191                if (ch == '\\') {
192                     ch = next ();
193                     switch (ch) {
194                     case '"':
195                     case '\\':
196                          break;
197                     case '\n':
198                     case '\r':
199                          while ((ch = next ()), ch == ' ' || ch == '\t');
200                          if (!ch)
201                               continue;
202                          again (ch);
203                          ch = ' ';
204                          break;
205                     case 'n':
206                          ch = '\n';
207                          break;
208                     default:
209                          cfg_error ("Bad use of \\ in quoted string");
210                     }
211                } else if ((ch == '\n') || (ch == '\r'))
212                     cfg_error ("newline is not allowed in quoted strings");
213                *here++ = ch;
214           }
215           cfg_error ("Quoted string is too long");
216           return 0;             /* not reached */
217      }
218      here = buf;
219      escaped = 0;
220      while (here - buf < MAX_TOKEN) {
221           if (escaped) {
222                if (ch == EOF)
223                     cfg_error ("\\ precedes EOF");
224                if (ch == '\n')
225                     line_num++;
226                else
227                     *here++ = ch == '\t' ? ' ' : ch;
228                escaped = 0;
229           } else {
230                if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == '#' ||
231                    ch == '=' || ch == EOF) {
232                     again (ch);
233                     *here = 0;
234                     return strdup (buf);
235                }
236                if (!(escaped = (ch == '\\')))
237                     *here++ = ch;
238           }
239           ch = next ();
240      }
241      cfg_error ("Token is too long");
242      return 0;                  /* not reached */
243 }
244
245 static void cfg_return_token (char *token)
246 {
247      last_token = token;
248 }
249
250 static int cfg_next (char **item, char **value)
251 {
252      char *this;
253
254      if (last_item) {
255           *item = last_item;
256           *value = last_value;
257           last_item = NULL;
258           return 1;
259      }
260      *value = NULL;
261      if (!(*item = cfg_get_token ()))
262           return 0;
263      if (!strcmp (*item, "="))
264           cfg_error ("Syntax error");
265      if (!(this = cfg_get_token ()))
266           return 1;
267      if (strcmp (this, "=")) {
268           cfg_return_token (this);
269           return 1;
270      }
271      if (!(*value = cfg_get_token ()))
272           cfg_error ("Value expected at EOF");
273      if (!strcmp (*value, "="))
274           cfg_error ("Syntax error after %s", *item);
275      return 1;
276 }
277
278 #if 0
279 // The one and only call to this procedure is commented out
280 // below, so we don't need this unless we decide to use it again.
281 static void cfg_return (char *item, char *value)
282 {
283      last_item = item;
284      last_value = value;
285 }
286 #endif
287
288 static int cfg_set (char *item, char *value)
289 {
290      CONFIG *walk;
291
292      if (!strcasecmp (item, "image")) {
293           struct IMAGES **p = &images;
294
295           while (*p)
296                p = &((*p)->next);
297           *p = (struct IMAGES *)malloc (sizeof (struct IMAGES));
298           if (*p == NULL) {
299                prom_printf("malloc error in cfg_set\n");
300                return -1;
301           }
302           (*p)->next = 0;
303           curr_table = ((*p)->table);
304           memcpy (curr_table, cf_image, sizeof (cf_image));
305      }
306      for (walk = curr_table; walk->type != cft_end; walk++) {
307           if (walk->name && !strcasecmp (walk->name, item)) {
308                if (value && walk->type != cft_strg)
309                     cfg_warn ("'%s' doesn't have a value", walk->name);
310                else if (!value && walk->type == cft_strg)
311                     cfg_warn ("Value expected for '%s'", walk->name);
312                else {
313                     if (walk->data)
314                          cfg_warn ("Duplicate entry '%s'", walk->name);
315                     if (walk->type == cft_flag)
316                          walk->data = &flag_set;
317                     else if (walk->type == cft_strg)
318                          walk->data = value;
319                }
320                break;
321           }
322      }
323      if (walk->type != cft_end)
324           return 1;
325 //    cfg_return (item, value);
326      return 0;
327 }
328
329 int cfg_parse (char *cfg_file, char *buff, int len)
330 {
331      char *item, *value;
332
333      file_name = cfg_file;
334      currp = buff;
335      endp = currp + len;
336
337      if (setjmp (env))
338           return -1;
339      while (1) {
340           if (!cfg_next (&item, &value))
341                return 0;
342           if (!cfg_set (item, value)) {
343 #if DEBUG
344                prom_printf("Can't set item %s to value %s\n", item, value);
345 #endif      
346           }
347           free (item);
348      }
349 }
350
351 static char *cfg_get_strg_i (CONFIG * table, char *item)
352 {
353      CONFIG *walk;
354
355      for (walk = table; walk->type != cft_end; walk++)
356           if (walk->name && !strcasecmp (walk->name, item))
357                return walk->data;
358      return 0;
359 }
360
361 char *cfg_get_strg (char *image, char *item)
362 {
363      struct IMAGES *p;
364      char *label, *alias;
365      char *ret;
366
367      if (!image)
368           return cfg_get_strg_i (cf_options, item);
369      for (p = images; p; p = p->next) {
370           label = cfg_get_strg_i (p->table, "label");
371           if (!label) {
372                label = cfg_get_strg_i (p->table, "image");
373                alias = strrchr (label, '/');
374                if (alias)
375                     label = alias + 1;
376           }
377           alias = cfg_get_strg_i (p->table, "alias");
378           if (!strcmp (label, image) || (alias && !strcmp (alias, image))) {
379                ret = cfg_get_strg_i (p->table, item);
380                if (!ret)
381                     ret = cfg_get_strg_i (cf_options, item);
382                return ret;
383           }
384      }
385      return 0;
386 }
387
388 int cfg_get_flag (char *image, char *item)
389 {
390      return !!cfg_get_strg (image, item);
391 }
392
393 static int printl_count = 0;
394 static void printlabel (char *label, int defflag)
395 {
396      int len = strlen (label);
397
398      if (!printl_count)
399           prom_printf ("\n");
400      prom_printf ("%s %s",defflag?"*":" ", label);
401      while (len++ < 25)
402           prom_putchar (' ');
403      printl_count++;
404      if (printl_count == 3)
405           printl_count = 0;
406 }
407
408 void cfg_print_images (void)
409 {
410      struct IMAGES *p;
411      char *label, *alias;
412
413      char *ret = cfg_get_strg_i (cf_options, "default");
414      int defflag=0;
415
416      printl_count = 0;
417      for (p = images; p; p = p->next) {
418           label = cfg_get_strg_i (p->table, "label");
419           if (!label) {
420                label = cfg_get_strg_i (p->table, "image");
421                alias = strrchr (label, '/');
422                if (alias)
423                     label = alias + 1;
424           }
425           if(!strcmp(ret,label))
426                defflag=1;
427           else
428                defflag=0;
429           alias = cfg_get_strg_i (p->table, "alias");
430           printlabel (label, defflag);
431           if (alias)
432                printlabel (alias, 0);
433      }
434      prom_printf("\n");
435 }
436
437 char *cfg_get_default (void)
438 {
439      char *label;
440      char *ret = cfg_get_strg_i (cf_options, "default");
441
442      if (ret)
443           return ret;
444      if (!images)
445           return 0;
446      ret = cfg_get_strg_i (images->table, "label");
447      if (!ret) {
448           ret = cfg_get_strg_i (images->table, "image");
449           label = strrchr (ret, '/');
450           if (label)
451                ret = label + 1;
452      }
453      return ret;
454 }
455
456 /* 
457  * Local variables:
458  * c-file-style: "K&R"
459  * c-basic-offset: 5
460  * End:
461  */