summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTilghman Lesher <tilghman@meg.abyt.es>2007-08-15 21:25:13 +0000
committerTilghman Lesher <tilghman@meg.abyt.es>2007-08-15 21:25:13 +0000
commit918348f03c7e127beadc02d7de8da16f833c120e (patch)
treeb013424f782e82f91e117a22cdd6451c4fc28113
parenteaeec33a5c66aabe0a8cd48986ff51a9341c52ec (diff)
Missing from murf's last trunk commit, which was why trunk won't compile
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@79623 65c4cc65-6c06-0410-ace0-fbb531ad65f3
-rw-r--r--include/asterisk/extconf.h244
-rw-r--r--include/asterisk/pval.h272
-rw-r--r--utils/conf2ael.c685
-rw-r--r--utils/extconf.c5922
-rw-r--r--utils/pval.c5344
5 files changed, 12467 insertions, 0 deletions
diff --git a/include/asterisk/extconf.h b/include/asterisk/extconf.h
new file mode 100644
index 000000000..f0e919575
--- /dev/null
+++ b/include/asterisk/extconf.h
@@ -0,0 +1,244 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007, Digium, Inc.
+ *
+ * Steve Murphy <murf@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.
+ */
+
+#ifndef _ASTERISK_EXTCONF_H
+#define _ASTERISK_EXTCONF_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#ifdef NOTYET
+/* I'm going to define all the structs mentioned below, to avoid
+ possible conflicts in declarations that might be introduced,
+ if we just include the files that define them-- this may be
+ unnecessary */
+
+struct ast_comment {
+ struct ast_comment *next;
+ char cmt[0];
+};
+
+struct ast_variable {
+ char *name;
+ char *value;
+ int lineno;
+ int object; /*!< 0 for variable, 1 for object */
+ int blanklines; /*!< Number of blanklines following entry */
+ struct ast_comment *precomments;
+ struct ast_comment *sameline;
+ struct ast_variable *next;
+ char stuff[0];
+};
+
+struct ast_category {
+ char name[80];
+ int ignored; /*!< do not let user of the config see this category */
+ int include_level;
+ struct ast_comment *precomments;
+ struct ast_comment *sameline;
+ struct ast_variable *root;
+ struct ast_variable *last;
+ struct ast_category *next;
+};
+
+struct ast_config {
+ struct ast_category *root;
+ struct ast_category *last;
+ struct ast_category *current;
+ struct ast_category *last_browse; /*!< used to cache the last category supplied via category_browse */
+ int include_level;
+ int max_include_level;
+};
+
+/* ================== above: the config world; below, the dialplan world */
+
+/*! \brief ast_app: A registered application */
+struct ast_app {
+ int (*execute)(struct ast_channel *chan, void *data);
+ const char *synopsis; /*!< Synopsis text for 'show applications' */
+ const char *description; /*!< Description (help text) for 'show application &lt;name&gt;' */
+ AST_RWLIST_ENTRY(ast_app) list; /*!< Next app in list */
+ void *module; /*!< Module this app belongs to */
+ char name[0]; /*!< Name of the application */
+};
+/*!
+ \brief ast_exten: An extension
+ The dialplan is saved as a linked list with each context
+ having it's own linked list of extensions - one item per
+ priority.
+*/
+struct ast_exten {
+ char *exten; /*!< Extension name */
+ int matchcid; /*!< Match caller id ? */
+ const char *cidmatch; /*!< Caller id to match for this extension */
+ int priority; /*!< Priority */
+ const char *label; /*!< Label */
+ struct ast_context *parent; /*!< The context this extension belongs to */
+ const char *app; /*!< Application to execute */
+ struct ast_app *cached_app; /*!< Cached location of application */
+ void *data; /*!< Data to use (arguments) */
+ void (*datad)(void *); /*!< Data destructor */
+ struct ast_exten *peer; /*!< Next higher priority with our extension */
+ const char *registrar; /*!< Registrar */
+ struct ast_exten *next; /*!< Extension with a greater ID */
+ char stuff[0];
+};
+/* from pbx.h */
+typedef int (*ast_state_cb_type)(char *context, char* id, enum ast_extension_states state, void *data);
+struct ast_timing {
+ int hastime; /*!< If time construct exists */
+ unsigned int monthmask; /*!< Mask for month */
+ unsigned int daymask; /*!< Mask for date */
+ unsigned int dowmask; /*!< Mask for day of week (mon-sun) */
+ unsigned int minmask[24]; /*!< Mask for minute */
+};
+/*! \brief ast_include: include= support in extensions.conf */
+struct ast_include {
+ const char *name;
+ const char *rname; /*!< Context to include */
+ const char *registrar; /*!< Registrar */
+ int hastime; /*!< If time construct exists */
+ struct ast_timing timing; /*!< time construct */
+ struct ast_include *next; /*!< Link them together */
+ char stuff[0];
+};
+
+/*! \brief ast_sw: Switch statement in extensions.conf */
+struct ast_sw {
+ char *name;
+ const char *registrar; /*!< Registrar */
+ char *data; /*!< Data load */
+ int eval;
+ AST_LIST_ENTRY(ast_sw) list;
+ char *tmpdata;
+ char stuff[0];
+};
+
+*! \brief ast_ignorepat: Ignore patterns in dial plan */
+struct ast_ignorepat {
+ const char *registrar;
+ struct ast_ignorepat *next;
+ const char pattern[0];
+};
+
+/*! \brief ast_context: An extension context */
+struct ast_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 */
+ struct ast_ignorepat *ignorepats; /*!< Patterns for which to continue playing dialtone */
+ const char *registrar; /*!< Registrar */
+ AST_LIST_HEAD_NOLOCK(, ast_sw) alts; /*!< Alternative switches */
+ ast_mutex_t macrolock; /*!< A lock to implement "exclusive" macros - held whilst a call is executing in the macro */
+ char name[0]; /*!< Name of the context */
+};
+
+#endif
+
+struct ast_config *localized_config_load(const char *filename);
+struct ast_config *localized_config_load_with_comments(const char *filename);
+struct ast_category *localized_category_get(const struct ast_config *config, const char *category_name);
+int localized_config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator);
+struct ast_context *localized_walk_contexts(struct ast_context *con);
+struct ast_exten *localized_walk_context_extensions(struct ast_context *con,
+ struct ast_exten *exten);
+struct ast_exten *localized_walk_extension_priorities(struct ast_exten *exten,
+ struct ast_exten *priority);
+struct ast_include *localized_walk_context_includes(struct ast_context *con,
+ struct ast_include *inc);
+struct ast_sw *localized_walk_context_switches(struct ast_context *con,
+ struct ast_sw *sw);
+
+void localized_context_destroy(struct ast_context *con, const char *registrar);
+int localized_pbx_load_module(void);
+
+struct ast_context *localized_context_create(struct ast_context **extcontexts, const char *name, const char *registrar);
+int localized_pbx_builtin_setvar(struct ast_channel *chan, void *data);
+int localized_context_add_ignorepat2(struct ast_context *con, const char *value, const char *registrar);
+int localized_context_add_switch2(struct ast_context *con, const char *value,
+ const char *data, int eval, const char *registrar);
+int localized_context_add_include2(struct ast_context *con, const char *value,
+ const char *registrar);
+int localized_add_extension2(struct ast_context *con,
+ int replace, const char *extension, int priority, const char *label, const char *callerid,
+ const char *application, void *data, void (*datad)(void *),
+ const char *registrar);
+void localized_merge_contexts_and_delete(struct ast_context **extcontexts, const char *registrar);
+int localized_context_verify_includes(struct ast_context *con);
+void localized_use_conf_dir(void);
+void localized_use_local_dir(void);
+
+
+#ifndef _ASTERISK_PBX_H
+/*!
+ * 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 */
+};
+#define AST_PBX_MAX_STACK 128
+
+/* request and result for pbx_find_extension */
+struct pbx_find_info {
+#if 0
+ const char *context;
+ const char *exten;
+ int priority;
+#endif
+
+ char *incstack[AST_PBX_MAX_STACK]; /* filled during the search */
+ int stacklen; /* modified during the search */
+ int status; /* set on return */
+ struct ast_switch *swo; /* set on return */
+ const char *data; /* set on return */
+ const char *foundcontext; /* set on return */
+};
+
+#define STATUS_NO_CONTEXT 1
+#define STATUS_NO_EXTENSION 2
+#define STATUS_NO_PRIORITY 3
+#define STATUS_NO_LABEL 4
+#define STATUS_SUCCESS 5
+
+#endif
+
+struct ast_exten *localized_find_extension(struct ast_context *bypass,
+ struct pbx_find_info *q,
+ const char *context,
+ const char *exten,
+ int priority,
+ const char *label,
+ const char *callerid,
+ enum ext_match_t action);
+
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_PBX_H */
diff --git a/include/asterisk/pval.h b/include/asterisk/pval.h
new file mode 100644
index 000000000..11d672db2
--- /dev/null
+++ b/include/asterisk/pval.h
@@ -0,0 +1,272 @@
+#ifndef _ASTERISK_PVAL_H
+#define _ASTERISK_PVAL_H
+
+
+typedef enum
+{
+ PV_WORD, /* an ident, string, name, label, etc. A user-supplied string. */ /* 0 */
+ PV_MACRO, /* 1 */
+ PV_CONTEXT, /* 2 */
+ PV_MACRO_CALL, /* 3 */
+ PV_APPLICATION_CALL, /* 4 */
+ PV_CASE, /* 5 */
+ PV_PATTERN, /* 6 */
+ PV_DEFAULT, /* 7 */
+ PV_CATCH, /* 8 */
+ PV_SWITCHES, /* 9 */
+ PV_ESWITCHES, /* 10 */
+ PV_INCLUDES, /* 11 */
+ PV_STATEMENTBLOCK, /* 12 */
+ PV_VARDEC, /* you know, var=val; */ /* 13 */
+ PV_GOTO, /* 14 */
+ PV_LABEL, /* 15 */
+ PV_FOR, /* 16 */
+ PV_WHILE, /* 17 */
+ PV_BREAK, /* 18 */
+ PV_RETURN, /* 19 */
+ PV_CONTINUE, /* 20 */
+ PV_IF, /* 21 */
+ PV_IFTIME, /* 22 */
+ PV_RANDOM, /* 23 */
+ PV_SWITCH, /* 24 */
+ PV_EXTENSION, /* 25 */
+ PV_IGNOREPAT, /* 26 */
+ PV_GLOBALS, /* 27 */
+ PV_LOCALVARDEC, /* 28 */
+} pvaltype;
+
+/* why this horrible mess? It's always been a tradeoff-- tons of structs,
+ each storing it's specific lists of goodies, or a 'simple' single struct,
+ with lots of fields, that catches all uses at once. Either you have a long
+ list of struct names and subnames, or you have a long list of field names,
+ and where/how they are used. I'm going with a single struct, using unions
+ to reduce storage. Some simple generalizations, and a long list of types,
+ and a book about what is used with what types.... Sorry!
+*/
+
+struct pval
+{
+ pvaltype type;
+ int startline;
+ int endline;
+ int startcol;
+ int endcol;
+ char *filename;
+
+ union
+ {
+ char *str; /* wow, used almost everywhere! */
+ struct pval *list; /* used in SWITCHES, ESWITCHES, INCLUDES, STATEMENTBLOCK, GOTO */
+ struct pval *statements;/* used in EXTENSION */
+ char *for_init; /* used in FOR */
+ } u1;
+ struct pval *u1_last; /* to build in-order lists -- looks like we only need one */
+
+ union
+ {
+ struct pval *arglist; /* used in macro_call, application_call, MACRO def, also attached to PWORD, the 4 timevals for includes */
+ struct pval *statements; /* used in case, default, catch, while's statement, CONTEXT elements, GLOBALS */
+ char *val; /* used in VARDEC */
+ char *for_test; /* used in FOR */
+ int label_in_case; /* a boolean for LABELs */
+ struct pval *goto_target; /* used in GOTO */
+ } u2;
+
+ union
+ {
+ char *for_inc; /* used in FOR */
+ struct pval *else_statements; /* used in IF */
+ struct pval *macro_statements; /* used in MACRO */
+ int abstract; /* used for context */
+ char *hints; /* used in EXTENSION */
+ int goto_target_in_case; /* used in GOTO */
+ struct ael_extension *compiled_label;
+ } u3;
+
+ union
+ {
+ struct pval *for_statements; /* used in PV_FOR */
+ int regexten; /* used in EXTENSION */
+ } u4;
+
+ struct pval *next; /* the pval at the end of this ptr will ALWAYS be of the same type as this one!
+ EXCEPT for objects of the different types, that are in the same list, like contexts & macros, etc */
+
+ struct pval *dad; /* the 'container' of this struct instance */
+ struct pval *prev; /* the opposite of the 'next' pointer */
+} ;
+
+
+typedef struct pval pval;
+
+#ifndef AAL_ARGCHECK
+/* for the time being, short circuit all the AAL related structures
+ without permanently removing the code; after/during the AAL
+ development, this code can be properly re-instated
+*/
+
+/* null definitions for structs passed down the infrastructure */
+struct argapp
+{
+ struct argapp *next;
+};
+
+#endif
+
+struct ast_context;
+
+#ifdef AAL_ARGCHECK
+int option_matches_j( struct argdesc *should, pval *is, struct argapp *app);
+int option_matches( struct argdesc *should, pval *is, struct argapp *app);
+int ael_is_funcname(char *name);
+#endif
+
+int do_pbx_load_module(void);
+int count_labels_in_current_context(char *label);
+int check_app_args(pval *appcall, pval *arglist, struct argapp *app);
+void check_pval(pval *item, struct argapp *apps, int in_globals);
+void check_pval_item(pval *item, struct argapp *apps, int in_globals);
+void check_switch_expr(pval *item, struct argapp *apps);
+void ast_expr_register_extra_error_info(char *errmsg);
+void ast_expr_clear_extra_error_info(void);
+int ast_expr(char *expr, char *buf, int length, struct ast_channel *chan);
+struct pval *find_macro(char *name);
+struct pval *find_context(char *name);
+struct pval *find_context(char *name);
+struct pval *find_macro(char *name);
+struct ael_priority *new_prio(void);
+struct ael_extension *new_exten(void);
+void linkprio(struct ael_extension *exten, struct ael_priority *prio);
+void destroy_extensions(struct ael_extension *exten);
+/* static void linkexten(struct ael_extension *exten, struct ael_extension *add);
+ static void gen_prios(struct ael_extension *exten, char *label, pval *statement, struct ael_extension *mother_exten, struct ast_context *context ); */
+void set_priorities(struct ael_extension *exten);
+void add_extensions(struct ael_extension *exten);
+void ast_compile_ael2(struct ast_context **local_contexts, struct pval *root);
+void destroy_pval(pval *item);
+void destroy_pval_item(pval *item);
+int is_float(char *arg );
+int is_int(char *arg );
+int is_empty(char *arg);
+
+/* PVAL PI */
+
+
+pval *pvalCreateNode( pvaltype type );
+pvaltype pvalObjectGetType( pval *p );
+
+void pvalWordSetString( pval *p, char *str);
+char *pvalWordGetString( pval *p );
+
+void pvalMacroSetName( pval *p, char *name);
+char *pvalMacroGetName( pval *p );
+void pvalMacroSetArglist( pval *p, pval *arglist );
+void pvalMacroAddArg( pval *p, pval *arg );
+pval *pvalMacroWalkArgs( pval *p, pval **arg );
+void pvalMacroAddStatement( pval *p, pval *statement );
+pval *pvalMacroWalkStatements( pval *p, pval **next_statement );
+
+void pvalContextSetName( pval *p, char *name);
+char *pvalContextGetName( pval *p );
+void pvalContextSetAbstract( pval *p );
+void pvalContextUnsetAbstract( pval *p );
+int pvalContextGetAbstract( pval *p );
+void pvalContextAddStatement( pval *p, pval *statement);
+pval *pvalContextWalkStatements( pval *p, pval **statements );
+
+void pvalMacroCallSetMacroName( pval *p, char *name );
+char* pvalMacroCallGetMacroName( pval *p );
+void pvalMacroCallSetArglist( pval *p, pval *arglist );
+void pvalMacroCallAddArg( pval *p, pval *arg );
+pval *pvalMacroCallWalkArgs( pval *p, pval **args );
+
+void pvalAppCallSetAppName( pval *p, char *name );
+char* pvalAppCallGetAppName( pval *p );
+void pvalAppCallSetArglist( pval *p, pval *arglist );
+void pvalAppCallAddArg( pval *p, pval *arg );
+pval *pvalAppCallWalkArgs( pval *p, pval **args );
+
+void pvalCasePatSetVal( pval *p, char *val );
+char* pvalCasePatGetVal( pval *p );
+void pvalCasePatDefAddStatement( pval *p, pval *statement );
+pval *pvalCasePatDefWalkStatements( pval *p, pval **statement );
+
+void pvalCatchSetExtName( pval *p, char *name );
+char* pvalCatchGetExtName( pval *p );
+void pvalCatchSetStatement( pval *p, pval *statement );
+pval *pvalCatchGetStatement( pval *p );
+
+void pvalSwitchesAddSwitch( pval *p, char *name );
+char* pvalSwitchesWalkNames( pval *p, pval **next_item );
+void pvalESwitchesAddSwitch( pval *p, char *name );
+char* pvalESwitchesWalkNames( pval *p, pval **next_item );
+
+void pvalIncludesAddInclude( pval *p, const char *include );
+
+void pvalIncludesAddIncludeWithTimeConstraints( pval *p, const char *include, char *hour_range, char *dom_range, char *dow_range, char *month_range );
+void pvalIncludeGetTimeConstraints( pval *p, char **hour_range, char **dom_range, char **dow_range, char **month_range );
+char* pvalIncludesWalk( pval *p, pval **next_item );
+
+void pvalStatementBlockAddStatement( pval *p, pval *statement);
+pval *pvalStatementBlockWalkStatements( pval *p, pval **next_statement);
+
+void pvalVarDecSetVarname( pval *p, char *name );
+void pvalVarDecSetValue( pval *p, char *value );
+char* pvalVarDecGetVarname( pval *p );
+char* pvalVarDecGetValue( pval *p );
+
+void pvalGotoSetTarget( pval *p, char *context, char *exten, char *label );
+void pvalGotoGetTarget( pval *p, char **context, char **exten, char **label );
+
+void pvalLabelSetName( pval *p, char *name );
+char* pvalLabelGetName( pval *p );
+
+void pvalForSetInit( pval *p, char *init );
+void pvalForSetTest( pval *p, char *test );
+void pvalForSetInc( pval *p, char *inc );
+void pvalForSetStatement( pval *p, pval *statement );
+char* pvalForGetInit( pval *p );
+char* pvalForGetTest( pval *p );
+char* pvalForGetInc( pval *p );
+pval* pvalForGetStatement( pval *p );
+
+
+void pvalIfSetCondition( pval *p, char *expr );
+char* pvalIfGetCondition( pval *p );
+void pvalIfTimeSetCondition( pval *p, char *hour_range, char *dow_range, char *dom_range, char *mon_range ); /* time range format: 24-hour format begin-end|dow range|dom range|month range */
+void pvalIfTimeGetCondition( pval *p, char **hour_range, char **dow_range, char **dom_range, char **month_range );
+void pvalRandomSetCondition( pval *p, char *percent );
+char* pvalRandomGetCondition( pval *p );
+void pvalConditionalSetThenStatement( pval *p, pval *statement );
+void pvalConditionalSetElseStatement( pval *p, pval *statement );
+pval* pvalConditionalGetThenStatement( pval *p );
+pval* pvalConditionalGetElseStatement( pval *p );
+
+void pvalSwitchSetTestexpr( pval *p, char *expr );
+char* pvalSwitchGetTestexpr( pval *p );
+void pvalSwitchAddCase( pval *p, pval *Case );
+pval* pvalSwitchWalkCases( pval *p, pval **next_case );
+
+void pvalExtenSetName( pval *p, char *name );
+char *pvalExtenGetName( pval *p );
+void pvalExtenSetRegexten( pval *p );
+void pvalExtenUnSetRegexten( pval *p );
+int pvalExtenGetRegexten( pval *p );
+void pvalExtenSetHints( pval *p, char *hints );
+char* pvalExtenGetHints( pval *p );
+void pvalExtenSetStatement( pval *p, pval *statement );
+pval* pvalExtenGetStatement( pval *p );
+
+void pvalIgnorePatSetPattern( pval *p, char *pat );
+char* pvalIgnorePatGetPattern( pval *p );
+
+void pvalGlobalsAddStatement( pval *p, pval *statement );
+pval* pvalGlobalsWalkStatements( pval *p, pval **next_statement );
+
+void pvalTopLevAddObject( pval *p, pval *contextOrObj );
+pval* pvalTopLevWalkObjects( pval *p, pval **next_obj );
+
+int pvalCheckType( pval *p, char *funcname, pvaltype type );
+
+
+#endif
diff --git a/utils/conf2ael.c b/utils/conf2ael.c
new file mode 100644
index 000000000..7caf0698a
--- /dev/null
+++ b/utils/conf2ael.c
@@ -0,0 +1,685 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006, Digium, Inc.
+ *
+ * Steve Murphy <murf@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.
+ */
+
+/*
+ *
+ * Reverse compile extensions.conf code into prototype AEL code
+ *
+ */
+
+
+
+#include "asterisk/autoconfig.h"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <locale.h>
+#include <ctype.h>
+#if !defined(SOLARIS) && !defined(__CYGWIN__)
+#include <err.h>
+#endif
+#include <errno.h>
+#include <regex.h>
+#include <limits.h>
+
+#include "asterisk/compat.h"
+#include "asterisk/pbx.h"
+#include "asterisk/ast_expr.h"
+#include "asterisk/channel.h"
+#include "asterisk/chanvars.h"
+#include "asterisk/module.h"
+#include "asterisk/app.h"
+#include "asterisk/config.h"
+#include "asterisk/options.h"
+#include "asterisk/callerid.h"
+#include "asterisk/ael_structs.h"
+#include "asterisk/devicestate.h"
+#include "asterisk/stringfields.h"
+#include "asterisk/ael_structs.h"
+#include "asterisk/pval.h"
+#include "asterisk/extconf.h"
+
+void get_start_stop(unsigned int *word, int bitsperword, int totalbits, int *start, int *end);
+int all_bits_set(unsigned int *word, int bitsperword, int totalbits);
+void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
+{
+ va_list vars;
+ va_start(vars,fmt);
+
+ printf("LOG: lev:%d file:%s line:%d func: %s ",
+ level, file, line, function);
+ vprintf(fmt, vars);
+ fflush(stdout);
+ va_end(vars);
+}
+
+extern char *days[];
+extern char *months[];
+char ast_config_AST_CONFIG_DIR[PATH_MAX];
+
+char *config = "extensions.conf";
+
+/*
+static char *registrar = "conf2ael";
+static char userscontext[AST_MAX_EXTENSION] = "default";
+static int static_config = 0;
+static int write_protect_config = 1;
+static int autofallthrough_config = 0;
+static int clearglobalvars_config = 0;
+char ast_config_AST_SYSTEM_NAME[20] = ""; */
+
+/*! Go no deeper than this through includes (not counting loops) */
+#define AST_PBX_MAX_STACK 128
+/* static AST_RWLIST_HEAD_STATIC(acf_root, ast_custom_function); */
+extern char ast_config_AST_CONFIG_DIR[PATH_MAX];
+
+/* modulation */
+void ast_register_file_version(void);
+void ast_unregister_file_version(void);
+
+void ast_register_file_version(void)
+{
+ /* if(!no_comp)
+ printf("Executed ast_register_file_version();\n"); */
+ /* I'm erasing this, because I don't think anyone really ever needs to see it anyway */
+}
+
+void ast_unregister_file_version(void)
+{
+ /* if(!no_comp)
+ printf("Executed ast_unregister_file_version();\n"); */
+ /* I'm erasing this, because I don't think anyone really ever needs to see it anyway */
+
+}
+
+
+
+/* experiment 1: see if it's easier just to use existing config code
+ * to read in the extensions.conf file. In this scenario,
+ I have to rip/copy code from other modules, because they
+ are staticly declared as-is. A solution would be to move
+ the ripped code to another location and make them available
+ to other modules and standalones */
+
+/* Our own version of ast_log, since the expr parser uses it. -- stolen from utils/check_expr.c */
+void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...) __attribute__ ((format (printf,5,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
+
+#define SWITCH_DATA_LENGTH 256
+
+#define VAR_BUF_SIZE 4096
+
+#define VAR_NORMAL 1
+#define VAR_SOFTTRAN 2
+#define VAR_HARDTRAN 3
+
+#define BACKGROUND_SKIP (1 << 0)
+#define BACKGROUND_NOANSWER (1 << 1)
+#define BACKGROUND_MATCHEXTEN (1 << 2)
+#define BACKGROUND_PLAYBACK (1 << 3)
+
+/*!
+ \brief ast_exten: An extension
+ The dialplan is saved as a linked list with each context
+ having it's own linked list of extensions - one item per
+ priority.
+*/
+struct ast_exten {
+ char *exten; /*!< Extension name */
+ int matchcid; /*!< Match caller id ? */
+ const char *cidmatch; /*!< Caller id to match for this extension */
+ int priority; /*!< Priority */
+ const char *label; /*!< Label */
+ struct ast_context *parent; /*!< The context this extension belongs to */
+ const char *app; /*!< Application to execute */
+ struct ast_app *cached_app; /*!< Cached location of application */
+ void *data; /*!< Data to use (arguments) */
+ void (*datad)(void *); /*!< Data destructor */
+ struct ast_exten *peer; /*!< Next higher priority with our extension */
+ const char *registrar; /*!< Registrar */
+ struct ast_exten *next; /*!< Extension with a greater ID */
+ char stuff[0];
+};
+
+
+/*! \brief ast_include: include= support in extensions.conf */
+struct ast_include {
+ const char *name;
+ const char *rname; /*!< Context to include */
+ const char *registrar; /*!< Registrar */
+ int hastime; /*!< If time construct exists */
+ struct ast_timing timing; /*!< time construct */
+ struct ast_include *next; /*!< Link them together */
+ char stuff[0];
+};
+
+/*! \brief ast_sw: Switch statement in extensions.conf */
+struct ast_sw {
+ char *name;
+ const char *registrar; /*!< Registrar */
+ char *data; /*!< Data load */
+ int eval;
+ AST_LIST_ENTRY(ast_sw) list;
+ char *tmpdata;
+ char stuff[0];
+};
+
+/*! \brief ast_ignorepat: Ignore patterns in dial plan */
+struct ast_ignorepat {
+ const char *registrar;
+ struct ast_ignorepat *next;
+ const char pattern[0];
+};
+
+/*! \brief ast_context: An extension context */
+struct ast_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 */
+ struct ast_ignorepat *ignorepats; /*!< Patterns for which to continue playing dialtone */
+ const char *registrar; /*!< Registrar */
+ AST_LIST_HEAD_NOLOCK(, ast_sw) alts; /*!< Alternative switches */
+ ast_mutex_t macrolock; /*!< A lock to implement "exclusive" macros - held whilst a call is executing in the macro */
+ char name[0]; /*!< Name of the context */
+};
+
+
+/*! \brief ast_app: A registered application */
+struct ast_app {
+ int (*execute)(struct ast_channel *chan, void *data);
+ const char *synopsis; /*!< Synopsis text for 'show applications' */
+ const char *description; /*!< Description (help text) for 'show application &lt;name&gt;' */
+ AST_RWLIST_ENTRY(ast_app) list; /*!< Next app in list */
+ struct module *module; /*!< Module this app belongs to */
+ char name[0]; /*!< Name of the application */
+};
+
+/*! \brief ast_state_cb: An extension state notify register item */
+struct ast_state_cb {
+ int id;
+ void *data;
+ ast_state_cb_type callback;
+ struct ast_state_cb *next;
+};
+
+/*! \brief Structure for dial plan hints
+
+ \note Hints are pointers from an extension in the dialplan to one or
+ more devices (tech/name)
+ - See \ref AstExtState
+*/
+struct ast_hint {
+ struct ast_exten *exten; /*!< Extension */
+ int laststate; /*!< Last known state */
+ struct ast_state_cb *callbacks; /*!< Callback list for this extension */
+ AST_RWLIST_ENTRY(ast_hint) list;/*!< Pointer to next hint in list */
+};
+
+struct store_hint {
+ char *context;
+ char *exten;
+ struct ast_state_cb *callbacks;
+ int laststate;
+ AST_LIST_ENTRY(store_hint) list;
+ char data[1];
+};
+
+AST_LIST_HEAD(store_hints, store_hint);
+
+static const struct cfextension_states {
+ int extension_state;
+ const char * const text;
+} extension_states[] = {
+ { AST_EXTENSION_NOT_INUSE, "Idle" },
+ { AST_EXTENSION_INUSE, "InUse" },
+ { AST_EXTENSION_BUSY, "Busy" },
+ { AST_EXTENSION_UNAVAILABLE, "Unavailable" },
+ { AST_EXTENSION_RINGING, "Ringing" },
+ { AST_EXTENSION_INUSE | AST_EXTENSION_RINGING, "InUse&Ringing" },
+ { AST_EXTENSION_ONHOLD, "Hold" },
+ { AST_EXTENSION_INUSE | AST_EXTENSION_ONHOLD, "InUse&Hold" }
+};
+#define STATUS_NO_CONTEXT 1
+#define STATUS_NO_EXTENSION 2
+#define STATUS_NO_PRIORITY 3
+#define STATUS_NO_LABEL 4
+#define STATUS_SUCCESS 5
+
+extern struct ast_context *local_contexts;
+extern struct ast_context *contexts;
+
+
+struct ast_custom_function *ast_custom_function_find(const char *name);
+
+
+struct ast_custom_function *ast_custom_function_find(const char *name)
+{
+ return 0; /* in "standalone" mode, functions are just not avail */
+}
+
+
+struct profile_entry {
+ const char *name;
+ uint64_t scale; /* if non-zero, values are scaled by this */
+ int64_t mark;
+ int64_t value;
+ int64_t events;
+};
+
+struct profile_data {
+ int entries;
+ int max_size;
+ struct profile_entry e[0];
+};
+
+static int bit_at(unsigned int *word, int bitsperword, int bitnum)
+{
+ return word[bitnum/bitsperword] & (1 << (bitnum % bitsperword));
+}
+
+void get_start_stop(unsigned int *word, int bitsperword, int totalbits, int *start, int *end)
+{
+ int i;
+ int thisbit, thatbit = bit_at(word, bitsperword, totalbits-1);
+
+ for (i=0; i<totalbits; i++) {
+ thisbit = bit_at(word, bitsperword, i);
+
+ if (thisbit != thatbit ) {
+ if (thisbit) {
+ *start = i;
+ } else {
+ *end = i;
+ }
+ }
+ thatbit = thisbit;
+ }
+}
+
+int all_bits_set(unsigned int *word, int bitsperword, int totalbits )
+{
+
+ int i, total=totalbits/bitsperword,bitmask = 0;
+
+ for (i=0; i<bitsperword; i++)
+ {
+ bitmask |= (1 << i);
+ }
+
+ for (i=0; i<total; i++)
+ {
+ if (word[i] != bitmask)
+ return 0;
+ }
+ return 1;
+}
+
+
+int main(int argc, char **argv)
+{
+ struct ast_context *tmp;
+ struct ast_exten *e, *eroot;
+ pval *tree, *tmptree, *sws;
+ struct ast_include *tmpi;
+ struct ast_sw *sw = 0;
+ struct ast_ignorepat *ipi;
+ pval *incl=0;
+
+ tree = 0;
+ tmptree = 0;
+
+ /* 3 simple steps: */
+ /* 1. read in the extensions.conf config file
+ * 2. traverse, and build an AEL tree
+ * 3. Output the AEL tree into a file
+ */
+ printf("WARNING: This is an EXTREMELY preliminary version of a program\n");
+ printf(" that will someday hopefully do a thoughful and intelligent\n");
+ printf(" job of transforming your extensions.conf file into an\n");
+ printf(" extensions.ael file.\n");
+ printf(" This version has absolutely no intelligence, and pretty\n");
+ printf(" much just does a direct conversion\n");
+ printf(" The result will most likely need careful attention to\n");
+ printf(" finish the job!!!!!\n");
+
+
+ strcpy(ast_config_AST_CONFIG_DIR,"/etc/asterisk");
+
+ printf("Loading %s/%s...\n", ast_config_AST_CONFIG_DIR, config);
+
+ localized_pbx_load_module();
+
+ printf("... Done!\n");
+
+ tmp = 0;
+ while ((tmp = localized_walk_contexts(tmp)) ) {
+ printf("Context: %s\n", tmp->name);
+ }
+ printf("=========\n");
+ printf("Sizeof(context)=%d\n", sizeof(struct ast_context));
+ printf("Sizeof(exten)=%d\n", sizeof(struct ast_exten));
+ printf("Sizeof(include)=%d\n", sizeof(struct ast_include));
+ printf("Sizeof(ignorepat)=%d\n", sizeof(struct ast_ignorepat));
+ printf("Sizeof(sw)=%d\n", sizeof(struct ast_sw));
+ tmp = 0;
+ while ((tmp = localized_walk_contexts(tmp)) ) {
+ /* printf("Context: %s\n", tmp->name); */
+ tmptree = pvalCreateNode(PV_CONTEXT);
+ if (!tree)
+ tree = tmptree;
+ else
+ pvalTopLevAddObject(tree, tmptree);
+
+ pvalContextSetName(tmptree, ast_strdup(tmp->name));
+
+ if (tmp->includes) {
+ incl = pvalCreateNode(PV_INCLUDES);
+ pvalContextAddStatement(tmptree, incl);
+ for (tmpi = tmp->includes; tmpi; ) { /* includes */
+ if (strchr(tmpi->name,'|')==0) {
+ if (tmpi->hastime)
+ {
+ char timerange[15];
+ char dowrange[10];
+ char domrange[10];
+ char monrange[10];
+ int startbit=0, endbit=0;
+
+ if (all_bits_set(tmpi->timing.minmask, 30, 720))
+ strcpy(timerange, "*");
+ else {
+ int hr, min;
+ char tbuf[20];
+ get_start_stop(tmpi->timing.minmask, 30, 720, &startbit, &endbit);
+ hr = startbit/30;
+ min = (startbit % 30) * 2;
+ sprintf(tbuf,"%02d:%02d", hr, min);
+ strcpy(timerange, tbuf);
+ hr = endbit/30;
+ min = (endbit % 30) * 2;
+ sprintf(tbuf,"%02d:%02d", hr, min);
+ strcat(timerange,"-");
+ strcat(timerange,tbuf);
+ }
+
+ if (all_bits_set(&tmpi->timing.dowmask, 7, 7))
+ strcpy(dowrange, "*");
+ else {
+ get_start_stop(&tmpi->timing.dowmask, 7, 7, &startbit, &endbit);
+ strcpy(dowrange, days[startbit]);
+ strcat(dowrange,"-");
+ strcat(dowrange, days[endbit]);
+ }
+
+ if (all_bits_set(&tmpi->timing.monthmask, 12, 12))
+ strcpy(monrange, "*");
+ else {
+ get_start_stop(&tmpi->timing.monthmask, 12, 12, &startbit, &endbit);
+ strcpy(monrange, months[startbit]);
+ strcat(monrange,"-");
+ strcat(monrange, months[endbit]);
+ }
+
+ if (all_bits_set(&tmpi->timing.daymask, 31, 31))
+ strcpy(domrange, "*");
+ else {
+ char tbuf[20];
+ get_start_stop(&tmpi->timing.daymask, 31, 31, &startbit, &endbit);
+ sprintf(tbuf,"%d", startbit);
+ strcpy(domrange, tbuf);
+ strcat(domrange,"-");
+ sprintf(tbuf,"%d", endbit);
+ strcat(domrange, tbuf);
+ }
+ /* now all 4 fields are set; what do we do? */
+ pvalIncludesAddIncludeWithTimeConstraints(incl, strdup(tmpi->name), strdup(timerange), strdup(domrange), strdup(dowrange), strdup(monrange));
+
+ } else {
+ pvalIncludesAddInclude(incl, strdup(tmpi->name));
+ }
+ } else { /* it appears the timing constraint info is tacked onto the name, carve it up and divvy it out */
+ char *dow,*dom,*mon;
+ char *all = strdup(tmpi->name);
+ char *hr = strchr(all,'|');
+ if (hr) {
+ *hr++ = 0;
+ dow = strchr(hr,'|');
+ if (dow) {
+ *dow++ = 0;
+ dom = strchr(dow,'|');
+ if (dom) {
+ *dom++ = 0;
+ mon = strchr(dom,'|');
+ if (mon) {
+ *mon++ = 0;
+ /* now all 4 fields are set; what do we do? */
+ pvalIncludesAddIncludeWithTimeConstraints(incl, all, hr, dow, dom, mon);
+ /* the original data is always best to keep (no 2-min rounding) */
+ } else {
+ ast_log(LOG_ERROR,"No month spec attached to include!\n");
+ }
+ } else {
+ ast_log(LOG_ERROR,"No day of month spec attached to include!\n");
+ }
+ } else {
+ ast_log(LOG_ERROR,"No day of week spec attached to include!\n");
+ }
+ }
+ }
+ tmpi = tmpi->next;
+ }
+ }
+ for (ipi = tmp->ignorepats; ipi; ) { /* ignorepats */
+ incl = pvalCreateNode(PV_IGNOREPAT);
+ pvalIgnorePatSetPattern(incl,(char *)ipi->pattern);
+ pvalContextAddStatement(tmptree, incl);
+ ipi = ipi->next;
+ }
+ eroot=0;
+ while ( (eroot = localized_walk_context_extensions(tmp, eroot)) ) {
+ pval *exten = pvalCreateNode(PV_EXTENSION);
+ pvalContextAddStatement(tmptree, exten);
+ pvalExtenSetName(exten, ast_strdup(eroot->exten));
+
+ if (eroot->peer) {
+ pval *block = pvalCreateNode(PV_STATEMENTBLOCK);
+ pvalExtenSetStatement(exten, block);
+
+ e = 0;
+ while ( (e = localized_walk_extension_priorities(eroot, e)) ) {
+ /* printf(" %s(%s)\n", e->app, (char*)e->data); */
+
+ pval *statemnt = pvalCreateNode(PV_APPLICATION_CALL);
+ pval *args = pvalCreateNode(PV_WORD);
+
+ pvalAppCallSetAppName(statemnt, ast_strdup(e->app));
+ pvalWordSetString(args, ast_strdup(e->data));
+ pvalAppCallAddArg(statemnt, args);
+
+ pvalStatementBlockAddStatement(block, statemnt);
+ }
+ } else {
+ pval *statemnt = pvalCreateNode(PV_APPLICATION_CALL);
+ pval *args = pvalCreateNode(PV_WORD);
+
+ /* printf(" %s (%s)\n", eroot->app, (char *)eroot->data); */
+
+ pvalAppCallSetAppName(statemnt, ast_strdup(eroot->app));
+ pvalWordSetString(args, ast_strdup(eroot->data));
+
+
+ pvalAppCallAddArg(statemnt, args);
+ pvalExtenSetStatement(exten, statemnt);
+ }
+
+ /* printf(" extension: %s\n", eroot->exten); */
+ }
+ if (AST_LIST_FIRST(&tmp->alts)) {
+ sws = pvalCreateNode(PV_SWITCHES);
+ pvalContextAddStatement(tmptree,sws);
+
+ sw = 0;
+ while ((sw = localized_walk_context_switches(tmp,sw)) ) {
+ pvalSwitchesAddSwitch(sws, ast_strdup(sw->name));
+ }
+ }
+ }
+ printf("Generating aelout.ael file...\n");
+
+ ael2_print("aelout.ael", tree);
+
+ printf("...Done!\n");
+ return 0;
+}
+
+
+/* ==================================== for linking internal stuff to external stuff */
+
+int pbx_builtin_setvar(struct ast_channel *chan, void *data)
+{
+ return localized_pbx_builtin_setvar(chan, data);
+}
+
+void pbx_substitute_variables_helper(struct ast_channel *c,const char *cp1,char *cp2,int count);
+void pbx_substitute_variables_helper(struct ast_channel *c,const char *cp1,char *cp2,int count)
+{
+ if (cp1 && *cp1)
+ strncpy(cp2,cp1,AST_MAX_EXTENSION); /* Right now, this routine is ONLY being called for
+ a possible var substitution on extension names,
+ so....! */
+ else
+ *cp2 = 0;
+}
+
+int ast_add_extension2(struct ast_context *con,
+ int replace, const char *extension, int priority, const char *label, const char *callerid,
+ const char *application, void *data, void (*datad)(void *),
+ const char *registrar)
+{
+ return localized_add_extension2(con, replace, extension, priority, label, callerid, application, data, datad, registrar);
+}
+
+int ast_context_add_ignorepat2(struct ast_context *con, const char *value, const char *registrar)
+{
+
+ return localized_context_add_ignorepat2(con, value, registrar);
+}
+
+int ast_context_add_switch2(struct ast_context *con, const char *value,
+ const char *data, int eval, const char *registrar)
+{
+
+ return localized_context_add_switch2(con, value, data, eval, registrar);
+}
+
+int ast_context_add_include2(struct ast_context *con, const char *value,
+ const char *registrar)
+{
+
+ return localized_context_add_include2(con, value,registrar);
+}
+
+struct ast_context *ast_context_create(struct ast_context **extcontexts, const char *name, const char *registrar)
+{
+ printf("Creating context %s, registrar=%s\n", name, registrar);
+
+ return localized_context_create(extcontexts, name, registrar);
+}
+
+void ast_cli_register_multiple(void);
+
+void ast_cli_register_multiple(void)
+{
+}
+
+void ast_module_register(const struct ast_module_info *x)
+{
+}
+
+void ast_module_unregister(const struct ast_module_info *x)
+{
+}
+
+void ast_cli_unregister_multiple(void);
+
+void ast_cli_unregister_multiple(void)
+{
+}
+
+struct ast_context *ast_walk_contexts(struct ast_context *con);
+struct ast_context *ast_walk_contexts(struct ast_context *con)
+{
+ return localized_walk_contexts(con);
+}
+
+void ast_context_destroy(struct ast_context *con, const char *registrar);
+
+void ast_context_destroy(struct ast_context *con, const char *registrar)
+{
+ return localized_context_destroy(con, registrar);
+}
+
+int ast_context_verify_includes(struct ast_context *con);
+
+int ast_context_verify_includes(struct ast_context *con)
+{
+ return localized_context_verify_includes(con);
+}
+
+void ast_merge_contexts_and_delete(struct ast_context **extcontexts, const char *registrar);
+
+void ast_merge_contexts_and_delete(struct ast_context **extcontexts, const char *registrar)
+{
+ localized_merge_contexts_and_delete(extcontexts, registrar);
+}
+
+struct ast_exten *pbx_find_extension(struct ast_channel *chan,
+ struct ast_context *bypass,
+ struct pbx_find_info *q,
+ const char *context,
+ const char *exten,
+ int priority,
+ const char *label,
+ const char *callerid,
+ enum ext_match_t action);
+
+struct ast_exten *pbx_find_extension(struct ast_channel *chan,
+ struct ast_context *bypass,
+ struct pbx_find_info *q,
+ const char *context,
+ const char *exten,
+ int priority,
+ const char *label,
+ const char *callerid,
+ enum ext_match_t action)
+{
+ return localized_find_extension(bypass, q, context, exten, priority, label, callerid, action);
+}
+
diff --git a/utils/extconf.c b/utils/extconf.c
new file mode 100644
index 000000000..121e59576
--- /dev/null
+++ b/utils/extconf.c
@@ -0,0 +1,5922 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006, Digium, Inc.
+ *
+ * Steve Murphy <murf@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.
+ */
+
+
+/*
+ *
+ * A condensation of the pbx_config stuff, to read into exensions.conf, and provide an interface to the data there,
+ * for operations outside of asterisk. A huge, awful hack.
+ *
+ */
+
+#include "asterisk/autoconfig.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <stdarg.h>
+#include <string.h>
+#include <locale.h>
+#include <ctype.h>
+#if !defined(SOLARIS) && !defined(__CYGWIN__)
+#include <err.h>
+#endif
+#include <regex.h>
+#include <limits.h>
+#include <pthread.h>
+#include <netdb.h>
+#include <sys/param.h>
+#define ASINCLUDE_GLOB 1
+#ifdef AST_INCLUDE_GLOB
+#if defined(__Darwin__) || defined(__CYGWIN__)
+#define GLOB_ABORTED GLOB_ABEND
+#endif
+# include <glob.h>
+#endif
+
+static char ast_config_AST_CONFIG_DIR[PATH_MAX] = {"/etc/asterisk"};
+#define AST_API_MODULE 1 /* gimme the inline defs! */
+struct ast_channel
+{
+ char x; /* basically empty! */
+};
+
+
+
+#include "asterisk/inline_api.h"
+#include "asterisk/compat.h"
+#include "asterisk/compiler.h"
+#include "asterisk/endian.h"
+#include "asterisk/ast_expr.h"
+#include "asterisk/ael_structs.h"
+#include "asterisk/pval.h"
+
+/* logger.h */
+#define EVENTLOG "event_log"
+#define QUEUELOG "queue_log"
+
+#define DEBUG_M(a) { \
+ a; \
+}
+
+#define VERBOSE_PREFIX_1 " "
+#define VERBOSE_PREFIX_2 " == "
+#define VERBOSE_PREFIX_3 " -- "
+#define VERBOSE_PREFIX_4 " > "
+
+/* IN CONFLICT: void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
+ __attribute__ ((format (printf, 5, 6))); */
+
+static void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...) __attribute__ ((format (printf,5,6)));
+
+
+void ast_backtrace(void);
+
+void ast_queue_log(const char *queuename, const char *callid, const char *agent, const char *event, const char *fmt, ...)
+ __attribute__ ((format (printf, 5, 6)));
+
+/* IN CONFLICT: void ast_verbose(const char *fmt, ...)
+ __attribute__ ((format (printf, 1, 2))); */
+
+int ast_register_verbose(void (*verboser)(const char *string));
+int ast_unregister_verbose(void (*verboser)(const char *string));
+
+void ast_console_puts(const char *string);
+
+void ast_console_puts_mutable(const char *string);
+void ast_console_toggle_mute(int fd);
+
+#define _A_ __FILE__, __LINE__, __PRETTY_FUNCTION__
+
+#ifdef LOG_DEBUG
+#undef LOG_DEBUG
+#endif
+#define __LOG_DEBUG 0
+#define LOG_DEBUG __LOG_DEBUG, _A_
+
+#ifdef LOG_EVENT
+#undef LOG_EVENT
+#endif
+#define __LOG_EVENT 1
+#define LOG_EVENT __LOG_EVENT, _A_
+
+#ifdef LOG_NOTICE
+#undef LOG_NOTICE
+#endif
+#define __LOG_NOTICE 2
+#define LOG_NOTICE __LOG_NOTICE, _A_
+
+#ifdef LOG_WARNING
+#undef LOG_WARNING
+#endif
+#define __LOG_WARNING 3
+#define LOG_WARNING __LOG_WARNING, _A_
+
+#ifdef LOG_ERROR
+#undef LOG_ERROR
+#endif
+#define __LOG_ERROR 4
+#define LOG_ERROR __LOG_ERROR, _A_
+
+#ifdef LOG_VERBOSE
+#undef LOG_VERBOSE
+#endif
+#define __LOG_VERBOSE 5
+#define LOG_VERBOSE __LOG_VERBOSE, _A_
+
+#ifdef LOG_DTMF
+#undef LOG_DTMF
+#endif
+#define __LOG_DTMF 6
+#define LOG_DTMF __LOG_DTMF, _A_
+
+/* from utils.h */
+
+static unsigned int __unsigned_int_flags_dummy;
+
+struct ast_flags { /* stolen from utils.h */
+ unsigned int flags;
+};
+#define ast_test_flag(p,flag) ({ \
+ typeof ((p)->flags) __p = (p)->flags; \
+ typeof (__unsigned_int_flags_dummy) __x = 0; \
+ (void) (&__p == &__x); \
+ ((p)->flags & (flag)); \
+ })
+
+#define ast_set2_flag(p,value,flag) do { \
+ typeof ((p)->flags) __p = (p)->flags; \
+ typeof (__unsigned_int_flags_dummy) __x = 0; \
+ (void) (&__p == &__x); \
+ if (value) \
+ (p)->flags |= (flag); \
+ else \
+ (p)->flags &= ~(flag); \
+ } while (0)
+
+
+#ifdef __AST_DEBUG_MALLOC
+static void ast_free(void *ptr) attribute_unused;
+static void ast_free(void *ptr)
+{
+ free(ptr);
+}
+#else
+#define ast_free free
+#endif
+
+#ifndef __AST_DEBUG_MALLOC
+
+#define MALLOC_FAILURE_MSG \
+ ast_log(LOG_ERROR, "Memory Allocation Failure in function %s at line %d of %s\n", func, lineno, file);
+/*!
+ * \brief A wrapper for malloc()
+ *
+ * ast_malloc() is a wrapper for malloc() that will generate an Asterisk log
+ * message in the case that the allocation fails.
+ *
+ * The argument and return value are the same as malloc()
+ */
+#define ast_malloc(len) \
+ _ast_malloc((len), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+AST_INLINE_API(
+void * attribute_malloc _ast_malloc(size_t len, const char *file, int lineno, const char *func),
+{
+ void *p;
+
+ if (!(p = malloc(len)))
+ MALLOC_FAILURE_MSG;
+
+ return p;
+}
+)
+
+/*!
+ * \brief A wrapper for calloc()
+ *
+ * ast_calloc() is a wrapper for calloc() that will generate an Asterisk log
+ * message in the case that the allocation fails.
+ *
+ * The arguments and return value are the same as calloc()
+ */
+#define ast_calloc(num, len) \
+ _ast_calloc((num), (len), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+AST_INLINE_API(
+void * attribute_malloc _ast_calloc(size_t num, size_t len, const char *file, int lineno, const char *func),
+{
+ void *p;
+
+ if (!(p = calloc(num, len)))
+ MALLOC_FAILURE_MSG;
+
+ return p;
+}
+)
+
+/*!
+ * \brief A wrapper for calloc() for use in cache pools
+ *
+ * ast_calloc_cache() is a wrapper for calloc() that will generate an Asterisk log
+ * message in the case that the allocation fails. When memory debugging is in use,
+ * the memory allocated by this function will be marked as 'cache' so it can be
+ * distinguished from normal memory allocations.
+ *
+ * The arguments and return value are the same as calloc()
+ */
+#define ast_calloc_cache(num, len) \
+ _ast_calloc((num), (len), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+/*!
+ * \brief A wrapper for realloc()
+ *
+ * ast_realloc() is a wrapper for realloc() that will generate an Asterisk log
+ * message in the case that the allocation fails.
+ *
+ * The arguments and return value are the same as realloc()
+ */
+#define ast_realloc(p, len) \
+ _ast_realloc((p), (len), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+AST_INLINE_API(
+void * attribute_malloc _ast_realloc(void *p, size_t len, const char *file, int lineno, const char *func),
+{
+ void *newp;
+
+ if (!(newp = realloc(p, len)))
+ MALLOC_FAILURE_MSG;
+
+ return newp;
+}
+)
+
+/*!
+ * \brief A wrapper for strdup()
+ *
+ * ast_strdup() is a wrapper for strdup() that will generate an Asterisk log
+ * message in the case that the allocation fails.
+ *
+ * ast_strdup(), unlike strdup(), can safely accept a NULL argument. If a NULL
+ * argument is provided, ast_strdup will return NULL without generating any
+ * kind of error log message.
+ *
+ * The argument and return value are the same as strdup()
+ */
+#define ast_strdup(str) \
+ _ast_strdup((str), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+AST_INLINE_API(
+char * attribute_malloc _ast_strdup(const char *str, const char *file, int lineno, const char *func),
+{
+ char *newstr = NULL;
+
+ if (str) {
+ if (!(newstr = strdup(str)))
+ MALLOC_FAILURE_MSG;
+ }
+
+ return newstr;
+}
+)
+
+/*!
+ * \brief A wrapper for strndup()
+ *
+ * ast_strndup() is a wrapper for strndup() that will generate an Asterisk log
+ * message in the case that the allocation fails.
+ *
+ * ast_strndup(), unlike strndup(), can safely accept a NULL argument for the
+ * string to duplicate. If a NULL argument is provided, ast_strdup will return
+ * NULL without generating any kind of error log message.
+ *
+ * The arguments and return value are the same as strndup()
+ */
+#define ast_strndup(str, len) \
+ _ast_strndup((str), (len), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+AST_INLINE_API(
+char * attribute_malloc _ast_strndup(const char *str, size_t len, const char *file, int lineno, const char *func),
+{
+ char *newstr = NULL;
+
+ if (str) {
+ if (!(newstr = strndup(str, len)))
+ MALLOC_FAILURE_MSG;
+ }
+
+ return newstr;
+}
+)
+
+/*!
+ * \brief A wrapper for asprintf()
+ *
+ * ast_asprintf() is a wrapper for asprintf() that will generate an Asterisk log
+ * message in the case that the allocation fails.
+ *
+ * The arguments and return value are the same as asprintf()
+ */
+#define ast_asprintf(ret, fmt, ...) \
+ _ast_asprintf((ret), __FILE__, __LINE__, __PRETTY_FUNCTION__, fmt, __VA_ARGS__)
+
+AST_INLINE_API(
+int _ast_asprintf(char **ret, const char *file, int lineno, const char *func, const char *fmt, ...),
+{
+ int res;
+ va_list ap;
+
+ va_start(ap, fmt);
+ if ((res = vasprintf(ret, fmt, ap)) == -1)
+ MALLOC_FAILURE_MSG;
+ va_end(ap);
+
+ return res;
+}
+)
+
+/*!
+ * \brief A wrapper for vasprintf()
+ *
+ * ast_vasprintf() is a wrapper for vasprintf() that will generate an Asterisk log
+ * message in the case that the allocation fails.
+ *
+ * The arguments and return value are the same as vasprintf()
+ */
+#define ast_vasprintf(ret, fmt, ap) \
+ _ast_vasprintf((ret), __FILE__, __LINE__, __PRETTY_FUNCTION__, (fmt), (ap))
+
+AST_INLINE_API(
+int _ast_vasprintf(char **ret, const char *file, int lineno, const char *func, const char *fmt, va_list ap),
+{
+ int res;
+
+ if ((res = vasprintf(ret, fmt, ap)) == -1)
+ MALLOC_FAILURE_MSG;
+
+ return res;
+}
+)
+
+#else
+
+/* If astmm is in use, let it handle these. Otherwise, it will report that
+ all allocations are coming from this header file */
+
+#define ast_malloc(a) malloc(a)
+#define ast_calloc(a,b) calloc(a,b)
+#define ast_realloc(a,b) realloc(a,b)
+#define ast_strdup(a) strdup(a)
+#define ast_strndup(a,b) strndup(a,b)
+#define ast_asprintf(a,b,c) asprintf(a,b,c)
+#define ast_vasprintf(a,b,c) vasprintf(a,b,c)
+
+#endif /* AST_DEBUG_MALLOC */
+
+#if !defined(ast_strdupa) && defined(__GNUC__)
+/*!
+ \brief duplicate a string in memory from the stack
+ \param s The string to duplicate
+
+ This macro will duplicate the given string. It returns a pointer to the stack
+ allocatted memory for the new string.
+*/
+#define ast_strdupa(s) \
+ (__extension__ \
+ ({ \
+ const char *__old = (s); \
+ size_t __len = strlen(__old) + 1; \
+ char *__new = __builtin_alloca(__len); \
+ memcpy (__new, __old, __len); \
+ __new; \
+ }))
+#endif
+
+
+/* from config.c */
+
+#define MAX_NESTED_COMMENTS 128
+#define COMMENT_START ";--"
+#define COMMENT_END "--;"
+#define COMMENT_META ';'
+#define COMMENT_TAG '-'
+
+static char *extconfig_conf = "extconfig.conf";
+
+/*! Growable string buffer */
+static char *comment_buffer; /*!< this will be a comment collector.*/
+static int comment_buffer_size; /*!< the amount of storage so far alloc'd for the comment_buffer */
+
+static char *lline_buffer; /*!< A buffer for stuff behind the ; */
+static int lline_buffer_size;
+
+#define CB_INCR 250
+
+struct ast_comment {
+ struct ast_comment *next;
+ char cmt[0];
+};
+
+static void CB_INIT(void)
+{
+ if (!comment_buffer) {
+ comment_buffer = ast_malloc(CB_INCR);
+ if (!comment_buffer)
+ return;
+ comment_buffer[0] = 0;
+ comment_buffer_size = CB_INCR;
+ lline_buffer = ast_malloc(CB_INCR);
+ if (!lline_buffer)
+ return;
+ lline_buffer[0] = 0;
+ lline_buffer_size = CB_INCR;
+ } else {
+ comment_buffer[0] = 0;
+ lline_buffer[0] = 0;
+ }
+}
+
+static void CB_ADD(char *str)
+{
+ int rem = comment_buffer_size - strlen(comment_buffer) - 1;
+ int siz = strlen(str);
+ if (rem < siz+1) {
+ comment_buffer = ast_realloc(comment_buffer, comment_buffer_size + CB_INCR + siz + 1);
+ if (!comment_buffer)
+ return;
+ comment_buffer_size += CB_INCR+siz+1;
+ }
+ strcat(comment_buffer,str);
+}
+
+static void CB_ADD_LEN(char *str, int len)
+{
+ int cbl = strlen(comment_buffer) + 1;
+ int rem = comment_buffer_size - cbl;
+ if (rem < len+1) {
+ comment_buffer = ast_realloc(comment_buffer, comment_buffer_size + CB_INCR + len + 1);
+ if (!comment_buffer)
+ return;
+ comment_buffer_size += CB_INCR+len+1;
+ }
+ strncat(comment_buffer,str,len);
+ comment_buffer[cbl+len-1] = 0;
+}
+
+static void LLB_ADD(char *str)
+{
+ int rem = lline_buffer_size - strlen(lline_buffer) - 1;
+ int siz = strlen(str);
+ if (rem < siz+1) {
+ lline_buffer = ast_realloc(lline_buffer, lline_buffer_size + CB_INCR + siz + 1);
+ if (!lline_buffer)
+ return;
+ lline_buffer_size += CB_INCR + siz + 1;
+ }
+ strcat(lline_buffer,str);
+}
+
+static void CB_RESET(void )
+{
+ comment_buffer[0] = 0;
+ lline_buffer[0] = 0;
+}
+
+/*! \brief Keep track of how many threads are currently trying to wait*() on
+ * a child process */
+static unsigned int safe_system_level = 0;
+static void *safe_system_prev_handler;
+
+/*! \brief NULL handler so we can collect the child exit status */
+static void null_sig_handler(int signal)
+{
+
+}
+
+void ast_replace_sigchld(void);
+
+void ast_replace_sigchld(void)
+{
+ unsigned int level;
+
+ level = safe_system_level++;
+
+ /* only replace the handler if it has not already been done */
+ if (level == 0)
+ safe_system_prev_handler = signal(SIGCHLD, null_sig_handler);
+
+}
+
+void ast_unreplace_sigchld(void);
+
+void ast_unreplace_sigchld(void)
+{
+ unsigned int level;
+
+ level = --safe_system_level;
+
+ /* only restore the handler if we are the last one */
+ if (level == 0)
+ signal(SIGCHLD, safe_system_prev_handler);
+
+}
+
+int ast_safe_system(const char *s);
+
+int ast_safe_system(const char *s)
+{
+ pid_t pid;
+#ifdef HAVE_WORKING_FORK
+ int x;
+#endif
+ int res;
+ struct rusage rusage;
+ int status;
+
+#if defined(HAVE_WORKING_FORK) || defined(HAVE_WORKING_VFORK)
+ ast_replace_sigchld();
+
+#ifdef HAVE_WORKING_FORK
+ pid = fork();
+#else
+ pid = vfork();
+#endif
+
+ if (pid == 0) {
+#ifdef HAVE_WORKING_FORK
+ /* Close file descriptors and launch system command */
+ for (x = STDERR_FILENO + 1; x < 4096; x++)
+ close(x);
+#endif
+ execl("/bin/sh", "/bin/sh", "-c", s, (char *) NULL);
+ _exit(1);
+ } else if (pid > 0) {
+ for(;;) {
+ res = wait4(pid, &status, 0, &rusage);
+ if (res > -1) {
+ res = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
+ break;
+ } else if (errno != EINTR)
+ break;
+ }
+ } else {
+ ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
+ res = -1;
+ }
+
+ ast_unreplace_sigchld();
+#else
+ res = -1;
+#endif
+
+ return res;
+}
+
+static struct ast_comment *ALLOC_COMMENT(const char *buffer)
+{
+ struct ast_comment *x = ast_calloc(1,sizeof(struct ast_comment)+strlen(buffer)+1);
+ strcpy(x->cmt, buffer);
+ return x;
+}
+
+static struct ast_config_map {
+ struct ast_config_map *next;
+ char *name;
+ char *driver;
+ char *database;
+ char *table;
+ char stuff[0];
+} *config_maps = NULL;
+
+static struct ast_config_engine *config_engine_list;
+
+#define MAX_INCLUDE_LEVEL 10
+
+
+struct ast_category {
+ char name[80];
+ int ignored; /*!< do not let user of the config see this category */
+ int include_level;
+ struct ast_comment *precomments;
+ struct ast_comment *sameline;
+ struct ast_variable *root;
+ struct ast_variable *last;
+ struct ast_category *next;
+};
+
+struct ast_config {
+ struct ast_category *root;
+ struct ast_category *last;
+ struct ast_category *current;
+ struct ast_category *last_browse; /*!< used to cache the last category supplied via category_browse */
+ int include_level;
+ int max_include_level;
+};
+
+typedef struct ast_config *config_load_func(const char *database, const char *table, const char *configfile, struct ast_config *config, int withcomments);
+typedef struct ast_variable *realtime_var_get(const char *database, const char *table, va_list ap);
+typedef struct ast_config *realtime_multi_get(const char *database, const char *table, va_list ap);
+typedef int realtime_update(const char *database, const char *table, const char *keyfield, const char *entity, va_list ap);
+
+/*! \brief Configuration engine structure, used to define realtime drivers */
+struct ast_config_engine {
+ char *name;
+ config_load_func *load_func;
+ realtime_var_get *realtime_func;
+ realtime_multi_get *realtime_multi_func;
+ realtime_update *update_func;
+ struct ast_config_engine *next;
+};
+
+static struct ast_config_engine *config_engine_list;
+
+/* from config.h */
+
+struct ast_variable {
+ char *name;
+ char *value;
+ int lineno;
+ int object; /*!< 0 for variable, 1 for object */
+ int blanklines; /*!< Number of blanklines following entry */
+ struct ast_comment *precomments;
+ struct ast_comment *sameline;
+ struct ast_variable *next;
+ char stuff[0];
+};
+
+static const char *ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable);
+static struct ast_config *config_text_file_load(const char *database, const char *table, const char *filename, struct ast_config *cfg, int withcomments);
+
+struct ast_config *localized_config_load_with_comments(const char *filename);
+static char *ast_category_browse(struct ast_config *config, const char *prev);
+static struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category);
+static void ast_variables_destroy(struct ast_variable *v);
+static void ast_config_destroy(struct ast_config *cfg);
+
+static struct ast_variable *ast_variable_new(const char *name, const char *value);
+
+static struct ast_variable *ast_variable_new(const char *name, const char *value)
+{
+ struct ast_variable *variable;
+ int name_len = strlen(name) + 1;
+
+ if ((variable = ast_calloc(1, name_len + strlen(value) + 1 + sizeof(*variable)))) {
+ variable->name = variable->stuff;
+ variable->value = variable->stuff + name_len;
+ strcpy(variable->name,name);
+ strcpy(variable->value,value);
+ }
+
+ return variable;
+}
+
+static void ast_variable_append(struct ast_category *category, struct ast_variable *variable);
+
+static 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;
+}
+
+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)
+{
+ return category_get(config, category_name, 0);
+}
+
+static struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category)
+{
+ struct ast_category *cat = NULL;
+
+ if (category && config->last_browse && (config->last_browse->name == category))
+ cat = config->last_browse;
+ else
+ cat = ast_category_get(config, category);
+
+ return (cat) ? cat->root : NULL;
+}
+
+static const char *ast_variable_retrieve(const 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;
+}
+
+static struct ast_variable *variable_clone(const struct ast_variable *old)
+{
+ struct ast_variable *new = ast_variable_new(old->name, old->value);
+
+ if (new) {
+ new->lineno = old->lineno;
+ new->object = old->object;
+ new->blanklines = old->blanklines;
+ /* TODO: clone comments? */
+ }
+
+ return new;
+}
+
+static void ast_variables_destroy(struct ast_variable *v)
+{
+ struct ast_variable *vn;
+
+ while (v) {
+ vn = v;
+ v = v->next;
+ free(vn);
+ }
+}
+
+static void ast_config_destroy(struct ast_config *cfg)
+{
+ struct ast_category *cat, *catn;
+
+ if (!cfg)
+ return;
+
+ cat = cfg->root;
+ while (cat) {
+ ast_variables_destroy(cat->root);
+ catn = cat;
+ cat = cat->next;
+ free(catn);
+ }
+ free(cfg);
+}
+
+
+/* options.h declars ast_options extern; I need it static? */
+
+#define AST_CACHE_DIR_LEN 512
+#define AST_FILENAME_MAX 80
+
+/*! \ingroup main_options */
+enum ast_option_flags {
+ /*! Allow \#exec in config files */
+ AST_OPT_FLAG_EXEC_INCLUDES = (1 << 0),
+ /*! Do not fork() */
+ AST_OPT_FLAG_NO_FORK = (1 << 1),
+ /*! Keep quiet */
+ AST_OPT_FLAG_QUIET = (1 << 2),
+ /*! Console mode */
+ AST_OPT_FLAG_CONSOLE = (1 << 3),
+ /*! Run in realtime Linux priority */
+ AST_OPT_FLAG_HIGH_PRIORITY = (1 << 4),
+ /*! Initialize keys for RSA authentication */
+ AST_OPT_FLAG_INIT_KEYS = (1 << 5),
+ /*! Remote console */
+ AST_OPT_FLAG_REMOTE = (1 << 6),
+ /*! Execute an asterisk CLI command upon startup */
+ AST_OPT_FLAG_EXEC = (1 << 7),
+ /*! Don't use termcap colors */
+ AST_OPT_FLAG_NO_COLOR = (1 << 8),
+ /*! Are we fully started yet? */
+ AST_OPT_FLAG_FULLY_BOOTED = (1 << 9),
+ /*! Trascode via signed linear */
+ AST_OPT_FLAG_TRANSCODE_VIA_SLIN = (1 << 10),
+ /*! Enable priority jumping in applications */
+ AST_OPT_FLAG_PRIORITY_JUMPING = (1 << 11),
+ /*! Dump core on a seg fault */
+ AST_OPT_FLAG_DUMP_CORE = (1 << 12),
+ /*! Cache sound files */
+ AST_OPT_FLAG_CACHE_RECORD_FILES = (1 << 13),
+ /*! Display timestamp in CLI verbose output */
+ AST_OPT_FLAG_TIMESTAMP = (1 << 14),
+ /*! Override config */
+ AST_OPT_FLAG_OVERRIDE_CONFIG = (1 << 15),
+ /*! Reconnect */
+ AST_OPT_FLAG_RECONNECT = (1 << 16),
+ /*! Transmit Silence during Record() */
+ AST_OPT_FLAG_TRANSMIT_SILENCE = (1 << 17),
+ /*! Suppress some warnings */
+ AST_OPT_FLAG_DONT_WARN = (1 << 18),
+ /*! End CDRs before the 'h' extension */
+ AST_OPT_FLAG_END_CDR_BEFORE_H_EXTEN = (1 << 19),
+ /*! Use Zaptel Timing for generators if available */
+ AST_OPT_FLAG_INTERNAL_TIMING = (1 << 20),
+ /*! Always fork, even if verbose or debug settings are non-zero */
+ AST_OPT_FLAG_ALWAYS_FORK = (1 << 21),
+ /*! Disable log/verbose output to remote consoles */
+ AST_OPT_FLAG_MUTE = (1 << 22)
+};
+
+/*! These are the options that set by default when Asterisk starts */
+#define AST_DEFAULT_OPTIONS AST_OPT_FLAG_TRANSCODE_VIA_SLIN
+
+#define ast_opt_exec_includes ast_test_flag(&ast_options, AST_OPT_FLAG_EXEC_INCLUDES)
+#define ast_opt_no_fork ast_test_flag(&ast_options, AST_OPT_FLAG_NO_FORK)
+#define ast_opt_quiet ast_test_flag(&ast_options, AST_OPT_FLAG_QUIET)
+#define ast_opt_console ast_test_flag(&ast_options, AST_OPT_FLAG_CONSOLE)
+#define ast_opt_high_priority ast_test_flag(&ast_options, AST_OPT_FLAG_HIGH_PRIORITY)
+#define ast_opt_init_keys ast_test_flag(&ast_options, AST_OPT_FLAG_INIT_KEYS)
+#define ast_opt_remote ast_test_flag(&ast_options, AST_OPT_FLAG_REMOTE)
+#define ast_opt_exec ast_test_flag(&ast_options, AST_OPT_FLAG_EXEC)
+#define ast_opt_no_color ast_test_flag(&ast_options, AST_OPT_FLAG_NO_COLOR)
+#define ast_fully_booted ast_test_flag(&ast_options, AST_OPT_FLAG_FULLY_BOOTED)
+#define ast_opt_transcode_via_slin ast_test_flag(&ast_options, AST_OPT_FLAG_TRANSCODE_VIA_SLIN)
+#define ast_opt_priority_jumping ast_test_flag(&ast_options, AST_OPT_FLAG_PRIORITY_JUMPING)
+#define ast_opt_dump_core ast_test_flag(&ast_options, AST_OPT_FLAG_DUMP_CORE)
+#define ast_opt_cache_record_files ast_test_flag(&ast_options, AST_OPT_FLAG_CACHE_RECORD_FILES)
+#define ast_opt_timestamp ast_test_flag(&ast_options, AST_OPT_FLAG_TIMESTAMP)
+#define ast_opt_override_config ast_test_flag(&ast_options, AST_OPT_FLAG_OVERRIDE_CONFIG)
+#define ast_opt_reconnect ast_test_flag(&ast_options, AST_OPT_FLAG_RECONNECT)
+#define ast_opt_transmit_silence ast_test_flag(&ast_options, AST_OPT_FLAG_TRANSMIT_SILENCE)
+#define ast_opt_dont_warn ast_test_flag(&ast_options, AST_OPT_FLAG_DONT_WARN)
+#define ast_opt_end_cdr_before_h_exten ast_test_flag(&ast_options, AST_OPT_FLAG_END_CDR_BEFORE_H_EXTEN)
+#define ast_opt_internal_timing ast_test_flag(&ast_options, AST_OPT_FLAG_INTERNAL_TIMING)
+#define ast_opt_always_fork ast_test_flag(&ast_options, AST_OPT_FLAG_ALWAYS_FORK)
+#define ast_opt_mute ast_test_flag(&ast_options, AST_OPT_FLAG_MUTE)
+
+/* IN CONFLICT: extern int option_verbose; */
+/* IN CONFLICT: extern int option_debug; */ /*!< Debugging */
+extern int option_maxcalls; /*!< Maximum number of simultaneous channels */
+extern double option_maxload;
+extern char defaultlanguage[];
+
+extern time_t ast_startuptime;
+extern time_t ast_lastreloadtime;
+extern pid_t ast_mainpid;
+
+extern char record_cache_dir[AST_CACHE_DIR_LEN];
+extern char debug_filename[AST_FILENAME_MAX];
+
+extern int ast_language_is_prefix;
+
+
+
+/* lock.h */
+
+#ifndef HAVE_MTX_PROFILE
+#define __MTX_PROF(a) return pthread_mutex_lock((a))
+#else
+#define __MTX_PROF(a) do { \
+ int i; \
+ /* profile only non-blocking events */ \
+ ast_mark(mtx_prof, 1); \
+ i = pthread_mutex_trylock((a)); \
+ ast_mark(mtx_prof, 0); \
+ if (!i) \
+ return i; \
+ else \
+ return pthread_mutex_lock((a)); \
+ } while (0)
+#endif /* HAVE_MTX_PROFILE */
+
+#define AST_PTHREADT_NULL (pthread_t) -1
+#define AST_PTHREADT_STOP (pthread_t) -2
+
+#if defined(SOLARIS) || defined(BSD)
+#define AST_MUTEX_INIT_W_CONSTRUCTORS
+#endif /* SOLARIS || BSD */
+
+/* Asterisk REQUIRES recursive (not error checking) mutexes
+ and will not run without them. */
+#if defined(PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP)
+#define PTHREAD_MUTEX_INIT_VALUE PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
+#define AST_MUTEX_KIND PTHREAD_MUTEX_RECURSIVE_NP
+#else
+#define PTHREAD_MUTEX_INIT_VALUE PTHREAD_MUTEX_INITIALIZER
+#define AST_MUTEX_KIND PTHREAD_MUTEX_RECURSIVE
+#endif /* PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP */
+
+#ifdef DEBUG_THREADS
+
+#define __ast_mutex_logger(...) do { if (canlog) ast_log(LOG_ERROR, __VA_ARGS__); else fprintf(stderr, __VA_ARGS__); } while (0)
+
+#ifdef THREAD_CRASH
+#define DO_THREAD_CRASH do { *((int *)(0)) = 1; } while(0)
+#else
+#define DO_THREAD_CRASH do { } while (0)
+#endif
+
+#define AST_MUTEX_INIT_VALUE { PTHREAD_MUTEX_INIT_VALUE, { NULL }, { 0 }, 0, { NULL }, { 0 } }
+
+#define AST_MAX_REENTRANCY 10
+
+struct ast_mutex_info {
+ pthread_mutex_t mutex;
+ const char *file[AST_MAX_REENTRANCY];
+ int lineno[AST_MAX_REENTRANCY];
+ int reentrancy;
+ const char *func[AST_MAX_REENTRANCY];
+ pthread_t thread[AST_MAX_REENTRANCY];
+};
+
+typedef struct ast_mutex_info ast_mutex_t;
+
+typedef pthread_cond_t ast_cond_t;
+
+static pthread_mutex_t empty_mutex;
+
+static void __attribute__((constructor)) init_empty_mutex(void)
+{
+ memset(&empty_mutex, 0, sizeof(empty_mutex));
+}
+
+static inline int __ast_pthread_mutex_init_attr(const char *filename, int lineno, const char *func,
+ const char *mutex_name, ast_mutex_t *t,
+ pthread_mutexattr_t *attr)
+{
+#ifdef AST_MUTEX_INIT_W_CONSTRUCTORS
+ int canlog = strcmp(filename, "logger.c");
+
+ if ((t->mutex) != ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
+ if ((t->mutex) != (empty_mutex)) {
+ __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is already initialized.\n",
+ filename, lineno, func, mutex_name);
+ __ast_mutex_logger("%s line %d (%s): Error: previously initialization of mutex '%s'.\n",
+ t->file[0], t->lineno[0], t->func[0], mutex_name);
+ DO_THREAD_CRASH;
+ return 0;
+ }
+ }
+#endif
+
+ t->file[0] = filename;
+ t->lineno[0] = lineno;
+ t->func[0] = func;
+ t->thread[0] = 0;
+ t->reentrancy = 0;
+
+ return pthread_mutex_init(&t->mutex, attr);
+}
+
+static inline int __ast_pthread_mutex_init(const char *filename, int lineno, const char *func,
+ const char *mutex_name, ast_mutex_t *t)
+{
+ static pthread_mutexattr_t attr;
+
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_settype(&attr, AST_MUTEX_KIND);
+
+ return __ast_pthread_mutex_init_attr(filename, lineno, func, mutex_name, t, &attr);
+}
+#define ast_mutex_init(pmutex) __ast_pthread_mutex_init(__FILE__, __LINE__, __PRETTY_FUNCTION__, #pmutex, pmutex)
+
+static inline int __ast_pthread_mutex_destroy(const char *filename, int lineno, const char *func,
+ const char *mutex_name, ast_mutex_t *t)
+{
+ int res;
+ int canlog = strcmp(filename, "logger.c");
+
+#ifdef AST_MUTEX_INIT_W_CONSTRUCTORS
+ if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
+ __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized.\n",
+ filename, lineno, func, mutex_name);
+ }
+#endif
+
+ res = pthread_mutex_trylock(&t->mutex);
+ switch (res) {
+ case 0:
+ pthread_mutex_unlock(&t->mutex);
+ break;
+ case EINVAL:
+ __ast_mutex_logger("%s line %d (%s): Error: attempt to destroy invalid mutex '%s'.\n",
+ filename, lineno, func, mutex_name);
+ break;
+ case EBUSY:
+ __ast_mutex_logger("%s line %d (%s): Error: attempt to destroy locked mutex '%s'.\n",
+ filename, lineno, func, mutex_name);
+ __ast_mutex_logger("%s line %d (%s): Error: '%s' was locked here.\n",
+ t->file[t->reentrancy-1], t->lineno[t->reentrancy-1], t->func[t->reentrancy-1], mutex_name);
+ break;
+ }
+
+ if ((res = pthread_mutex_destroy(&t->mutex)))
+ __ast_mutex_logger("%s line %d (%s): Error destroying mutex: %s\n",
+ filename, lineno, func, strerror(res));
+#ifndef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
+ else
+ t->mutex = PTHREAD_MUTEX_INIT_VALUE;
+#endif
+ t->file[0] = filename;
+ t->lineno[0] = lineno;
+ t->func[0] = func;
+
+ return res;
+}
+
+static inline int __ast_pthread_mutex_lock(const char *filename, int lineno, const char *func,
+ const char* mutex_name, ast_mutex_t *t)
+{
+ int res;
+ int canlog = strcmp(filename, "logger.c");
+
+#if defined(AST_MUTEX_INIT_W_CONSTRUCTORS)
+ if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
+ __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized.\n",
+ filename, lineno, func, mutex_name);
+ ast_mutex_init(t);
+ }
+#endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
+
+#ifdef DETECT_DEADLOCKS
+ {
+ time_t seconds = time(NULL);
+ time_t current;
+ do {
+#ifdef HAVE_MTX_PROFILE
+ ast_mark(mtx_prof, 1);
+#endif
+ res = pthread_mutex_trylock(&t->mutex);
+#ifdef HAVE_MTX_PROFILE
+ ast_mark(mtx_prof, 0);
+#endif
+ if (res == EBUSY) {
+ current = time(NULL);
+ if ((current - seconds) && (!((current - seconds) % 5))) {
+ __ast_mutex_logger("%s line %d (%s): Deadlock? waited %d sec for mutex '%s'?\n",
+ filename, lineno, func, (int)(current - seconds), mutex_name);
+ __ast_mutex_logger("%s line %d (%s): '%s' was locked here.\n",
+ t->file[t->reentrancy-1], t->lineno[t->reentrancy-1],
+ t->func[t->reentrancy-1], mutex_name);
+ }
+ usleep(200);
+ }
+ } while (res == EBUSY);
+ }
+#else
+#ifdef HAVE_MTX_PROFILE
+ ast_mark(mtx_prof, 1);
+ res = pthread_mutex_trylock(&t->mutex);
+ ast_mark(mtx_prof, 0);
+ if (res)
+#endif
+ res = pthread_mutex_lock(&t->mutex);
+#endif /* DETECT_DEADLOCKS */
+
+ if (!res) {
+ if (t->reentrancy < AST_MAX_REENTRANCY) {
+ t->file[t->reentrancy] = filename;
+ t->lineno[t->reentrancy] = lineno;
+ t->func[t->reentrancy] = func;
+ t->thread[t->reentrancy] = pthread_self();
+ t->reentrancy++;
+ } else {
+ __ast_mutex_logger("%s line %d (%s): '%s' really deep reentrancy!\n",
+ filename, lineno, func, mutex_name);
+ }
+ } else {
+ __ast_mutex_logger("%s line %d (%s): Error obtaining mutex: %s\n",
+ filename, lineno, func, strerror(errno));
+ DO_THREAD_CRASH;
+ }
+
+ return res;
+}
+
+static inline int __ast_pthread_mutex_trylock(const char *filename, int lineno, const char *func,
+ const char* mutex_name, ast_mutex_t *t)
+{
+ int res;
+ int canlog = strcmp(filename, "logger.c");
+
+#if defined(AST_MUTEX_INIT_W_CONSTRUCTORS)
+ if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
+ __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized.\n",
+ filename, lineno, func, mutex_name);
+ ast_mutex_init(t);
+ }
+#endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
+
+ if (!(res = pthread_mutex_trylock(&t->mutex))) {
+ if (t->reentrancy < AST_MAX_REENTRANCY) {
+ t->file[t->reentrancy] = filename;
+ t->lineno[t->reentrancy] = lineno;
+ t->func[t->reentrancy] = func;
+ t->thread[t->reentrancy] = pthread_self();
+ t->reentrancy++;
+ } else {
+ __ast_mutex_logger("%s line %d (%s): '%s' really deep reentrancy!\n",
+ filename, lineno, func, mutex_name);
+ }
+ } else {
+ __ast_mutex_logger("%s line %d (%s): Warning: '%s' was locked here.\n",
+ t->file[t->reentrancy-1], t->lineno[t->reentrancy-1], t->func[t->reentrancy-1], mutex_name);
+ }
+
+ return res;
+}
+
+static inline int __ast_pthread_mutex_unlock(const char *filename, int lineno, const char *func,
+ const char *mutex_name, ast_mutex_t *t)
+{
+ int res;
+ int canlog = strcmp(filename, "logger.c");
+
+#ifdef AST_MUTEX_INIT_W_CONSTRUCTORS
+ if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
+ __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized.\n",
+ filename, lineno, func, mutex_name);
+ }
+#endif
+
+ if (t->reentrancy && (t->thread[t->reentrancy-1] != pthread_self())) {
+ __ast_mutex_logger("%s line %d (%s): attempted unlock mutex '%s' without owning it!\n",
+ filename, lineno, func, mutex_name);
+ __ast_mutex_logger("%s line %d (%s): '%s' was locked here.\n",
+ t->file[t->reentrancy-1], t->lineno[t->reentrancy-1], t->func[t->reentrancy-1], mutex_name);
+ DO_THREAD_CRASH;
+ }
+
+ if (--t->reentrancy < 0) {
+ __ast_mutex_logger("%s line %d (%s): mutex '%s' freed more times than we've locked!\n",
+ filename, lineno, func, mutex_name);
+ t->reentrancy = 0;
+ }
+
+ if (t->reentrancy < AST_MAX_REENTRANCY) {
+ t->file[t->reentrancy] = NULL;
+ t->lineno[t->reentrancy] = 0;
+ t->func[t->reentrancy] = NULL;
+ t->thread[t->reentrancy] = 0;
+ }
+
+ if ((res = pthread_mutex_unlock(&t->mutex))) {
+ __ast_mutex_logger("%s line %d (%s): Error releasing mutex: %s\n",
+ filename, lineno, func, strerror(res));
+ DO_THREAD_CRASH;
+ }
+
+ return res;
+}
+
+static inline int __ast_cond_init(const char *filename, int lineno, const char *func,
+ const char *cond_name, ast_cond_t *cond, pthread_condattr_t *cond_attr)
+{
+ return pthread_cond_init(cond, cond_attr);
+}
+
+static inline int __ast_cond_signal(const char *filename, int lineno, const char *func,
+ const char *cond_name, ast_cond_t *cond)
+{
+ return pthread_cond_signal(cond);
+}
+
+static inline int __ast_cond_broadcast(const char *filename, int lineno, const char *func,
+ const char *cond_name, ast_cond_t *cond)
+{
+ return pthread_cond_broadcast(cond);
+}
+
+static inline int __ast_cond_destroy(const char *filename, int lineno, const char *func,
+ const char *cond_name, ast_cond_t *cond)
+{
+ return pthread_cond_destroy(cond);
+}
+
+static inline int __ast_cond_wait(const char *filename, int lineno, const char *func,
+ const char *cond_name, const char *mutex_name,
+ ast_cond_t *cond, ast_mutex_t *t)
+{
+ int res;
+ int canlog = strcmp(filename, "logger.c");
+
+#ifdef AST_MUTEX_INIT_W_CONSTRUCTORS
+ if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
+ __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized.\n",
+ filename, lineno, func, mutex_name);
+ }
+#endif
+
+ if (t->reentrancy && (t->thread[t->reentrancy-1] != pthread_self())) {
+ __ast_mutex_logger("%s line %d (%s): attempted unlock mutex '%s' without owning it!\n",
+ filename, lineno, func, mutex_name);
+ __ast_mutex_logger("%s line %d (%s): '%s' was locked here.\n",
+ t->file[t->reentrancy-1], t->lineno[t->reentrancy-1], t->func[t->reentrancy-1], mutex_name);
+ DO_THREAD_CRASH;
+ }
+
+ if (--t->reentrancy < 0) {
+ __ast_mutex_logger("%s line %d (%s): mutex '%s' freed more times than we've locked!\n",
+ filename, lineno, func, mutex_name);
+ t->reentrancy = 0;
+ }
+
+ if (t->reentrancy < AST_MAX_REENTRANCY) {
+ t->file[t->reentrancy] = NULL;
+ t->lineno[t->reentrancy] = 0;
+ t->func[t->reentrancy] = NULL;
+ t->thread[t->reentrancy] = 0;
+ }
+
+ if ((res = pthread_cond_wait(cond, &t->mutex))) {
+ __ast_mutex_logger("%s line %d (%s): Error waiting on condition mutex '%s'\n",
+ filename, lineno, func, strerror(res));
+ DO_THREAD_CRASH;
+ } else {
+ if (t->reentrancy < AST_MAX_REENTRANCY) {
+ t->file[t->reentrancy] = filename;
+ t->lineno[t->reentrancy] = lineno;
+ t->func[t->reentrancy] = func;
+ t->thread[t->reentrancy] = pthread_self();
+ t->reentrancy++;
+ } else {
+ __ast_mutex_logger("%s line %d (%s): '%s' really deep reentrancy!\n",
+ filename, lineno, func, mutex_name);
+ }
+ }
+
+ return res;
+}
+
+static inline int __ast_cond_timedwait(const char *filename, int lineno, const char *func,
+ const char *cond_name, const char *mutex_name, ast_cond_t *cond,
+ ast_mutex_t *t, const struct timespec *abstime)
+{
+ int res;
+ int canlog = strcmp(filename, "logger.c");
+
+#ifdef AST_MUTEX_INIT_W_CONSTRUCTORS
+ if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
+ __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized.\n",
+ filename, lineno, func, mutex_name);
+ }
+#endif
+
+ if (t->reentrancy && (t->thread[t->reentrancy-1] != pthread_self())) {
+ __ast_mutex_logger("%s line %d (%s): attempted unlock mutex '%s' without owning it!\n",
+ filename, lineno, func, mutex_name);
+ __ast_mutex_logger("%s line %d (%s): '%s' was locked here.\n",
+ t->file[t->reentrancy-1], t->lineno[t->reentrancy-1], t->func[t->reentrancy-1], mutex_name);
+ DO_THREAD_CRASH;
+ }
+
+ if (--t->reentrancy < 0) {
+ __ast_mutex_logger("%s line %d (%s): mutex '%s' freed more times than we've locked!\n",
+ filename, lineno, func, mutex_name);
+ t->reentrancy = 0;
+ }
+
+ if (t->reentrancy < AST_MAX_REENTRANCY) {
+ t->file[t->reentrancy] = NULL;
+ t->lineno[t->reentrancy] = 0;
+ t->func[t->reentrancy] = NULL;
+ t->thread[t->reentrancy] = 0;
+ }
+
+ if ((res = pthread_cond_timedwait(cond, &t->mutex, abstime)) && (res != ETIMEDOUT)) {
+ __ast_mutex_logger("%s line %d (%s): Error waiting on condition mutex '%s'\n",
+ filename, lineno, func, strerror(res));
+ DO_THREAD_CRASH;
+ } else {
+ if (t->reentrancy < AST_MAX_REENTRANCY) {
+ t->file[t->reentrancy] = filename;
+ t->lineno[t->reentrancy] = lineno;
+ t->func[t->reentrancy] = func;
+ t->thread[t->reentrancy] = pthread_self();
+ t->reentrancy++;
+ } else {
+ __ast_mutex_logger("%s line %d (%s): '%s' really deep reentrancy!\n",
+ filename, lineno, func, mutex_name);
+ }
+ }
+
+ return res;
+}
+
+#define ast_mutex_destroy(a) __ast_pthread_mutex_destroy(__FILE__, __LINE__, __PRETTY_FUNCTION__, #a, a)
+#define ast_mutex_lock(a) __ast_pthread_mutex_lock(__FILE__, __LINE__, __PRETTY_FUNCTION__, #a, a)
+#define ast_mutex_unlock(a) __ast_pthread_mutex_unlock(__FILE__, __LINE__, __PRETTY_FUNCTION__, #a, a)
+#define ast_mutex_trylock(a) __ast_pthread_mutex_trylock(__FILE__, __LINE__, __PRETTY_FUNCTION__, #a, a)
+#define ast_cond_init(cond, attr) __ast_cond_init(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, cond, attr)
+#define ast_cond_destroy(cond) __ast_cond_destroy(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, cond)
+#define ast_cond_signal(cond) __ast_cond_signal(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, cond)
+#define ast_cond_broadcast(cond) __ast_cond_broadcast(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, cond)
+#define ast_cond_wait(cond, mutex) __ast_cond_wait(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, #mutex, cond, mutex)
+#define ast_cond_timedwait(cond, mutex, time) __ast_cond_timedwait(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, #mutex, cond, mutex, time)
+
+#else /* !DEBUG_THREADS */
+
+
+typedef pthread_mutex_t ast_mutex_t;
+
+#define AST_MUTEX_INIT_VALUE ((ast_mutex_t) PTHREAD_MUTEX_INIT_VALUE)
+
+static inline int ast_mutex_init(ast_mutex_t *pmutex)
+{
+ pthread_mutexattr_t attr;
+
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_settype(&attr, AST_MUTEX_KIND);
+
+ return pthread_mutex_init(pmutex, &attr);
+}
+
+#define ast_pthread_mutex_init(pmutex,a) pthread_mutex_init(pmutex,a)
+
+static inline int ast_mutex_unlock(ast_mutex_t *pmutex)
+{
+ return pthread_mutex_unlock(pmutex);
+}
+
+static inline int ast_mutex_destroy(ast_mutex_t *pmutex)
+{
+ return pthread_mutex_destroy(pmutex);
+}
+
+static inline int ast_mutex_lock(ast_mutex_t *pmutex)
+{
+ __MTX_PROF(pmutex);
+}
+
+static inline int ast_mutex_trylock(ast_mutex_t *pmutex)
+{
+ return pthread_mutex_trylock(pmutex);
+}
+
+typedef pthread_cond_t ast_cond_t;
+
+static inline int ast_cond_init(ast_cond_t *cond, pthread_condattr_t *cond_attr)
+{
+ return pthread_cond_init(cond, cond_attr);
+}
+
+static inline int ast_cond_signal(ast_cond_t *cond)
+{
+ return pthread_cond_signal(cond);
+}
+
+static inline int ast_cond_broadcast(ast_cond_t *cond)
+{
+ return pthread_cond_broadcast(cond);
+}
+
+static inline int ast_cond_destroy(ast_cond_t *cond)
+{
+ return pthread_cond_destroy(cond);
+}
+
+static inline int ast_cond_wait(ast_cond_t *cond, ast_mutex_t *t)
+{
+ return pthread_cond_wait(cond, t);
+}
+
+static inline int ast_cond_timedwait(ast_cond_t *cond, ast_mutex_t *t, const struct timespec *abstime)
+{
+ return pthread_cond_timedwait(cond, t, abstime);
+}
+
+#endif /* !DEBUG_THREADS */
+
+#if defined(AST_MUTEX_INIT_W_CONSTRUCTORS)
+/* If AST_MUTEX_INIT_W_CONSTRUCTORS is defined, use file scope
+ constructors/destructors to create/destroy mutexes. */
+#define __AST_MUTEX_DEFINE(scope, mutex) \
+ scope ast_mutex_t mutex = AST_MUTEX_INIT_VALUE; \
+static void __attribute__ ((constructor)) init_##mutex(void) \
+{ \
+ ast_mutex_init(&mutex); \
+} \
+static void __attribute__ ((destructor)) fini_##mutex(void) \
+{ \
+ ast_mutex_destroy(&mutex); \
+}
+#else /* !AST_MUTEX_INIT_W_CONSTRUCTORS */
+/* By default, use static initialization of mutexes. */
+#define __AST_MUTEX_DEFINE(scope, mutex) \
+ scope ast_mutex_t mutex = AST_MUTEX_INIT_VALUE
+#endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
+
+#define pthread_mutex_t use_ast_mutex_t_instead_of_pthread_mutex_t
+#define pthread_mutex_lock use_ast_mutex_lock_instead_of_pthread_mutex_lock
+#define pthread_mutex_unlock use_ast_mutex_unlock_instead_of_pthread_mutex_unlock
+#define pthread_mutex_trylock use_ast_mutex_trylock_instead_of_pthread_mutex_trylock
+#define pthread_mutex_init use_ast_mutex_init_instead_of_pthread_mutex_init
+#define pthread_mutex_destroy use_ast_mutex_destroy_instead_of_pthread_mutex_destroy
+#define pthread_cond_t use_ast_cond_t_instead_of_pthread_cond_t
+#define pthread_cond_init use_ast_cond_init_instead_of_pthread_cond_init
+#define pthread_cond_destroy use_ast_cond_destroy_instead_of_pthread_cond_destroy
+#define pthread_cond_signal use_ast_cond_signal_instead_of_pthread_cond_signal
+#define pthread_cond_broadcast use_ast_cond_broadcast_instead_of_pthread_cond_broadcast
+#define pthread_cond_wait use_ast_cond_wait_instead_of_pthread_cond_wait
+#define pthread_cond_timedwait use_ast_cond_timedwait_instead_of_pthread_cond_timedwait
+
+#define AST_MUTEX_DEFINE_STATIC(mutex) __AST_MUTEX_DEFINE(static, mutex)
+
+#define AST_MUTEX_INITIALIZER __use_AST_MUTEX_DEFINE_STATIC_rather_than_AST_MUTEX_INITIALIZER__
+
+#define gethostbyname __gethostbyname__is__not__reentrant__use__ast_gethostbyname__instead__
+
+#ifndef __linux__
+#define pthread_create __use_ast_pthread_create_instead__
+#endif
+
+typedef pthread_rwlock_t ast_rwlock_t;
+
+static inline int ast_rwlock_init(ast_rwlock_t *prwlock)
+{
+ pthread_rwlockattr_t attr;
+
+ pthread_rwlockattr_init(&attr);
+
+#ifdef HAVE_PTHREAD_RWLOCK_PREFER_WRITER_NP
+ pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NP);
+#endif
+
+ return pthread_rwlock_init(prwlock, &attr);
+}
+
+static inline int ast_rwlock_destroy(ast_rwlock_t *prwlock)
+{
+ return pthread_rwlock_destroy(prwlock);
+}
+
+static inline int ast_rwlock_unlock(ast_rwlock_t *prwlock)
+{
+ return pthread_rwlock_unlock(prwlock);
+}
+
+static inline int ast_rwlock_rdlock(ast_rwlock_t *prwlock)
+{
+ return pthread_rwlock_rdlock(prwlock);
+}
+
+static inline int ast_rwlock_tryrdlock(ast_rwlock_t *prwlock)
+{
+ return pthread_rwlock_tryrdlock(prwlock);
+}
+
+static inline int ast_rwlock_wrlock(ast_rwlock_t *prwlock)
+{
+ return pthread_rwlock_wrlock(prwlock);
+}
+
+static inline int ast_rwlock_trywrlock(ast_rwlock_t *prwlock)
+{
+ return pthread_rwlock_trywrlock(prwlock);
+}
+
+/* Statically declared read/write locks */
+
+#ifndef HAVE_PTHREAD_RWLOCK_INITIALIZER
+#define __AST_RWLOCK_DEFINE(scope, rwlock) \
+ scope ast_rwlock_t rwlock; \
+static void __attribute__ ((constructor)) init_##rwlock(void) \
+{ \
+ ast_rwlock_init(&rwlock); \
+} \
+static void __attribute__ ((destructor)) fini_##rwlock(void) \
+{ \
+ ast_rwlock_destroy(&rwlock); \
+}
+#else
+#define AST_RWLOCK_INIT_VALUE PTHREAD_RWLOCK_INITIALIZER
+#define __AST_RWLOCK_DEFINE(scope, rwlock) \
+ scope ast_rwlock_t rwlock = AST_RWLOCK_INIT_VALUE
+#endif
+
+#define AST_RWLOCK_DEFINE_STATIC(rwlock) __AST_RWLOCK_DEFINE(static, rwlock)
+
+/*
+ * Initial support for atomic instructions.
+ * For platforms that have it, use the native cpu instruction to
+ * implement them. For other platforms, resort to a 'slow' version
+ * (defined in utils.c) that protects the atomic instruction with
+ * a single lock.
+ * The slow versions is always available, for testing purposes,
+ * as ast_atomic_fetchadd_int_slow()
+ */
+
+int ast_atomic_fetchadd_int_slow(volatile int *p, int v);
+
+#if defined(HAVE_OSX_ATOMICS)
+#include "libkern/OSAtomic.h"
+#endif
+
+/*! \brief Atomically add v to *p and return * the previous value of *p.
+ * This can be used to handle reference counts, and the return value
+ * can be used to generate unique identifiers.
+ */
+
+#if defined(HAVE_GCC_ATOMICS)
+AST_INLINE_API(int ast_atomic_fetchadd_int(volatile int *p, int v),
+{
+ return __sync_fetch_and_add(p, v);
+})
+#elif defined(HAVE_OSX_ATOMICS) && (SIZEOF_INT == 4)
+AST_INLINE_API(int ast_atomic_fetchadd_int(volatile int *p, int v),
+{
+ return OSAtomicAdd32(v, (int32_t *) p);
+})
+#elif defined(HAVE_OSX_ATOMICS) && (SIZEOF_INT == 8)
+AST_INLINE_API(int ast_atomic_fetchadd_int(volatile int *p, int v),
+{
+ return OSAtomicAdd64(v, (int64_t *) p);
+#elif defined (__i386__)
+AST_INLINE_API(int ast_atomic_fetchadd_int(volatile int *p, int v),
+{
+ __asm __volatile (
+ " lock xaddl %0, %1 ; "
+ : "+r" (v), /* 0 (result) */
+ "=m" (*p) /* 1 */
+ : "m" (*p)); /* 2 */
+ return (v);
+})
+#else /* low performance version in utils.c */
+AST_INLINE_API(int ast_atomic_fetchadd_int(volatile int *p, int v),
+{
+ return ast_atomic_fetchadd_int_slow(p, v);
+})
+#endif
+
+/*! \brief decrement *p by 1 and return true if the variable has reached 0.
+ * Useful e.g. to check if a refcount has reached 0.
+ */
+#if defined(HAVE_GCC_ATOMICS)
+AST_INLINE_API(int ast_atomic_dec_and_test(volatile int *p),
+{
+ return __sync_sub_and_fetch(p, 1) == 0;
+})
+#elif defined(HAVE_OSX_ATOMICS) && (SIZEOF_INT == 4)
+AST_INLINE_API(int ast_atomic_dec_and_test(volatile int *p),
+{
+ return OSAtomicAdd32( -1, (int32_t *) p) == 0;
+})
+#elif defined(HAVE_OSX_ATOMICS) && (SIZEOF_INT == 8)
+AST_INLINE_API(int ast_atomic_dec_and_test(volatile int *p),
+{
+ return OSAtomicAdd64( -1, (int64_t *) p) == 0;
+#else
+AST_INLINE_API(int ast_atomic_dec_and_test(volatile int *p),
+{
+ int a = ast_atomic_fetchadd_int(p, -1);
+ return a == 1; /* true if the value is 0 now (so it was 1 previously) */
+})
+#endif
+
+#ifndef DEBUG_CHANNEL_LOCKS
+/*! \brief Lock a channel. If DEBUG_CHANNEL_LOCKS is defined
+ in the Makefile, print relevant output for debugging */
+#define ast_channel_lock(x) ast_mutex_lock(&x->lock)
+/*! \brief Unlock a channel. If DEBUG_CHANNEL_LOCKS is defined
+ in the Makefile, print relevant output for debugging */
+#define ast_channel_unlock(x) ast_mutex_unlock(&x->lock)
+/*! \brief Try locking a channel. If DEBUG_CHANNEL_LOCKS is defined
+ in the Makefile, print relevant output for debugging */
+#define ast_channel_trylock(x) ast_mutex_trylock(&x->lock)
+#else
+
+struct ast_channel;
+
+/*! \brief Lock AST channel (and print debugging output)
+\note You need to enable DEBUG_CHANNEL_LOCKS for this function */
+int ast_channel_lock(struct ast_channel *chan);
+
+/*! \brief Unlock AST channel (and print debugging output)
+\note You need to enable DEBUG_CHANNEL_LOCKS for this function
+*/
+int ast_channel_unlock(struct ast_channel *chan);
+
+/*! \brief Lock AST channel (and print debugging output)
+\note You need to enable DEBUG_CHANNEL_LOCKS for this function */
+int ast_channel_trylock(struct ast_channel *chan);
+#endif
+
+
+/* linkedlists.h */
+
+#define AST_LIST_LOCK(head) \
+ ast_mutex_lock(&(head)->lock)
+
+/*!
+ \brief Write locks a list.
+ \param head This is a pointer to the list head structure
+
+ This macro attempts to place an exclusive write lock in the
+ list head structure pointed to by head.
+ Returns non-zero on success, 0 on failure
+*/
+#define AST_RWLIST_WRLOCK(head) \
+ ast_rwlock_wrlock(&(head)->lock)
+
+/*!
+ \brief Read locks a list.
+ \param head This is a pointer to the list head structure
+
+ This macro attempts to place a read lock in the
+ list head structure pointed to by head.
+ Returns non-zero on success, 0 on failure
+*/
+#define AST_RWLIST_RDLOCK(head) \
+ ast_rwlock_rdlock(&(head)->lock)
+
+/*!
+ \brief Locks a list, without blocking if the list is locked.
+ \param head This is a pointer to the list head structure
+
+ This macro attempts to place an exclusive lock in the
+ list head structure pointed to by head.
+ Returns non-zero on success, 0 on failure
+*/
+#define AST_LIST_TRYLOCK(head) \
+ ast_mutex_trylock(&(head)->lock)
+
+/*!
+ \brief Write locks a list, without blocking if the list is locked.
+ \param head This is a pointer to the list head structure
+
+ This macro attempts to place an exclusive write lock in the
+ list head structure pointed to by head.
+ Returns non-zero on success, 0 on failure
+*/
+#define AST_RWLIST_TRYWRLOCK(head) \
+ ast_rwlock_trywrlock(&(head)->lock)
+
+/*!
+ \brief Read locks a list, without blocking if the list is locked.
+ \param head This is a pointer to the list head structure
+
+ This macro attempts to place a read lock in the
+ list head structure pointed to by head.
+ Returns non-zero on success, 0 on failure
+*/
+#define AST_RWLIST_TRYRDLOCK(head) \
+ ast_rwlock_tryrdlock(&(head)->lock)
+
+/*!
+ \brief Attempts to unlock a list.
+ \param head This is a pointer to the list head structure
+
+ This macro attempts to remove an exclusive lock from the
+ list head structure pointed to by head. If the list
+ was not locked by this thread, this macro has no effect.
+*/
+#define AST_LIST_UNLOCK(head) \
+ ast_mutex_unlock(&(head)->lock)
+
+/*!
+ \brief Attempts to unlock a read/write based list.
+ \param head This is a pointer to the list head structure
+
+ This macro attempts to remove a read or write lock from the
+ list head structure pointed to by head. If the list
+ was not locked by this thread, this macro has no effect.
+*/
+#define AST_RWLIST_UNLOCK(head) \
+ ast_rwlock_unlock(&(head)->lock)
+
+/*!
+ \brief Defines a structure to be used to hold a list of specified type.
+ \param name This will be the name of the defined structure.
+ \param type This is the type of each list entry.
+
+ This macro creates a structure definition that can be used
+ to hold a list of the entries of type \a type. It does not actually
+ declare (allocate) a structure; to do that, either follow this
+ macro with the desired name of the instance you wish to declare,
+ or use the specified \a name to declare instances elsewhere.
+
+ Example usage:
+ \code
+ static AST_LIST_HEAD(entry_list, entry) entries;
+ \endcode
+
+ This would define \c struct \c entry_list, and declare an instance of it named
+ \a entries, all intended to hold a list of type \c struct \c entry.
+*/
+#define AST_LIST_HEAD(name, type) \
+struct name { \
+ struct type *first; \
+ struct type *last; \
+ ast_mutex_t lock; \
+}
+
+/*!
+ \brief Defines a structure to be used to hold a read/write list of specified type.
+ \param name This will be the name of the defined structure.
+ \param type This is the type of each list entry.
+
+ This macro creates a structure definition that can be used
+ to hold a list of the entries of type \a type. It does not actually
+ declare (allocate) a structure; to do that, either follow this
+ macro with the desired name of the instance you wish to declare,
+ or use the specified \a name to declare instances elsewhere.
+
+ Example usage:
+ \code
+ static AST_RWLIST_HEAD(entry_list, entry) entries;
+ \endcode
+
+ This would define \c struct \c entry_list, and declare an instance of it named
+ \a entries, all intended to hold a list of type \c struct \c entry.
+*/
+#define AST_RWLIST_HEAD(name, type) \
+struct name { \
+ struct type *first; \
+ struct type *last; \
+ ast_rwlock_t lock; \
+}
+
+/*!
+ \brief Defines a structure to be used to hold a list of specified type (with no lock).
+ \param name This will be the name of the defined structure.
+ \param type This is the type of each list entry.
+
+ This macro creates a structure definition that can be used
+ to hold a list of the entries of type \a type. It does not actually
+ declare (allocate) a structure; to do that, either follow this
+ macro with the desired name of the instance you wish to declare,
+ or use the specified \a name to declare instances elsewhere.
+
+ Example usage:
+ \code
+ static AST_LIST_HEAD_NOLOCK(entry_list, entry) entries;
+ \endcode
+
+ This would define \c struct \c entry_list, and declare an instance of it named
+ \a entries, all intended to hold a list of type \c struct \c entry.
+*/
+#define AST_LIST_HEAD_NOLOCK(name, type) \
+struct name { \
+ struct type *first; \
+ struct type *last; \
+}
+
+/*!
+ \brief Defines initial values for a declaration of AST_LIST_HEAD
+*/
+#define AST_LIST_HEAD_INIT_VALUE { \
+ .first = NULL, \
+ .last = NULL, \
+ .lock = AST_MUTEX_INIT_VALUE, \
+ }
+
+/*!
+ \brief Defines initial values for a declaration of AST_RWLIST_HEAD
+*/
+#define AST_RWLIST_HEAD_INIT_VALUE { \
+ .first = NULL, \
+ .last = NULL, \
+ .lock = AST_RWLOCK_INIT_VALUE, \
+ }
+
+/*!
+ \brief Defines initial values for a declaration of AST_LIST_HEAD_NOLOCK
+*/
+#define AST_LIST_HEAD_NOLOCK_INIT_VALUE { \
+ .first = NULL, \
+ .last = NULL, \
+ }
+
+/*!
+ \brief Defines a structure to be used to hold a list of specified type, statically initialized.
+ \param name This will be the name of the defined structure.
+ \param type This is the type of each list entry.
+
+ This macro creates a structure definition that can be used
+ to hold a list of the entries of type \a type, and allocates an instance
+ of it, initialized to be empty.
+
+ Example usage:
+ \code
+ static AST_LIST_HEAD_STATIC(entry_list, entry);
+ \endcode
+
+ This would define \c struct \c entry_list, intended to hold a list of
+ type \c struct \c entry.
+*/
+#if defined(AST_MUTEX_INIT_W_CONSTRUCTORS)
+#define AST_LIST_HEAD_STATIC(name, type) \
+struct name { \
+ struct type *first; \
+ struct type *last; \
+ ast_mutex_t lock; \
+} name; \
+static void __attribute__ ((constructor)) init_##name(void) \
+{ \
+ AST_LIST_HEAD_INIT(&name); \
+} \
+static void __attribute__ ((destructor)) fini_##name(void) \
+{ \
+ AST_LIST_HEAD_DESTROY(&name); \
+} \
+struct __dummy_##name
+#else
+#define AST_LIST_HEAD_STATIC(name, type) \
+struct name { \
+ struct type *first; \
+ struct type *last; \
+ ast_mutex_t lock; \
+} name = AST_LIST_HEAD_INIT_VALUE
+#endif
+
+/*!
+ \brief Defines a structure to be used to hold a read/write list of specified type, statically initialized.
+ \param name This will be the name of the defined structure.
+ \param type This is the type of each list entry.
+
+ This macro creates a structure definition that can be used
+ to hold a list of the entries of type \a type, and allocates an instance
+ of it, initialized to be empty.
+
+ Example usage:
+ \code
+ static AST_RWLIST_HEAD_STATIC(entry_list, entry);
+ \endcode
+
+ This would define \c struct \c entry_list, intended to hold a list of
+ type \c struct \c entry.
+*/
+#ifndef AST_RWLOCK_INIT_VALUE
+#define AST_RWLIST_HEAD_STATIC(name, type) \
+struct name { \
+ struct type *first; \
+ struct type *last; \
+ ast_rwlock_t lock; \
+} name; \
+static void __attribute__ ((constructor)) init_##name(void) \
+{ \
+ AST_RWLIST_HEAD_INIT(&name); \
+} \
+static void __attribute__ ((destructor)) fini_##name(void) \
+{ \
+ AST_RWLIST_HEAD_DESTROY(&name); \
+} \
+struct __dummy_##name
+#else
+#define AST_RWLIST_HEAD_STATIC(name, type) \
+struct name { \
+ struct type *first; \
+ struct type *last; \
+ ast_rwlock_t lock; \
+} name = AST_RWLIST_HEAD_INIT_VALUE
+#endif
+
+/*!
+ \brief Defines a structure to be used to hold a list of specified type, statically initialized.
+
+ This is the same as AST_LIST_HEAD_STATIC, except without the lock included.
+*/
+#define AST_LIST_HEAD_NOLOCK_STATIC(name, type) \
+struct name { \
+ struct type *first; \
+ struct type *last; \
+} name = AST_LIST_HEAD_NOLOCK_INIT_VALUE
+
+/*!
+ \brief Initializes a list head structure with a specified first entry.
+ \param head This is a pointer to the list head structure
+ \param entry pointer to the list entry that will become the head of the list
+
+ This macro initializes a list head structure by setting the head
+ entry to the supplied value and recreating the embedded lock.
+*/
+#define AST_LIST_HEAD_SET(head, entry) do { \
+ (head)->first = (entry); \
+ (head)->last = (entry); \
+ ast_mutex_init(&(head)->lock); \
+} while (0)
+
+/*!
+ \brief Initializes an rwlist head structure with a specified first entry.
+ \param head This is a pointer to the list head structure
+ \param entry pointer to the list entry that will become the head of the list
+
+ This macro initializes a list head structure by setting the head
+ entry to the supplied value and recreating the embedded lock.
+*/
+#define AST_RWLIST_HEAD_SET(head, entry) do { \
+ (head)->first = (entry); \
+ (head)->last = (entry); \
+ ast_rwlock_init(&(head)->lock); \
+} while (0)
+
+/*!
+ \brief Initializes a list head structure with a specified first entry.
+ \param head This is a pointer to the list head structure
+ \param entry pointer to the list entry that will become the head of the list
+
+ This macro initializes a list head structure by setting the head
+ entry to the supplied value.
+*/
+#define AST_LIST_HEAD_SET_NOLOCK(head, entry) do { \
+ (head)->first = (entry); \
+ (head)->last = (entry); \
+} while (0)
+
+/*!
+ \brief Declare a forward link structure inside a list entry.
+ \param type This is the type of each list entry.
+
+ This macro declares a structure to be used to link list entries together.
+ It must be used inside the definition of the structure named in
+ \a type, as follows:
+
+ \code
+ struct list_entry {
+ ...
+ AST_LIST_ENTRY(list_entry) list;
+ }
+ \endcode
+
+ The field name \a list here is arbitrary, and can be anything you wish.
+*/
+#define AST_LIST_ENTRY(type) \
+struct { \
+ struct type *next; \
+}
+
+#define AST_RWLIST_ENTRY AST_LIST_ENTRY
+
+/*!
+ \brief Returns the first entry contained in a list.
+ \param head This is a pointer to the list head structure
+ */
+#define AST_LIST_FIRST(head) ((head)->first)
+
+#define AST_RWLIST_FIRST AST_LIST_FIRST
+
+/*!
+ \brief Returns the last entry contained in a list.
+ \param head This is a pointer to the list head structure
+ */
+#define AST_LIST_LAST(head) ((head)->last)
+
+#define AST_RWLIST_LAST AST_LIST_LAST
+
+/*!
+ \brief Returns the next entry in the list after the given entry.
+ \param elm This is a pointer to the current entry.
+ \param field This is the name of the field (declared using AST_LIST_ENTRY())
+ used to link entries of this list together.
+*/
+#define AST_LIST_NEXT(elm, field) ((elm)->field.next)
+
+#define AST_RWLIST_NEXT AST_LIST_NEXT
+
+/*!
+ \brief Checks whether the specified list contains any entries.
+ \param head This is a pointer to the list head structure
+
+ Returns non-zero if the list has entries, zero if not.
+ */
+#define AST_LIST_EMPTY(head) (AST_LIST_FIRST(head) == NULL)
+
+#define AST_RWLIST_EMPTY AST_LIST_EMPTY
+
+/*!
+ \brief Loops over (traverses) the entries in a list.
+ \param head This is a pointer to the list head structure
+ \param var This is the name of the variable that will hold a pointer to the
+ current list entry on each iteration. It must be declared before calling
+ this macro.
+ \param field This is the name of the field (declared using AST_LIST_ENTRY())
+ used to link entries of this list together.
+
+ This macro is use to loop over (traverse) the entries in a list. It uses a
+ \a for loop, and supplies the enclosed code with a pointer to each list
+ entry as it loops. It is typically used as follows:
+ \code
+ static AST_LIST_HEAD(entry_list, list_entry) entries;
+ ...
+ struct list_entry {
+ ...
+ AST_LIST_ENTRY(list_entry) list;
+ }
+ ...
+ struct list_entry *current;
+ ...
+ AST_LIST_TRAVERSE(&entries, current, list) {
+ (do something with current here)
+ }
+ \endcode
+ \warning If you modify the forward-link pointer contained in the \a current entry while
+ inside the loop, the behavior will be unpredictable. At a minimum, the following
+ macros will modify the forward-link pointer, and should not be used inside
+ AST_LIST_TRAVERSE() against the entry pointed to by the \a current pointer without
+ careful consideration of their consequences:
+ \li AST_LIST_NEXT() (when used as an lvalue)
+ \li AST_LIST_INSERT_AFTER()
+ \li AST_LIST_INSERT_HEAD()
+ \li AST_LIST_INSERT_TAIL()
+*/
+#define AST_LIST_TRAVERSE(head,var,field) \
+ for((var) = (head)->first; (var); (var) = (var)->field.next)
+
+#define AST_RWLIST_TRAVERSE AST_LIST_TRAVERSE
+
+/*!
+ \brief Loops safely over (traverses) the entries in a list.
+ \param head This is a pointer to the list head structure
+ \param var This is the name of the variable that will hold a pointer to the
+ current list entry on each iteration. It must be declared before calling
+ this macro.
+ \param field This is the name of the field (declared using AST_LIST_ENTRY())
+ used to link entries of this list together.
+
+ This macro is used to safely loop over (traverse) the entries in a list. It
+ uses a \a for loop, and supplies the enclosed code with a pointer to each list
+ entry as it loops. It is typically used as follows:
+
+ \code
+ static AST_LIST_HEAD(entry_list, list_entry) entries;
+ ...
+ struct list_entry {
+ ...
+ AST_LIST_ENTRY(list_entry) list;
+ }
+ ...
+ struct list_entry *current;
+ ...
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&entries, current, list) {
+ (do something with current here)
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ \endcode
+
+ It differs from AST_LIST_TRAVERSE() in that the code inside the loop can modify
+ (or even free, after calling AST_LIST_REMOVE_CURRENT()) the entry pointed to by
+ the \a current pointer without affecting the loop traversal.
+*/
+#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field) { \
+ typeof((head)->first) __list_next; \
+ typeof((head)->first) __list_prev = NULL; \
+ typeof((head)->first) __new_prev = NULL; \
+ for ((var) = (head)->first, __new_prev = (var), \
+ __list_next = (var) ? (var)->field.next : NULL; \
+ (var); \
+ __list_prev = __new_prev, (var) = __list_next, \
+ __new_prev = (var), \
+ __list_next = (var) ? (var)->field.next : NULL \
+ )
+
+#define AST_RWLIST_TRAVERSE_SAFE_BEGIN AST_LIST_TRAVERSE_SAFE_BEGIN
+
+/*!
+ \brief Removes the \a current entry from a list during a traversal.
+ \param head This is a pointer to the list head structure
+ \param field This is the name of the field (declared using AST_LIST_ENTRY())
+ used to link entries of this list together.
+
+ \note This macro can \b only be used inside an AST_LIST_TRAVERSE_SAFE_BEGIN()
+ block; it is used to unlink the current entry from the list without affecting
+ the list traversal (and without having to re-traverse the list to modify the
+ previous entry, if any).
+ */
+#define AST_LIST_REMOVE_CURRENT(head, field) \
+ __new_prev->field.next = NULL; \
+ __new_prev = __list_prev; \
+ if (__list_prev) \
+ __list_prev->field.next = __list_next; \
+ else \
+ (head)->first = __list_next; \
+ if (!__list_next) \
+ (head)->last = __list_prev;
+
+#define AST_RWLIST_REMOVE_CURRENT AST_LIST_REMOVE_CURRENT
+
+/*!
+ \brief Inserts a list entry before the current entry during a traversal.
+ \param head This is a pointer to the list head structure
+ \param elm This is a pointer to the entry to be inserted.
+ \param field This is the name of the field (declared using AST_LIST_ENTRY())
+ used to link entries of this list together.
+
+ \note This macro can \b only be used inside an AST_LIST_TRAVERSE_SAFE_BEGIN()
+ block.
+ */
+#define AST_LIST_INSERT_BEFORE_CURRENT(head, elm, field) do { \
+ if (__list_prev) { \
+ (elm)->field.next = __list_prev->field.next; \
+ __list_prev->field.next = elm; \
+ } else { \
+ (elm)->field.next = (head)->first; \
+ (head)->first = (elm); \
+ } \
+ __new_prev = (elm); \
+} while (0)
+
+#define AST_RWLIST_INSERT_BEFORE_CURRENT AST_LIST_INSERT_BEFORE_CURRENT
+
+/*!
+ \brief Closes a safe loop traversal block.
+ */
+#define AST_LIST_TRAVERSE_SAFE_END }
+
+#define AST_RWLIST_TRAVERSE_SAFE_END AST_LIST_TRAVERSE_SAFE_END
+
+/*!
+ \brief Initializes a list head structure.
+ \param head This is a pointer to the list head structure
+
+ This macro initializes a list head structure by setting the head
+ entry to \a NULL (empty list) and recreating the embedded lock.
+*/
+#define AST_LIST_HEAD_INIT(head) { \
+ (head)->first = NULL; \
+ (head)->last = NULL; \
+ ast_mutex_init(&(head)->lock); \
+}
+
+/*!
+ \brief Initializes an rwlist head structure.
+ \param head This is a pointer to the list head structure
+
+ This macro initializes a list head structure by setting the head
+ entry to \a NULL (empty list) and recreating the embedded lock.
+*/
+#define AST_RWLIST_HEAD_INIT(head) { \
+ (head)->first = NULL; \
+ (head)->last = NULL; \
+ ast_rwlock_init(&(head)->lock); \
+}
+
+/*!
+ \brief Destroys a list head structure.
+ \param head This is a pointer to the list head structure
+
+ This macro destroys a list head structure by setting the head
+ entry to \a NULL (empty list) and destroying the embedded lock.
+ It does not free the structure from memory.
+*/
+#define AST_LIST_HEAD_DESTROY(head) { \
+ (head)->first = NULL; \
+ (head)->last = NULL; \
+ ast_mutex_destroy(&(head)->lock); \
+}
+
+/*!
+ \brief Destroys an rwlist head structure.
+ \param head This is a pointer to the list head structure
+
+ This macro destroys a list head structure by setting the head
+ entry to \a NULL (empty list) and destroying the embedded lock.
+ It does not free the structure from memory.
+*/
+#define AST_RWLIST_HEAD_DESTROY(head) { \
+ (head)->first = NULL; \
+ (head)->last = NULL; \
+ ast_rwlock_destroy(&(head)->lock); \
+}
+
+/*!
+ \brief Initializes a list head structure.
+ \param head This is a pointer to the list head structure
+
+ This macro initializes a list head structure by setting the head
+ entry to \a NULL (empty list). There is no embedded lock handling
+ with this macro.
+*/
+#define AST_LIST_HEAD_INIT_NOLOCK(head) { \
+ (head)->first = NULL; \
+ (head)->last = NULL; \
+}
+
+/*!
+ \brief Inserts a list entry after a given entry.
+ \param head This is a pointer to the list head structure
+ \param listelm This is a pointer to the entry after which the new entry should
+ be inserted.
+ \param elm This is a pointer to the entry to be inserted.
+ \param field This is the name of the field (declared using AST_LIST_ENTRY())
+ used to link entries of this list together.
+ */
+#define AST_LIST_INSERT_AFTER(head, listelm, elm, field) do { \
+ (elm)->field.next = (listelm)->field.next; \
+ (listelm)->field.next = (elm); \
+ if ((head)->last == (listelm)) \
+ (head)->last = (elm); \
+} while (0)
+
+#define AST_RWLIST_INSERT_AFTER AST_LIST_INSERT_AFTER
+
+/*!
+ \brief Inserts a list entry at the head of a list.
+ \param head This is a pointer to the list head structure
+ \param elm This is a pointer to the entry to be inserted.
+ \param field This is the name of the field (declared using AST_LIST_ENTRY())
+ used to link entries of this list together.
+ */
+#define AST_LIST_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.next = (head)->first; \
+ (head)->first = (elm); \
+ if (!(head)->last) \
+ (head)->last = (elm); \
+} while (0)
+
+#define AST_RWLIST_INSERT_HEAD AST_LIST_INSERT_HEAD
+
+/*!
+ \brief Appends a list entry to the tail of a list.
+ \param head This is a pointer to the list head structure
+ \param elm This is a pointer to the entry to be appended.
+ \param field This is the name of the field (declared using AST_LIST_ENTRY())
+ used to link entries of this list together.
+
+ Note: The link field in the appended entry is \b not modified, so if it is
+ actually the head of a list itself, the entire list will be appended
+ temporarily (until the next AST_LIST_INSERT_TAIL is performed).
+ */
+#define AST_LIST_INSERT_TAIL(head, elm, field) do { \
+ if (!(head)->first) { \
+ (head)->first = (elm); \
+ (head)->last = (elm); \
+ } else { \
+ (head)->last->field.next = (elm); \
+ (head)->last = (elm); \
+ } \
+} while (0)
+
+#define AST_RWLIST_INSERT_TAIL AST_LIST_INSERT_TAIL
+
+/*!
+ \brief Appends a whole list to the tail of a list.
+ \param head This is a pointer to the list head structure
+ \param list This is a pointer to the list to be appended.
+ \param field This is the name of the field (declared using AST_LIST_ENTRY())
+ used to link entries of this list together.
+ */
+#define AST_LIST_APPEND_LIST(head, list, field) do { \
+ if (!(head)->first) { \
+ (head)->first = (list)->first; \
+ (head)->last = (list)->last; \
+ } else { \
+ (head)->last->field.next = (list)->first; \
+ (head)->last = (list)->last; \
+ } \
+} while (0)
+
+#define AST_RWLIST_APPEND_LIST AST_LIST_APPEND_LIST
+
+/*!
+ \brief Removes and returns the head entry from a list.
+ \param head This is a pointer to the list head structure
+ \param field This is the name of the field (declared using AST_LIST_ENTRY())
+ used to link entries of this list together.
+
+ Removes the head entry from the list, and returns a pointer to it.
+ This macro is safe to call on an empty list.
+ */
+#define AST_LIST_REMOVE_HEAD(head, field) ({ \
+ typeof((head)->first) cur = (head)->first; \
+ if (cur) { \
+ (head)->first = cur->field.next; \
+ cur->field.next = NULL; \
+ if ((head)->last == cur) \
+ (head)->last = NULL; \
+ } \
+ cur; \
+ })
+
+#define AST_RWLIST_REMOVE_HEAD AST_LIST_REMOVE_HEAD
+
+/*!
+ \brief Removes a specific entry from a list.
+ \param head This is a pointer to the list head structure
+ \param elm This is a pointer to the entry to be removed.
+ \param field This is the name of the field (declared using AST_LIST_ENTRY())
+ used to link entries of this list together.
+ \warning The removed entry is \b not freed nor modified in any way.
+ */
+#define AST_LIST_REMOVE(head, elm, field) do { \
+ if ((head)->first == (elm)) { \
+ (head)->first = (elm)->field.next; \
+ if ((head)->last == (elm)) \
+ (head)->last = NULL; \
+ } else { \
+ typeof(elm) curelm = (head)->first; \
+ while (curelm && (curelm->field.next != (elm))) \
+ curelm = curelm->field.next; \
+ if (curelm) { \
+ curelm->field.next = (elm)->field.next; \
+ if ((head)->last == (elm)) \
+ (head)->last = curelm; \
+ } \
+ } \
+ (elm)->field.next = NULL; \
+} while (0)
+
+#define AST_RWLIST_REMOVE AST_LIST_REMOVE
+
+/* chanvars.h */
+
+struct ast_var_t {
+ AST_LIST_ENTRY(ast_var_t) entries;
+ char *value;
+ char name[0];
+};
+
+AST_LIST_HEAD_NOLOCK(varshead, ast_var_t);
+
+AST_RWLOCK_DEFINE_STATIC(globalslock);
+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); */
+
+static struct ast_var_t *ast_var_assign(const char *name, const char *value);
+
+static void ast_var_delete(struct ast_var_t *var);
+
+/*from channel.h */
+#define AST_MAX_EXTENSION 80 /*!< Max length of an extension */
+
+
+/* from pbx.h */
+#define PRIORITY_HINT -1 /*!< Special Priority for a hint */
+
+enum ast_extension_states {
+ AST_EXTENSION_REMOVED = -2, /*!< Extension removed */
+ AST_EXTENSION_DEACTIVATED = -1, /*!< Extension hint removed */
+ AST_EXTENSION_NOT_INUSE = 0, /*!< No device INUSE or BUSY */
+ AST_EXTENSION_INUSE = 1 << 0, /*!< One or more devices INUSE */
+ AST_EXTENSION_BUSY = 1 << 1, /*!< All devices BUSY */
+ AST_EXTENSION_UNAVAILABLE = 1 << 2, /*!< All devices UNAVAILABLE/UNREGISTERED */
+ AST_EXTENSION_RINGING = 1 << 3, /*!< All devices RINGING */
+ AST_EXTENSION_ONHOLD = 1 << 4, /*!< All devices ONHOLD */
+};
+
+struct ast_custom_function {
+ const char *name; /*!< Name */
+ const char *synopsis; /*!< Short description for "show functions" */
+ const char *desc; /*!< Help text that explains it all */
+ const char *syntax; /*!< Syntax description */
+ int (*read)(struct ast_channel *, const char *, char *, char *, size_t); /*!< Read function, if read is supported */
+ int (*write)(struct ast_channel *, const char *, char *, const char *); /*!< Write function, if write is supported */
+ AST_RWLIST_ENTRY(ast_custom_function) acflist;
+};
+
+typedef int (ast_switch_f)(struct ast_channel *chan, const char *context,
+ const char *exten, int priority, const char *callerid, const char *data);
+
+struct ast_switch {
+ AST_LIST_ENTRY(ast_switch) list;
+ const char *name; /*!< Name of the switch */
+ const char *description; /*!< Description of the switch */
+
+ ast_switch_f *exists;
+ ast_switch_f *canmatch;
+ ast_switch_f *exec;
+ ast_switch_f *matchmore;
+};
+
+
+static char *config = "extensions.conf";
+static char *registrar = "conf2ael";
+static char userscontext[AST_MAX_EXTENSION] = "default";
+static int static_config = 0;
+static int write_protect_config = 1;
+static int autofallthrough_config = 0;
+static int clearglobalvars_config = 0;
+/*! Go no deeper than this through includes (not counting loops) */
+#define AST_PBX_MAX_STACK 128
+static void pbx_substitute_variables_helper(struct ast_channel *c,const char *cp1,char *cp2,int count);
+
+
+/* taken from strings.h */
+
+static force_inline int ast_strlen_zero(const char *s)
+{
+ return (!s || (*s == '\0'));
+}
+
+#define S_OR(a, b) (!ast_strlen_zero(a) ? (a) : (b))
+
+AST_INLINE_API(
+void ast_copy_string(char *dst, const char *src, size_t size),
+{
+ while (*src && size) {
+ *dst++ = *src++;
+ size--;
+ }
+ if (__builtin_expect(!size, 0))
+ dst--;
+ *dst = '\0';
+}
+)
+
+AST_INLINE_API(
+char *ast_skip_blanks(const char *str),
+{
+ while (*str && *str < 33)
+ str++;
+ return (char *)str;
+}
+)
+
+/*!
+ \brief Trims trailing whitespace characters from a string.
+ \param ast_trim_blanks function being used
+ \param str the input string
+ \return a pointer to the modified string
+ */
+AST_INLINE_API(
+char *ast_trim_blanks(char *str),
+{
+ char *work = str;
+
+ if (work) {
+ work += strlen(work) - 1;
+ /* It's tempting to only want to erase after we exit this loop,
+ but since ast_trim_blanks *could* receive a constant string
+ (which we presumably wouldn't have to touch), we shouldn't
+ actually set anything unless we must, and it's easier just
+ to set each position to \0 than to keep track of a variable
+ for it */
+ while ((work >= str) && *work < 33)
+ *(work--) = '\0';
+ }
+ return str;
+}
+)
+
+/*!
+ \brief Strip leading/trailing whitespace from a string.
+ \param s The string to be stripped (will be modified).
+ \return The stripped string.
+
+ This functions strips all leading and trailing whitespace
+ characters from the input string, and returns a pointer to
+ the resulting string. The string is modified in place.
+*/
+AST_INLINE_API(
+char *ast_strip(char *s),
+{
+ s = ast_skip_blanks(s);
+ if (s)
+ ast_trim_blanks(s);
+ return s;
+}
+)
+
+
+/* stolen from callerid.c */
+
+/*! \brief Clean up phone string
+ * remove '(', ' ', ')', non-trailing '.', and '-' not in square brackets.
+ * Basically, remove anything that could be invalid in a pattern.
+ */
+static void ast_shrink_phone_number(char *n)
+{
+ int x, y=0;
+ int bracketed = 0;
+
+ for (x=0; n[x]; x++) {
+ switch(n[x]) {
+ case '[':
+ bracketed++;
+ n[y++] = n[x];
+ break;
+ case ']':
+ bracketed--;
+ n[y++] = n[x];
+ break;
+ case '-':
+ if (bracketed)
+ n[y++] = n[x];
+ break;
+ case '.':
+ if (!n[x+1])
+ n[y++] = n[x];
+ break;
+ default:
+ if (!strchr("()", n[x]))
+ n[y++] = n[x];
+ }
+ }
+ n[y] = '\0';
+}
+
+
+/* stolen from chanvars.c */
+
+static const char *ast_var_name(const struct ast_var_t *var)
+{
+ const char *name;
+
+ if (var == NULL || (name = var->name) == NULL)
+ return NULL;
+ /* Return the name without the initial underscores */
+ if (name[0] == '_') {
+ name++;
+ if (name[0] == '_')
+ name++;
+ }
+ return name;
+}
+
+
+/* stolen from asterisk.c */
+
+static struct ast_flags ast_options = { AST_DEFAULT_OPTIONS };
+static int option_verbose = 0; /*!< Verbosity level */
+static int option_debug = 0; /*!< Debug level */
+
+
+/* experiment 1: see if it's easier just to use existing config code
+ * to read in the extensions.conf file. In this scenario,
+ I have to rip/copy code from other modules, because they
+ are staticly declared as-is. A solution would be to move
+ the ripped code to another location and make them available
+ to other modules and standalones */
+
+/* Our own version of ast_log, since the expr parser uses it. -- stolen from utils/check_expr.c */
+
+static void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
+{
+ va_list vars;
+ va_start(vars,fmt);
+
+ printf("LOG: lev:%d file:%s line:%d func: %s ",
+ level, file, line, function);
+ vprintf(fmt, vars);
+ fflush(stdout);
+ va_end(vars);
+}
+
+static void ast_verbose(const char *fmt, ...)
+{
+ va_list vars;
+ va_start(vars,fmt);
+
+ printf("VERBOSE: ");
+ vprintf(fmt, vars);
+ fflush(stdout);
+ va_end(vars);
+}
+
+/* stolen from main/utils.c */
+static char *ast_process_quotes_and_slashes(char *start, char find, char replace_with)
+{
+ char *dataPut = start;
+ int inEscape = 0;
+ int inQuotes = 0;
+
+ for (; *start; start++) {
+ if (inEscape) {
+ *dataPut++ = *start; /* Always goes verbatim */
+ inEscape = 0;
+ } else {
+ if (*start == '\\') {
+ inEscape = 1; /* Do not copy \ into the data */
+ } else if (*start == '\'') {
+ inQuotes = 1 - inQuotes; /* Do not copy ' into the data */
+ } else {
+ /* Replace , with |, unless in quotes */
+ *dataPut++ = inQuotes ? *start : ((*start == find) ? replace_with : *start);
+ }
+ }
+ }
+ if (start != dataPut)
+ *dataPut = 0;
+ return dataPut;
+}
+
+static int ast_true(const char *s)
+{
+ if (ast_strlen_zero(s))
+ return 0;
+
+ /* Determine if this is a true value */
+ if (!strcasecmp(s, "yes") ||
+ !strcasecmp(s, "true") ||
+ !strcasecmp(s, "y") ||
+ !strcasecmp(s, "t") ||
+ !strcasecmp(s, "1") ||
+ !strcasecmp(s, "on"))
+ return -1;
+
+ return 0;
+}
+
+/* stolen from pbx.c */
+#define VAR_BUF_SIZE 4096
+
+#define VAR_NORMAL 1
+#define VAR_SOFTTRAN 2
+#define VAR_HARDTRAN 3
+
+#define BACKGROUND_SKIP (1 << 0)
+#define BACKGROUND_NOANSWER (1 << 1)
+#define BACKGROUND_MATCHEXTEN (1 << 2)
+#define BACKGROUND_PLAYBACK (1 << 3)
+
+/*!
+ \brief ast_exten: An extension
+ The dialplan is saved as a linked list with each context
+ having it's own linked list of extensions - one item per
+ priority.
+*/
+struct ast_exten {
+ char *exten; /*!< Extension name */
+ int matchcid; /*!< Match caller id ? */
+ const char *cidmatch; /*!< Caller id to match for this extension */
+ int priority; /*!< Priority */
+ const char *label; /*!< Label */
+ struct ast_context *parent; /*!< The context this extension belongs to */
+ const char *app; /*!< Application to execute */
+ struct ast_app *cached_app; /*!< Cached location of application */
+ void *data; /*!< Data to use (arguments) */
+ void (*datad)(void *); /*!< Data destructor */
+ struct ast_exten *peer; /*!< Next higher priority with our extension */
+ const char *registrar; /*!< Registrar */
+ struct ast_exten *next; /*!< Extension with a greater ID */
+ char stuff[0];
+};
+/* from pbx.h */
+typedef int (*ast_state_cb_type)(char *context, char* id, enum ast_extension_states state, void *data);
+struct ast_timing {
+ int hastime; /*!< If time construct exists */
+ unsigned int monthmask; /*!< Mask for month */
+ unsigned int daymask; /*!< Mask for date */
+ unsigned int dowmask; /*!< Mask for day of week (mon-sun) */
+ unsigned int minmask[24]; /*!< Mask for minute */
+};
+/* end of pbx.h */
+/*! \brief ast_include: include= support in extensions.conf */
+struct ast_include {
+ const char *name;
+ const char *rname; /*!< Context to include */
+ const char *registrar; /*!< Registrar */
+ int hastime; /*!< If time construct exists */
+ struct ast_timing timing; /*!< time construct */
+ struct ast_include *next; /*!< Link them together */
+ char stuff[0];
+};
+
+/*! \brief ast_sw: Switch statement in extensions.conf */
+struct ast_sw {
+ char *name;
+ const char *registrar; /*!< Registrar */
+ char *data; /*!< Data load */
+ int eval;
+ AST_LIST_ENTRY(ast_sw) list;
+ char *tmpdata;
+ char stuff[0];
+};
+
+/*! \brief ast_ignorepat: Ignore patterns in dial plan */
+struct ast_ignorepat {
+ const char *registrar;
+ struct ast_ignorepat *next;
+ const char pattern[0];
+};
+
+/*! \brief ast_context: An extension context */
+struct ast_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 */
+ struct ast_ignorepat *ignorepats; /*!< Patterns for which to continue playing dialtone */
+ const char *registrar; /*!< Registrar */
+ AST_LIST_HEAD_NOLOCK(, ast_sw) alts; /*!< Alternative switches */
+ ast_mutex_t macrolock; /*!< A lock to implement "exclusive" macros - held whilst a call is executing in the macro */
+ char name[0]; /*!< Name of the context */
+};
+
+
+/*! \brief ast_app: A registered application */
+struct ast_app {
+ int (*execute)(struct ast_channel *chan, void *data);
+ const char *synopsis; /*!< Synopsis text for 'show applications' */
+ const char *description; /*!< Description (help text) for 'show application &lt;name&gt;' */
+ AST_RWLIST_ENTRY(ast_app) list; /*!< Next app in list */
+ void *module; /*!< Module this app belongs to */
+ char name[0]; /*!< Name of the application */
+};
+
+
+/*! \brief ast_state_cb: An extension state notify register item */
+struct ast_state_cb {
+ int id;
+ void *data;
+ ast_state_cb_type callback;
+ struct ast_state_cb *next;
+};
+
+/*! \brief Structure for dial plan hints
+
+ \note Hints are pointers from an extension in the dialplan to one or
+ more devices (tech/name)
+ - See \ref AstExtState
+*/
+struct ast_hint {
+ struct ast_exten *exten; /*!< Extension */
+ int laststate; /*!< Last known state */
+ struct ast_state_cb *callbacks; /*!< Callback list for this extension */
+ AST_RWLIST_ENTRY(ast_hint) list;/*!< Pointer to next hint in list */
+};
+
+struct store_hint {
+ char *context;
+ char *exten;
+ struct ast_state_cb *callbacks;
+ int laststate;
+ AST_LIST_ENTRY(store_hint) list;
+ char data[1];
+};
+
+AST_LIST_HEAD(store_hints, store_hint);
+
+static const struct cfextension_states {
+ int extension_state;
+ const char * const text;
+} extension_states[] = {
+ { AST_EXTENSION_NOT_INUSE, "Idle" },
+ { AST_EXTENSION_INUSE, "InUse" },
+ { AST_EXTENSION_BUSY, "Busy" },
+ { AST_EXTENSION_UNAVAILABLE, "Unavailable" },
+ { AST_EXTENSION_RINGING, "Ringing" },
+ { AST_EXTENSION_INUSE | AST_EXTENSION_RINGING, "InUse&Ringing" },
+ { AST_EXTENSION_ONHOLD, "Hold" },
+ { AST_EXTENSION_INUSE | AST_EXTENSION_ONHOLD, "InUse&Hold" }
+};
+#define STATUS_NO_CONTEXT 1
+#define STATUS_NO_EXTENSION 2
+#define STATUS_NO_PRIORITY 3
+#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_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);
+}
+
+
+/* chopped this one off at the knees! */
+static int ast_func_write(struct ast_channel *chan, const char *function, const char *value)
+{
+
+ /* ast_log(LOG_ERROR, "Function %s not registered\n", function); we are not interested in the details here */
+
+ 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);
+ }
+
+}
+
+static int pbx_builtin_setvar(struct ast_channel *chan, void *data)
+{
+ char *name, *value, *mydata;
+ int argc;
+ char *argv[24]; /* this will only support a maximum of 24 variables being set in a single operation */
+ int global = 0;
+ int x;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Set requires at least one variable name/value pair.\n");
+ return 0;
+ }
+
+ mydata = ast_strdupa(data);
+ argc = ast_app_separate_args(mydata, '|', argv, sizeof(argv) / sizeof(argv[0]));
+
+ /* check for a trailing flags argument */
+ if ((argc > 1) && !strchr(argv[argc-1], '=')) {
+ argc--;
+ if (strchr(argv[argc], 'g'))
+ global = 1;
+ }
+
+ for (x = 0; x < argc; x++) {
+ name = argv[x];
+ if ((value = strchr(name, '='))) {
+ *value++ = '\0';
+ pbx_builtin_setvar_helper((global) ? NULL : chan, name, value);
+ } else
+ ast_log(LOG_WARNING, "Ignoring entry '%s' with no = (and not last 'options' entry)\n", name);
+ }
+
+ return(0);
+}
+
+int localized_pbx_builtin_setvar(struct ast_channel *chan, void *data);
+
+int localized_pbx_builtin_setvar(struct ast_channel *chan, void *data)
+{
+ return pbx_builtin_setvar(chan, data);
+}
+
+
+/*! \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;
+}
+
+struct ast_category *localized_category_get(const struct ast_config *config, const char *category_name);
+
+struct ast_category *localized_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++;
+ /* silence is golden!
+ 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);
+ /* silence is golden
+ 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, "EXTENSIONS.CONF: No '=' (equal sign) in line %d of %s\n", lineno, configfile);
+ }
+ }
+ return 0;
+}
+
+static int use_local_dir = 1;
+
+void localized_use_local_dir(void);
+void localized_use_conf_dir(void);
+
+void localized_use_local_dir(void)
+{
+ use_local_dir = 1;
+}
+
+void localized_use_conf_dir(void)
+{
+ use_local_dir = 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 {
+ if (use_local_dir)
+ snprintf(fn, sizeof(fn), "./%s", filename);
+ else
+ snprintf(fn, sizeof(fn), "%s/%s", (char *)ast_config_AST_CONFIG_DIR, filename);
+ }
+
+ if (withcomments && cfg && cfg->include_level < 2 ) {
+ CB_INIT();
+ }
+
+#ifdef AST_INCLUDE_GLOB
+ {
+ int glob_ret;
+ glob_t globbuf;
+
+ 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
+ if (cfg && cfg->include_level == 1 && withcomments && comment_buffer) {
+ if (comment_buffer) {
+ 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;
+
+ 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;
+}
+
+struct ast_config *localized_config_load(const char *filename);
+
+struct ast_config *localized_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;
+}
+
+struct ast_config *localized_config_load_with_comments(const char *filename);
+
+struct ast_config *localized_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;
+}
+
+
+
+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;
+}
+
+int localized_config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator);
+
+int localized_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;
+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 */
+ return 0x0000 | (c & 0xff);
+
+ case 'N': /* 2..9 */
+ return 0x0700 | '2' ;
+
+ case 'X': /* 0..9 */
+ return 0x0900 | '0';
+
+ case 'Z': /* 1..9 */
+ return 0x0800 | '1';
+
+ case '.': /* wildcard */
+ return 0x10000;
+
+ case '!': /* earlymatch */
+ return 0x20000; /* less specific than NULL */
+
+ case '\0': /* empty string */
+ *p = NULL;
+ return 0x30000;
+
+ case '[': /* pattern */
+ break;
+ }
+ /* locate end of set */
+ end = strchr(*p, ']');
+
+ if (end == NULL) {
+ ast_log(LOG_WARNING, "Wrong usage of [] in the extension\n");
+ return 0x40000; /* XXX make this entry go last... */
+ }
+
+ bzero(chars, sizeof(chars)); /* clear all chars in the set */
+ for (; *p < end ; (*p)++) {
+ unsigned char c1, c2; /* first-last char in range */
+ c1 = (unsigned char)((*p)[0]);
+ if (*p + 2 < end && (*p)[1] == '-') { /* this is a range */
+ c2 = (unsigned char)((*p)[2]);
+ *p += 2; /* skip a total of 3 chars */
+ } else /* individual character */
+ c2 = c1;
+ if (c1 < cmin)
+ cmin = c1;
+ for (; c1 <= c2; c1++) {
+ uint32_t mask = 1 << (c1 % 32);
+ if ( (chars[ c1 / 32 ] & mask) == 0)
+ count += 0x100;
+ chars[ c1 / 32 ] |= mask;
+ }
+ }
+ (*p)++;
+ return count == 0 ? 0x30000 : (count | cmin);
+}
+
+/*!
+ * \brief the full routine to compare extensions in rules.
+ */
+static int ext_cmp(const char *a, const char *b)
+{
+ /* make sure non-patterns come first.
+ * If a is not a pattern, it either comes first or
+ * we use strcmp to compare the strings.
+ */
+ int ret = 0;
+
+ if (a[0] != '_')
+ return (b[0] == '_') ? -1 : strcmp(a, b);
+
+ /* Now we know a is a pattern; if b is not, a comes first */
+ if (b[0] != '_')
+ return 1;
+#if 0 /* old mode for ext matching */
+ return strcmp(a, b);
+#endif
+ /* ok we need full pattern sorting routine */
+ while (!ret && a && b)
+ ret = ext_cmp1(&a) - ext_cmp1(&b);
+ if (ret == 0)
+ return 0;
+ else
+ return (ret > 0) ? 1 : -1;
+}
+
+/*! \brief copy a string skipping whitespace */
+static int ext_strncpy(char *dst, const char *src, int len)
+{
+ int count=0;
+
+ while (*src && (count < len - 1)) {
+ switch(*src) {
+ case ' ':
+ /* otherwise exten => [a-b],1,... doesn't work */
+ /* case '-': */
+ /* Ignore */
+ break;
+ default:
+ *dst = *src;
+ dst++;
+ }
+ src++;
+ count++;
+ }
+ *dst = '\0';
+
+ return count;
+}
+
+/*
+ * Wrapper around _extension_match_core() to do performance measurement
+ * using the profiling code.
+ */
+static int ast_check_timing(const struct ast_timing *i)
+{
+ struct tm tm;
+ time_t t = time(NULL);
+
+ localtime_r(&t,&tm);
+
+ /* If it's not the right month, return */
+ if (!(i->monthmask & (1 << tm.tm_mon)))
+ return 0;
+
+ /* If it's not that time of the month.... */
+ /* Warning, tm_mday has range 1..31! */
+ if (!(i->daymask & (1 << (tm.tm_mday-1))))
+ return 0;
+
+ /* If it's not the right day of the week */
+ if (!(i->dowmask & (1 << tm.tm_wday)))
+ return 0;
+
+ /* Sanity check the hour just to be safe */
+ if ((tm.tm_hour < 0) || (tm.tm_hour > 23)) {
+ ast_log(LOG_WARNING, "Insane time...\n");
+ return 0;
+ }
+
+ /* Now the tough part, we calculate if it fits
+ in the right time based on min/hour */
+ if (!(i->minmask[tm.tm_hour] & (1 << (tm.tm_min / 2))))
+ return 0;
+
+ /* If we got this far, then we're good */
+ return 1;
+}
+
+#ifdef NOT_ANYMORE
+static struct ast_switch *pbx_findswitch(const char *sw)
+{
+ struct ast_switch *asw;
+
+ AST_RWLIST_TRAVERSE(&switches, asw, list) {
+ if (!strcasecmp(asw->name, sw))
+ break;
+ }
+
+ return asw;
+}
+#endif
+
+
+static struct ast_context *ast_walk_contexts(struct ast_context *con);
+
+static struct ast_context *ast_walk_contexts(struct ast_context *con)
+{
+ return con ? con->next : contexts;
+}
+
+struct ast_context *localized_walk_contexts(struct ast_context *con);
+struct ast_context *localized_walk_contexts(struct ast_context *con)
+{
+ return ast_walk_contexts(con);
+}
+
+
+
+static struct ast_exten *ast_walk_context_extensions(struct ast_context *con,
+ struct ast_exten *exten);
+
+static struct ast_exten *ast_walk_context_extensions(struct ast_context *con,
+ struct ast_exten *exten)
+{
+ if (!exten)
+ return con ? con->root : NULL;
+ else
+ return exten->next;
+}
+
+struct ast_exten *localized_walk_context_extensions(struct ast_context *con,
+ struct ast_exten *exten);
+struct ast_exten *localized_walk_context_extensions(struct ast_context *con,
+ struct ast_exten *exten)
+{
+ return ast_walk_context_extensions(con,exten);
+}
+
+
+static struct ast_exten *ast_walk_extension_priorities(struct ast_exten *exten,
+ struct ast_exten *priority);
+
+static struct ast_exten *ast_walk_extension_priorities(struct ast_exten *exten,
+ struct ast_exten *priority)
+{
+ return priority ? priority->peer : exten;
+}
+
+struct ast_exten *localized_walk_extension_priorities(struct ast_exten *exten,
+ struct ast_exten *priority);
+struct ast_exten *localized_walk_extension_priorities(struct ast_exten *exten,
+ struct ast_exten *priority)
+{
+ return ast_walk_extension_priorities(exten, priority);
+}
+
+
+
+static struct ast_include *ast_walk_context_includes(struct ast_context *con,
+ struct ast_include *inc);
+
+static struct ast_include *ast_walk_context_includes(struct ast_context *con,
+ struct ast_include *inc)
+{
+ if (!inc)
+ return con ? con->includes : NULL;
+ else
+ return inc->next;
+}
+
+struct ast_include *localized_walk_context_includes(struct ast_context *con,
+ struct ast_include *inc);
+struct ast_include *localized_walk_context_includes(struct ast_context *con,
+ struct ast_include *inc)
+{
+ return ast_walk_context_includes(con, inc);
+}
+
+
+static struct ast_sw *ast_walk_context_switches(struct ast_context *con,
+ struct ast_sw *sw);
+
+static struct ast_sw *ast_walk_context_switches(struct ast_context *con,
+ struct ast_sw *sw)
+{
+ if (!sw)
+ return con ? AST_LIST_FIRST(&con->alts) : NULL;
+ else
+ return AST_LIST_NEXT(sw, list);
+}
+
+struct ast_sw *localized_walk_context_switches(struct ast_context *con,
+ struct ast_sw *sw);
+struct ast_sw *localized_walk_context_switches(struct ast_context *con,
+ struct ast_sw *sw)
+{
+ return ast_walk_context_switches(con, sw);
+}
+
+
+static struct ast_context *ast_context_find(const char *name);
+
+static struct ast_context *ast_context_find(const char *name)
+{
+ struct ast_context *tmp = NULL;
+ while ( (tmp = ast_walk_contexts(tmp)) ) {
+ if (!name || !strcasecmp(name, tmp->name))
+ break;
+ }
+ return tmp;
+}
+
+/* request and result for pbx_find_extension */
+struct pbx_find_info {
+#if 0
+ const char *context;
+ const char *exten;
+ int priority;
+#endif
+
+ char *incstack[AST_PBX_MAX_STACK]; /* filled during the search */
+ int stacklen; /* modified during the search */
+ int status; /* set on return */
+ struct ast_switch *swo; /* set on return */
+ const char *data; /* set on return */
+ const char *foundcontext; /* set on return */
+};
+
+/*
+ * Internal function for ast_extension_{match|close}
+ * return 0 on no-match, 1 on match, 2 on early match.
+ * mode is as follows:
+ * E_MATCH success only on exact match
+ * E_MATCHMORE success only on partial match (i.e. leftover digits in pattern)
+ * E_CANMATCH either of the above.
+ */
+
+static int _extension_match_core(const char *pattern, const char *data, enum ext_match_t mode)
+{
+ mode &= E_MATCH_MASK; /* only consider the relevant bits */
+
+ if ( (mode == E_MATCH) && (pattern[0] == '_') && (strcasecmp(pattern,data)==0) ) /* note: if this test is left out, then _x. will not match _x. !!! */
+ return 1;
+
+ if (pattern[0] != '_') { /* not a pattern, try exact or partial match */
+ int ld = strlen(data), lp = strlen(pattern);
+
+ if (lp < ld) /* pattern too short, cannot match */
+ return 0;
+ /* depending on the mode, accept full or partial match or both */
+ if (mode == E_MATCH)
+ return !strcmp(pattern, data); /* 1 on match, 0 on fail */
+ if (ld == 0 || !strncasecmp(pattern, data, ld)) /* partial or full match */
+ return (mode == E_MATCHMORE) ? lp > ld : 1; /* XXX should consider '!' and '/' ? */
+ else
+ return 0;
+ }
+ pattern++; /* skip leading _ */
+ /*
+ * XXX below we stop at '/' which is a separator for the CID info. However we should
+ * not store '/' in the pattern at all. When we insure it, we can remove the checks.
+ */
+ while (*data && *pattern && *pattern != '/') {
+ const char *end;
+
+ if (*data == '-') { /* skip '-' in data (just a separator) */
+ data++;
+ continue;
+ }
+ switch (toupper(*pattern)) {
+ case '[': /* a range */
+ end = strchr(pattern+1, ']'); /* XXX should deal with escapes ? */
+ if (end == NULL) {
+ ast_log(LOG_WARNING, "Wrong usage of [] in the extension\n");
+ return 0; /* unconditional failure */
+ }
+ for (pattern++; pattern != end; pattern++) {
+ if (pattern+2 < end && pattern[1] == '-') { /* this is a range */
+ if (*data >= pattern[0] && *data <= pattern[2])
+ break; /* match found */
+ else {
+ pattern += 2; /* skip a total of 3 chars */
+ continue;
+ }
+ } else if (*data == pattern[0])
+ break; /* match found */
+ }
+ if (pattern == end)
+ return 0;
+ pattern = end; /* skip and continue */
+ break;
+ case 'N':
+ if (*data < '2' || *data > '9')
+ return 0;
+ break;
+ case 'X':
+ if (*data < '0' || *data > '9')
+ return 0;
+ break;
+ case 'Z':
+ if (*data < '1' || *data > '9')
+ return 0;
+ break;
+ case '.': /* Must match, even with more digits */
+ return 1;
+ case '!': /* Early match */
+ return 2;
+ case ' ':
+ case '-': /* Ignore these in patterns */
+ data--; /* compensate the final data++ */
+ break;
+ default:
+ if (*data != *pattern)
+ return 0;
+ }
+ data++;
+ pattern++;
+ }
+ if (*data) /* data longer than pattern, no match */
+ return 0;
+ /*
+ * match so far, but ran off the end of the data.
+ * Depending on what is next, determine match or not.
+ */
+ if (*pattern == '\0' || *pattern == '/') /* exact match */
+ return (mode == E_MATCHMORE) ? 0 : 1; /* this is a failure for E_MATCHMORE */
+ else if (*pattern == '!') /* early match */
+ return 2;
+ else /* partial match */
+ return (mode == E_MATCH) ? 0 : 1; /* this is a failure for E_MATCH */
+}
+
+static int extension_match_core(const char *pattern, const char *data, enum ext_match_t mode)
+{
+ int i;
+ i = _extension_match_core(pattern, data, mode);
+ return i;
+}
+
+static int ast_extension_match(const char *pattern, const char *data);
+
+static int ast_extension_match(const char *pattern, const char *data)
+{
+ return extension_match_core(pattern, data, E_MATCH);
+}
+
+static int matchcid(const char *cidpattern, const char *callerid)
+{
+ /* If the Caller*ID pattern is empty, then we're matching NO Caller*ID, so
+ failing to get a number should count as a match, otherwise not */
+
+ if (ast_strlen_zero(callerid))
+ return ast_strlen_zero(cidpattern) ? 1 : 0;
+
+ return ast_extension_match(cidpattern, callerid);
+}
+
+static inline int include_valid(struct ast_include *i)
+{
+ if (!i->hastime)
+ return 1;
+
+ return ast_check_timing(&(i->timing));
+}
+
+
+
+static struct ast_exten *pbx_find_extension(struct ast_channel *chan,
+ struct ast_context *bypass,
+ struct pbx_find_info *q,
+ const char *context,
+ const char *exten,
+ int priority,
+ const char *label,
+ const char *callerid,
+ enum ext_match_t action);
+
+
+static struct ast_exten *pbx_find_extension(struct ast_channel *chan,
+ struct ast_context *bypass,
+ struct pbx_find_info *q,
+ const char *context,
+ const char *exten,
+ int priority,
+ const char *label,
+ const char *callerid,
+ enum ext_match_t action)
+{
+ int x;
+ struct ast_context *tmp;
+ struct ast_exten *e, *eroot;
+ struct ast_include *i;
+
+ /* Initialize status if appropriate */
+ if (q->stacklen == 0) {
+ q->status = STATUS_NO_CONTEXT;
+ q->swo = NULL;
+ q->data = NULL;
+ q->foundcontext = NULL;
+ } else if (q->stacklen >= AST_PBX_MAX_STACK) {
+ ast_log(LOG_WARNING, "Maximum PBX stack exceeded\n");
+ return NULL;
+ }
+ /* Check first to see if we've already been checked */
+ for (x = 0; x < q->stacklen; x++) {
+ if (!strcasecmp(q->incstack[x], context))
+ return NULL;
+ }
+ if (bypass) /* bypass means we only look there */
+ tmp = bypass;
+ else { /* look in contexts */
+ tmp = NULL;
+ while ((tmp = ast_walk_contexts(tmp)) ) {
+ if (!strcmp(tmp->name, context))
+ break;
+ }
+ if (!tmp)
+ return NULL;
+ }
+ if (q->status < STATUS_NO_EXTENSION)
+ q->status = STATUS_NO_EXTENSION;
+
+ /* scan the list trying to match extension and CID */
+ eroot = NULL;
+ while ( (eroot = ast_walk_context_extensions(tmp, eroot)) ) {
+ int match = extension_match_core(eroot->exten, exten, action);
+ /* 0 on fail, 1 on match, 2 on earlymatch */
+
+ if (!match || (eroot->matchcid && !matchcid(eroot->cidmatch, callerid)))
+ continue; /* keep trying */
+ if (match == 2 && action == E_MATCHMORE) {
+ /* We match an extension ending in '!'.
+ * The decision in this case is final and is NULL (no match).
+ */
+ return NULL;
+ }
+ /* found entry, now look for the right priority */
+ if (q->status < STATUS_NO_PRIORITY)
+ q->status = STATUS_NO_PRIORITY;
+ e = NULL;
+ while ( (e = ast_walk_extension_priorities(eroot, e)) ) {
+ /* Match label or priority */
+ if (action == E_FINDLABEL) {
+ if (q->status < STATUS_NO_LABEL)
+ q->status = STATUS_NO_LABEL;
+ if (label && e->label && !strcmp(label, e->label))
+ break; /* found it */
+ } else if (e->priority == priority) {
+ break; /* found it */
+ } /* else keep searching */
+ }
+ if (e) { /* found a valid match */
+ q->status = STATUS_SUCCESS;
+ q->foundcontext = context;
+ return e;
+ }
+ }
+#ifdef NOT_RIGHT_NOW
+ /* Check alternative switches??? */
+ AST_LIST_TRAVERSE(&tmp->alts, sw, list) {
+ struct ast_switch *asw = pbx_findswitch(sw->name);
+ ast_switch_f *aswf = NULL;
+ char *datap;
+
+ if (!asw) {
+ ast_log(LOG_WARNING, "No such switch '%s'\n", sw->name);
+ continue;
+ }
+ /* No need to Substitute variables now; we shouldn't be here if there's any */
+
+ /* equivalent of extension_match_core() at the switch level */
+ if (action == E_CANMATCH)
+ aswf = asw->canmatch;
+ else if (action == E_MATCHMORE)
+ aswf = asw->matchmore;
+ else /* action == E_MATCH */
+ aswf = asw->exists;
+ datap = sw->eval ? sw->tmpdata : sw->data;
+ res = !aswf ? 0 : aswf(chan, context, exten, priority, callerid, datap);
+ if (res) { /* Got a match */
+ q->swo = asw;
+ q->data = datap;
+ q->foundcontext = context;
+ /* XXX keep status = STATUS_NO_CONTEXT ? */
+ return NULL;
+ }
+ }
+#endif
+ q->incstack[q->stacklen++] = tmp->name; /* Setup the stack */
+ /* Now try any includes we have in this context */
+ for (i = tmp->includes; i; i = i->next) {
+ if (include_valid(i)) {
+ if ((e = pbx_find_extension(NULL, bypass, q, i->rname, exten, priority, label, callerid, action)))
+ return e;
+ if (q->swo)
+ return NULL;
+ }
+ }
+ return NULL;
+}
+
+struct ast_exten *localized_find_extension(struct ast_context *bypass,
+ struct pbx_find_info *q,
+ const char *context,
+ const char *exten,
+ int priority,
+ const char *label,
+ const char *callerid,
+ enum ext_match_t action);
+
+struct ast_exten *localized_find_extension(struct ast_context *bypass,
+ struct pbx_find_info *q,
+ const char *context,
+ const char *exten,
+ int priority,
+ const char *label,
+ const char *callerid,
+ enum ext_match_t action)
+{
+ return pbx_find_extension(NULL, bypass, q, context, exten, priority, label, callerid, action);
+}
+
+
+static struct ast_context *contexts;
+AST_RWLOCK_DEFINE_STATIC(conlock); /*!< Lock for the ast_context list */
+
+static const char *ast_get_context_name(struct ast_context *con);
+
+static const char *ast_get_context_name(struct ast_context *con)
+{
+ return con ? con->name : NULL;
+}
+
+/*
+ * errno values
+ * ENOMEM - out of memory
+ * EBUSY - can't lock
+ * EEXIST - already included
+ * EINVAL - there is no existence of context for inclusion
+ */
+static int ast_context_add_include2(struct ast_context *con, const char *value,
+ const char *registrar);
+
+static int ast_context_add_include2(struct ast_context *con, const char *value,
+ const char *registrar)
+{
+ struct ast_include *new_include;
+ char *c;
+ struct ast_include *i, *il = NULL; /* include, include_last */
+ int length;
+ char *p;
+
+ length = sizeof(struct ast_include);
+ length += 2 * (strlen(value) + 1);
+
+ /* allocate new include structure ... */
+ if (!(new_include = ast_calloc(1, length)))
+ return -1;
+ /* Fill in this structure. Use 'p' for assignments, as the fields
+ * in the structure are 'const char *'
+ */
+ p = new_include->stuff;
+ new_include->name = p;
+ strcpy(p, value);
+ p += strlen(value) + 1;
+ new_include->rname = p;
+ strcpy(p, value);
+ /* Strip off timing info, and process if it is there */
+ if ( (c = strchr(p, '|')) ) {
+ *c++ = '\0';
+ new_include->hastime = ast_build_timing(&(new_include->timing), c);
+ }
+ new_include->next = NULL;
+ new_include->registrar = registrar;
+
+
+ /* ... go to last include and check if context is already included too... */
+ for (i = con->includes; i; i = i->next) {
+ if (!strcasecmp(i->name, new_include->name)) {
+ free(new_include);
+ errno = EEXIST;
+ return -1;
+ }
+ il = i;
+ }
+
+ /* ... include new context into context list, unlock, return */
+ if (il)
+ il->next = new_include;
+ else
+ con->includes = new_include;
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Including context '%s' in context '%s'\n", new_include->name, ast_get_context_name(con));
+
+ return 0;
+}
+
+int localized_context_add_include2(struct ast_context *con, const char *value,
+ const char *registrar);
+int localized_context_add_include2(struct ast_context *con, const char *value,
+ const char *registrar)
+{
+ return ast_context_add_include2(con, value, registrar);
+}
+
+
+
+static int ast_context_add_ignorepat2(struct ast_context *con, const char *value, const char *registrar);
+
+static int ast_context_add_ignorepat2(struct ast_context *con, const char *value, const char *registrar)
+{
+ struct ast_ignorepat *ignorepat, *ignorepatc, *ignorepatl = NULL;
+ int length;
+ length = sizeof(struct ast_ignorepat);
+ length += strlen(value) + 1;
+ if (!(ignorepat = ast_calloc(1, length)))
+ return -1;
+ /* The cast to char * is because we need to write the initial value.
+ * The field is not supposed to be modified otherwise
+ */
+ strcpy((char *)ignorepat->pattern, value);
+ ignorepat->next = NULL;
+ ignorepat->registrar = registrar;
+ for (ignorepatc = con->ignorepats; ignorepatc; ignorepatc = ignorepatc->next) {
+ ignorepatl = ignorepatc;
+ if (!strcasecmp(ignorepatc->pattern, value)) {
+ /* Already there */
+ errno = EEXIST;
+ return -1;
+ }
+ }
+ if (ignorepatl)
+ ignorepatl->next = ignorepat;
+ else
+ con->ignorepats = ignorepat;
+ return 0;
+
+}
+
+int localized_context_add_ignorepat2(struct ast_context *con, const char *value, const char *registrar);
+
+int localized_context_add_ignorepat2(struct ast_context *con, const char *value, const char *registrar)
+{
+ return ast_context_add_ignorepat2(con, value, registrar);
+}
+
+
+/*
+ * Lock context list functions ...
+ */
+
+static int ast_wrlock_contexts(void)
+{
+ return ast_rwlock_wrlock(&conlock);
+}
+
+static int ast_unlock_contexts(void)
+{
+ return ast_rwlock_unlock(&conlock);
+}
+
+static int ast_wrlock_context(struct ast_context *con)
+{
+ return ast_rwlock_wrlock(&con->lock);
+}
+
+static int ast_unlock_context(struct ast_context *con)
+{
+ return ast_rwlock_unlock(&con->lock);
+}
+
+/*
+ * errno values
+ * ENOMEM - out of memory
+ * EBUSY - can't lock
+ * EEXIST - already included
+ * EINVAL - there is no existence of context for inclusion
+ */
+static int ast_context_add_switch2(struct ast_context *con, const char *value,
+ const char *data, int eval, const char *registrar);
+
+static int ast_context_add_switch2(struct ast_context *con, const char *value,
+ const char *data, int eval, const char *registrar)
+{
+ struct ast_sw *new_sw;
+ struct ast_sw *i;
+ int length;
+ char *p;
+
+ length = sizeof(struct ast_sw);
+ length += strlen(value) + 1;
+ if (data)
+ length += strlen(data);
+ length++;
+ if (eval) {
+ /* Create buffer for evaluation of variables */
+ length += SWITCH_DATA_LENGTH;
+ length++;
+ }
+
+ /* allocate new sw structure ... */
+ if (!(new_sw = ast_calloc(1, length)))
+ return -1;
+ /* ... fill in this structure ... */
+ p = new_sw->stuff;
+ new_sw->name = p;
+ strcpy(new_sw->name, value);
+ p += strlen(value) + 1;
+ new_sw->data = p;
+ if (data) {
+ strcpy(new_sw->data, data);
+ p += strlen(data) + 1;
+ } else {
+ strcpy(new_sw->data, "");
+ p++;
+ }
+ if (eval)
+ new_sw->tmpdata = p;
+ new_sw->eval = eval;
+ new_sw->registrar = registrar;
+
+ /* ... go to last sw and check if context is already swd too... */
+ AST_LIST_TRAVERSE(&con->alts, i, list) {
+ if (!strcasecmp(i->name, new_sw->name) && !strcasecmp(i->data, new_sw->data)) {
+ free(new_sw);
+ errno = EEXIST;
+ return -1;
+ }
+ }
+
+ /* ... sw new context into context list, unlock, return */
+ AST_LIST_INSERT_TAIL(&con->alts, new_sw, list);
+
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Including switch '%s/%s' in context '%s'\n", new_sw->name, new_sw->data, ast_get_context_name(con));
+
+ return 0;
+}
+
+int localized_context_add_switch2(struct ast_context *con, const char *value,
+ const char *data, int eval, const char *registrar);
+
+int localized_context_add_switch2(struct ast_context *con, const char *value,
+ const char *data, int eval, const char *registrar)
+{
+ return ast_context_add_switch2(con, value, data, eval, registrar);
+}
+
+static struct ast_context *__ast_context_create(struct ast_context **extcontexts, const char *name, const char *registrar, int existsokay)
+{
+ struct ast_context *tmp, **local_contexts;
+ int length = sizeof(struct ast_context) + strlen(name) + 1;
+
+ if (!extcontexts) {
+ ast_wrlock_contexts();
+ local_contexts = &contexts;
+ } else
+ local_contexts = extcontexts;
+
+ for (tmp = *local_contexts; tmp; tmp = tmp->next) {
+ if (!strcasecmp(tmp->name, name)) {
+ if (!existsokay) {
+ ast_log(LOG_WARNING, "Tried to register context '%s', already in use\n", name);
+ tmp = NULL;
+ }
+ if (!extcontexts)
+ ast_unlock_contexts();
+ return tmp;
+ }
+ }
+ if ((tmp = ast_calloc(1, length))) {
+ ast_rwlock_init(&tmp->lock);
+ ast_mutex_init(&tmp->macrolock);
+ strcpy(tmp->name, name);
+ tmp->root = NULL;
+ tmp->registrar = registrar;
+ tmp->next = *local_contexts;
+ tmp->includes = NULL;
+ tmp->ignorepats = NULL;
+ *local_contexts = tmp;
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Registered context '%s'\n", tmp->name);
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "Registered extension context '%s'\n", tmp->name);
+ }
+
+ if (!extcontexts)
+ ast_unlock_contexts();
+ return tmp;
+}
+
+/*! \brief
+ * Main interface to add extensions to the list for out context.
+ *
+ * We sort extensions in order of matching preference, so that we can
+ * stop the search as soon as we find a suitable match.
+ * This ordering also takes care of wildcards such as '.' (meaning
+ * "one or more of any character") and '!' (which is 'earlymatch',
+ * meaning "zero or more of any character" but also impacts the
+ * return value from CANMATCH and EARLYMATCH.
+ *
+ * The extension match rules defined in the devmeeting 2006.05.05 are
+ * quite simple: WE SELECT THE LONGEST MATCH.
+ * In detail, "longest" means the number of matched characters in
+ * the extension. In case of ties (e.g. _XXX and 333) in the length
+ * of a pattern, we give priority to entries with the smallest cardinality
+ * (e.g, [5-9] comes before [2-8] before the former has only 5 elements,
+ * while the latter has 7, etc.
+ * In case of same cardinality, the first element in the range counts.
+ * If we still have a tie, any final '!' will make this as a possibly
+ * less specific pattern.
+ *
+ * EBUSY - can't lock
+ * EEXIST - extension with the same priority exist and no replace is set
+ *
+ */
+static int ast_add_extension2(struct ast_context *con,
+ int replace, const char *extension, int priority, const char *label, const char *callerid,
+ const char *application, void *data, void (*datad)(void *),
+ const char *registrar)
+{
+ /*
+ * Sort extensions (or patterns) according to the rules indicated above.
+ * These are implemented by the function ext_cmp()).
+ * All priorities for the same ext/pattern/cid are kept in a list,
+ * using the 'peer' field as a link field..
+ */
+ struct ast_exten *tmp, *e, *el = NULL;
+ int res;
+ int length;
+ char *p;
+
+ /* if we are adding a hint, and there are global variables, and the hint
+ contains variable references, then expand them --- NOT In this situation!!!
+ */
+
+ length = sizeof(struct ast_exten);
+ length += strlen(extension) + 1;
+ length += strlen(application) + 1;
+ if (label)
+ length += strlen(label) + 1;
+ if (callerid)
+ length += strlen(callerid) + 1;
+ else
+ length ++; /* just the '\0' */
+
+ /* Be optimistic: Build the extension structure first */
+ if (datad == NULL)
+ datad = null_datad;
+ if (!(tmp = ast_calloc(1, length)))
+ return -1;
+
+ /* use p as dst in assignments, as the fields are const char * */
+ p = tmp->stuff;
+ if (label) {
+ tmp->label = p;
+ strcpy(p, label);
+ p += strlen(label) + 1;
+ }
+ tmp->exten = p;
+ p += ext_strncpy(p, extension, strlen(extension) + 1) + 1;
+ tmp->priority = priority;
+ tmp->cidmatch = p; /* but use p for assignments below */
+ if (callerid) {
+ p += ext_strncpy(p, callerid, strlen(callerid) + 1) + 1;
+ tmp->matchcid = 1;
+ } else {
+ *p++ = '\0';
+ tmp->matchcid = 0;
+ }
+ tmp->app = p;
+ strcpy(p, application);
+ tmp->parent = con;
+ tmp->data = data;
+ tmp->datad = datad;
+ tmp->registrar = registrar;
+
+ res = 0; /* some compilers will think it is uninitialized otherwise */
+ for (e = con->root; e; el = e, e = e->next) { /* scan the extension list */
+ res = ext_cmp(e->exten, extension);
+ if (res == 0) { /* extension match, now look at cidmatch */
+ if (!e->matchcid && !tmp->matchcid)
+ res = 0;
+ else if (tmp->matchcid && !e->matchcid)
+ res = 1;
+ else if (e->matchcid && !tmp->matchcid)
+ res = -1;
+ else
+ res = strcasecmp(e->cidmatch, tmp->cidmatch);
+ }
+ if (res >= 0)
+ break;
+ }
+ if (e && res == 0) { /* exact match, insert in the pri chain */
+ res = add_pri(con, tmp, el, e, replace);
+ if (res < 0) {
+ errno = EEXIST; /* XXX do we care ? */
+ return 0; /* XXX should we return -1 maybe ? */
+ }
+ } else {
+ /*
+ * not an exact match, this is the first entry with this pattern,
+ * so insert in the main list right before 'e' (if any)
+ */
+ tmp->next = e;
+ if (el)
+ el->next = tmp;
+ else
+ con->root = tmp;
+ if (tmp->priority == PRIORITY_HINT)
+ ast_add_hint(tmp);
+ }
+ if (option_debug) {
+ if (tmp->matchcid) {
+ ast_log(LOG_DEBUG, "Added extension '%s' priority %d (CID match '%s') to %s\n",
+ tmp->exten, tmp->priority, tmp->cidmatch, con->name);
+ } else {
+ ast_log(LOG_DEBUG, "Added extension '%s' priority %d to %s\n",
+ tmp->exten, tmp->priority, con->name);
+ }
+ }
+ if (option_verbose > 2) {
+ if (tmp->matchcid) {
+ ast_verbose( VERBOSE_PREFIX_3 "Added extension '%s' priority %d (CID match '%s')to %s\n",
+ tmp->exten, tmp->priority, tmp->cidmatch, con->name);
+ } else {
+ ast_verbose( VERBOSE_PREFIX_3 "Added extension '%s' priority %d to %s\n",
+ tmp->exten, tmp->priority, con->name);
+ }
+ }
+ return 0;
+}
+
+int localized_add_extension2(struct ast_context *con,
+ int replace, const char *extension, int priority, const char *label, const char *callerid,
+ const char *application, void *data, void (*datad)(void *),
+ const char *registrar);
+
+int localized_add_extension2(struct ast_context *con,
+ int replace, const char *extension, int priority, const char *label, const char *callerid,
+ const char *application, void *data, void (*datad)(void *),
+ const char *registrar)
+{
+ return ast_add_extension2(con, replace, extension, priority, label, callerid, application, data, datad, registrar);
+}
+
+
+
+/*! \brief The return value depends on the action:
+ *
+ * E_MATCH, E_CANMATCH, E_MATCHMORE require a real match,
+ * and return 0 on failure, -1 on match;
+ * E_FINDLABEL maps the label to a priority, and returns
+ * the priority on success, ... XXX
+ * E_SPAWN, spawn an application,
+ * and return 0 on success, -1 on failure.
+ */
+static int pbx_extension_helper(struct ast_channel *c, struct ast_context *con,
+ const char *context, const char *exten, int priority,
+ const char *label, const char *callerid, enum ext_match_t action)
+{
+ struct ast_exten *e;
+ int res;
+ struct pbx_find_info q = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
+
+ int matching_action = (action == E_MATCH || action == E_CANMATCH || action == E_MATCHMORE);
+
+ e = pbx_find_extension(NULL, con, &q, context, exten, priority, label, callerid, action);
+ if (e) {
+ if (matching_action) {
+ return -1; /* success, we found it */
+ } else if (action == E_FINDLABEL) { /* map the label to a priority */
+ res = e->priority;
+ return res; /* the priority we were looking for */
+ } else { /* spawn */
+
+ /* NOT!!!!! */
+ return 0;
+ }
+ } else if (q.swo) { /* not found here, but in another switch */
+ if (matching_action)
+ return -1;
+ else {
+ if (!q.swo->exec) {
+ ast_log(LOG_WARNING, "No execution engine for switch %s\n", q.swo->name);
+ res = -1;
+ }
+ return q.swo->exec(c, q.foundcontext ? q.foundcontext : context, exten, priority, callerid, q.data);
+ }
+ } else { /* not found anywhere, see what happened */
+ switch (q.status) {
+ case STATUS_NO_CONTEXT:
+ if (!matching_action)
+ ast_log(LOG_NOTICE, "Cannot find extension context '%s'\n", context);
+ break;
+ case STATUS_NO_EXTENSION:
+ if (!matching_action)
+ ast_log(LOG_NOTICE, "Cannot find extension '%s' in context '%s'\n", exten, context);
+ break;
+ case STATUS_NO_PRIORITY:
+ if (!matching_action)
+ ast_log(LOG_NOTICE, "No such priority %d in extension '%s' in context '%s'\n", priority, exten, context);
+ break;
+ case STATUS_NO_LABEL:
+ if (context)
+ ast_log(LOG_NOTICE, "No such label '%s' in extension '%s' in context '%s'\n", label, exten, context);
+ break;
+ default:
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Shouldn't happen!\n");
+ }
+
+ return (matching_action) ? 0 : -1;
+ }
+}
+
+static int ast_findlabel_extension2(struct ast_channel *c, struct ast_context *con, const char *exten, const char *label, const char *callerid);
+
+static int ast_findlabel_extension2(struct ast_channel *c, struct ast_context *con, const char *exten, const char *label, const char *callerid)
+{
+ return pbx_extension_helper(c, con, NULL, exten, 0, label, callerid, E_FINDLABEL);
+}
+
+static struct ast_context *ast_context_find_or_create(struct ast_context **extcontexts, const char *name, const char *registrar)
+{
+ return __ast_context_create(extcontexts, name, registrar, 1);
+}
+
+struct ast_context *localized_context_create(struct ast_context **extcontexts, const char *name, const char *registrar);
+
+struct ast_context *localized_context_create(struct ast_context **extcontexts, const char *name, const char *registrar)
+{
+ return __ast_context_create(extcontexts, name, registrar, 0);
+}
+
+
+
+/* chopped this one off at the knees */
+static int ast_func_read(struct ast_channel *chan, const char *function, char *workspace, size_t len)
+{
+ ast_log(LOG_ERROR, "Function %s not registered\n", function);
+ return -1;
+}
+
+/*! \brief extract offset:length from variable name.
+ * Returns 1 if there is a offset:length part, which is
+ * trimmed off (values go into variables)
+ */
+static int parse_variable_name(char *var, int *offset, int *length, int *isfunc)
+{
+ int parens=0;
+
+ *offset = 0;
+ *length = INT_MAX;
+ *isfunc = 0;
+ for (; *var; var++) {
+ if (*var == '(') {
+ (*isfunc)++;
+ parens++;
+ } else if (*var == ')') {
+ parens--;
+ } else if (*var == ':' && parens == 0) {
+ *var++ = '\0';
+ sscanf(var, "%d:%d", offset, length);
+ return 1; /* offset:length valid */
+ }
+ }
+ return 0;
+}
+
+static const char *ast_var_value(const struct ast_var_t *var)
+{
+ return (var ? var->value : NULL);
+}
+
+/*! \brief takes a substring. It is ok to call with value == workspace.
+ *
+ * offset < 0 means start from the end of the string and set the beginning
+ * to be that many characters back.
+ * length is the length of the substring. A value less than 0 means to leave
+ * that many off the end.
+ * Always return a copy in workspace.
+ */
+static char *substring(const char *value, int offset, int length, char *workspace, size_t workspace_len)
+{
+ char *ret = workspace;
+ int lr; /* length of the input string after the copy */
+
+ ast_copy_string(workspace, value, workspace_len); /* always make a copy */
+
+ lr = strlen(ret); /* compute length after copy, so we never go out of the workspace */
+
+ /* Quick check if no need to do anything */
+ if (offset == 0 && length >= lr) /* take the whole string */
+ return ret;
+
+ if (offset < 0) { /* translate negative offset into positive ones */
+ offset = lr + offset;
+ if (offset < 0) /* If the negative offset was greater than the length of the string, just start at the beginning */
+ offset = 0;
+ }
+
+ /* too large offset result in empty string so we know what to return */
+ if (offset >= lr)
+ return ret + lr; /* the final '\0' */
+
+ ret += offset; /* move to the start position */
+ if (length >= 0 && length < lr - offset) /* truncate if necessary */
+ ret[length] = '\0';
+ else if (length < 0) {
+ if (lr > offset - length) /* After we remove from the front and from the rear, is there anything left? */
+ ret[lr + length - offset] = '\0';
+ else
+ ret[0] = '\0';
+ }
+
+ return ret;
+}
+
+/*! \brief Support for Asterisk built-in variables in the dialplan
+\note See also
+ - \ref AstVar Channel variables
+ - \ref AstCauses The HANGUPCAUSE variable
+ */
+static void pbx_retrieve_variable(struct ast_channel *c, const char *var, char **ret, char *workspace, int workspacelen, struct varshead *headp)
+{
+ const char not_found = '\0';
+ char *tmpvar;
+ const char *s; /* the result */
+ int offset, length;
+ int i, need_substring;
+ struct varshead *places[2] = { headp, &globals }; /* list of places where we may look */
+
+ /*
+ * Make a copy of var because parse_variable_name() modifies the string.
+ * Then if called directly, we might need to run substring() on the result;
+ * remember this for later in 'need_substring', 'offset' and 'length'
+ */
+ tmpvar = ast_strdupa(var); /* parse_variable_name modifies the string */
+ need_substring = parse_variable_name(tmpvar, &offset, &length, &i /* ignored */);
+
+ /*
+ * Look first into predefined variables, then into variable lists.
+ * Variable 's' points to the result, according to the following rules:
+ * s == &not_found (set at the beginning) means that we did not find a
+ * matching variable and need to look into more places.
+ * If s != &not_found, s is a valid result string as follows:
+ * s = NULL if the variable does not have a value;
+ * you typically do this when looking for an unset predefined variable.
+ * s = workspace if the result has been assembled there;
+ * typically done when the result is built e.g. with an snprintf(),
+ * so we don't need to do an additional copy.
+ * s != workspace in case we have a string, that needs to be copied
+ * (the ast_copy_string is done once for all at the end).
+ * Typically done when the result is already available in some string.
+ */
+ s = &not_found; /* default value */
+ if (s == &not_found) { /* look for more */
+ if (!strcmp(var, "EPOCH")) {
+ snprintf(workspace, workspacelen, "%u",(int)time(NULL));
+ }
+
+ s = workspace;
+ }
+ /* if not found, look into chanvars or global vars */
+ for (i = 0; s == &not_found && i < (sizeof(places) / sizeof(places[0])); i++) {
+ struct ast_var_t *variables;
+ if (!places[i])
+ continue;
+ if (places[i] == &globals)
+ ast_rwlock_rdlock(&globalslock);
+ AST_LIST_TRAVERSE(places[i], variables, entries) {
+ if (strcasecmp(ast_var_name(variables), var)==0) {
+ s = ast_var_value(variables);
+ break;
+ }
+ }
+ if (places[i] == &globals)
+ ast_rwlock_unlock(&globalslock);
+ }
+ if (s == &not_found || s == NULL)
+ *ret = NULL;
+ else {
+ if (s != workspace)
+ ast_copy_string(workspace, s, workspacelen);
+ *ret = workspace;
+ if (need_substring)
+ *ret = substring(*ret, offset, length, workspace, workspacelen);
+ }
+}
+
+static void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int count)
+{
+ /* Substitutes variables into cp2, based on string cp1, and assuming cp2 to be
+ zero-filled */
+ char *cp4;
+ const char *tmp, *whereweare;
+ int length, offset, offset2, isfunction;
+ char *workspace = NULL;
+ char *ltmp = NULL, *var = NULL;
+ char *nextvar, *nextexp, *nextthing;
+ char *vars, *vare;
+ int pos, brackets, needsub, len;
+
+ whereweare=tmp=cp1;
+ while (!ast_strlen_zero(whereweare) && count) {
+ /* Assume we're copying the whole remaining string */
+ pos = strlen(whereweare);
+ nextvar = NULL;
+ nextexp = NULL;
+ nextthing = strchr(whereweare, '$');
+ if (nextthing) {
+ switch (nextthing[1]) {
+ case '{':
+ nextvar = nextthing;
+ pos = nextvar - whereweare;
+ break;
+ case '[':
+ nextexp = nextthing;
+ pos = nextexp - whereweare;
+ break;
+ }
+ }
+
+ if (pos) {
+ /* Can't copy more than 'count' bytes */
+ if (pos > count)
+ pos = count;
+
+ /* Copy that many bytes */
+ memcpy(cp2, whereweare, pos);
+
+ count -= pos;
+ cp2 += pos;
+ whereweare += pos;
+ }
+
+ if (nextvar) {
+ /* We have a variable. Find the start and end, and determine
+ if we are going to have to recursively call ourselves on the
+ contents */
+ vars = vare = nextvar + 2;
+ brackets = 1;
+ needsub = 0;
+
+ /* Find the end of it */
+ while (brackets && *vare) {
+ if ((vare[0] == '$') && (vare[1] == '{')) {
+ needsub++;
+ } else if (vare[0] == '{') {
+ brackets++;
+ } else if (vare[0] == '}') {
+ brackets--;
+ } else if ((vare[0] == '$') && (vare[1] == '['))
+ needsub++;
+ vare++;
+ }
+ if (brackets)
+ ast_log(LOG_NOTICE, "Error in extension logic (missing '}' in '%s')\n", cp1);
+ len = vare - vars - 1;
+
+ /* Skip totally over variable string */
+ whereweare += (len + 3);
+
+ if (!var)
+ var = alloca(VAR_BUF_SIZE);
+
+ /* Store variable name (and truncate) */
+ ast_copy_string(var, vars, len + 1);
+
+ /* Substitute if necessary */
+ if (needsub) {
+ if (!ltmp)
+ ltmp = alloca(VAR_BUF_SIZE);
+
+ memset(ltmp, 0, VAR_BUF_SIZE);
+ pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1);
+ vars = ltmp;
+ } else {
+ vars = var;
+ }
+
+ if (!workspace)
+ workspace = alloca(VAR_BUF_SIZE);
+
+ workspace[0] = '\0';
+
+ parse_variable_name(vars, &offset, &offset2, &isfunction);
+ if (isfunction) {
+ /* Evaluate function */
+ cp4 = ast_func_read(c, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace;
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Function result is '%s'\n", cp4 ? cp4 : "(null)");
+ } else {
+ /* Retrieve variable value */
+ pbx_retrieve_variable(c, vars, &cp4, workspace, VAR_BUF_SIZE, headp);
+ }
+ if (cp4) {
+ cp4 = substring(cp4, offset, offset2, workspace, VAR_BUF_SIZE);
+
+ length = strlen(cp4);
+ if (length > count)
+ length = count;
+ memcpy(cp2, cp4, length);
+ count -= length;
+ cp2 += length;
+ }
+ } else if (nextexp) {
+ /* We have an expression. Find the start and end, and determine
+ if we are going to have to recursively call ourselves on the
+ contents */
+ vars = vare = nextexp + 2;
+ brackets = 1;
+ needsub = 0;
+
+ /* Find the end of it */
+ while (brackets && *vare) {
+ if ((vare[0] == '$') && (vare[1] == '[')) {
+ needsub++;
+ brackets++;
+ vare++;
+ } else if (vare[0] == '[') {
+ brackets++;
+ } else if (vare[0] == ']') {
+ brackets--;
+ } else if ((vare[0] == '$') && (vare[1] == '{')) {
+ needsub++;
+ vare++;
+ }
+ vare++;
+ }
+ if (brackets)
+ ast_log(LOG_NOTICE, "Error in extension logic (missing ']')\n");
+ len = vare - vars - 1;
+
+ /* Skip totally over expression */
+ whereweare += (len + 3);
+
+ if (!var)
+ var = alloca(VAR_BUF_SIZE);
+
+ /* Store variable name (and truncate) */
+ ast_copy_string(var, vars, len + 1);
+
+ /* Substitute if necessary */
+ if (needsub) {
+ if (!ltmp)
+ ltmp = alloca(VAR_BUF_SIZE);
+
+ memset(ltmp, 0, VAR_BUF_SIZE);
+ pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1);
+ vars = ltmp;
+ } else {
+ vars = var;
+ }
+
+ length = ast_expr(vars, cp2, count, NULL);
+
+ if (length) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Expression result is '%s'\n", cp2);
+ count -= length;
+ cp2 += length;
+ }
+ } else
+ break;
+ }
+}
+
+static void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count)
+{
+ pbx_substitute_variables_helper_full(c, NULL, cp1, cp2, count);
+}
+
+
+static int pbx_load_config(const char *config_file);
+
+static int pbx_load_config(const char *config_file)
+{
+ struct ast_config *cfg;
+ char *end;
+ char *label;
+ char realvalue[256];
+ int lastpri = -2;
+ struct ast_context *con;
+ struct ast_variable *v;
+ const char *cxt;
+ const char *aft;
+
+ cfg = localized_config_load(config_file);
+ if (!cfg)
+ return 0;
+
+ /* Use existing config to populate the PBX table */
+ static_config = ast_true(ast_variable_retrieve(cfg, "general", "static"));
+ write_protect_config = ast_true(ast_variable_retrieve(cfg, "general", "writeprotect"));
+ if ((aft = ast_variable_retrieve(cfg, "general", "autofallthrough")))
+ autofallthrough_config = ast_true(aft);
+ clearglobalvars_config = ast_true(ast_variable_retrieve(cfg, "general", "clearglobalvars"));
+ ast_set2_flag(&ast_options, ast_true(ast_variable_retrieve(cfg, "general", "priorityjumping")), AST_OPT_FLAG_PRIORITY_JUMPING);
+
+ if ((cxt = ast_variable_retrieve(cfg, "general", "userscontext")))
+ ast_copy_string(userscontext, cxt, sizeof(userscontext));
+ else
+ ast_copy_string(userscontext, "default", sizeof(userscontext));
+
+ for (v = ast_variable_browse(cfg, "globals"); v; v = v->next) {
+ memset(realvalue, 0, sizeof(realvalue));
+ pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
+ pbx_builtin_setvar_helper(NULL, v->name, realvalue);
+ }
+ for (cxt = NULL; (cxt = ast_category_browse(cfg, cxt)); ) {
+ /* All categories but "general" or "globals" are considered contexts */
+ if (!strcasecmp(cxt, "general") || !strcasecmp(cxt, "globals"))
+ continue;
+ con=ast_context_find_or_create(&local_contexts,cxt, registrar);
+ if (con == NULL)
+ continue;
+
+ for (v = ast_variable_browse(cfg, cxt); v; v = v->next) {
+ if (!strcasecmp(v->name, "exten")) {
+ char *tc = ast_strdup(v->value);
+ if (tc) {
+ int ipri = -2;
+ char realext[256]="";
+ char *plus, *firstp, *firstc;
+ char *pri, *appl, *data, *cidmatch;
+ char *stringp = tc;
+ char *ext = strsep(&stringp, ",");
+ if (!ext)
+ ext="";
+ pbx_substitute_variables_helper(NULL, ext, realext, sizeof(realext) - 1);
+ cidmatch = strchr(realext, '/');
+ if (cidmatch) {
+ *cidmatch++ = '\0';
+ ast_shrink_phone_number(cidmatch);
+ }
+ pri = strsep(&stringp, ",");
+ if (!pri)
+ pri="";
+ label = strchr(pri, '(');
+ if (label) {
+ *label++ = '\0';
+ end = strchr(label, ')');
+ if (end)
+ *end = '\0';
+ else
+ ast_log(LOG_WARNING, "Label missing trailing ')' at line %d\n", v->lineno);
+ }
+ plus = strchr(pri, '+');
+ if (plus)
+ *plus++ = '\0';
+ if (!strcmp(pri,"hint"))
+ ipri=PRIORITY_HINT;
+ else if (!strcmp(pri, "next") || !strcmp(pri, "n")) {
+ if (lastpri > -2)
+ ipri = lastpri + 1;
+ else
+ ast_log(LOG_WARNING, "Can't use 'next' priority on the first entry!\n");
+ } else if (!strcmp(pri, "same") || !strcmp(pri, "s")) {
+ if (lastpri > -2)
+ ipri = lastpri;
+ else
+ ast_log(LOG_WARNING, "Can't use 'same' priority on the first entry!\n");
+ } else if (sscanf(pri, "%d", &ipri) != 1 &&
+ (ipri = ast_findlabel_extension2(NULL, con, realext, pri, cidmatch)) < 1) {
+ ast_log(LOG_WARNING, "Invalid priority/label '%s' at line %d\n", pri, v->lineno);
+ ipri = 0;
+ }
+ appl = S_OR(stringp, "");
+ /* Find the first occurrence of either '(' or ',' */
+ firstc = strchr(appl, ',');
+ firstp = strchr(appl, '(');
+ if (firstc && (!firstp || firstc < firstp)) {
+ /* comma found, no parenthesis */
+ /* or both found, but comma found first */
+ appl = strsep(&stringp, ",");
+ data = stringp;
+ } else if (!firstc && !firstp) {
+ /* Neither found */
+ data = "";
+ } else {
+ /* Final remaining case is parenthesis found first */
+ appl = strsep(&stringp, "(");
+ data = stringp;
+ end = strrchr(data, ')');
+ if ((end = strrchr(data, ')'))) {
+ *end = '\0';
+ } else {
+ ast_log(LOG_WARNING, "No closing parenthesis found? '%s(%s'\n", appl, data);
+ }
+ ast_process_quotes_and_slashes(data, ',', '|');
+ }
+
+ if (!data)
+ data="";
+ appl = ast_skip_blanks(appl);
+ if (ipri) {
+ if (plus)
+ ipri += atoi(plus);
+ lastpri = ipri;
+ if (!ast_opt_dont_warn && !strcmp(realext, "_."))
+ ast_log(LOG_WARNING, "The use of '_.' for an extension is strongly discouraged and can have unexpected behavior. Please use '_X.' instead at line %d\n", v->lineno);
+ if (ast_add_extension2(con, 0, realext, ipri, label, cidmatch, appl, strdup(data), ast_free, registrar)) {
+ ast_log(LOG_WARNING, "Unable to register extension at line %d\n", v->lineno);
+ }
+ }
+ free(tc);
+ }
+ } else if (!strcasecmp(v->name, "include")) {
+ memset(realvalue, 0, sizeof(realvalue));
+ pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
+ if (ast_context_add_include2(con, realvalue, registrar))
+ ast_log(LOG_WARNING, "Unable to include context '%s' in context '%s'\n", v->value, cxt);
+ } else if (!strcasecmp(v->name, "ignorepat")) {
+ memset(realvalue, 0, sizeof(realvalue));
+ pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
+ if (ast_context_add_ignorepat2(con, realvalue, registrar))
+ ast_log(LOG_WARNING, "Unable to include ignorepat '%s' in context '%s'\n", v->value, cxt);
+ } else if (!strcasecmp(v->name, "switch") || !strcasecmp(v->name, "lswitch") || !strcasecmp(v->name, "eswitch")) {
+ char *stringp= realvalue;
+ char *appl, *data;
+
+ memset(realvalue, 0, sizeof(realvalue));
+ if (!strcasecmp(v->name, "switch"))
+ pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
+ else
+ ast_copy_string(realvalue, v->value, sizeof(realvalue));
+ appl = strsep(&stringp, "/");
+ data = strsep(&stringp, ""); /* XXX what for ? */
+ if (!data)
+ data = "";
+ if (ast_context_add_switch2(con, appl, data, !strcasecmp(v->name, "eswitch"), registrar))
+ ast_log(LOG_WARNING, "Unable to include switch '%s' in context '%s'\n", v->value, cxt);
+ } else {
+ ast_log(LOG_WARNING, "==!!== Unknown directive: %s at line %d -- IGNORING!!!\n", v->name, v->lineno);
+ }
+ }
+ }
+ ast_config_destroy(cfg);
+ return 1;
+}
+
+static void __ast_context_destroy(struct ast_context *con, const char *registrar)
+{
+ struct ast_context *tmp, *tmpl=NULL;
+ struct ast_include *tmpi;
+ struct ast_sw *sw;
+ struct ast_exten *e, *el, *en;
+ struct ast_ignorepat *ipi;
+
+ for (tmp = contexts; tmp; ) {
+ struct ast_context *next; /* next starting point */
+ for (; tmp; tmpl = tmp, tmp = tmp->next) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "check ctx %s %s\n", tmp->name, tmp->registrar);
+ if ( (!registrar || !strcasecmp(registrar, tmp->registrar)) &&
+ (!con || !strcasecmp(tmp->name, con->name)) )
+ break; /* found it */
+ }
+ if (!tmp) /* not found, we are done */
+ break;
+ ast_wrlock_context(tmp);
+ if (option_debug)
+ ast_log(LOG_DEBUG, "delete ctx %s %s\n", tmp->name, tmp->registrar);
+ next = tmp->next;
+ if (tmpl)
+ tmpl->next = next;
+ else
+ contexts = next;
+ /* Okay, now we're safe to let it go -- in a sense, we were
+ ready to let it go as soon as we locked it. */
+ ast_unlock_context(tmp);
+ for (tmpi = tmp->includes; tmpi; ) { /* Free includes */
+ struct ast_include *tmpil = tmpi;
+ tmpi = tmpi->next;
+ free(tmpil);
+ }
+ for (ipi = tmp->ignorepats; ipi; ) { /* Free ignorepats */
+ struct ast_ignorepat *ipl = ipi;
+ ipi = ipi->next;
+ free(ipl);
+ }
+ while ((sw = AST_LIST_REMOVE_HEAD(&tmp->alts, list)))
+ free(sw);
+ for (e = tmp->root; e;) {
+ for (en = e->peer; en;) {
+ el = en;
+ en = en->peer;
+ destroy_exten(el);
+ }
+ el = e;
+ e = e->next;
+ destroy_exten(el);
+ }
+ ast_rwlock_destroy(&tmp->lock);
+ free(tmp);
+ /* if we have a specific match, we are done, otherwise continue */
+ tmp = con ? NULL : next;
+ }
+}
+
+void localized_context_destroy(struct ast_context *con, const char *registrar);
+
+void localized_context_destroy(struct ast_context *con, const char *registrar)
+{
+ ast_wrlock_contexts();
+ __ast_context_destroy(con,registrar);
+ ast_unlock_contexts();
+}
+
+
+static void ast_merge_contexts_and_delete(struct ast_context **extcontexts, const char *registrar)
+{
+ struct ast_context *tmp, *lasttmp = NULL;
+
+ /* it is very important that this function hold the hint list lock _and_ the conlock
+ during its operation; not only do we need to ensure that the list of contexts
+ and extensions does not change, but also that no hint callbacks (watchers) are
+ added or removed during the merge/delete process
+
+ in addition, the locks _must_ be taken in this order, because there are already
+ other code paths that use this order
+ */
+ ast_wrlock_contexts();
+
+ tmp = *extcontexts;
+ if (registrar) {
+ /* XXX remove previous contexts from same registrar */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "must remove any reg %s\n", registrar);
+ __ast_context_destroy(NULL,registrar);
+ while (tmp) {
+ lasttmp = tmp;
+ tmp = tmp->next;
+ }
+ } else {
+ /* XXX remove contexts with the same name */
+ while (tmp) {
+ ast_log(LOG_WARNING, "must remove %s reg %s\n", tmp->name, tmp->registrar);
+ __ast_context_destroy(tmp,tmp->registrar);
+ lasttmp = tmp;
+ tmp = tmp->next;
+ }
+ }
+ if (lasttmp) {
+ lasttmp->next = contexts;
+ contexts = *extcontexts;
+ *extcontexts = NULL;
+ } else
+ ast_log(LOG_WARNING, "Requested contexts didn't get merged\n");
+
+ ast_unlock_contexts();
+
+ return;
+}
+
+void localized_merge_contexts_and_delete(struct ast_context **extcontexts, const char *registrar);
+
+void localized_merge_contexts_and_delete(struct ast_context **extcontexts, const char *registrar)
+{
+ ast_merge_contexts_and_delete(extcontexts, registrar);
+}
+
+static int ast_context_verify_includes(struct ast_context *con)
+{
+ struct ast_include *inc = NULL;
+ int res = 0;
+
+ while ( (inc = ast_walk_context_includes(con, inc)) )
+ if (!ast_context_find(inc->rname)) {
+ res = -1;
+ if (strcasecmp(inc->rname,"parkedcalls")!=0)
+ ast_log(LOG_WARNING, "Context '%s' tries to include the nonexistent context '%s'\n",
+ ast_get_context_name(con), inc->rname);
+ }
+ return res;
+}
+
+int localized_context_verify_includes(struct ast_context *con);
+
+int localized_context_verify_includes(struct ast_context *con)
+{
+ return ast_context_verify_includes(con);
+}
+
+int localized_pbx_load_module(void);
+
+int localized_pbx_load_module(void)
+{
+ struct ast_context *con;
+
+ if(!pbx_load_config(config))
+ return -1 /* AST_MODULE_LOAD_DECLINE*/;
+
+ /* pbx_load_users(); */ /* does this affect the dialplan? */
+
+ ast_merge_contexts_and_delete(&local_contexts, registrar);
+
+ for (con = NULL; (con = ast_walk_contexts(con));)
+ ast_context_verify_includes(con);
+
+ printf("=== Loading extensions.conf ===\n");
+ con = 0;
+ while ((con = ast_walk_contexts(con)) ) {
+ printf("Context: %s\n", con->name);
+ }
+ printf("=========\n");
+
+ return 0;
+}
+
diff --git a/utils/pval.c b/utils/pval.c
new file mode 100644
index 000000000..d2c37149e
--- /dev/null
+++ b/utils/pval.c
@@ -0,0 +1,5344 @@
+
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006, Digium, Inc.
+ *
+ * Steve Murphy <murf@parsetree.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 Compile symbolic Asterisk Extension Logic into Asterisk extensions, version 2.
+ *
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <regex.h>
+#include <sys/stat.h>
+
+#include "asterisk/pbx.h"
+#include "asterisk/config.h"
+#include "asterisk/module.h"
+#include "asterisk/logger.h"
+#include "asterisk/cli.h"
+#include "asterisk/app.h"
+#include "asterisk/callerid.h"
+#include "asterisk/pval.h"
+#include "asterisk/ael_structs.h"
+#ifdef AAL_ARGCHECK
+#include "asterisk/argdesc.h"
+#endif
+
+extern int localized_pbx_load_module(void);
+
+static char expr_output[2096];
+#define AST_PBX_MAX_STACK 128
+
+/* these functions are in ../ast_expr2.fl */
+
+static int errs, warns;
+static int notes;
+#ifdef STANDALONE
+static int extensions_dot_conf_loaded = 0;
+#endif
+static char *registrar = "pbx_ael";
+
+static pval *current_db;
+static pval *current_context;
+static pval *current_extension;
+
+static const char *match_context;
+static const char *match_exten;
+static const char *match_label;
+static int in_abstract_context;
+static int count_labels; /* true, put matcher in label counting mode */
+static int label_count; /* labels are only meant to be counted in a context or exten */
+static int return_on_context_match;
+static pval *last_matched_label;
+struct pval *match_pval(pval *item);
+static void check_timerange(pval *p);
+static void check_dow(pval *DOW);
+static void check_day(pval *DAY);
+static void check_month(pval *MON);
+static void check_expr2_input(pval *expr, char *str);
+static int extension_matches(pval *here, const char *exten, const char *pattern);
+static void check_goto(pval *item);
+static void find_pval_goto_item(pval *item, int lev);
+static void find_pval_gotos(pval *item, int lev);
+static int check_break(pval *item);
+static int check_continue(pval *item);
+static void check_label(pval *item);
+static void check_macro_returns(pval *macro);
+
+static struct pval *find_label_in_current_context(char *exten, char *label, pval *curr_cont);
+static struct pval *find_first_label_in_current_context(char *label, pval *curr_cont);
+static void print_pval_list(FILE *fin, pval *item, int depth);
+
+static struct pval *find_label_in_current_extension(const char *label, pval *curr_ext);
+static struct pval *find_label_in_current_db(const char *context, const char *exten, const char *label);
+static pval *get_goto_target(pval *item);
+static int label_inside_case(pval *label);
+static void attach_exten(struct ael_extension **list, struct ael_extension *newmem);
+static void fix_gotos_in_extensions(struct ael_extension *exten);
+static pval *get_extension_or_contxt(pval *p);
+static pval *get_contxt(pval *p);
+static void remove_spaces_before_equals(char *str);
+
+/* PRETTY PRINTER FOR AEL: ============================================================================= */
+
+static void print_pval(FILE *fin, pval *item, int depth)
+{
+ int i;
+ pval *lp;
+
+ for (i=0; i<depth; i++) {
+ fprintf(fin, "\t"); /* depth == indentation */
+ }
+
+ switch ( item->type ) {
+ case PV_WORD:
+ fprintf(fin,"%s;\n", item->u1.str); /* usually, words are encapsulated in something else */
+ break;
+
+ case PV_MACRO:
+ fprintf(fin,"macro %s(", item->u1.str);
+ for (lp=item->u2.arglist; lp; lp=lp->next) {
+ if (lp != item->u2.arglist )
+ fprintf(fin,", ");
+ fprintf(fin,"%s", lp->u1.str);
+ }
+ fprintf(fin,") {\n");
+ print_pval_list(fin,item->u3.macro_statements,depth+1);
+ for (i=0; i<depth; i++) {
+ fprintf(fin,"\t"); /* depth == indentation */
+ }
+ fprintf(fin,"};\n\n");
+ break;
+
+ case PV_CONTEXT:
+ if ( item->u3.abstract )
+ fprintf(fin,"abstract context %s {\n", item->u1.str);
+ else
+ fprintf(fin,"context %s {\n", item->u1.str);
+ print_pval_list(fin,item->u2.statements,depth+1);
+ for (i=0; i<depth; i++) {
+ fprintf(fin,"\t"); /* depth == indentation */
+ }
+ fprintf(fin,"};\n\n");
+ break;
+
+ case PV_MACRO_CALL:
+ fprintf(fin,"&%s(", item->u1.str);
+ for (lp=item->u2.arglist; lp; lp=lp->next) {
+ if ( lp != item->u2.arglist )
+ fprintf(fin,", ");
+ fprintf(fin,"%s", lp->u1.str);
+ }
+ fprintf(fin,");\n");
+ break;
+
+ case PV_APPLICATION_CALL:
+ fprintf(fin,"%s(", item->u1.str);
+ for (lp=item->u2.arglist; lp; lp=lp->next) {
+ if ( lp != item->u2.arglist )
+ fprintf(fin,",");
+ fprintf(fin,"%s", lp->u1.str);
+ }
+ fprintf(fin,");\n");
+ break;
+
+ case PV_CASE:
+ fprintf(fin,"case %s:\n", item->u1.str);
+ print_pval_list(fin,item->u2.statements, depth+1);
+ break;
+
+ case PV_PATTERN:
+ fprintf(fin,"pattern %s:\n", item->u1.str);
+ print_pval_list(fin,item->u2.statements, depth+1);
+ break;
+
+ case PV_DEFAULT:
+ fprintf(fin,"default:\n");
+ print_pval_list(fin,item->u2.statements, depth+1);
+ break;
+
+ case PV_CATCH:
+ fprintf(fin,"catch %s {\n", item->u1.str);
+ print_pval_list(fin,item->u2.statements, depth+1);
+ for (i=0; i<depth; i++) {
+ fprintf(fin,"\t"); /* depth == indentation */
+ }
+ fprintf(fin,"};\n");
+ break;
+
+ case PV_SWITCHES:
+ fprintf(fin,"switches {\n");
+ print_pval_list(fin,item->u1.list,depth+1);
+ for (i=0; i<depth; i++) {
+ fprintf(fin,"\t"); /* depth == indentation */
+ }
+ fprintf(fin,"};\n");
+ break;
+
+ case PV_ESWITCHES:
+ fprintf(fin,"eswitches {\n");
+ print_pval_list(fin,item->u1.list,depth+1);
+ for (i=0; i<depth; i++) {
+ fprintf(fin,"\t"); /* depth == indentation */
+ }
+ fprintf(fin,"};\n");
+ break;
+
+ case PV_INCLUDES:
+ fprintf(fin,"includes {\n");
+ for (lp=item->u1.list; lp; lp=lp->next) {
+ for (i=0; i<depth+1; i++) {
+ fprintf(fin,"\t"); /* depth == indentation */
+ }
+ fprintf(fin,"%s", lp->u1.str); /* usually, words are encapsulated in something else */
+ if (lp->u2.arglist)
+ fprintf(fin,"|%s|%s|%s|%s",
+ lp->u2.arglist->u1.str,
+ lp->u2.arglist->next->u1.str,
+ lp->u2.arglist->next->next->u1.str,
+ lp->u2.arglist->next->next->next->u1.str
+ );
+ fprintf(fin,";\n"); /* usually, words are encapsulated in something else */
+ }
+
+ for (i=0; i<depth; i++) {
+ fprintf(fin,"\t"); /* depth == indentation */
+ }
+ fprintf(fin,"};\n");
+ break;
+
+ case PV_STATEMENTBLOCK:
+ fprintf(fin,"{\n");
+ print_pval_list(fin,item->u1.list, depth+1);
+ for (i=0; i<depth; i++) {
+ fprintf(fin,"\t"); /* depth == indentation */
+ }
+ fprintf(fin,"}\n");
+ break;
+
+ case PV_VARDEC:
+ fprintf(fin,"%s=%s;\n", item->u1.str, item->u2.val);
+ break;
+
+ case PV_LOCALVARDEC:
+ fprintf(fin,"local %s=%s;\n", item->u1.str, item->u2.val);
+ break;
+
+ case PV_GOTO:
+ fprintf(fin,"goto %s", item->u1.list->u1.str);
+ if ( item->u1.list->next )
+ fprintf(fin,",%s", item->u1.list->next->u1.str);
+ if ( item->u1.list->next && item->u1.list->next->next )
+ fprintf(fin,",%s", item->u1.list->next->next->u1.str);
+ fprintf(fin,"\n");
+ break;
+
+ case PV_LABEL:
+ fprintf(fin,"%s:\n", item->u1.str);
+ break;
+
+ case PV_FOR:
+ fprintf(fin,"for (%s; %s; %s)\n", item->u1.for_init, item->u2.for_test, item->u3.for_inc);
+ print_pval_list(fin,item->u4.for_statements,depth+1);
+ break;
+
+ case PV_WHILE:
+ fprintf(fin,"while (%s)\n", item->u1.str);
+ print_pval_list(fin,item->u2.statements,depth+1);
+ break;
+
+ case PV_BREAK:
+ fprintf(fin,"break;\n");
+ break;
+
+ case PV_RETURN:
+ fprintf(fin,"return;\n");
+ break;
+
+ case PV_CONTINUE:
+ fprintf(fin,"continue;\n");
+ break;
+
+ case PV_RANDOM:
+ case PV_IFTIME:
+ case PV_IF:
+ if ( item->type == PV_IFTIME ) {
+
+ fprintf(fin,"ifTime ( %s|%s|%s|%s )\n",
+ item->u1.list->u1.str,
+ item->u1.list->next->u1.str,
+ item->u1.list->next->next->u1.str,
+ item->u1.list->next->next->next->u1.str
+ );
+ } else if ( item->type == PV_RANDOM ) {
+ fprintf(fin,"random ( %s )\n", item->u1.str );
+ } else
+ fprintf(fin,"if ( %s )\n", item->u1.str);
+ if ( item->u2.statements && item->u2.statements->next ) {
+ for (i=0; i<depth; i++) {
+ fprintf(fin,"\t"); /* depth == indentation */
+ }
+ fprintf(fin,"{\n");
+ print_pval_list(fin,item->u2.statements,depth+1);
+ for (i=0; i<depth; i++) {
+ fprintf(fin,"\t"); /* depth == indentation */
+ }
+ if ( item->u3.else_statements )
+ fprintf(fin,"}\n");
+ else
+ fprintf(fin,"};\n");
+ } else if (item->u2.statements ) {
+ print_pval_list(fin,item->u2.statements,depth+1);
+ } else {
+ if (item->u3.else_statements )
+ fprintf(fin, " {} ");
+ else
+ fprintf(fin, " {}; ");
+ }
+ if ( item->u3.else_statements ) {
+ for (i=0; i<depth; i++) {
+ fprintf(fin,"\t"); /* depth == indentation */
+ }
+ fprintf(fin,"else\n");
+ print_pval_list(fin,item->u3.else_statements, depth);
+ }
+ break;
+
+ case PV_SWITCH:
+ fprintf(fin,"switch( %s ) {\n", item->u1.str);
+ print_pval_list(fin,item->u2.statements,depth+1);
+ for (i=0; i<depth; i++) {
+ fprintf(fin,"\t"); /* depth == indentation */
+ }
+ fprintf(fin,"}\n");
+ break;
+
+ case PV_EXTENSION:
+ if ( item->u4.regexten )
+ fprintf(fin, "regexten ");
+ if ( item->u3.hints )
+ fprintf(fin,"hints(%s) ", item->u3.hints);
+
+ fprintf(fin,"%s => \n", item->u1.str);
+ print_pval_list(fin,item->u2.statements,depth+1);
+ break;
+
+ case PV_IGNOREPAT:
+ fprintf(fin,"ignorepat => %s;\n", item->u1.str);
+ break;
+
+ case PV_GLOBALS:
+ fprintf(fin,"globals {\n");
+ print_pval_list(fin,item->u1.statements,depth+1);
+ for (i=0; i<depth; i++) {
+ fprintf(fin,"\t"); /* depth == indentation */
+ }
+ fprintf(fin,"}\n");
+ break;
+ }
+}
+
+static void print_pval_list(FILE *fin, pval *item, int depth)
+{
+ pval *i;
+
+ for (i=item; i; i=i->next) {
+ print_pval(fin, i, depth);
+ }
+}
+
+void ael2_print(char *fname, pval *tree)
+{
+ FILE *fin = fopen(fname,"w");
+ if ( !fin ) {
+ ast_log(LOG_ERROR, "Couldn't open %s for writing.\n", fname);
+ return;
+ }
+ print_pval_list(fin, tree, 0);
+ fclose(fin);
+}
+
+
+/* EMPTY TEMPLATE FUNCS FOR AEL TRAVERSAL: ============================================================================= */
+
+void traverse_pval_template(pval *item, int depth);
+void traverse_pval_item_template(pval *item, int depth);
+
+
+void traverse_pval_item_template(pval *item, int depth)/* depth comes in handy for a pretty print (indentation),
+ but you may not need it */
+{
+ pval *lp;
+
+ switch ( item->type ) {
+ case PV_WORD:
+ /* fields: item->u1.str == string associated with this (word). */
+ break;
+
+ case PV_MACRO:
+ /* fields: item->u1.str == name of macro
+ item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
+ item->u2.arglist->u1.str == argument
+ item->u2.arglist->next == next arg
+
+ item->u3.macro_statements == pval list of statements in macro body.
+ */
+ for (lp=item->u2.arglist; lp; lp=lp->next) {
+
+ }
+ traverse_pval_item_template(item->u3.macro_statements,depth+1);
+ break;
+
+ case PV_CONTEXT:
+ /* fields: item->u1.str == name of context
+ item->u2.statements == pval list of statements in context body
+ item->u3.abstract == int 1 if an abstract keyword were present
+ */
+ traverse_pval_item_template(item->u2.statements,depth+1);
+ break;
+
+ case PV_MACRO_CALL:
+ /* fields: item->u1.str == name of macro to call
+ item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
+ item->u2.arglist->u1.str == argument
+ item->u2.arglist->next == next arg
+ */
+ for (lp=item->u2.arglist; lp; lp=lp->next) {
+ }
+ break;
+
+ case PV_APPLICATION_CALL:
+ /* fields: item->u1.str == name of application to call
+ item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
+ item->u2.arglist->u1.str == argument
+ item->u2.arglist->next == next arg
+ */
+ for (lp=item->u2.arglist; lp; lp=lp->next) {
+ }
+ break;
+
+ case PV_CASE:
+ /* fields: item->u1.str == value of case
+ item->u2.statements == pval list of statements under the case
+ */
+ traverse_pval_item_template(item->u2.statements,depth+1);
+ break;
+
+ case PV_PATTERN:
+ /* fields: item->u1.str == value of case
+ item->u2.statements == pval list of statements under the case
+ */
+ traverse_pval_item_template(item->u2.statements,depth+1);
+ break;
+
+ case PV_DEFAULT:
+ /* fields:
+ item->u2.statements == pval list of statements under the case
+ */
+ traverse_pval_item_template(item->u2.statements,depth+1);
+ break;
+
+ case PV_CATCH:
+ /* fields: item->u1.str == name of extension to catch
+ item->u2.statements == pval list of statements in context body
+ */
+ traverse_pval_item_template(item->u2.statements,depth+1);
+ break;
+
+ case PV_SWITCHES:
+ /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
+ */
+ traverse_pval_item_template(item->u1.list,depth+1);
+ break;
+
+ case PV_ESWITCHES:
+ /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
+ */
+ traverse_pval_item_template(item->u1.list,depth+1);
+ break;
+
+ case PV_INCLUDES:
+ /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
+ item->u2.arglist == pval list of 4 PV_WORD elements for time values
+ */
+ traverse_pval_item_template(item->u1.list,depth+1);
+ traverse_pval_item_template(item->u2.arglist,depth+1);
+ break;
+
+ case PV_STATEMENTBLOCK:
+ /* fields: item->u1.list == pval list of statements in block, one per entry in the list
+ */
+ traverse_pval_item_template(item->u1.list,depth+1);
+ break;
+
+ case PV_LOCALVARDEC:
+ case PV_VARDEC:
+ /* fields: item->u1.str == variable name
+ item->u2.val == variable value to assign
+ */
+ break;
+
+ case PV_GOTO:
+ /* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user.
+ item->u1.list->u1.str == where the data on a PV_WORD will always be.
+ */
+
+ if ( item->u1.list->next )
+ ;
+ if ( item->u1.list->next && item->u1.list->next->next )
+ ;
+
+ break;
+
+ case PV_LABEL:
+ /* fields: item->u1.str == label name
+ */
+ break;
+
+ case PV_FOR:
+ /* fields: item->u1.for_init == a string containing the initalizer
+ item->u2.for_test == a string containing the loop test
+ item->u3.for_inc == a string containing the loop increment
+
+ item->u4.for_statements == a pval list of statements in the for ()
+ */
+ traverse_pval_item_template(item->u4.for_statements,depth+1);
+ break;
+
+ case PV_WHILE:
+ /* fields: item->u1.str == the while conditional, as supplied by user
+
+ item->u2.statements == a pval list of statements in the while ()
+ */
+ traverse_pval_item_template(item->u2.statements,depth+1);
+ break;
+
+ case PV_BREAK:
+ /* fields: none
+ */
+ break;
+
+ case PV_RETURN:
+ /* fields: none
+ */
+ break;
+
+ case PV_CONTINUE:
+ /* fields: none
+ */
+ break;
+
+ case PV_IFTIME:
+ /* fields: item->u1.list == there are 4 linked PV_WORDs here.
+
+ item->u2.statements == a pval list of statements in the if ()
+ item->u3.else_statements == a pval list of statements in the else
+ (could be zero)
+ */
+ traverse_pval_item_template(item->u2.statements,depth+1);
+ if ( item->u3.else_statements ) {
+ traverse_pval_item_template(item->u3.else_statements,depth+1);
+ }
+ break;
+
+ case PV_RANDOM:
+ /* fields: item->u1.str == the random number expression, as supplied by user
+
+ item->u2.statements == a pval list of statements in the if ()
+ item->u3.else_statements == a pval list of statements in the else
+ (could be zero)
+ */
+ traverse_pval_item_template(item->u2.statements,depth+1);
+ if ( item->u3.else_statements ) {
+ traverse_pval_item_template(item->u3.else_statements,depth+1);
+ }
+ break;
+
+ case PV_IF:
+ /* fields: item->u1.str == the if conditional, as supplied by user
+
+ item->u2.statements == a pval list of statements in the if ()
+ item->u3.else_statements == a pval list of statements in the else
+ (could be zero)
+ */
+ traverse_pval_item_template(item->u2.statements,depth+1);
+ if ( item->u3.else_statements ) {
+ traverse_pval_item_template(item->u3.else_statements,depth+1);
+ }
+ break;
+
+ case PV_SWITCH:
+ /* fields: item->u1.str == the switch expression
+
+ item->u2.statements == a pval list of statements in the switch,
+ (will be case statements, most likely!)
+ */
+ traverse_pval_item_template(item->u2.statements,depth+1);
+ break;
+
+ case PV_EXTENSION:
+ /* fields: item->u1.str == the extension name, label, whatever it's called
+
+ item->u2.statements == a pval list of statements in the extension
+ item->u3.hints == a char * hint argument
+ item->u4.regexten == an int boolean. non-zero says that regexten was specified
+ */
+ traverse_pval_item_template(item->u2.statements,depth+1);
+ break;
+
+ case PV_IGNOREPAT:
+ /* fields: item->u1.str == the ignorepat data
+ */
+ break;
+
+ case PV_GLOBALS:
+ /* fields: item->u1.statements == pval list of statements, usually vardecs
+ */
+ traverse_pval_item_template(item->u1.statements,depth+1);
+ break;
+ }
+}
+
+void traverse_pval_template(pval *item, int depth) /* depth comes in handy for a pretty print (indentation),
+ but you may not need it */
+{
+ pval *i;
+
+ for (i=item; i; i=i->next) {
+ traverse_pval_item_template(i, depth);
+ }
+}
+
+
+/* SEMANTIC CHECKING FOR AEL: ============================================================================= */
+
+/* (not all that is syntactically legal is good! */
+
+
+static void check_macro_returns(pval *macro)
+{
+ pval *i;
+ if (!macro->u3.macro_statements)
+ {
+ pval *z = calloc(1, sizeof(struct pval));
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The macro %s is empty! I will insert a return.\n",
+ macro->filename, macro->startline, macro->endline, macro->u1.str);
+
+ z->type = PV_RETURN;
+ z->startline = macro->startline;
+ z->endline = macro->endline;
+ z->startcol = macro->startcol;
+ z->endcol = macro->endcol;
+ z->filename = strdup(macro->filename);
+
+ macro->u3.macro_statements = z;
+ return;
+ }
+ for (i=macro->u3.macro_statements; i; i=i->next) {
+ /* if the last statement in the list is not return, then insert a return there */
+ if (i->next == NULL) {
+ if (i->type != PV_RETURN) {
+ pval *z = calloc(1, sizeof(struct pval));
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The macro %s does not end with a return; I will insert one.\n",
+ macro->filename, macro->startline, macro->endline, macro->u1.str);
+
+ z->type = PV_RETURN;
+ z->startline = macro->startline;
+ z->endline = macro->endline;
+ z->startcol = macro->startcol;
+ z->endcol = macro->endcol;
+ z->filename = strdup(macro->filename);
+
+ i->next = z;
+ return;
+ }
+ }
+ }
+ return;
+}
+
+
+
+static int extension_matches(pval *here, const char *exten, const char *pattern)
+{
+ int err1;
+ regex_t preg;
+
+ /* simple case, they match exactly, the pattern and exten name */
+ if (!strcmp(pattern,exten) == 0)
+ return 1;
+
+ if (pattern[0] == '_') {
+ char reg1[2000];
+ const char *p;
+ char *r = reg1;
+
+ if ( strlen(pattern)*5 >= 2000 ) /* safety valve */ {
+ ast_log(LOG_ERROR,"Error: The pattern %s is way too big. Pattern matching cancelled.\n",
+ pattern);
+ return 0;
+ }
+ /* form a regular expression from the pattern, and then match it against exten */
+ *r++ = '^'; /* what if the extension is a pattern ?? */
+ *r++ = '_'; /* what if the extension is a pattern ?? */
+ *r++ = '?';
+ for (p=pattern+1; *p; p++) {
+ switch ( *p ) {
+ case 'X':
+ *r++ = '[';
+ *r++ = '0';
+ *r++ = '-';
+ *r++ = '9';
+ *r++ = 'X';
+ *r++ = ']';
+ break;
+
+ case 'Z':
+ *r++ = '[';
+ *r++ = '1';
+ *r++ = '-';
+ *r++ = '9';
+ *r++ = 'Z';
+ *r++ = ']';
+ break;
+
+ case 'N':
+ *r++ = '[';
+ *r++ = '2';
+ *r++ = '-';
+ *r++ = '9';
+ *r++ = 'N';
+ *r++ = ']';
+ break;
+
+ case '[':
+ while ( *p && *p != ']' ) {
+ *r++ = *p++;
+ }
+ if ( *p != ']') {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The extension pattern '%s' is missing a closing bracket \n",
+ here->filename, here->startline, here->endline, pattern);
+ }
+ break;
+
+ case '.':
+ case '!':
+ *r++ = '.';
+ *r++ = '*';
+ break;
+ case '*':
+ *r++ = '\\';
+ *r++ = '*';
+ break;
+ default:
+ *r++ = *p;
+ break;
+
+ }
+ }
+ *r++ = '$'; /* what if the extension is a pattern ?? */
+ *r++ = *p++; /* put in the closing null */
+ err1 = regcomp(&preg, reg1, REG_NOSUB|REG_EXTENDED);
+ if ( err1 ) {
+ char errmess[500];
+ regerror(err1,&preg,errmess,sizeof(errmess));
+ regfree(&preg);
+ ast_log(LOG_WARNING, "Regcomp of %s failed, error code %d\n",
+ reg1, err1);
+ return 0;
+ }
+ err1 = regexec(&preg, exten, 0, 0, 0);
+ regfree(&preg);
+
+ if ( err1 ) {
+ /* ast_log(LOG_NOTICE,"*****************************[%d]Extension %s did not match %s(%s)\n",
+ err1,exten, pattern, reg1); */
+ return 0; /* no match */
+ } else {
+ /* ast_log(LOG_NOTICE,"*****************************Extension %s matched %s\n",
+ exten, pattern); */
+ return 1;
+ }
+
+
+ } else {
+ if ( strcmp(exten,pattern) == 0 ) {
+ return 1;
+ } else
+ return 0;
+ }
+}
+
+
+static void check_expr2_input(pval *expr, char *str)
+{
+ int spaces = strspn(str,"\t \n");
+ if ( !strncmp(str+spaces,"$[",2) ) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The expression '%s' is redundantly wrapped in '$[ ]'. \n",
+ expr->filename, expr->startline, expr->endline, str);
+ warns++;
+ }
+}
+
+static void check_includes(pval *includes)
+{
+ struct pval *p4;
+ for (p4=includes->u1.list; p4; p4=p4->next) {
+ /* for each context pointed to, find it, then find a context/label that matches the
+ target here! */
+ char *incl_context = p4->u1.str;
+ /* find a matching context name */
+ struct pval *that_other_context = find_context(incl_context);
+ if (!that_other_context && strcmp(incl_context, "parkedcalls") != 0) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The included context '%s' cannot be found.\n",
+ includes->filename, includes->startline, includes->endline, incl_context);
+ warns++;
+ }
+ }
+}
+
+
+static void check_timerange(pval *p)
+{
+ char *times;
+ char *e;
+ int s1, s2;
+ int e1, e2;
+
+ times = ast_strdupa(p->u1.str);
+
+ /* Star is all times */
+ if (ast_strlen_zero(times) || !strcmp(times, "*")) {
+ return;
+ }
+ /* Otherwise expect a range */
+ e = strchr(times, '-');
+ if (!e) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The time range format (%s) requires a '-' surrounded by two 24-hour times of day!\n",
+ p->filename, p->startline, p->endline, times);
+ warns++;
+ return;
+ }
+ *e = '\0';
+ e++;
+ while (*e && !isdigit(*e))
+ e++;
+ if (!*e) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The time range format (%s) is missing the end time!\n",
+ p->filename, p->startline, p->endline, p->u1.str);
+ warns++;
+ }
+ if (sscanf(times, "%d:%d", &s1, &s2) != 2) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start time (%s) isn't quite right!\n",
+ p->filename, p->startline, p->endline, times);
+ warns++;
+ }
+ if (sscanf(e, "%d:%d", &e1, &e2) != 2) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end time (%s) isn't quite right!\n",
+ p->filename, p->startline, p->endline, times);
+ warns++;
+ }
+
+ s1 = s1 * 30 + s2/2;
+ if ((s1 < 0) || (s1 >= 24*30)) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start time (%s) is out of range!\n",
+ p->filename, p->startline, p->endline, times);
+ warns++;
+ }
+ e1 = e1 * 30 + e2/2;
+ if ((e1 < 0) || (e1 >= 24*30)) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end time (%s) is out of range!\n",
+ p->filename, p->startline, p->endline, e);
+ warns++;
+ }
+ return;
+}
+
+static char *days[] =
+{
+ "sun",
+ "mon",
+ "tue",
+ "wed",
+ "thu",
+ "fri",
+ "sat",
+};
+
+/*! \brief get_dow: Get day of week */
+static void check_dow(pval *DOW)
+{
+ char *dow;
+ char *c;
+ /* The following line is coincidence, really! */
+ int s, e;
+
+ dow = ast_strdupa(DOW->u1.str);
+
+ /* Check for all days */
+ if (ast_strlen_zero(dow) || !strcmp(dow, "*"))
+ return;
+ /* Get start and ending days */
+ c = strchr(dow, '-');
+ if (c) {
+ *c = '\0';
+ c++;
+ } else
+ c = NULL;
+ /* Find the start */
+ s = 0;
+ while ((s < 7) && strcasecmp(dow, days[s])) s++;
+ if (s >= 7) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The day (%s) must be one of 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', or 'sat'!\n",
+ DOW->filename, DOW->startline, DOW->endline, dow);
+ warns++;
+ }
+ if (c) {
+ e = 0;
+ while ((e < 7) && strcasecmp(c, days[e])) e++;
+ if (e >= 7) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end day (%s) must be one of 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', or 'sat'!\n",
+ DOW->filename, DOW->startline, DOW->endline, c);
+ warns++;
+ }
+ } else
+ e = s;
+}
+
+static void check_day(pval *DAY)
+{
+ char *day;
+ char *c;
+ /* The following line is coincidence, really! */
+ int s, e;
+
+ day = ast_strdupa(DAY->u1.str);
+
+ /* Check for all days */
+ if (ast_strlen_zero(day) || !strcmp(day, "*")) {
+ return;
+ }
+ /* Get start and ending days */
+ c = strchr(day, '-');
+ if (c) {
+ *c = '\0';
+ c++;
+ }
+ /* Find the start */
+ if (sscanf(day, "%d", &s) != 1) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start day of month (%s) must be a number!\n",
+ DAY->filename, DAY->startline, DAY->endline, day);
+ warns++;
+ }
+ else if ((s < 1) || (s > 31)) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start day of month (%s) must be a number in the range [1-31]!\n",
+ DAY->filename, DAY->startline, DAY->endline, day);
+ warns++;
+ }
+ s--;
+ if (c) {
+ if (sscanf(c, "%d", &e) != 1) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end day of month (%s) must be a number!\n",
+ DAY->filename, DAY->startline, DAY->endline, c);
+ warns++;
+ }
+ else if ((e < 1) || (e > 31)) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end day of month (%s) must be a number in the range [1-31]!\n",
+ DAY->filename, DAY->startline, DAY->endline, day);
+ warns++;
+ }
+ e--;
+ } else
+ e = s;
+}
+
+static char *months[] =
+{
+ "jan",
+ "feb",
+ "mar",
+ "apr",
+ "may",
+ "jun",
+ "jul",
+ "aug",
+ "sep",
+ "oct",
+ "nov",
+ "dec",
+};
+
+static void check_month(pval *MON)
+{
+ char *mon;
+ char *c;
+ /* The following line is coincidence, really! */
+ int s, e;
+
+ mon = ast_strdupa(MON->u1.str);
+
+ /* Check for all days */
+ if (ast_strlen_zero(mon) || !strcmp(mon, "*"))
+ return ;
+ /* Get start and ending days */
+ c = strchr(mon, '-');
+ if (c) {
+ *c = '\0';
+ c++;
+ }
+ /* Find the start */
+ s = 0;
+ while ((s < 12) && strcasecmp(mon, months[s])) s++;
+ if (s >= 12) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start month (%s) must be a one of: 'jan', 'feb', ..., 'dec'!\n",
+ MON->filename, MON->startline, MON->endline, mon);
+ warns++;
+ }
+ if (c) {
+ e = 0;
+ while ((e < 12) && strcasecmp(mon, months[e])) e++;
+ if (e >= 12) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end month (%s) must be a one of: 'jan', 'feb', ..., 'dec'!\n",
+ MON->filename, MON->startline, MON->endline, c);
+ warns++;
+ }
+ } else
+ e = s;
+}
+
+static int check_break(pval *item)
+{
+ pval *p = item;
+
+ while( p && p->type != PV_MACRO && p->type != PV_CONTEXT ) /* early cutout, sort of */ {
+ /* a break is allowed in WHILE, FOR, CASE, DEFAULT, PATTERN; otherwise, it don't make
+ no sense */
+ if( p->type == PV_CASE || p->type == PV_DEFAULT || p->type == PV_PATTERN
+ || p->type == PV_WHILE || p->type == PV_FOR ) {
+ return 1;
+ }
+ p = p->dad;
+ }
+ ast_log(LOG_ERROR,"Error: file %s, line %d-%d: 'break' not in switch, for, or while statement!\n",
+ item->filename, item->startline, item->endline);
+ errs++;
+
+ return 0;
+}
+
+static int check_continue(pval *item)
+{
+ pval *p = item;
+
+ while( p && p->type != PV_MACRO && p->type != PV_CONTEXT ) /* early cutout, sort of */ {
+ /* a break is allowed in WHILE, FOR, CASE, DEFAULT, PATTERN; otherwise, it don't make
+ no sense */
+ if( p->type == PV_WHILE || p->type == PV_FOR ) {
+ return 1;
+ }
+ p = p->dad;
+ }
+ ast_log(LOG_ERROR,"Error: file %s, line %d-%d: 'continue' not in 'for' or 'while' statement!\n",
+ item->filename, item->startline, item->endline);
+ errs++;
+
+ return 0;
+}
+
+static struct pval *in_macro(pval *item)
+{
+ struct pval *curr;
+ curr = item;
+ while( curr ) {
+ if( curr->type == PV_MACRO ) {
+ return curr;
+ }
+ curr = curr->dad;
+ }
+ return 0;
+}
+
+static struct pval *in_context(pval *item)
+{
+ struct pval *curr;
+ curr = item;
+ while( curr ) {
+ if( curr->type == PV_MACRO || curr->type == PV_CONTEXT ) {
+ return curr;
+ }
+ curr = curr->dad;
+ }
+ return 0;
+}
+
+
+/* general purpose goto finder */
+
+static void check_label(pval *item)
+{
+ struct pval *curr;
+ struct pval *x;
+ int alright = 0;
+
+ /* A label outside an extension just plain does not make sense! */
+
+ curr = item;
+
+ while( curr ) {
+ if( curr->type == PV_MACRO || curr->type == PV_EXTENSION ) {
+ alright = 1;
+ break;
+ }
+ curr = curr->dad;
+ }
+ if( !alright )
+ {
+ ast_log(LOG_ERROR,"Error: file %s, line %d-%d: Label %s is not within an extension or macro!\n",
+ item->filename, item->startline, item->endline, item->u1.str);
+ errs++;
+ }
+
+
+ /* basically, ensure that a label is not repeated in a context. Period.
+ The method: well, for each label, find the first label in the context
+ with the same name. If it's not the current label, then throw an error. */
+
+
+ /* printf("==== check_label: ====\n"); */
+ if( !current_extension )
+ curr = current_context;
+ else
+ curr = current_extension;
+
+ x = find_first_label_in_current_context((char *)item->u1.str, curr);
+ /* printf("Hey, check_label found with item = %x, and x is %x, and currcont is %x, label name is %s\n", item,x, current_context, (char *)item->u1.str); */
+ if( x && x != item )
+ {
+ ast_log(LOG_ERROR,"Error: file %s, line %d-%d: Duplicate label %s! Previously defined at file %s, line %d.\n",
+ item->filename, item->startline, item->endline, item->u1.str, x->filename, x->startline);
+ errs++;
+ }
+ /* printf("<<<<< check_label: ====\n"); */
+}
+
+static pval *get_goto_target(pval *item)
+{
+ /* just one item-- the label should be in the current extension */
+ pval *curr_ext = get_extension_or_contxt(item); /* containing exten, or macro */
+ pval *curr_cont;
+
+ if (item->u1.list && !item->u1.list->next && !strstr((item->u1.list)->u1.str,"${")) {
+ struct pval *x = find_label_in_current_extension((char*)((item->u1.list)->u1.str), curr_ext);
+ return x;
+ }
+
+ curr_cont = get_contxt(item);
+
+ /* TWO items */
+ if (item->u1.list->next && !item->u1.list->next->next) {
+ if (!strstr((item->u1.list)->u1.str,"${")
+ && !strstr(item->u1.list->next->u1.str,"${") ) /* Don't try to match variables */ {
+ struct pval *x = find_label_in_current_context((char *)item->u1.list->u1.str, (char *)item->u1.list->next->u1.str, curr_cont);
+ return x;
+ }
+ }
+
+ /* All 3 items! */
+ if (item->u1.list->next && item->u1.list->next->next) {
+ /* all three */
+ pval *first = item->u1.list;
+ pval *second = item->u1.list->next;
+ pval *third = item->u1.list->next->next;
+
+ if (!strstr((item->u1.list)->u1.str,"${")
+ && !strstr(item->u1.list->next->u1.str,"${")
+ && !strstr(item->u1.list->next->next->u1.str,"${")) /* Don't try to match variables */ {
+ struct pval *x = find_label_in_current_db((char*)first->u1.str, (char*)second->u1.str, (char*)third->u1.str);
+ if (!x) {
+
+ struct pval *p3;
+ struct pval *that_context = find_context(item->u1.list->u1.str);
+
+ /* the target of the goto could be in an included context!! Fancy that!! */
+ /* look for includes in the current context */
+ if (that_context) {
+ for (p3=that_context->u2.statements; p3; p3=p3->next) {
+ if (p3->type == PV_INCLUDES) {
+ struct pval *p4;
+ for (p4=p3->u1.list; p4; p4=p4->next) {
+ /* for each context pointed to, find it, then find a context/label that matches the
+ target here! */
+ char *incl_context = p4->u1.str;
+ /* find a matching context name */
+ struct pval *that_other_context = find_context(incl_context);
+ if (that_other_context) {
+ struct pval *x3;
+ x3 = find_label_in_current_context((char *)item->u1.list->next->u1.str, (char *)item->u1.list->next->next->u1.str, that_other_context);
+ if (x3) {
+ return x3;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return x;
+ }
+ }
+ return 0;
+}
+
+static void check_goto(pval *item)
+{
+ /* check for the target of the goto-- does it exist? */
+ if ( !(item->u1.list)->next && !(item->u1.list)->u1.str ) {
+ ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto: empty label reference found!\n",
+ item->filename, item->startline, item->endline);
+ errs++;
+ }
+
+ /* just one item-- the label should be in the current extension */
+
+ if (item->u1.list && !item->u1.list->next && !strstr((item->u1.list)->u1.str,"${")) {
+ struct pval *z = get_extension_or_contxt(item);
+ struct pval *x = 0;
+ if (z)
+ x = find_label_in_current_extension((char*)((item->u1.list)->u1.str), z); /* if in macro, use current context instead */
+ /* printf("Called find_label_in_current_extension with arg %s; current_extension is %x: %d\n",
+ (char*)((item->u1.list)->u1.str), current_extension?current_extension:current_context, current_extension?current_extension->type:current_context->type); */
+ if (!x) {
+ ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto: no label %s exists in the current extension!\n",
+ item->filename, item->startline, item->endline, item->u1.list->u1.str);
+ errs++;
+ }
+ else
+ return;
+ }
+
+ /* TWO items */
+ if (item->u1.list->next && !item->u1.list->next->next) {
+ /* two items */
+ /* printf("Calling find_label_in_current_context with args %s, %s\n",
+ (char*)((item->u1.list)->u1.str), (char *)item->u1.list->next->u1.str); */
+ if (!strstr((item->u1.list)->u1.str,"${")
+ && !strstr(item->u1.list->next->u1.str,"${") ) /* Don't try to match variables */ {
+ struct pval *z = get_contxt(item);
+ struct pval *x = 0;
+
+ if (z)
+ x = find_label_in_current_context((char *)item->u1.list->u1.str, (char *)item->u1.list->next->u1.str, z);
+
+ if (!x) {
+ ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto: no label '%s,%s' exists in the current context, or any of its inclusions!\n",
+ item->filename, item->startline, item->endline, item->u1.list->u1.str, item->u1.list->next->u1.str );
+ errs++;
+ }
+ else
+ return;
+ }
+ }
+
+ /* All 3 items! */
+ if (item->u1.list->next && item->u1.list->next->next) {
+ /* all three */
+ pval *first = item->u1.list;
+ pval *second = item->u1.list->next;
+ pval *third = item->u1.list->next->next;
+
+ /* printf("Calling find_label_in_current_db with args %s, %s, %s\n",
+ (char*)first->u1.str, (char*)second->u1.str, (char*)third->u1.str); */
+ if (!strstr((item->u1.list)->u1.str,"${")
+ && !strstr(item->u1.list->next->u1.str,"${")
+ && !strstr(item->u1.list->next->next->u1.str,"${")) /* Don't try to match variables */ {
+ struct pval *x = find_label_in_current_db((char*)first->u1.str, (char*)second->u1.str, (char*)third->u1.str);
+ if (!x) {
+ struct pval *p3;
+ struct pval *found = 0;
+ struct pval *that_context = find_context(item->u1.list->u1.str);
+
+ /* the target of the goto could be in an included context!! Fancy that!! */
+ /* look for includes in the current context */
+ if (that_context) {
+ for (p3=that_context->u2.statements; p3; p3=p3->next) {
+ if (p3->type == PV_INCLUDES) {
+ struct pval *p4;
+ for (p4=p3->u1.list; p4; p4=p4->next) {
+ /* for each context pointed to, find it, then find a context/label that matches the
+ target here! */
+ char *incl_context = p4->u1.str;
+ /* find a matching context name */
+ struct pval *that_other_context = find_context(incl_context);
+ if (that_other_context) {
+ struct pval *x3;
+ x3 = find_label_in_current_context((char *)item->u1.list->next->u1.str, (char *)item->u1.list->next->next->u1.str, that_other_context);
+ if (x3) {
+ found = x3;
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (!found) {
+ ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto: no label %s|%s exists in the context %s or its inclusions!\n",
+ item->filename, item->startline, item->endline, item->u1.list->next->u1.str, item->u1.list->next->next->u1.str, item->u1.list->u1.str );
+ errs++;
+ } else {
+ struct pval *mac = in_macro(item); /* is this goto inside a macro? */
+ if( mac ) { /* yes! */
+ struct pval *targ = in_context(found);
+ if( mac != targ )
+ {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: It's bad form to have a goto in a macro to a target outside the macro!\n",
+ item->filename, item->startline, item->endline);
+ warns++;
+ }
+ }
+ }
+ } else {
+ /* here is where code would go to check for target existence in extensions.conf files */
+#ifdef STANDALONE
+ struct pbx_find_info pfiq = {.stacklen = 0 };
+ extern int localized_pbx_load_module(void);
+ /* if this is a standalone, we will need to make sure the
+ localized load of extensions.conf is done */
+ if (!extensions_dot_conf_loaded) {
+ localized_pbx_load_module();
+ extensions_dot_conf_loaded++;
+ }
+
+ pbx_find_extension(NULL, NULL, &pfiq, first->u1.str, second->u1.str, atoi(third->u1.str),
+ atoi(third->u1.str) ? NULL : third->u1.str, NULL,
+ atoi(third->u1.str) ? E_MATCH : E_FINDLABEL);
+
+ if (pfiq.status != STATUS_SUCCESS) {
+ ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: goto: Couldn't find goto target %s|%s|%s, not even in extensions.conf!\n",
+ item->filename, item->startline, item->endline, first->u1.str, second->u1.str, third->u1.str);
+ warns++;
+ }
+#else
+ ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: goto: Couldn't find goto target %s|%s|%s in the AEL code!\n",
+ item->filename, item->startline, item->endline, first->u1.str, second->u1.str, third->u1.str);
+ warns++;
+#endif
+ }
+ } else {
+ struct pval *mac = in_macro(item); /* is this goto inside a macro? */
+ if( mac ) { /* yes! */
+ struct pval *targ = in_context(x);
+ if( mac != targ )
+ {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: It's bad form to have a goto in a macro to a target outside the macro!\n",
+ item->filename, item->startline, item->endline);
+ warns++;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+static void find_pval_goto_item(pval *item, int lev)
+{
+ struct pval *p4;
+ if (lev>100) {
+ ast_log(LOG_ERROR,"find_pval_goto in infinite loop!\n\n");
+ return;
+ }
+
+ switch ( item->type ) {
+ case PV_MACRO:
+ /* fields: item->u1.str == name of macro
+ item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
+ item->u2.arglist->u1.str == argument
+ item->u2.arglist->next == next arg
+
+ item->u3.macro_statements == pval list of statements in macro body.
+ */
+
+ /* printf("Descending into matching macro %s\n", match_context); */
+ find_pval_gotos(item->u3.macro_statements,lev+1); /* if we're just searching for a context, don't bother descending into them */
+
+ break;
+
+ case PV_CONTEXT:
+ /* fields: item->u1.str == name of context
+ item->u2.statements == pval list of statements in context body
+ item->u3.abstract == int 1 if an abstract keyword were present
+ */
+ break;
+
+ case PV_CASE:
+ /* fields: item->u1.str == value of case
+ item->u2.statements == pval list of statements under the case
+ */
+ find_pval_gotos(item->u2.statements,lev+1);
+ break;
+
+ case PV_PATTERN:
+ /* fields: item->u1.str == value of case
+ item->u2.statements == pval list of statements under the case
+ */
+ find_pval_gotos(item->u2.statements,lev+1);
+ break;
+
+ case PV_DEFAULT:
+ /* fields:
+ item->u2.statements == pval list of statements under the case
+ */
+ find_pval_gotos(item->u2.statements,lev+1);
+ break;
+
+ case PV_CATCH:
+ /* fields: item->u1.str == name of extension to catch
+ item->u2.statements == pval list of statements in context body
+ */
+ find_pval_gotos(item->u2.statements,lev+1);
+ break;
+
+ case PV_STATEMENTBLOCK:
+ /* fields: item->u1.list == pval list of statements in block, one per entry in the list
+ */
+ find_pval_gotos(item->u1.list,lev+1);
+ break;
+
+ case PV_GOTO:
+ /* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user.
+ item->u1.list->u1.str == where the data on a PV_WORD will always be.
+ */
+ check_goto(item); /* THE WHOLE FUNCTION OF THIS ENTIRE ROUTINE!!!! */
+ break;
+
+ case PV_INCLUDES:
+ /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
+ */
+ for (p4=item->u1.list; p4; p4=p4->next) {
+ /* for each context pointed to, find it, then find a context/label that matches the
+ target here! */
+ char *incl_context = p4->u1.str;
+ /* find a matching context name */
+ struct pval *that_context = find_context(incl_context);
+ if (that_context) {
+ find_pval_gotos(that_context,lev+1); /* keep working up the includes */
+ }
+ }
+ break;
+
+ case PV_FOR:
+ /* fields: item->u1.for_init == a string containing the initalizer
+ item->u2.for_test == a string containing the loop test
+ item->u3.for_inc == a string containing the loop increment
+
+ item->u4.for_statements == a pval list of statements in the for ()
+ */
+ find_pval_gotos(item->u4.for_statements,lev+1);
+ break;
+
+ case PV_WHILE:
+ /* fields: item->u1.str == the while conditional, as supplied by user
+
+ item->u2.statements == a pval list of statements in the while ()
+ */
+ find_pval_gotos(item->u2.statements,lev+1);
+ break;
+
+ case PV_RANDOM:
+ /* fields: item->u1.str == the random number expression, as supplied by user
+
+ item->u2.statements == a pval list of statements in the if ()
+ item->u3.else_statements == a pval list of statements in the else
+ (could be zero)
+ fall thru to PV_IF */
+
+ case PV_IFTIME:
+ /* fields: item->u1.list == the time values, 4 of them, as PV_WORD structs in a list
+
+ item->u2.statements == a pval list of statements in the if ()
+ item->u3.else_statements == a pval list of statements in the else
+ (could be zero)
+ fall thru to PV_IF*/
+ case PV_IF:
+ /* fields: item->u1.str == the if conditional, as supplied by user
+
+ item->u2.statements == a pval list of statements in the if ()
+ item->u3.else_statements == a pval list of statements in the else
+ (could be zero)
+ */
+ find_pval_gotos(item->u2.statements,lev+1);
+
+ if (item->u3.else_statements) {
+ find_pval_gotos(item->u3.else_statements,lev+1);
+ }
+ break;
+
+ case PV_SWITCH:
+ /* fields: item->u1.str == the switch expression
+
+ item->u2.statements == a pval list of statements in the switch,
+ (will be case statements, most likely!)
+ */
+ find_pval_gotos(item->u3.else_statements,lev+1);
+ break;
+
+ case PV_EXTENSION:
+ /* fields: item->u1.str == the extension name, label, whatever it's called
+
+ item->u2.statements == a pval list of statements in the extension
+ item->u3.hints == a char * hint argument
+ item->u4.regexten == an int boolean. non-zero says that regexten was specified
+ */
+
+ find_pval_gotos(item->u2.statements,lev+1);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void find_pval_gotos(pval *item,int lev)
+{
+ pval *i;
+
+ for (i=item; i; i=i->next) {
+
+ find_pval_goto_item(i, lev);
+ }
+}
+
+
+
+/* general purpose label finder */
+static struct pval *match_pval_item(pval *item)
+{
+ pval *x;
+
+ switch ( item->type ) {
+ case PV_MACRO:
+ /* fields: item->u1.str == name of macro
+ item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
+ item->u2.arglist->u1.str == argument
+ item->u2.arglist->next == next arg
+
+ item->u3.macro_statements == pval list of statements in macro body.
+ */
+ /* printf(" matching in MACRO %s, match_context=%s; retoncontmtch=%d; \n", item->u1.str, match_context, return_on_context_match); */
+ if (!strcmp(match_context,"*") || !strcmp(item->u1.str, match_context)) {
+
+ /* printf("MACRO: match context is: %s\n", match_context); */
+
+ if (return_on_context_match && !strcmp(item->u1.str, match_context)) /* if we're just searching for a context, don't bother descending into them */ {
+ /* printf("Returning on matching macro %s\n", match_context); */
+ return item;
+ }
+
+
+ if (!return_on_context_match) {
+ /* printf("Descending into matching macro %s/%s\n", match_context, item->u1.str); */
+ if ((x=match_pval(item->u3.macro_statements))) {
+ /* printf("Responded with pval match %x\n", x); */
+ return x;
+ }
+ }
+ } else {
+ /* printf("Skipping context/macro %s\n", item->u1.str); */
+ }
+
+ break;
+
+ case PV_CONTEXT:
+ /* fields: item->u1.str == name of context
+ item->u2.statements == pval list of statements in context body
+ item->u3.abstract == int 1 if an abstract keyword were present
+ */
+ /* printf(" matching in CONTEXT\n"); */
+ if (!strcmp(match_context,"*") || !strcmp(item->u1.str, match_context)) {
+ if (return_on_context_match && !strcmp(item->u1.str, match_context)) {
+ /* printf("Returning on matching context %s\n", match_context); */
+ /* printf("non-CONTEXT: Responded with pval match %x\n", x); */
+ return item;
+ }
+
+ if (!return_on_context_match ) {
+ /* printf("Descending into matching context %s\n", match_context); */
+ if ((x=match_pval(item->u2.statements))) /* if we're just searching for a context, don't bother descending into them */ {
+ /* printf("CONTEXT: Responded with pval match %x\n", x); */
+ return x;
+ }
+ }
+ } else {
+ /* printf("Skipping context/macro %s\n", item->u1.str); */
+ }
+ break;
+
+ case PV_CASE:
+ /* fields: item->u1.str == value of case
+ item->u2.statements == pval list of statements under the case
+ */
+ /* printf(" matching in CASE\n"); */
+ if ((x=match_pval(item->u2.statements))) {
+ /* printf("CASE: Responded with pval match %x\n", x); */
+ return x;
+ }
+ break;
+
+ case PV_PATTERN:
+ /* fields: item->u1.str == value of case
+ item->u2.statements == pval list of statements under the case
+ */
+ /* printf(" matching in PATTERN\n"); */
+ if ((x=match_pval(item->u2.statements))) {
+ /* printf("PATTERN: Responded with pval match %x\n", x); */
+ return x;
+ }
+ break;
+
+ case PV_DEFAULT:
+ /* fields:
+ item->u2.statements == pval list of statements under the case
+ */
+ /* printf(" matching in DEFAULT\n"); */
+ if ((x=match_pval(item->u2.statements))) {
+ /* printf("DEFAULT: Responded with pval match %x\n", x); */
+ return x;
+ }
+ break;
+
+ case PV_CATCH:
+ /* fields: item->u1.str == name of extension to catch
+ item->u2.statements == pval list of statements in context body
+ */
+ /* printf(" matching in CATCH\n"); */
+ if ((x=match_pval(item->u2.statements))) {
+ /* printf("CATCH: Responded with pval match %x\n", x); */
+ return x;
+ }
+ break;
+
+ case PV_STATEMENTBLOCK:
+ /* fields: item->u1.list == pval list of statements in block, one per entry in the list
+ */
+ /* printf(" matching in STATEMENTBLOCK\n"); */
+ if ((x=match_pval(item->u1.list))) {
+ /* printf("STATEMENTBLOCK: Responded with pval match %x\n", x); */
+ return x;
+ }
+ break;
+
+ case PV_LABEL:
+ /* fields: item->u1.str == label name
+ */
+ /* printf("PV_LABEL %s (cont=%s, exten=%s\n",
+ item->u1.str, current_context->u1.str, (current_extension?current_extension->u1.str:"<macro>"));*/
+
+ if (count_labels) {
+ if (!strcmp(match_label, item->u1.str)) {
+ label_count++;
+ last_matched_label = item;
+ }
+
+ } else {
+ if (!strcmp(match_label, item->u1.str)) {
+ /* printf("LABEL: Responded with pval match %x\n", x); */
+ return item;
+ }
+ }
+ break;
+
+ case PV_FOR:
+ /* fields: item->u1.for_init == a string containing the initalizer
+ item->u2.for_test == a string containing the loop test
+ item->u3.for_inc == a string containing the loop increment
+
+ item->u4.for_statements == a pval list of statements in the for ()
+ */
+ /* printf(" matching in FOR\n"); */
+ if ((x=match_pval(item->u4.for_statements))) {
+ /* printf("FOR: Responded with pval match %x\n", x);*/
+ return x;
+ }
+ break;
+
+ case PV_WHILE:
+ /* fields: item->u1.str == the while conditional, as supplied by user
+
+ item->u2.statements == a pval list of statements in the while ()
+ */
+ /* printf(" matching in WHILE\n"); */
+ if ((x=match_pval(item->u2.statements))) {
+ /* printf("WHILE: Responded with pval match %x\n", x); */
+ return x;
+ }
+ break;
+
+ case PV_RANDOM:
+ /* fields: item->u1.str == the random number expression, as supplied by user
+
+ item->u2.statements == a pval list of statements in the if ()
+ item->u3.else_statements == a pval list of statements in the else
+ (could be zero)
+ fall thru to PV_IF */
+
+ case PV_IFTIME:
+ /* fields: item->u1.list == the time values, 4 of them, as PV_WORD structs in a list
+
+ item->u2.statements == a pval list of statements in the if ()
+ item->u3.else_statements == a pval list of statements in the else
+ (could be zero)
+ fall thru to PV_IF*/
+ case PV_IF:
+ /* fields: item->u1.str == the if conditional, as supplied by user
+
+ item->u2.statements == a pval list of statements in the if ()
+ item->u3.else_statements == a pval list of statements in the else
+ (could be zero)
+ */
+ /* printf(" matching in IF/IFTIME/RANDOM\n"); */
+ if ((x=match_pval(item->u2.statements))) {
+ return x;
+ }
+ if (item->u3.else_statements) {
+ if ((x=match_pval(item->u3.else_statements))) {
+ /* printf("IF/IFTIME/RANDOM: Responded with pval match %x\n", x); */
+ return x;
+ }
+ }
+ break;
+
+ case PV_SWITCH:
+ /* fields: item->u1.str == the switch expression
+
+ item->u2.statements == a pval list of statements in the switch,
+ (will be case statements, most likely!)
+ */
+ /* printf(" matching in SWITCH\n"); */
+ if ((x=match_pval(item->u2.statements))) {
+ /* printf("SWITCH: Responded with pval match %x\n", x); */
+ return x;
+ }
+ break;
+
+ case PV_EXTENSION:
+ /* fields: item->u1.str == the extension name, label, whatever it's called
+
+ item->u2.statements == a pval list of statements in the extension
+ item->u3.hints == a char * hint argument
+ item->u4.regexten == an int boolean. non-zero says that regexten was specified
+ */
+ /* printf(" matching in EXTENSION\n"); */
+ if (!strcmp(match_exten,"*") || extension_matches(item, match_exten, item->u1.str) ) {
+ /* printf("Descending into matching exten %s => %s\n", match_exten, item->u1.str); */
+ if (strcmp(match_label,"1") == 0) {
+ if (item->u2.statements) {
+ struct pval *p5 = item->u2.statements;
+ while (p5 && p5->type == PV_LABEL) /* find the first non-label statement in this context. If it exists, there's a "1" */
+ p5 = p5->next;
+ if (p5)
+ return p5;
+ else
+ return 0;
+ }
+ else
+ return 0;
+ }
+
+ if ((x=match_pval(item->u2.statements))) {
+ /* printf("EXTENSION: Responded with pval match %x\n", x); */
+ return x;
+ }
+ } else {
+ /* printf("Skipping exten %s\n", item->u1.str); */
+ }
+ break;
+ default:
+ /* printf(" matching in default = %d\n", item->type); */
+ break;
+ }
+ return 0;
+}
+
+struct pval *match_pval(pval *item)
+{
+ pval *i;
+
+ for (i=item; i; i=i->next) {
+ pval *x;
+ /* printf(" -- match pval: item %d\n", i->type); */
+
+ if ((x = match_pval_item(i))) {
+ /* printf("match_pval: returning x=%x\n", (int)x); */
+ return x; /* cut the search short */
+ }
+ }
+ return 0;
+}
+
+#if 0
+int count_labels_in_current_context(char *label)
+{
+ label_count = 0;
+ count_labels = 1;
+ return_on_context_match = 0;
+ match_pval(current_context->u2.statements);
+
+ return label_count;
+}
+#endif
+
+struct pval *find_first_label_in_current_context(char *label, pval *curr_cont)
+{
+ /* printf(" --- Got args %s, %s\n", exten, label); */
+ struct pval *ret;
+ struct pval *p3;
+
+ count_labels = 0;
+ return_on_context_match = 0;
+ match_context = "*";
+ match_exten = "*";
+ match_label = label;
+
+ ret = match_pval(curr_cont);
+ if (ret)
+ return ret;
+
+ /* the target of the goto could be in an included context!! Fancy that!! */
+ /* look for includes in the current context */
+ for (p3=curr_cont->u2.statements; p3; p3=p3->next) {
+ if (p3->type == PV_INCLUDES) {
+ struct pval *p4;
+ for (p4=p3->u1.list; p4; p4=p4->next) {
+ /* for each context pointed to, find it, then find a context/label that matches the
+ target here! */
+ char *incl_context = p4->u1.str;
+ /* find a matching context name */
+ struct pval *that_context = find_context(incl_context);
+ if (that_context) {
+ struct pval *x3;
+ x3 = find_first_label_in_current_context(label, that_context);
+ if (x3) {
+ return x3;
+ }
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+struct pval *find_label_in_current_context(char *exten, char *label, pval *curr_cont)
+{
+ /* printf(" --- Got args %s, %s\n", exten, label); */
+ struct pval *ret;
+ struct pval *p3;
+
+ count_labels = 0;
+ return_on_context_match = 0;
+ match_context = "*";
+ match_exten = exten;
+ match_label = label;
+ ret = match_pval(curr_cont->u2.statements);
+ if (ret)
+ return ret;
+
+ /* the target of the goto could be in an included context!! Fancy that!! */
+ /* look for includes in the current context */
+ for (p3=curr_cont->u2.statements; p3; p3=p3->next) {
+ if (p3->type == PV_INCLUDES) {
+ struct pval *p4;
+ for (p4=p3->u1.list; p4; p4=p4->next) {
+ /* for each context pointed to, find it, then find a context/label that matches the
+ target here! */
+ char *incl_context = p4->u1.str;
+ /* find a matching context name */
+ struct pval *that_context = find_context(incl_context);
+ if (that_context) {
+ struct pval *x3;
+ x3 = find_label_in_current_context(exten, label, that_context);
+ if (x3) {
+ return x3;
+ }
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static struct pval *find_label_in_current_extension(const char *label, pval *curr_ext)
+{
+ /* printf(" --- Got args %s\n", label); */
+ count_labels = 0;
+ return_on_context_match = 0;
+ match_context = "*";
+ match_exten = "*";
+ match_label = label;
+ return match_pval(curr_ext);
+}
+
+static struct pval *find_label_in_current_db(const char *context, const char *exten, const char *label)
+{
+ /* printf(" --- Got args %s, %s, %s\n", context, exten, label); */
+ count_labels = 0;
+ return_on_context_match = 0;
+
+ match_context = context;
+ match_exten = exten;
+ match_label = label;
+
+ return match_pval(current_db);
+}
+
+
+struct pval *find_macro(char *name)
+{
+ return_on_context_match = 1;
+ count_labels = 0;
+ match_context = name;
+ match_exten = "*"; /* don't really need to set these, shouldn't be reached */
+ match_label = "*";
+ return match_pval(current_db);
+}
+
+struct pval *find_context(char *name)
+{
+ return_on_context_match = 1;
+ count_labels = 0;
+ match_context = name;
+ match_exten = "*"; /* don't really need to set these, shouldn't be reached */
+ match_label = "*";
+ return match_pval(current_db);
+}
+
+int is_float(char *arg )
+{
+ char *s;
+ for (s=arg; *s; s++) {
+ if (*s != '.' && (*s < '0' || *s > '9'))
+ return 0;
+ }
+ return 1;
+}
+int is_int(char *arg )
+{
+ char *s;
+ for (s=arg; *s; s++) {
+ if (*s < '0' || *s > '9')
+ return 0;
+ }
+ return 1;
+}
+int is_empty(char *arg)
+{
+ if (!arg)
+ return 1;
+ if (*arg == 0)
+ return 1;
+ while (*arg) {
+ if (*arg != ' ' && *arg != '\t')
+ return 0;
+ arg++;
+ }
+ return 1;
+}
+
+#ifdef AAL_ARGCHECK
+int option_matches_j( struct argdesc *should, pval *is, struct argapp *app)
+{
+ struct argchoice *ac;
+ char *opcop,*q,*p;
+
+ switch (should->dtype) {
+ case ARGD_OPTIONSET:
+ if ( strstr(is->u1.str,"${") )
+ return 0; /* no checking anything if there's a var reference in there! */
+
+ opcop = ast_strdupa(is->u1.str);
+
+ for (q=opcop;*q;q++) { /* erase the innards of X(innard) type arguments, so we don't get confused later */
+ if ( *q == '(' ) {
+ p = q+1;
+ while (*p && *p != ')' )
+ *p++ = '+';
+ q = p+1;
+ }
+ }
+
+ for (ac=app->opts; ac; ac=ac->next) {
+ if (strlen(ac->name)>1 && strchr(ac->name,'(') == 0 && strcmp(ac->name,is->u1.str) == 0) /* multichar option, no parens, and a match? */
+ return 0;
+ }
+ for (ac=app->opts; ac; ac=ac->next) {
+ if (strlen(ac->name)==1 || strchr(ac->name,'(')) {
+ char *p = strchr(opcop,ac->name[0]); /* wipe out all matched options in the user-supplied string */
+
+ if (p && *p == 'j') {
+ ast_log(LOG_ERROR, "Error: file %s, line %d-%d: The j option in the %s application call is not appropriate for AEL!\n",
+ is->filename, is->startline, is->endline, app->name);
+ errs++;
+ }
+
+ if (p) {
+ *p = '+';
+ if (ac->name[1] == '(') {
+ if (*(p+1) != '(') {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The %c option in the %s application call should have an (argument), but doesn't!\n",
+ is->filename, is->startline, is->endline, ac->name[0], app->name);
+ warns++;
+ }
+ }
+ }
+ }
+ }
+ for (q=opcop; *q; q++) {
+ if ( *q != '+' && *q != '(' && *q != ')') {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The %c option in the %s application call is not available as an option!\n",
+ is->filename, is->startline, is->endline, *q, app->name);
+ warns++;
+ }
+ }
+ return 1;
+ break;
+ default:
+ return 0;
+ }
+
+}
+
+int option_matches( struct argdesc *should, pval *is, struct argapp *app)
+{
+ struct argchoice *ac;
+ char *opcop;
+
+ switch (should->dtype) {
+ case ARGD_STRING:
+ if (is_empty(is->u1.str) && should->type == ARGD_REQUIRED)
+ return 0;
+ if (is->u1.str && strlen(is->u1.str) > 0) /* most will match */
+ return 1;
+ break;
+
+ case ARGD_INT:
+ if (is_int(is->u1.str))
+ return 1;
+ else
+ return 0;
+ break;
+
+ case ARGD_FLOAT:
+ if (is_float(is->u1.str))
+ return 1;
+ else
+ return 0;
+ break;
+
+ case ARGD_ENUM:
+ if( !is->u1.str || strlen(is->u1.str) == 0 )
+ return 1; /* a null arg in the call will match an enum, I guess! */
+ for (ac=should->choices; ac; ac=ac->next) {
+ if (strcmp(ac->name,is->u1.str) == 0)
+ return 1;
+ }
+ return 0;
+ break;
+
+ case ARGD_OPTIONSET:
+ opcop = ast_strdupa(is->u1.str);
+
+ for (ac=app->opts; ac; ac=ac->next) {
+ if (strlen(ac->name)>1 && strchr(ac->name,'(') == 0 && strcmp(ac->name,is->u1.str) == 0) /* multichar option, no parens, and a match? */
+ return 1;
+ }
+ for (ac=app->opts; ac; ac=ac->next) {
+ if (strlen(ac->name)==1 || strchr(ac->name,'(')) {
+ char *p = strchr(opcop,ac->name[0]); /* wipe out all matched options in the user-supplied string */
+
+ if (p) {
+ *p = '+';
+ if (ac->name[1] == '(') {
+ if (*(p+1) == '(') {
+ char *q = p+1;
+ while (*q && *q != ')') {
+ *q++ = '+';
+ }
+ *q = '+';
+ }
+ }
+ }
+ }
+ }
+ return 1;
+ break;
+ case ARGD_VARARG:
+ return 1; /* matches anything */
+ break;
+ }
+ return 1; /* unless some for-sure match or non-match returns, then it must be close enough ... */
+}
+#endif
+
+int check_app_args(pval* appcall, pval *arglist, struct argapp *app)
+{
+#ifdef AAL_ARGCHECK
+ struct argdesc *ad = app->args;
+ pval *pa;
+ int z;
+
+ for (pa = arglist; pa; pa=pa->next) {
+ if (!ad) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: Extra argument %s not in application call to %s !\n",
+ arglist->filename, arglist->startline, arglist->endline, pa->u1.str, app->name);
+ warns++;
+ return 1;
+ } else {
+ /* find the first entry in the ad list that will match */
+ do {
+ if ( ad->dtype == ARGD_VARARG ) /* once we hit the VARARG, all bets are off. Discontinue the comparisons */
+ break;
+
+ z= option_matches( ad, pa, app);
+ if (!z) {
+ if ( !arglist )
+ arglist=appcall;
+
+ if (ad->type == ARGD_REQUIRED) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: Required argument %s not in application call to %s !\n",
+ arglist->filename, arglist->startline, arglist->endline, ad->dtype==ARGD_OPTIONSET?"options":ad->name, app->name);
+ warns++;
+ return 1;
+ }
+ } else if (z && ad->dtype == ARGD_OPTIONSET) {
+ option_matches_j( ad, pa, app);
+ }
+ ad = ad->next;
+ } while (ad && !z);
+ }
+ }
+ /* any app nodes left, that are not optional? */
+ for ( ; ad; ad=ad->next) {
+ if (ad->type == ARGD_REQUIRED && ad->dtype != ARGD_VARARG) {
+ if ( !arglist )
+ arglist=appcall;
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: Required argument %s not in application call to %s !\n",
+ arglist->filename, arglist->startline, arglist->endline, ad->dtype==ARGD_OPTIONSET?"options":ad->name, app->name);
+ warns++;
+ return 1;
+ }
+ }
+ return 0;
+#else
+ return 0;
+#endif
+}
+
+void check_switch_expr(pval *item, struct argapp *apps)
+{
+#ifdef AAL_ARGCHECK
+ /* get and clean the variable name */
+ char *buff1, *p;
+ struct argapp *a,*a2;
+ struct appsetvar *v,*v2;
+ struct argchoice *c;
+ pval *t;
+
+ p = item->u1.str;
+ while (p && *p && (*p == ' ' || *p == '\t' || *p == '$' || *p == '{' ) )
+ p++;
+
+ buff1 = ast_strdupa(p);
+
+ while (strlen(buff1) > 0 && ( buff1[strlen(buff1)-1] == '}' || buff1[strlen(buff1)-1] == ' ' || buff1[strlen(buff1)-1] == '\t'))
+ buff1[strlen(buff1)-1] = 0;
+ /* buff1 now contains the variable name */
+ v = 0;
+ for (a=apps; a; a=a->next) {
+ for (v=a->setvars;v;v=v->next) {
+ if (strcmp(v->name,buff1) == 0) {
+ break;
+ }
+ }
+ if ( v )
+ break;
+ }
+ if (v && v->vals) {
+ /* we have a match, to a variable that has a set of determined values */
+ int def= 0;
+ int pat = 0;
+ int f1 = 0;
+
+ /* first of all, does this switch have a default case ? */
+ for (t=item->u2.statements; t; t=t->next) {
+ if (t->type == PV_DEFAULT) {
+ def =1;
+ break;
+ }
+ if (t->type == PV_PATTERN) {
+ pat++;
+ }
+ }
+ if (def || pat) /* nothing to check. All cases accounted for! */
+ return;
+ for (c=v->vals; c; c=c->next) {
+ f1 = 0;
+ for (t=item->u2.statements; t; t=t->next) {
+ if (t->type == PV_CASE || t->type == PV_PATTERN) {
+ if (!strcmp(t->u1.str,c->name)) {
+ f1 = 1;
+ break;
+ }
+ }
+ }
+ if (!f1) {
+ ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: switch with expression(%s) does not handle the case of %s !\n",
+ item->filename, item->startline, item->endline, item->u1.str, c->name);
+ warns++;
+ }
+ }
+ /* next, is there an app call in the current exten, that would set this var? */
+ f1 = 0;
+ t = current_extension->u2.statements;
+ if ( t && t->type == PV_STATEMENTBLOCK )
+ t = t->u1.statements;
+ for (; t && t != item; t=t->next) {
+ if (t->type == PV_APPLICATION_CALL) {
+ /* find the application that matches the u1.str */
+ for (a2=apps; a2; a2=a2->next) {
+ if (strcasecmp(a2->name, t->u1.str)==0) {
+ for (v2=a2->setvars; v2; v2=v2->next) {
+ if (strcmp(v2->name, buff1) == 0) {
+ /* found an app that sets the var */
+ f1 = 1;
+ break;
+ }
+ }
+ }
+ if (f1)
+ break;
+ }
+ }
+ if (f1)
+ break;
+ }
+
+ /* see if it sets the var */
+ if (!f1) {
+ ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: Couldn't find an application call in this extension that sets the expression (%s) value!\n",
+ item->filename, item->startline, item->endline, item->u1.str);
+ warns++;
+ }
+ }
+#else
+ pval *t,*tl=0,*p2;
+ int def= 0;
+
+ /* first of all, does this switch have a default case ? */
+ for (t=item->u2.statements; t; t=t->next) {
+ if (t->type == PV_DEFAULT) {
+ def =1;
+ break;
+ }
+ tl = t;
+ }
+ if (def) /* nothing to check. All cases accounted for! */
+ return;
+ /* if no default, warn and insert a default case at the end */
+ p2 = tl->next = calloc(1, sizeof(struct pval));
+
+ p2->type = PV_DEFAULT;
+ p2->startline = tl->startline;
+ p2->endline = tl->endline;
+ p2->startcol = tl->startcol;
+ p2->endcol = tl->endcol;
+ p2->filename = strdup(tl->filename);
+ ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: A default case was automatically added to the switch.\n",
+ p2->filename, p2->startline, p2->endline);
+ warns++;
+
+#endif
+}
+
+static void check_context_names(void)
+{
+ pval *i,*j;
+ for (i=current_db; i; i=i->next) {
+ if (i->type == PV_CONTEXT || i->type == PV_MACRO) {
+ for (j=i->next; j; j=j->next) {
+ if ( j->type == PV_CONTEXT || j->type == PV_MACRO ) {
+ if ( !strcmp(i->u1.str, j->u1.str) )
+ {
+ ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: The context name (%s) is also declared in file %s, line %d-%d!\n",
+ i->filename, i->startline, i->endline, i->u1.str, j->filename, j->startline, j->endline);
+ warns++;
+ }
+ }
+ }
+ }
+ }
+}
+
+static void check_abstract_reference(pval *abstract_context)
+{
+ pval *i,*j;
+ /* find some context includes that reference this context */
+
+
+ /* otherwise, print out a warning */
+ for (i=current_db; i; i=i->next) {
+ if (i->type == PV_CONTEXT) {
+ for (j=i->u2. statements; j; j=j->next) {
+ if ( j->type == PV_INCLUDES ) {
+ struct pval *p4;
+ for (p4=j->u1.list; p4; p4=p4->next) {
+ /* for each context pointed to, find it, then find a context/label that matches the
+ target here! */
+ if ( !strcmp(p4->u1.str, abstract_context->u1.str) )
+ return; /* found a match! */
+ }
+ }
+ }
+ }
+ }
+ ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: Couldn't find a reference to this abstract context (%s) in any other context!\n",
+ abstract_context->filename, abstract_context->startline, abstract_context->endline, abstract_context->u1.str);
+ warns++;
+}
+
+
+void check_pval_item(pval *item, struct argapp *apps, int in_globals)
+{
+ pval *lp;
+#ifdef AAL_ARGCHECK
+ struct argapp *app, *found;
+#endif
+ struct pval *macro_def;
+ struct pval *app_def;
+
+ char errmsg[4096];
+ char *strp;
+
+ switch (item->type) {
+ case PV_WORD:
+ /* fields: item->u1.str == string associated with this (word).
+ item->u2.arglist == pval list of 4 PV_WORD elements for time values (only in PV_INCLUDES) */
+ break;
+
+ case PV_MACRO:
+ /* fields: item->u1.str == name of macro
+ item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
+ item->u2.arglist->u1.str == argument
+ item->u2.arglist->next == next arg
+
+ item->u3.macro_statements == pval list of statements in macro body.
+ */
+ in_abstract_context = 0;
+ current_context = item;
+ current_extension = 0;
+
+ check_macro_returns(item);
+
+ for (lp=item->u2.arglist; lp; lp=lp->next) {
+
+ }
+ check_pval(item->u3.macro_statements, apps,in_globals);
+ break;
+
+ case PV_CONTEXT:
+ /* fields: item->u1.str == name of context
+ item->u2.statements == pval list of statements in context body
+ item->u3.abstract == int 1 if an abstract keyword were present
+ */
+ current_context = item;
+ current_extension = 0;
+ if ( item->u3.abstract ) {
+ in_abstract_context = 1;
+ check_abstract_reference(item);
+ } else
+ in_abstract_context = 0;
+ check_pval(item->u2.statements, apps,in_globals);
+ break;
+
+ case PV_MACRO_CALL:
+ /* fields: item->u1.str == name of macro to call
+ item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
+ item->u2.arglist->u1.str == argument
+ item->u2.arglist->next == next arg
+ */
+#ifdef STANDALONE
+ /* if this is a standalone, we will need to make sure the
+ localized load of extensions.conf is done */
+ if (!extensions_dot_conf_loaded) {
+ localized_pbx_load_module();
+ extensions_dot_conf_loaded++;
+ }
+#endif
+ macro_def = find_macro(item->u1.str);
+ if (!macro_def) {
+#ifdef STANDALONE
+ struct pbx_find_info pfiq = {.stacklen = 0 };
+ struct pbx_find_info pfiq2 = {.stacklen = 0 };
+
+ /* look for the macro in the extensions.conf world */
+ pbx_find_extension(NULL, NULL, &pfiq, item->u1.str, "s", 1, NULL, NULL, E_MATCH);
+
+ if (pfiq.status != STATUS_SUCCESS) {
+ char namebuf2[256];
+ snprintf(namebuf2, 256, "macro-%s", item->u1.str);
+
+ /* look for the macro in the extensions.conf world */
+ pbx_find_extension(NULL, NULL, &pfiq2, namebuf2, "s", 1, NULL, NULL, E_MATCH);
+
+ if (pfiq2.status == STATUS_SUCCESS) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: macro call to non-existent %s! (macro-%s was found in the extensions.conf stuff, but we are using gosubs!)\n",
+ item->filename, item->startline, item->endline, item->u1.str, item->u1.str);
+ warns++;
+ } else {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: macro call to non-existent %s! (Not even in the extensions.conf stuff!)\n",
+ item->filename, item->startline, item->endline, item->u1.str);
+ warns++;
+ }
+ }
+#else
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: macro call to %s cannot be found in the AEL code!\n",
+ item->filename, item->startline, item->endline, item->u1.str);
+ warns++;
+
+#endif
+#ifdef THIS_IS_1DOT4
+ char namebuf2[256];
+ snprintf(namebuf2, 256, "macro-%s", item->u1.str);
+
+ /* look for the macro in the extensions.conf world */
+ pbx_find_extension(NULL, NULL, &pfiq, namebuf2, "s", 1, NULL, NULL, E_MATCH);
+
+ if (pfiq.status != STATUS_SUCCESS) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: macro call to %s was not found in the AEL, nor the extensions.conf !\n",
+ item->filename, item->startline, item->endline, item->u1.str);
+ warns++;
+ }
+
+#endif
+
+ } else if (macro_def->type != PV_MACRO) {
+ ast_log(LOG_ERROR,"Error: file %s, line %d-%d: macro call to %s references a context, not a macro!\n",
+ item->filename, item->startline, item->endline, item->u1.str);
+ errs++;
+ } else {
+ /* macro_def is a MACRO, so do the args match in number? */
+ int hereargs = 0;
+ int thereargs = 0;
+
+ for (lp=item->u2.arglist; lp; lp=lp->next) {
+ hereargs++;
+ }
+ for (lp=macro_def->u2.arglist; lp; lp=lp->next) {
+ thereargs++;
+ }
+ if (hereargs != thereargs ) {
+ ast_log(LOG_ERROR, "Error: file %s, line %d-%d: The macro call to %s has %d arguments, but the macro definition has %d arguments\n",
+ item->filename, item->startline, item->endline, item->u1.str, hereargs, thereargs);
+ errs++;
+ }
+ }
+ break;
+
+ case PV_APPLICATION_CALL:
+ /* fields: item->u1.str == name of application to call
+ item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
+ item->u2.arglist->u1.str == argument
+ item->u2.arglist->next == next arg
+ */
+ /* Need to check to see if the application is available! */
+ app_def = find_context(item->u1.str);
+ if (app_def && app_def->type == PV_MACRO) {
+ ast_log(LOG_ERROR,"Error: file %s, line %d-%d: application call to %s references an existing macro, but had no & preceding it!\n",
+ item->filename, item->startline, item->endline, item->u1.str);
+ errs++;
+ }
+ if (strcasecmp(item->u1.str,"GotoIf") == 0
+ || strcasecmp(item->u1.str,"GotoIfTime") == 0
+ || strcasecmp(item->u1.str,"while") == 0
+ || strcasecmp(item->u1.str,"endwhile") == 0
+ || strcasecmp(item->u1.str,"random") == 0
+ || strcasecmp(item->u1.str,"gosub") == 0
+ || strcasecmp(item->u1.str,"return") == 0
+ || strcasecmp(item->u1.str,"gosubif") == 0
+ || strcasecmp(item->u1.str,"continuewhile") == 0
+ || strcasecmp(item->u1.str,"endwhile") == 0
+ || strcasecmp(item->u1.str,"execif") == 0
+ || strcasecmp(item->u1.str,"execiftime") == 0
+ || strcasecmp(item->u1.str,"exitwhile") == 0
+ || strcasecmp(item->u1.str,"goto") == 0
+ || strcasecmp(item->u1.str,"macro") == 0
+ || strcasecmp(item->u1.str,"macroexclusive") == 0
+ || strcasecmp(item->u1.str,"macroif") == 0
+ || strcasecmp(item->u1.str,"stackpop") == 0
+ || strcasecmp(item->u1.str,"execIf") == 0 ) {
+ ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: application call to %s affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead!\n",
+ item->filename, item->startline, item->endline, item->u1.str);
+ warns++;
+ }
+ if (strcasecmp(item->u1.str,"macroexit") == 0) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: I am converting the MacroExit call here to a return statement.\n",
+ item->filename, item->startline, item->endline);
+ item->type = PV_RETURN;
+ free(item->u1.str);
+ item->u1.str = 0;
+ }
+
+#ifdef AAL_ARGCHECK
+ found = 0;
+ for (app=apps; app; app=app->next) {
+ if (strcasecmp(app->name, item->u1.str) == 0) {
+ found =app;
+ break;
+ }
+ }
+ if (!found) {
+ ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: application call to %s not listed in applist database!\n",
+ item->filename, item->startline, item->endline, item->u1.str);
+ warns++;
+ } else
+ check_app_args(item, item->u2.arglist, app);
+#endif
+ break;
+
+ case PV_CASE:
+ /* fields: item->u1.str == value of case
+ item->u2.statements == pval list of statements under the case
+ */
+ /* Make sure sequence of statements under case is terminated with goto, return, or break */
+ /* find the last statement */
+ check_pval(item->u2.statements, apps,in_globals);
+ break;
+
+ case PV_PATTERN:
+ /* fields: item->u1.str == value of case
+ item->u2.statements == pval list of statements under the case
+ */
+ /* Make sure sequence of statements under case is terminated with goto, return, or break */
+ /* find the last statement */
+
+ check_pval(item->u2.statements, apps,in_globals);
+ break;
+
+ case PV_DEFAULT:
+ /* fields:
+ item->u2.statements == pval list of statements under the case
+ */
+
+ check_pval(item->u2.statements, apps,in_globals);
+ break;
+
+ case PV_CATCH:
+ /* fields: item->u1.str == name of extension to catch
+ item->u2.statements == pval list of statements in context body
+ */
+ check_pval(item->u2.statements, apps,in_globals);
+ break;
+
+ case PV_SWITCHES:
+ /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
+ */
+ check_pval(item->u1.list, apps,in_globals);
+ break;
+
+ case PV_ESWITCHES:
+ /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
+ */
+ check_pval(item->u1.list, apps,in_globals);
+ break;
+
+ case PV_INCLUDES:
+ /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
+ */
+ check_pval(item->u1.list, apps,in_globals);
+ check_includes(item);
+ for (lp=item->u1.list; lp; lp=lp->next){
+ char *incl_context = lp->u1.str;
+ struct pval *that_context = find_context(incl_context);
+
+ if ( lp->u2.arglist ) {
+ check_timerange(lp->u2.arglist);
+ check_dow(lp->u2.arglist->next);
+ check_day(lp->u2.arglist->next->next);
+ check_month(lp->u2.arglist->next->next->next);
+ }
+
+ if (that_context) {
+ find_pval_gotos(that_context->u2.statements,0);
+
+ }
+ }
+ break;
+
+ case PV_STATEMENTBLOCK:
+ /* fields: item->u1.list == pval list of statements in block, one per entry in the list
+ */
+ check_pval(item->u1.list, apps,in_globals);
+ break;
+
+ case PV_VARDEC:
+ /* fields: item->u1.str == variable name
+ item->u2.val == variable value to assign
+ */
+ /* the RHS of a vardec is encapsulated in a $[] expr. Is it legal? */
+ if( !in_globals ) { /* don't check stuff inside the globals context; no wrapping in $[ ] there... */
+ snprintf(errmsg,sizeof(errmsg), "file %s, line %d, columns %d-%d, variable declaration expr '%s':", item->filename, item->startline, item->startcol, item->endcol, item->u2.val);
+ ast_expr_register_extra_error_info(errmsg);
+ ast_expr(item->u2.val, expr_output, sizeof(expr_output),NULL);
+ ast_expr_clear_extra_error_info();
+ if ( strpbrk(item->u2.val,"~!-+<>=*/&^") && !strstr(item->u2.val,"${") ) {
+ ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n",
+ item->filename, item->startline, item->endline, item->u2.val);
+ warns++;
+ }
+ check_expr2_input(item,item->u2.val);
+ }
+ break;
+
+ case PV_LOCALVARDEC:
+ /* fields: item->u1.str == variable name
+ item->u2.val == variable value to assign
+ */
+ /* the RHS of a vardec is encapsulated in a $[] expr. Is it legal? */
+ snprintf(errmsg,sizeof(errmsg), "file %s, line %d, columns %d-%d, variable declaration expr '%s':", item->filename, item->startline, item->startcol, item->endcol, item->u2.val);
+ ast_expr_register_extra_error_info(errmsg);
+ ast_expr(item->u2.val, expr_output, sizeof(expr_output),NULL);
+ ast_expr_clear_extra_error_info();
+ if ( strpbrk(item->u2.val,"~!-+<>=*/&^") && !strstr(item->u2.val,"${") ) {
+ ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n",
+ item->filename, item->startline, item->endline, item->u2.val);
+ warns++;
+ }
+ check_expr2_input(item,item->u2.val);
+ break;
+
+ case PV_GOTO:
+ /* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user.
+ item->u1.list->u1.str == where the data on a PV_WORD will always be.
+ */
+ /* don't check goto's in abstract contexts */
+ if ( in_abstract_context )
+ break;
+
+ check_goto(item);
+ break;
+
+ case PV_LABEL:
+ /* fields: item->u1.str == label name
+ */
+ if ( strspn(item->u1.str, "0123456789") == strlen(item->u1.str) ) {
+ ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: label '%s' is numeric, this is bad practice!\n",
+ item->filename, item->startline, item->endline, item->u1.str);
+ warns++;
+ }
+
+ check_label(item);
+ break;
+
+ case PV_FOR:
+ /* fields: item->u1.for_init == a string containing the initalizer
+ item->u2.for_test == a string containing the loop test
+ item->u3.for_inc == a string containing the loop increment
+
+ item->u4.for_statements == a pval list of statements in the for ()
+ */
+ snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, for test expr '%s':", item->filename, item->startline, item->startcol, item->endcol, item->u2.for_test);
+ ast_expr_register_extra_error_info(errmsg);
+
+ strp = strchr(item->u1.for_init, '=');
+ if (strp) {
+ ast_expr(strp+1, expr_output, sizeof(expr_output),NULL);
+ }
+ ast_expr(item->u2.for_test, expr_output, sizeof(expr_output),NULL);
+ strp = strchr(item->u3.for_inc, '=');
+ if (strp) {
+ ast_expr(strp+1, expr_output, sizeof(expr_output),NULL);
+ }
+ if ( strpbrk(item->u2.for_test,"~!-+<>=*/&^") && !strstr(item->u2.for_test,"${") ) {
+ ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n",
+ item->filename, item->startline, item->endline, item->u2.for_test);
+ warns++;
+ }
+ if ( strpbrk(item->u3.for_inc,"~!-+<>=*/&^") && !strstr(item->u3.for_inc,"${") ) {
+ ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n",
+ item->filename, item->startline, item->endline, item->u3.for_inc);
+ warns++;
+ }
+ check_expr2_input(item,item->u2.for_test);
+ check_expr2_input(item,item->u3.for_inc);
+
+ ast_expr_clear_extra_error_info();
+ check_pval(item->u4.for_statements, apps,in_globals);
+ break;
+
+ case PV_WHILE:
+ /* fields: item->u1.str == the while conditional, as supplied by user
+
+ item->u2.statements == a pval list of statements in the while ()
+ */
+ snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, while expr '%s':", item->filename, item->startline, item->startcol, item->endcol, item->u1.str);
+ ast_expr_register_extra_error_info(errmsg);
+ ast_expr(item->u1.str, expr_output, sizeof(expr_output),NULL);
+ ast_expr_clear_extra_error_info();
+ if ( strpbrk(item->u1.str,"~!-+<>=*/&^") && !strstr(item->u1.str,"${") ) {
+ ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n",
+ item->filename, item->startline, item->endline, item->u1.str);
+ warns++;
+ }
+ check_expr2_input(item,item->u1.str);
+ check_pval(item->u2.statements, apps,in_globals);
+ break;
+
+ case PV_BREAK:
+ /* fields: none
+ */
+ check_break(item);
+ break;
+
+ case PV_RETURN:
+ /* fields: none
+ */
+ break;
+
+ case PV_CONTINUE:
+ /* fields: none
+ */
+ check_continue(item);
+ break;
+
+ case PV_RANDOM:
+ /* fields: item->u1.str == the random number expression, as supplied by user
+
+ item->u2.statements == a pval list of statements in the if ()
+ item->u3.else_statements == a pval list of statements in the else
+ (could be zero)
+ */
+ snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, random expr '%s':", item->filename, item->startline, item->startcol, item->endcol, item->u1.str);
+ ast_expr_register_extra_error_info(errmsg);
+ ast_expr(item->u1.str, expr_output, sizeof(expr_output),NULL);
+ ast_expr_clear_extra_error_info();
+ if ( strpbrk(item->u1.str,"~!-+<>=*/&^") && !strstr(item->u1.str,"${") ) {
+ ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: random expression '%s' has operators, but no variables. Interesting...\n",
+ item->filename, item->startline, item->endline, item->u1.str);
+ warns++;
+ }
+ check_expr2_input(item,item->u1.str);
+ check_pval(item->u2.statements, apps,in_globals);
+ if (item->u3.else_statements) {
+ check_pval(item->u3.else_statements, apps,in_globals);
+ }
+ break;
+
+ case PV_IFTIME:
+ /* fields: item->u1.list == the if time values, 4 of them, each in PV_WORD, linked list
+
+ item->u2.statements == a pval list of statements in the if ()
+ item->u3.else_statements == a pval list of statements in the else
+ (could be zero)
+ */
+ if ( item->u2.arglist ) {
+ check_timerange(item->u1.list);
+ check_dow(item->u1.list->next);
+ check_day(item->u1.list->next->next);
+ check_month(item->u1.list->next->next->next);
+ }
+
+ check_pval(item->u2.statements, apps,in_globals);
+ if (item->u3.else_statements) {
+ check_pval(item->u3.else_statements, apps,in_globals);
+ }
+ break;
+
+ case PV_IF:
+ /* fields: item->u1.str == the if conditional, as supplied by user
+
+ item->u2.statements == a pval list of statements in the if ()
+ item->u3.else_statements == a pval list of statements in the else
+ (could be zero)
+ */
+ snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, if expr '%s':", item->filename, item->startline, item->startcol, item->endcol, item->u1.str);
+ ast_expr_register_extra_error_info(errmsg);
+ ast_expr(item->u1.str, expr_output, sizeof(expr_output),NULL);
+ ast_expr_clear_extra_error_info();
+ if ( strpbrk(item->u1.str,"~!-+<>=*/&^") && !strstr(item->u1.str,"${") ) {
+ ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression '%s' has operators, but no variables. Interesting...\n",
+ item->filename, item->startline, item->endline, item->u1.str);
+ warns++;
+ }
+ check_expr2_input(item,item->u1.str);
+ check_pval(item->u2.statements, apps,in_globals);
+ if (item->u3.else_statements) {
+ check_pval(item->u3.else_statements, apps,in_globals);
+ }
+ break;
+
+ case PV_SWITCH:
+ /* fields: item->u1.str == the switch expression
+
+ item->u2.statements == a pval list of statements in the switch,
+ (will be case statements, most likely!)
+ */
+ /* we can check the switch expression, see if it matches any of the app variables...
+ if it does, then, are all the possible cases accounted for? */
+ check_switch_expr(item, apps);
+ check_pval(item->u2.statements, apps,in_globals);
+ break;
+
+ case PV_EXTENSION:
+ /* fields: item->u1.str == the extension name, label, whatever it's called
+
+ item->u2.statements == a pval list of statements in the extension
+ item->u3.hints == a char * hint argument
+ item->u4.regexten == an int boolean. non-zero says that regexten was specified
+ */
+ current_extension = item ;
+
+ check_pval(item->u2.statements, apps,in_globals);
+ break;
+
+ case PV_IGNOREPAT:
+ /* fields: item->u1.str == the ignorepat data
+ */
+ break;
+
+ case PV_GLOBALS:
+ /* fields: item->u1.statements == pval list of statements, usually vardecs
+ */
+ in_abstract_context = 0;
+ check_pval(item->u1.statements, apps, 1);
+ break;
+ default:
+ break;
+ }
+}
+
+void check_pval(pval *item, struct argapp *apps, int in_globals)
+{
+ pval *i;
+
+ /* checks to do:
+ 1. Do goto's point to actual labels?
+ 2. Do macro calls reference a macro?
+ 3. Does the number of macro args match the definition?
+ 4. Is a macro call missing its & at the front?
+ 5. Application calls-- we could check syntax for existing applications,
+ but I need some some sort of universal description bnf for a general
+ sort of method for checking arguments, in number, maybe even type, at least.
+ Don't want to hand code checks for hundreds of applications.
+ */
+
+ for (i=item; i; i=i->next) {
+ check_pval_item(i,apps,in_globals);
+ }
+}
+
+void ael2_semantic_check(pval *item, int *arg_errs, int *arg_warns, int *arg_notes)
+{
+
+#ifdef AAL_ARGCHECK
+ int argapp_errs =0;
+ char *rfilename;
+#endif
+ struct argapp *apps=0;
+
+#ifdef AAL_ARGCHECK
+ rfilename = alloca(10 + strlen(ast_config_AST_VAR_DIR));
+ sprintf(rfilename, "%s/applist", ast_config_AST_VAR_DIR);
+
+ apps = argdesc_parse(rfilename, &argapp_errs); /* giveth */
+#endif
+ current_db = item;
+ errs = warns = notes = 0;
+
+ check_context_names();
+ check_pval(item, apps, 0);
+
+#ifdef AAL_ARGCHECK
+ argdesc_destroy(apps); /* taketh away */
+#endif
+ current_db = 0;
+
+ *arg_errs = errs;
+ *arg_warns = warns;
+ *arg_notes = notes;
+}
+
+/* =============================================================================================== */
+/* "CODE" GENERATOR -- Convert the AEL representation to asterisk extension language */
+/* =============================================================================================== */
+
+static int control_statement_count = 0;
+
+struct ael_priority *new_prio(void)
+{
+ struct ael_priority *x = (struct ael_priority *)calloc(sizeof(struct ael_priority),1);
+ return x;
+}
+
+struct ael_extension *new_exten(void)
+{
+ struct ael_extension *x = (struct ael_extension *)calloc(sizeof(struct ael_extension),1);
+ return x;
+}
+
+void linkprio(struct ael_extension *exten, struct ael_priority *prio)
+{
+ if (!exten->plist) {
+ exten->plist = prio;
+ exten->plist_last = prio;
+ } else {
+ exten->plist_last->next = prio;
+ exten->plist_last = prio;
+ }
+ if( !prio->exten )
+ prio->exten = exten; /* don't override the switch value */
+}
+
+void destroy_extensions(struct ael_extension *exten)
+{
+ struct ael_extension *ne, *nen;
+ for (ne=exten; ne; ne=nen) {
+ struct ael_priority *pe, *pen;
+
+ if (ne->name)
+ free(ne->name);
+
+ /* cidmatch fields are allocated with name, and freed when
+ the name field is freed. Don't do a free for this field,
+ unless you LIKE to see a crash! */
+
+ if (ne->hints)
+ free(ne->hints);
+
+ for (pe=ne->plist; pe; pe=pen) {
+ pen = pe->next;
+ if (pe->app)
+ free(pe->app);
+ pe->app = 0;
+ if (pe->appargs)
+ free(pe->appargs);
+ pe->appargs = 0;
+ pe->origin = 0;
+ pe->goto_true = 0;
+ pe->goto_false = 0;
+ free(pe);
+ }
+ nen = ne->next_exten;
+ ne->next_exten = 0;
+ ne->plist =0;
+ ne->plist_last = 0;
+ ne->next_exten = 0;
+ ne->loop_break = 0;
+ ne->loop_continue = 0;
+ free(ne);
+ }
+}
+
+static int label_inside_case(pval *label)
+{
+ pval *p = label;
+
+ while( p && p->type != PV_MACRO && p->type != PV_CONTEXT ) /* early cutout, sort of */ {
+ if( p->type == PV_CASE || p->type == PV_DEFAULT || p->type == PV_PATTERN ) {
+ return 1;
+ }
+
+ p = p->dad;
+ }
+ return 0;
+}
+
+static void linkexten(struct ael_extension *exten, struct ael_extension *add)
+{
+ add->next_exten = exten->next_exten; /* this will reverse the order. Big deal. */
+ exten->next_exten = add;
+}
+
+static void remove_spaces_before_equals(char *str)
+{
+ char *p;
+ while( str && *str && *str != '=' )
+ {
+ if( *str == ' ' || *str == '\n' || *str == '\r' || *str == '\t' )
+ {
+ p = str;
+ while( *p )
+ {
+ *p = *(p+1);
+ p++;
+ }
+ }
+ else
+ str++;
+ }
+}
+
+/* =============================================================================================== */
+/* "CODE" GENERATOR -- Convert the AEL representation to asterisk extension language */
+/* =============================================================================================== */
+
+static void gen_match_to_pattern(char *pattern, char *result)
+{
+ /* the result will be a string that will be matched by pattern */
+ char *p=pattern, *t=result;
+ while (*p) {
+ if (*p == 'x' || *p == 'n' || *p == 'z' || *p == 'X' || *p == 'N' || *p == 'Z')
+ *t++ = '9';
+ else if (*p == '[') {
+ char *z = p+1;
+ while (*z != ']')
+ z++;
+ if (*(z+1)== ']')
+ z++;
+ *t++=*(p+1); /* use the first char in the set */
+ p = z;
+ } else {
+ *t++ = *p;
+ }
+ p++;
+ }
+ *t++ = 0; /* cap it off */
+}
+
+static void gen_prios(struct ael_extension *exten, char *label, pval *statement, struct ael_extension *mother_exten, struct ast_context *this_context )
+{
+ pval *p,*p2,*p3;
+ struct ael_priority *pr;
+ struct ael_priority *for_init, *for_test, *for_inc, *for_loop, *for_end;
+ struct ael_priority *while_test, *while_loop, *while_end;
+ struct ael_priority *switch_test, *switch_end, *fall_thru;
+ struct ael_priority *if_test, *if_end, *if_skip, *if_false;
+#ifdef OLD_RAND_ACTION
+ struct ael_priority *rand_test, *rand_end, *rand_skip;
+#endif
+ char buf1[2000];
+ char buf2[2000];
+ char *strp, *strp2;
+ char new_label[2000];
+ int default_exists;
+ int local_control_statement_count;
+ int first;
+ struct ael_priority *loop_break_save;
+ struct ael_priority *loop_continue_save;
+ struct ael_extension *switch_case;
+
+ for (p=statement; p; p=p->next) {
+ switch (p->type) {
+ case PV_VARDEC:
+ pr = new_prio();
+ pr->type = AEL_APPCALL;
+ snprintf(buf1,sizeof(buf1),"%s=$[%s]", p->u1.str, p->u2.val);
+ pr->app = strdup("Set");
+ remove_spaces_before_equals(buf1);
+ pr->appargs = strdup(buf1);
+ pr->origin = p;
+ linkprio(exten, pr);
+ break;
+
+ case PV_LOCALVARDEC:
+ pr = new_prio();
+ pr->type = AEL_APPCALL;
+ snprintf(buf1,sizeof(buf1),"LOCAL(%s)=$[%s]", p->u1.str, p->u2.val);
+ pr->app = strdup("Set");
+ remove_spaces_before_equals(buf1);
+ pr->appargs = strdup(buf1);
+ pr->origin = p;
+ linkprio(exten, pr);
+ break;
+
+ case PV_GOTO:
+ pr = new_prio();
+ pr->type = AEL_APPCALL;
+ p->u2.goto_target = get_goto_target(p);
+ if( p->u2.goto_target ) {
+ p->u3.goto_target_in_case = p->u2.goto_target->u2.label_in_case = label_inside_case(p->u2.goto_target);
+ }
+
+ if (!p->u1.list->next) /* just one */ {
+ pr->app = strdup("Goto");
+ if (!mother_exten)
+ pr->appargs = strdup(p->u1.list->u1.str);
+ else { /* for the case of simple within-extension gotos in case/pattern/default statement blocks: */
+ snprintf(buf1,sizeof(buf1),"%s,%s", mother_exten->name, p->u1.list->u1.str);
+ pr->appargs = strdup(buf1);
+ }
+
+ } else if (p->u1.list->next && !p->u1.list->next->next) /* two */ {
+ snprintf(buf1,sizeof(buf1),"%s,%s", p->u1.list->u1.str, p->u1.list->next->u1.str);
+ pr->app = strdup("Goto");
+ pr->appargs = strdup(buf1);
+ } else if (p->u1.list->next && p->u1.list->next->next) {
+ snprintf(buf1,sizeof(buf1),"%s,%s,%s", p->u1.list->u1.str,
+ p->u1.list->next->u1.str,
+ p->u1.list->next->next->u1.str);
+ pr->app = strdup("Goto");
+ pr->appargs = strdup(buf1);
+ }
+ pr->origin = p;
+ linkprio(exten, pr);
+ break;
+
+ case PV_LABEL:
+ pr = new_prio();
+ pr->type = AEL_LABEL;
+ pr->origin = p;
+ p->u3.compiled_label = exten;
+ linkprio(exten, pr);
+ break;
+
+ case PV_FOR:
+ control_statement_count++;
+ loop_break_save = exten->loop_break; /* save them, then restore before leaving */
+ loop_continue_save = exten->loop_continue;
+ snprintf(new_label,sizeof(new_label),"for-%s-%d", label, control_statement_count);
+ for_init = new_prio();
+ for_inc = new_prio();
+ for_test = new_prio();
+ for_loop = new_prio();
+ for_end = new_prio();
+ for_init->type = AEL_APPCALL;
+ for_inc->type = AEL_APPCALL;
+ for_test->type = AEL_FOR_CONTROL;
+ for_test->goto_false = for_end;
+ for_loop->type = AEL_CONTROL1; /* simple goto */
+ for_end->type = AEL_APPCALL;
+ for_init->app = strdup("Set");
+
+ strcpy(buf2,p->u1.for_init);
+ remove_spaces_before_equals(buf2);
+ strp = strchr(buf2, '=');
+ strp2 = strchr(p->u1.for_init, '=');
+ if (strp) {
+ *(strp+1) = 0;
+ strcat(buf2,"$[");
+ strncat(buf2,strp2+1, sizeof(buf2)-strlen(strp2+1)-2);
+ strcat(buf2,"]");
+ for_init->appargs = strdup(buf2);
+ } else
+ for_init->appargs = strdup(p->u1.for_init);
+
+ for_inc->app = strdup("Set");
+
+ strcpy(buf2,p->u3.for_inc);
+ remove_spaces_before_equals(buf2);
+ strp = strchr(buf2, '=');
+ strp2 = strchr(p->u3.for_inc, '=');
+ if (strp) {
+ *(strp+1) = 0;
+ strcat(buf2,"$[");
+ strncat(buf2,strp2+1, sizeof(buf2)-strlen(strp2+1)-2);
+ strcat(buf2,"]");
+ for_inc->appargs = strdup(buf2);
+ } else
+ for_inc->appargs = strdup(p->u3.for_inc);
+ snprintf(buf1,sizeof(buf1),"$[%s]",p->u2.for_test);
+ for_test->app = 0;
+ for_test->appargs = strdup(buf1);
+ for_loop->goto_true = for_test;
+ snprintf(buf1,sizeof(buf1),"Finish for-%s-%d", label, control_statement_count);
+ for_end->app = strdup("NoOp");
+ for_end->appargs = strdup(buf1);
+ /* link & load! */
+ linkprio(exten, for_init);
+ linkprio(exten, for_test);
+
+ /* now, put the body of the for loop here */
+ exten->loop_break = for_end;
+ exten->loop_continue = for_inc;
+
+ gen_prios(exten, new_label, p->u4.for_statements, mother_exten, this_context); /* this will link in all the statements here */
+
+ linkprio(exten, for_inc);
+ linkprio(exten, for_loop);
+ linkprio(exten, for_end);
+
+
+ exten->loop_break = loop_break_save;
+ exten->loop_continue = loop_continue_save;
+ for_loop->origin = p;
+ break;
+
+ case PV_WHILE:
+ control_statement_count++;
+ loop_break_save = exten->loop_break; /* save them, then restore before leaving */
+ loop_continue_save = exten->loop_continue;
+ snprintf(new_label,sizeof(new_label),"while-%s-%d", label, control_statement_count);
+ while_test = new_prio();
+ while_loop = new_prio();
+ while_end = new_prio();
+ while_test->type = AEL_FOR_CONTROL;
+ while_test->goto_false = while_end;
+ while_loop->type = AEL_CONTROL1; /* simple goto */
+ while_end->type = AEL_APPCALL;
+ snprintf(buf1,sizeof(buf1),"$[%s]",p->u1.str);
+ while_test->app = 0;
+ while_test->appargs = strdup(buf1);
+ while_loop->goto_true = while_test;
+ snprintf(buf1,sizeof(buf1),"Finish while-%s-%d", label, control_statement_count);
+ while_end->app = strdup("NoOp");
+ while_end->appargs = strdup(buf1);
+
+ linkprio(exten, while_test);
+
+ /* now, put the body of the for loop here */
+ exten->loop_break = while_end;
+ exten->loop_continue = while_test;
+
+ gen_prios(exten, new_label, p->u2.statements, mother_exten, this_context); /* this will link in all the while body statements here */
+
+ linkprio(exten, while_loop);
+ linkprio(exten, while_end);
+
+
+ exten->loop_break = loop_break_save;
+ exten->loop_continue = loop_continue_save;
+ while_loop->origin = p;
+ break;
+
+ case PV_SWITCH:
+ control_statement_count++;
+ local_control_statement_count = control_statement_count;
+ loop_break_save = exten->loop_break; /* save them, then restore before leaving */
+ loop_continue_save = exten->loop_continue;
+ snprintf(new_label,sizeof(new_label),"sw-%s-%d", label, control_statement_count);
+
+ switch_test = new_prio();
+ switch_end = new_prio();
+ switch_test->type = AEL_APPCALL;
+ switch_end->type = AEL_APPCALL;
+ snprintf(buf1,sizeof(buf1),"sw-%d-%s,10",control_statement_count, p->u1.str);
+ switch_test->app = strdup("Goto");
+ switch_test->appargs = strdup(buf1);
+ snprintf(buf1,sizeof(buf1),"Finish switch-%s-%d", label, control_statement_count);
+ switch_end->app = strdup("NoOp");
+ switch_end->appargs = strdup(buf1);
+ switch_end->origin = p;
+ switch_end->exten = exten;
+
+ linkprio(exten, switch_test);
+ linkprio(exten, switch_end);
+
+ exten->loop_break = switch_end;
+ exten->loop_continue = 0;
+ default_exists = 0;
+
+ for (p2=p->u2.statements; p2; p2=p2->next) {
+ /* now, for each case/default put the body of the for loop here */
+ if (p2->type == PV_CASE) {
+ /* ok, generate a extension and link it in */
+ switch_case = new_exten();
+ switch_case->context = this_context;
+ switch_case->is_switch = 1;
+ /* the break/continue locations are inherited from parent */
+ switch_case->loop_break = exten->loop_break;
+ switch_case->loop_continue = exten->loop_continue;
+
+ linkexten(exten,switch_case);
+ snprintf(buf1,sizeof(buf1),"sw-%d-%s", local_control_statement_count, p2->u1.str);
+ switch_case->name = strdup(buf1);
+ snprintf(new_label,sizeof(new_label),"sw-%s-%s-%d", label, p2->u1.str, local_control_statement_count);
+
+ gen_prios(switch_case, new_label, p2->u2.statements, exten, this_context); /* this will link in all the case body statements here */
+
+ /* here is where we write code to "fall thru" to the next case... if there is one... */
+ for (p3=p2->u2.statements; p3; p3=p3->next) {
+ if (!p3->next)
+ break;
+ }
+ /* p3 now points the last statement... */
+ if (!p3 || ( p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN) ) {
+ /* is there a following CASE/PATTERN/DEFAULT? */
+ if (p2->next && p2->next->type == PV_CASE) {
+ fall_thru = new_prio();
+ fall_thru->type = AEL_APPCALL;
+ fall_thru->app = strdup("Goto");
+ snprintf(buf1,sizeof(buf1),"sw-%d-%s,10",local_control_statement_count, p2->next->u1.str);
+ fall_thru->appargs = strdup(buf1);
+ linkprio(switch_case, fall_thru);
+ } else if (p2->next && p2->next->type == PV_PATTERN) {
+ fall_thru = new_prio();
+ fall_thru->type = AEL_APPCALL;
+ fall_thru->app = strdup("Goto");
+ gen_match_to_pattern(p2->next->u1.str, buf2);
+ snprintf(buf1,sizeof(buf1),"sw-%d-%s,10", local_control_statement_count, buf2);
+ fall_thru->appargs = strdup(buf1);
+ linkprio(switch_case, fall_thru);
+ } else if (p2->next && p2->next->type == PV_DEFAULT) {
+ fall_thru = new_prio();
+ fall_thru->type = AEL_APPCALL;
+ fall_thru->app = strdup("Goto");
+ snprintf(buf1,sizeof(buf1),"sw-%d-.,10",local_control_statement_count);
+ fall_thru->appargs = strdup(buf1);
+ linkprio(switch_case, fall_thru);
+ } else if (!p2->next) {
+ fall_thru = new_prio();
+ fall_thru->type = AEL_CONTROL1;
+ fall_thru->goto_true = switch_end;
+ fall_thru->app = strdup("Goto");
+ linkprio(switch_case, fall_thru);
+ }
+ }
+ if (switch_case->return_needed) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */
+ char buf[2000];
+ struct ael_priority *np2 = new_prio();
+ np2->type = AEL_APPCALL;
+ np2->app = strdup("NoOp");
+ snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name);
+ np2->appargs = strdup(buf);
+ linkprio(switch_case, np2);
+ switch_case-> return_target = np2;
+ }
+ } else if (p2->type == PV_PATTERN) {
+ /* ok, generate a extension and link it in */
+ switch_case = new_exten();
+ switch_case->context = this_context;
+ switch_case->is_switch = 1;
+ /* the break/continue locations are inherited from parent */
+ switch_case->loop_break = exten->loop_break;
+ switch_case->loop_continue = exten->loop_continue;
+
+ linkexten(exten,switch_case);
+ snprintf(buf1,sizeof(buf1),"_sw-%d-%s", local_control_statement_count, p2->u1.str);
+ switch_case->name = strdup(buf1);
+ snprintf(new_label,sizeof(new_label),"sw-%s-%s-%d", label, p2->u1.str, local_control_statement_count);
+
+ gen_prios(switch_case, new_label, p2->u2.statements, exten, this_context); /* this will link in all the while body statements here */
+ /* here is where we write code to "fall thru" to the next case... if there is one... */
+ for (p3=p2->u2.statements; p3; p3=p3->next) {
+ if (!p3->next)
+ break;
+ }
+ /* p3 now points the last statement... */
+ if (!p3 || ( p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN)) {
+ /* is there a following CASE/PATTERN/DEFAULT? */
+ if (p2->next && p2->next->type == PV_CASE) {
+ fall_thru = new_prio();
+ fall_thru->type = AEL_APPCALL;
+ fall_thru->app = strdup("Goto");
+ snprintf(buf1,sizeof(buf1),"sw-%d-%s,10",local_control_statement_count, p2->next->u1.str);
+ fall_thru->appargs = strdup(buf1);
+ linkprio(switch_case, fall_thru);
+ } else if (p2->next && p2->next->type == PV_PATTERN) {
+ fall_thru = new_prio();
+ fall_thru->type = AEL_APPCALL;
+ fall_thru->app = strdup("Goto");
+ gen_match_to_pattern(p2->next->u1.str, buf2);
+ snprintf(buf1,sizeof(buf1),"sw-%d-%s,10",local_control_statement_count, buf2);
+ fall_thru->appargs = strdup(buf1);
+ linkprio(switch_case, fall_thru);
+ } else if (p2->next && p2->next->type == PV_DEFAULT) {
+ fall_thru = new_prio();
+ fall_thru->type = AEL_APPCALL;
+ fall_thru->app = strdup("Goto");
+ snprintf(buf1,sizeof(buf1),"sw-%d-.,10",local_control_statement_count);
+ fall_thru->appargs = strdup(buf1);
+ linkprio(switch_case, fall_thru);
+ } else if (!p2->next) {
+ fall_thru = new_prio();
+ fall_thru->type = AEL_CONTROL1;
+ fall_thru->goto_true = switch_end;
+ fall_thru->app = strdup("Goto");
+ linkprio(switch_case, fall_thru);
+ }
+ }
+ if (switch_case->return_needed) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */
+ char buf[2000];
+ struct ael_priority *np2 = new_prio();
+ np2->type = AEL_APPCALL;
+ np2->app = strdup("NoOp");
+ snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name);
+ np2->appargs = strdup(buf);
+ linkprio(switch_case, np2);
+ switch_case-> return_target = np2;
+ }
+ } else if (p2->type == PV_DEFAULT) {
+ default_exists++;
+ /* ok, generate a extension and link it in */
+ switch_case = new_exten();
+ switch_case->context = this_context;
+ switch_case->is_switch = 1;
+ /* the break/continue locations are inherited from parent */
+ switch_case->loop_break = exten->loop_break;
+ switch_case->loop_continue = exten->loop_continue;
+ linkexten(exten,switch_case);
+ snprintf(buf1,sizeof(buf1),"_sw-%d-.", local_control_statement_count);
+ switch_case->name = strdup(buf1);
+
+ snprintf(new_label,sizeof(new_label),"sw-%s-default-%d", label, local_control_statement_count);
+
+ gen_prios(switch_case, new_label, p2->u2.statements, exten, this_context); /* this will link in all the while body statements here */
+
+ /* here is where we write code to "fall thru" to the next case... if there is one... */
+ for (p3=p2->u2.statements; p3; p3=p3->next) {
+ if (!p3->next)
+ break;
+ }
+ /* p3 now points the last statement... */
+ if (!p3 || (p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN)) {
+ /* is there a following CASE/PATTERN/DEFAULT? */
+ if (p2->next && p2->next->type == PV_CASE) {
+ fall_thru = new_prio();
+ fall_thru->type = AEL_APPCALL;
+ fall_thru->app = strdup("Goto");
+ snprintf(buf1,sizeof(buf1),"sw-%d-%s,10",local_control_statement_count, p2->next->u1.str);
+ fall_thru->appargs = strdup(buf1);
+ linkprio(switch_case, fall_thru);
+ } else if (p2->next && p2->next->type == PV_PATTERN) {
+ fall_thru = new_prio();
+ fall_thru->type = AEL_APPCALL;
+ fall_thru->app = strdup("Goto");
+ gen_match_to_pattern(p2->next->u1.str, buf2);
+ snprintf(buf1,sizeof(buf1),"sw-%d-%s,10",local_control_statement_count, buf2);
+ fall_thru->appargs = strdup(buf1);
+ linkprio(switch_case, fall_thru);
+ } else if (p2->next && p2->next->type == PV_DEFAULT) {
+ fall_thru = new_prio();
+ fall_thru->type = AEL_APPCALL;
+ fall_thru->app = strdup("Goto");
+ snprintf(buf1,sizeof(buf1),"sw-%d-.,10",local_control_statement_count);
+ fall_thru->appargs = strdup(buf1);
+ linkprio(switch_case, fall_thru);
+ } else if (!p2->next) {
+ fall_thru = new_prio();
+ fall_thru->type = AEL_CONTROL1;
+ fall_thru->goto_true = switch_end;
+ fall_thru->app = strdup("Goto");
+ linkprio(switch_case, fall_thru);
+ }
+ }
+ if (switch_case->return_needed) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */
+ char buf[2000];
+ struct ael_priority *np2 = new_prio();
+ np2->type = AEL_APPCALL;
+ np2->app = strdup("NoOp");
+ snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name);
+ np2->appargs = strdup(buf);
+ linkprio(switch_case, np2);
+ switch_case-> return_target = np2;
+ }
+ } else {
+ /* what could it be??? */
+ }
+ }
+
+ exten->loop_break = loop_break_save;
+ exten->loop_continue = loop_continue_save;
+ switch_test->origin = p;
+ switch_end->origin = p;
+ break;
+
+ case PV_MACRO_CALL:
+ pr = new_prio();
+ pr->type = AEL_APPCALL;
+ snprintf(buf1,sizeof(buf1),"%s,s,1", p->u1.str);
+ first = 1;
+ for (p2 = p->u2.arglist; p2; p2 = p2->next) {
+ if (first)
+ {
+ strcat(buf1,"(");
+ first = 0;
+ }
+ else
+ strcat(buf1,",");
+ strcat(buf1,p2->u1.str);
+ }
+ if (!first)
+ strcat(buf1,")");
+
+ pr->app = strdup("Gosub");
+ pr->appargs = strdup(buf1);
+ pr->origin = p;
+ linkprio(exten, pr);
+ break;
+
+ case PV_APPLICATION_CALL:
+ pr = new_prio();
+ pr->type = AEL_APPCALL;
+ buf1[0] = 0;
+ for (p2 = p->u2.arglist; p2; p2 = p2->next) {
+ if (p2 != p->u2.arglist )
+ strcat(buf1,",");
+ strcat(buf1,p2->u1.str);
+ }
+ pr->app = strdup(p->u1.str);
+ pr->appargs = strdup(buf1);
+ pr->origin = p;
+ linkprio(exten, pr);
+ break;
+
+ case PV_BREAK:
+ pr = new_prio();
+ pr->type = AEL_CONTROL1; /* simple goto */
+ pr->goto_true = exten->loop_break;
+ pr->origin = p;
+ linkprio(exten, pr);
+ break;
+
+ case PV_RETURN: /* hmmmm */
+ pr = new_prio();
+ pr->type = AEL_RETURN; /* simple Return */
+ /* exten->return_needed++; */
+ pr->app = strdup("Return");
+ pr->appargs = strdup("");
+ pr->origin = p;
+ linkprio(exten, pr);
+ break;
+
+ case PV_CONTINUE:
+ pr = new_prio();
+ pr->type = AEL_CONTROL1; /* simple goto */
+ pr->goto_true = exten->loop_continue;
+ pr->origin = p;
+ linkprio(exten, pr);
+ break;
+
+ case PV_IFTIME:
+ control_statement_count++;
+ snprintf(new_label,sizeof(new_label),"iftime-%s-%d", label, control_statement_count);
+
+ if_test = new_prio();
+ if_test->type = AEL_IFTIME_CONTROL;
+ snprintf(buf1,sizeof(buf1),"%s,%s,%s,%s",
+ p->u1.list->u1.str,
+ p->u1.list->next->u1.str,
+ p->u1.list->next->next->u1.str,
+ p->u1.list->next->next->next->u1.str);
+ if_test->app = 0;
+ if_test->appargs = strdup(buf1);
+ if_test->origin = p;
+
+ if_end = new_prio();
+ if_end->type = AEL_APPCALL;
+ snprintf(buf1,sizeof(buf1),"Finish iftime-%s-%d", label, control_statement_count);
+ if_end->app = strdup("NoOp");
+ if_end->appargs = strdup(buf1);
+
+ if (p->u3.else_statements) {
+ if_skip = new_prio();
+ if_skip->type = AEL_CONTROL1; /* simple goto */
+ if_skip->goto_true = if_end;
+ if_skip->origin = p;
+
+ } else {
+ if_skip = 0;
+
+ if_test->goto_false = if_end;
+ }
+
+ if_false = new_prio();
+ if_false->type = AEL_CONTROL1;
+ if (p->u3.else_statements) {
+ if_false->goto_true = if_skip; /* +1 */
+ } else {
+ if_false->goto_true = if_end;
+ }
+
+ /* link & load! */
+ linkprio(exten, if_test);
+ linkprio(exten, if_false);
+
+ /* now, put the body of the if here */
+
+ gen_prios(exten, new_label, p->u2.statements, mother_exten, this_context); /* this will link in all the statements here */
+
+ if (p->u3.else_statements) {
+ linkprio(exten, if_skip);
+ gen_prios(exten, new_label, p->u3.else_statements, mother_exten, this_context); /* this will link in all the statements here */
+
+ }
+
+ linkprio(exten, if_end);
+
+ break;
+
+ case PV_RANDOM:
+ case PV_IF:
+ control_statement_count++;
+ snprintf(new_label,sizeof(new_label),"if-%s-%d", label, control_statement_count);
+
+ if_test = new_prio();
+ if_end = new_prio();
+ if_test->type = AEL_IF_CONTROL;
+ if_end->type = AEL_APPCALL;
+ if ( p->type == PV_RANDOM )
+ snprintf(buf1,sizeof(buf1),"$[${RAND(0,99)} < (%s)]",p->u1.str);
+ else
+ snprintf(buf1,sizeof(buf1),"$[%s]",p->u1.str);
+ if_test->app = 0;
+ if_test->appargs = strdup(buf1);
+ snprintf(buf1,sizeof(buf1),"Finish if-%s-%d", label, control_statement_count);
+ if_end->app = strdup("NoOp");
+ if_end->appargs = strdup(buf1);
+ if_test->origin = p;
+
+ if (p->u3.else_statements) {
+ if_skip = new_prio();
+ if_skip->type = AEL_CONTROL1; /* simple goto */
+ if_skip->goto_true = if_end;
+ if_test->goto_false = if_skip;;
+ } else {
+ if_skip = 0;
+ if_test->goto_false = if_end;;
+ }
+
+ /* link & load! */
+ linkprio(exten, if_test);
+
+ /* now, put the body of the if here */
+
+ gen_prios(exten, new_label, p->u2.statements, mother_exten, this_context); /* this will link in all the statements here */
+
+ if (p->u3.else_statements) {
+ linkprio(exten, if_skip);
+ gen_prios(exten, new_label, p->u3.else_statements, mother_exten, this_context); /* this will link in all the statements here */
+
+ }
+
+ linkprio(exten, if_end);
+
+ break;
+
+ case PV_STATEMENTBLOCK:
+ gen_prios(exten, label, p->u1.list, mother_exten, this_context ); /* recurse into the block */
+ break;
+
+ case PV_CATCH:
+ control_statement_count++;
+ /* generate an extension with name of catch, put all catch stats
+ into this exten! */
+ switch_case = new_exten();
+ switch_case->context = this_context;
+ linkexten(exten,switch_case);
+ switch_case->name = strdup(p->u1.str);
+ snprintf(new_label,sizeof(new_label),"catch-%s-%d",p->u1.str, control_statement_count);
+
+ gen_prios(switch_case, new_label, p->u2.statements,mother_exten,this_context); /* this will link in all the catch body statements here */
+ if (switch_case->return_needed) { /* returns now generate a Return() app call, no longer a goto to the end of the exten */
+ char buf[2000];
+ struct ael_priority *np2 = new_prio();
+ np2->type = AEL_APPCALL;
+ np2->app = strdup("NoOp");
+ snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name);
+ np2->appargs = strdup(buf);
+ linkprio(switch_case, np2);
+ switch_case-> return_target = np2;
+ }
+
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void set_priorities(struct ael_extension *exten)
+{
+ int i;
+ struct ael_priority *pr;
+ do {
+ if (exten->is_switch)
+ i = 10;
+ else if (exten->regexten)
+ i=2;
+ else
+ i=1;
+
+ for (pr=exten->plist; pr; pr=pr->next) {
+ pr->priority_num = i;
+
+ if (!pr->origin || (pr->origin && pr->origin->type != PV_LABEL) ) /* Labels don't show up in the dialplan,
+ but we want them to point to the right
+ priority, which would be the next line
+ after the label; */
+ i++;
+ }
+
+ exten = exten->next_exten;
+ } while ( exten );
+}
+
+void add_extensions(struct ael_extension *exten)
+{
+ struct ael_priority *pr;
+ char *label=0;
+ char realext[AST_MAX_EXTENSION];
+ if (!exten) {
+ ast_log(LOG_WARNING, "This file is Empty!\n" );
+ return;
+ }
+ do {
+ struct ael_priority *last = 0;
+
+ memset(realext, '\0', sizeof(realext)); /* make sure this is properly initialized */
+ pbx_substitute_variables_helper(NULL, exten->name, realext, sizeof(realext) - 1);
+ if (exten->hints) {
+ if (ast_add_extension2(exten->context, 0 /*no replace*/, realext, PRIORITY_HINT, NULL, exten->cidmatch,
+ exten->hints, NULL, ast_free, registrar)) {
+ ast_log(LOG_WARNING, "Unable to add step at priority 'hint' of extension '%s'\n",
+ exten->name);
+ }
+ }
+
+ for (pr=exten->plist; pr; pr=pr->next) {
+ char app[2000];
+ char appargs[2000];
+
+ /* before we can add the extension, we need to prep the app/appargs;
+ the CONTROL types need to be done after the priority numbers are calculated.
+ */
+ if (pr->type == AEL_LABEL) /* don't try to put labels in the dialplan! */ {
+ last = pr;
+ continue;
+ }
+
+ if (pr->app)
+ strcpy(app, pr->app);
+ else
+ app[0] = 0;
+ if (pr->appargs )
+ strcpy(appargs, pr->appargs);
+ else
+ appargs[0] = 0;
+ switch( pr->type ) {
+ case AEL_APPCALL:
+ /* easy case. Everything is all set up */
+ break;
+
+ case AEL_CONTROL1: /* FOR loop, WHILE loop, BREAK, CONTINUE, IF, IFTIME */
+ /* simple, unconditional goto. */
+ strcpy(app,"Goto");
+ if (pr->goto_true->origin && pr->goto_true->origin->type == PV_SWITCH ) {
+ snprintf(appargs,sizeof(appargs),"%s,%d", pr->goto_true->exten->name, pr->goto_true->priority_num);
+ } else if (pr->goto_true->origin && pr->goto_true->origin->type == PV_IFTIME && pr->goto_true->origin->u3.else_statements ) {
+ snprintf(appargs,sizeof(appargs),"%d", pr->goto_true->priority_num+1);
+ } else
+ snprintf(appargs,sizeof(appargs),"%d", pr->goto_true->priority_num);
+ break;
+
+ case AEL_FOR_CONTROL: /* WHILE loop test, FOR loop test */
+ strcpy(app,"GotoIf");
+ snprintf(appargs,sizeof(appargs),"%s?%d:%d", pr->appargs, pr->priority_num+1, pr->goto_false->priority_num);
+ break;
+
+ case AEL_IF_CONTROL:
+ strcpy(app,"GotoIf");
+ if (pr->origin->u3.else_statements )
+ snprintf(appargs,sizeof(appargs),"%s?%d:%d", pr->appargs, pr->priority_num+1, pr->goto_false->priority_num+1);
+ else
+ snprintf(appargs,sizeof(appargs),"%s?%d:%d", pr->appargs, pr->priority_num+1, pr->goto_false->priority_num);
+ break;
+
+ case AEL_RAND_CONTROL:
+ strcpy(app,"Random");
+ snprintf(appargs,sizeof(appargs),"%s:%d", pr->appargs, pr->goto_true->priority_num+1);
+ break;
+
+ case AEL_IFTIME_CONTROL:
+ strcpy(app,"GotoIfTime");
+ snprintf(appargs,sizeof(appargs),"%s?%d", pr->appargs, pr->priority_num+2);
+ break;
+
+ case AEL_RETURN:
+ strcpy(app,"Return");
+ appargs[0] = 0;
+ break;
+
+ default:
+ break;
+ }
+ if (last && last->type == AEL_LABEL ) {
+ label = last->origin->u1.str;
+ }
+ else
+ label = 0;
+
+ if (ast_add_extension2(exten->context, 0 /*no replace*/, realext, pr->priority_num, (label?label:NULL), exten->cidmatch,
+ app, strdup(appargs), ast_free, registrar)) {
+ ast_log(LOG_WARNING, "Unable to add step at priority '%d' of extension '%s'\n", pr->priority_num,
+ exten->name);
+ }
+ last = pr;
+ }
+ exten = exten->next_exten;
+ } while ( exten );
+}
+
+static void attach_exten(struct ael_extension **list, struct ael_extension *newmem)
+{
+ /* travel to the end of the list... */
+ struct ael_extension *lptr;
+ if( !*list ) {
+ *list = newmem;
+ return;
+ }
+ lptr = *list;
+
+ while( lptr->next_exten ) {
+ lptr = lptr->next_exten;
+ }
+ /* lptr should now pointing to the last element in the list; it has a null next_exten pointer */
+ lptr->next_exten = newmem;
+}
+
+static pval *get_extension_or_contxt(pval *p)
+{
+ while( p && p->type != PV_EXTENSION && p->type != PV_CONTEXT && p->type != PV_MACRO ) {
+
+ p = p->dad;
+ }
+
+ return p;
+}
+
+static pval *get_contxt(pval *p)
+{
+ while( p && p->type != PV_CONTEXT && p->type != PV_MACRO ) {
+
+ p = p->dad;
+ }
+
+ return p;
+}
+
+static void fix_gotos_in_extensions(struct ael_extension *exten)
+{
+ struct ael_extension *e;
+ for(e=exten;e;e=e->next_exten) {
+
+ struct ael_priority *p;
+ for(p=e->plist;p;p=p->next) {
+
+ if( p->origin && p->origin->type == PV_GOTO && p->origin->u3.goto_target_in_case ) {
+
+ /* fix the extension of the goto target to the actual extension in the post-compiled dialplan */
+
+ pval *target = p->origin->u2.goto_target;
+ struct ael_extension *z = target->u3.compiled_label;
+ pval *pv2 = p->origin;
+ char buf1[500];
+ char *apparg_save = p->appargs;
+
+ p->appargs = 0;
+ if (!pv2->u1.list->next) /* just one -- it won't hurt to repeat the extension */ {
+ snprintf(buf1,sizeof(buf1),"%s,%s", z->name, pv2->u1.list->u1.str);
+ p->appargs = strdup(buf1);
+
+ } else if (pv2->u1.list->next && !pv2->u1.list->next->next) /* two */ {
+ snprintf(buf1,sizeof(buf1),"%s,%s", z->name, pv2->u1.list->next->u1.str);
+ p->appargs = strdup(buf1);
+ } else if (pv2->u1.list->next && pv2->u1.list->next->next) {
+ snprintf(buf1,sizeof(buf1),"%s,%s,%s", pv2->u1.list->u1.str,
+ z->name,
+ pv2->u1.list->next->next->u1.str);
+ p->appargs = strdup(buf1);
+ }
+ else
+ printf("WHAT? The goto doesn't fall into one of three cases for GOTO????\n");
+
+ if( apparg_save ) {
+ free(apparg_save);
+ }
+ }
+ }
+ }
+}
+
+
+void ast_compile_ael2(struct ast_context **local_contexts, struct pval *root)
+{
+ pval *p,*p2;
+ struct ast_context *context;
+ char buf[2000];
+ struct ael_extension *exten;
+ struct ael_extension *exten_list = 0;
+
+ for (p=root; p; p=p->next ) { /* do the globals first, so they'll be there
+ when we try to eval them */
+ switch (p->type) {
+ case PV_GLOBALS:
+ /* just VARDEC elements */
+ for (p2=p->u1.list; p2; p2=p2->next) {
+ char buf2[2000];
+ snprintf(buf2,sizeof(buf2),"%s=%s", p2->u1.str, p2->u2.val);
+ pbx_builtin_setvar(NULL, buf2);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ for (p=root; p; p=p->next ) {
+ pval *lp;
+ int argc;
+
+ switch (p->type) {
+ case PV_MACRO:
+
+ context = ast_context_create(local_contexts, p->u1.str, registrar);
+
+ exten = new_exten();
+ exten->context = context;
+ exten->name = strdup("s");
+ argc = 1;
+ for (lp=p->u2.arglist; lp; lp=lp->next) {
+ /* for each arg, set up a "Set" command */
+ struct ael_priority *np2 = new_prio();
+ np2->type = AEL_APPCALL;
+ np2->app = strdup("Set");
+ snprintf(buf,sizeof(buf),"LOCAL(%s)=${ARG%d}", lp->u1.str, argc++);
+ remove_spaces_before_equals(buf);
+ np2->appargs = strdup(buf);
+ linkprio(exten, np2);
+ }
+
+ /* CONTAINS APPCALLS, CATCH, just like extensions... */
+ gen_prios(exten, p->u1.str, p->u3.macro_statements, 0, context );
+ if (exten->return_needed) { /* most likely, this will go away */
+ struct ael_priority *np2 = new_prio();
+ np2->type = AEL_APPCALL;
+ np2->app = strdup("NoOp");
+ snprintf(buf,sizeof(buf),"End of Macro %s-%s",p->u1.str, exten->name);
+ np2->appargs = strdup(buf);
+ linkprio(exten, np2);
+ exten-> return_target = np2;
+ }
+
+ set_priorities(exten);
+ attach_exten(&exten_list, exten);
+ break;
+
+ case PV_GLOBALS:
+ /* already done */
+ break;
+
+ case PV_CONTEXT:
+ context = ast_context_create(local_contexts, p->u1.str, registrar);
+
+ /* contexts contain: ignorepat, includes, switches, eswitches, extensions, */
+ for (p2=p->u2.statements; p2; p2=p2->next) {
+ pval *p3;
+ char *s3;
+
+ switch (p2->type) {
+ case PV_EXTENSION:
+ exten = new_exten();
+ exten->name = strdup(p2->u1.str);
+ exten->context = context;
+
+ if( (s3=strchr(exten->name, '/') ) != 0 )
+ {
+ *s3 = 0;
+ exten->cidmatch = s3+1;
+ }
+
+ if ( p2->u3.hints )
+ exten->hints = strdup(p2->u3.hints);
+ exten->regexten = p2->u4.regexten;
+ gen_prios(exten, p->u1.str, p2->u2.statements, 0, context );
+ if (exten->return_needed) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */
+ struct ael_priority *np2 = new_prio();
+ np2->type = AEL_APPCALL;
+ np2->app = strdup("NoOp");
+ snprintf(buf,sizeof(buf),"End of Extension %s", exten->name);
+ np2->appargs = strdup(buf);
+ linkprio(exten, np2);
+ exten-> return_target = np2;
+ }
+ /* is the last priority in the extension a label? Then add a trailing no-op */
+ if( !exten->plist_last )
+ {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: Empty Extension!\n",
+ p2->filename, p2->startline, p2->endline);
+ }
+
+ if ( exten->plist_last && exten->plist_last->type == AEL_LABEL ) {
+ struct ael_priority *np2 = new_prio();
+ np2->type = AEL_APPCALL;
+ np2->app = strdup("NoOp");
+ snprintf(buf,sizeof(buf),"A NoOp to follow a trailing label %s", exten->plist_last->origin->u1.str);
+ np2->appargs = strdup(buf);
+ linkprio(exten, np2);
+ }
+
+ set_priorities(exten);
+ attach_exten(&exten_list, exten);
+ break;
+
+ case PV_IGNOREPAT:
+ ast_context_add_ignorepat2(context, p2->u1.str, registrar);
+ break;
+
+ case PV_INCLUDES:
+ for (p3 = p2->u1.list; p3 ;p3=p3->next) {
+ if ( p3->u2.arglist ) {
+ snprintf(buf,sizeof(buf), "%s,%s,%s,%s,%s",
+ p3->u1.str,
+ p3->u2.arglist->u1.str,
+ p3->u2.arglist->next->u1.str,
+ p3->u2.arglist->next->next->u1.str,
+ p3->u2.arglist->next->next->next->u1.str);
+ ast_context_add_include2(context, buf, registrar);
+ } else
+ ast_context_add_include2(context, p3->u1.str, registrar);
+ }
+ break;
+
+ case PV_SWITCHES:
+ for (p3 = p2->u1.list; p3 ;p3=p3->next) {
+ char *c = strchr(p3->u1.str, '/');
+ if (c) {
+ *c = '\0';
+ c++;
+ } else
+ c = "";
+
+ ast_context_add_switch2(context, p3->u1.str, c, 0, registrar);
+ }
+ break;
+
+ case PV_ESWITCHES:
+ for (p3 = p2->u1.list; p3 ;p3=p3->next) {
+ char *c = strchr(p3->u1.str, '/');
+ if (c) {
+ *c = '\0';
+ c++;
+ } else
+ c = "";
+
+ ast_context_add_switch2(context, p3->u1.str, c, 1, registrar);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ break;
+
+ default:
+ /* huh? what? */
+ break;
+
+ }
+ }
+ /* moved these from being done after a macro or extension were processed,
+ to after all processing is done, for the sake of fixing gotos to labels inside cases... */
+ /* I guess this would be considered 2nd pass of compiler now... */
+ fix_gotos_in_extensions(exten_list); /* find and fix extension ref in gotos to labels that are in case statements */
+ add_extensions(exten_list); /* actually makes calls to create priorities in ast_contexts -- feeds dialplan to asterisk */
+ destroy_extensions(exten_list); /* all that remains is an empty husk, discard of it as is proper */
+
+}
+
+
+/* DESTROY the PVAL tree ============================================================================ */
+
+
+
+void destroy_pval_item(pval *item)
+{
+ if (item == NULL) {
+ ast_log(LOG_WARNING, "null item\n");
+ return;
+ }
+
+ if (item->filename)
+ free(item->filename);
+
+ switch (item->type) {
+ case PV_WORD:
+ /* fields: item->u1.str == string associated with this (word). */
+ if (item->u1.str )
+ free(item->u1.str);
+ if ( item->u2.arglist )
+ destroy_pval(item->u2.arglist);
+ break;
+
+ case PV_MACRO:
+ /* fields: item->u1.str == name of macro
+ item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
+ item->u2.arglist->u1.str == argument
+ item->u2.arglist->next == next arg
+
+ item->u3.macro_statements == pval list of statements in macro body.
+ */
+ destroy_pval(item->u2.arglist);
+ if (item->u1.str )
+ free(item->u1.str);
+ destroy_pval(item->u3.macro_statements);
+ break;
+
+ case PV_CONTEXT:
+ /* fields: item->u1.str == name of context
+ item->u2.statements == pval list of statements in context body
+ item->u3.abstract == int 1 if an abstract keyword were present
+ */
+ if (item->u1.str)
+ free(item->u1.str);
+ destroy_pval(item->u2.statements);
+ break;
+
+ case PV_MACRO_CALL:
+ /* fields: item->u1.str == name of macro to call
+ item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
+ item->u2.arglist->u1.str == argument
+ item->u2.arglist->next == next arg
+ */
+ if (item->u1.str)
+ free(item->u1.str);
+ destroy_pval(item->u2.arglist);
+ break;
+
+ case PV_APPLICATION_CALL:
+ /* fields: item->u1.str == name of application to call
+ item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
+ item->u2.arglist->u1.str == argument
+ item->u2.arglist->next == next arg
+ */
+ if (item->u1.str)
+ free(item->u1.str);
+ destroy_pval(item->u2.arglist);
+ break;
+
+ case PV_CASE:
+ /* fields: item->u1.str == value of case
+ item->u2.statements == pval list of statements under the case
+ */
+ if (item->u1.str)
+ free(item->u1.str);
+ destroy_pval(item->u2.statements);
+ break;
+
+ case PV_PATTERN:
+ /* fields: item->u1.str == value of case
+ item->u2.statements == pval list of statements under the case
+ */
+ if (item->u1.str)
+ free(item->u1.str);
+ destroy_pval(item->u2.statements);
+ break;
+
+ case PV_DEFAULT:
+ /* fields:
+ item->u2.statements == pval list of statements under the case
+ */
+ destroy_pval(item->u2.statements);
+ break;
+
+ case PV_CATCH:
+ /* fields: item->u1.str == name of extension to catch
+ item->u2.statements == pval list of statements in context body
+ */
+ if (item->u1.str)
+ free(item->u1.str);
+ destroy_pval(item->u2.statements);
+ break;
+
+ case PV_SWITCHES:
+ /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
+ */
+ destroy_pval(item->u1.list);
+ break;
+
+ case PV_ESWITCHES:
+ /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
+ */
+ destroy_pval(item->u1.list);
+ break;
+
+ case PV_INCLUDES:
+ /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
+ item->u2.arglist == pval list of 4 PV_WORD elements for time values
+ */
+ destroy_pval(item->u1.list);
+ break;
+
+ case PV_STATEMENTBLOCK:
+ /* fields: item->u1.list == pval list of statements in block, one per entry in the list
+ */
+ destroy_pval(item->u1.list);
+ break;
+
+ case PV_LOCALVARDEC:
+ case PV_VARDEC:
+ /* fields: item->u1.str == variable name
+ item->u2.val == variable value to assign
+ */
+ if (item->u1.str)
+ free(item->u1.str);
+ if (item->u2.val)
+ free(item->u2.val);
+ break;
+
+ case PV_GOTO:
+ /* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user.
+ item->u1.list->u1.str == where the data on a PV_WORD will always be.
+ */
+
+ destroy_pval(item->u1.list);
+ break;
+
+ case PV_LABEL:
+ /* fields: item->u1.str == label name
+ */
+ if (item->u1.str)
+ free(item->u1.str);
+ break;
+
+ case PV_FOR:
+ /* fields: item->u1.for_init == a string containing the initalizer
+ item->u2.for_test == a string containing the loop test
+ item->u3.for_inc == a string containing the loop increment
+
+ item->u4.for_statements == a pval list of statements in the for ()
+ */
+ if (item->u1.for_init)
+ free(item->u1.for_init);
+ if (item->u2.for_test)
+ free(item->u2.for_test);
+ if (item->u3.for_inc)
+ free(item->u3.for_inc);
+ destroy_pval(item->u4.for_statements);
+ break;
+
+ case PV_WHILE:
+ /* fields: item->u1.str == the while conditional, as supplied by user
+
+ item->u2.statements == a pval list of statements in the while ()
+ */
+ if (item->u1.str)
+ free(item->u1.str);
+ destroy_pval(item->u2.statements);
+ break;
+
+ case PV_BREAK:
+ /* fields: none
+ */
+ break;
+
+ case PV_RETURN:
+ /* fields: none
+ */
+ break;
+
+ case PV_CONTINUE:
+ /* fields: none
+ */
+ break;
+
+ case PV_IFTIME:
+ /* fields: item->u1.list == the 4 time values, in PV_WORD structs, linked list
+
+ item->u2.statements == a pval list of statements in the if ()
+ item->u3.else_statements == a pval list of statements in the else
+ (could be zero)
+ */
+ destroy_pval(item->u1.list);
+ destroy_pval(item->u2.statements);
+ if (item->u3.else_statements) {
+ destroy_pval(item->u3.else_statements);
+ }
+ break;
+
+ case PV_RANDOM:
+ /* fields: item->u1.str == the random percentage, as supplied by user
+
+ item->u2.statements == a pval list of statements in the true part ()
+ item->u3.else_statements == a pval list of statements in the else
+ (could be zero)
+ fall thru to If */
+ case PV_IF:
+ /* fields: item->u1.str == the if conditional, as supplied by user
+
+ item->u2.statements == a pval list of statements in the if ()
+ item->u3.else_statements == a pval list of statements in the else
+ (could be zero)
+ */
+ if (item->u1.str)
+ free(item->u1.str);
+ destroy_pval(item->u2.statements);
+ if (item->u3.else_statements) {
+ destroy_pval(item->u3.else_statements);
+ }
+ break;
+
+ case PV_SWITCH:
+ /* fields: item->u1.str == the switch expression
+
+ item->u2.statements == a pval list of statements in the switch,
+ (will be case statements, most likely!)
+ */
+ if (item->u1.str)
+ free(item->u1.str);
+ destroy_pval(item->u2.statements);
+ break;
+
+ case PV_EXTENSION:
+ /* fields: item->u1.str == the extension name, label, whatever it's called
+
+ item->u2.statements == a pval list of statements in the extension
+ item->u3.hints == a char * hint argument
+ item->u4.regexten == an int boolean. non-zero says that regexten was specified
+ */
+ if (item->u1.str)
+ free(item->u1.str);
+ if (item->u3.hints)
+ free(item->u3.hints);
+ destroy_pval(item->u2.statements);
+ break;
+
+ case PV_IGNOREPAT:
+ /* fields: item->u1.str == the ignorepat data
+ */
+ if (item->u1.str)
+ free(item->u1.str);
+ break;
+
+ case PV_GLOBALS:
+ /* fields: item->u1.statements == pval list of statements, usually vardecs
+ */
+ destroy_pval(item->u1.statements);
+ break;
+ }
+ free(item);
+}
+
+void destroy_pval(pval *item)
+{
+ pval *i,*nxt;
+
+ for (i=item; i; i=nxt) {
+ nxt = i->next;
+
+ destroy_pval_item(i);
+ }
+}
+
+#ifdef AAL_ARGCHECK
+static char *ael_funclist[] =
+{
+ "AGENT",
+ "ARRAY",
+ "BASE64_DECODE",
+ "BASE64_ENCODE",
+ "CALLERID",
+ "CDR",
+ "CHANNEL",
+ "CHECKSIPDOMAIN",
+ "CHECK_MD5",
+ "CURL",
+ "CUT",
+ "DB",
+ "DB_EXISTS",
+ "DUNDILOOKUP",
+ "ENUMLOOKUP",
+ "ENV",
+ "EVAL",
+ "EXISTS",
+ "FIELDQTY",
+ "FILTER",
+ "GROUP",
+ "GROUP_COUNT",
+ "GROUP_LIST",
+ "GROUP_MATCH_COUNT",
+ "IAXPEER",
+ "IF",
+ "IFTIME",
+ "ISNULL",
+ "KEYPADHASH",
+ "LANGUAGE",
+ "LEN",
+ "MATH",
+ "MD5",
+ "MUSICCLASS",
+ "QUEUEAGENTCOUNT",
+ "QUEUE_MEMBER_COUNT",
+ "QUEUE_MEMBER_LIST",
+ "QUOTE",
+ "RAND",
+ "REGEX",
+ "SET",
+ "SHA1",
+ "SIPCHANINFO",
+ "SIPPEER",
+ "SIP_HEADER",
+ "SORT",
+ "STAT",
+ "STRFTIME",
+ "STRPTIME",
+ "TIMEOUT",
+ "TXTCIDNAME",
+ "URIDECODE",
+ "URIENCODE",
+ "VMCOUNT"
+};
+
+
+int ael_is_funcname(char *name)
+{
+ int s,t;
+ t = sizeof(ael_funclist)/sizeof(char*);
+ s = 0;
+ while ((s < t) && strcasecmp(name, ael_funclist[s]))
+ s++;
+ if ( s < t )
+ return 1;
+ else
+ return 0;
+}
+#endif
+
+
+/* PVAL PI */
+
+/* ----------------- implementation ------------------- */
+
+
+int pvalCheckType( pval *p, char *funcname, pvaltype type )
+{
+ if (p->type != type)
+ {
+ ast_log(LOG_ERROR, "Func: %s the pval passed is not appropriate for this function!\n", funcname);
+ return 0;
+ }
+ return 1;
+}
+
+
+pval *pvalCreateNode( pvaltype type )
+{
+ pval *p = calloc(1,sizeof(pval)); /* why, oh why, don't I use ast_calloc? Way, way, way too messy if I do! */
+ p->type = type; /* remember, this can be used externally or internally to asterisk */
+ return p;
+}
+
+pvaltype pvalObjectGetType( pval *p )
+{
+ return p->type;
+}
+
+
+void pvalWordSetString( pval *p, char *str)
+{
+ if (!pvalCheckType(p, "pvalWordSetString", PV_WORD))
+ return;
+ p->u1.str = str;
+}
+
+char *pvalWordGetString( pval *p )
+{
+ if (!pvalCheckType(p, "pvalWordGetString", PV_WORD))
+ return 0;
+ return p->u1.str;
+}
+
+
+void pvalMacroSetName( pval *p, char *name)
+{
+ if (!pvalCheckType(p, "pvalMacroSetName", PV_MACRO))
+ return;
+ p->u1.str = name;
+}
+
+char *pvalMacroGetName( pval *p )
+{
+ if (!pvalCheckType(p, "pvalMacroGetName", PV_MACRO))
+ return 0;
+ return p->u1.str;
+}
+
+void pvalMacroSetArglist( pval *p, pval *arglist )
+{
+ if (!pvalCheckType(p, "pvalMacroSetArglist", PV_MACRO))
+ return;
+ p->u2.arglist = arglist;
+}
+
+void pvalMacroAddArg( pval *p, pval *arg ) /* single arg only! */
+{
+ if (!pvalCheckType(p, "pvalMacroAddArg", PV_MACRO))
+ return;
+ if (!p->u2.arglist)
+ p->u2.arglist = arg;
+ else
+ linku1(p->u2.arglist, arg);
+
+}
+
+pval *pvalMacroWalkArgs( pval *p, pval **arg )
+{
+ if (!pvalCheckType(p, "pvalMacroWalkArgs", PV_MACRO))
+ return 0;
+ if (!(*arg))
+ *arg = p->u2.arglist;
+ else {
+ *arg = (*arg)->next;
+ }
+ return *arg;
+}
+
+void pvalMacroAddStatement( pval *p, pval *statement )
+{
+ if (!pvalCheckType(p, "pvalMacroAddStatement", PV_MACRO))
+ return;
+ if (!p->u3.macro_statements)
+ p->u3.macro_statements = statement;
+ else
+ linku1(p->u3.macro_statements, statement);
+
+
+}
+
+pval *pvalMacroWalkStatements( pval *p, pval **next_statement )
+{
+ if (!pvalCheckType(p, "pvalMacroWalkStatements", PV_MACRO))
+ return 0;
+ if (!(*next_statement))
+ *next_statement = p->u3.macro_statements;
+ else {
+ *next_statement = (*next_statement)->next;
+ }
+ return *next_statement;
+}
+
+
+
+void pvalContextSetName( pval *p, char *name)
+{
+ if (!pvalCheckType(p, "pvalContextSetName", PV_CONTEXT))
+ return;
+ p->u1.str = name;
+}
+
+char *pvalContextGetName( pval *p )
+{
+ if (!pvalCheckType(p, "pvalContextGetName", PV_CONTEXT))
+ return 0;
+ return p->u1.str;
+}
+
+void pvalContextSetAbstract( pval *p )
+{
+ if (!pvalCheckType(p, "pvalContextSetAbstract", PV_CONTEXT))
+ return;
+ p->u3.abstract = 1;
+}
+
+void pvalContextUnsetAbstract( pval *p )
+{
+ if (!pvalCheckType(p, "pvalContextUnsetAbstract", PV_CONTEXT))
+ return;
+ p->u3.abstract = 0;
+}
+
+int pvalContextGetAbstract( pval *p )
+{
+ if (!pvalCheckType(p, "pvalContextGetAbstract", PV_CONTEXT))
+ return 0;
+ return p->u3.abstract;
+}
+
+
+
+void pvalContextAddStatement( pval *p, pval *statement) /* this includes SWITCHES, INCLUDES, IGNOREPAT, etc */
+{
+ if (!pvalCheckType(p, "pvalContextAddStatement", PV_CONTEXT))
+ return;
+ if (!p->u2.statements)
+ p->u2.statements = statement;
+ else
+ linku1(p->u2.statements, statement);
+}
+
+pval *pvalContextWalkStatements( pval *p, pval **statements )
+{
+ if (!pvalCheckType(p, "pvalContextWalkStatements", PV_CONTEXT))
+ return 0;
+ if (!(*statements))
+ *statements = p->u2.statements;
+ else {
+ *statements = (*statements)->next;
+ }
+ return *statements;
+}
+
+
+void pvalMacroCallSetMacroName( pval *p, char *name )
+{
+ if (!pvalCheckType(p, "pvalMacroCallSetMacroName", PV_MACRO_CALL))
+ return;
+ p->u1.str = name;
+}
+
+char* pvalMacroCallGetMacroName( pval *p )
+{
+ if (!pvalCheckType(p, "pvalMacroCallGetMacroName", PV_MACRO_CALL))
+ return 0;
+ return p->u1.str;
+}
+
+void pvalMacroCallSetArglist( pval *p, pval *arglist )
+{
+ if (!pvalCheckType(p, "pvalMacroCallSetArglist", PV_MACRO_CALL))
+ return;
+ p->u2.arglist = arglist;
+}
+
+void pvalMacroCallAddArg( pval *p, pval *arg )
+{
+ if (!pvalCheckType(p, "pvalMacroCallGetAddArg", PV_MACRO_CALL))
+ return;
+ if (!p->u2.arglist)
+ p->u2.arglist = arg;
+ else
+ linku1(p->u2.arglist, arg);
+}
+
+pval *pvalMacroCallWalkArgs( pval *p, pval **args )
+{
+ if (!pvalCheckType(p, "pvalMacroCallWalkArgs", PV_MACRO_CALL))
+ return 0;
+ if (!(*args))
+ *args = p->u2.arglist;
+ else {
+ *args = (*args)->next;
+ }
+ return *args;
+}
+
+
+void pvalAppCallSetAppName( pval *p, char *name )
+{
+ if (!pvalCheckType(p, "pvalAppCallSetAppName", PV_APPLICATION_CALL))
+ return;
+ p->u1.str = name;
+}
+
+char* pvalAppCallGetAppName( pval *p )
+{
+ if (!pvalCheckType(p, "pvalAppCallGetAppName", PV_APPLICATION_CALL))
+ return 0;
+ return p->u1.str;
+}
+
+void pvalAppCallSetArglist( pval *p, pval *arglist )
+{
+ if (!pvalCheckType(p, "pvalAppCallSetArglist", PV_APPLICATION_CALL))
+ return;
+ p->u2.arglist = arglist;
+}
+
+void pvalAppCallAddArg( pval *p, pval *arg )
+{
+ if (!pvalCheckType(p, "pvalAppCallAddArg", PV_APPLICATION_CALL))
+ return;
+ if (!p->u2.arglist)
+ p->u2.arglist = arg;
+ else
+ linku1(p->u2.arglist, arg);
+}
+
+pval *pvalAppCallWalkArgs( pval *p, pval **args )
+{
+ if (!pvalCheckType(p, "pvalAppCallWalkArgs", PV_APPLICATION_CALL))
+ return 0;
+ if (!(*args))
+ *args = p->u2.arglist;
+ else {
+ *args = (*args)->next;
+ }
+ return *args;
+}
+
+
+void pvalCasePatSetVal( pval *p, char *val )
+{
+ if (!pvalCheckType(p, "pvalAppCallWalkArgs", PV_APPLICATION_CALL))
+ return;
+ p->u1.str = val;
+}
+
+char* pvalCasePatGetVal( pval *p )
+{
+ return p->u1.str;
+}
+
+void pvalCasePatDefAddStatement( pval *p, pval *statement )
+{
+ if (!p->u2.arglist)
+ p->u2.statements = statement;
+ else
+ linku1(p->u2.statements, statement);
+}
+
+pval *pvalCasePatDefWalkStatements( pval *p, pval **statement )
+{
+ if (!(*statement))
+ *statement = p->u2.statements;
+ else {
+ *statement = (*statement)->next;
+ }
+ return *statement;
+}
+
+
+void pvalCatchSetExtName( pval *p, char *name )
+{
+ if (!pvalCheckType(p, "pvalCatchSetExtName", PV_CATCH))
+ return;
+ p->u1.str = name;
+}
+
+char* pvalCatchGetExtName( pval *p )
+{
+ if (!pvalCheckType(p, "pvalCatchGetExtName", PV_CATCH))
+ return 0;
+ return p->u1.str;
+}
+
+void pvalCatchSetStatement( pval *p, pval *statement )
+{
+ if (!pvalCheckType(p, "pvalCatchSetStatement", PV_CATCH))
+ return;
+ p->u2.statements = statement;
+}
+
+pval *pvalCatchGetStatement( pval *p )
+{
+ if (!pvalCheckType(p, "pvalCatchGetStatement", PV_CATCH))
+ return 0;
+ return p->u2.statements;
+}
+
+
+void pvalSwitchesAddSwitch( pval *p, char *name )
+{
+ pval *s;
+ if (!pvalCheckType(p, "pvalSwitchesAddSwitch", PV_SWITCHES))
+ return;
+ s = pvalCreateNode(PV_WORD);
+ s->u1.str = name;
+ p->u1.list = linku1(p->u1.list, s);
+}
+
+char* pvalSwitchesWalkNames( pval *p, pval **next_item )
+{
+ if (!pvalCheckType(p, "pvalSwitchesWalkNames", PV_SWITCHES))
+ return 0;
+ if (!(*next_item))
+ *next_item = p->u1.list;
+ else {
+ *next_item = (*next_item)->next;
+ }
+ return (*next_item)->u1.str;
+}
+
+void pvalESwitchesAddSwitch( pval *p, char *name )
+{
+ pval *s;
+ if (!pvalCheckType(p, "pvalESwitchesAddSwitch", PV_ESWITCHES))
+ return;
+ s = pvalCreateNode(PV_WORD);
+ s->u1.str = name;
+ p->u1.list = linku1(p->u1.list, s);
+}
+
+char* pvalESwitchesWalkNames( pval *p, pval **next_item )
+{
+ if (!pvalCheckType(p, "pvalESwitchesWalkNames", PV_ESWITCHES))
+ return 0;
+ if (!(*next_item))
+ *next_item = p->u1.list;
+ else {
+ *next_item = (*next_item)->next;
+ }
+ return (*next_item)->u1.str;
+}
+
+
+void pvalIncludesAddInclude( pval *p, const char *include )
+{
+ pval *s;
+ if (!pvalCheckType(p, "pvalIncludesAddSwitch", PV_INCLUDES))
+ return;
+ s = pvalCreateNode(PV_WORD);
+ s->u1.str = (char *)include;
+ p->u1.list = linku1(p->u1.list, s);
+}
+ /* an include is a WORD with string set to path */
+
+void pvalIncludesAddIncludeWithTimeConstraints( pval *p, const char *include, char *hour_range, char *dom_range, char *dow_range, char *month_range )
+{
+ pval *hr = pvalCreateNode(PV_WORD);
+ pval *dom = pvalCreateNode(PV_WORD);
+ pval *dow = pvalCreateNode(PV_WORD);
+ pval *mon = pvalCreateNode(PV_WORD);
+ pval *s = pvalCreateNode(PV_WORD);
+
+ if (!pvalCheckType(p, "pvalIncludeAddIncludeWithTimeConstraints", PV_INCLUDES))
+ return;
+
+ s->u1.str = (char *)include;
+ p->u1.list = linku1(p->u1.list, s);
+
+ hr->u1.str = hour_range;
+ dom->u1.str = dom_range;
+ dow->u1.str = dow_range;
+ mon->u1.str = month_range;
+
+ s->u2.arglist = hr;
+
+ hr->next = dom;
+ dom->next = dow;
+ dow->next = mon;
+ mon->next = 0;
+}
+ /* is this right??? come back and correct it */ /*the ptr is to the WORD */
+void pvalIncludeGetTimeConstraints( pval *p, char **hour_range, char **dom_range, char **dow_range, char **month_range )
+{
+ if (!pvalCheckType(p, "pvalIncludeGetTimeConstraints", PV_WORD))
+ return;
+ if (p->u2.arglist) {
+ *hour_range = p->u2.arglist->u1.str;
+ *dom_range = p->u2.arglist->next->u1.str;
+ *dow_range = p->u2.arglist->next->next->u1.str;
+ *month_range = p->u2.arglist->next->next->next->u1.str;
+ } else {
+ *hour_range = 0;
+ *dom_range = 0;
+ *dow_range = 0;
+ *month_range = 0;
+ }
+}
+ /* is this right??? come back and correct it */ /*the ptr is to the WORD */
+char* pvalIncludesWalk( pval *p, pval **next_item )
+{
+ if (!pvalCheckType(p, "pvalIncludesWalk", PV_INCLUDES))
+ return 0;
+ if (!(*next_item))
+ *next_item = p->u1.list;
+ else {
+ *next_item = (*next_item)->next;
+ }
+ return (*next_item)->u1.str;
+}
+
+
+void pvalStatementBlockAddStatement( pval *p, pval *statement)
+{
+ if (!pvalCheckType(p, "pvalStatementBlockAddStatement", PV_STATEMENTBLOCK))
+ return;
+ p->u1.list = linku1(p->u1.list, statement);
+}
+
+pval *pvalStatementBlockWalkStatements( pval *p, pval **next_statement)
+{
+ if (!pvalCheckType(p, "pvalStatementBlockWalkStatements", PV_STATEMENTBLOCK))
+ return 0;
+ if (!(*next_statement))
+ *next_statement = p->u1.list;
+ else {
+ *next_statement = (*next_statement)->next;
+ }
+ return *next_statement;
+}
+
+void pvalVarDecSetVarname( pval *p, char *name )
+{
+ if (!pvalCheckType(p, "pvalVarDecSetVarname", PV_VARDEC))
+ return;
+ p->u1.str = name;
+}
+
+void pvalVarDecSetValue( pval *p, char *value )
+{
+ if (!pvalCheckType(p, "pvalVarDecSetValue", PV_VARDEC))
+ return;
+ p->u2.val = value;
+}
+
+char* pvalVarDecGetVarname( pval *p )
+{
+ if (!pvalCheckType(p, "pvalVarDecGetVarname", PV_VARDEC))
+ return 0;
+ return p->u1.str;
+}
+
+char* pvalVarDecGetValue( pval *p )
+{
+ if (!pvalCheckType(p, "pvalVarDecGetValue", PV_VARDEC))
+ return 0;
+ return p->u2.val;
+}
+
+void pvalGotoSetTarget( pval *p, char *context, char *exten, char *label )
+{
+ pval *con, *ext, *pri;
+
+ if (!pvalCheckType(p, "pvalGotoSetTarget", PV_GOTO))
+ return;
+ if (context && strlen(context)) {
+ con = pvalCreateNode(PV_WORD);
+ ext = pvalCreateNode(PV_WORD);
+ pri = pvalCreateNode(PV_WORD);
+
+ con->u1.str = context;
+ ext->u1.str = exten;
+ pri->u1.str = label;
+
+ con->next = ext;
+ ext->next = pri;
+ p->u1.list = con;
+ } else if (exten && strlen(exten)) {
+ ext = pvalCreateNode(PV_WORD);
+ pri = pvalCreateNode(PV_WORD);
+
+ ext->u1.str = exten;
+ pri->u1.str = label;
+
+ ext->next = pri;
+ p->u1.list = ext;
+ } else {
+ pri = pvalCreateNode(PV_WORD);
+
+ pri->u1.str = label;
+
+ p->u1.list = pri;
+ }
+}
+
+void pvalGotoGetTarget( pval *p, char **context, char **exten, char **label )
+{
+ if (!pvalCheckType(p, "pvalGotoGetTarget", PV_GOTO))
+ return;
+ if (p->u1.list && p->u1.list->next && p->u1.list->next->next) {
+ *context = p->u1.list->u1.str;
+ *exten = p->u1.list->next->u1.str;
+ *label = p->u1.list->next->next->u1.str;
+
+ } else if (p->u1.list && p->u1.list->next ) {
+ *exten = p->u1.list->u1.str;
+ *label = p->u1.list->next->u1.str;
+ *context = 0;
+
+ } else if (p->u1.list) {
+ *label = p->u1.list->u1.str;
+ *context = 0;
+ *exten = 0;
+
+ } else {
+ *context = 0;
+ *exten = 0;
+ *label = 0;
+ }
+}
+
+
+void pvalLabelSetName( pval *p, char *name )
+{
+ if (!pvalCheckType(p, "pvalLabelSetName", PV_LABEL))
+ return;
+ p->u1.str = name;
+}
+
+char* pvalLabelGetName( pval *p )
+{
+ if (!pvalCheckType(p, "pvalLabelGetName", PV_LABEL))
+ return 0;
+ return p->u1.str;
+}
+
+
+void pvalForSetInit( pval *p, char *init )
+{
+ if (!pvalCheckType(p, "pvalForSetInit", PV_FOR))
+ return;
+ p->u1.for_init = init;
+}
+
+void pvalForSetTest( pval *p, char *test )
+{
+ if (!pvalCheckType(p, "pvalForSetTest", PV_FOR))
+ return;
+ p->u2.for_test = test;
+}
+
+void pvalForSetInc( pval *p, char *inc )
+{
+ if (!pvalCheckType(p, "pvalForSetInc", PV_FOR))
+ return;
+ p->u3.for_inc = inc;
+}
+
+void pvalForSetStatement( pval *p, pval *statement )
+{
+ if (!pvalCheckType(p, "pvalForSetStatement", PV_FOR))
+ return;
+ p->u4.for_statements = statement;
+}
+
+char* pvalForGetInit( pval *p )
+{
+ if (!pvalCheckType(p, "pvalForGetInit", PV_FOR))
+ return 0;
+ return p->u1.for_init;
+}
+
+char* pvalForGetTest( pval *p )
+{
+ if (!pvalCheckType(p, "pvalForGetTest", PV_FOR))
+ return 0;
+ return p->u2.for_test;
+}
+
+char* pvalForGetInc( pval *p )
+{
+ if (!pvalCheckType(p, "pvalForGetInc", PV_FOR))
+ return 0;
+ return p->u3.for_inc;
+}
+
+pval* pvalForGetStatement( pval *p )
+{
+ if (!pvalCheckType(p, "pvalForGetStatement", PV_FOR))
+ return 0;
+ return p->u4.for_statements;
+}
+
+
+
+void pvalIfSetCondition( pval *p, char *expr )
+{
+ if (!pvalCheckType(p, "pvalIfSetCondition", PV_IF))
+ return;
+ p->u1.str = expr;
+}
+
+char* pvalIfGetCondition( pval *p )
+{
+ if (!pvalCheckType(p, "pvalIfGetCondition", PV_IFTIME))
+ return 0;
+ return p->u1.str;
+}
+
+void pvalIfTimeSetCondition( pval *p, char *hour_range, char *dow_range, char *dom_range, char *mon_range ) /* time range format: 24-hour format begin-end|dow range|dom range|month range */
+{
+ pval *hr = pvalCreateNode(PV_WORD);
+ pval *dow = pvalCreateNode(PV_WORD);
+ pval *dom = pvalCreateNode(PV_WORD);
+ pval *mon = pvalCreateNode(PV_WORD);
+ if (!pvalCheckType(p, "pvalIfTimeSetCondition", PV_IFTIME))
+ return;
+ pvalWordSetString(hr, hour_range);
+ pvalWordSetString(dow, dow_range);
+ pvalWordSetString(dom, dom_range);
+ pvalWordSetString(mon, mon_range);
+ dom->next = mon;
+ dow->next = dom;
+ hr->next = dow;
+ p->u1.list = hr;
+}
+
+ /* is this right??? come back and correct it */
+void pvalIfTimeGetCondition( pval *p, char **hour_range, char **dow_range, char **dom_range, char **month_range )
+{
+ if (!pvalCheckType(p, "pvalIfTimeGetCondition", PV_IFTIME))
+ return;
+ *hour_range = p->u1.list->u1.str;
+ *dow_range = p->u1.list->next->u1.str;
+ *dom_range = p->u1.list->next->next->u1.str;
+ *month_range = p->u1.list->next->next->next->u1.str;
+}
+
+void pvalRandomSetCondition( pval *p, char *percent )
+{
+ if (!pvalCheckType(p, "pvalRandomSetCondition", PV_RANDOM))
+ return;
+ p->u1.str = percent;
+}
+
+char* pvalRandomGetCondition( pval *p )
+{
+ if (!pvalCheckType(p, "pvalRandomGetCondition", PV_RANDOM))
+ return 0;
+ return p->u1.str;
+}
+
+void pvalConditionalSetThenStatement( pval *p, pval *statement )
+{
+ p->u2.statements = statement;
+}
+
+void pvalConditionalSetElseStatement( pval *p, pval *statement )
+{
+ p->u3.else_statements = statement;
+}
+
+pval* pvalConditionalGetThenStatement( pval *p )
+{
+ return p->u2.statements;
+}
+
+pval* pvalConditionalGetElseStatement( pval *p )
+{
+ return p->u3.else_statements;
+}
+
+void pvalSwitchSetTestexpr( pval *p, char *expr )
+{
+ if (!pvalCheckType(p, "pvalSwitchSetTestexpr", PV_SWITCH))
+ return;
+ p->u1.str = expr;
+}
+
+char* pvalSwitchGetTestexpr( pval *p )
+{
+ if (!pvalCheckType(p, "pvalSwitchGetTestexpr", PV_SWITCH))
+ return 0;
+ return p->u1.str;
+}
+
+void pvalSwitchAddCase( pval *p, pval *Case )
+{
+ if (!pvalCheckType(p, "pvalSwitchAddCase", PV_SWITCH))
+ return;
+ if (!pvalCheckType(Case, "pvalSwitchAddCase", PV_CASE))
+ return;
+ if (!p->u2.statements)
+ p->u2.statements = Case;
+ else
+ linku1(p->u2.statements, Case);
+}
+
+pval* pvalSwitchWalkCases( pval *p, pval **next_case )
+{
+ if (!pvalCheckType(p, "pvalSwitchWalkCases", PV_SWITCH))
+ return 0;
+ if (!(*next_case))
+ *next_case = p->u2.statements;
+ else {
+ *next_case = (*next_case)->next;
+ }
+ return *next_case;
+}
+
+
+void pvalExtenSetName( pval *p, char *name )
+{
+ if (!pvalCheckType(p, "pvalExtenSetName", PV_EXTENSION))
+ return;
+ p->u1.str = name;
+}
+
+char* pvalExtenGetName( pval *p )
+{
+ if (!pvalCheckType(p, "pvalExtenGetName", PV_EXTENSION))
+ return 0;
+ return p->u1.str;
+}
+
+void pvalExtenSetRegexten( pval *p )
+{
+ if (!pvalCheckType(p, "pvalExtenSetRegexten", PV_EXTENSION))
+ return;
+ p->u4.regexten = 1;
+}
+
+void pvalExtenUnSetRegexten( pval *p )
+{
+ if (!pvalCheckType(p, "pvalExtenUnSetRegexten", PV_EXTENSION))
+ return;
+ p->u4.regexten = 0;
+}
+
+int pvalExtenGetRegexten( pval *p )
+{
+ if (!pvalCheckType(p, "pvalExtenGetRegexten", PV_EXTENSION))
+ return 0;
+ return p->u4.regexten;
+}
+
+void pvalExtenSetHints( pval *p, char *hints )
+{
+ if (!pvalCheckType(p, "pvalExtenSetHints", PV_EXTENSION))
+ return;
+ p->u3.hints = hints;
+}
+
+char* pvalExtenGetHints( pval *p )
+{
+ if (!pvalCheckType(p, "pvalExtenGetHints", PV_EXTENSION))
+ return 0;
+ return p->u3.hints;
+}
+
+void pvalExtenSetStatement( pval *p, pval *statement )
+{
+ if (!pvalCheckType(p, "pvalExtenSetStatement", PV_EXTENSION))
+ return;
+ p->u2.statements = statement;
+}
+
+pval* pvalExtenGetStatement( pval *p )
+{
+ if (!pvalCheckType(p, "pvalExtenGetStatement", PV_EXTENSION))
+ return 0;
+ return p->u2.statements;
+}
+
+
+void pvalIgnorePatSetPattern( pval *p, char *pat )
+{
+ if (!pvalCheckType(p, "pvalIgnorePatSetPattern", PV_IGNOREPAT))
+ return;
+ p->u1.str = pat;
+}
+
+char* pvalIgnorePatGetPattern( pval *p )
+{
+ if (!pvalCheckType(p, "pvalIgnorePatGetPattern", PV_IGNOREPAT))
+ return 0;
+ return p->u1.str;
+}
+
+
+void pvalGlobalsAddStatement( pval *p, pval *statement )
+{
+ if (p->type != PV_GLOBALS) {
+ ast_log(LOG_ERROR, "pvalGlobalsAddStatement called where first arg is not a Globals!\n");
+ } else {
+ if (!p->u1.statements) {
+ p->u1.statements = statement;
+ } else {
+ p->u1.statements = linku1(p->u1.statements,statement);
+ }
+ }
+}
+
+pval* pvalGlobalsWalkStatements( pval *p, pval **next_statement )
+{
+ if (!pvalCheckType(p, "pvalGlobalsWalkStatements", PV_GLOBALS))
+ return 0;
+ if (!next_statement) {
+ *next_statement = p;
+ return p;
+ } else {
+ *next_statement = (*next_statement)->next;
+ return (*next_statement)->next;
+ }
+}
+
+
+void pvalTopLevAddObject( pval *p, pval *contextOrObj )
+{
+ if (p) {
+ linku1(p,contextOrObj);
+ } else {
+ ast_log(LOG_ERROR, "First arg to pvalTopLevel is NULL!\n");
+ }
+}
+
+pval *pvalTopLevWalkObjects(pval *p, pval **next_obj )
+{
+ if (!next_obj) {
+ *next_obj = p;
+ return p;
+ } else {
+ *next_obj = (*next_obj)->next;
+ return (*next_obj)->next;
+ }
+}
+
+/* append second element to the list in the first one via next pointers */
+pval * linku1(pval *head, pval *tail)
+{
+ if (!head)
+ return tail;
+ if (tail) {
+ if (!head->next) {
+ head->next = tail;
+ } else {
+ head->u1_last->next = tail;
+ }
+ head->u1_last = tail;
+ tail->prev = head; /* the dad link only points to containers */
+ }
+ return head;
+}
+
+#ifdef HERE_BY_MISTAKE_I_THINK
+static char *config = "extensions.ael";
+int do_pbx_load_module(void)
+{
+ int errs, sem_err, sem_warn, sem_note;
+ char *rfilename;
+ struct ast_context *local_contexts=NULL, *con;
+ struct pval *parse_tree;
+
+ ast_log(LOG_NOTICE, "Starting AEL load process.\n");
+ if (config[0] == '/')
+ rfilename = (char *)config;
+ else {
+ rfilename = alloca(strlen(config) + strlen(ast_config_AST_CONFIG_DIR) + 2);
+ sprintf(rfilename, "%s/%s", ast_config_AST_CONFIG_DIR, config);
+ }
+ ast_log(LOG_NOTICE, "AEL load process: calculated config file name '%s'.\n", rfilename);
+
+ if (access(rfilename,R_OK) != 0) {
+ ast_log(LOG_NOTICE, "File %s not found; AEL declining load\n", rfilename);
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ parse_tree = ael2_parse(rfilename, &errs);
+ ast_log(LOG_NOTICE, "AEL load process: parsed config file name '%s'.\n", rfilename);
+ ael2_semantic_check(parse_tree, &sem_err, &sem_warn, &sem_note);
+ if (errs == 0 && sem_err == 0) {
+ ast_log(LOG_NOTICE, "AEL load process: checked config file name '%s'.\n", rfilename);
+ ast_compile_ael2(&local_contexts, parse_tree);
+ ast_log(LOG_NOTICE, "AEL load process: compiled config file name '%s'.\n", rfilename);
+
+ ast_merge_contexts_and_delete(&local_contexts, registrar);
+ ast_log(LOG_NOTICE, "AEL load process: merged config file name '%s'.\n", rfilename);
+ for (con = ast_walk_contexts(NULL); con; con = ast_walk_contexts(con))
+ ast_context_verify_includes(con);
+ ast_log(LOG_NOTICE, "AEL load process: verified config file name '%s'.\n", rfilename);
+ } else {
+ ast_log(LOG_ERROR, "Sorry, but %d syntax errors and %d semantic errors were detected. It doesn't make sense to compile.\n", errs, sem_err);
+ destroy_pval(parse_tree); /* free up the memory */
+ return AST_MODULE_LOAD_FAILURE;
+ }
+ destroy_pval(parse_tree); /* free up the memory */
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+#endif
+