changeset 10175:eaac22ecc168 HEAD

config: Added SET_DEFLIST_UNIQUE type. Settings in unique sections can be overridden.
author Timo Sirainen <tss@iki.fi>
date Fri, 23 Oct 2009 19:10:42 -0400
parents 429641734346
children 9e0123366fc5
files src/config/config-parser.c src/config/config-request.c src/lib-settings/settings-parser.c src/lib-settings/settings-parser.h
diffstat 4 files changed, 214 insertions(+), 53 deletions(-) [+]
line wrap: on
line diff
--- a/src/config/config-parser.c	Fri Oct 23 19:09:33 2009 -0400
+++ b/src/config/config-parser.c	Fri Oct 23 19:10:42 2009 -0400
@@ -565,14 +565,49 @@
 	return 0;
 }
 
+static const char *section_name_escape(const char *name)
+{
+#define CHAR_NEED_ESCAPE(c) \
+	((c) == '=' || (c) == SETTINGS_SEPARATOR || (c) == '\\')
+	string_t *str;
+	unsigned int i;
+
+	for (i = 0; name[i] != '\0'; i++) {
+		if (CHAR_NEED_ESCAPE(name[i]))
+			break;
+	}
+	if (name[i] == '\0')
+		return name;
+
+	str = t_str_new(i + strlen(name+i) + 8);
+	str_append_n(str, name, i);
+	for (; name[i] != '\0'; i++) {
+		switch (name[i]) {
+		case '=':
+			str_append(str, "\\e");
+			break;
+		case SETTINGS_SEPARATOR:
+			str_append(str, "\\s");
+			break;
+		case '\\':
+			str_append(str, "\\\\");
+			break;
+		default:
+			str_append_c(str, name[i]);
+			break;
+		}
+	}
+	return str_c(str);
+}
+
 int config_parse_file(const char *path, bool expand_files,
 		      const char **error_r)
 {
 	struct input_stack root;
 	struct parser_context ctx;
 	unsigned int pathlen = 0;
-	unsigned int i, count, counter = 0, cur_counter;
-	const char *errormsg, *key, *value;
+	unsigned int i, count, counter = 0;
+	const char *errormsg, *key, *value, *section_name;
 	string_t *str, *full_line;
 	enum config_line_type type;
 	char *line;
@@ -650,20 +685,25 @@
 			}
 
 			/* new config section */
+			if (*value == '\0') {
+				/* no section name, use a counter */
+				section_name = dec2str(counter++);
+			} else {
+				section_name = section_name_escape(value);
+			}
 			str_truncate(str, pathlen);
 			str_append(str, key);
 			pathlen = str_len(str);
-			cur_counter = counter++;
 
 			str_append_c(str, '=');
-			str_printfa(str, "%u", cur_counter);
+			str_append(str, section_name);
 
 			if (config_apply_line(&ctx, key, str_c(str), value, &errormsg) < 0)
 				break;
 
 			str_truncate(str, pathlen);
 			str_append_c(str, SETTINGS_SEPARATOR);
-			str_printfa(str, "%u", cur_counter);
+			str_append(str, section_name);
 			str_append_c(str, SETTINGS_SEPARATOR);
 			pathlen = str_len(str);
 			break;
--- a/src/config/config-request.c	Fri Oct 23 19:09:33 2009 -0400
+++ b/src/config/config-request.c	Fri Oct 23 19:10:42 2009 -0400
@@ -157,7 +157,8 @@
 				str_append(ctx->value, *val);
 			break;
 		}
-		case SET_DEFLIST: {
+		case SET_DEFLIST:
+		case SET_DEFLIST_UNIQUE: {
 			const ARRAY_TYPE(void_array) *val = value;
 			const ARRAY_TYPE(void_array) *change_val = change_value;
 
@@ -212,8 +213,8 @@
 					  def->key, NULL);
 			if (hash_table_lookup(ctx->keys, key) == NULL) {
 				ctx->callback(key, str_c(ctx->value),
-					      def->type == SET_DEFLIST,
-					      ctx->context);
+					SETTING_TYPE_IS_DEFLIST(def->type),
+					ctx->context);
 				hash_table_insert(ctx->keys, key, key);
 			}
 		}
--- a/src/lib-settings/settings-parser.c	Fri Oct 23 19:09:33 2009 -0400
+++ b/src/lib-settings/settings-parser.c	Fri Oct 23 19:10:42 2009 -0400
@@ -61,6 +61,11 @@
 	MEMBER(parent_offset) (size_t)-1
 };
 
