diff --git a/src/core/settings.c b/src/core/settings.c index f34eb6642..bb5a382b4 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -117,6 +117,108 @@ struct simple_settings simple_settings_root = { /** Root settings block */ #define settings_root simple_settings_root.settings +/** + * Find child named settings block + * + * @v parent Parent settings block + * @v name Name within this parent + * @ret settings Settings block, or NULL + */ +static struct settings * find_child_settings ( struct settings *parent, + const char *name ) { + struct settings *settings; + + /* Treat empty name as meaning "this block" */ + if ( ! *name ) + return parent; + + /* Look for child with matching name */ + list_for_each_entry ( settings, &parent->children, siblings ) { + if ( strcmp ( settings->name, name ) == 0 ) + return settings; + } + + return NULL; +} + +/** + * Find or create child named settings block + * + * @v parent Parent settings block + * @v name Name within this parent + * @ret settings Settings block, or NULL + */ +static struct settings * autovivify_child_settings ( struct settings *parent, + const char *name ) { + struct { + struct simple_settings simple; + char name[ strlen ( name ) + 1 /* NUL */ ]; + } *new_child; + struct settings *settings; + + /* Return existing settings, if existent */ + if ( ( settings = find_child_settings ( parent, name ) ) != NULL ) + return settings; + + /* Create new simple settings block */ + new_child = zalloc ( sizeof ( *new_child ) ); + if ( ! new_child ) { + DBGC ( parent, "Settings %p could not create child %s\n", + parent, name ); + return NULL; + } + memcpy ( new_child->name, name, sizeof ( new_child->name ) ); + simple_settings_init ( &new_child->simple, NULL, new_child->name ); + settings = &new_child->simple.settings; + register_settings ( settings, parent ); + return settings; +} + +/** + * Parse settings block name + * + * @v name Name + * @v get_child Function to find or create child settings block + * @ret settings Settings block, or NULL + */ +static struct settings * +parse_settings_name ( const char *name, + struct settings * ( * get_child ) ( struct settings *, + const char * ) ) { + struct settings *settings = &settings_root; + char name_copy[ strlen ( name ) + 1 ]; + char *subname; + char *remainder; + + /* Create modifiable copy of name */ + memcpy ( name_copy, name, sizeof ( name_copy ) ); + remainder = name_copy; + + /* Parse each name component in turn */ + while ( remainder ) { + subname = remainder; + remainder = strchr ( subname, '.' ); + if ( remainder ) + *(remainder++) = '\0'; + settings = get_child ( settings, subname ); + if ( ! settings ) + break; + } + + return settings; +} + +/** + * Find named settings block + * + * @v name Name + * @ret settings Settings block, or NULL + */ +struct settings * find_settings ( const char *name ) { + + return parse_settings_name ( name, find_child_settings ); +} + /** * Apply all settings * @@ -228,52 +330,6 @@ void unregister_settings ( struct settings *settings ) { apply_settings(); } -/** - * Find child named settings block - * - * @v parent Parent settings block - * @v name Name within this parent - * @ret settings Settings block, or NULL - */ -struct settings * find_child_settings ( struct settings *parent, - const char *name ) { - struct settings *settings; - size_t len; - - /* NULL parent => add to settings root */ - if ( parent == NULL ) - parent = &settings_root; - - /* Look for a child whose name matches the initial component */ - list_for_each_entry ( settings, &parent->children, siblings ) { - len = strlen ( settings->name ); - if ( strncmp ( name, settings->name, len ) != 0 ) - continue; - if ( name[len] == 0 ) - return settings; - if ( name[len] == '.' ) - return find_child_settings ( settings, - ( name + len + 1 ) ); - } - - return NULL; -} - -/** - * Find named settings block - * - * @v name Name - * @ret settings Settings block, or NULL - */ -struct settings * find_settings ( const char *name ) { - - /* If name is empty, use the root */ - if ( ! *name ) - return &settings_root; - - return find_child_settings ( &settings_root, name ); -} - /****************************************************************************** * * Core settings routines @@ -641,6 +697,7 @@ static struct setting_type * find_setting_type ( const char *name ) { * Parse setting name * * @v name Name of setting + * @v get_child Function to find or create child settings block * @v settings Settings block to fill in * @v setting Setting to fill in * @ret rc Return status code @@ -649,8 +706,11 @@ static struct setting_type * find_setting_type ( const char *name ) { * "[settings_name/]tag_name[:type_name]" and fills in the appropriate * fields. */ -static int parse_setting_name ( const char *name, struct settings **settings, - struct setting *setting ) { +static int +parse_setting_name ( const char *name, + struct settings * ( * get_child ) ( struct settings *, + const char * ), + struct settings **settings, struct setting *setting ) { char tmp_name[ strlen ( name ) + 1 ]; char *settings_name; char *setting_name; @@ -677,7 +737,7 @@ static int parse_setting_name ( const char *name, struct settings **settings, /* Identify settings block, if specified */ if ( settings_name ) { - *settings = find_settings ( settings_name ); + *settings = parse_settings_name ( settings_name, get_child ); if ( *settings == NULL ) { DBG ( "Unrecognised settings block \"%s\" in \"%s\"\n", settings_name, name ); @@ -731,7 +791,8 @@ int storef_named_setting ( const char *name, const char *value ) { struct setting setting; int rc; - if ( ( rc = parse_setting_name ( name, &settings, &setting ) ) != 0 ) + if ( ( rc = parse_setting_name ( name, autovivify_child_settings, + &settings, &setting ) ) != 0 ) return rc; return storef_setting ( settings, &setting, value ); } @@ -749,7 +810,8 @@ int fetchf_named_setting ( const char *name, char *buf, size_t len ) { struct setting setting; int rc; - if ( ( rc = parse_setting_name ( name, &settings, &setting ) ) != 0 ) + if ( ( rc = parse_setting_name ( name, find_child_settings, + &settings, &setting ) ) != 0 ) return rc; return fetchf_setting ( settings, &setting, buf, len ); } diff --git a/src/include/gpxe/settings.h b/src/include/gpxe/settings.h index 9e62cdeab..fe9c8082c 100644 --- a/src/include/gpxe/settings.h +++ b/src/include/gpxe/settings.h @@ -193,8 +193,6 @@ extern int fetch_uuid_setting ( struct settings *settings, struct setting *setting, union uuid *uuid ); extern int setting_cmp ( struct setting *a, struct setting *b ); -extern struct settings * find_child_settings ( struct settings *parent, - const char *name ); extern struct settings * find_settings ( const char *name ); extern int storef_setting ( struct settings *settings,