This commit is contained in:
Steven Jackson 2017-05-06 18:59:43 +00:00 committed by GitHub
commit 1b565985fb
6 changed files with 186 additions and 18 deletions

View file

@ -20,19 +20,27 @@
#include "module.h" #include "module.h"
CONFIG_NODE *config_node_find(CONFIG_NODE *node, const char *key) CONFIG_NODE *config_node_find(CONFIG_NODE *parent, const char *key)
{ {
GSList *tmp; GSList *tmp;
g_return_val_if_fail(node != NULL, NULL); g_return_val_if_fail(parent != NULL, NULL);
g_return_val_if_fail(key != NULL, NULL); g_return_val_if_fail(key != NULL, NULL);
g_return_val_if_fail(is_node_list(node), NULL); g_return_val_if_fail(is_node_list(parent), NULL);
for (tmp = node->value; tmp != NULL; tmp = tmp->next) { for (tmp = parent->value; tmp != NULL; tmp = tmp->next) {
CONFIG_NODE *node = tmp->data; CONFIG_NODE *node = tmp->data;
if (node->key != NULL && g_ascii_strcasecmp(node->key, key) == 0) if (node->key != NULL && g_ascii_strcasecmp(node->key, key) == 0)
return node; return node;
/* the primary children of an include node should also be searched */
if (node->type == NODE_TYPE_INCLUDE) {
CONFIG_INCLUDE *inc = node->value;
node = config_node_find(inc->rec->mainnode, key);
if (node)
return node;
}
} }
return NULL; return NULL;

View file

@ -2,20 +2,26 @@
#define __ICONFIG_H #define __ICONFIG_H
enum { enum {
NODE_TYPE_KEY, NODE_TYPE_KEY,
NODE_TYPE_VALUE, NODE_TYPE_VALUE,
NODE_TYPE_BLOCK, NODE_TYPE_INCLUDE,
NODE_TYPE_LIST, NODE_TYPE_BLOCK,
NODE_TYPE_COMMENT NODE_TYPE_LIST,
NODE_TYPE_COMMENT,
NODE_TYPE_COUNT
}; };
#define has_node_value(a) \ #define has_node_value(a) \
((a)->type == NODE_TYPE_KEY || (a)->type == NODE_TYPE_VALUE) ((a)->type == NODE_TYPE_KEY || (a)->type == NODE_TYPE_VALUE || \
(a)->type == NODE_TYPE_INCLUDE)
#define is_node_list(a) \ #define is_node_list(a) \
((a)->type == NODE_TYPE_BLOCK || (a)->type == NODE_TYPE_LIST) ((a)->type == NODE_TYPE_BLOCK || (a)->type == NODE_TYPE_LIST)
typedef struct _CONFIG_NODE CONFIG_NODE; typedef struct _CONFIG_NODE CONFIG_NODE;
typedef struct _CONFIG_REC CONFIG_REC; typedef struct _CONFIG_REC CONFIG_REC;
typedef struct _CONFIG_INCLUDE CONFIG_INCLUDE;
struct _CONFIG_NODE { struct _CONFIG_NODE {
int type; int type;
@ -23,6 +29,13 @@ struct _CONFIG_NODE {
void *value; void *value;
}; };
struct _CONFIG_INCLUDE {
/* the user probably specified a relative path from the root config, store
it for writing back, but set the rec->fname to the absolute path */
char *original_path;
CONFIG_REC *rec;
};
/* a = { x=y; y=z; } /* a = { x=y; y=z; }
node1: type = NODE_TYPE_BLOCK, key = "a", value = (GSList *) nodes node1: type = NODE_TYPE_BLOCK, key = "a", value = (GSList *) nodes
@ -44,12 +57,27 @@ struct _CONFIG_NODE {
also in comments so they won't be forgotten when the config file is also in comments so they won't be forgotten when the config file is
written. written.
Include nodes have key=NULL, the path value is stored in CONFIG_INCLUDE's
original_path member as it's usually converted to an absolute path. All of
the include node's modifications and caches are stored in the 'root' config
and a write of the root config causes all included configs to be written
whether a modification has been made in them or not. A list of included
file names is maintained in the root rec to prevent circular inclusions.
It could eventually be better to keep a list of GFiles and use g_file_equal
for the comparison, but right now it doesn't even cover symbolic links.
*/ */
struct _CONFIG_REC { struct _CONFIG_REC {
/* used for updating the root config's modifycounter from included configs */
CONFIG_REC *root_rec;
/* this list is only maintained in the root_rec, it's to prevent circular
inclusions */
GHashTable *includes;
char *fname; char *fname;
int create_mode; int create_mode;
int modifycounter; /* increase every time something is changed */ /* use config_rec_increase_modifycounter every time something changes */
int modifycounter;
char *last_error; char *last_error;
CONFIG_NODE *mainnode; CONFIG_NODE *mainnode;
@ -113,7 +141,7 @@ int config_set_bool(CONFIG_REC *rec, const char *section, const char *key, int v
/* Handling the configuration directly with nodes - /* Handling the configuration directly with nodes -
useful when you need to read all values in a block/list. */ useful when you need to read all values in a block/list. */
CONFIG_NODE *config_node_find(CONFIG_NODE *node, const char *key); CONFIG_NODE *config_node_find(CONFIG_NODE *parent, const char *key);
/* Find the section from node - if not found create it unless new_type is -1. /* Find the section from node - if not found create it unless new_type is -1.
You can also specify in new_type if it's NODE_TYPE_LIST or NODE_TYPE_BLOCK */ You can also specify in new_type if it's NODE_TYPE_LIST or NODE_TYPE_BLOCK */
CONFIG_NODE *config_node_section(CONFIG_REC *rec, CONFIG_NODE *parent, const char *key, int new_type); CONFIG_NODE *config_node_section(CONFIG_REC *rec, CONFIG_NODE *parent, const char *key, int new_type);
@ -144,6 +172,9 @@ int config_node_get_bool(CONFIG_NODE *parent, const char *key, int def);
void config_node_set_str(CONFIG_REC *rec, CONFIG_NODE *parent, const char *key, const char *value); void config_node_set_str(CONFIG_REC *rec, CONFIG_NODE *parent, const char *key, const char *value);
void config_node_set_int(CONFIG_REC *rec, CONFIG_NODE *parent, const char *key, int value); void config_node_set_int(CONFIG_REC *rec, CONFIG_NODE *parent, const char *key, int value);
void config_node_set_bool(CONFIG_REC *rec, CONFIG_NODE *parent, const char *key, int value); void config_node_set_bool(CONFIG_REC *rec, CONFIG_NODE *parent, const char *key, int value);
/* if a relative path is specified then you can access the absolute path using
node->value->fname. the key is set to the specified include path */
void config_node_set_include(CONFIG_REC *rec, CONFIG_NODE *parent, const char *fname);
/* Remove one node from block/list. */ /* Remove one node from block/list. */
void config_node_remove(CONFIG_REC *rec, CONFIG_NODE *parent, CONFIG_NODE *node); void config_node_remove(CONFIG_REC *rec, CONFIG_NODE *parent, CONFIG_NODE *node);

View file

@ -2,5 +2,5 @@
#include "iconfig.h" #include "iconfig.h"
/* private */ /* private */
int config_error(CONFIG_REC *rec, const char *msg); int config_error(CONFIG_REC *rec, const char *msg, ...);

View file

@ -43,10 +43,16 @@ static unsigned int g_istr_hash(gconstpointer v)
return h /* % M */; return h /* % M */;
} }
int config_error(CONFIG_REC *rec, const char *msg) int config_error(CONFIG_REC *rec, const char *msg, ...)
{ {
va_list vl;
g_free_and_null(rec->last_error); g_free_and_null(rec->last_error);
rec->last_error = g_strdup(msg);
va_start(vl, msg);
rec->last_error = g_strdup_vprintf(msg, vl);
va_end(vl);
return -1; return -1;
} }
@ -133,6 +139,7 @@ static GTokenType config_parse_symbol(CONFIG_REC *rec, CONFIG_NODE *node)
GTokenType last_char; GTokenType last_char;
int print_warning; int print_warning;
char *key; char *key;
int key_is_include = FALSE;
g_return_val_if_fail(rec != NULL, G_TOKEN_ERROR); g_return_val_if_fail(rec != NULL, G_TOKEN_ERROR);
g_return_val_if_fail(node != NULL, G_TOKEN_ERROR); g_return_val_if_fail(node != NULL, G_TOKEN_ERROR);
@ -147,12 +154,23 @@ static GTokenType config_parse_symbol(CONFIG_REC *rec, CONFIG_NODE *node)
(rec->scanner->token == G_TOKEN_STRING)) { (rec->scanner->token == G_TOKEN_STRING)) {
key = g_strdup(rec->scanner->value.v_string); key = g_strdup(rec->scanner->value.v_string);
config_parse_warn_missing(rec, node, '=', TRUE); if (g_ascii_strcasecmp(key, "include") == 0)
key_is_include = TRUE;
else
config_parse_warn_missing(rec, node, '=', TRUE);
config_parse_get_token(rec->scanner, node); config_parse_get_token(rec->scanner, node);
} }
switch (rec->scanner->token) { switch (rec->scanner->token) {
case G_TOKEN_STRING: case G_TOKEN_STRING:
if (key_is_include) {
g_free_not_null(key);
config_node_set_include(rec, node, rec->scanner->value.v_string);
config_parse_warn_missing(rec, node, last_char, TRUE);
return G_TOKEN_NONE;
}
/* value */ /* value */
config_node_set_str(rec, node, key, rec->scanner->value.v_string); config_node_set_str(rec, node, key, rec->scanner->value.v_string);
g_free_not_null(key); g_free_not_null(key);
@ -332,6 +350,8 @@ void config_close(CONFIG_REC *rec)
g_hash_table_destroy(rec->cache_nodes); g_hash_table_destroy(rec->cache_nodes);
g_free_not_null(rec->last_error); g_free_not_null(rec->last_error);
g_free_not_null(rec->fname); g_free_not_null(rec->fname);
if (rec->includes)
g_hash_table_destroy(rec->includes);
g_free(rec); g_free(rec);
} }

View file

@ -20,6 +20,15 @@
#include "module.h" #include "module.h"
static void config_rec_increase_modifycounter(CONFIG_REC *rec)
{
g_return_if_fail(rec != NULL);
/* handle CONFIG_RECs which are includes of other configs */
if (rec->root_rec)
rec->root_rec->modifycounter++;
}
static void cache_remove(CONFIG_REC *rec, CONFIG_NODE *node) static void cache_remove(CONFIG_REC *rec, CONFIG_NODE *node)
{ {
char *path; char *path;
@ -39,7 +48,7 @@ void config_node_remove(CONFIG_REC *rec, CONFIG_NODE *parent, CONFIG_NODE *node)
if (parent == NULL) if (parent == NULL)
parent = rec->mainnode; parent = rec->mainnode;
rec->modifycounter++; config_rec_increase_modifycounter(rec);
cache_remove(rec, node); cache_remove(rec, node);
parent->value = g_slist_remove(parent->value, node); parent->value = g_slist_remove(parent->value, node);
@ -49,6 +58,16 @@ void config_node_remove(CONFIG_REC *rec, CONFIG_NODE *parent, CONFIG_NODE *node)
case NODE_TYPE_COMMENT: case NODE_TYPE_COMMENT:
g_free_not_null(node->value); g_free_not_null(node->value);
break; break;
case NODE_TYPE_INCLUDE: {
CONFIG_INCLUDE *inc = node->value;
if (inc) {
g_hash_table_remove(inc->rec->root_rec->includes, inc->original_path);
config_close(inc->rec);
g_free_not_null(inc->original_path);
g_free(inc);
}
break;
}
case NODE_TYPE_BLOCK: case NODE_TYPE_BLOCK:
case NODE_TYPE_LIST: case NODE_TYPE_LIST:
while (node->value != NULL) while (node->value != NULL)
@ -88,6 +107,79 @@ void config_nodes_remove_all(CONFIG_REC *rec)
config_node_remove(rec, rec->mainnode, ((GSList *) rec->mainnode->value)->data); config_node_remove(rec, rec->mainnode, ((GSList *) rec->mainnode->value)->data);
} }
void config_node_set_include(CONFIG_REC *rec, CONFIG_NODE *parent, const char *fname)
{
CONFIG_NODE *node;
CONFIG_INCLUDE *inc;
CONFIG_REC *root_rec;
char *full_path = NULL;
g_return_if_fail(rec != NULL);
g_return_if_fail(parent != NULL);
if (!fname || !*fname) {
config_error(rec, "Included filename is empty.\n");
return;
}
/* if the parent rec has no root_rec, it is the root */
root_rec = rec->root_rec ? rec->root_rec : rec;
if (!root_rec->includes) {
root_rec->includes = g_hash_table_new_full(g_str_hash, g_str_equal,
g_free, NULL);
if (!root_rec->includes)
return;
}
if (g_hash_table_contains(root_rec->includes, fname)) {
config_error(rec, "'%s' has already been included.\n", fname);
return;
}
if (!g_path_is_absolute(fname)) {
gchar *dirname = g_path_get_dirname(rec->fname);
if (!dirname) {
config_error(rec, "g_path_get_dirname failed.\n");
return;
}
full_path = g_build_filename(dirname, fname, NULL);
g_free(dirname);
}
node = g_new0(CONFIG_NODE, 1);
if (!node) {
return;
}
node->type = NODE_TYPE_INCLUDE;
inc = g_new0(CONFIG_INCLUDE, 1);
if (!inc) {
config_node_remove(rec, parent, node);
return;
}
node->value = inc;
inc->original_path = g_strdup(fname);
inc->rec = config_open(full_path ? full_path : fname, 0660);
if (!inc->rec) {
config_error(rec, "Unable to open '%s': %s.\n", fname,
strerror(errno));
config_node_remove(rec, parent, node);
return;
}
g_free_not_null(full_path);
inc->rec->root_rec = root_rec;
g_hash_table_add(root_rec->includes, g_strdup(fname));
if (config_parse(inc->rec) != 0) {
config_node_remove(rec, parent, node);
return;
}
parent->value = g_slist_append(parent->value, node);
config_rec_increase_modifycounter(rec);
}
void config_node_set_str(CONFIG_REC *rec, CONFIG_NODE *parent, const char *key, const char *value) void config_node_set_str(CONFIG_REC *rec, CONFIG_NODE *parent, const char *key, const char *value)
{ {
CONFIG_NODE *node; CONFIG_NODE *node;
@ -125,7 +217,7 @@ void config_node_set_str(CONFIG_REC *rec, CONFIG_NODE *parent, const char *key,
} }
node->value = g_strdup(value); node->value = g_strdup(value);
rec->modifycounter++; config_rec_increase_modifycounter(rec);
} }
void config_node_set_int(CONFIG_REC *rec, CONFIG_NODE *parent, const char *key, int value) void config_node_set_int(CONFIG_REC *rec, CONFIG_NODE *parent, const char *key, int value)

View file

@ -149,6 +149,18 @@ static int config_write_node(CONFIG_REC *rec, CONFIG_NODE *node, int line_feeds)
if (config_write_word(rec, node->value, TRUE) == -1) if (config_write_word(rec, node->value, TRUE) == -1)
return -1; return -1;
break; break;
case NODE_TYPE_INCLUDE: {
CONFIG_INCLUDE *inc = node->value;
if (config_write_str(rec, "include ") == -1)
return -1;
if (config_write_word(rec, inc->original_path, TRUE) == -1)
return -1;
config_write(inc->rec, NULL, -1);
break;
}
case NODE_TYPE_BLOCK: case NODE_TYPE_BLOCK:
/* key = { */ /* key = { */
if (node->key != NULL) { if (node->key != NULL) {
@ -217,6 +229,11 @@ static int config_node_get_length(CONFIG_REC *rec, CONFIG_NODE *node)
/* "value, " */ /* "value, " */
len = 2 + strlen(node->value); len = 2 + strlen(node->value);
break; break;
case NODE_TYPE_INCLUDE: {
CONFIG_INCLUDE *inc = node->value;
len = strlen("include \"\"") + strlen(inc->rec->fname);
break;
}
case NODE_TYPE_BLOCK: case NODE_TYPE_BLOCK:
case NODE_TYPE_LIST: case NODE_TYPE_LIST:
/* "{ list }; " */ /* "{ list }; " */