Newer
Older
* Asterisk -- An open source telephony toolkit.
* Copyright (C) 1999 - 2010, Digium, Inc.
Mark Spencer
committed
* Mark Spencer <markster@digium.com>
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*! \file
*
* \brief Configuration File Parser
*
* \author Mark Spencer <markster@digium.com>
*
* Includes the Asterisk Realtime API - ARA
/*** MODULEINFO
<support_level>core</support_level>
***/
Kevin P. Fleming
committed
#include "asterisk.h"
#include "asterisk/paths.h" /* use ast_config_AST_CONFIG_DIR */
#include "asterisk/network.h" /* we do some sockaddr manipulation here */
#include <string.h>
#include <libgen.h>
Kevin P. Fleming
committed
#include <sys/stat.h>
#include <math.h> /* HUGE_VAL */
#include <regex.h>
Kevin P. Fleming
committed
#include "asterisk/config.h"
#include "asterisk/cli.h"
#include "asterisk/lock.h"
#include "asterisk/utils.h"
#include "asterisk/channel.h"
#include "asterisk/app.h"
Steve Murphy
committed
#include "asterisk/astobj2.h"
#include "asterisk/strings.h" /* for the ast_str_*() API */
#define MAX_NESTED_COMMENTS 128
#define COMMENT_START ";--"
#define COMMENT_END "--;"
#define COMMENT_META ';'
#define COMMENT_TAG '-'
/*!
* Define the minimum filename space to reserve for each
* ast_variable in case the filename is renamed later by
* ast_include_rename().
*/
#define MIN_VARIABLE_FNAME_SPACE 40
static char *extconfig_conf = "extconfig.conf";
static struct ao2_container *cfg_hooks;
static void config_hook_exec(const char *filename, const char *module, const struct ast_config *cfg);
Matthew Jordan
committed
static inline struct ast_variable *variable_list_switch(struct ast_variable *l1, struct ast_variable *l2);
static int does_category_match(struct ast_category *cat, const char *category_name,
const char *match, char sep);
/*! \brief Structure to keep comments for rewriting configuration files */
struct ast_comment {
struct ast_comment *next;
/*! Comment body allocated after struct. */
/*! \brief Hold the mtime for config files, so if we don't need to reread our config, don't. */
struct cache_file_include {
AST_LIST_ENTRY(cache_file_include) list;
Richard Mudgett
committed
/*! Filename or wildcard pattern as specified by the including file. */
char include[0];
};
struct cache_file_mtime {
AST_LIST_ENTRY(cache_file_mtime) list;
AST_LIST_HEAD_NOLOCK(includes, cache_file_include) includes;
unsigned int has_exec:1;
Richard Mudgett
committed
/*! stat() file size */
unsigned long stat_size;
/*! stat() file modtime nanoseconds */
unsigned long stat_mtime_nsec;
/*! stat() file modtime seconds since epoc */
time_t stat_mtime;
/*! String stuffed in filename[] after the filename string. */
const char *who_asked;
/*! Filename and who_asked stuffed after it. */
char filename[0];
};
/*! Cached file mtime list. */
static AST_LIST_HEAD_STATIC(cfmtime_head, cache_file_mtime);
static int init_appendbuf(void *data)
{
struct ast_str **str = data;
*str = ast_str_create(16);
return *str ? 0 : -1;
}
AST_THREADSTORAGE_CUSTOM(appendbuf, init_appendbuf, ast_free_ptr);
/* comment buffers are better implemented using the ast_str_*() API */
#define CB_SIZE 250 /* initial size of comment buffers */
static void CB_ADD(struct ast_str **cb, const char *str)
ast_str_append(cb, 0, "%s", str);
static void CB_ADD_LEN(struct ast_str **cb, const char *str, int len)
char *s = ast_alloca(len + 1);
memcpy(s, str, len);
s[len] = '\0';
ast_str_append(cb, 0, "%s", s);
static void CB_RESET(struct ast_str *cb, struct ast_str *llb)
{
if (cb) {
ast_str_reset(cb);
}
if (llb) {
ast_str_reset(llb);
}
static struct ast_comment *ALLOC_COMMENT(struct ast_str *buffer)
struct ast_comment *x = NULL;
if (!buffer || !ast_str_strlen(buffer)) {
return NULL;
}
if ((x = ast_calloc(1, sizeof(*x) + ast_str_strlen(buffer) + 1))) {
strcpy(x->cmt, ast_str_buffer(buffer)); /* SAFE */
}
Steve Murphy
committed
/* I need to keep track of each config file, and all its inclusions,
so that we can track blank lines in each */
Steve Murphy
committed
char *fname;
int lineno;
};
static int hash_string(const void *obj, const int flags)
{
char *str = ((struct inclfile *) obj)->fname;
Steve Murphy
committed
int total;
for (total = 0; *str; str++) {
Steve Murphy
committed
unsigned int tmp = total;
total <<= 1; /* multiply by 2 */
total += tmp; /* multiply by 3 */
total <<= 2; /* multiply by 12 */
total += tmp; /* multiply by 13 */
total += ((unsigned int) (*str));
Steve Murphy
committed
}
if (total < 0) {
Steve Murphy
committed
total = -total;
}
Steve Murphy
committed
return total;
}
static int hashtab_compare_strings(void *a, void *b, int flags)
Steve Murphy
committed
{
const struct inclfile *ae = a, *be = b;
return !strcmp(ae->fname, be->fname) ? CMP_MATCH | CMP_STOP : 0;
Steve Murphy
committed
}
static struct ast_config_map {
struct ast_config_map *next;
/*! Stored in stuff[] at struct end. */
const char *name;
/*! Stored in stuff[] at struct end. */
const char *driver;
/*! Stored in stuff[] at struct end. */
const char *database;
/*! Stored in stuff[] at struct end. */
const char *table;
/*! Contents of name, driver, database, and table in that order stuffed here. */
char stuff[0];
} *config_maps = NULL;
AST_MUTEX_DEFINE_STATIC(config_lock);
static struct ast_config_engine *config_engine_list;
#define MAX_INCLUDE_LEVEL 10
struct ast_category_template_instance {
char name[80]; /* redundant? */
const struct ast_category *inst;
AST_LIST_ENTRY(ast_category_template_instance) next;
};
Olle Johansson
committed
char name[80];
int ignored; /*!< do not let user of the config see this category -- set by (!) after the category decl; a template */
Steve Murphy
committed
int include_level;
/*!
* \brief The file name from whence this declaration was read
* \note Will never be NULL
*/
char *file;
Steve Murphy
committed
int lineno;
AST_LIST_HEAD_NOLOCK(template_instance_list, ast_category_template_instance) template_instances;
struct ast_comment *precomments;
struct ast_comment *sameline;
Steve Murphy
committed
struct ast_comment *trailing; /*!< the last object in the list will get assigned any trailing comments when EOF is hit */
/*! First category variable in the list. */
/*! Last category variable in the list. */
/*! Previous node in the list. */
struct ast_category *prev;
/*! Next node in the list. */
struct ast_category *next;
};
struct ast_config {
/*! First config category in the list. */
/*! Last config category in the list. */
struct ast_category *last;
struct ast_category *current;
Steve Murphy
committed
struct ast_category *last_browse; /*!< used to cache the last category supplied via category_browse */
int include_level;
int max_include_level;
Steve Murphy
committed
struct ast_config_include *includes; /*!< a list of inclusions, which should describe the entire tree */
};
struct ast_config_include {
/*!
* \brief file name in which the include occurs
* \note Will never be NULL
*/
char *include_location_file;
Steve Murphy
committed
int include_location_lineno; /*!< lineno where include occurred */
int exec; /*!< set to non-zero if its a #exec statement */
/*!
* \brief if it's an exec, you'll have both the /var/tmp to read, and the original script
* \note Will never be NULL if exec is non-zero
*/
char *exec_file;
/*!
* \brief file name included
* \note Will never be NULL
*/
char *included_file;
Steve Murphy
committed
int inclusion_count; /*!< if the file is included more than once, a running count thereof -- but, worry not,
we explode the instances and will include those-- so all entries will be unique */
int output; /*!< a flag to indicate if the inclusion has been output */
struct ast_config_include *next; /*!< ptr to next inclusion in the list */
static void ast_variable_destroy(struct ast_variable *doomed);
static void ast_includes_destroy(struct ast_config_include *incls);
struct ast_variable *_ast_variable_new(const char *name, const char *value, const char *filename, const char *file, const char *func, int lineno)
Tilghman Lesher
committed
#else
struct ast_variable *ast_variable_new(const char *name, const char *value, const char *filename)
Tilghman Lesher
committed
#endif
int name_len = strlen(name) + 1;
int val_len = strlen(value) + 1;
int fn_len = strlen(filename) + 1;
/* Ensure a minimum length in case the filename is changed later. */
if (fn_len < MIN_VARIABLE_FNAME_SPACE) {
fn_len = MIN_VARIABLE_FNAME_SPACE;
}
if (
(variable = __ast_calloc(1, fn_len + name_len + val_len + sizeof(*variable), file, lineno, func))
Tilghman Lesher
committed
#else
(variable = ast_calloc(1, fn_len + name_len + val_len + sizeof(*variable)))
Tilghman Lesher
committed
#endif
char *dst = variable->stuff; /* writable space starts here */
/* Put file first so ast_include_rename() can calculate space available. */
variable->file = strcpy(dst, filename);
dst += fn_len;
variable->name = strcpy(dst, name);
dst += name_len;
variable->value = strcpy(dst, value);
/*!
* \internal
* \brief Move the contents from the source to the destination variable.
*
* \param dst_var Destination variable node
* \param src_var Source variable node
*
* \return Nothing
*/
static void ast_variable_move(struct ast_variable *dst_var, struct ast_variable *src_var)
{
dst_var->lineno = src_var->lineno;
dst_var->object = src_var->object;
dst_var->blanklines = src_var->blanklines;
dst_var->precomments = src_var->precomments;
src_var->precomments = NULL;
dst_var->sameline = src_var->sameline;
src_var->sameline = NULL;
dst_var->trailing = src_var->trailing;
src_var->trailing = NULL;
}
Steve Murphy
committed
struct ast_config_include *ast_include_new(struct ast_config *conf, const char *from_file, const char *included_file, int is_exec, const char *exec_file, int from_lineno, char *real_included_file_name, int real_included_file_name_size)
{
/* a file should be included ONCE. Otherwise, if one of the instances is changed,
* then all be changed. -- how do we know to include it? -- Handling modified
* instances is possible, I'd have
* to create a new master for each instance. */
Steve Murphy
committed
struct ast_config_include *inc;
Tilghman Lesher
committed
struct stat statbuf;
Steve Murphy
committed
inc = ast_include_find(conf, included_file);
Tilghman Lesher
committed
if (inc) {
do {
inc->inclusion_count++;
snprintf(real_included_file_name, real_included_file_name_size, "%s~~%d", included_file, inc->inclusion_count);
} while (stat(real_included_file_name, &statbuf) == 0);
Steve Murphy
committed
ast_log(LOG_WARNING,"'%s', line %d: Same File included more than once! This data will be saved in %s if saved back to disk.\n", from_file, from_lineno, real_included_file_name);
} else
*real_included_file_name = 0;
Steve Murphy
committed
inc = ast_calloc(1,sizeof(struct ast_config_include));
if (!inc) {
return NULL;
}
Steve Murphy
committed
inc->include_location_file = ast_strdup(from_file);
inc->include_location_lineno = from_lineno;
if (!ast_strlen_zero(real_included_file_name))
inc->included_file = ast_strdup(real_included_file_name);
else
inc->included_file = ast_strdup(included_file);
Steve Murphy
committed
inc->exec = is_exec;
if (is_exec)
inc->exec_file = ast_strdup(exec_file);
if (!inc->include_location_file
|| !inc->included_file
|| (is_exec && !inc->exec_file)) {
ast_includes_destroy(inc);
return NULL;
}
Steve Murphy
committed
/* attach this new struct to the conf struct */
inc->next = conf->includes;
conf->includes = inc;
Steve Murphy
committed
return inc;
}
void ast_include_rename(struct ast_config *conf, const char *from_file, const char *to_file)
{
struct ast_config_include *incl;
struct ast_category *cat;
Steve Murphy
committed
int from_len = strlen(from_file);
int to_len = strlen(to_file);
Steve Murphy
committed
if (strcmp(from_file, to_file) == 0) /* no use wasting time if the name is the same */
return;
Steve Murphy
committed
/* the manager code allows you to read in one config file, then
* write it back out under a different name. But, the new arrangement
* ties output lines to the file name. So, before you try to write
* the config file to disk, better riffle thru the data and make sure
* the file names are changed.
*/
Steve Murphy
committed
/* file names are on categories, includes (of course), and on variables. So,
Steve Murphy
committed
for (incl = conf->includes; incl; incl=incl->next) {
if (strcmp(incl->include_location_file,from_file) == 0) {
if (from_len >= to_len)
strcpy(incl->include_location_file, to_file);
else {
/* Keep the old filename if the allocation fails. */
str = ast_strdup(to_file);
if (str) {
ast_free(incl->include_location_file);
incl->include_location_file = str;
}
Steve Murphy
committed
}
}
}
for (cat = conf->root; cat; cat = cat->next) {
struct ast_variable **prev;
struct ast_variable *v;
struct ast_variable *new_var;
Steve Murphy
committed
if (strcmp(cat->file,from_file) == 0) {
if (from_len >= to_len)
strcpy(cat->file, to_file);
else {
/* Keep the old filename if the allocation fails. */
str = ast_strdup(to_file);
if (str) {
ast_free(cat->file);
cat->file = str;
}
Steve Murphy
committed
}
}
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
for (prev = &cat->root, v = cat->root; v; prev = &v->next, v = v->next) {
if (strcmp(v->file, from_file)) {
continue;
}
/*
* Calculate actual space available. The file string is
* intentionally stuffed before the name string just so we can
* do this.
*/
if (to_len < v->name - v->file) {
/* The new name will fit in the available space. */
str = (char *) v->file;/* Stupid compiler complains about discarding qualifiers even though I used a cast. */
strcpy(str, to_file);/* SAFE */
continue;
}
/* Keep the old filename if the allocation fails. */
new_var = ast_variable_new(v->name, v->value, to_file);
if (!new_var) {
continue;
}
/* Move items from the old list node to the replacement node. */
ast_variable_move(new_var, v);
/* Replace the old node in the list with the new node. */
new_var->next = v->next;
if (cat->last == v) {
cat->last = new_var;
Steve Murphy
committed
}
*prev = new_var;
ast_variable_destroy(v);
v = new_var;
Steve Murphy
committed
}
}
}
struct ast_config_include *ast_include_find(struct ast_config *conf, const char *included_file)
{
struct ast_config_include *x;
Steve Murphy
committed
if (strcmp(x->included_file,included_file) == 0)
return x;
}
return 0;
}
void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
{
if (!variable)
return;
if (category->last)
category->last->next = variable;
else
category->root = variable;
category->last = variable;
while (category->last->next)
category->last = category->last->next;
void ast_variable_insert(struct ast_category *category, struct ast_variable *variable, const char *line)
{
struct ast_variable *cur = category->root;
int lineno;
int insertline;
if (!variable || sscanf(line, "%30d", &insertline) != 1) {
Ryan Brindley
committed
}
if (!insertline) {
variable->next = category->root;
category->root = variable;
} else {
for (lineno = 1; lineno < insertline; lineno++) {
cur = cur->next;
Ryan Brindley
committed
if (!cur->next) {
Ryan Brindley
committed
}
}
variable->next = cur->next;
cur->next = variable;
}
}
static void ast_comment_destroy(struct ast_comment **comment)
{
struct ast_comment *n, *p;
for (p = *comment; p; p = n) {
n = p->next;
ast_free(p);
}
*comment = NULL;
}
static void ast_variable_destroy(struct ast_variable *doomed)
{
ast_comment_destroy(&doomed->precomments);
ast_comment_destroy(&doomed->sameline);
ast_comment_destroy(&doomed->trailing);
ast_free(doomed);
}
struct ast_variable *ast_variables_dup(struct ast_variable *var)
{
struct ast_variable *cloned;
struct ast_variable *tmp;
if (!(cloned = ast_variable_new(var->name, var->value, var->file))) {
return NULL;
}
tmp = cloned;
while ((var = var->next)) {
if (!(tmp->next = ast_variable_new(var->name, var->value, var->file))) {
ast_variables_destroy(cloned);
return NULL;
}
tmp = tmp->next;
}
return cloned;
}
struct ast_variable *ast_variables_reverse(struct ast_variable *var)
{
struct ast_variable *var1, *var2;
var1 = var;
if (!var1 || !var1->next) {
return var1;
}
var2 = var1->next;
var1->next = NULL;
while (var2) {
struct ast_variable *next = var2->next;
var2->next = var1;
var1 = var2;
var2 = next;
}
return var1;
}
void ast_variables_destroy(struct ast_variable *v)
{
struct ast_variable *vn;
Joshua Colp
committed
while (v) {
struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category)
{
struct ast_category *cat;
if (config->last_browse && (config->last_browse->name == category)) {
Ryan Brindley
committed
} else {
cat = ast_category_get(config, category, NULL);
Ryan Brindley
committed
}
return (cat) ? cat->root : NULL;
Matthew Jordan
committed
static inline struct ast_variable *variable_list_switch(struct ast_variable *l1, struct ast_variable *l2)
{
l1->next = l2->next;
l2->next = l1;
return l2;
}
struct ast_variable *ast_variable_list_sort(struct ast_variable *start)
{
struct ast_variable *p, *q;
struct ast_variable top;
memset(&top, 0, sizeof(top));
top.next = start;
if (start != NULL && start->next != NULL) {
while (changed) {
changed = 0;
q = ⊤
p = top.next;
while (p->next != NULL) {
if (p->next != NULL && strcmp(p->name, p->next->name) > 0) {
q->next = variable_list_switch(p, p->next);
changed = 1;
}
q = p;
if (p->next != NULL)
p = p->next;
}
}
}
return top.next;
struct ast_variable *ast_variable_list_append_hint(struct ast_variable **head, struct ast_variable *search_hint, struct ast_variable *newvar)
{
struct ast_variable *curr;
ast_assert(head != NULL);
if (!*head) {
*head = newvar;
} else {
if (search_hint == NULL) {
search_hint = *head;
}
for (curr = search_hint; curr->next; curr = curr->next);
curr->next = newvar;
}
for (curr = newvar; curr->next; curr = curr->next);
return curr;
}
Tilghman Lesher
committed
const char *ast_config_option(struct ast_config *cfg, const char *cat, const char *var)
Tilghman Lesher
committed
const char *tmp;
tmp = ast_variable_retrieve(cfg, cat, var);
Ryan Brindley
committed
if (!tmp) {
tmp = ast_variable_retrieve(cfg, "general", var);
Ryan Brindley
committed
}
return tmp;
}
const char *ast_variable_retrieve(struct ast_config *config, const char *category, const char *variable)
{
struct ast_variable *v;
if (category) {
for (v = ast_variable_browse(config, category); v; v = v->next) {
if (!strcasecmp(variable, v->name)) {
return v->value;
}
}
} else {
struct ast_category *cat;
for (cat = config->root; cat; cat = cat->next) {
for (v = cat->root; v; v = v->next) {
if (!strcasecmp(variable, v->name)) {
return v->value;
}
}
}
}
return NULL;
}
const char *ast_variable_retrieve_filtered(struct ast_config *config,
const char *category, const char *variable, const char *filter)
struct ast_category *cat = NULL;
const char *value;
while ((cat = ast_category_browse_filtered(config, category, cat, filter))) {
value = ast_variable_find(cat, variable);
if (value) {
return value;
}
return NULL;
}
const char *ast_variable_find(const struct ast_category *category, const char *variable)
{
return ast_variable_find_in_list(category->root, variable);
}
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
const struct ast_variable *ast_variable_find_variable_in_list(const struct ast_variable *list, const char *variable_name)
{
const struct ast_variable *v;
for (v = list; v; v = v->next) {
if (!strcasecmp(variable_name, v->name)) {
return v;
}
}
return NULL;
}
int ast_variables_match(const struct ast_variable *left, const struct ast_variable *right)
{
char *op;
if (left == right) {
return 1;
}
if (!(left && right)) {
return 0;
}
op = strrchr(right->name, ' ');
if (op) {
op++;
}
return ast_strings_match(left->value, op ? ast_strdupa(op) : NULL, right->value);
}
int ast_variable_lists_match(const struct ast_variable *left, const struct ast_variable *right, int exact_match)
{
const struct ast_variable *field;
int right_count = 0;
int left_count = 0;
if (left == right) {
return 1;
}
if (!(left && right)) {
return 0;
}
for (field = right; field; field = field->next) {
char *space = strrchr(field->name, ' ');
const struct ast_variable *old;
char * name = (char *)field->name;
if (space) {
name = ast_strdup(field->name);
if (!name) {
return 0;
}
name[space - field->name] = '\0';
}
old = ast_variable_find_variable_in_list(left, name);
if (name != field->name) {
ast_free(name);
}
if (exact_match) {
if (!old || strcmp(old->value, field->value)) {
return 0;
}
} else {
if (!ast_variables_match(old, field)) {
return 0;
}
}
right_count++;
}
if (exact_match) {
for (field = left; field; field = field->next) {
left_count++;
}
if (right_count != left_count) {
return 0;
}
}
return 1;
}
const char *ast_variable_find_in_list(const struct ast_variable *list, const char *variable)
{
const struct ast_variable *v;
for (v = list; v; v = v->next) {
if (!strcasecmp(variable, v->name)) {
return v->value;
}
const char *ast_variable_find_last_in_list(const struct ast_variable *list, const char *variable)
{
const struct ast_variable *v;
const char *found = NULL;
for (v = list; v; v = v->next) {
if (!strcasecmp(variable, v->name)) {
found = v->value;
}
}
return found;
}
static struct ast_variable *variable_clone(const struct ast_variable *old)
{
Steve Murphy
committed
struct ast_variable *new = ast_variable_new(old->name, old->value, old->file);
if (new) {
new->lineno = old->lineno;
new->object = old->object;
new->blanklines = old->blanklines;
/* TODO: clone comments? */
}
return new;
}
static void move_variables(struct ast_category *old, struct ast_category *new)
{
struct ast_variable *var = old->root;
/* we can just move the entire list in a single op */
ast_variable_append(new, var);
/*! \brief Returns true if ALL of the regex expressions and category name match.
* Both can be NULL (I.E. no predicate) which results in a true return;
*/
static int does_category_match(struct ast_category *cat, const char *category_name,
const char *match, char sep)
{
char *dupmatch;
char *nvp = NULL;
int match_found = 0, match_expressions = 0;
int template_ok = 0;
/* Only match on category name if it's not a NULL or empty string */
if (!ast_strlen_zero(category_name) && strcasecmp(cat->name, category_name)) {
return 0;
}
/* If match is NULL or empty, automatically match if not a template */
if (ast_strlen_zero(match)) {
return !cat->ignored;
}
dupmatch = ast_strdupa(match);
while ((nvp = ast_strsep(&dupmatch, sep, AST_STRSEP_STRIP))) {
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
struct ast_variable *v;
char *match_name;
char *match_value = NULL;
char *regerr;
int rc;
regex_t r_name, r_value;
match_expressions++;
match_name = ast_strsep(&nvp, '=', AST_STRSEP_STRIP);
match_value = ast_strsep(&nvp, '=', AST_STRSEP_STRIP);
/* an empty match value is OK. A NULL match value (no =) is NOT. */
if (match_value == NULL) {
break;
}
if (!strcmp("TEMPLATES", match_name)) {
if (!strcasecmp("include", match_value)) {
if (cat->ignored) {
template_ok = 1;
}
match_found++;
} else if (!strcasecmp("restrict", match_value)) {
if (cat->ignored) {
match_found++;
template_ok = 1;
} else {
break;
}
}
continue;
}
if ((rc = regcomp(&r_name, match_name, REG_EXTENDED | REG_NOSUB))) {
regerr = ast_alloca(128);
regerror(rc, &r_name, regerr, 128);
ast_log(LOG_ERROR, "Regular expression '%s' failed to compile: %s\n",
match_name, regerr);
regfree(&r_name);
return 0;
}
if ((rc = regcomp(&r_value, match_value, REG_EXTENDED | REG_NOSUB))) {
regerr = ast_alloca(128);
regerror(rc, &r_value, regerr, 128);
ast_log(LOG_ERROR, "Regular expression '%s' failed to compile: %s\n",
match_value, regerr);
regfree(&r_name);
regfree(&r_value);
return 0;
}
for (v = cat->root; v; v = v->next) {
if (!regexec(&r_name, v->name, 0, NULL, 0)
&& !regexec(&r_value, v->value, 0, NULL, 0)) {
match_found++;
break;
}
}
regfree(&r_name);
regfree(&r_value);
}
if (match_found == match_expressions && (!cat->ignored || template_ok)) {
return 1;
}
return 0;
}
static struct ast_category *new_category(const char *name, const char *in_file, int lineno, int template)
{
struct ast_category *category;
category = ast_calloc(1, sizeof(*category));
if (!category) {
return NULL;
}
category->file = ast_strdup(in_file);
if (!category->file) {
ast_category_destroy(category);
return NULL;
}
ast_copy_string(category->name, name, sizeof(category->name));
Steve Murphy
committed
category->lineno = lineno; /* if you don't know the lineno, set it to 999999 or something real big */
category->ignored = template;
struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno)
{
return new_category(name, in_file, lineno, 0);
}
struct ast_category *ast_category_new_template(const char *name, const char *in_file, int lineno)
return new_category(name, in_file, lineno, 1);
}
static struct ast_category *category_get_sep(const struct ast_config *config,
const char *category_name, const char *filter, char sep, char pointer_match_possible)
{
struct ast_category *cat;
if (pointer_match_possible) {
for (cat = config->root; cat; cat = cat->next) {
if (cat->name == category_name && does_category_match(cat, category_name, filter, sep)) {
return cat;
}