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
changes/43/43/1
George Joseph 10 years ago
parent 9ea8dd036f
commit 42e4cb7174

@ -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;
}

Loading…
Cancel
Save