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