Commit yaboot 1.3.4-pre3
[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 }