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