| /* |
| * cfg.c - Handling and parsing of yaboot.conf |
| * |
| * Copyright (C) 1995 Werner Almesberger |
| * 1996 Jakub Jelinek |
| * |
| * 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. |
| */ |
| |
| #include "setjmp.h" |
| #include "stdarg.h" |
| #include "stdlib.h" |
| #include "string.h" |
| #include "types.h" |
| #include "prom.h" |
| |
| /* Imported functions */ |
| extern int strcasecmp(const char *s1, const char *s2); |
| extern int strncasecmp(const char *cs, const char *ct, size_t n); |
| extern char bootoncelabel[1024]; |
| |
| 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 |
| #define EOF -1 |
| |
| CONFIG cf_options[] = |
| { |
| {cft_strg, "device", NULL}, |
| {cft_strg, "partition", NULL}, |
| {cft_strg, "default", NULL}, |
| {cft_strg, "timeout", NULL}, |
| {cft_strg, "password", NULL}, |
| {cft_flag, "restricted", 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_strg, "init-code", NULL}, |
| {cft_strg, "init-message", NULL}, |
| {cft_strg, "fgcolor", NULL}, |
| {cft_strg, "bgcolor", NULL}, |
| {cft_strg, "ptypewarning", NULL}, |
| {cft_end, NULL, NULL}}; |
| |
| CONFIG cf_image[] = |
| { |
| {cft_strg, "image", NULL}, |
| {cft_strg, "label", NULL}, |
| {cft_strg, "alias", NULL}, |
| {cft_flag, "single-key", NULL}, |
| {cft_flag, "restricted", 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, "novideo", NULL}, |
| {cft_strg, "sysmap", 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 = NULL; |
| static char *endp = NULL; |
| static char *file_name = NULL; |
| static CONFIG *curr_table = cf_options; |
| static int ignore_entry; |
| static jmp_buf env; |
| |
| static struct IMAGES { |
| CONFIG table[sizeof (cf_image) / sizeof (cf_image[0])]; |
| int obsolete; |
| struct IMAGES *next; |
| } *images = NULL; |
| |
| void cfg_error (char *msg,...) |
| { |
| va_list ap; |
| |
| va_start (ap, msg); |
| prom_printf ("Config file error: "); |
| prom_vprintf (msg, ap); |
| va_end (ap); |
| prom_printf (" near line %d in file %s\n", line_num, file_name); |
| longjmp (env, 1); |
| } |
| |
| void cfg_warn (char *msg,...) |
| { |
| va_list ap; |
| |
| va_start (ap, msg); |
| prom_printf ("Config file warning: "); |
| prom_vprintf (msg, ap); |
| va_end (ap); |
| prom_printf (" near line %d in file %s\n", line_num, file_name); |
| } |
| |
| inline int getc () |
| { |
| if (currp == endp) |
| return EOF; |
| return *currp++; |
| } |
| |
| #define next_raw next |
| static int next (void) |
| { |
| int ch; |
| |
| if (!back) |
| return 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' || ch == '\r') |
| if (ch == '\n' || ch == '\r') |
| line_num++; |
| if (ch == EOF || ch == (int)NULL) |
| return NULL; |
| if (ch != '#') |
| break; |
| while (ch = next_raw (), (ch != '\n' && ch != '\r')) |
| 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 (); |
| switch (ch) { |
| case '"': |
| case '\\': |
| break; |
| case '\n': |
| case '\r': |
| while ((ch = next ()), ch == ' ' || ch == '\t'); |
| if (!ch) |
| continue; |
| again (ch); |
| ch = ' '; |
| break; |
| case 'n': |
| ch = '\n'; |
| break; |
| default: |
| cfg_error ("Bad use of \\ in quoted string"); |
| } |
| } else if ((ch == '\n') || (ch == '\r')) |
| cfg_error ("newline is 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 == '\r' || 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 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; |
| } |
| |
| #if 0 |
| // The one and only call to this procedure is commented out |
| // below, so we don't need this unless we decide to use it again. |
| static void cfg_return (char *item, char *value) |
| { |
| last_item = item; |
| last_value = value; |
| } |
| #endif |
| |
| static int match_arch(const char *name) |
| { |
| static prom_handle root; |
| static char model[256], *p; |
| |
| if (!root) { |
| if (!(root = prom_finddevice("/"))) |
| return 0; |
| } |
| |
| if (!model[0]) { |
| if (!prom_getprop(root, "compatible", model, sizeof(model))) |
| return 0; |
| } |
| |
| for (p = model; *p; p += strlen(p) + 1) { |
| if (!strcasecmp(p, name)) |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| static void check_for_obsolete(const char *label) |
| { |
| struct IMAGES *p; |
| char *cur_label; |
| |
| /* Make sure our current entry isn't obsolete (ignored) */ |
| for (p = images; p; p = p->next) { |
| if (curr_table == p->table && p->obsolete) |
| return; |
| } |
| |
| for (p = images; p; p = p->next) { |
| if (curr_table == p->table) |
| continue; |
| |
| cur_label = cfg_get_strg_i (p->table, "label"); |
| if (!cur_label) |
| cur_label = cfg_get_strg_i (p->table, "image"); |
| |
| if (!cur_label) |
| continue; |
| |
| if (!strcasecmp(cur_label, label)) |
| p->obsolete = 1; |
| } |
| } |
| |
| static int cfg_set (char *item, char *value) |
| { |
| CONFIG *walk; |
| |
| if (!strncasecmp (item, "image", 5)) { |
| struct IMAGES **p = &images; |
| int ignore = 0; |
| |
| if (item[5] == '[' && item[strlen(item) - 1] == ']') { |
| char *s, *q = item; |
| |
| /* Get rid of braces */ |
| item[strlen(item) - 1] = 0; |
| item[5] = 0; |
| |
| for (s = item + 6; q; s = q) { |
| q = strchr(s, '|'); |
| if (q) |
| *q++ = 0; |
| |
| if (match_arch(s)) |
| goto cfg_set_cont; |
| } |
| /* This just creates an unused table. It will get ignored */ |
| ignore = 1; |
| } else if (item[5]) |
| goto cfg_set_redo; |
| |
| cfg_set_cont: |
| while (*p) |
| p = &((*p)->next); |
| *p = (struct IMAGES *)malloc (sizeof (struct IMAGES)); |
| if (*p == NULL) { |
| prom_printf("malloc error in cfg_set\n"); |
| return -1; |
| } |
| (*p)->next = 0; |
| (*p)->obsolete = ignore; |
| 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_warn ("'%s' doesn't have a value", walk->name); |
| else if (!value && walk->type == cft_strg) |
| cfg_warn ("Value expected for '%s'", walk->name); |
| else { |
| if (!strcasecmp (item, "label")) |
| check_for_obsolete(value); |
| if (walk->data) |
| cfg_warn ("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) |
| return 1; |
| |
| //cfg_return (item, value); |
| |
| return 0; |
| } |
| |
| static int cfg_reset () |
| { |
| CONFIG *walk; |
| #if DEBUG |
| prom_printf("Resetting image table\n"); |
| #endif |
| line_num = 0; |
| images = NULL; |
| curr_table = NULL; |
| curr_table = cf_options; |
| for (walk = curr_table; walk->type != cft_end; walk++) { |
| #if DEBUG |
| prom_printf("ItemA %s = %s\n", walk->name, walk->data); |
| #endif |
| if (walk->data != NULL) |
| walk->data = NULL; |
| #if DEBUG |
| prom_printf("ItemB %s = %s\n\n", walk->name, walk->data); |
| #endif |
| } |
| |
| return 0; |
| } |
| |
| int cfg_parse (char *cfg_file, char *buff, int len) |
| { |
| char *item, *value; |
| |
| file_name = cfg_file; |
| currp = buff; |
| endp = currp + len; |
| |
| cfg_reset(); |
| |
| if (setjmp (env)) |
| return -1; |
| while (1) { |
| if (!cfg_next (&item, &value)) |
| return 0; |
| if (!cfg_set (item, value)) { |
| #if DEBUG |
| prom_printf("Can't set item %s to value %s\n", item, value); |
| #endif |
| } |
| free (item); |
| } |
| } |
| |
| 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) { |
| if (p->obsolete) |
| continue; |
| |
| 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 (char *label, int defflag) |
| { |
| int len = strlen (label); |
| char a = ' '; |
| |
| if (!printl_count) |
| prom_printf ("\n"); |
| switch (defflag) { |
| case 1: a='*'; break; |
| case 2: a='&'; break; |
| default: a=' '; break; |
| } |
| prom_printf ("%c %s", a, label); |
| while (len++ < 25) |
| prom_putchar (' '); |
| printl_count++; |
| if (printl_count == 3) |
| printl_count = 0; |
| } |
| |
| void cfg_print_images (void) |
| { |
| struct IMAGES *p; |
| char *label, *alias; |
| |
| char *ret = cfg_get_strg_i (cf_options, "default"); |
| int defflag=0; |
| |
| printl_count = 0; |
| for (p = images; p; p = p->next) { |
| if (p->obsolete) |
| continue; |
| |
| 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; |
| } |
| if(!strcmp(bootoncelabel,label)) |
| defflag=2; |
| else if(!strcmp(ret,label)) |
| defflag=1; |
| else |
| defflag=0; |
| alias = cfg_get_strg_i (p->table, "alias"); |
| printlabel (label, defflag); |
| if (alias) |
| printlabel (alias, 0); |
| } |
| prom_printf("\n"); |
| } |
| |
| char *cfg_get_default (void) |
| { |
| char *label; |
| struct IMAGES *p; |
| char *ret = cfg_get_strg_i (cf_options, "default"); |
| |
| if (ret) |
| return ret; |
| if (!images) |
| return 0; |
| |
| for (p = images; p && p->obsolete; p = p->next); |
| if (!p) |
| return 0; |
| |
| ret = cfg_get_strg_i (p->table, "label"); |
| if (!ret) { |
| ret = cfg_get_strg_i (p->table, "image"); |
| label = strrchr (ret, '/'); |
| if (label) |
| ret = label + 1; |
| } |
| return ret; |
| } |
| |
| /* |
| * cfg_set_default_by_mac () |
| * return 1 if the default cf_option was changed to label with the MAC addr |
| * return 0 if not changed |
| */ |
| int cfg_set_default_by_mac (char *mac_addr) |
| { |
| CONFIG *walk; |
| struct IMAGES *tmp; |
| char * label = NULL; |
| int haslabel = 0; |
| |
| /* check if there is an image label equal to mac_addr */ |
| for (tmp = images; tmp; tmp = tmp->next) { |
| label = cfg_get_strg_i (tmp->table, "label"); |
| if (!strcmp(label,mac_addr)){ |
| haslabel = 1; |
| } |
| } |
| |
| if (!haslabel) |
| return 0; |
| else { |
| /* |
| * if there is an image label equal to mac_addr, change the default |
| * cf_options to this image label |
| */ |
| for (walk = cf_options; walk->type != cft_end; walk++) { |
| if (!strcasecmp(walk->name,"default")) { |
| walk->data = mac_addr; |
| return 1; |
| } |
| } |
| return 0; |
| } |
| } |
| |
| /* |
| * Local variables: |
| * c-file-style: "k&r" |
| * c-basic-offset: 5 |
| * End: |
| */ |