[asterisk-commits] murf: branch murf/bug_7638 r58925 -
/team/murf/bug_7638/utils/extconf.c
asterisk-commits at lists.digium.com
asterisk-commits at lists.digium.com
Thu Mar 15 12:03:56 MST 2007
Author: murf
Date: Thu Mar 15 14:03:56 2007
New Revision: 58925
URL: http://svn.digium.com/view/asterisk?view=rev&rev=58925
Log:
Moved the stuff for pbx_load back to the second half of the file; gathered everything needed to form the contexts. Almost compiles. Need to form entry points for needed functionality.
Modified:
team/murf/bug_7638/utils/extconf.c
Modified: team/murf/bug_7638/utils/extconf.c
URL: http://svn.digium.com/view/asterisk/team/murf/bug_7638/utils/extconf.c?view=diff&rev=58925&r1=58924&r2=58925
==============================================================================
--- team/murf/bug_7638/utils/extconf.c (original)
+++ team/murf/bug_7638/utils/extconf.c Thu Mar 15 14:03:56 2007
@@ -2323,6 +2323,8 @@
AST_LIST_HEAD_NOLOCK(varshead, ast_var_t);
+static struct varshead globals = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
+
/* IN CONFLICT: struct ast_var_t *ast_var_assign(const char *name, const char *value); */
@@ -2610,34 +2612,6 @@
}
/* stolen from pbx.c */
-struct ast_context;
-struct ast_app;
-#ifdef LOW_MEMORY
-#define EXT_DATA_SIZE 256
-#else
-#define EXT_DATA_SIZE 8192
-#endif
-/*!
- * When looking up extensions, we can have different requests
- * identified by the 'action' argument, as follows.
- * Note that the coding is such that the low 4 bits are the
- * third argument to extension_match_core.
- */
-enum ext_match_t {
- E_MATCHMORE = 0x00, /* extension can match but only with more 'digits' */
- E_CANMATCH = 0x01, /* extension can match with or without more 'digits' */
- E_MATCH = 0x02, /* extension is an exact match */
- E_MATCH_MASK = 0x03, /* mask for the argument to extension_match_core() */
- E_SPAWN = 0x12, /* want to spawn an extension. Requires exact match */
- E_FINDLABEL = 0x22 /* returns the priority for a given label. Requires exact match */
-};
-
-#ifdef NOT_ANYMORE
-static AST_RWLIST_HEAD_STATIC(switches, ast_switch);
-#endif
-
-#define SWITCH_DATA_LENGTH 256
-
#define VAR_BUF_SIZE 4096
#define VAR_NORMAL 1
@@ -2712,7 +2686,7 @@
/*! \brief ast_context: An extension context */
struct ast_context {
- ast_mutex_t lock; /*!< A lock to prevent multiple threads from clobbering the context */
+ ast_rwlock_t lock; /*!< A lock to prevent multiple threads from clobbering the context */
struct ast_exten *root; /*!< The root of the list of extensions */
struct ast_context *next; /*!< Link them together */
struct ast_include *includes; /*!< Include other contexts */
@@ -2786,12 +2760,1578 @@
#define STATUS_NO_LABEL 4
#define STATUS_SUCCESS 5
+
+#if defined ( __i386__) && (defined(__FreeBSD__) || defined(linux))
+#if defined(__FreeBSD__)
+#include <machine/cpufunc.h>
+#elif defined(linux)
+static __inline uint64_t
+rdtsc(void)
+{
+ uint64_t rv;
+
+ __asm __volatile(".byte 0x0f, 0x31" : "=A" (rv));
+ return (rv);
+}
+#endif
+#else /* supply a dummy function on other platforms */
+static __inline uint64_t
+rdtsc(void)
+{
+ return 0;
+}
+#endif
+
+
+static struct ast_custom_function *ast_custom_function_find(const char *name)
+{
+ struct ast_custom_function *acf = NULL;
+
+ AST_RWLIST_TRAVERSE(&acf_root, acf, acflist) {
+ if (!strcmp(name, acf->name))
+ break;
+ }
+
+ return acf;
+}
+
+
+/*! \brief return a pointer to the arguments of the function,
+ * and terminates the function name with '\\0'
+ */
+static char *func_args(const char *function)
+{
+ char *args = strchr(function, '(');
+
+ if (!args)
+ ast_log(LOG_WARNING, "Function doesn't contain parentheses. Assuming null argument.\n");
+ else {
+ char *p;
+ *args++ = '\0';
+ if ((p = strrchr(args, ')')) )
+ *p = '\0';
+ else
+ ast_log(LOG_WARNING, "Can't find trailing parenthesis?\n");
+ }
+ return args;
+}
+
+static struct ast_var_t *ast_var_assign(const char *name, const char *value)
+{
+ struct ast_var_t *var;
+ int name_len = strlen(name) + 1;
+ int value_len = strlen(value) + 1;
+
+ if (!(var = ast_calloc(sizeof(*var) + name_len + value_len, sizeof(char)))) {
+ return NULL;
+ }
+
+ ast_copy_string(var->name, name, name_len);
+ var->value = var->name + name_len;
+ ast_copy_string(var->value, value, value_len);
+
+ return var;
+}
+
+static void ast_var_delete(struct ast_var_t *var)
+{
+ if (var)
+ free(var);
+}
+
+
+static int ast_func_write(struct ast_channel *chan, const char *function, const char *value)
+{
+ char *args = func_args(function);
+ struct ast_custom_function *acfptr = ast_custom_function_find(function);
+
+ if (acfptr == NULL)
+ ast_log(LOG_ERROR, "Function %s not registered\n", function);
+ else if (!acfptr->write)
+ ast_log(LOG_ERROR, "Function %s cannot be written to\n", function);
+ else
+ return acfptr->write(chan, function, args, value);
+
+ return -1;
+}
+
+static unsigned int ast_app_separate_args(char *buf, char delim, char **array, int arraylen)
+{
+ int argc;
+ char *scan;
+ int paren = 0, quote = 0;
+
+ if (!buf || !array || !arraylen)
+ return 0;
+
+ memset(array, 0, arraylen * sizeof(*array));
+
+ scan = buf;
+
+ for (argc = 0; *scan && (argc < arraylen - 1); argc++) {
+ array[argc] = scan;
+ for (; *scan; scan++) {
+ if (*scan == '(')
+ paren++;
+ else if (*scan == ')') {
+ if (paren)
+ paren--;
+ } else if (*scan == '"' && delim != '"') {
+ quote = quote ? 0 : 1;
+ /* Remove quote character from argument */
+ memmove(scan, scan + 1, strlen(scan));
+ scan--;
+ } else if (*scan == '\\') {
+ /* Literal character, don't parse */
+ memmove(scan, scan + 1, strlen(scan));
+ } else if ((*scan == delim) && !paren && !quote) {
+ *scan++ = '\0';
+ break;
+ }
+ }
+ }
+
+ if (*scan)
+ array[argc++] = scan;
+
+ return argc;
+}
+
+static void pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
+{
+ struct ast_var_t *newvariable;
+ struct varshead *headp;
+ const char *nametail = name;
+
+ /* XXX may need locking on the channel ? */
+ if (name[strlen(name)-1] == ')') {
+ char *function = ast_strdupa(name);
+
+ ast_func_write(chan, function, value);
+ return;
+ }
+
+ headp = &globals;
+
+ /* For comparison purposes, we have to strip leading underscores */
+ if (*nametail == '_') {
+ nametail++;
+ if (*nametail == '_')
+ nametail++;
+ }
+
+ AST_LIST_TRAVERSE (headp, newvariable, entries) {
+ if (strcasecmp(ast_var_name(newvariable), nametail) == 0) {
+ /* there is already such a variable, delete it */
+ AST_LIST_REMOVE(headp, newvariable, entries);
+ ast_var_delete(newvariable);
+ break;
+ }
+ }
+
+ if (value) {
+ if ((option_verbose > 1) && (headp == &globals))
+ ast_verbose(VERBOSE_PREFIX_2 "Setting global variable '%s' to '%s'\n", name, value);
+ newvariable = ast_var_assign(name, value);
+ AST_LIST_INSERT_HEAD(headp, newvariable, entries);
+ }
+
+}
+
+/*! \brief Helper for get_range.
+ * return the index of the matching entry, starting from 1.
+ * If names is not supplied, try numeric values.
+ */
+static int lookup_name(const char *s, char *const names[], int max)
+{
+ int i;
+
+ if (names) {
+ for (i = 0; names[i]; i++) {
+ if (!strcasecmp(s, names[i]))
+ return i+1;
+ }
+ } else if (sscanf(s, "%d", &i) == 1 && i >= 1 && i <= max) {
+ return i;
+ }
+ return 0; /* error return */
+}
+
+/*! \brief helper function to return a range up to max (7, 12, 31 respectively).
+ * names, if supplied, is an array of names that should be mapped to numbers.
+ */
+static unsigned get_range(char *src, int max, char *const names[], const char *msg)
+{
+ int s, e; /* start and ending position */
+ unsigned int mask = 0;
+
+ /* Check for whole range */
+ if (ast_strlen_zero(src) || !strcmp(src, "*")) {
+ s = 0;
+ e = max - 1;
+ } else {
+ /* Get start and ending position */
+ char *c = strchr(src, '-');
+ if (c)
+ *c++ = '\0';
+ /* Find the start */
+ s = lookup_name(src, names, max);
+ if (!s) {
+ ast_log(LOG_WARNING, "Invalid %s '%s', assuming none\n", msg, src);
+ return 0;
+ }
+ s--;
+ if (c) { /* find end of range */
+ e = lookup_name(c, names, max);
+ if (!e) {
+ ast_log(LOG_WARNING, "Invalid end %s '%s', assuming none\n", msg, c);
+ return 0;
+ }
+ e--;
+ } else
+ e = s;
+ }
+ /* Fill the mask. Remember that ranges are cyclic */
+ mask = 1 << e; /* initialize with last element */
+ while (s != e) {
+ if (s >= max) {
+ s = 0;
+ mask |= (1 << s);
+ } else {
+ mask |= (1 << s);
+ s++;
+ }
+ }
+ return mask;
+}
+
+/*! \brief store a bitmask of valid times, one bit each 2 minute */
+static void get_timerange(struct ast_timing *i, char *times)
+{
+ char *e;
+ int x;
+ int s1, s2;
+ int e1, e2;
+ /* int cth, ctm; */
+
+ /* start disabling all times, fill the fields with 0's, as they may contain garbage */
+ memset(i->minmask, 0, sizeof(i->minmask));
+
+ /* 2-minutes per bit, since the mask has only 32 bits :( */
+ /* Star is all times */
+ if (ast_strlen_zero(times) || !strcmp(times, "*")) {
+ for (x=0; x<24; x++)
+ i->minmask[x] = 0x3fffffff; /* 30 bits */
+ return;
+ }
+ /* Otherwise expect a range */
+ e = strchr(times, '-');
+ if (!e) {
+ ast_log(LOG_WARNING, "Time range is not valid. Assuming no restrictions based on time.\n");
+ return;
+ }
+ *e++ = '\0';
+ /* XXX why skip non digits ? */
+ while (*e && !isdigit(*e))
+ e++;
+ if (!*e) {
+ ast_log(LOG_WARNING, "Invalid time range. Assuming no restrictions based on time.\n");
+ return;
+ }
+ if (sscanf(times, "%d:%d", &s1, &s2) != 2) {
+ ast_log(LOG_WARNING, "%s isn't a time. Assuming no restrictions based on time.\n", times);
+ return;
+ }
+ if (sscanf(e, "%d:%d", &e1, &e2) != 2) {
+ ast_log(LOG_WARNING, "%s isn't a time. Assuming no restrictions based on time.\n", e);
+ return;
+ }
+ /* XXX this needs to be optimized */
+#if 1
+ s1 = s1 * 30 + s2/2;
+ if ((s1 < 0) || (s1 >= 24*30)) {
+ ast_log(LOG_WARNING, "%s isn't a valid start time. Assuming no time.\n", times);
+ return;
+ }
+ e1 = e1 * 30 + e2/2;
+ if ((e1 < 0) || (e1 >= 24*30)) {
+ ast_log(LOG_WARNING, "%s isn't a valid end time. Assuming no time.\n", e);
+ return;
+ }
+ /* Go through the time and enable each appropriate bit */
+ for (x=s1;x != e1;x = (x + 1) % (24 * 30)) {
+ i->minmask[x/30] |= (1 << (x % 30));
+ }
+ /* Do the last one */
+ i->minmask[x/30] |= (1 << (x % 30));
+#else
+ for (cth=0; cth<24; cth++) {
+ /* Initialize masks to blank */
+ i->minmask[cth] = 0;
+ for (ctm=0; ctm<30; ctm++) {
+ if (
+ /* First hour with more than one hour */
+ (((cth == s1) && (ctm >= s2)) &&
+ ((cth < e1)))
+ /* Only one hour */
+ || (((cth == s1) && (ctm >= s2)) &&
+ ((cth == e1) && (ctm <= e2)))
+ /* In between first and last hours (more than 2 hours) */
+ || ((cth > s1) &&
+ (cth < e1))
+ /* Last hour with more than one hour */
+ || ((cth > s1) &&
+ ((cth == e1) && (ctm <= e2)))
+ )
+ i->minmask[cth] |= (1 << (ctm / 2));
+ }
+ }
+#endif
+ /* All done */
+ return;
+}
+
+static void null_datad(void *foo)
+{
+}
+
+/*! \brief Find realtime engine for realtime family */
+static struct ast_config_engine *find_engine(const char *family, char *database, int dbsiz, char *table, int tabsiz)
+{
+ struct ast_config_engine *eng, *ret = NULL;
+ struct ast_config_map *map;
+
+
+ for (map = config_maps; map; map = map->next) {
+ if (!strcasecmp(family, map->name)) {
+ if (database)
+ ast_copy_string(database, map->database, dbsiz);
+ if (table)
+ ast_copy_string(table, map->table ? map->table : family, tabsiz);
+ break;
+ }
+ }
+
+ /* Check if the required driver (engine) exist */
+ if (map) {
+ for (eng = config_engine_list; !ret && eng; eng = eng->next) {
+ if (!strcasecmp(eng->name, map->driver))
+ ret = eng;
+ }
+ }
+
+
+ /* if we found a mapping, but the engine is not available, then issue a warning */
+ if (map && !ret)
+ ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver);
+
+ return ret;
+}
+
+struct ast_category *ast_config_get_current_category(const struct ast_config *cfg);
+
+struct ast_category *ast_config_get_current_category(const struct ast_config *cfg)
+{
+ return cfg->current;
+}
+
+static struct ast_category *ast_category_new(const char *name);
+
+static struct ast_category *ast_category_new(const char *name)
+{
+ struct ast_category *category;
+
+ if ((category = ast_calloc(1, sizeof(*category))))
+ ast_copy_string(category->name, name, sizeof(category->name));
+ return category;
+}
+
+static struct ast_category *category_get(const struct ast_config *config, const char *category_name, int ignored);
+
+static struct ast_category *category_get(const struct ast_config *config, const char *category_name, int ignored)
+{
+ struct ast_category *cat;
+
+ /* try exact match first, then case-insensitive match */
+ for (cat = config->root; cat; cat = cat->next) {
+ if (cat->name == category_name && (ignored || !cat->ignored))
+ return cat;
+ }
+
+ for (cat = config->root; cat; cat = cat->next) {
+ if (!strcasecmp(cat->name, category_name) && (ignored || !cat->ignored))
+ return cat;
+ }
+
+ return NULL;
+}
+
+static struct ast_category *ast_category_get(const struct ast_config *config, const char *category_name);
+
+static struct ast_category *ast_category_get(const struct ast_config *config, const char *category_name)
+{
+ return category_get(config, category_name, 0);
+}
+
+static void move_variables(struct ast_category *old, struct ast_category *new)
+{
+ struct ast_variable *var = old->root;
+ old->root = NULL;
+#if 1
+ /* we can just move the entire list in a single op */
+ ast_variable_append(new, var);
+#else
+ while (var) {
+ struct ast_variable *next = var->next;
+ var->next = NULL;
+ ast_variable_append(new, var);
+ var = next;
+ }
+#endif
+}
+
+static void inherit_category(struct ast_category *new, const struct ast_category *base)
+{
+ struct ast_variable *var;
+
+ for (var = base->root; var; var = var->next)
+ ast_variable_append(new, variable_clone(var));
+}
+
+static void ast_category_append(struct ast_config *config, struct ast_category *category);
+
+static void ast_category_append(struct ast_config *config, struct ast_category *category)
+{
+ if (config->last)
+ config->last->next = category;
+ else
+ config->root = category;
+ config->last = category;
+ config->current = category;
+}
+
+static void ast_category_destroy(struct ast_category *cat);
+
+static void ast_category_destroy(struct ast_category *cat)
+{
+ ast_variables_destroy(cat->root);
+ free(cat);
+}
+
+static struct ast_config_engine text_file_engine = {
+ .name = "text",
+ .load_func = config_text_file_load,
+};
+
+
+static struct ast_config *ast_config_internal_load(const char *filename, struct ast_config *cfg, int withcomments);
+
+static struct ast_config *ast_config_internal_load(const char *filename, struct ast_config *cfg, int withcomments)
+{
+ char db[256];
+ char table[256];
+ struct ast_config_engine *loader = &text_file_engine;
+ struct ast_config *result;
+
+ if (cfg->include_level == cfg->max_include_level) {
+ ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level);
+ return NULL;
+ }
+
+ cfg->include_level++;
+ ast_log(LOG_WARNING, "internal loading file %s level=%d\n", filename, cfg->include_level);
+
+ if (strcmp(filename, extconfig_conf) && strcmp(filename, "asterisk.conf") && config_engine_list) {
+ struct ast_config_engine *eng;
+
+ eng = find_engine(filename, db, sizeof(db), table, sizeof(table));
+
+
+ if (eng && eng->load_func) {
+ loader = eng;
+ } else {
+ eng = find_engine("global", db, sizeof(db), table, sizeof(table));
+ if (eng && eng->load_func)
+ loader = eng;
+ }
+ }
+
+ result = loader->load_func(db, table, filename, cfg, withcomments);
+ ast_log(LOG_WARNING, "finished internal loading file %s level=%d\n", filename, cfg->include_level);
+
+ if (result)
+ result->include_level--;
+
+ return result;
+}
+
+
+static int process_text_line(struct ast_config *cfg, struct ast_category **cat, char *buf, int lineno, const char *configfile, int withcomments)
+{
+ char *c;
+ char *cur = buf;
+ struct ast_variable *v;
+ char cmd[512], exec_file[512];
+ int object, do_exec, do_include;
+
+ /* Actually parse the entry */
+ if (cur[0] == '[') {
+ struct ast_category *newcat = NULL;
+ char *catname;
+
+ /* A category header */
+ c = strchr(cur, ']');
+ if (!c) {
+ ast_log(LOG_WARNING, "parse error: no closing ']', line %d of %s\n", lineno, configfile);
+ return -1;
+ }
+ *c++ = '\0';
+ cur++;
+ if (*c++ != '(')
+ c = NULL;
+ catname = cur;
+ if (!(*cat = newcat = ast_category_new(catname))) {
+ return -1;
+ }
+ /* add comments */
+ if (withcomments && comment_buffer && comment_buffer[0] ) {
+ newcat->precomments = ALLOC_COMMENT(comment_buffer);
+ }
+ if (withcomments && lline_buffer && lline_buffer[0] ) {
+ newcat->sameline = ALLOC_COMMENT(lline_buffer);
+ }
+ if( withcomments )
+ CB_RESET();
+
+ /* If there are options or categories to inherit from, process them now */
+ if (c) {
+ if (!(cur = strchr(c, ')'))) {
+ ast_log(LOG_WARNING, "parse error: no closing ')', line %d of %s\n", lineno, configfile);
+ return -1;
+ }
+ *cur = '\0';
+ while ((cur = strsep(&c, ","))) {
+ if (!strcasecmp(cur, "!")) {
+ (*cat)->ignored = 1;
+ } else if (!strcasecmp(cur, "+")) {
+ *cat = category_get(cfg, catname, 1);
+ if (!*cat) {
+ ast_config_destroy(cfg);
+ if (newcat)
+ ast_category_destroy(newcat);
+ ast_log(LOG_WARNING, "Category addition requested, but category '%s' does not exist, line %d of %s\n", catname, lineno, configfile);
+ return -1;
+ }
+ if (newcat) {
+ move_variables(newcat, *cat);
+ ast_category_destroy(newcat);
+ newcat = NULL;
+ }
+ } else {
+ struct ast_category *base;
+
+ base = category_get(cfg, cur, 1);
+ if (!base) {
+ ast_log(LOG_WARNING, "Inheritance requested, but category '%s' does not exist, line %d of %s\n", cur, lineno, configfile);
+ return -1;
+ }
+ inherit_category(*cat, base);
+ }
+ }
+ }
+ if (newcat)
+ ast_category_append(cfg, *cat);
+ } else if (cur[0] == '#') {
+ /* A directive */
+ cur++;
+ c = cur;
+ while(*c && (*c > 32)) c++;
+ if (*c) {
+ *c = '\0';
+ /* Find real argument */
+ c = ast_skip_blanks(c + 1);
+ if (!*c)
+ c = NULL;
+ } else
+ c = NULL;
+ do_include = !strcasecmp(cur, "include");
+ if(!do_include)
+ do_exec = !strcasecmp(cur, "exec");
+ else
+ do_exec = 0;
+ if (do_exec && !ast_opt_exec_includes) {
+ ast_log(LOG_WARNING, "Cannot perform #exec unless execincludes option is enabled in asterisk.conf (options section)!\n");
+ do_exec = 0;
+ }
+ if (do_include || do_exec) {
+ if (c) {
+ /* Strip off leading and trailing "'s and <>'s */
+ while((*c == '<') || (*c == '>') || (*c == '\"')) c++;
+ /* Get rid of leading mess */
+ cur = c;
+ while (!ast_strlen_zero(cur)) {
+ c = cur + strlen(cur) - 1;
+ if ((*c == '>') || (*c == '<') || (*c == '\"'))
+ *c = '\0';
+ else
+ break;
+ }
+ /* #exec </path/to/executable>
+ We create a tmp file, then we #include it, then we delete it. */
+ if (do_exec) {
+ snprintf(exec_file, sizeof(exec_file), "/var/tmp/exec.%d.%ld", (int)time(NULL), (long)pthread_self());
+ snprintf(cmd, sizeof(cmd), "%s > %s 2>&1", cur, exec_file);
+ ast_safe_system(cmd);
+ cur = exec_file;
+ } else
+ exec_file[0] = '\0';
+ /* A #include */
+ ast_log(LOG_WARNING, "Reading in included file %s withcomments=%d\n", cur, withcomments);
+
+ do_include = ast_config_internal_load(cur, cfg, withcomments) ? 1 : 0;
+ if(!ast_strlen_zero(exec_file))
+ unlink(exec_file);
+ if(!do_include)
+ return 0;
+ ast_log(LOG_WARNING, "Done reading in included file %s withcomments=%d\n", cur, withcomments);
+
+ } else {
+ ast_log(LOG_WARNING, "Directive '#%s' needs an argument (%s) at line %d of %s\n",
+ do_exec ? "exec" : "include",
+ do_exec ? "/path/to/executable" : "filename",
+ lineno,
+ configfile);
+ }
+ }
+ else
+ ast_log(LOG_WARNING, "Unknown directive '%s' at line %d of %s\n", cur, lineno, configfile);
+ } else {
+ /* Just a line (variable = value) */
+ if (!*cat) {
+ ast_log(LOG_WARNING,
+ "parse error: No category context for line %d of %s\n", lineno, configfile);
+ return -1;
+ }
+ c = strchr(cur, '=');
+ if (c) {
+ *c = 0;
+ c++;
+ /* Ignore > in => */
+ if (*c== '>') {
+ object = 1;
+ c++;
+ } else
+ object = 0;
+ if ((v = ast_variable_new(ast_strip(cur), ast_strip(c)))) {
+ v->lineno = lineno;
+ v->object = object;
+ /* Put and reset comments */
+ v->blanklines = 0;
+ ast_variable_append(*cat, v);
+ /* add comments */
+ if (withcomments && comment_buffer && comment_buffer[0] ) {
+ v->precomments = ALLOC_COMMENT(comment_buffer);
+ }
+ if (withcomments && lline_buffer && lline_buffer[0] ) {
+ v->sameline = ALLOC_COMMENT(lline_buffer);
+ }
+ if( withcomments )
+ CB_RESET();
+
+ } else {
+ return -1;
+ }
+ } else {
+ ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
+ }
+ }
+ return 0;
+}
+
+
+static struct ast_config *config_text_file_load(const char *database, const char *table, const char *filename, struct ast_config *cfg, int withcomments)
+{
+ char fn[256];
+ char buf[8192];
+ char *new_buf, *comment_p, *process_buf;
+ FILE *f;
+ int lineno=0;
+ int comment = 0, nest[MAX_NESTED_COMMENTS];
+ struct ast_category *cat = NULL;
+ int count = 0;
+ struct stat statbuf;
+
+ cat = ast_config_get_current_category(cfg);
+
+ if (filename[0] == '/') {
+ ast_copy_string(fn, filename, sizeof(fn));
+ } else {
+ snprintf(fn, sizeof(fn), "%s/%s", (char *)ast_config_AST_CONFIG_DIR, filename);
+ }
+
+ ast_log(LOG_WARNING, "textfile load %s \n", fn);
+
+ if (withcomments && cfg && cfg->include_level < 2 ) {
+ CB_INIT();
+ }
+
+#ifdef AST_INCLUDE_GLOB
+ {
+ int glob_ret;
+ glob_t globbuf;
+ ast_log(LOG_WARNING, "textfile load %s glob\n", fn);
+
+ globbuf.gl_offs = 0; /* initialize it to silence gcc */
+#ifdef SOLARIS
+ glob_ret = glob(fn, GLOB_NOCHECK, NULL, &globbuf);
+#else
+ glob_ret = glob(fn, GLOB_NOMAGIC|GLOB_BRACE, NULL, &globbuf);
+#endif
+ if (glob_ret == GLOB_NOSPACE)
+ ast_log(LOG_WARNING,
+ "Glob Expansion of pattern '%s' failed: Not enough memory\n", fn);
+ else if (glob_ret == GLOB_ABORTED)
+ ast_log(LOG_WARNING,
+ "Glob Expansion of pattern '%s' failed: Read error\n", fn);
+ else {
+ /* loop over expanded files */
+ int i;
+ for (i=0; i<globbuf.gl_pathc; i++) {
+ ast_copy_string(fn, globbuf.gl_pathv[i], sizeof(fn));
+#endif
+ do {
+ if (stat(fn, &statbuf))
+ continue;
+
+ if (!S_ISREG(statbuf.st_mode)) {
+ ast_log(LOG_WARNING, "'%s' is not a regular file, ignoring\n", fn);
+ continue;
+ }
+ if (option_verbose > 1) {
+ ast_verbose(VERBOSE_PREFIX_2 "Parsing '%s': ", fn);
+ fflush(stdout);
+ }
+ if (!(f = fopen(fn, "r"))) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "No file to parse: %s\n", fn);
+ if (option_verbose > 1)
+ ast_verbose( "Not found (%s)\n", strerror(errno));
+ continue;
+ }
+ count++;
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Parsing %s\n", fn);
+ if (option_verbose > 1)
+ ast_verbose("Found\n");
+ while(!feof(f)) {
+ lineno++;
+ if (fgets(buf, sizeof(buf), f)) {
+ if ( withcomments ) {
+ CB_ADD(lline_buffer); /* add the current lline buffer to the comment buffer */
+ lline_buffer[0] = 0; /* erase the lline buffer */
+ }
+
+ new_buf = buf;
+ if (comment)
+ process_buf = NULL;
+ else
+ process_buf = buf;
+
+ while ((comment_p = strchr(new_buf, COMMENT_META))) {
+ if ((comment_p > new_buf) && (*(comment_p-1) == '\\')) {
+ /* Yuck, gotta memmove */
+ memmove(comment_p - 1, comment_p, strlen(comment_p) + 1);
+ new_buf = comment_p;
+ } else if(comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) {
+ /* Meta-Comment start detected ";--" */
+ if (comment < MAX_NESTED_COMMENTS) {
+ *comment_p = '\0';
+ new_buf = comment_p + 3;
+ comment++;
+ nest[comment-1] = lineno;
+ } else {
+ ast_log(LOG_ERROR, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS);
+ }
+ } else if ((comment_p >= new_buf + 2) &&
+ (*(comment_p - 1) == COMMENT_TAG) &&
+ (*(comment_p - 2) == COMMENT_TAG)) {
+ /* Meta-Comment end detected */
+ comment--;
+ new_buf = comment_p + 1;
+ if (!comment) {
+ /* Back to non-comment now */
+ if (process_buf) {
+ /* Actually have to move what's left over the top, then continue */
+ char *oldptr;
+ oldptr = process_buf + strlen(process_buf);
+ if ( withcomments ) {
+ CB_ADD(";");
+ CB_ADD_LEN(oldptr+1,new_buf-oldptr-1);
+ }
+
+ memmove(oldptr, new_buf, strlen(new_buf) + 1);
+ new_buf = oldptr;
+ } else
+ process_buf = new_buf;
+ }
+ } else {
+ if (!comment) {
+ /* If ; is found, and we are not nested in a comment,
+ we immediately stop all comment processing */
+ if ( withcomments ) {
+ LLB_ADD(comment_p);
+ }
+ *comment_p = '\0';
+ new_buf = comment_p;
+ } else
+ new_buf = comment_p + 1;
+ }
+ }
+ if( withcomments && comment && !process_buf )
+ {
+ CB_ADD(buf); /* the whole line is a comment, store it */
+ }
+
+ if (process_buf) {
+ char *buf = ast_strip(process_buf);
+ if (!ast_strlen_zero(buf)) {
+ if (process_text_line(cfg, &cat, buf, lineno, filename, withcomments)) {
+ cfg = NULL;
+ break;
+ }
+ }
+ }
+ }
+ }
+ fclose(f);
+ } while(0);
+ if (comment) {
+ ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment]);
+ }
+#ifdef AST_INCLUDE_GLOB
+ if (!cfg)
+ break;
+ }
+ globfree(&globbuf);
+ }
+ }
+#endif
+ ast_log(LOG_WARNING, "textfile load %s almost done\n", fn);
+ if (cfg && cfg->include_level == 1 && withcomments && comment_buffer) {
+ if (comment_buffer) {
+ ast_log(LOG_WARNING, "textfile load %s almost almost done\n", fn);
+ free(comment_buffer);
+ free(lline_buffer);
+ comment_buffer=0;
+ lline_buffer=0;
+ comment_buffer_size=0;
+ lline_buffer_size=0;
+ }
+ }
+ if (count == 0)
+ return NULL;
+
+ ast_log(LOG_WARNING, "textfile load %s done\n", fn);
+ return cfg;
+}
+
+
+static struct ast_config *ast_config_new(void) ;
+
+static struct ast_config *ast_config_new(void)
+{
+ struct ast_config *config;
+
+ if ((config = ast_calloc(1, sizeof(*config))))
+ config->max_include_level = MAX_INCLUDE_LEVEL;
+ return config;
+}
+
+static struct ast_config *ast_config_load(const char *filename);
+
+static struct ast_config *ast_config_load(const char *filename)
+{
+ struct ast_config *cfg;
+ struct ast_config *result;
+
+ cfg = ast_config_new();
+ if (!cfg)
+ return NULL;
+
+ result = ast_config_internal_load(filename, cfg, 0);
+ if (!result)
+ ast_config_destroy(cfg);
+
+ return result;
+}
+
+static struct ast_config *ast_config_load_with_comments(const char *filename)
+{
+ struct ast_config *cfg;
+ struct ast_config *result;
+
+ cfg = ast_config_new();
+ if (!cfg)
+ return NULL;
+
+ result = ast_config_internal_load(filename, cfg, 1);
+ if (!result)
+ ast_config_destroy(cfg);
+
+ return result;
+}
+
+static struct ast_category *next_available_category(struct ast_category *cat)
+{
+ for (; cat && cat->ignored; cat = cat->next);
+
+ return cat;
+}
+
+static char *ast_category_browse(struct ast_config *config, const char *prev)
+{
+ struct ast_category *cat = NULL;
+
+ if (prev && config->last_browse && (config->last_browse->name == prev))
+ cat = config->last_browse->next;
+ else if (!prev && config->root)
+ cat = config->root;
+ else if (prev) {
+ for (cat = config->root; cat; cat = cat->next) {
+ if (cat->name == prev) {
+ cat = cat->next;
+ break;
+ }
+ }
+ if (!cat) {
+ for (cat = config->root; cat; cat = cat->next) {
+ if (!strcasecmp(cat->name, prev)) {
+ cat = cat->next;
+ break;
+ }
+ }
+ }
+ }
+
+ if (cat)
+ cat = next_available_category(cat);
+
+ config->last_browse = cat;
+ return (cat) ? cat->name : NULL;
+}
+
+
+
+static int bit_at(unsigned int *word, int bitsperword, int bitnum);
+
+static int bit_at(unsigned int *word, int bitsperword, int bitnum)
+{
+ return word[bitnum/bitsperword] & (1 << (bitnum % bitsperword));
+}
+
+void ast_config_set_current_category(struct ast_config *cfg, const struct ast_category *cat);
+
+void ast_config_set_current_category(struct ast_config *cfg, const struct ast_category *cat)
+{
+ /* cast below is just to silence compiler warning about dropping "const" */
+ cfg->current = (struct ast_category *) cat;
+}
+
+static int config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator);
+
+static int config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
+{
+ FILE *f;
+ char fn[256];
+ char date[256]="";
+ time_t t;
+ struct ast_variable *var;
+ struct ast_category *cat;
+ struct ast_comment *cmt;
+ int blanklines = 0;
+
+ if (configfile[0] == '/') {
+ ast_copy_string(fn, configfile, sizeof(fn));
+ } else {
+ snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, configfile);
+ }
+ time(&t);
+ ast_copy_string(date, ctime(&t), sizeof(date));
+#ifdef __CYGWIN__
+ if ((f = fopen(fn, "w+"))) {
+#else
+ if ((f = fopen(fn, "w"))) {
+#endif
+ if (option_verbose > 1)
+ ast_verbose(VERBOSE_PREFIX_2 "Saving '%s': ", fn);
+ fprintf(f, ";!\n");
+ fprintf(f, ";! Automatically generated configuration file\n");
+ if (strcmp(configfile, fn))
+ fprintf(f, ";! Filename: %s (%s)\n", configfile, fn);
+ else
+ fprintf(f, ";! Filename: %s\n", configfile);
+ fprintf(f, ";! Generator: %s\n", generator);
+ fprintf(f, ";! Creation Date: %s", date);
+ fprintf(f, ";!\n");
+ cat = cfg->root;
+ while(cat) {
+ /* Dump section with any appropriate comment */
+ for (cmt = cat->precomments; cmt; cmt=cmt->next)
+ {
+ if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
+ fprintf(f,"%s", cmt->cmt);
+ }
+ if (!cat->precomments)
+ fprintf(f,"\n");
+ fprintf(f, "[%s]", cat->name);
+ for(cmt = cat->sameline; cmt; cmt=cmt->next)
+ {
+ fprintf(f,"%s", cmt->cmt);
+ }
+ if (!cat->sameline)
+ fprintf(f,"\n");
+ var = cat->root;
+ while(var) {
+ for (cmt = var->precomments; cmt; cmt=cmt->next)
+ {
+ if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
+ fprintf(f,"%s", cmt->cmt);
+ }
+ if (var->sameline)
+ fprintf(f, "%s %s %s %s", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
+ else
+ fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value);
+ if (var->blanklines) {
+ blanklines = var->blanklines;
+ while (blanklines--)
+ fprintf(f, "\n");
+ }
+
+ var = var->next;
+ }
+#if 0
+ /* Put an empty line */
+ fprintf(f, "\n");
+#endif
+ cat = cat->next;
+ }
+ if ((option_verbose > 1) && !option_debug)
+ ast_verbose("Saved\n");
+ } else {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Unable to open for writing: %s\n", fn);
+ if (option_verbose > 1)
+ ast_verbose(VERBOSE_PREFIX_2 "Unable to write (%s)", strerror(errno));
+ return -1;
+ }
+ fclose(f);
+ return 0;
+}
+
+/* ================ the Line ========================================
+ above this line, you have what you need to load a config file,
+ and below it, you have what you need to process the extensions.conf
+ file into the context/exten/prio stuff. They are both in one file
+ to make things simpler */
+
static struct ast_context *local_contexts = NULL;
static struct ast_context *contexts = NULL;
-
-static struct varshead globals = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
+struct ast_context;
+struct ast_app;
+#ifdef LOW_MEMORY
+#define EXT_DATA_SIZE 256
+#else
+#define EXT_DATA_SIZE 8192
+#endif
+/*!
+ * When looking up extensions, we can have different requests
+ * identified by the 'action' argument, as follows.
+ * Note that the coding is such that the low 4 bits are the
+ * third argument to extension_match_core.
+ */
+enum ext_match_t {
+ E_MATCHMORE = 0x00, /* extension can match but only with more 'digits' */
+ E_CANMATCH = 0x01, /* extension can match with or without more 'digits' */
+ E_MATCH = 0x02, /* extension is an exact match */
+ E_MATCH_MASK = 0x03, /* mask for the argument to extension_match_core() */
+ E_SPAWN = 0x12, /* want to spawn an extension. Requires exact match */
+ E_FINDLABEL = 0x22 /* returns the priority for a given label. Requires exact match */
+};
+
+#ifdef NOT_ANYMORE
+static AST_RWLIST_HEAD_STATIC(switches, ast_switch);
+#endif
+
+#define SWITCH_DATA_LENGTH 256
+
+static const char *ast_get_extension_app(struct ast_exten *e)
+{
+ return e ? e->app : NULL;
+}
+
+static const char *ast_get_extension_name(struct ast_exten *exten)
+{
+ return exten ? exten->exten : NULL;
+}
static AST_RWLIST_HEAD_STATIC(hints, ast_hint);
+
+/*! \brief ast_change_hint: Change hint for an extension */
+static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
+{
+ struct ast_hint *hint;
+ int res = -1;
+
+ AST_RWLIST_TRAVERSE(&hints, hint, list) {
+ if (hint->exten == oe) {
+ hint->exten = ne;
+ res = 0;
+ break;
+ }
+ }
+
+ return res;
+}
+
+/*! \brief ast_add_hint: Add hint to hint list, check initial extension state */
+static int ast_add_hint(struct ast_exten *e)
+{
+ struct ast_hint *hint;
+
+ if (!e)
+ return -1;
+
+
+ /* Search if hint exists, do nothing */
+ AST_RWLIST_TRAVERSE(&hints, hint, list) {
+ if (hint->exten == e) {
+ if (option_debug > 1)
+ ast_log(LOG_DEBUG, "HINTS: Not re-adding existing hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e));
+ return -1;
+ }
+ }
+
+ if (option_debug > 1)
+ ast_log(LOG_DEBUG, "HINTS: Adding hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e));
+
+ if (!(hint = ast_calloc(1, sizeof(*hint)))) {
+ return -1;
+ }
+ /* Initialize and insert new item at the top */
+ hint->exten = e;
+ AST_RWLIST_INSERT_HEAD(&hints, hint, list);
+
+ return 0;
+}
+
+/*! \brief add the extension in the priority chain.
+ * returns 0 on success, -1 on failure
+ */
+static int add_pri(struct ast_context *con, struct ast_exten *tmp,
+ struct ast_exten *el, struct ast_exten *e, int replace)
+{
+ struct ast_exten *ep;
+
+ for (ep = NULL; e ; ep = e, e = e->peer) {
+ if (e->priority >= tmp->priority)
+ break;
+ }
+ if (!e) { /* go at the end, and ep is surely set because the list is not empty */
+ ep->peer = tmp;
+ return 0; /* success */
+ }
+ if (e->priority == tmp->priority) {
+ /* Can't have something exactly the same. Is this a
+ replacement? If so, replace, otherwise, bonk. */
+ if (!replace) {
+ ast_log(LOG_WARNING, "Unable to register extension '%s', priority %d in '%s', already in use\n", tmp->exten, tmp->priority, con->name);
+ tmp->datad(tmp->data);
+ free(tmp);
+ return -1;
+ }
+ /* we are replacing e, so copy the link fields and then update
+ * whoever pointed to e to point to us
+ */
+ tmp->next = e->next; /* not meaningful if we are not first in the peer list */
+ tmp->peer = e->peer; /* always meaningful */
+ if (ep) /* We're in the peer list, just insert ourselves */
+ ep->peer = tmp;
+ else if (el) /* We're the first extension. Take over e's functions */
+ el->next = tmp;
+ else /* We're the very first extension. */
+ con->root = tmp;
+ if (tmp->priority == PRIORITY_HINT)
+ ast_change_hint(e,tmp);
+ /* Destroy the old one */
+ e->datad(e->data);
+ free(e);
+ } else { /* Slip ourselves in just before e */
+ tmp->peer = e;
+ tmp->next = e->next; /* extension chain, or NULL if e is not the first extension */
+ if (ep) /* Easy enough, we're just in the peer list */
+ ep->peer = tmp;
+ else { /* we are the first in some peer list, so link in the ext list */
+ if (el)
+ el->next = tmp; /* in the middle... */
+ else
+ con->root = tmp; /* ... or at the head */
+ e->next = NULL; /* e is no more at the head, so e->next must be reset */
+ }
+ /* And immediately return success. */
+ if (tmp->priority == PRIORITY_HINT)
+ ast_add_hint(tmp);
+ }
+ return 0;
+}
+
+/*! \brief ast_remove_hint: Remove hint from extension */
+static int ast_remove_hint(struct ast_exten *e)
+{
+ /* Cleanup the Notifys if hint is removed */
+ struct ast_hint *hint;
+ struct ast_state_cb *cblist, *cbprev;
+ int res = -1;
+
+ if (!e)
+ return -1;
+
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&hints, hint, list) {
+ if (hint->exten == e) {
+ cbprev = NULL;
+ cblist = hint->callbacks;
+ while (cblist) {
+ /* Notify with -1 and remove all callbacks */
+ cbprev = cblist;
+ cblist = cblist->next;
+ free(cbprev);
+ }
+ hint->callbacks = NULL;
+ AST_RWLIST_REMOVE_CURRENT(&hints, list);
+ free(hint);
+ res = 0;
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END
+
+ return res;
+}
+
+static void destroy_exten(struct ast_exten *e)
+{
+ if (e->priority == PRIORITY_HINT)
+ ast_remove_hint(e);
+
+ if (e->datad)
+ e->datad(e->data);
+ free(e);
+}
+
+char *days[] =
+{
+ "sun",
+ "mon",
+ "tue",
+ "wed",
+ "thu",
+ "fri",
+ "sat",
+ NULL,
+};
+
+char *months[] =
+{
+ "jan",
+ "feb",
+ "mar",
+ "apr",
+ "may",
+ "jun",
+ "jul",
+ "aug",
+ "sep",
+ "oct",
+ "nov",
+ "dec",
+ NULL,
+};
+
+static int ast_build_timing(struct ast_timing *i, const char *info_in)
+{
+ char info_save[256];
+ char *info;
+
+ /* Check for empty just in case */
+ if (ast_strlen_zero(info_in))
+ return 0;
+ /* make a copy just in case we were passed a static string */
+ ast_copy_string(info_save, info_in, sizeof(info_save));
+ info = info_save;
+ /* Assume everything except time */
+ i->monthmask = 0xfff; /* 12 bits */
+ i->daymask = 0x7fffffffU; /* 31 bits */
+ i->dowmask = 0x7f; /* 7 bits */
+ /* on each call, use strsep() to move info to the next argument */
+ get_timerange(i, strsep(&info, "|"));
+ if (info)
+ i->dowmask = get_range(strsep(&info, "|"), 7, days, "day of week");
+ if (info)
+ i->daymask = get_range(strsep(&info, "|"), 31, NULL, "day");
+ if (info)
+ i->monthmask = get_range(strsep(&info, "|"), 12, months, "month");
+ return 1;
+}
+
+/*!
+ * \brief helper functions to sort extensions and patterns in the desired way,
+ * so that more specific patterns appear first.
+ *
+ * ext_cmp1 compares individual characters (or sets of), returning
+ * an int where bits 0-7 are the ASCII code of the first char in the set,
+ * while bit 8-15 are the cardinality of the set minus 1.
+ * This way more specific patterns (smaller cardinality) appear first.
+ * Wildcards have a special value, so that we can directly compare them to
+ * sets by subtracting the two values. In particular:
+ * 0x000xx one character, xx
+ * 0x0yyxx yy character set starting with xx
+ * 0x10000 '.' (one or more of anything)
+ * 0x20000 '!' (zero or more of anything)
+ * 0x30000 NUL (end of string)
+ * 0x40000 error in set.
+ * The pointer to the string is advanced according to needs.
+ * NOTES:
+ * 1. the empty set is equivalent to NUL.
+ * 2. given that a full set has always 0 as the first element,
+ * we could encode the special cases as 0xffXX where XX
+ * is 1, 2, 3, 4 as used above.
+ */
+static int ext_cmp1(const char **p)
+{
+ uint32_t chars[8];
+ int c, cmin = 0xff, count = 0;
+ const char *end;
+
+ /* load, sign extend and advance pointer until we find
+ * a valid character.
+ */
+ while ( (c = *(*p)++) && (c == ' ' || c == '-') )
+ ; /* ignore some characters */
+
+ /* always return unless we have a set of chars */
+ switch (c) {
+ default: /* ordinary character */
[... 1993 lines stripped ...]
More information about the asterisk-commits
mailing list