Missing from murf's last trunk commit, which was why trunk won't compile
authorTilghman Lesher <tilghman@meg.abyt.es>
Wed, 15 Aug 2007 21:25:13 +0000 (21:25 +0000)
committerTilghman Lesher <tilghman@meg.abyt.es>
Wed, 15 Aug 2007 21:25:13 +0000 (21:25 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@79623 65c4cc65-6c06-0410-ace0-fbb531ad65f3

include/asterisk/extconf.h [new file with mode: 0644]
include/asterisk/pval.h [new file with mode: 0644]
utils/conf2ael.c [new file with mode: 0644]
utils/extconf.c [new file with mode: 0644]
utils/pval.c [new file with mode: 0644]

diff --git a/include/asterisk/extconf.h b/include/asterisk/extconf.h
new file mode 100644 (file)
index 0000000..f0e9195
--- /dev/null
@@ -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 (file)
index 0000000..11d672d
--- /dev/null
@@ -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 (file)
index 0000000..7caf069
--- /dev/null
@@ -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 (file)
index 0000000..121e595
--- /dev/null
@@ -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 (file)
index 0000000..d2c3714
--- /dev/null
@@ -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) {
+