+static int
+settings_apply(struct setting_link *dest_link,
+	       const struct setting_link *src_link,
+	       pool_t pool, const char **conflict_key_r);
+
 struct setting_parser_context *
 settings_parser_init(pool_t set_pool, const struct setting_parser_info *root,
 		     enum settings_parser_flags flags)
@@ -262,6 +267,7 @@
 
 static int
 get_deflist(struct setting_parser_context *ctx, struct setting_link *parent,
+	    const struct setting_define *def,
 	    const struct setting_parser_info *info,
 	    const char *key, const char *value, ARRAY_TYPE(void_array) *result,
 	    ARRAY_TYPE(void_array) *change_result)
@@ -284,7 +290,11 @@
 
 		full_key = p_strconcat(ctx->parser_pool, key,
 				       SETTINGS_SEPARATOR_S, *list, NULL);
-		if (hash_table_lookup(ctx->links, full_key) != NULL) {
+		link = hash_table_lookup(ctx->links, full_key);
+		if (link != NULL) {
+			if (def != NULL && def->type == SET_DEFLIST_UNIQUE &&
+			    link->parent == parent && link->info == info)
+				return 0;
 			ctx->error = p_strconcat(ctx->parser_pool, full_key,
 						 " already exists", NULL);
 			return -1;
@@ -363,13 +373,14 @@
 			return -1;
 		break;
 	case SET_DEFLIST:
+	case SET_DEFLIST_UNIQUE:
 		ctx->prev_info = def->list_info;
-		return get_deflist(ctx, link, def->list_info,
+		return get_deflist(ctx, link, def, def->list_info,
 				   key, value, (ARRAY_TYPE(void_array) *)ptr,
 				   (ARRAY_TYPE(void_array) *)change_ptr);
 	case SET_STRLIST: {
 		ctx->prev_info = &strlist_info;
-		if (get_deflist(ctx, link, &strlist_info, key, value,
+		if (get_deflist(ctx, link, NULL, &strlist_info, key, value,
 				(ARRAY_TYPE(void_array) *)ptr, NULL) < 0)
 			return -1;
 		break;
@@ -707,10 +718,10 @@
 	}
 
 	for (def = info->defines; def->key != NULL; def++) {
-		if (def->type != SET_DEFLIST)
+		if (!SETTING_TYPE_IS_DEFLIST(def->type))
 			continue;
 
-		val = CONST_PTR_OFFSET(set, def->offset);;
+		val = CONST_PTR_OFFSET(set, def->offset);
 		if (!array_is_created(val))
 			continue;
 
@@ -791,6 +802,12 @@
 	for (def = info->defines; def->key != NULL; def++) {
 		value = PTR_OFFSET(set, def->offset);
 		switch (def->type) {
+		case SET_BOOL:
+		case SET_UINT:
+		case SET_STR:
+		case SET_ENUM:
+		case SET_STRLIST:
+			break;
 		case SET_STR_VARS: {
 			const char **val = value;
 
@@ -811,7 +828,8 @@
 			}
 			break;
 		}
-		case SET_DEFLIST: {
+		case SET_DEFLIST:
+		case SET_DEFLIST_UNIQUE: {
 			const ARRAY_TYPE(void_array) *val = value;
 
 			if (!array_is_created(val))
@@ -825,8 +843,6 @@
 			}
 			break;
 		}
-		default:
-			break;
 		}
 	}
 }
@@ -855,6 +871,12 @@
 	for (def = info->defines; def->key != NULL; def++) {
 		value = CONST_PTR_OFFSET(set, def->offset);
 		switch (def->type) {
+		case SET_BOOL:
+		case SET_UINT:
+		case SET_STR:
+		case SET_ENUM:
+		case SET_STRLIST:
+			break;
 		case SET_STR_VARS: {
 			const char *const *val = value;
 
@@ -873,7 +895,8 @@
 			}
 			break;
 		}
-		case SET_DEFLIST: {
+		case SET_DEFLIST:
+		case SET_DEFLIST_UNIQUE: {
 			const ARRAY_TYPE(void_array) *val = value;
 
 			if (!array_is_created(val))
@@ -889,8 +912,6 @@
 			}
 			break;
 		}
-		default:
-			break;
 		}
 	}
 	return FALSE;
@@ -936,6 +957,7 @@
 		break;
 	}
 	case SET_DEFLIST:
+	case SET_DEFLIST_UNIQUE:
 		return FALSE;
 	case SET_STRLIST: {
 		const ARRAY_TYPE(const_string) *src_arr = src;
@@ -1025,7 +1047,8 @@
 		case SET_STRLIST:
 			*((char *)dest) = *((char *)src);
 			break;
-		case SET_DEFLIST: {
+		case SET_DEFLIST:
+		case SET_DEFLIST_UNIQUE: {
 			const ARRAY_TYPE(void_array) *src_arr = src;
 			ARRAY_TYPE(void_array) *dest_arr = dest;
 			void *child_set;
@@ -1268,7 +1291,7 @@
 
 	dest_set = p_malloc(pool, info->struct_size);
 	for (def = info->defines; def->key != NULL; def++) {
-		if (def->type != SET_DEFLIST)
+		if (!SETTING_TYPE_IS_DEFLIST(def->type))
 			continue;
 
 		src_arr = CONST_PTR_OFFSET(change_set, def->offset);
@@ -1288,6 +1311,119 @@
 	return dest_set;
 }
 
+static void settings_copy_deflist(const struct setting_define *def,
+				  const struct setting_link *src_link,
+				  struct setting_link *dest_link,
+				  pool_t pool)
+{
+	const ARRAY_TYPE(void_array) *src_arr;
+	ARRAY_TYPE(void_array) *dest_arr;
+	void *const *children, *child_set;
+	unsigned int i, count;
+
+	src_arr = CONST_PTR_OFFSET(src_link->set_struct, def->offset);
+	dest_arr = PTR_OFFSET(dest_link->set_struct, def->offset);
+
+	if (!array_is_created(src_arr))
+		return;
+
+	children = array_get(src_arr, &count);
+	if (!array_is_created(dest_arr))
+		p_array_init(dest_arr, pool, count);
+	for (i = 0; i < count; i++) {
+		child_set = settings_dup(def->list_info, children[i], pool);
+		array_append(dest_arr, &child_set, 1);
+		settings_set_parent(def->list_info, child_set,
+				    dest_link->set_struct);
+	}
+
+	/* copy changes */
+	dest_arr = PTR_OFFSET(dest_link->change_struct, def->offset);
+	if (!array_is_created(dest_arr))
+		p_array_init(dest_arr, pool, count);
+	for (i = 0; i < count; i++) {
+		child_set = settings_changes_init(def->list_info,
+						  children[i], pool);
+		array_append(dest_arr, &child_set, 1);
+	}
+}
+
+static int
+settings_copy_deflist_unique(const struct setting_define *def,
+			     const struct setting_link *src_link,
+			     struct setting_link *dest_link,
+			     pool_t pool, const char **conflict_key_r)
+{
+	struct setting_link child_dest_link, child_src_link;
+	const ARRAY_TYPE(void_array) *src_arr, *src_carr;
+	ARRAY_TYPE(void_array) *dest_arr, *dest_carr;
+	void *const *src_children, *const *src_cchildren;
+	void *const *dest_children, *const *dest_cchildren, *child_set;
+	const char *const *src_namep, *const *dest_namep;
+	unsigned int i, j, src_count, dest_count, ccount;
+	unsigned int type_offset;
+
+	i_assert(def->list_info->type_offset != (size_t)-1);
+
+	src_arr = CONST_PTR_OFFSET(src_link->set_struct, def->offset);
+	src_carr = CONST_PTR_OFFSET(src_link->change_struct, def->offset);
+	dest_arr = PTR_OFFSET(dest_link->set_struct, def->offset);
+	dest_carr = PTR_OFFSET(dest_link->change_struct, def->offset);
+
+	if (!array_is_created(src_arr))
+		return 0;
+	type_offset = def->list_info->type_offset;
+
+	memset(&child_dest_link, 0, sizeof(child_dest_link));
+	memset(&child_src_link, 0, sizeof(child_src_link));
+
+	child_dest_link.info = child_src_link.info = def->list_info;
+
+	src_children = array_get(src_arr, &src_count);
+	src_cchildren = array_get(src_carr, &ccount);
+	i_assert(src_count == ccount);
+	if (!array_is_created(dest_arr)) {
+		p_array_init(dest_arr, pool, src_count);
+		p_array_init(dest_carr, pool, src_count);
+	}
+	for (i = 0; i < src_count; i++) {
+		src_namep = CONST_PTR_OFFSET(src_children[i], type_offset);
+		dest_children = array_get(dest_arr, &dest_count);
+		dest_cchildren = array_get(dest_carr, &ccount);
+		i_assert(dest_count == ccount);
+		for (j = 0; j < dest_count; j++) {
+			dest_namep = CONST_PTR_OFFSET(dest_children[j],
+						      type_offset);
+			if (strcmp(*src_namep, *dest_namep) == 0)
+				break;
+		}
+
+		if (j < dest_count) {
+			/* merge */
+			child_src_link.set_struct = src_children[i];
+			child_src_link.change_struct = src_cchildren[i];
+			child_dest_link.set_struct = dest_children[j];
+			child_dest_link.change_struct = dest_cchildren[j];
+			if (settings_apply(&child_dest_link, &child_src_link,
+					   pool, conflict_key_r) < 0)
+				return -1;
+		} else {
+			/* append */
+			child_set = settings_dup(def->list_info,
+						 src_children[i], pool);
+			array_append(dest_arr, &child_set, 1);
+			settings_set_parent(def->list_info, child_set,
+					    dest_link->set_struct);
+
+			child_set = settings_changes_init(def->list_info,
+							  src_cchildren[i],
+							  pool);
+			array_append(dest_carr, &child_set, 1);
+		}
+	}
+	return 0;
+}
+
 static int
 settings_apply(struct setting_link *dest_link,
 	       const struct setting_link *src_link,
@@ -1295,8 +1431,7 @@
 {
 	const struct setting_define *def;
 	const void *src, *csrc;
-	void *dest, *cdest, *const *children;
-	unsigned int i, count;
+	void *dest, *cdest;
 
 	for (def = dest_link->info->defines; def->key != NULL; def++) {
 		csrc = CONST_PTR_OFFSET(src_link->change_struct, def->offset);
@@ -1304,6 +1439,8 @@
 
 		if (def->type == SET_DEFLIST || def->type == SET_STRLIST) {
 			/* just add the new values */
+		} else if (def->type == SET_DEFLIST_UNIQUE) {
+			/* merge sections */
 		} else if (*((const char *)csrc) == 0) {
 			/* unchanged */
 			continue;
@@ -1322,36 +1459,16 @@
 		src = CONST_PTR_OFFSET(src_link->set_struct, def->offset);
 		dest = PTR_OFFSET(dest_link->set_struct, def->offset);
 
-		if (!setting_copy(def->type, src, dest, pool)) {
-			const ARRAY_TYPE(void_array) *src_arr = src;
-			ARRAY_TYPE(void_array) *dest_arr = dest;
-			void *child_set;
-
-			if (!array_is_created(src_arr))
-				continue;
-
-			children = array_get(src_arr, &count);
-			if (!array_is_created(dest_arr))
-				p_array_init(dest_arr, pool, count);
-			for (i = 0; i < count; i++) {
-				child_set = settings_dup(def->list_info,
-							 children[i], pool);
-				array_append(dest_arr, &child_set, 1);
-				settings_set_parent(def->list_info, child_set,
-						    dest_link->set_struct);
-			}
-
-			/* copy changes */
-			dest_arr = cdest;
-			if (!array_is_created(dest_arr))
-				p_array_init(dest_arr, pool, count);
-			for (i = 0; i < count; i++) {
-				child_set =
-					settings_changes_init(def->list_info,
-							      children[i],
-							      pool);
-				array_append(dest_arr, &child_set, 1);
-			}
+		if (setting_copy(def->type, src, dest, pool)) {
+			/* non-list */
+		} else if (def->type == SET_DEFLIST) {
+			settings_copy_deflist(def, src_link, dest_link, pool);
+		} else {
+			i_assert(def->type == SET_DEFLIST_UNIQUE);
+			if (settings_copy_deflist_unique(def, src_link,
+							 dest_link, pool,
+							 conflict_key_r) < 0)
+				return -1;
 		}
 	}
 	return 0;
--- a/src/lib-settings/settings-parser.h	Fri Oct 23 19:09:33 2009 -0400
+++ b/src/lib-settings/settings-parser.h	Fri Oct 23 19:10:42 2009 -0400
@@ -23,8 +23,11 @@
 	SET_STR_VARS, /* string with %variables */
 	SET_ENUM,
 	SET_DEFLIST, /* of type array_t */
+	SET_DEFLIST_UNIQUE,
 	SET_STRLIST /* of type ARRAY_TYPE(const_string) */
 };
+#define SETTING_TYPE_IS_DEFLIST(type) \
+	((type) == SET_DEFLIST || (type) == SET_DEFLIST_UNIQUE)
 
 #define SETTING_DEFINE_LIST_END { 0, NULL, 0, NULL }