[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