| /* Handling and parsing of silo.conf |
| |
| Copyright (C) 1995 Werner Almesberger |
| 1996,1998 Jakub Jelinek |
| 2001 Ben Collins |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
| USA. */ |
| |
| /* This file gets included by the silo program (userspace), try to be nice */ |
| #ifndef SILOCONFCHECK |
| |
| #include <silo.h> |
| #include <stringops.h> |
| #include <setjmp.h> |
| #ifndef NULL |
| #define NULL (void *)0 |
| #endif |
| #include <promlib.h> |
| |
| #endif /* !SILOCONFCHECK */ |
| |
| typedef enum { |
| cft_strg, cft_flag, cft_end |
| } CONFIG_TYPE; |
| |
| typedef struct { |
| CONFIG_TYPE type; |
| char *name; |
| void *data; |
| } CONFIG; |
| |
| #define MAX_TOKEN 200 |
| #define MAX_VAR_NAME MAX_TOKEN |
| #ifndef EOF |
| #define EOF -1 |
| #endif |
| |
| CONFIG cf_options[] = |
| { |
| {cft_strg, "device", NULL}, |
| {cft_strg, "partition", NULL}, |
| {cft_strg, "default", NULL}, |
| {cft_strg, "timeout", NULL}, |
| {cft_strg, "message", NULL}, |
| {cft_strg, "root", NULL}, |
| {cft_strg, "ramdisk", NULL}, |
| {cft_flag, "read-only", NULL}, |
| {cft_flag, "read-write", NULL}, |
| {cft_strg, "append", NULL}, |
| {cft_strg, "initrd", NULL}, |
| {cft_flag, "initrd-prompt", NULL}, |
| {cft_strg, "initrd-size", NULL}, |
| {cft_flag, "pause-after", NULL}, |
| {cft_strg, "pause-message", NULL}, |
| {cft_flag, "fill-reboot-cmd", NULL}, |
| {cft_strg, "password", NULL}, |
| {cft_flag, "restricted", NULL}, |
| {cft_strg, "proll", NULL}, |
| {cft_flag, "partition-boot", NULL}, |
| {cft_strg, "secondary", NULL}, |
| {cft_end, NULL, NULL}}; |
| |
| CONFIG cf_image[] = |
| { |
| {cft_strg, "image", NULL}, |
| {cft_strg, "other", NULL}, |
| {cft_strg, "label", NULL}, |
| {cft_strg, "alias", NULL}, |
| {cft_strg, "device", NULL}, |
| {cft_strg, "partition", NULL}, |
| {cft_strg, "root", NULL}, |
| {cft_strg, "ramdisk", NULL}, |
| {cft_flag, "read-only", NULL}, |
| {cft_flag, "read-write", NULL}, |
| {cft_strg, "append", NULL}, |
| {cft_strg, "literal", NULL}, |
| {cft_strg, "initrd", NULL}, |
| {cft_flag, "initrd-prompt", NULL}, |
| {cft_strg, "initrd-size", NULL}, |
| {cft_flag, "pause-after", NULL}, |
| {cft_strg, "pause-message", NULL}, |
| {cft_flag, "solaris", NULL}, |
| {cft_flag, "flash", NULL}, |
| {cft_flag, "fill-reboot-cmd", NULL}, |
| {cft_strg, "bootblock", NULL}, |
| {cft_flag, "single-key", NULL}, |
| {cft_strg, "proll", NULL}, |
| {cft_end, NULL, NULL}}; |
| |
| static char flag_set; |
| static char *last_token = NULL, *last_item = NULL, *last_value = NULL; |
| static int line_num; |
| static int back = 0; /* can go back by one char */ |
| static char *currp; |
| static char *endp; |
| static char *file_name; |
| static CONFIG *curr_table = cf_options; |
| static jmp_buf env; |
| |
| static struct IMAGES { |
| CONFIG table[sizeof (cf_image) / sizeof (cf_image[0])]; |
| struct IMAGES *next; |
| } *images = NULL; |
| |
| void cfg_error (char *msg,...) |
| { |
| va_list ap; |
| |
| va_start (ap, msg); |
| printf ("Config file error: "); |
| vprintf (msg, ap); |
| va_end (ap); |
| printf (" near line %d in file %s\n", line_num, file_name); |
| longjmp (env, 1); |
| } |
| |
| inline int my_getc () |
| { |
| if (currp == endp) |
| return EOF; |
| return *currp++; |
| } |
| |
| #define next_raw next |
| static int next (void) |
| { |
| int ch; |
| |
| if (!back) |
| return my_getc (); |
| ch = back; |
| back = 0; |
| return ch; |
| } |
| |
| static void again (int ch) |
| { |
| back = ch; |
| } |
| |
| static char *cfg_get_token (void) |
| { |
| char buf[MAX_TOKEN + 1]; |
| char *here; |
| int ch, escaped; |
| |
| if (last_token) { |
| here = last_token; |
| last_token = NULL; |
| return here; |
| } |
| while (1) { |
| while (ch = next (), ch == ' ' || ch == '\t' || ch == '\n') |
| if (ch == '\n') |
| line_num++; |
| if (ch == EOF) |
| return NULL; |
| if (ch != '#') |
| break; |
| while (ch = next_raw (), ch != '\n') |
| if (ch == EOF) |
| return NULL; |
| line_num++; |
| } |
| if (ch == '=') |
| return strdup ("="); |
| if (ch == '"') { |
| here = buf; |
| while (here - buf < MAX_TOKEN) { |
| if ((ch = next ()) == EOF) |
| cfg_error ("EOF in quoted string"); |
| if (ch == '"') { |
| *here = 0; |
| return strdup (buf); |
| } |
| if (ch == '\\') { |
| ch = next (); |
| if (ch != '"' && ch != '\\' && ch != '\n') |
| cfg_error ("Bad use of \\ in quoted string"); |
| if (ch == '\n') { |
| while ((ch = next ()), ch == ' ' || ch == '\t'); |
| if (!ch) |
| continue; |
| again (ch); |
| ch = ' '; |
| } |
| } |
| if (ch == '\n' || ch == '\t') |
| cfg_error ("\\n and \\t are not allowed in quoted strings"); |
| *here++ = ch; |
| } |
| cfg_error ("Quoted string is too long"); |
| return 0; /* not reached */ |
| } |
| here = buf; |
| escaped = 0; |
| while (here - buf < MAX_TOKEN) { |
| if (escaped) { |
| if (ch == EOF) |
| cfg_error ("\\ precedes EOF"); |
| if (ch == '\n') |
| line_num++; |
| else |
| *here++ = ch == '\t' ? ' ' : ch; |
| escaped = 0; |
| } else { |
| if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '#' || |
| ch == '=' || ch == EOF) { |
| again (ch); |
| *here = 0; |
| return strdup (buf); |
| } |
| if (!(escaped = (ch == '\\'))) |
| *here++ = ch; |
| } |
| ch = next (); |
| } |
| cfg_error ("Token is too long"); |
| return 0; /* not reached */ |
| } |
| |
| static void cfg_return_token (char *token) |
| { |
| last_token = token; |
| } |
| |
| static int cfg_next (char **item, char **value) |
| { |
| char *this; |
| |
| if (last_item) { |
| *item = last_item; |
| *value = last_value; |
| last_item = NULL; |
| return 1; |
| } |
| *value = NULL; |
| if (!(*item = cfg_get_token ())) |
| return 0; |
| if (!strcmp (*item, "=")) |
| cfg_error ("Syntax error"); |
| if (!(this = cfg_get_token ())) |
| return 1; |
| if (strcmp (this, "=")) { |
| cfg_return_token (this); |
| return 1; |
| } |
| if (!(*value = cfg_get_token ())) |
| cfg_error ("Value expected at EOF"); |
| if (!strcmp (*value, "=")) |
| cfg_error ("Syntax error after %s", *item); |
| return 1; |
| } |
| |
| static void cfg_return (char *item, char *value) |
| { |
| last_item = item; |
| last_value = value; |
| } |
| |
| static int cfg_set (char *item, char *value) |
| { |
| CONFIG *walk; |
| |
| if (!strncasecmp (item, "image", 5) || !strcasecmp (item, "other")) { |
| struct IMAGES **p = &images; |
| |
| if (item[5] == '[' && item[strlen(item) - 1] == ']') { |
| char *p, *q = item; |
| |
| item[5] = 0; |
| for (p = item + 6; q; p = q) { |
| q = strchr(p, ','); |
| if (q) |
| *q++ = 0; |
| if (strncasecmp (p, "sun4", 4)) |
| return 0; |
| switch (tolower(p[4])) { |
| case 0: |
| case ']': if (architecture == sun4) goto cfg_set_cont; break; |
| case 'c': if (architecture == sun4c) goto cfg_set_cont; break; |
| case 'd': if (architecture == sun4d) goto cfg_set_cont; break; |
| case 'e': if (architecture == sun4e) goto cfg_set_cont; break; |
| case 'm': if (architecture == sun4m) goto cfg_set_cont; break; |
| case 'u': if (architecture == sun4u) goto cfg_set_cont; break; |
| default: return 0; |
| } |
| } |
| curr_table = (CONFIG *)malloc(sizeof (cf_image)); |
| memcpy (curr_table, cf_image, sizeof (cf_image)); |
| goto cfg_set_redo; |
| } |
| if (item[5]) |
| return 0; |
| cfg_set_cont: |
| while (*p) |
| p = &((*p)->next); |
| *p = malloc (sizeof (struct IMAGES)); |
| (*p)->next = 0; |
| curr_table = ((*p)->table); |
| memcpy (curr_table, cf_image, sizeof (cf_image)); |
| } |
| cfg_set_redo: |
| for (walk = curr_table; walk->type != cft_end; walk++) { |
| if (walk->name && !strcasecmp (walk->name, item)) { |
| if (value && walk->type != cft_strg) |
| cfg_error ("'%s' doesn't have a value", walk->name); |
| if (!value && walk->type == cft_strg) |
| cfg_error ("Value expected for '%s'", walk->name); |
| if (walk->data) |
| cfg_error ("Duplicate entry '%s'", walk->name); |
| if (walk->type == cft_flag) |
| walk->data = &flag_set; |
| else if (walk->type == cft_strg) |
| walk->data = value; |
| break; |
| } |
| } |
| if (walk->type != cft_end) { |
| if (!strcasecmp (item, "other")) { |
| item = "image"; |
| goto cfg_set_redo; |
| } |
| return 1; |
| } |
| cfg_return (item, value); |
| return 0; |
| } |
| |
| int cfg_parse (char *cfg_file, char *buff, int len) |
| { |
| char *item, *value; |
| |
| file_name = cfg_file; |
| currp = buff; |
| endp = currp + len; |
| if (setjmp (env)) |
| return -1; |
| while (1) { |
| if (!cfg_next (&item, &value)) |
| return 0; |
| if (!cfg_set (item, value)) |
| return -1; |
| free (item); |
| } |
| } |
| |
| static char *cfg_get_strg_i (CONFIG * table, char *item) |
| { |
| CONFIG *walk; |
| |
| for (walk = table; walk->type != cft_end; walk++) |
| if (walk->name && !strcasecmp (walk->name, item)) |
| return walk->data; |
| return 0; |
| } |
| |
| char *cfg_get_strg (char *image, char *item) |
| { |
| struct IMAGES *p; |
| char *label, *alias; |
| char *ret; |
| |
| if (!image) |
| return cfg_get_strg_i (cf_options, item); |
| for (p = images; p; p = p->next) { |
| label = cfg_get_strg_i (p->table, "label"); |
| if (!label) { |
| label = cfg_get_strg_i (p->table, "image"); |
| alias = strrchr (label, '/'); |
| if (alias) |
| label = alias + 1; |
| } |
| alias = cfg_get_strg_i (p->table, "alias"); |
| if (!strcmp (label, image) || (alias && !strcmp (alias, image))) { |
| ret = cfg_get_strg_i (p->table, item); |
| if (!ret) |
| ret = cfg_get_strg_i (cf_options, item); |
| return ret; |
| } |
| } |
| return 0; |
| } |
| |
| int cfg_get_flag (char *image, char *item) |
| { |
| return !!cfg_get_strg (image, item); |
| } |
| |
| static int printl_count = 0; |
| static void printlabel (const char *label, char **addr, const char *match) |
| { |
| int len = strlen (label); |
| |
| if (match == NULL || |
| (strlen(label) >= strlen(match) && |
| !strncmp(match, label, strlen(match)))) { |
| |
| if (*addr != NULL) { |
| strcpy(*addr, label); |
| *addr += strlen(label) + 1; |
| **addr = '\0'; |
| } else { |
| if (!(printl_count % 3)) |
| printf ("\n"); |
| printf ("%s", label); |
| while (len++ < 25) |
| putchar (' '); |
| } |
| printl_count++; |
| } |
| } |
| |
| int cfg_print_images (char *addr, char *match) |
| { |
| struct IMAGES *p; |
| char *label, *alias; |
| |
| printl_count = 0; |
| if (addr) addr[0] = '\0'; |
| |
| for (p = images; p; p = p->next) { |
| label = cfg_get_strg_i (p->table, "label"); |
| if (!label) { |
| label = cfg_get_strg_i (p->table, "image"); |
| alias = strrchr (label, '/'); |
| if (alias) |
| label = alias + 1; |
| } |
| alias = cfg_get_strg_i (p->table, "alias"); |
| printlabel(label, &addr, match); |
| if (alias) |
| printlabel (alias, &addr, match); |
| } |
| if (!addr && printl_count) |
| printf ("\n"); |
| return printl_count; |
| } |
| |
| char *cfg_get_default (void) |
| { |
| char *label; |
| char *ret = cfg_get_strg_i (cf_options, "default"); |
| |
| if (ret) |
| return ret; |
| if (!images) |
| return 0; |
| ret = cfg_get_strg_i (images->table, "label"); |
| if (!ret) { |
| ret = cfg_get_strg_i (images->table, "image"); |
| label = strrchr (ret, '/'); |
| if (label) |
| ret = label + 1; |
| } |
| return ret; |
| } |