mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-10-31 02:37:10 +00:00 
			
		
		
		
	func_config: Add ability to retrieve specific occurrence of a variable
I guess nobody uses templates with AST_CONFIG because today if you have a context that inherits from a template and you call AST_CONFIG on the context, you'll get the value from the template even if you've overridden it in the context. This is because AST_CONFIG only gets the first occurrence which is always from the template. This patch adds an optional 'index' parameter to AST_CONFIG which lets you specify the exact occurrence to retrieve, or '-1' to retrieve the last. The default behavior is the current behavior. Tested-by: George Joseph Review: https://reviewboard.asterisk.org/r/4313/ git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@430315 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
		| @@ -49,6 +49,13 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") | ||||
| 			<parameter name="config_file" required="true" /> | ||||
| 			<parameter name="category" required="true" /> | ||||
| 			<parameter name="variable_name" required="true" /> | ||||
| 			<parameter name="index" required="false"> | ||||
| 				<para>If there are multiple variables with the same name, you can specify | ||||
| 				<literal>0</literal> for the first item (default), <literal>-1</literal> for the last | ||||
| 				item, or any other number for that specific item.  <literal>-1</literal> is useful | ||||
| 				when the variable is derived from a template and you want the effective value (the last | ||||
| 				occurrence), not the value from the template (the first occurrence).</para> | ||||
| 			</parameter> | ||||
| 		</syntax> | ||||
| 		<description> | ||||
| 			<para>This function reads a variable from an Asterisk configuration file.</para> | ||||
| @@ -65,14 +72,17 @@ struct config_item { | ||||
|  | ||||
| static AST_RWLIST_HEAD_STATIC(configs, config_item); | ||||
|  | ||||
| static int config_function_read(struct ast_channel *chan, const char *cmd, char *data,  | ||||
| 	char *buf, size_t len)  | ||||
| static int config_function_read(struct ast_channel *chan, const char *cmd, char *data, | ||||
| 	char *buf, size_t len) | ||||
| { | ||||
| 	struct ast_config *cfg; | ||||
| 	struct ast_flags cfg_flags = { CONFIG_FLAG_FILEUNCHANGED }; | ||||
| 	const char *val; | ||||
| 	char *parse; | ||||
| 	struct config_item *cur; | ||||
| 	int index = 0; | ||||
| 	struct ast_variable *var; | ||||
| 	struct ast_variable *found = NULL; | ||||
| 	int ix = 0; | ||||
| 	AST_DECLARE_APP_ARGS(args, | ||||
| 		AST_APP_ARG(filename); | ||||
| 		AST_APP_ARG(category); | ||||
| @@ -103,6 +113,13 @@ static int config_function_read(struct ast_channel *chan, const char *cmd, char | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	if (!ast_strlen_zero(args.index)) { | ||||
| 		if (!sscanf(args.index, "%d", &index)) { | ||||
| 			ast_log(LOG_ERROR, "AST_CONFIG() index must be an integer\n"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (!(cfg = ast_config_load(args.filename, cfg_flags)) || cfg == CONFIG_STATUS_FILEINVALID) { | ||||
| 		return -1; | ||||
| 	} | ||||
| @@ -164,14 +181,29 @@ static int config_function_read(struct ast_channel *chan, const char *cmd, char | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (!(val = ast_variable_retrieve(cfg, args.category, args.variable))) { | ||||
| 		ast_debug(1, "'%s' not found in [%s] of '%s'\n", args.variable, | ||||
| 			args.category, args.filename); | ||||
| 	for (var = ast_category_root(cfg, args.category); var; var = var->next) { | ||||
| 		if (strcasecmp(args.variable, var->name)) { | ||||
| 			continue; | ||||
| 		} | ||||
| 		found = var; | ||||
| 		if (index == -1) { | ||||
| 			continue; | ||||
| 		} | ||||
| 		if (ix == index) { | ||||
| 			break; | ||||
| 		} | ||||
| 		found = NULL; | ||||
| 		ix++; | ||||
| 	} | ||||
|  | ||||
| 	if (!found) { | ||||
| 		ast_debug(1, "'%s' not found at index %d in [%s] of '%s'.  Maximum index found: %d\n", | ||||
| 			args.variable, index, args.category, args.filename, ix); | ||||
| 		AST_RWLIST_UNLOCK(&configs); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	ast_copy_string(buf, val, len); | ||||
| 	ast_copy_string(buf, found->value, len); | ||||
|  | ||||
| 	/* Unlock down here, so there's no chance the struct goes away while we're using it. */ | ||||
| 	AST_RWLIST_UNLOCK(&configs); | ||||
|   | ||||
| @@ -42,6 +42,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$"); | ||||
| #include "asterisk/config_options.h" | ||||
| #include "asterisk/netsock2.h" | ||||
| #include "asterisk/acl.h" | ||||
| #include "asterisk/pbx.h" | ||||
| #include "asterisk/frame.h" | ||||
| #include "asterisk/utils.h" | ||||
| #include "asterisk/logger.h" | ||||
| @@ -1504,6 +1505,156 @@ AST_TEST_DEFINE(config_options_test) | ||||
| 	return res; | ||||
| } | ||||
|  | ||||
| AST_TEST_DEFINE(config_dialplan_function) | ||||
| { | ||||
| 	enum ast_test_result_state res = AST_TEST_PASS; | ||||
| 	FILE *config_file; | ||||
| 	char filename[PATH_MAX]; | ||||
| 	struct ast_str *buf; | ||||
|  | ||||
| 	switch (cmd) { | ||||
| 	case TEST_INIT: | ||||
| 		info->name = "config_dialplan_function"; | ||||
| 		info->category = "/main/config/"; | ||||
| 		info->summary = "Test AST_CONFIG dialplan function"; | ||||
| 		info->description = "Test AST_CONFIG dialplan function"; | ||||
| 		return AST_TEST_NOT_RUN; | ||||
| 	case TEST_EXECUTE: | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	snprintf(filename, sizeof(filename), "%s/%s", | ||||
| 			ast_config_AST_CONFIG_DIR, CONFIG_FILE); | ||||
| 	config_file = fopen(filename, "w"); | ||||
|  | ||||
| 	if (!config_file) { | ||||
| 		return AST_TEST_FAIL; | ||||
| 	} | ||||
|  | ||||
| 	fputs( | ||||
| 		"[c1t](!)\n" | ||||
| 		"var1=val1\n" | ||||
| 		"var1=val2\n" | ||||
| 		"var2=val21\n" | ||||
| 		"\n" | ||||
| 		"[c1](c1t)\n" | ||||
| 		"var1=val3\n" | ||||
| 		"var1=val4\n" | ||||
| 		, config_file); | ||||
|  | ||||
| 	fclose(config_file); | ||||
|  | ||||
| 	if (!(buf = ast_str_create(32))) { | ||||
| 		ast_test_status_update(test, "Failed to allocate return buffer\n"); | ||||
| 		res = AST_TEST_FAIL; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	if (ast_func_read2(NULL, "AST_CONFIG("CONFIG_FILE",c1,var1)", &buf, 32)) { | ||||
| 		ast_test_status_update(test, "Failed to retrieve field 'var1'\n"); | ||||
| 		res = AST_TEST_FAIL; | ||||
| 		goto out; | ||||
| 	} | ||||
| 	if (strcmp(ast_str_buffer(buf), "val1")) { | ||||
| 		ast_test_status_update(test, "Got '%s', should be '%s'\n", | ||||
| 			ast_str_buffer(buf), "val1"); | ||||
| 		res = AST_TEST_FAIL; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	ast_str_reset(buf); | ||||
| 	if (ast_func_read2(NULL, "AST_CONFIG("CONFIG_FILE",c1,var1,0)", &buf, 32)) { | ||||
| 		ast_test_status_update(test, "Failed to retrieve field 'var1'\n"); | ||||
| 		res = AST_TEST_FAIL; | ||||
| 		goto out; | ||||
| 	} | ||||
| 	if (strcmp(ast_str_buffer(buf), "val1")) { | ||||
| 		ast_test_status_update(test, "Got '%s', should be '%s'\n", | ||||
| 			ast_str_buffer(buf), "val1"); | ||||
| 		res = AST_TEST_FAIL; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	ast_str_reset(buf); | ||||
| 	if (ast_func_read2(NULL, "AST_CONFIG("CONFIG_FILE",c1,var1,1)", &buf, 32)) { | ||||
| 		ast_test_status_update(test, "Failed to retrieve field 'var1'\n"); | ||||
| 		res = AST_TEST_FAIL; | ||||
| 		goto out; | ||||
| 	} | ||||
| 	if (strcmp(ast_str_buffer(buf), "val2")) { | ||||
| 		ast_test_status_update(test, "Got '%s', should be '%s'\n", | ||||
| 			ast_str_buffer(buf), "val2"); | ||||
| 		res = AST_TEST_FAIL; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	ast_str_reset(buf); | ||||
| 	if (ast_func_read2(NULL, "AST_CONFIG("CONFIG_FILE",c1,var1,2)", &buf, 32)) { | ||||
| 		ast_test_status_update(test, "Failed to retrieve field 'var1'\n"); | ||||
| 		res = AST_TEST_FAIL; | ||||
| 		goto out; | ||||
| 	} | ||||
| 	if (strcmp(ast_str_buffer(buf), "val3")) { | ||||
| 		ast_test_status_update(test, "Got '%s', should be '%s'\n", | ||||
| 			ast_str_buffer(buf), "val3"); | ||||
| 		res = AST_TEST_FAIL; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	ast_str_reset(buf); | ||||
| 	if (ast_func_read2(NULL, "AST_CONFIG("CONFIG_FILE",c1,var1,3)", &buf, 32)) { | ||||
| 		ast_test_status_update(test, "Failed to retrieve field 'var1'\n"); | ||||
| 		res = AST_TEST_FAIL; | ||||
| 		goto out; | ||||
| 	} | ||||
| 	if (strcmp(ast_str_buffer(buf), "val4")) { | ||||
| 		ast_test_status_update(test, "Got '%s', should be '%s'\n", | ||||
| 			ast_str_buffer(buf), "val4"); | ||||
| 		res = AST_TEST_FAIL; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	ast_str_reset(buf); | ||||
| 	if (ast_func_read2(NULL, "AST_CONFIG("CONFIG_FILE",c1,var1,-1)", &buf, 32)) { | ||||
| 		ast_test_status_update(test, "Failed to retrieve field 'var1'\n"); | ||||
| 		res = AST_TEST_FAIL; | ||||
| 		goto out; | ||||
| 	} | ||||
| 	if (strcmp(ast_str_buffer(buf), "val4")) { | ||||
| 		ast_test_status_update(test, "Got '%s', should be '%s'\n", | ||||
| 			ast_str_buffer(buf), "val4"); | ||||
| 		res = AST_TEST_FAIL; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	ast_str_reset(buf); | ||||
| 	if (ast_func_read2(NULL, "AST_CONFIG("CONFIG_FILE",c1,var2,-1)", &buf, 32)) { | ||||
| 		ast_test_status_update(test, "Failed to retrieve field 'var2'\n"); | ||||
| 		res = AST_TEST_FAIL; | ||||
| 		goto out; | ||||
| 	} | ||||
| 	if (strcmp(ast_str_buffer(buf), "val21")) { | ||||
| 		ast_test_status_update(test, "Got '%s', should be '%s'\n", | ||||
| 			ast_str_buffer(buf), "val21"); | ||||
| 		res = AST_TEST_FAIL; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	ast_str_reset(buf); | ||||
| 	if (!ast_func_read2(NULL, "AST_CONFIG("CONFIG_FILE",c1,var1,5)", &buf, 32)) { | ||||
| 		ast_test_status_update(test, "Should not have retrieved a value\n"); | ||||
| 		res = AST_TEST_FAIL; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| out: | ||||
| 	if (buf) { | ||||
| 		ast_free(buf); | ||||
| 	} | ||||
| 	delete_config_file(); | ||||
| 	return res; | ||||
| } | ||||
|  | ||||
| static int unload_module(void) | ||||
| { | ||||
| 	AST_TEST_UNREGISTER(config_basic_ops); | ||||
| @@ -1513,6 +1664,7 @@ static int unload_module(void) | ||||
| 	AST_TEST_UNREGISTER(config_hook); | ||||
| 	AST_TEST_UNREGISTER(ast_parse_arg_test); | ||||
| 	AST_TEST_UNREGISTER(config_options_test); | ||||
| 	AST_TEST_UNREGISTER(config_dialplan_function); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| @@ -1525,6 +1677,7 @@ static int load_module(void) | ||||
| 	AST_TEST_REGISTER(config_hook); | ||||
| 	AST_TEST_REGISTER(ast_parse_arg_test); | ||||
| 	AST_TEST_REGISTER(config_options_test); | ||||
| 	AST_TEST_REGISTER(config_dialplan_function); | ||||
| 	return AST_MODULE_LOAD_SUCCESS; | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user