channel: Clear channel flag in error branch.
[asterisk/asterisk.git] / funcs / func_config.c
index 6bed8a9..7279cc9 100644 (file)
  * \ingroup functions
  */
 
-#include "asterisk.h"
+/*** MODULEINFO
+       <support_level>core</support_level>
+ ***/
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+#include "asterisk.h"
 
 #include "asterisk/module.h"
 #include "asterisk/channel.h"
 #include "asterisk/pbx.h"
 #include "asterisk/app.h"
 
+/*** DOCUMENTATION
+       <function name="AST_CONFIG" language="en_US">
+               <synopsis>
+                       Retrieve a variable from a configuration file.
+               </synopsis>
+               <syntax>
+                       <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>
+               </description>
+       </function>
+
+***/
+
 struct config_item {
        AST_RWLIST_ENTRY(config_item) entry;
        struct ast_config *cfg;
@@ -44,14 +70,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);
@@ -82,7 +111,14 @@ static int config_function_read(struct ast_channel *chan, const char *cmd, char
                return -1;
        }
 
-       if (!(cfg = ast_config_load(args.filename, cfg_flags))) {
+       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;
        }
 
@@ -99,7 +135,7 @@ static int config_function_read(struct ast_channel *chan, const char *cmd, char
                        /* At worst, we might leak an entry while upgrading locks */
                        AST_RWLIST_UNLOCK(&configs);
                        AST_RWLIST_WRLOCK(&configs);
-                       if (!(cur = ast_malloc(sizeof(*cur) + strlen(args.filename) + 1))) {
+                       if (!(cur = ast_calloc(1, sizeof(*cur) + strlen(args.filename) + 1))) {
                                AST_RWLIST_UNLOCK(&configs);
                                return -1;
                        }
@@ -107,7 +143,7 @@ static int config_function_read(struct ast_channel *chan, const char *cmd, char
                        strcpy(cur->filename, args.filename);
 
                        ast_clear_flag(&cfg_flags, CONFIG_FLAG_FILEUNCHANGED);
-                       if (!(cfg = ast_config_load(args.filename, cfg_flags))) {
+                       if (!(cfg = ast_config_load(args.filename, cfg_flags)) || cfg == CONFIG_STATUS_FILEINVALID) {
                                ast_free(cur);
                                AST_RWLIST_UNLOCK(&configs);
                                return -1;
@@ -128,7 +164,7 @@ static int config_function_read(struct ast_channel *chan, const char *cmd, char
                }
 
                if (!cur) {
-                       if (!(cur = ast_malloc(sizeof(*cur) + strlen(args.filename) + 1))) {
+                       if (!(cur = ast_calloc(1, sizeof(*cur) + strlen(args.filename) + 1))) {
                                AST_RWLIST_UNLOCK(&configs);
                                return -1;
                        }
@@ -143,14 +179,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_log(LOG_ERROR, "'%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);
@@ -160,11 +211,6 @@ static int config_function_read(struct ast_channel *chan, const char *cmd, char
 
 static struct ast_custom_function config_function = {
        .name = "AST_CONFIG",
-       .syntax = "AST_CONFIG(config_file,category,variable_name)",
-       .synopsis = "Retrieve a variable from a configuration file",
-       .desc = 
-       "   This function reads a variable from an Asterisk configuration file.\n"
-       "",
        .read = config_function_read,
 };