Commit yaboot 1.3.0
[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, "splash", NULL},
66     {cft_strg, "fgcolor", NULL},
67     {cft_strg, "bgcolor", 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, "splash", NULL},
92     {cft_strg, "sysmap", NULL},
93     {cft_end, NULL, NULL}};
94
95 static char flag_set;
96 static char *last_token = NULL, *last_item = NULL, *last_value = NULL;
97 static int line_num;
98 static int back = 0;            /* can go back by one char */
99 static char *currp = NULL;
100 static char *endp = NULL;
101 static char *file_name = NULL;
102 static CONFIG *curr_table = cf_options;
103 static jmp_buf env;
104
105 static struct IMAGES {
106     CONFIG table[sizeof (cf_image) / sizeof (cf_image[0])];
107     struct IMAGES *next;
108 } *images = NULL;
109
110 void cfg_error (char *msg,...)
111 {
112     va_list ap;
113
114     va_start (ap, msg);
115     prom_printf ("Config file error: ");
116     prom_vprintf (msg, ap);
117     va_end (ap);
118     prom_printf (" near line %d in file %s\n", line_num, file_name);
119     longjmp (env, 1);
120 }
121
122 void cfg_warn (char *msg,...)
123 {
124     va_list ap;
125
126     va_start (ap, msg);
127     prom_printf ("Config file warning: ");
128     prom_vprintf (msg, ap);
129     va_end (ap);
130     prom_printf (" near line %d in file %s\n", line_num, file_name);
131 }
132
133 inline int getc ()
134 {
135     if (currp == endp)
136         return EOF;
137     return *currp++;
138 }
139
140 #define next_raw next
141 static int next (void)
142 {
143     int ch;
144
145     if (!back)
146         return getc ();
147     ch = back;
148     back = 0;
149     return ch;
150 }
151
152 static void again (int ch)
153 {
154     back = ch;
155 }
156
157 static char *cfg_get_token (void)
158 {
159     char buf[MAX_TOKEN + 1];
160     char *here;
161     int ch, escaped;
162
163     if (last_token) {
164         here = last_token;
165         last_token = NULL;
166         return here;
167     }
168     while (1) {
169         while (ch = next (), ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')
170             if (ch == '\n' || ch == '\r')
171                 line_num++;
172         if (ch == EOF || ch == (int)NULL)
173             return NULL;
174         if (ch != '#')
175             break;
176         while (ch = next_raw (), (ch != '\n' && ch != '\r'))
177             if (ch == EOF)
178                 return NULL;
179         line_num++;
180     }
181     if (ch == '=')
182         return strdup ("=");
183     if (ch == '"') {
184         here = buf;
185         while (here - buf < MAX_TOKEN) {
186             if ((ch = next ()) == EOF)
187                 cfg_error ("EOF in quoted string");
188             if (ch == '"') {
189                 *here = 0;
190                 return strdup (buf);
191             }
192             if (ch == '\\') {
193                 ch = next ();
194                 switch (ch) {
195                 case '"':
196                 case '\\':
197                     break;
198                 case '\n':
199                 case '\r':
200                     while ((ch = next ()), ch == ' ' || ch == '\t');
201                     if (!ch)
202                         continue;
203                     again (ch);
204                     ch = ' ';
205                     break;
206                 case 'n':
207                     ch = '\n';
208                     break;
209                 default:
210                     cfg_error ("Bad use of \\ in quoted string");
211                 }
212             } else if ((ch == '\n') || (ch == '\r'))
213                 cfg_error ("newline is not allowed in quoted strings");
214             *here++ = ch;
215         }
216         cfg_error ("Quoted string is too long");
217         return 0;               /* not reached */
218     }
219     here = buf;
220     escaped = 0;
221     while (here - buf < MAX_TOKEN) {
222         if (escaped) {
223             if (ch == EOF)
224                 cfg_error ("\\ precedes EOF");
225             if (ch == '\n')
226                 line_num++;
227             else
228                 *here++ = ch == '\t' ? ' ' : ch;
229             escaped = 0;
230         } else {
231             if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == '#' ||
232                 ch == '=' || ch == EOF) {
233                 again (ch);
234                 *here = 0;
235                 return strdup (buf);
236             }
237             if (!(escaped = (ch == '\\')))
238                 *here++ = ch;
239         }
240         ch = next ();
241     }
242     cfg_error ("Token is too long");
243     return 0;                   /* not reached */
244 }
245
246 static void cfg_return_token (char *token)
247 {
248     last_token = token;
249 }
250
251 static int cfg_next (char **item, char **value)
252 {
253     char *this;
254
255     if (last_item) {
256         *item = last_item;
257         *value = last_value;
258         last_item = NULL;
259         return 1;
260     }
261     *value = NULL;
262     if (!(*item = cfg_get_token ()))
263         return 0;
264     if (!strcmp (*item, "="))
265         cfg_error ("Syntax error");
266     if (!(this = cfg_get_token ()))
267         return 1;
268     if (strcmp (this, "=")) {
269         cfg_return_token (this);
270         return 1;
271     }
272     if (!(*value = cfg_get_token ()))
273         cfg_error ("Value expected at EOF");
274     if (!strcmp (*value, "="))
275         cfg_error ("Syntax error after %s", *item);
276     return 1;
277 }
278
279 #if 0
280 // The one and only call to this procedure is commented out
281 // below, so we don't need this unless we decide to use it again.
282 static void cfg_return (char *item, char *value)
283 {
284     last_item = item;
285     last_value = value;
286 }
287 #endif
288
289 static int cfg_set (char *item, char *value)
290 {
291     CONFIG *walk;
292
293     if (!strcasecmp (item, "image")) {
294         struct IMAGES **p = &images;
295
296         while (*p)
297             p = &((*p)->next);
298         *p = (struct IMAGES *)malloc (sizeof (struct IMAGES));
299         if (*p == NULL) {
300                 prom_printf("malloc error in cfg_set\n");
301                 return -1;
302         }
303         (*p)->next = 0;
304         curr_table = ((*p)->table);
305         memcpy (curr_table, cf_image, sizeof (cf_image));
306     }
307     for (walk = curr_table; walk->type != cft_end; walk++) {
308         if (walk->name && !strcasecmp (walk->name, item)) {
309             if (value && walk->type != cft_strg)
310                 cfg_warn ("'%s' doesn't have a value", walk->name);
311             else if (!value && walk->type == cft_strg)
312                 cfg_warn ("Value expected for '%s'", walk->name);
313             else {
314                 if (walk->data)
315                     cfg_warn ("Duplicate entry '%s'", walk->name);
316                 if (walk->type == cft_flag)
317                     walk->data = &flag_set;
318                 else if (walk->type == cft_strg)
319                     walk->data = value;
320             }
321             break;
322         }
323     }
324     if (walk->type != cft_end)
325         return 1;
326 //    cfg_return (item, value);
327     return 0;
328 }
329
330 int cfg_parse (char *cfg_file, char *buff, int len)
331 {
332     char *item, *value;
333
334     file_name = cfg_file;
335     currp = buff;
336     endp = currp + len;
337
338     if (setjmp (env))
339         return -1;
340     while (1) {
341         if (!cfg_next (&item, &value))
342             return 0;
343         if (!cfg_set (item, value)) {
344 #if DEBUG
345             prom_printf("Can't set item %s to value %s\n", item, value);
346 #endif      
347         }
348         free (item);
349     }
350 }
351
352 static char *cfg_get_strg_i (CONFIG * table, char *item)
353 {
354     CONFIG *walk;
355
356     for (walk = table; walk->type != cft_end; walk++)
357         if (walk->name && !strcasecmp (walk->name, item))
358             return walk->data;
359     return 0;
360 }
361
362 char *cfg_get_strg (char *image, char *item)
363 {
364     struct IMAGES *p;
365     char *label, *alias;
366     char *ret;
367
368     if (!image)
369         return cfg_get_strg_i (cf_options, item);
370     for (p = images; p; p = p->next) {
371         label = cfg_get_strg_i (p->table, "label");
372         if (!label) {
373             label = cfg_get_strg_i (p->table, "image");
374             alias = strrchr (label, '/');
375             if (alias)
376                 label = alias + 1;
377         }
378         alias = cfg_get_strg_i (p->table, "alias");
379         if (!strcmp (label, image) || (alias && !strcmp (alias, image))) {
380             ret = cfg_get_strg_i (p->table, item);
381             if (!ret)
382                 ret = cfg_get_strg_i (cf_options, item);
383             return ret;
384         }
385     }
386     return 0;
387 }
388
389 int cfg_get_flag (char *image, char *item)
390 {
391     return !!cfg_get_strg (image, item);
392 }
393
394 static int printl_count = 0;
395 static void printlabel (char *label, int defflag)
396 {
397     int len = strlen (label);
398
399     if (!printl_count)
400         prom_printf ("\n");
401     prom_printf ("%s %s",defflag?"*":" ", label);
402     while (len++ < 25)
403         prom_putchar (' ');
404     printl_count++;
405     if (printl_count == 3)
406         printl_count = 0;
407 }
408
409 void cfg_print_images (void)
410 {
411     struct IMAGES *p;
412     char *label, *alias;
413
414     char *ret = cfg_get_strg_i (cf_options, "default");
415     int defflag=0;
416
417     printl_count = 0;
418     for (p = images; p; p = p->next) {
419         label = cfg_get_strg_i (p->table, "label");
420         if (!label) {
421             label = cfg_get_strg_i (p->table, "image");
422             alias = strrchr (label, '/');
423             if (alias)
424                 label = alias + 1;
425         }
426         if(!strcmp(ret,label))
427                 defflag=1;
428         else
429                 defflag=0;
430         alias = cfg_get_strg_i (p->table, "alias");
431         printlabel (label, defflag);
432         if (alias)
433             printlabel (alias, 0);
434     }
435     prom_printf ("\n\nYou can also type in custom image locations, in the form\n"
436             "{prom_path;}partno/path_to_image or {prom_path;}{partno}[start-end]\n"
437             "Example: hd:3,/vmlinux\n\n");
438 }
439
440 char *cfg_get_default (void)
441 {
442     char *label;
443     char *ret = cfg_get_strg_i (cf_options, "default");
444
445     if (ret)
446         return ret;
447     if (!images)
448         return 0;
449     ret = cfg_get_strg_i (images->table, "label");
450     if (!ret) {
451         ret = cfg_get_strg_i (images->table, "image");
452         label = strrchr (ret, '/');
453         if (label)
454             ret = label + 1;
455     }
456     return ret;
457 }