diff --git a/CHANGES b/CHANGES index 25ad24c1e1973a2067620ceef7058863a83fc2fa..283ad2c37fd7aee339bd5c378fbb5cb8e30d8cb2 100644 --- a/CHANGES +++ b/CHANGES @@ -186,6 +186,12 @@ AEL Changes by saying "local myvar=someval;" or using Set() in this fashion: Set(LOCAL(myvar)=someval); ("local" is now an AEL keyword). + * utils/conf2ael introduced. Will convert an extensions.conf + file into extensions.ael. Very crude and unfinished, but + will be improved as time goes by. Should be useful for a + first pass at conversion. + * aelparse will now read extensions.conf to see if a referenced + macro or context is there before issueing a warning. Zaptel channel driver (chan_zap) Changes ---------------------------------------- diff --git a/UPGRADE.txt b/UPGRADE.txt index 034ecaf30fb5c5e23aa46adfb41dc6fba9ef4588..c957a765ed44bf58cb0f1615832b61a00aa415fb 100644 --- a/UPGRADE.txt +++ b/UPGRADE.txt @@ -17,6 +17,10 @@ AEL: not include it, but really, you should make sure that all execution paths within your macros end in "return;". +* The conf2ael program is 'introduced' in this release; it is in a rather + crude state, but deemed useful for making a first pass at converting + extensions.conf code into AEL. More intelligence will come with time. + Core: * The 'languageprefix' option in asterisk.conf is now deprecated, and diff --git a/include/asterisk/ael_structs.h b/include/asterisk/ael_structs.h index e220dd24f1208db950a062bd5ba64b961b816f0f..133c4266ab58b834fa88380dd3d57ddec0aee34b 100644 --- a/include/asterisk/ael_structs.h +++ b/include/asterisk/ael_structs.h @@ -25,6 +25,8 @@ #ifndef _ASTERISK_AEL_STRUCTS_H #define _ASTERISK_AEL_STRUCTS_H +#include "pval.h" + #if !defined(SOLARIS) && !defined(__CYGWIN__) /* #include <err.h> */ #else @@ -46,118 +48,15 @@ # endif -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, /* you know, local var=val; */ /* 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; - #if 0 -pval *npval(pvaltype type, int first_line, int last_line, int first_column, int last_column); -void linku1(pval *head, pval *tail); -void print_pval_list(FILE *f, pval *item, int depth); -void print_pval(FILE *f, pval *item, int depth); +#endif void ael2_semantic_check(pval *item, int *errs, int *warns, int *notes); -struct pval *find_label_in_current_context(char *exten, char *label); -struct pval *find_label_in_current_extension(char *label); -int count_labels_in_current_context(char *label); -struct pval *find_label_in_current_db(char *context, char *exten, char *label); +pval *npval(pvaltype type, int first_line, int last_line, int first_column, int last_column); +pval *linku1(pval *head, pval *tail); void ael2_print(char *fname, pval *tree); -#endif struct pval *ael2_parse(char *fname, int *errs); /* in ael.flex */ void destroy_pval(pval *item); - + extern char *prev_word; /* in ael.flex */ #ifndef YY_TYPEDEF_YY_SCANNER_T diff --git a/include/asterisk/ast_expr.h b/include/asterisk/ast_expr.h index 6ca201f42ec48b6748780d33f95020284f4200d4..c478281d03bfbf6b74459df73fb08fdadf73bf1c 100644 --- a/include/asterisk/ast_expr.h +++ b/include/asterisk/ast_expr.h @@ -19,7 +19,6 @@ #ifndef _ASTERISK_EXPR_H #define _ASTERISK_EXPR_H #ifndef STANDALONE -#include "asterisk/channel.h" #endif #if defined(__cplusplus) || defined(c_plusplus) extern "C" { diff --git a/include/asterisk/pbx.h b/include/asterisk/pbx.h index 3ff142e637132d1658fd1d188fb42c01e59a726f..b18b85b075724219f5998f8a05257764fdc6e085 100644 --- a/include/asterisk/pbx.h +++ b/include/asterisk/pbx.h @@ -904,6 +904,52 @@ int ast_func_read(struct ast_channel *chan, const char *function, char *workspac */ int ast_func_write(struct ast_channel *chan, const char *function, const char *value); +/*! + * 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 STATUS_NO_CONTEXT 1 +#define STATUS_NO_EXTENSION 2 +#define STATUS_NO_PRIORITY 3 +#define STATUS_NO_LABEL 4 +#define STATUS_SUCCESS 5 +#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 */ +}; + +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); + + + #if defined(__cplusplus) || defined(c_plusplus) } #endif diff --git a/main/pbx.c b/main/pbx.c index 7d1b554d08c22ed487c74d36f94e9e890a2c9935..259385428f16ec92e1f10aa297d82789c43948a2 100644 --- a/main/pbx.c +++ b/main/pbx.c @@ -768,21 +768,6 @@ static int ext_cmp(const char *a, const char *b) return (ret > 0) ? 1 : -1; } -/*! - * 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 */ -}; - /*! * \internal * \brief used ast_extension_{match|close} @@ -947,23 +932,7 @@ static int matchcid(const char *cidpattern, const char *callerid) return ast_extension_match(cidpattern, callerid); } -/*! 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 */ -}; - -static struct ast_exten *pbx_find_extension(struct ast_channel *chan, +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) diff --git a/pbx/Makefile b/pbx/Makefile index 22eb2c7789e5787a226c9cf32701379ee5ded6e7..6a229a8d8dc9568e8bd33ab4c5f6a5475f6d8100 100644 --- a/pbx/Makefile +++ b/pbx/Makefile @@ -28,20 +28,6 @@ include $(ASTTOPDIR)/Makefile.moddir_rules clean:: rm -f ael/*.o -ael/ael_lex.o: ael/ael_lex.c ../include/asterisk/ael_structs.h ael/ael.tab.h -ael/ael_lex.o: ASTCFLAGS+=-I. - -ael/ael.tab.o: ael/ael.tab.c ael/ael.tab.h ../include/asterisk/ael_structs.h -ael/ael.tab.o: ASTCFLAGS+=-I. -DYYENABLE_NLS=0 - -$(if $(filter pbx_ael,$(EMBEDDED_MODS)),modules.link,pbx_ael.so): ael/ael.tab.o ael/ael_lex.o - -ael/ael_lex.c: - (cd ael; flex ael.flex; sed -i -e "/begin standard C headers/i#include \"asterisk.h\"" ael_lex.c) - -ael/ael.tab.c ael/ael.tab.h: - (cd ael; bison -v -d ael.y) - dundi-parser.o: dundi-parser.h dundi-parser.o: ASTCFLAGS+=-I. diff --git a/pbx/ael/ael-test/ref.ael-ntest10 b/pbx/ael/ael-test/ref.ael-ntest10 index 3ecc171c5aa6fe7ed810a9db94f493fded71bdc4..02024ee461df5bed53705e494924c6249444dbde 100644 --- a/pbx/ael/ael-test/ref.ael-ntest10 +++ b/pbx/ael/ael-test/ref.ael-ntest10 @@ -5,27 +5,36 @@ (You can use the -w option to dump extensions.conf format to extensions.conf.aeldump) -LOG: lev:2 file:pbx_ael.c line:4092 func: pbx_load_module Starting AEL load process. -LOG: lev:2 file:pbx_ael.c line:4099 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. -LOG: lev:2 file:pbx_ael.c line:4107 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 1-4: The macro endsess does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 6-9: The macro nullchk does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 11-26: The macro endcall does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:2356 func: check_switch_expr Warning: file ./extensions.ael, line 13-13: A default case was automatically added to the switch. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 28-44: The macro endcall2 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:2356 func: check_switch_expr Warning: file ./extensions.ael, line 36-36: A default case was automatically added to the switch. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 46-68: The macro endcall3 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:2356 func: check_switch_expr Warning: file ./extensions.ael, line 48-48: A default case was automatically added to the switch. -LOG: lev:3 file:pbx_ael.c line:2356 func: check_switch_expr Warning: file ./extensions.ael, line 60-60: A default case was automatically added to the switch. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 70-96: The macro endcall4 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:2356 func: check_switch_expr Warning: file ./extensions.ael, line 72-72: A default case was automatically added to the switch. -LOG: lev:3 file:pbx_ael.c line:2356 func: check_switch_expr Warning: file ./extensions.ael, line 84-84: A default case was automatically added to the switch. -LOG: lev:3 file:pbx_ael.c line:2356 func: check_switch_expr Warning: file ./extensions.ael, line 87-87: A default case was automatically added to the switch. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 98-131: The macro endcall5 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:2356 func: check_switch_expr Warning: file ./extensions.ael, line 106-106: A default case was automatically added to the switch. -LOG: lev:3 file:pbx_ael.c line:2356 func: check_switch_expr Warning: file ./extensions.ael, line 119-119: A default case was automatically added to the switch. -LOG: lev:3 file:pbx_ael.c line:2356 func: check_switch_expr Warning: file ./extensions.ael, line 122-122: A default case was automatically added to the switch. -LOG: lev:2 file:pbx_ael.c line:4110 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:1680 func: pbx_load_module Starting AEL load process. +LOG: lev:2 file:pbx_ael.c line:1687 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:1695 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 1-4: The macro endsess does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 6-9: The macro nullchk does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 11-26: The macro endcall does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:2280 func: check_switch_expr Warning: file ./extensions.ael, line 13-13: A default case was automatically added to the switch. +LOG: lev:3 file:extconf.c line:3959 func: add_pri Unable to register extension '_', priority 1 in 'test6', already in use +LOG: lev:3 file:extconf.c line:3959 func: add_pri Unable to register extension '_X.', priority 1 in 'test6', already in use +=== Loading extensions.conf === +Context: test6 +Context: test5 +Context: macro-funcA +Context: macro-funcB +Context: macro-funcC +========= +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 28-44: The macro endcall2 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:2280 func: check_switch_expr Warning: file ./extensions.ael, line 36-36: A default case was automatically added to the switch. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 46-68: The macro endcall3 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:2280 func: check_switch_expr Warning: file ./extensions.ael, line 48-48: A default case was automatically added to the switch. +LOG: lev:3 file:pval.c line:2280 func: check_switch_expr Warning: file ./extensions.ael, line 60-60: A default case was automatically added to the switch. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 70-96: The macro endcall4 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:2280 func: check_switch_expr Warning: file ./extensions.ael, line 72-72: A default case was automatically added to the switch. +LOG: lev:3 file:pval.c line:2280 func: check_switch_expr Warning: file ./extensions.ael, line 84-84: A default case was automatically added to the switch. +LOG: lev:3 file:pval.c line:2280 func: check_switch_expr Warning: file ./extensions.ael, line 87-87: A default case was automatically added to the switch. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 98-131: The macro endcall5 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:2280 func: check_switch_expr Warning: file ./extensions.ael, line 106-106: A default case was automatically added to the switch. +LOG: lev:3 file:pval.c line:2280 func: check_switch_expr Warning: file ./extensions.ael, line 119-119: A default case was automatically added to the switch. +LOG: lev:3 file:pval.c line:2280 func: check_switch_expr Warning: file ./extensions.ael, line 122-122: A default case was automatically added to the switch. +LOG: lev:2 file:pbx_ael.c line:1698 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. Executed ast_context_create(conts, name=endsess, registrar=pbx_ael); Executed ast_context_create(conts, name=nullchk, registrar=pbx_ael); Executed ast_context_create(conts, name=endcall, registrar=pbx_ael); @@ -154,9 +163,9 @@ Executed ast_add_extension2(context=endcall5, rep=0, exten=sw-16-out, priority=1 Executed ast_add_extension2(context=endcall5, rep=0, exten=sw-16-out, priority=13, label=(null), callerid=(null), appl=Goto, data=sw-21-in,ptr1, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=endcall5, rep=0, exten=sw-16-out, priority=14, label=(null), callerid=(null), appl=NoOp, data=Finish if-sw-endcall5-out-16-17, FREE, registrar=pbx_ael); Executed ast_add_extension2(context=endcall5, rep=0, exten=sw-16-out, priority=15, label=(null), callerid=(null), appl=Goto, data=sw-16-in,10, FREE, registrar=pbx_ael); -LOG: lev:2 file:pbx_ael.c line:4112 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:1700 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. Executed ast_merge_contexts_and_delete(); -LOG: lev:2 file:pbx_ael.c line:4115 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:1703 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. Executed ast_walk_contexts(); -LOG: lev:2 file:pbx_ael.c line:4118 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. -LOG: lev:4 file:ael2_parse line:522 func: main 7 contexts, 27 extensions, 121 priorities +LOG: lev:2 file:pbx_ael.c line:1706 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. +LOG: lev:4 file:ael2_parse line:521 func: main 7 contexts, 27 extensions, 121 priorities diff --git a/pbx/ael/ael-test/ref.ael-test1 b/pbx/ael/ael-test/ref.ael-test1 index 5ef73ab381211b325d1f8a43d1458b6220d19c9d..92b2c6b6b38f0a04e5215a823af50f9ed27e9366 100644 --- a/pbx/ael/ael-test/ref.ael-test1 +++ b/pbx/ael/ael-test/ref.ael-test1 @@ -2,18 +2,18 @@ (If you find progress and other non-error messages irritating, you can use -q to suppress them) (You can use the -w option to dump extensions.conf format to extensions.conf.aeldump) -LOG: lev:2 file:pbx_ael.c line:4092 func: pbx_load_module Starting AEL load process. -LOG: lev:2 file:pbx_ael.c line:4099 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. -LOG: lev:2 file:pbx_ael.c line:4107 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 2-16: The macro testdial does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 18-25: The macro exten-gen does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:1408 func: check_goto Warning: file ./extensions.ael, line 21-21: It's bad form to have a goto in a macro to a target outside the macro! -LOG: lev:3 file:pbx_ael.c line:1408 func: check_goto Warning: file ./extensions.ael, line 23-23: It's bad form to have a goto in a macro to a target outside the macro! -LOG: lev:3 file:pbx_ael.c line:2356 func: check_switch_expr Warning: file ./extensions.ael, line 58-58: A default case was automatically added to the switch. -LOG: lev:3 file:pbx_ael.c line:989 func: check_dow Warning: file ./extensions.ael, line 67-67: The day (m0n) must be one of 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', or 'sat'! -LOG: lev:3 file:pbx_ael.c line:947 func: check_timerange Warning: file ./extensions.ael, line 78-78: The end time (25:00) is out of range! -LOG: lev:2 file:pbx_ael.c line:4110 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. -LOG: lev:2 file:pbx_ael.c line:4112 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. -LOG: lev:2 file:pbx_ael.c line:4115 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. -LOG: lev:2 file:pbx_ael.c line:4118 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. -LOG: lev:4 file:ael2_parse line:522 func: main 5 contexts, 14 extensions, 155 priorities +LOG: lev:2 file:pbx_ael.c line:1680 func: pbx_load_module Starting AEL load process. +LOG: lev:2 file:pbx_ael.c line:1687 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:1695 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 2-16: The macro testdial does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 18-25: The macro exten-gen does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:1358 func: check_goto Warning: file ./extensions.ael, line 21-21: It's bad form to have a goto in a macro to a target outside the macro! +LOG: lev:3 file:pval.c line:1358 func: check_goto Warning: file ./extensions.ael, line 23-23: It's bad form to have a goto in a macro to a target outside the macro! +LOG: lev:3 file:pval.c line:2280 func: check_switch_expr Warning: file ./extensions.ael, line 58-58: A default case was automatically added to the switch. +LOG: lev:3 file:pval.c line:918 func: check_dow Warning: file ./extensions.ael, line 67-67: The day (m0n) must be one of 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', or 'sat'! +LOG: lev:3 file:pval.c line:876 func: check_timerange Warning: file ./extensions.ael, line 78-78: The end time (25:00) is out of range! +LOG: lev:2 file:pbx_ael.c line:1698 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:1700 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:1703 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:1706 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. +LOG: lev:4 file:ael2_parse line:521 func: main 5 contexts, 14 extensions, 155 priorities diff --git a/pbx/ael/ael-test/ref.ael-test11 b/pbx/ael/ael-test/ref.ael-test11 index 7211c2b6964456aae0715791dad33683e6841de4..6f1e062e465c2ebcece03fd52608c453c7fc3340 100644 --- a/pbx/ael/ael-test/ref.ael-test11 +++ b/pbx/ael/ael-test/ref.ael-test11 @@ -2,13 +2,13 @@ (If you find progress and other non-error messages irritating, you can use -q to suppress them) (You can use the -w option to dump extensions.conf format to extensions.conf.aeldump) -LOG: lev:2 file:pbx_ael.c line:4092 func: pbx_load_module Starting AEL load process. -LOG: lev:2 file:pbx_ael.c line:4099 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. -LOG: lev:2 file:pbx_ael.c line:4107 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. -LOG: lev:4 file:pbx_ael.c line:1214 func: check_label Error: file ./extensions.ael, line 13-13: Duplicate label lab1! Previously defined at file ./extensions.ael, line 8. -LOG: lev:3 file:pbx_ael.c line:2356 func: check_switch_expr Warning: file ./extensions.ael, line 32-32: A default case was automatically added to the switch. -LOG: lev:3 file:pbx_ael.c line:2356 func: check_switch_expr Warning: file ./extensions.ael, line 44-44: A default case was automatically added to the switch. -LOG: lev:3 file:pbx_ael.c line:2356 func: check_switch_expr Warning: file ./extensions.ael, line 47-47: A default case was automatically added to the switch. -LOG: lev:4 file:pbx_ael.c line:1214 func: check_label Error: file ./extensions.ael, line 49-49: Duplicate label ptr1! Previously defined at file ./extensions.ael, line 33. -LOG: lev:4 file:pbx_ael.c line:4120 func: pbx_load_module Sorry, but 0 syntax errors and 2 semantic errors were detected. It doesn't make sense to compile. -LOG: lev:4 file:ael2_parse line:522 func: main 0 contexts, 0 extensions, 0 priorities +LOG: lev:2 file:pbx_ael.c line:1680 func: pbx_load_module Starting AEL load process. +LOG: lev:2 file:pbx_ael.c line:1687 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:1695 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. +LOG: lev:4 file:pval.c line:1143 func: check_label Error: file ./extensions.ael, line 13-13: Duplicate label lab1! Previously defined at file ./extensions.ael, line 8. +LOG: lev:3 file:pval.c line:2280 func: check_switch_expr Warning: file ./extensions.ael, line 32-32: A default case was automatically added to the switch. +LOG: lev:3 file:pval.c line:2280 func: check_switch_expr Warning: file ./extensions.ael, line 44-44: A default case was automatically added to the switch. +LOG: lev:3 file:pval.c line:2280 func: check_switch_expr Warning: file ./extensions.ael, line 47-47: A default case was automatically added to the switch. +LOG: lev:4 file:pval.c line:1143 func: check_label Error: file ./extensions.ael, line 49-49: Duplicate label ptr1! Previously defined at file ./extensions.ael, line 33. +LOG: lev:4 file:pbx_ael.c line:1708 func: pbx_load_module Sorry, but 0 syntax errors and 2 semantic errors were detected. It doesn't make sense to compile. +LOG: lev:4 file:ael2_parse line:521 func: main 0 contexts, 0 extensions, 0 priorities diff --git a/pbx/ael/ael-test/ref.ael-test14 b/pbx/ael/ael-test/ref.ael-test14 index 2c51ae79f6a998479ade3e3b78c289b58b6c902a..6c0d3770aba97f69cb875fdf646318771d2e66c6 100644 --- a/pbx/ael/ael-test/ref.ael-test14 +++ b/pbx/ael/ael-test/ref.ael-test14 @@ -2,11 +2,11 @@ (If you find progress and other non-error messages irritating, you can use -q to suppress them) (You can use the -w option to dump extensions.conf format to extensions.conf.aeldump) -LOG: lev:2 file:pbx_ael.c line:4092 func: pbx_load_module Starting AEL load process. -LOG: lev:2 file:pbx_ael.c line:4099 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. -LOG: lev:2 file:pbx_ael.c line:4107 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. -LOG: lev:3 file:pbx_ael.c line:2356 func: check_switch_expr Warning: file ./extensions.ael, line 13-13: A default case was automatically added to the switch. -LOG: lev:4 file:pbx_ael.c line:1138 func: check_continue Error: file ./extensions.ael, line 15-15: 'continue' not in 'for' or 'while' statement! -LOG: lev:4 file:pbx_ael.c line:1119 func: check_break Error: file ./extensions.ael, line 17-17: 'break' not in switch, for, or while statement! -LOG: lev:4 file:pbx_ael.c line:4120 func: pbx_load_module Sorry, but 0 syntax errors and 2 semantic errors were detected. It doesn't make sense to compile. -LOG: lev:4 file:ael2_parse line:522 func: main 0 contexts, 0 extensions, 0 priorities +LOG: lev:2 file:pbx_ael.c line:1680 func: pbx_load_module Starting AEL load process. +LOG: lev:2 file:pbx_ael.c line:1687 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:1695 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. +LOG: lev:3 file:pval.c line:2280 func: check_switch_expr Warning: file ./extensions.ael, line 13-13: A default case was automatically added to the switch. +LOG: lev:4 file:pval.c line:1067 func: check_continue Error: file ./extensions.ael, line 15-15: 'continue' not in 'for' or 'while' statement! +LOG: lev:4 file:pval.c line:1048 func: check_break Error: file ./extensions.ael, line 17-17: 'break' not in switch, for, or while statement! +LOG: lev:4 file:pbx_ael.c line:1708 func: pbx_load_module Sorry, but 0 syntax errors and 2 semantic errors were detected. It doesn't make sense to compile. +LOG: lev:4 file:ael2_parse line:521 func: main 0 contexts, 0 extensions, 0 priorities diff --git a/pbx/ael/ael-test/ref.ael-test15 b/pbx/ael/ael-test/ref.ael-test15 index 2240d269b2f78be207757af91f20691954286351..892e516494df7862a155182016df7b8bc6010582 100644 --- a/pbx/ael/ael-test/ref.ael-test15 +++ b/pbx/ael/ael-test/ref.ael-test15 @@ -2,12 +2,12 @@ (If you find progress and other non-error messages irritating, you can use -q to suppress them) (You can use the -w option to dump extensions.conf format to extensions.conf.aeldump) -LOG: lev:2 file:pbx_ael.c line:3910 func: pbx_load_module Starting AEL load process. -LOG: lev:2 file:pbx_ael.c line:3917 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. -LOG: lev:2 file:pbx_ael.c line:3925 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. -LOG: lev:2 file:pbx_ael.c line:3928 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. -LOG: lev:3 file:pbx_ael.c line:3813 func: ast_compile_ael2 Warning: file ./extensions.ael, line 8-13: Empty Extension! -LOG: lev:2 file:pbx_ael.c line:3930 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. -LOG: lev:2 file:pbx_ael.c line:3933 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. -LOG: lev:2 file:pbx_ael.c line:3936 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. -LOG: lev:4 file:ael2_parse line:479 func: main 1 contexts, 0 extensions, 0 priorities +LOG: lev:2 file:pbx_ael.c line:113 func: pbx_load_module Starting AEL load process. +LOG: lev:2 file:pbx_ael.c line:120 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:128 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:131 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. +LOG: lev:3 file:pval.c line:3905 func: ast_compile_ael2 Warning: file ./extensions.ael, line 8-13: Empty Extension! +LOG: lev:2 file:pbx_ael.c line:133 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:136 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:139 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. +LOG: lev:4 file:ael2_parse line:499 func: main 1 contexts, 0 extensions, 0 priorities diff --git a/pbx/ael/ael-test/ref.ael-test16 b/pbx/ael/ael-test/ref.ael-test16 index 323a726c4ebf4ea247ccfdb55e2652cea41066db..ffa9260099c4dcc9e32f7d179eeb4418e580336c 100644 --- a/pbx/ael/ael-test/ref.ael-test16 +++ b/pbx/ael/ael-test/ref.ael-test16 @@ -2,13 +2,13 @@ (If you find progress and other non-error messages irritating, you can use -q to suppress them) (You can use the -w option to dump extensions.conf format to extensions.conf.aeldump) -LOG: lev:2 file:pbx_ael.c line:3914 func: pbx_load_module Starting AEL load process. -LOG: lev:2 file:pbx_ael.c line:3921 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:113 func: pbx_load_module Starting AEL load process. +LOG: lev:2 file:pbx_ael.c line:120 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. LOG: lev:3 file:ael.y line:205 func: ael_yyparse ==== File: ./extensions.ael, Line 1, Cols: 19-19: Warning! The empty context real-small will be IGNORED! -LOG: lev:2 file:pbx_ael.c line:3929 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. -LOG: lev:2 file:pbx_ael.c line:3932 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. -LOG: lev:3 file:pbx_ael.c line:3545 func: add_extensions This file is Empty! -LOG: lev:2 file:pbx_ael.c line:3934 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. -LOG: lev:2 file:pbx_ael.c line:3937 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. -LOG: lev:2 file:pbx_ael.c line:3940 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. -LOG: lev:4 file:ael2_parse line:479 func: main 0 contexts, 0 extensions, 0 priorities +LOG: lev:2 file:pbx_ael.c line:128 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:131 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. +LOG: lev:3 file:pval.c line:3633 func: add_extensions This file is Empty! +LOG: lev:2 file:pbx_ael.c line:133 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:136 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:139 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. +LOG: lev:4 file:ael2_parse line:499 func: main 0 contexts, 0 extensions, 0 priorities diff --git a/pbx/ael/ael-test/ref.ael-test19 b/pbx/ael/ael-test/ref.ael-test19 index 7ff3fd4e1b1c15f88fae1af8802960b5660dfba1..a283b2db95fa43619642b51d2a0d35ccbdcc5559 100644 --- a/pbx/ael/ael-test/ref.ael-test19 +++ b/pbx/ael/ael-test/ref.ael-test19 @@ -2,15 +2,18 @@ (If you find progress and other non-error messages irritating, you can use -q to suppress them) (You can use the -w option to dump extensions.conf format to extensions.conf.aeldump) -LOG: lev:2 file:pbx_ael.c line:4092 func: pbx_load_module Starting AEL load process. -LOG: lev:2 file:pbx_ael.c line:4099 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. -LOG: lev:2 file:pbx_ael.c line:4107 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. -LOG: lev:4 file:pbx_ael.c line:2372 func: check_context_names Error: file ./extensions.ael, line 49-62: The context name (incoming) is also declared in file ./extensions.ael, line 62-69! -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 71-175: The macro std-priv-exten does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:2356 func: check_switch_expr Warning: file ./extensions.ael, line 245-246: A default case was automatically added to the switch. -LOG: lev:3 file:pbx_ael.c line:2472 func: check_pval_item Warning: file ./extensions.ael, line 312-312: macro call to non-existent funcA ! Hopefully it is present in extensions.conf! -LOG: lev:3 file:pbx_ael.c line:2472 func: check_pval_item Warning: file ./extensions.ael, line 313-313: macro call to non-existent funcD ! Hopefully it is present in extensions.conf! -LOG: lev:3 file:pbx_ael.c line:1398 func: check_goto Warning: file ./extensions.ael, line 319-319: goto: no context test5 could be found that matches the goto target! -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 363-366: The macro dialoutpstn does not end with a return; I will insert one. -LOG: lev:4 file:pbx_ael.c line:4120 func: pbx_load_module Sorry, but 0 syntax errors and 1 semantic errors were detected. It doesn't make sense to compile. -LOG: lev:4 file:ael2_parse line:522 func: main 0 contexts, 0 extensions, 0 priorities +LOG: lev:2 file:pbx_ael.c line:1680 func: pbx_load_module Starting AEL load process. +LOG: lev:2 file:pbx_ael.c line:1687 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:1695 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. +LOG: lev:3 file:pval.c line:2296 func: check_context_names Warning: file ./extensions.ael, line 49-62: The context name (incoming) is also declared in file ./extensions.ael, line 62-69! +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 71-175: The macro std-priv-exten does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:2280 func: check_switch_expr Warning: file ./extensions.ael, line 245-246: A default case was automatically added to the switch. +LOG: lev:3 file:pval.c line:2422 func: check_pval_item Warning: file ./extensions.ael, line 312-312: macro call to non-existent funcA! (Not even in the extensions.conf stuff!) +LOG: lev:3 file:pval.c line:2422 func: check_pval_item Warning: file ./extensions.ael, line 313-313: macro call to non-existent funcD! (Not even in the extensions.conf stuff!) +LOG: lev:3 file:pval.c line:1342 func: check_goto Warning: file ./extensions.ael, line 319-319: goto: Couldn't find goto target test5|s|1, not even in extensions.conf! +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 363-366: The macro dialoutpstn does not end with a return; I will insert one. +LOG: lev:2 file:pbx_ael.c line:1698 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:1700 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:1703 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:1706 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. +LOG: lev:4 file:ael2_parse line:521 func: main 13 contexts, 51 extensions, 182 priorities diff --git a/pbx/ael/ael-test/ref.ael-test2 b/pbx/ael/ael-test/ref.ael-test2 index 161117904c09a73506b9972c1a0ff378d3758518..aea70b561ff3e1d7bf44dc0698487d5b4c8d2fa3 100644 --- a/pbx/ael/ael-test/ref.ael-test2 +++ b/pbx/ael/ael-test/ref.ael-test2 @@ -2,28 +2,28 @@ (If you find progress and other non-error messages irritating, you can use -q to suppress them) (You can use the -w option to dump extensions.conf format to extensions.conf.aeldump) -LOG: lev:2 file:pbx_ael.c line:4092 func: pbx_load_module Starting AEL load process. -LOG: lev:2 file:pbx_ael.c line:4099 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:1680 func: pbx_load_module Starting AEL load process. +LOG: lev:2 file:pbx_ael.c line:1687 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. LOG: lev:2 file:ael.flex line:430 func: ael_yylex --Read in included file ./apptest.ael2, 3474 chars LOG: lev:3 file:ael.y line:538 func: ael_yyparse ==== File: ./apptest.ael2, Line 46, Cols: 8-11: Suggestion: Use the goto statement instead of the Goto() application call in AEL. -LOG: lev:2 file:pbx_ael.c line:4107 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. -LOG: lev:3 file:pbx_ael.c line:2530 func: check_pval_item Warning: file ./apptest.ael2, line 35-35: application call to EndWhile affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! -LOG: lev:3 file:pbx_ael.c line:2530 func: check_pval_item Warning: file ./apptest.ael2, line 37-37: application call to ExecIf affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! -LOG: lev:3 file:pbx_ael.c line:2530 func: check_pval_item Warning: file ./apptest.ael2, line 38-38: application call to ExecIfTime affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! -LOG: lev:3 file:pbx_ael.c line:2530 func: check_pval_item Warning: file ./apptest.ael2, line 44-44: application call to Gosub affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! -LOG: lev:3 file:pbx_ael.c line:2530 func: check_pval_item Warning: file ./apptest.ael2, line 45-45: application call to GosubIf affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! -LOG: lev:3 file:pbx_ael.c line:1398 func: check_goto Warning: file ./apptest.ael2, line 46-46: goto: no context cont could be found that matches the goto target! -LOG: lev:3 file:pbx_ael.c line:2530 func: check_pval_item Warning: file ./apptest.ael2, line 47-47: application call to GotoIf affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! -LOG: lev:3 file:pbx_ael.c line:2530 func: check_pval_item Warning: file ./apptest.ael2, line 48-48: application call to GotoIfTime affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! -LOG: lev:3 file:pbx_ael.c line:2530 func: check_pval_item Warning: file ./apptest.ael2, line 58-58: application call to Macro affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! -LOG: lev:3 file:pbx_ael.c line:2535 func: check_pval_item Warning: file ./apptest.ael2, line 59-59: I am converting the MacroExit call here to a return statement. -LOG: lev:3 file:pbx_ael.c line:2530 func: check_pval_item Warning: file ./apptest.ael2, line 60-60: application call to MacroIf affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! -LOG: lev:3 file:pbx_ael.c line:2530 func: check_pval_item Warning: file ./apptest.ael2, line 85-85: application call to Random affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! -LOG: lev:3 file:pbx_ael.c line:2530 func: check_pval_item Warning: file ./apptest.ael2, line 94-94: application call to Return affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! -LOG: lev:3 file:pbx_ael.c line:2530 func: check_pval_item Warning: file ./apptest.ael2, line 119-119: application call to StackPop affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! -LOG: lev:3 file:pbx_ael.c line:2530 func: check_pval_item Warning: file ./apptest.ael2, line 141-141: application call to While affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! -LOG: lev:2 file:pbx_ael.c line:4110 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. -LOG: lev:2 file:pbx_ael.c line:4112 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. -LOG: lev:2 file:pbx_ael.c line:4115 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. -LOG: lev:2 file:pbx_ael.c line:4118 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. -LOG: lev:4 file:ael2_parse line:522 func: main 1 contexts, 1 extensions, 142 priorities +LOG: lev:2 file:pbx_ael.c line:1695 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. +LOG: lev:3 file:pval.c line:2503 func: check_pval_item Warning: file ./apptest.ael2, line 35-35: application call to EndWhile affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2503 func: check_pval_item Warning: file ./apptest.ael2, line 37-37: application call to ExecIf affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2503 func: check_pval_item Warning: file ./apptest.ael2, line 38-38: application call to ExecIfTime affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2503 func: check_pval_item Warning: file ./apptest.ael2, line 44-44: application call to Gosub affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2503 func: check_pval_item Warning: file ./apptest.ael2, line 45-45: application call to GosubIf affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:1342 func: check_goto Warning: file ./apptest.ael2, line 46-46: goto: Couldn't find goto target cont|exten|prior, not even in extensions.conf! +LOG: lev:3 file:pval.c line:2503 func: check_pval_item Warning: file ./apptest.ael2, line 47-47: application call to GotoIf affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2503 func: check_pval_item Warning: file ./apptest.ael2, line 48-48: application call to GotoIfTime affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2503 func: check_pval_item Warning: file ./apptest.ael2, line 58-58: application call to Macro affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2508 func: check_pval_item Warning: file ./apptest.ael2, line 59-59: I am converting the MacroExit call here to a return statement. +LOG: lev:3 file:pval.c line:2503 func: check_pval_item Warning: file ./apptest.ael2, line 60-60: application call to MacroIf affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2503 func: check_pval_item Warning: file ./apptest.ael2, line 85-85: application call to Random affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2503 func: check_pval_item Warning: file ./apptest.ael2, line 94-94: application call to Return affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2503 func: check_pval_item Warning: file ./apptest.ael2, line 119-119: application call to StackPop affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2503 func: check_pval_item Warning: file ./apptest.ael2, line 141-141: application call to While affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:2 file:pbx_ael.c line:1698 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:1700 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:1703 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:1706 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. +LOG: lev:4 file:ael2_parse line:521 func: main 1 contexts, 1 extensions, 142 priorities diff --git a/pbx/ael/ael-test/ref.ael-test3 b/pbx/ael/ael-test/ref.ael-test3 index ede90246b5ee7a2ef8a9e5833b28576f807fdae9..cbd6392e88666be8067aafeadf1e53a93705eaab 100644 --- a/pbx/ael/ael-test/ref.ael-test3 +++ b/pbx/ael/ael-test/ref.ael-test3 @@ -2,99 +2,99 @@ (If you find progress and other non-error messages irritating, you can use -q to suppress them) (You can use the -w option to dump extensions.conf format to extensions.conf.aeldump) -LOG: lev:2 file:pbx_ael.c line:4092 func: pbx_load_module Starting AEL load process. -LOG: lev:2 file:pbx_ael.c line:4099 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:1680 func: pbx_load_module Starting AEL load process. +LOG: lev:2 file:pbx_ael.c line:1687 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. LOG: lev:2 file:ael.flex line:430 func: ael_yylex --Read in included file ./include1.ael2, 78 chars LOG: lev:2 file:ael.flex line:430 func: ael_yylex --Read in included file ./include2.ael2, 98 chars LOG: lev:2 file:ael.flex line:430 func: ael_yylex --Read in included file ./include3.ael2, 57 chars LOG: lev:2 file:ael.flex line:430 func: ael_yylex --Read in included file ./include5.ael2, 56 chars LOG: lev:2 file:ael.flex line:430 func: ael_yylex --Read in included file ./include4.ael2, 87 chars LOG: lev:2 file:ael.flex line:430 func: ael_yylex --Read in included file ./telemarket_torture.ael2, 28036 chars -LOG: lev:2 file:pbx_ael.c line:4107 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 14-34: The macro std-exten does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:1408 func: check_goto Warning: file ./extensions.ael, line 17-17: It's bad form to have a goto in a macro to a target outside the macro! -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 36-59: The macro std-priv-exten_1 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 62-85: The macro std-priv-exten_2 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 88-111: The macro std-priv-exten_3 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 114-137: The macro std-priv-exten_4 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 140-163: The macro std-priv-exten_5 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 166-189: The macro std-priv-exten_6 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 192-215: The macro std-priv-exten_7 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 218-241: The macro std-priv-exten_8 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 244-267: The macro std-priv-exten_9 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 270-293: The macro std-priv-exten_10 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 296-319: The macro std-priv-exten_11 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 322-345: The macro std-priv-exten_12 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 348-371: The macro std-priv-exten_13 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 374-397: The macro std-priv-exten_14 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 400-423: The macro std-priv-exten_15 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 426-449: The macro std-priv-exten_16 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 452-475: The macro std-priv-exten_17 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 478-501: The macro std-priv-exten_18 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 504-527: The macro std-priv-exten_19 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 530-553: The macro std-priv-exten_20 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 556-579: The macro std-priv-exten_21 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 582-605: The macro std-priv-exten_22 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 608-631: The macro std-priv-exten_23 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 634-657: The macro std-priv-exten_24 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 660-683: The macro std-priv-exten_25 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 686-709: The macro std-priv-exten_26 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 712-735: The macro std-priv-exten_27 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 738-761: The macro std-priv-exten_28 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 764-787: The macro std-priv-exten_29 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 790-813: The macro std-priv-exten_30 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 816-839: The macro std-priv-exten_31 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 842-865: The macro std-priv-exten_32 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 868-891: The macro std-priv-exten_33 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 894-917: The macro std-priv-exten_34 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 920-943: The macro std-priv-exten_35 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 946-969: The macro std-priv-exten_36 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 972-995: The macro std-priv-exten_37 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 998-1021: The macro std-priv-exten_38 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 1024-1047: The macro std-priv-exten_39 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 1050-1073: The macro std-priv-exten_40 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 1076-1099: The macro std-priv-exten_41 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 1102-1125: The macro std-priv-exten_42 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 1128-1151: The macro std-priv-exten_43 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 1154-1177: The macro std-priv-exten_44 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 1180-1203: The macro std-priv-exten_45 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 1206-1229: The macro std-priv-exten_46 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 1232-1255: The macro std-priv-exten_47 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 1258-1281: The macro std-priv-exten_48 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 1284-1307: The macro std-priv-exten_49 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 1310-1333: The macro std-priv-exten_50 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 1336-1359: The macro std-priv-exten_51 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 1362-1385: The macro std-priv-exten_52 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 1388-1411: The macro std-priv-exten_53 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 1414-1437: The macro std-priv-exten_54 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 1440-1463: The macro std-priv-exten_55 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 1466-1489: The macro std-priv-exten_56 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 1492-1515: The macro std-priv-exten_57 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 1518-1541: The macro std-priv-exten_58 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 1544-1567: The macro std-priv-exten_59 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 1570-1593: The macro std-priv-exten_60 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 1596-1619: The macro std-priv-exten_61 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 1622-1645: The macro std-priv-exten_62 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 1648-1671: The macro std-priv-exten_63 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 1674-1697: The macro std-priv-exten_64 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 1700-1723: The macro std-priv-exten_65 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 1726-1749: The macro std-priv-exten_66 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 1752-1775: The macro std-priv-exten_67 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 1778-1801: The macro std-priv-exten_68 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 1804-1827: The macro std-priv-exten_69 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 1830-1853: The macro std-priv-exten_70 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 1856-1879: The macro std-priv-exten_71 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 1882-1905: The macro std-priv-exten_72 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 1908-1931: The macro std-priv-exten_73 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 1934-1957: The macro std-priv-exten does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 1959-1995: The macro fillcidname does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 1997-2015: The macro ciddial does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 2017-2028: The macro ciddial3 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 2030-2048: The macro ciddial2 does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 2050-2065: The macro callerid-liar does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 2067-2072: The macro callerid-bad does not end with a return; I will insert one. -LOG: lev:2 file:pbx_ael.c line:4110 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. -LOG: lev:2 file:pbx_ael.c line:4112 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. -LOG: lev:2 file:pbx_ael.c line:4115 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. -LOG: lev:2 file:pbx_ael.c line:4118 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. -LOG: lev:4 file:ael2_parse line:522 func: main 172 contexts, 858 extensions, 2406 priorities +LOG: lev:2 file:pbx_ael.c line:1695 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 14-34: The macro std-exten does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:1358 func: check_goto Warning: file ./extensions.ael, line 17-17: It's bad form to have a goto in a macro to a target outside the macro! +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 36-59: The macro std-priv-exten_1 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 62-85: The macro std-priv-exten_2 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 88-111: The macro std-priv-exten_3 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 114-137: The macro std-priv-exten_4 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 140-163: The macro std-priv-exten_5 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 166-189: The macro std-priv-exten_6 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 192-215: The macro std-priv-exten_7 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 218-241: The macro std-priv-exten_8 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 244-267: The macro std-priv-exten_9 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 270-293: The macro std-priv-exten_10 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 296-319: The macro std-priv-exten_11 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 322-345: The macro std-priv-exten_12 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 348-371: The macro std-priv-exten_13 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 374-397: The macro std-priv-exten_14 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 400-423: The macro std-priv-exten_15 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 426-449: The macro std-priv-exten_16 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 452-475: The macro std-priv-exten_17 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 478-501: The macro std-priv-exten_18 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 504-527: The macro std-priv-exten_19 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 530-553: The macro std-priv-exten_20 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 556-579: The macro std-priv-exten_21 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 582-605: The macro std-priv-exten_22 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 608-631: The macro std-priv-exten_23 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 634-657: The macro std-priv-exten_24 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 660-683: The macro std-priv-exten_25 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 686-709: The macro std-priv-exten_26 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 712-735: The macro std-priv-exten_27 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 738-761: The macro std-priv-exten_28 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 764-787: The macro std-priv-exten_29 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 790-813: The macro std-priv-exten_30 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 816-839: The macro std-priv-exten_31 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 842-865: The macro std-priv-exten_32 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 868-891: The macro std-priv-exten_33 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 894-917: The macro std-priv-exten_34 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 920-943: The macro std-priv-exten_35 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 946-969: The macro std-priv-exten_36 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 972-995: The macro std-priv-exten_37 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 998-1021: The macro std-priv-exten_38 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 1024-1047: The macro std-priv-exten_39 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 1050-1073: The macro std-priv-exten_40 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 1076-1099: The macro std-priv-exten_41 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 1102-1125: The macro std-priv-exten_42 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 1128-1151: The macro std-priv-exten_43 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 1154-1177: The macro std-priv-exten_44 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 1180-1203: The macro std-priv-exten_45 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 1206-1229: The macro std-priv-exten_46 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 1232-1255: The macro std-priv-exten_47 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 1258-1281: The macro std-priv-exten_48 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 1284-1307: The macro std-priv-exten_49 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 1310-1333: The macro std-priv-exten_50 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 1336-1359: The macro std-priv-exten_51 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 1362-1385: The macro std-priv-exten_52 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 1388-1411: The macro std-priv-exten_53 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 1414-1437: The macro std-priv-exten_54 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 1440-1463: The macro std-priv-exten_55 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 1466-1489: The macro std-priv-exten_56 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 1492-1515: The macro std-priv-exten_57 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 1518-1541: The macro std-priv-exten_58 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 1544-1567: The macro std-priv-exten_59 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 1570-1593: The macro std-priv-exten_60 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 1596-1619: The macro std-priv-exten_61 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 1622-1645: The macro std-priv-exten_62 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 1648-1671: The macro std-priv-exten_63 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 1674-1697: The macro std-priv-exten_64 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 1700-1723: The macro std-priv-exten_65 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 1726-1749: The macro std-priv-exten_66 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 1752-1775: The macro std-priv-exten_67 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 1778-1801: The macro std-priv-exten_68 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 1804-1827: The macro std-priv-exten_69 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 1830-1853: The macro std-priv-exten_70 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 1856-1879: The macro std-priv-exten_71 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 1882-1905: The macro std-priv-exten_72 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 1908-1931: The macro std-priv-exten_73 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 1934-1957: The macro std-priv-exten does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 1959-1995: The macro fillcidname does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 1997-2015: The macro ciddial does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 2017-2028: The macro ciddial3 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 2030-2048: The macro ciddial2 does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 2050-2065: The macro callerid-liar does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 2067-2072: The macro callerid-bad does not end with a return; I will insert one. +LOG: lev:2 file:pbx_ael.c line:1698 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:1700 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:1703 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:1706 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. +LOG: lev:4 file:ael2_parse line:521 func: main 172 contexts, 858 extensions, 2406 priorities diff --git a/pbx/ael/ael-test/ref.ael-test4 b/pbx/ael/ael-test/ref.ael-test4 index 161117904c09a73506b9972c1a0ff378d3758518..aea70b561ff3e1d7bf44dc0698487d5b4c8d2fa3 100644 --- a/pbx/ael/ael-test/ref.ael-test4 +++ b/pbx/ael/ael-test/ref.ael-test4 @@ -2,28 +2,28 @@ (If you find progress and other non-error messages irritating, you can use -q to suppress them) (You can use the -w option to dump extensions.conf format to extensions.conf.aeldump) -LOG: lev:2 file:pbx_ael.c line:4092 func: pbx_load_module Starting AEL load process. -LOG: lev:2 file:pbx_ael.c line:4099 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:1680 func: pbx_load_module Starting AEL load process. +LOG: lev:2 file:pbx_ael.c line:1687 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. LOG: lev:2 file:ael.flex line:430 func: ael_yylex --Read in included file ./apptest.ael2, 3474 chars LOG: lev:3 file:ael.y line:538 func: ael_yyparse ==== File: ./apptest.ael2, Line 46, Cols: 8-11: Suggestion: Use the goto statement instead of the Goto() application call in AEL. -LOG: lev:2 file:pbx_ael.c line:4107 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. -LOG: lev:3 file:pbx_ael.c line:2530 func: check_pval_item Warning: file ./apptest.ael2, line 35-35: application call to EndWhile affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! -LOG: lev:3 file:pbx_ael.c line:2530 func: check_pval_item Warning: file ./apptest.ael2, line 37-37: application call to ExecIf affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! -LOG: lev:3 file:pbx_ael.c line:2530 func: check_pval_item Warning: file ./apptest.ael2, line 38-38: application call to ExecIfTime affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! -LOG: lev:3 file:pbx_ael.c line:2530 func: check_pval_item Warning: file ./apptest.ael2, line 44-44: application call to Gosub affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! -LOG: lev:3 file:pbx_ael.c line:2530 func: check_pval_item Warning: file ./apptest.ael2, line 45-45: application call to GosubIf affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! -LOG: lev:3 file:pbx_ael.c line:1398 func: check_goto Warning: file ./apptest.ael2, line 46-46: goto: no context cont could be found that matches the goto target! -LOG: lev:3 file:pbx_ael.c line:2530 func: check_pval_item Warning: file ./apptest.ael2, line 47-47: application call to GotoIf affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! -LOG: lev:3 file:pbx_ael.c line:2530 func: check_pval_item Warning: file ./apptest.ael2, line 48-48: application call to GotoIfTime affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! -LOG: lev:3 file:pbx_ael.c line:2530 func: check_pval_item Warning: file ./apptest.ael2, line 58-58: application call to Macro affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! -LOG: lev:3 file:pbx_ael.c line:2535 func: check_pval_item Warning: file ./apptest.ael2, line 59-59: I am converting the MacroExit call here to a return statement. -LOG: lev:3 file:pbx_ael.c line:2530 func: check_pval_item Warning: file ./apptest.ael2, line 60-60: application call to MacroIf affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! -LOG: lev:3 file:pbx_ael.c line:2530 func: check_pval_item Warning: file ./apptest.ael2, line 85-85: application call to Random affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! -LOG: lev:3 file:pbx_ael.c line:2530 func: check_pval_item Warning: file ./apptest.ael2, line 94-94: application call to Return affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! -LOG: lev:3 file:pbx_ael.c line:2530 func: check_pval_item Warning: file ./apptest.ael2, line 119-119: application call to StackPop affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! -LOG: lev:3 file:pbx_ael.c line:2530 func: check_pval_item Warning: file ./apptest.ael2, line 141-141: application call to While affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! -LOG: lev:2 file:pbx_ael.c line:4110 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. -LOG: lev:2 file:pbx_ael.c line:4112 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. -LOG: lev:2 file:pbx_ael.c line:4115 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. -LOG: lev:2 file:pbx_ael.c line:4118 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. -LOG: lev:4 file:ael2_parse line:522 func: main 1 contexts, 1 extensions, 142 priorities +LOG: lev:2 file:pbx_ael.c line:1695 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. +LOG: lev:3 file:pval.c line:2503 func: check_pval_item Warning: file ./apptest.ael2, line 35-35: application call to EndWhile affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2503 func: check_pval_item Warning: file ./apptest.ael2, line 37-37: application call to ExecIf affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2503 func: check_pval_item Warning: file ./apptest.ael2, line 38-38: application call to ExecIfTime affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2503 func: check_pval_item Warning: file ./apptest.ael2, line 44-44: application call to Gosub affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2503 func: check_pval_item Warning: file ./apptest.ael2, line 45-45: application call to GosubIf affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:1342 func: check_goto Warning: file ./apptest.ael2, line 46-46: goto: Couldn't find goto target cont|exten|prior, not even in extensions.conf! +LOG: lev:3 file:pval.c line:2503 func: check_pval_item Warning: file ./apptest.ael2, line 47-47: application call to GotoIf affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2503 func: check_pval_item Warning: file ./apptest.ael2, line 48-48: application call to GotoIfTime affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2503 func: check_pval_item Warning: file ./apptest.ael2, line 58-58: application call to Macro affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2508 func: check_pval_item Warning: file ./apptest.ael2, line 59-59: I am converting the MacroExit call here to a return statement. +LOG: lev:3 file:pval.c line:2503 func: check_pval_item Warning: file ./apptest.ael2, line 60-60: application call to MacroIf affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2503 func: check_pval_item Warning: file ./apptest.ael2, line 85-85: application call to Random affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2503 func: check_pval_item Warning: file ./apptest.ael2, line 94-94: application call to Return affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2503 func: check_pval_item Warning: file ./apptest.ael2, line 119-119: application call to StackPop affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:3 file:pval.c line:2503 func: check_pval_item Warning: file ./apptest.ael2, line 141-141: application call to While affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead! +LOG: lev:2 file:pbx_ael.c line:1698 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:1700 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:1703 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:1706 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. +LOG: lev:4 file:ael2_parse line:521 func: main 1 contexts, 1 extensions, 142 priorities diff --git a/pbx/ael/ael-test/ref.ael-test5 b/pbx/ael/ael-test/ref.ael-test5 index 4cc110631d497bb8db62cd7bb09345fac2474163..d9c4130c44ad57d3fbf0dce432955e8e6839251e 100644 --- a/pbx/ael/ael-test/ref.ael-test5 +++ b/pbx/ael/ael-test/ref.ael-test5 @@ -2,14 +2,14 @@ (If you find progress and other non-error messages irritating, you can use -q to suppress them) (You can use the -w option to dump extensions.conf format to extensions.conf.aeldump) -LOG: lev:2 file:pbx_ael.c line:3910 func: pbx_load_module Starting AEL load process. -LOG: lev:2 file:pbx_ael.c line:3917 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. -LOG: lev:2 file:pbx_ael.c line:3925 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. -LOG: lev:3 file:pbx_ael.c line:712 func: check_macro_returns Warning: file ./extensions.ael, line 130-183: The macro stdexten does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:712 func: check_macro_returns Warning: file ./extensions.ael, line 185-192: The macro uvm does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:712 func: check_macro_returns Warning: file ./extensions.ael, line 194-201: The macro bvm does not end with a return; I will insert one. -LOG: lev:2 file:pbx_ael.c line:3928 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. -LOG: lev:2 file:pbx_ael.c line:3930 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. -LOG: lev:2 file:pbx_ael.c line:3933 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. -LOG: lev:2 file:pbx_ael.c line:3936 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. -LOG: lev:4 file:ael2_parse line:479 func: main 38 contexts, 90 extensions, 492 priorities +LOG: lev:2 file:pbx_ael.c line:113 func: pbx_load_module Starting AEL load process. +LOG: lev:2 file:pbx_ael.c line:120 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:128 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. +LOG: lev:3 file:pval.c line:678 func: check_macro_returns Warning: file ./extensions.ael, line 130-183: The macro stdexten does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:678 func: check_macro_returns Warning: file ./extensions.ael, line 185-192: The macro uvm does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:678 func: check_macro_returns Warning: file ./extensions.ael, line 194-201: The macro bvm does not end with a return; I will insert one. +LOG: lev:2 file:pbx_ael.c line:131 func: pbx_load_module AEL load process: checked config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:133 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:136 func: pbx_load_module AEL load process: merged config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:139 func: pbx_load_module AEL load process: verified config file name './extensions.ael'. +LOG: lev:4 file:ael2_parse line:499 func: main 38 contexts, 90 extensions, 492 priorities diff --git a/pbx/ael/ael-test/ref.ael-test6 b/pbx/ael/ael-test/ref.ael-test6 index ec5fc131827b109c8a1479481e0b3dd0861cbebc..37b4ef93948281d62c033e11921cd15664ce57c5 100644 --- a/pbx/ael/ael-test/ref.ael-test6 +++ b/pbx/ael/ael-test/ref.ael-test6 @@ -2,8 +2,8 @@ (If you find progress and other non-error messages irritating, you can use -q to suppress them) (You can use the -w option to dump extensions.conf format to extensions.conf.aeldump) -LOG: lev:2 file:pbx_ael.c line:4092 func: pbx_load_module Starting AEL load process. -LOG: lev:2 file:pbx_ael.c line:4099 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:1680 func: pbx_load_module Starting AEL load process. +LOG: lev:2 file:pbx_ael.c line:1687 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. LOG: lev:4 file:ael.flex line:277 func: ael_yylex File=./extensions.ael, line=165, column=49: Mismatched '}' in expression! LOG: lev:4 file:ael.y line:764 func: ael_yyerror ==== File: ./extensions.ael, Line 165, Cols: 51-51: Error: syntax error, unexpected '=', expecting ')' LOG: lev:4 file:ael.y line:764 func: ael_yyerror ==== File: ./extensions.ael, Line 169, Cols: 24-24: Error: syntax error, unexpected '&' @@ -11,14 +11,14 @@ LOG: lev:4 file:ael.flex line:277 func: ael_yylex File=./extensions.ael, line= LOG: lev:4 file:ael.y line:764 func: ael_yyerror ==== File: ./extensions.ael, Line 222, Cols: 43-43: Error: syntax error, unexpected '=', expecting ')' LOG: lev:4 file:ael.y line:764 func: ael_yyerror ==== File: ./extensions.ael, Line 226, Cols: 16-16: Error: syntax error, unexpected '&' LOG: lev:4 file:ael.y line:764 func: ael_yyerror ==== File: ./extensions.ael, Line 291, Cols: 21-28: Error: syntax error, unexpected word, expecting '(' or ';' or '=' or ':' -LOG: lev:2 file:pbx_ael.c line:4107 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 116-125: The macro dialout does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 129-168: The macro stdexten does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 184-191: The macro uvm does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 193-200: The macro bvm does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 202-207: The macro checkdnd does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 209-216: The macro checkcf does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 218-225: The macro checkcfb does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:739 func: check_macro_returns Warning: file ./extensions.ael, line 688-694: The macro check-psd-exists does not end with a return; I will insert one. -LOG: lev:4 file:pbx_ael.c line:4120 func: pbx_load_module Sorry, but 5 syntax errors and 0 semantic errors were detected. It doesn't make sense to compile. -LOG: lev:4 file:ael2_parse line:522 func: main 0 contexts, 0 extensions, 0 priorities +LOG: lev:2 file:pbx_ael.c line:1695 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 116-125: The macro dialout does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 129-168: The macro stdexten does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 184-191: The macro uvm does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 193-200: The macro bvm does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 202-207: The macro checkdnd does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 209-216: The macro checkcf does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 218-225: The macro checkcfb does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 688-694: The macro check-psd-exists does not end with a return; I will insert one. +LOG: lev:4 file:pbx_ael.c line:1708 func: pbx_load_module Sorry, but 5 syntax errors and 0 semantic errors were detected. It doesn't make sense to compile. +LOG: lev:4 file:ael2_parse line:521 func: main 0 contexts, 0 extensions, 0 priorities diff --git a/pbx/ael/ael-test/ref.ael-test7 b/pbx/ael/ael-test/ref.ael-test7 index ae015b9dfd16b7e67af961550c810a4bab84a309..57707a0c1bea46c71d714eb5b338ec7e8a1f7066 100644 --- a/pbx/ael/ael-test/ref.ael-test7 +++ b/pbx/ael/ael-test/ref.ael-test7 @@ -2,19 +2,28 @@ (If you find progress and other non-error messages irritating, you can use -q to suppress them) (You can use the -w option to dump extensions.conf format to extensions.conf.aeldump) -LOG: lev:2 file:pbx_ael.c line:4026 func: pbx_load_module Starting AEL load process. -LOG: lev:2 file:pbx_ael.c line:4033 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. -LOG: lev:2 file:pbx_ael.c line:4041 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. -LOG: lev:3 file:pbx_ael.c line:734 func: check_macro_returns Warning: file ./extensions.ael, line 22-42: The macro stdexten does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:734 func: check_macro_returns Warning: file ./extensions.ael, line 44-49: The macro announce_minutes does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:734 func: check_macro_returns Warning: file ./extensions.ael, line 59-89: The macro checkanddial does not end with a return; I will insert one. -LOG: lev:3 file:pbx_ael.c line:734 func: check_macro_returns Warning: file ./extensions.ael, line 91-100: The macro trunkdial does not end with a return; I will insert one. -LOG: lev:4 file:pbx_ael.c line:2459 func: check_pval_item Error: file ./extensions.ael, line 98-98: The macro call to checkanddial has 5 arguments, but the macro definition has 7 arguments -LOG: lev:3 file:pbx_ael.c line:734 func: check_macro_returns Warning: file ./extensions.ael, line 102-112: The macro checklocal does not end with a return; I will insert one. -LOG: lev:4 file:pbx_ael.c line:2459 func: check_pval_item Error: file ./extensions.ael, line 107-107: The macro call to checkanddial has 5 arguments, but the macro definition has 7 arguments -LOG: lev:3 file:pbx_ael.c line:734 func: check_macro_returns Warning: file ./extensions.ael, line 114-119: The macro autodial does not end with a return; I will insert one. -LOG: lev:4 file:pbx_ael.c line:2459 func: check_pval_item Error: file ./extensions.ael, line 284-284: The macro call to checkanddial has 5 arguments, but the macro definition has 7 arguments -LOG: lev:4 file:pbx_ael.c line:2459 func: check_pval_item Error: file ./extensions.ael, line 287-287: The macro call to checkanddial has 5 arguments, but the macro definition has 7 arguments -LOG: lev:3 file:pbx_ael.c line:2440 func: check_pval_item Warning: file ./extensions.ael, line 452-452: macro call to non-existent std-exten-ael ! Hopefully it is present in extensions.conf! -LOG: lev:4 file:pbx_ael.c line:4054 func: pbx_load_module Sorry, but 0 syntax errors and 4 semantic errors were detected. It doesn't make sense to compile. -LOG: lev:4 file:ael2_parse line:512 func: main 0 contexts, 0 extensions, 0 priorities +LOG: lev:2 file:pbx_ael.c line:1680 func: pbx_load_module Starting AEL load process. +LOG: lev:2 file:pbx_ael.c line:1687 func: pbx_load_module AEL load process: calculated config file name './extensions.ael'. +LOG: lev:2 file:pbx_ael.c line:1695 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 22-42: The macro stdexten does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 44-49: The macro announce_minutes does not end with a return; I will insert one. +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 59-89: The macro checkanddial does not end with a return; I will insert one. +LOG: lev:3 file:extconf.c line:3959 func: add_pri Unable to register extension '_', priority 1 in 'test6', already in use +LOG: lev:3 file:extconf.c line:3959 func: add_pri Unable to register extension '_X.', priority 1 in 'test6', already in use +=== Loading extensions.conf === +Context: test6 +Context: test5 +Context: macro-funcA +Context: macro-funcB +Context: macro-funcC +========= +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 91-100: The macro trunkdial does not end with a return; I will insert one. +LOG: lev:4 file:pval.c line:2464 func: check_pval_item Error: file ./extensions.ael, line 98-98: The macro call to checkanddial has 5 arguments, but the macro definition has 7 arguments +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 102-112: The macro checklocal does not end with a return; I will insert one. +LOG: lev:4 file:pval.c line:2464 func: check_pval_item Error: file ./extensions.ael, line 107-107: The macro call to checkanddial has 5 arguments, but the macro definition has 7 arguments +LOG: lev:3 file:pval.c line:668 func: check_macro_returns Warning: file ./extensions.ael, line 114-119: The macro autodial does not end with a return; I will insert one. +LOG: lev:4 file:pval.c line:2464 func: check_pval_item Error: file ./extensions.ael, line 284-284: The macro call to checkanddial has 5 arguments, but the macro definition has 7 arguments +LOG: lev:4 file:pval.c line:2464 func: check_pval_item Error: file ./extensions.ael, line 287-287: The macro call to checkanddial has 5 arguments, but the macro definition has 7 arguments +LOG: lev:3 file:pval.c line:2422 func: check_pval_item Warning: file ./extensions.ael, line 452-452: macro call to non-existent std-exten-ael! (Not even in the extensions.conf stuff!) +LOG: lev:4 file:pbx_ael.c line:1708 func: pbx_load_module Sorry, but 0 syntax errors and 4 semantic errors were detected. It doesn't make sense to compile. +LOG: lev:4 file:ael2_parse line:521 func: main 0 contexts, 0 extensions, 0 priorities diff --git a/pbx/pbx_ael.c b/pbx/pbx_ael.c index e9dde83ade63e020c0d832bc34421c6357c8151e..39fe8ff2004f014b6bc602d634432ccf6d0899c8 100644 --- a/pbx/pbx_ael.c +++ b/pbx/pbx_ael.c @@ -22,6 +22,10 @@ * */ +/*** MODULEINFO + <depend>res_ael_share</depend> + ***/ + #include "asterisk.h" ASTERISK_FILE_VERSION(__FILE__, "$Revision$") @@ -44,12 +48,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/app.h" #include "asterisk/callerid.h" #include "asterisk/ael_structs.h" +#include "asterisk/pval.h" #ifdef AAL_ARGCHECK #include "asterisk/argdesc.h" #endif -static char expr_output[2096]; - /* these functions are in ../ast_expr2.fl */ #define DEBUG_READ (1 << 0) @@ -60,9 +63,8 @@ static char expr_output[2096]; static char *config = "extensions.ael"; static char *registrar = "pbx_ael"; static int pbx_load_module(void); - -static int errs, warns; -static int notes; +static int warns, errs; +static struct pval *current_db; #ifndef AAL_ARGCHECK /* for the time being, short circuit all the AAL related structures @@ -70,12 +72,6 @@ static int notes; development, this code can be properly re-instated */ -/*! \brief null definitions for structs passed down the infrastructure */ -struct argapp -{ - struct argapp *next; -}; - #endif #ifdef AAL_ARGCHECK @@ -90,7 +86,6 @@ 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); @@ -99,8 +94,6 @@ 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); @@ -109,32 +102,16 @@ void destroy_pval_item(pval *item); int is_float(char *arg ); int is_int(char *arg ); int is_empty(char *arg); -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); @@ -144,8 +121,6 @@ static struct pval *find_label_in_current_extension(const char *label, pval *cur 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); @@ -456,22 +431,246 @@ static void ael2_print(char *fname, pval *tree) #endif -/* EMPTY TEMPLATE FUNCS FOR AEL TRAVERSAL: ============================================================================= */ -void traverse_pval_template(pval *item, int depth); -void traverse_pval_item_template(pval *item, int depth); +/* SEMANTIC CHECKING FOR AEL: ============================================================================= */ + +/* (not all that is syntactically legal is good! */ -void traverse_pval_item_template(pval *item, int depth)/* depth comes in handy for a pretty print (indentation), - but you may not need it */ +static struct pval *in_macro(pval *item) { - pval *lp; + struct pval *curr; + curr = item; + while( curr ) { + if( curr->type == PV_MACRO ) { + return curr; + } + curr = curr->dad; + } + return 0; +} + +static struct pval *in_context(pval *item) +{ + struct pval *curr; + curr = item; + while( curr ) { + if( curr->type == PV_MACRO || curr->type == PV_CONTEXT ) { + return curr; + } + curr = curr->dad; + } + return 0; +} + + +static pval *get_goto_target(pval *item) +{ + /* just one item-- the label should be in the current extension */ + pval *curr_ext = get_extension_or_contxt(item); /* containing exten, or macro */ + pval *curr_cont; - switch ( item->type ) { - case PV_WORD: - /* fields: item->u1.str == string associated with this (word). */ - break; + if (item->u1.list && !item->u1.list->next && !strstr((item->u1.list)->u1.str,"${")) { + struct pval *x = find_label_in_current_extension((char*)((item->u1.list)->u1.str), curr_ext); + return x; + } + + curr_cont = get_contxt(item); + + /* TWO items */ + if (item->u1.list->next && !item->u1.list->next->next) { + if (!strstr((item->u1.list)->u1.str,"${") + && !strstr(item->u1.list->next->u1.str,"${") ) /* Don't try to match variables */ { + struct pval *x = find_label_in_current_context((char *)item->u1.list->u1.str, (char *)item->u1.list->next->u1.str, curr_cont); + return x; + } + } + + /* All 3 items! */ + if (item->u1.list->next && item->u1.list->next->next) { + /* all three */ + pval *first = item->u1.list; + pval *second = item->u1.list->next; + pval *third = item->u1.list->next->next; + + if (!strstr((item->u1.list)->u1.str,"${") + && !strstr(item->u1.list->next->u1.str,"${") + && !strstr(item->u1.list->next->next->u1.str,"${")) /* Don't try to match variables */ { + struct pval *x = find_label_in_current_db((char*)first->u1.str, (char*)second->u1.str, (char*)third->u1.str); + if (!x) { + + struct pval *p3; + struct pval *that_context = find_context(item->u1.list->u1.str); + + /* the target of the goto could be in an included context!! Fancy that!! */ + /* look for includes in the current context */ + if (that_context) { + for (p3=that_context->u2.statements; p3; p3=p3->next) { + if (p3->type == PV_INCLUDES) { + struct pval *p4; + for (p4=p3->u1.list; p4; p4=p4->next) { + /* for each context pointed to, find it, then find a context/label that matches the + target here! */ + char *incl_context = p4->u1.str; + /* find a matching context name */ + struct pval *that_other_context = find_context(incl_context); + if (that_other_context) { + struct pval *x3; + x3 = find_label_in_current_context((char *)item->u1.list->next->u1.str, (char *)item->u1.list->next->next->u1.str, that_other_context); + if (x3) { + return x3; + } + } + } + } + } + } + } + return x; + } + } + return 0; +} + +static void check_goto(pval *item) +{ + /* check for the target of the goto-- does it exist? */ + if ( !(item->u1.list)->next && !(item->u1.list)->u1.str ) { + ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto: empty label reference found!\n", + item->filename, item->startline, item->endline); + errs++; + } + + /* just one item-- the label should be in the current extension */ + + if (item->u1.list && !item->u1.list->next && !strstr((item->u1.list)->u1.str,"${")) { + struct pval *z = get_extension_or_contxt(item); + struct pval *x = 0; + if (z) + x = find_label_in_current_extension((char*)((item->u1.list)->u1.str), z); /* if in macro, use current context instead */ + /* printf("Called find_label_in_current_extension with arg %s; current_extension is %x: %d\n", + (char*)((item->u1.list)->u1.str), current_extension?current_extension:current_context, current_extension?current_extension->type:current_context->type); */ + if (!x) { + ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto: no label %s exists in the current extension!\n", + item->filename, item->startline, item->endline, item->u1.list->u1.str); + errs++; + } + else + return; + } + + /* TWO items */ + if (item->u1.list->next && !item->u1.list->next->next) { + /* two items */ + /* printf("Calling find_label_in_current_context with args %s, %s\n", + (char*)((item->u1.list)->u1.str), (char *)item->u1.list->next->u1.str); */ + if (!strstr((item->u1.list)->u1.str,"${") + && !strstr(item->u1.list->next->u1.str,"${") ) /* Don't try to match variables */ { + struct pval *z = get_contxt(item); + struct pval *x = 0; + + if (z) + x = find_label_in_current_context((char *)item->u1.list->u1.str, (char *)item->u1.list->next->u1.str, z); + + if (!x) { + ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto: no label %s|%s exists in the current context, or any of its inclusions!\n", + item->filename, item->startline, item->endline, item->u1.list->u1.str, item->u1.list->next->u1.str ); + errs++; + } + else + return; + } + } + + /* All 3 items! */ + if (item->u1.list->next && item->u1.list->next->next) { + /* all three */ + pval *first = item->u1.list; + pval *second = item->u1.list->next; + pval *third = item->u1.list->next->next; + /* printf("Calling find_label_in_current_db with args %s, %s, %s\n", + (char*)first->u1.str, (char*)second->u1.str, (char*)third->u1.str); */ + if (!strstr((item->u1.list)->u1.str,"${") + && !strstr(item->u1.list->next->u1.str,"${") + && !strstr(item->u1.list->next->next->u1.str,"${")) /* Don't try to match variables */ { + struct pval *x = find_label_in_current_db((char*)first->u1.str, (char*)second->u1.str, (char*)third->u1.str); + if (!x) { + struct pval *p3; + struct pval *found = 0; + struct pval *that_context = find_context(item->u1.list->u1.str); + + /* the target of the goto could be in an included context!! Fancy that!! */ + /* look for includes in the current context */ + if (that_context) { + for (p3=that_context->u2.statements; p3; p3=p3->next) { + if (p3->type == PV_INCLUDES) { + struct pval *p4; + for (p4=p3->u1.list; p4; p4=p4->next) { + /* for each context pointed to, find it, then find a context/label that matches the + target here! */ + char *incl_context = p4->u1.str; + /* find a matching context name */ + struct pval *that_other_context = find_context(incl_context); + if (that_other_context) { + struct pval *x3; + x3 = find_label_in_current_context((char *)item->u1.list->next->u1.str, (char *)item->u1.list->next->next->u1.str, that_other_context); + if (x3) { + found = x3; + break; + } + } + } + } + } + if (!found) { + ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto: no label %s|%s exists in the context %s or its inclusions!\n", + item->filename, item->startline, item->endline, item->u1.list->next->u1.str, item->u1.list->next->next->u1.str, item->u1.list->u1.str ); + errs++; + } else { + struct pval *mac = in_macro(item); /* is this goto inside a macro? */ + if( mac ) { /* yes! */ + struct pval *targ = in_context(found); + if( mac != targ ) + { + ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: It's bad form to have a goto in a macro to a target outside the macro!\n", + item->filename, item->startline, item->endline); + warns++; + } + } + } + } else { + /* here is where code would go to check for target existence in extensions.conf files */ + ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: goto: no context %s could be found that matches the goto target!\n", + item->filename, item->startline, item->endline, item->u1.list->u1.str); + warns++; /* this is just a warning, because this context could be in extensions.conf or somewhere */ + } + } else { + struct pval *mac = in_macro(item); /* is this goto inside a macro? */ + if( mac ) { /* yes! */ + struct pval *targ = in_context(x); + if( mac != targ ) + { + ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: It's bad form to have a goto in a macro to a target outside the macro!\n", + item->filename, item->startline, item->endline); + warns++; + } + } + } + } + } +} + + +static void find_pval_goto_item(pval *item, int lev) +{ + struct pval *p4; + if (lev>100) { + ast_log(LOG_ERROR,"find_pval_goto in infinite loop!\n\n"); + return; + } + + switch ( item->type ) { case PV_MACRO: /* fields: item->u1.str == name of macro item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user @@ -480,10 +679,10 @@ void traverse_pval_item_template(pval *item, int depth)/* depth comes in handy f item->u3.macro_statements == pval list of statements in macro body. */ - for (lp=item->u2.arglist; lp; lp=lp->next) { + + /* printf("Descending into matching macro %s\n", match_context); */ + find_pval_gotos(item->u3.macro_statements,lev+1); /* if we're just searching for a context, don't bother descending into them */ - } - traverse_pval_item_template(item->u3.macro_statements,depth+1); break; case PV_CONTEXT: @@ -491,107 +690,64 @@ void traverse_pval_item_template(pval *item, int depth)/* depth comes in handy f item->u2.statements == pval list of statements in context body item->u3.abstract == int 1 if an abstract keyword were present */ - traverse_pval_item_template(item->u2.statements,depth+1); - break; - - case PV_MACRO_CALL: - /* fields: item->u1.str == name of macro to call - item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user - item->u2.arglist->u1.str == argument - item->u2.arglist->next == next arg - */ - for (lp=item->u2.arglist; lp; lp=lp->next) { - } - break; - - case PV_APPLICATION_CALL: - /* fields: item->u1.str == name of application to call - item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user - item->u2.arglist->u1.str == argument - item->u2.arglist->next == next arg - */ - for (lp=item->u2.arglist; lp; lp=lp->next) { - } - break; - - case PV_CASE: - /* fields: item->u1.str == value of case - item->u2.statements == pval list of statements under the case - */ - traverse_pval_item_template(item->u2.statements,depth+1); + break; + + case PV_CASE: + /* fields: item->u1.str == value of case + item->u2.statements == pval list of statements under the case + */ + find_pval_gotos(item->u2.statements,lev+1); break; case PV_PATTERN: /* fields: item->u1.str == value of case item->u2.statements == pval list of statements under the case */ - traverse_pval_item_template(item->u2.statements,depth+1); + find_pval_gotos(item->u2.statements,lev+1); break; case PV_DEFAULT: /* fields: item->u2.statements == pval list of statements under the case */ - traverse_pval_item_template(item->u2.statements,depth+1); + find_pval_gotos(item->u2.statements,lev+1); break; case PV_CATCH: /* fields: item->u1.str == name of extension to catch item->u2.statements == pval list of statements in context body */ - traverse_pval_item_template(item->u2.statements,depth+1); - break; - - case PV_SWITCHES: - /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list - */ - traverse_pval_item_template(item->u1.list,depth+1); - break; - - case PV_ESWITCHES: - /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list - */ - traverse_pval_item_template(item->u1.list,depth+1); - break; - - case PV_INCLUDES: - /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list - item->u2.arglist == pval list of 4 PV_WORD elements for time values - */ - traverse_pval_item_template(item->u1.list,depth+1); - traverse_pval_item_template(item->u2.arglist,depth+1); + find_pval_gotos(item->u2.statements,lev+1); break; case PV_STATEMENTBLOCK: /* fields: item->u1.list == pval list of statements in block, one per entry in the list */ - traverse_pval_item_template(item->u1.list,depth+1); - break; - - case PV_VARDEC: - case PV_LOCALVARDEC: - /* fields: item->u1.str == variable name - item->u2.val == variable value to assign - */ + find_pval_gotos(item->u1.list,lev+1); break; case PV_GOTO: /* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user. item->u1.list->u1.str == where the data on a PV_WORD will always be. */ - - if ( item->u1.list->next ) - ; - if ( item->u1.list->next && item->u1.list->next->next ) - ; - + check_goto(item); /* THE WHOLE FUNCTION OF THIS ENTIRE ROUTINE!!!! */ break; - case PV_LABEL: - /* fields: item->u1.str == label name + case PV_INCLUDES: + /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list */ + for (p4=item->u1.list; p4; p4=p4->next) { + /* for each context pointed to, find it, then find a context/label that matches the + target here! */ + char *incl_context = p4->u1.str; + /* find a matching context name */ + struct pval *that_context = find_context(incl_context); + if (that_context) { + find_pval_gotos(that_context,lev+1); /* keep working up the includes */ + } + } break; - + case PV_FOR: /* fields: item->u1.for_init == a string containing the initalizer item->u2.for_test == a string containing the loop test @@ -599,7 +755,7 @@ void traverse_pval_item_template(pval *item, int depth)/* depth comes in handy f item->u4.for_statements == a pval list of statements in the for () */ - traverse_pval_item_template(item->u4.for_statements,depth+1); + find_pval_gotos(item->u4.for_statements,lev+1); break; case PV_WHILE: @@ -607,50 +763,24 @@ void traverse_pval_item_template(pval *item, int depth)/* depth comes in handy f item->u2.statements == a pval list of statements in the while () */ - traverse_pval_item_template(item->u2.statements,depth+1); - break; - - case PV_BREAK: - /* fields: none - */ - break; - - case PV_RETURN: - /* fields: none - */ - break; - - case PV_CONTINUE: - /* fields: none - */ + find_pval_gotos(item->u2.statements,lev+1); break; - case PV_IFTIME: - /* fields: item->u1.list == there are 4 linked PV_WORDs here. + case PV_RANDOM: + /* fields: item->u1.str == the random number expression, as supplied by user item->u2.statements == a pval list of statements in the if () item->u3.else_statements == a pval list of statements in the else (could be zero) - */ - traverse_pval_item_template(item->u2.statements,depth+1); - if ( item->u3.else_statements ) { - traverse_pval_item_template(item->u3.else_statements,depth+1); - } - break; - - case PV_RANDOM: - /* fields: item->u1.str == the random number expression, as supplied by user + fall thru to PV_IF */ + + case PV_IFTIME: + /* fields: item->u1.list == the time values, 4 of them, as PV_WORD structs in a list item->u2.statements == a pval list of statements in the if () item->u3.else_statements == a pval list of statements in the else (could be zero) - */ - traverse_pval_item_template(item->u2.statements,depth+1); - if ( item->u3.else_statements ) { - traverse_pval_item_template(item->u3.else_statements,depth+1); - } - break; - + fall thru to PV_IF*/ case PV_IF: /* fields: item->u1.str == the if conditional, as supplied by user @@ -658,9 +788,10 @@ void traverse_pval_item_template(pval *item, int depth)/* depth comes in handy f item->u3.else_statements == a pval list of statements in the else (could be zero) */ - traverse_pval_item_template(item->u2.statements,depth+1); - if ( item->u3.else_statements ) { - traverse_pval_item_template(item->u3.else_statements,depth+1); + find_pval_gotos(item->u2.statements,lev+1); + + if (item->u3.else_statements) { + find_pval_gotos(item->u3.else_statements,lev+1); } break; @@ -670,7 +801,7 @@ void traverse_pval_item_template(pval *item, int depth)/* depth comes in handy f item->u2.statements == a pval list of statements in the switch, (will be case statements, most likely!) */ - traverse_pval_item_template(item->u2.statements,depth+1); + find_pval_gotos(item->u3.else_statements,lev+1); break; case PV_EXTENSION: @@ -680,2227 +811,138 @@ void traverse_pval_item_template(pval *item, int depth)/* depth comes in handy f item->u3.hints == a char * hint argument item->u4.regexten == an int boolean. non-zero says that regexten was specified */ - traverse_pval_item_template(item->u2.statements,depth+1); - break; - - case PV_IGNOREPAT: - /* fields: item->u1.str == the ignorepat data - */ - break; - - case PV_GLOBALS: - /* fields: item->u1.statements == pval list of statements, usually vardecs - */ - traverse_pval_item_template(item->u1.statements,depth+1); + + find_pval_gotos(item->u2.statements,lev+1); break; - } -} -void traverse_pval_template(pval *item, int depth) /* depth comes in handy for a pretty print (indentation), - but you may not need it */ -{ - pval *i; - - for (i=item; i; i=i->next) { - traverse_pval_item_template(i, depth); + default: + break; } } - -/* SEMANTIC CHECKING FOR AEL: ============================================================================= */ - -/* (not all that is syntactically legal is good! */ - - -static void check_macro_returns(pval *macro) +static void find_pval_gotos(pval *item,int lev) { pval *i; - if (!macro->u3.macro_statements) - { - pval *z = calloc(1, sizeof(struct pval)); - ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The macro %s is empty! I will insert a return.\n", - macro->filename, macro->startline, macro->endline, macro->u1.str); - - z->type = PV_RETURN; - z->startline = macro->startline; - z->endline = macro->endline; - z->startcol = macro->startcol; - z->endcol = macro->endcol; - z->filename = strdup(macro->filename); - - macro->u3.macro_statements = z; - return; - } - for (i=macro->u3.macro_statements; i; i=i->next) { - /* if the last statement in the list is not return, then insert a return there */ - if (i->next == NULL) { - if (i->type != PV_RETURN) { - pval *z = calloc(1, sizeof(struct pval)); - ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The macro %s does not end with a return; I will insert one.\n", - macro->filename, macro->startline, macro->endline, macro->u1.str); - - z->type = PV_RETURN; - z->startline = macro->startline; - z->endline = macro->endline; - z->startcol = macro->startcol; - z->endcol = macro->endcol; - z->filename = strdup(macro->filename); - - i->next = z; - return; - } - } - } - return; -} - - -static int extension_matches(pval *here, const char *exten, const char *pattern) -{ - int err1; - regex_t preg; - - /* simple case, they match exactly, the pattern and exten name */ - if (!strcmp(pattern,exten) == 0) - return 1; - - if (pattern[0] == '_') { - char reg1[2000]; - const char *p; - char *r = reg1; - - if ( strlen(pattern)*5 >= 2000 ) /* safety valve */ { - ast_log(LOG_ERROR,"Error: The pattern %s is way too big. Pattern matching cancelled.\n", - pattern); - return 0; - } - /* form a regular expression from the pattern, and then match it against exten */ - *r++ = '^'; /* what if the extension is a pattern ?? */ - *r++ = '_'; /* what if the extension is a pattern ?? */ - *r++ = '?'; - for (p=pattern+1; *p; p++) { - switch ( *p ) { - case 'X': - *r++ = '['; - *r++ = '0'; - *r++ = '-'; - *r++ = '9'; - *r++ = 'X'; - *r++ = ']'; - break; - - case 'Z': - *r++ = '['; - *r++ = '1'; - *r++ = '-'; - *r++ = '9'; - *r++ = 'Z'; - *r++ = ']'; - break; - - case 'N': - *r++ = '['; - *r++ = '2'; - *r++ = '-'; - *r++ = '9'; - *r++ = 'N'; - *r++ = ']'; - break; - - case '[': - while ( *p && *p != ']' ) { - *r++ = *p++; - } - if ( *p != ']') { - ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The extension pattern '%s' is missing a closing bracket \n", - here->filename, here->startline, here->endline, pattern); - } - break; - - case '.': - case '!': - *r++ = '.'; - *r++ = '*'; - break; - case '*': - *r++ = '\\'; - *r++ = '*'; - break; - default: - *r++ = *p; - break; - - } - } - *r++ = '$'; /* what if the extension is a pattern ?? */ - *r++ = *p++; /* put in the closing null */ - err1 = regcomp(&preg, reg1, REG_NOSUB|REG_EXTENDED); - if ( err1 ) { - char errmess[500]; - regerror(err1,&preg,errmess,sizeof(errmess)); - regfree(&preg); - ast_log(LOG_WARNING, "Regcomp of %s failed, error code %d\n", - reg1, err1); - return 0; - } - err1 = regexec(&preg, exten, 0, 0, 0); - regfree(&preg); - - if ( err1 ) { - /* ast_log(LOG_NOTICE,"*****************************[%d]Extension %s did not match %s(%s)\n", - err1,exten, pattern, reg1); */ - return 0; /* no match */ - } else { - /* ast_log(LOG_NOTICE,"*****************************Extension %s matched %s\n", - exten, pattern); */ - return 1; - } - + for (i=item; i; i=i->next) { - } else { - if ( strcmp(exten,pattern) == 0 ) { - return 1; - } else - return 0; - } -} - - -static void check_expr2_input(pval *expr, char *str) -{ - int spaces = strspn(str,"\t \n"); - if ( !strncmp(str+spaces,"$[",2) ) { - ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The expression '%s' is redundantly wrapped in '$[ ]'. \n", - expr->filename, expr->startline, expr->endline, str); - warns++; - } -} - -static void check_includes(pval *includes) -{ - struct pval *p4; - for (p4=includes->u1.list; p4; p4=p4->next) { - /* for each context pointed to, find it, then find a context/label that matches the - target here! */ - char *incl_context = p4->u1.str; - /* find a matching context name */ - struct pval *that_other_context = find_context(incl_context); - if (!that_other_context && strcmp(incl_context, "parkedcalls") != 0) { - ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The included context '%s' cannot be found.\n", - includes->filename, includes->startline, includes->endline, incl_context); - warns++; - } - } -} - - -static void check_timerange(pval *p) -{ - char *times; - char *e; - int s1, s2; - int e1, e2; - - times = ast_strdupa(p->u1.str); - - /* Star is all times */ - if (ast_strlen_zero(times) || !strcmp(times, "*")) { - return; - } - /* Otherwise expect a range */ - e = strchr(times, '-'); - if (!e) { - ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The time range format (%s) requires a '-' surrounded by two 24-hour times of day!\n", - p->filename, p->startline, p->endline, times); - warns++; - return; - } - *e = '\0'; - e++; - while (*e && !isdigit(*e)) - e++; - if (!*e) { - ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The time range format (%s) is missing the end time!\n", - p->filename, p->startline, p->endline, p->u1.str); - warns++; - } - if (sscanf(times, "%d:%d", &s1, &s2) != 2) { - ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start time (%s) isn't quite right!\n", - p->filename, p->startline, p->endline, times); - warns++; - } - if (sscanf(e, "%d:%d", &e1, &e2) != 2) { - ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end time (%s) isn't quite right!\n", - p->filename, p->startline, p->endline, times); - warns++; - } - - s1 = s1 * 30 + s2/2; - if ((s1 < 0) || (s1 >= 24*30)) { - ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start time (%s) is out of range!\n", - p->filename, p->startline, p->endline, times); - warns++; - } - e1 = e1 * 30 + e2/2; - if ((e1 < 0) || (e1 >= 24*30)) { - ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end time (%s) is out of range!\n", - p->filename, p->startline, p->endline, e); - warns++; - } - return; -} - -static char *days[] = -{ - "sun", - "mon", - "tue", - "wed", - "thu", - "fri", - "sat", -}; - -/*! \brief get_dow: Get day of week */ -static void check_dow(pval *DOW) -{ - char *dow; - char *c; - /* The following line is coincidence, really! */ - int s, e; - - dow = ast_strdupa(DOW->u1.str); - - /* Check for all days */ - if (ast_strlen_zero(dow) || !strcmp(dow, "*")) - return; - /* Get start and ending days */ - c = strchr(dow, '-'); - if (c) { - *c = '\0'; - c++; - } else - c = NULL; - /* Find the start */ - s = 0; - while ((s < 7) && strcasecmp(dow, days[s])) s++; - if (s >= 7) { - ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The day (%s) must be one of 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', or 'sat'!\n", - DOW->filename, DOW->startline, DOW->endline, dow); - warns++; - } - if (c) { - e = 0; - while ((e < 7) && strcasecmp(c, days[e])) e++; - if (e >= 7) { - ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end day (%s) must be one of 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', or 'sat'!\n", - DOW->filename, DOW->startline, DOW->endline, c); - warns++; - } - } else - e = s; -} - -static void check_day(pval *DAY) -{ - char *day; - char *c; - /* The following line is coincidence, really! */ - int s, e; - - day = ast_strdupa(DAY->u1.str); - - /* Check for all days */ - if (ast_strlen_zero(day) || !strcmp(day, "*")) { - return; - } - /* Get start and ending days */ - c = strchr(day, '-'); - if (c) { - *c = '\0'; - c++; - } - /* Find the start */ - if (sscanf(day, "%d", &s) != 1) { - ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start day of month (%s) must be a number!\n", - DAY->filename, DAY->startline, DAY->endline, day); - warns++; - } - else if ((s < 1) || (s > 31)) { - ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start day of month (%s) must be a number in the range [1-31]!\n", - DAY->filename, DAY->startline, DAY->endline, day); - warns++; + find_pval_goto_item(i, lev); } - s--; - if (c) { - if (sscanf(c, "%d", &e) != 1) { - ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end day of month (%s) must be a number!\n", - DAY->filename, DAY->startline, DAY->endline, c); - warns++; - } - else if ((e < 1) || (e > 31)) { - ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end day of month (%s) must be a number in the range [1-31]!\n", - DAY->filename, DAY->startline, DAY->endline, day); - warns++; - } - e--; - } else - e = s; } -static char *months[] = -{ - "jan", - "feb", - "mar", - "apr", - "may", - "jun", - "jul", - "aug", - "sep", - "oct", - "nov", - "dec", -}; - -static void check_month(pval *MON) -{ - char *mon; - char *c; - /* The following line is coincidence, really! */ - int s, e; - - mon = ast_strdupa(MON->u1.str); - /* Check for all days */ - if (ast_strlen_zero(mon) || !strcmp(mon, "*")) - return ; - /* Get start and ending days */ - c = strchr(mon, '-'); - if (c) { - *c = '\0'; - c++; - } - /* Find the start */ - s = 0; - while ((s < 12) && strcasecmp(mon, months[s])) s++; - if (s >= 12) { - ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start month (%s) must be a one of: 'jan', 'feb', ..., 'dec'!\n", - MON->filename, MON->startline, MON->endline, mon); - warns++; - } - if (c) { - e = 0; - while ((e < 12) && strcasecmp(mon, months[e])) e++; - if (e >= 12) { - ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end month (%s) must be a one of: 'jan', 'feb', ..., 'dec'!\n", - MON->filename, MON->startline, MON->endline, c); - warns++; - } - } else - e = s; -} -static int check_break(pval *item) +struct pval *find_first_label_in_current_context(char *label, pval *curr_cont) { - pval *p = item; - - while( p && p->type != PV_MACRO && p->type != PV_CONTEXT ) /* early cutout, sort of */ { - /* a break is allowed in WHILE, FOR, CASE, DEFAULT, PATTERN; otherwise, it don't make - no sense */ - if( p->type == PV_CASE || p->type == PV_DEFAULT || p->type == PV_PATTERN - || p->type == PV_WHILE || p->type == PV_FOR ) { - return 1; - } - p = p->dad; - } - ast_log(LOG_ERROR,"Error: file %s, line %d-%d: 'break' not in switch, for, or while statement!\n", - item->filename, item->startline, item->endline); - errs++; + /* printf(" --- Got args %s, %s\n", exten, label); */ + struct pval *ret; + struct pval *p3; + struct pval *startpt = ((curr_cont->type==PV_MACRO)?curr_cont->u3.macro_statements: curr_cont->u2.statements); - return 0; -} - -static int check_continue(pval *item) -{ - pval *p = item; + count_labels = 0; + return_on_context_match = 0; + match_context = "*"; + match_exten = "*"; + match_label = label; - while( p && p->type != PV_MACRO && p->type != PV_CONTEXT ) /* early cutout, sort of */ { - /* a break is allowed in WHILE, FOR, CASE, DEFAULT, PATTERN; otherwise, it don't make - no sense */ - if( p->type == PV_WHILE || p->type == PV_FOR ) { - return 1; - } - p = p->dad; - } - ast_log(LOG_ERROR,"Error: file %s, line %d-%d: 'continue' not in 'for' or 'while' statement!\n", - item->filename, item->startline, item->endline); - errs++; - - return 0; -} - -static struct pval *in_macro(pval *item) -{ - struct pval *curr; - curr = item; - while( curr ) { - if( curr->type == PV_MACRO ) { - return curr; - } - curr = curr->dad; - } - return 0; -} - -static struct pval *in_context(pval *item) -{ - struct pval *curr; - curr = item; - while( curr ) { - if( curr->type == PV_MACRO || curr->type == PV_CONTEXT ) { - return curr; - } - curr = curr->dad; - } - return 0; -} - - -/* general purpose goto finder */ - -static void check_label(pval *item) -{ - struct pval *curr; - struct pval *x; - int alright = 0; - - /* A label outside an extension just plain does not make sense! */ - - curr = item; - - while( curr ) { - if( curr->type == PV_MACRO || curr->type == PV_EXTENSION ) { - alright = 1; - break; - } - curr = curr->dad; - } - if( !alright ) - { - ast_log(LOG_ERROR,"Error: file %s, line %d-%d: Label %s is not within an extension or macro!\n", - item->filename, item->startline, item->endline, item->u1.str); - errs++; - } - - - /* basically, ensure that a label is not repeated in a context. Period. - The method: well, for each label, find the first label in the context - with the same name. If it's not the current label, then throw an error. */ - - - /* printf("==== check_label: ====\n"); */ - if( !current_extension ) - curr = current_context; - else - curr = current_extension; - - x = find_first_label_in_current_context((char *)item->u1.str, curr); - /* printf("Hey, check_label found with item = %x, and x is %x, and currcont is %x, label name is %s\n", item,x, current_context, (char *)item->u1.str); */ - if( x && x != item ) - { - ast_log(LOG_ERROR,"Error: file %s, line %d-%d: Duplicate label %s! Previously defined at file %s, line %d.\n", - item->filename, item->startline, item->endline, item->u1.str, x->filename, x->startline); - errs++; - } - /* printf("<<<<< check_label: ====\n"); */ -} - -static pval *get_goto_target(pval *item) -{ - /* just one item-- the label should be in the current extension */ - pval *curr_ext = get_extension_or_contxt(item); /* containing exten, or macro */ - pval *curr_cont; - - if (item->u1.list && !item->u1.list->next && !strstr((item->u1.list)->u1.str,"${")) { - struct pval *x = find_label_in_current_extension((char*)((item->u1.list)->u1.str), curr_ext); - return x; - } - - curr_cont = get_contxt(item); - - /* TWO items */ - if (item->u1.list->next && !item->u1.list->next->next) { - if (!strstr((item->u1.list)->u1.str,"${") - && !strstr(item->u1.list->next->u1.str,"${") ) /* Don't try to match variables */ { - struct pval *x = find_label_in_current_context((char *)item->u1.list->u1.str, (char *)item->u1.list->next->u1.str, curr_cont); - return x; - } - } - - /* All 3 items! */ - if (item->u1.list->next && item->u1.list->next->next) { - /* all three */ - pval *first = item->u1.list; - pval *second = item->u1.list->next; - pval *third = item->u1.list->next->next; - - if (!strstr((item->u1.list)->u1.str,"${") - && !strstr(item->u1.list->next->u1.str,"${") - && !strstr(item->u1.list->next->next->u1.str,"${")) /* Don't try to match variables */ { - struct pval *x = find_label_in_current_db((char*)first->u1.str, (char*)second->u1.str, (char*)third->u1.str); - if (!x) { - - struct pval *p3; - struct pval *that_context = find_context(item->u1.list->u1.str); - - /* the target of the goto could be in an included context!! Fancy that!! */ - /* look for includes in the current context */ - if (that_context) { - for (p3=that_context->u2.statements; p3; p3=p3->next) { - if (p3->type == PV_INCLUDES) { - struct pval *p4; - for (p4=p3->u1.list; p4; p4=p4->next) { - /* for each context pointed to, find it, then find a context/label that matches the - target here! */ - char *incl_context = p4->u1.str; - /* find a matching context name */ - struct pval *that_other_context = find_context(incl_context); - if (that_other_context) { - struct pval *x3; - x3 = find_label_in_current_context((char *)item->u1.list->next->u1.str, (char *)item->u1.list->next->next->u1.str, that_other_context); - if (x3) { - return x3; - } - } - } - } - } - } - } - return x; - } - } - return 0; -} - -static void check_goto(pval *item) -{ - /* check for the target of the goto-- does it exist? */ - if ( !(item->u1.list)->next && !(item->u1.list)->u1.str ) { - ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto: empty label reference found!\n", - item->filename, item->startline, item->endline); - errs++; - } - - /* just one item-- the label should be in the current extension */ - - if (item->u1.list && !item->u1.list->next && !strstr((item->u1.list)->u1.str,"${")) { - struct pval *z = get_extension_or_contxt(item); - struct pval *x = 0; - if (z) - x = find_label_in_current_extension((char*)((item->u1.list)->u1.str), z); /* if in macro, use current context instead */ - /* printf("Called find_label_in_current_extension with arg %s; current_extension is %x: %d\n", - (char*)((item->u1.list)->u1.str), current_extension?current_extension:current_context, current_extension?current_extension->type:current_context->type); */ - if (!x) { - ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto: no label %s exists in the current extension!\n", - item->filename, item->startline, item->endline, item->u1.list->u1.str); - errs++; - } - else - return; - } - - /* TWO items */ - if (item->u1.list->next && !item->u1.list->next->next) { - /* two items */ - /* printf("Calling find_label_in_current_context with args %s, %s\n", - (char*)((item->u1.list)->u1.str), (char *)item->u1.list->next->u1.str); */ - if (!strstr((item->u1.list)->u1.str,"${") - && !strstr(item->u1.list->next->u1.str,"${") ) /* Don't try to match variables */ { - struct pval *z = get_contxt(item); - struct pval *x = 0; - - if (z) - x = find_label_in_current_context((char *)item->u1.list->u1.str, (char *)item->u1.list->next->u1.str, z); - - if (!x) { - ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto: no label %s|%s exists in the current context, or any of its inclusions!\n", - item->filename, item->startline, item->endline, item->u1.list->u1.str, item->u1.list->next->u1.str ); - errs++; - } - else - return; - } - } - - /* All 3 items! */ - if (item->u1.list->next && item->u1.list->next->next) { - /* all three */ - pval *first = item->u1.list; - pval *second = item->u1.list->next; - pval *third = item->u1.list->next->next; - - /* printf("Calling find_label_in_current_db with args %s, %s, %s\n", - (char*)first->u1.str, (char*)second->u1.str, (char*)third->u1.str); */ - if (!strstr((item->u1.list)->u1.str,"${") - && !strstr(item->u1.list->next->u1.str,"${") - && !strstr(item->u1.list->next->next->u1.str,"${")) /* Don't try to match variables */ { - struct pval *x = find_label_in_current_db((char*)first->u1.str, (char*)second->u1.str, (char*)third->u1.str); - if (!x) { - struct pval *p3; - struct pval *found = 0; - struct pval *that_context = find_context(item->u1.list->u1.str); - - /* the target of the goto could be in an included context!! Fancy that!! */ - /* look for includes in the current context */ + ret = match_pval(curr_cont); + if (ret) + return ret; + + /* the target of the goto could be in an included context!! Fancy that!! */ + /* look for includes in the current context */ + for (p3=startpt; p3; p3=p3->next) { + if (p3->type == PV_INCLUDES) { + struct pval *p4; + for (p4=p3->u1.list; p4; p4=p4->next) { + /* for each context pointed to, find it, then find a context/label that matches the + target here! */ + char *incl_context = p4->u1.str; + /* find a matching context name */ + struct pval *that_context = find_context(incl_context); if (that_context) { - for (p3=that_context->u2.statements; p3; p3=p3->next) { - if (p3->type == PV_INCLUDES) { - struct pval *p4; - for (p4=p3->u1.list; p4; p4=p4->next) { - /* for each context pointed to, find it, then find a context/label that matches the - target here! */ - char *incl_context = p4->u1.str; - /* find a matching context name */ - struct pval *that_other_context = find_context(incl_context); - if (that_other_context) { - struct pval *x3; - x3 = find_label_in_current_context((char *)item->u1.list->next->u1.str, (char *)item->u1.list->next->next->u1.str, that_other_context); - if (x3) { - found = x3; - break; - } - } - } - } - } - if (!found) { - ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto: no label %s|%s exists in the context %s or its inclusions!\n", - item->filename, item->startline, item->endline, item->u1.list->next->u1.str, item->u1.list->next->next->u1.str, item->u1.list->u1.str ); - errs++; - } else { - struct pval *mac = in_macro(item); /* is this goto inside a macro? */ - if( mac ) { /* yes! */ - struct pval *targ = in_context(found); - if( mac != targ ) - { - ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: It's bad form to have a goto in a macro to a target outside the macro!\n", - item->filename, item->startline, item->endline); - warns++; - } - } - } - } else { - /* here is where code would go to check for target existence in extensions.conf files */ - ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: goto: no context %s could be found that matches the goto target!\n", - item->filename, item->startline, item->endline, item->u1.list->u1.str); - warns++; /* this is just a warning, because this context could be in extensions.conf or somewhere */ - } - } else { - struct pval *mac = in_macro(item); /* is this goto inside a macro? */ - if( mac ) { /* yes! */ - struct pval *targ = in_context(x); - if( mac != targ ) - { - ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: It's bad form to have a goto in a macro to a target outside the macro!\n", - item->filename, item->startline, item->endline); - warns++; - } - } - } - } - } -} - - -static void find_pval_goto_item(pval *item, int lev) -{ - struct pval *p4; - if (lev>100) { - ast_log(LOG_ERROR,"find_pval_goto in infinite loop!\n\n"); - return; - } - - switch ( item->type ) { - case PV_MACRO: - /* fields: item->u1.str == name of macro - item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user - item->u2.arglist->u1.str == argument - item->u2.arglist->next == next arg - - item->u3.macro_statements == pval list of statements in macro body. - */ - - /* printf("Descending into matching macro %s\n", match_context); */ - find_pval_gotos(item->u3.macro_statements,lev+1); /* if we're just searching for a context, don't bother descending into them */ - - break; - - case PV_CONTEXT: - /* fields: item->u1.str == name of context - item->u2.statements == pval list of statements in context body - item->u3.abstract == int 1 if an abstract keyword were present - */ - break; - - case PV_CASE: - /* fields: item->u1.str == value of case - item->u2.statements == pval list of statements under the case - */ - find_pval_gotos(item->u2.statements,lev+1); - break; - - case PV_PATTERN: - /* fields: item->u1.str == value of case - item->u2.statements == pval list of statements under the case - */ - find_pval_gotos(item->u2.statements,lev+1); - break; - - case PV_DEFAULT: - /* fields: - item->u2.statements == pval list of statements under the case - */ - find_pval_gotos(item->u2.statements,lev+1); - break; - - case PV_CATCH: - /* fields: item->u1.str == name of extension to catch - item->u2.statements == pval list of statements in context body - */ - find_pval_gotos(item->u2.statements,lev+1); - break; - - case PV_STATEMENTBLOCK: - /* fields: item->u1.list == pval list of statements in block, one per entry in the list - */ - find_pval_gotos(item->u1.list,lev+1); - break; - - case PV_GOTO: - /* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user. - item->u1.list->u1.str == where the data on a PV_WORD will always be. - */ - check_goto(item); /* THE WHOLE FUNCTION OF THIS ENTIRE ROUTINE!!!! */ - break; - - case PV_INCLUDES: - /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list - */ - for (p4=item->u1.list; p4; p4=p4->next) { - /* for each context pointed to, find it, then find a context/label that matches the - target here! */ - char *incl_context = p4->u1.str; - /* find a matching context name */ - struct pval *that_context = find_context(incl_context); - if (that_context) { - find_pval_gotos(that_context,lev+1); /* keep working up the includes */ - } - } - break; - - case PV_FOR: - /* fields: item->u1.for_init == a string containing the initalizer - item->u2.for_test == a string containing the loop test - item->u3.for_inc == a string containing the loop increment - - item->u4.for_statements == a pval list of statements in the for () - */ - find_pval_gotos(item->u4.for_statements,lev+1); - break; - - case PV_WHILE: - /* fields: item->u1.str == the while conditional, as supplied by user - - item->u2.statements == a pval list of statements in the while () - */ - find_pval_gotos(item->u2.statements,lev+1); - break; - - case PV_RANDOM: - /* fields: item->u1.str == the random number expression, as supplied by user - - item->u2.statements == a pval list of statements in the if () - item->u3.else_statements == a pval list of statements in the else - (could be zero) - fall thru to PV_IF */ - - case PV_IFTIME: - /* fields: item->u1.list == the time values, 4 of them, as PV_WORD structs in a list - - item->u2.statements == a pval list of statements in the if () - item->u3.else_statements == a pval list of statements in the else - (could be zero) - fall thru to PV_IF*/ - case PV_IF: - /* fields: item->u1.str == the if conditional, as supplied by user - - item->u2.statements == a pval list of statements in the if () - item->u3.else_statements == a pval list of statements in the else - (could be zero) - */ - find_pval_gotos(item->u2.statements,lev+1); - - if (item->u3.else_statements) { - find_pval_gotos(item->u3.else_statements,lev+1); - } - break; - - case PV_SWITCH: - /* fields: item->u1.str == the switch expression - - item->u2.statements == a pval list of statements in the switch, - (will be case statements, most likely!) - */ - find_pval_gotos(item->u3.else_statements,lev+1); - break; - - case PV_EXTENSION: - /* fields: item->u1.str == the extension name, label, whatever it's called - - item->u2.statements == a pval list of statements in the extension - item->u3.hints == a char * hint argument - item->u4.regexten == an int boolean. non-zero says that regexten was specified - */ - - find_pval_gotos(item->u2.statements,lev+1); - break; - - default: - break; - } -} - -static void find_pval_gotos(pval *item,int lev) -{ - pval *i; - - for (i=item; i; i=i->next) { - - find_pval_goto_item(i, lev); - } -} - - - -/* general purpose label finder */ -static struct pval *match_pval_item(pval *item) -{ - pval *x; - - switch ( item->type ) { - case PV_MACRO: - /* fields: item->u1.str == name of macro - item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user - item->u2.arglist->u1.str == argument - item->u2.arglist->next == next arg - - item->u3.macro_statements == pval list of statements in macro body. - */ - /* printf(" matching in MACRO %s, match_context=%s; retoncontmtch=%d; \n", item->u1.str, match_context, return_on_context_match); */ - if (!strcmp(match_context,"*") || !strcmp(item->u1.str, match_context)) { - - /* printf("MACRO: match context is: %s\n", match_context); */ - - if (return_on_context_match && !strcmp(item->u1.str, match_context)) /* if we're just searching for a context, don't bother descending into them */ { - /* printf("Returning on matching macro %s\n", match_context); */ - return item; - } - - - if (!return_on_context_match) { - /* printf("Descending into matching macro %s/%s\n", match_context, item->u1.str); */ - if ((x=match_pval(item->u3.macro_statements))) { - /* printf("Responded with pval match %x\n", x); */ - return x; - } - } - } else { - /* printf("Skipping context/macro %s\n", item->u1.str); */ - } - - break; - - case PV_CONTEXT: - /* fields: item->u1.str == name of context - item->u2.statements == pval list of statements in context body - item->u3.abstract == int 1 if an abstract keyword were present - */ - /* printf(" matching in CONTEXT\n"); */ - if (!strcmp(match_context,"*") || !strcmp(item->u1.str, match_context)) { - if (return_on_context_match && !strcmp(item->u1.str, match_context)) { - /* printf("Returning on matching context %s\n", match_context); */ - /* printf("non-CONTEXT: Responded with pval match %x\n", x); */ - return item; - } - - if (!return_on_context_match ) { - /* printf("Descending into matching context %s\n", match_context); */ - if ((x=match_pval(item->u2.statements))) /* if we're just searching for a context, don't bother descending into them */ { - /* printf("CONTEXT: Responded with pval match %x\n", x); */ - return x; - } - } - } else { - /* printf("Skipping context/macro %s\n", item->u1.str); */ - } - break; - - case PV_CASE: - /* fields: item->u1.str == value of case - item->u2.statements == pval list of statements under the case - */ - /* printf(" matching in CASE\n"); */ - if ((x=match_pval(item->u2.statements))) { - /* printf("CASE: Responded with pval match %x\n", x); */ - return x; - } - break; - - case PV_PATTERN: - /* fields: item->u1.str == value of case - item->u2.statements == pval list of statements under the case - */ - /* printf(" matching in PATTERN\n"); */ - if ((x=match_pval(item->u2.statements))) { - /* printf("PATTERN: Responded with pval match %x\n", x); */ - return x; - } - break; - - case PV_DEFAULT: - /* fields: - item->u2.statements == pval list of statements under the case - */ - /* printf(" matching in DEFAULT\n"); */ - if ((x=match_pval(item->u2.statements))) { - /* printf("DEFAULT: Responded with pval match %x\n", x); */ - return x; - } - break; - - case PV_CATCH: - /* fields: item->u1.str == name of extension to catch - item->u2.statements == pval list of statements in context body - */ - /* printf(" matching in CATCH\n"); */ - if (!strcmp(match_exten,"*") || extension_matches(item, match_exten, item->u1.str) ) { - /* printf("Descending into matching catch %s => %s\n", match_exten, item->u1.str); */ - if (strcmp(match_label,"1") == 0) { - if (item->u2.statements) { - struct pval *p5 = item->u2.statements; - while (p5 && p5->type == PV_LABEL) /* find the first non-label statement in this context. If it exists, there's a "1" */ - p5 = p5->next; - if (p5) - return p5; - else - return 0; - } - else - return 0; - } - - if ((x=match_pval(item->u2.statements))) { - /* printf("CATCH: Responded with pval match %x\n", (unsigned int)x); */ - return x; - } - } else { - /* printf("Skipping catch %s\n", item->u1.str); */ - } - break; - - case PV_STATEMENTBLOCK: - /* fields: item->u1.list == pval list of statements in block, one per entry in the list - */ - /* printf(" matching in STATEMENTBLOCK\n"); */ - if ((x=match_pval(item->u1.list))) { - /* printf("STATEMENTBLOCK: Responded with pval match %x\n", x); */ - return x; - } - break; - - case PV_LABEL: - /* fields: item->u1.str == label name - */ - /* printf("PV_LABEL %s (cont=%s, exten=%s\n", - item->u1.str, current_context->u1.str, (current_extension?current_extension->u1.str:"<macro>"));*/ - - if (count_labels) { - if (!strcmp(match_label, item->u1.str)) { - label_count++; - last_matched_label = item; - } - - } else { - if (!strcmp(match_label, item->u1.str)) { - /* printf("LABEL: Responded with pval match %x\n", x); */ - return item; - } - } - break; - - case PV_FOR: - /* fields: item->u1.for_init == a string containing the initalizer - item->u2.for_test == a string containing the loop test - item->u3.for_inc == a string containing the loop increment - - item->u4.for_statements == a pval list of statements in the for () - */ - /* printf(" matching in FOR\n"); */ - if ((x=match_pval(item->u4.for_statements))) { - /* printf("FOR: Responded with pval match %x\n", x);*/ - return x; - } - break; - - case PV_WHILE: - /* fields: item->u1.str == the while conditional, as supplied by user - - item->u2.statements == a pval list of statements in the while () - */ - /* printf(" matching in WHILE\n"); */ - if ((x=match_pval(item->u2.statements))) { - /* printf("WHILE: Responded with pval match %x\n", x); */ - return x; - } - break; - - case PV_RANDOM: - /* fields: item->u1.str == the random number expression, as supplied by user - - item->u2.statements == a pval list of statements in the if () - item->u3.else_statements == a pval list of statements in the else - (could be zero) - fall thru to PV_IF */ - - case PV_IFTIME: - /* fields: item->u1.list == the time values, 4 of them, as PV_WORD structs in a list - - item->u2.statements == a pval list of statements in the if () - item->u3.else_statements == a pval list of statements in the else - (could be zero) - fall thru to PV_IF*/ - case PV_IF: - /* fields: item->u1.str == the if conditional, as supplied by user - - item->u2.statements == a pval list of statements in the if () - item->u3.else_statements == a pval list of statements in the else - (could be zero) - */ - /* printf(" matching in IF/IFTIME/RANDOM\n"); */ - if ((x=match_pval(item->u2.statements))) { - return x; - } - if (item->u3.else_statements) { - if ((x=match_pval(item->u3.else_statements))) { - /* printf("IF/IFTIME/RANDOM: Responded with pval match %x\n", x); */ - return x; - } - } - break; - - case PV_SWITCH: - /* fields: item->u1.str == the switch expression - - item->u2.statements == a pval list of statements in the switch, - (will be case statements, most likely!) - */ - /* printf(" matching in SWITCH\n"); */ - if ((x=match_pval(item->u2.statements))) { - /* printf("SWITCH: Responded with pval match %x\n", x); */ - return x; - } - break; - - case PV_EXTENSION: - /* fields: item->u1.str == the extension name, label, whatever it's called - - item->u2.statements == a pval list of statements in the extension - item->u3.hints == a char * hint argument - item->u4.regexten == an int boolean. non-zero says that regexten was specified - */ - /* printf(" matching in EXTENSION\n"); */ - if (!strcmp(match_exten,"*") || extension_matches(item, match_exten, item->u1.str) ) { - /* printf("Descending into matching exten %s => %s\n", match_exten, item->u1.str); */ - if (strcmp(match_label,"1") == 0) { - if (item->u2.statements) { - struct pval *p5 = item->u2.statements; - while (p5 && p5->type == PV_LABEL) /* find the first non-label statement in this context. If it exists, there's a "1" */ - p5 = p5->next; - if (p5) - return p5; - else - return 0; - } - else - return 0; - } - - if ((x=match_pval(item->u2.statements))) { - /* printf("EXTENSION: Responded with pval match %x\n", x); */ - return x; - } - } else { - /* printf("Skipping exten %s\n", item->u1.str); */ - } - break; - default: - /* printf(" matching in default = %d\n", item->type); */ - break; - } - return 0; -} - -struct pval *match_pval(pval *item) -{ - pval *i; - - for (i=item; i; i=i->next) { - pval *x; - /* printf(" -- match pval: item %d\n", i->type); */ - - if ((x = match_pval_item(i))) { - /* printf("match_pval: returning x=%x\n", (int)x); */ - return x; /* cut the search short */ - } - } - return 0; -} - -#if 0 -int count_labels_in_current_context(char *label) -{ - label_count = 0; - count_labels = 1; - return_on_context_match = 0; - match_pval(current_context->u2.statements); - - return label_count; -} -#endif - -struct pval *find_first_label_in_current_context(char *label, pval *curr_cont) -{ - /* printf(" --- Got args %s, %s\n", exten, label); */ - struct pval *ret; - struct pval *p3; - struct pval *startpt = ((curr_cont->type==PV_MACRO)?curr_cont->u3.macro_statements: curr_cont->u2.statements); - - count_labels = 0; - return_on_context_match = 0; - match_context = "*"; - match_exten = "*"; - match_label = label; - - ret = match_pval(curr_cont); - if (ret) - return ret; - - /* the target of the goto could be in an included context!! Fancy that!! */ - /* look for includes in the current context */ - for (p3=startpt; p3; p3=p3->next) { - if (p3->type == PV_INCLUDES) { - struct pval *p4; - for (p4=p3->u1.list; p4; p4=p4->next) { - /* for each context pointed to, find it, then find a context/label that matches the - target here! */ - char *incl_context = p4->u1.str; - /* find a matching context name */ - struct pval *that_context = find_context(incl_context); - if (that_context) { - struct pval *x3; - x3 = find_first_label_in_current_context(label, that_context); - if (x3) { - return x3; - } - } - } - } - } - return 0; -} - -struct pval *find_label_in_current_context(char *exten, char *label, pval *curr_cont) -{ - /* printf(" --- Got args %s, %s\n", exten, label); */ - struct pval *ret; - struct pval *p3; - struct pval *startpt; - - count_labels = 0; - return_on_context_match = 0; - match_context = "*"; - match_exten = exten; - match_label = label; - if (curr_cont->type == PV_MACRO) - startpt = curr_cont->u3.macro_statements; - else - startpt = curr_cont->u2.statements; - - ret = match_pval(startpt); - if (ret) - return ret; - - /* the target of the goto could be in an included context!! Fancy that!! */ - /* look for includes in the current context */ - for (p3=startpt; p3; p3=p3->next) { - if (p3->type == PV_INCLUDES) { - struct pval *p4; - for (p4=p3->u1.list; p4; p4=p4->next) { - /* for each context pointed to, find it, then find a context/label that matches the - target here! */ - char *incl_context = p4->u1.str; - /* find a matching context name */ - struct pval *that_context = find_context(incl_context); - if (that_context) { - struct pval *x3; - x3 = find_label_in_current_context(exten, label, that_context); - if (x3) { - return x3; - } - } - } - } - } - return 0; -} - -static struct pval *find_label_in_current_extension(const char *label, pval *curr_ext) -{ - /* printf(" --- Got args %s\n", label); */ - count_labels = 0; - return_on_context_match = 0; - match_context = "*"; - match_exten = "*"; - match_label = label; - return match_pval(curr_ext); -} - -static struct pval *find_label_in_current_db(const char *context, const char *exten, const char *label) -{ - /* printf(" --- Got args %s, %s, %s\n", context, exten, label); */ - count_labels = 0; - return_on_context_match = 0; - - match_context = context; - match_exten = exten; - match_label = label; - - return match_pval(current_db); -} - - -struct pval *find_macro(char *name) -{ - return_on_context_match = 1; - count_labels = 0; - match_context = name; - match_exten = "*"; /* don't really need to set these, shouldn't be reached */ - match_label = "*"; - return match_pval(current_db); -} - -struct pval *find_context(char *name) -{ - return_on_context_match = 1; - count_labels = 0; - match_context = name; - match_exten = "*"; /* don't really need to set these, shouldn't be reached */ - match_label = "*"; - return match_pval(current_db); -} - -int is_float(char *arg ) -{ - char *s; - for (s=arg; *s; s++) { - if (*s != '.' && (*s < '0' || *s > '9')) - return 0; - } - return 1; -} -int is_int(char *arg ) -{ - char *s; - for (s=arg; *s; s++) { - if (*s < '0' || *s > '9') - return 0; - } - return 1; -} -int is_empty(char *arg) -{ - if (!arg) - return 1; - if (*arg == 0) - return 1; - while (*arg) { - if (*arg != ' ' && *arg != '\t') - return 0; - arg++; - } - return 1; -} - -#ifdef AAL_ARGCHECK -int option_matches_j( struct argdesc *should, pval *is, struct argapp *app) -{ - struct argchoice *ac; - char *opcop,*q,*p; - - switch (should->dtype) { - case ARGD_OPTIONSET: - if ( strstr(is->u1.str,"${") ) - return 0; /* no checking anything if there's a var reference in there! */ - - opcop = ast_strdupa(is->u1.str); - - for (q=opcop;*q;q++) { /* erase the innards of X(innard) type arguments, so we don't get confused later */ - if ( *q == '(' ) { - p = q+1; - while (*p && *p != ')' ) - *p++ = '+'; - q = p+1; - } - } - - for (ac=app->opts; ac; ac=ac->next) { - if (strlen(ac->name)>1 && strchr(ac->name,'(') == 0 && strcmp(ac->name,is->u1.str) == 0) /* multichar option, no parens, and a match? */ - return 0; - } - for (ac=app->opts; ac; ac=ac->next) { - if (strlen(ac->name)==1 || strchr(ac->name,'(')) { - char *p = strchr(opcop,ac->name[0]); /* wipe out all matched options in the user-supplied string */ - - if (p && *p == 'j') { - ast_log(LOG_ERROR, "Error: file %s, line %d-%d: The j option in the %s application call is not appropriate for AEL!\n", - is->filename, is->startline, is->endline, app->name); - errs++; - } - - if (p) { - *p = '+'; - if (ac->name[1] == '(') { - if (*(p+1) != '(') { - ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The %c option in the %s application call should have an (argument), but doesn't!\n", - is->filename, is->startline, is->endline, ac->name[0], app->name); - warns++; - } - } - } - } - } - for (q=opcop; *q; q++) { - if ( *q != '+' && *q != '(' && *q != ')') { - ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The %c option in the %s application call is not available as an option!\n", - is->filename, is->startline, is->endline, *q, app->name); - warns++; - } - } - return 1; - break; - default: - return 0; - } - -} - -int option_matches( struct argdesc *should, pval *is, struct argapp *app) -{ - struct argchoice *ac; - char *opcop; - - switch (should->dtype) { - case ARGD_STRING: - if (is_empty(is->u1.str) && should->type == ARGD_REQUIRED) - return 0; - if (is->u1.str && strlen(is->u1.str) > 0) /* most will match */ - return 1; - break; - - case ARGD_INT: - if (is_int(is->u1.str)) - return 1; - else - return 0; - break; - - case ARGD_FLOAT: - if (is_float(is->u1.str)) - return 1; - else - return 0; - break; - - case ARGD_ENUM: - if( !is->u1.str || strlen(is->u1.str) == 0 ) - return 1; /* a null arg in the call will match an enum, I guess! */ - for (ac=should->choices; ac; ac=ac->next) { - if (strcmp(ac->name,is->u1.str) == 0) - return 1; - } - return 0; - break; - - case ARGD_OPTIONSET: - opcop = ast_strdupa(is->u1.str); - - for (ac=app->opts; ac; ac=ac->next) { - if (strlen(ac->name)>1 && strchr(ac->name,'(') == 0 && strcmp(ac->name,is->u1.str) == 0) /* multichar option, no parens, and a match? */ - return 1; - } - for (ac=app->opts; ac; ac=ac->next) { - if (strlen(ac->name)==1 || strchr(ac->name,'(')) { - char *p = strchr(opcop,ac->name[0]); /* wipe out all matched options in the user-supplied string */ - - if (p) { - *p = '+'; - if (ac->name[1] == '(') { - if (*(p+1) == '(') { - char *q = p+1; - while (*q && *q != ')') { - *q++ = '+'; - } - *q = '+'; - } - } - } - } - } - return 1; - break; - case ARGD_VARARG: - return 1; /* matches anything */ - break; - } - return 1; /* unless some for-sure match or non-match returns, then it must be close enough ... */ -} -#endif - -int check_app_args(pval* appcall, pval *arglist, struct argapp *app) -{ -#ifdef AAL_ARGCHECK - struct argdesc *ad = app->args; - pval *pa; - int z; - - for (pa = arglist; pa; pa=pa->next) { - if (!ad) { - ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: Extra argument %s not in application call to %s !\n", - arglist->filename, arglist->startline, arglist->endline, pa->u1.str, app->name); - warns++; - return 1; - } else { - /* find the first entry in the ad list that will match */ - do { - if ( ad->dtype == ARGD_VARARG ) /* once we hit the VARARG, all bets are off. Discontinue the comparisons */ - break; - - z= option_matches( ad, pa, app); - if (!z) { - if ( !arglist ) - arglist=appcall; - - if (ad->type == ARGD_REQUIRED) { - ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: Required argument %s not in application call to %s !\n", - arglist->filename, arglist->startline, arglist->endline, ad->dtype==ARGD_OPTIONSET?"options":ad->name, app->name); - warns++; - return 1; - } - } else if (z && ad->dtype == ARGD_OPTIONSET) { - option_matches_j( ad, pa, app); - } - ad = ad->next; - } while (ad && !z); - } - } - /* any app nodes left, that are not optional? */ - for ( ; ad; ad=ad->next) { - if (ad->type == ARGD_REQUIRED && ad->dtype != ARGD_VARARG) { - if ( !arglist ) - arglist=appcall; - ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: Required argument %s not in application call to %s !\n", - arglist->filename, arglist->startline, arglist->endline, ad->dtype==ARGD_OPTIONSET?"options":ad->name, app->name); - warns++; - return 1; - } - } - return 0; -#else - return 0; -#endif -} - -void check_switch_expr(pval *item, struct argapp *apps) -{ -#ifdef AAL_ARGCHECK - /* get and clean the variable name */ - char *buff1, *p; - struct argapp *a,*a2; - struct appsetvar *v,*v2; - struct argchoice *c; - pval *t; - - p = item->u1.str; - while (p && *p && (*p == ' ' || *p == '\t' || *p == '$' || *p == '{' ) ) - p++; - - buff1 = ast_strdupa(p); - - while (strlen(buff1) > 0 && ( buff1[strlen(buff1)-1] == '}' || buff1[strlen(buff1)-1] == ' ' || buff1[strlen(buff1)-1] == '\t')) - buff1[strlen(buff1)-1] = 0; - /* buff1 now contains the variable name */ - v = 0; - for (a=apps; a; a=a->next) { - for (v=a->setvars;v;v=v->next) { - if (strcmp(v->name,buff1) == 0) { - break; - } - } - if ( v ) - break; - } - if (v && v->vals) { - /* we have a match, to a variable that has a set of determined values */ - int def= 0; - int pat = 0; - int f1 = 0; - - /* first of all, does this switch have a default case ? */ - for (t=item->u2.statements; t; t=t->next) { - if (t->type == PV_DEFAULT) { - def =1; - break; - } - if (t->type == PV_PATTERN) { - pat++; - } - } - if (def || pat) /* nothing to check. All cases accounted for! */ - return; - for (c=v->vals; c; c=c->next) { - f1 = 0; - for (t=item->u2.statements; t; t=t->next) { - if (t->type == PV_CASE || t->type == PV_PATTERN) { - if (!strcmp(t->u1.str,c->name)) { - f1 = 1; - break; - } - } - } - if (!f1) { - ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: switch with expression(%s) does not handle the case of %s !\n", - item->filename, item->startline, item->endline, item->u1.str, c->name); - warns++; - } - } - /* next, is there an app call in the current exten, that would set this var? */ - f1 = 0; - t = current_extension->u2.statements; - if ( t && t->type == PV_STATEMENTBLOCK ) - t = t->u1.statements; - for (; t && t != item; t=t->next) { - if (t->type == PV_APPLICATION_CALL) { - /* find the application that matches the u1.str */ - for (a2=apps; a2; a2=a2->next) { - if (strcasecmp(a2->name, t->u1.str)==0) { - for (v2=a2->setvars; v2; v2=v2->next) { - if (strcmp(v2->name, buff1) == 0) { - /* found an app that sets the var */ - f1 = 1; - break; - } - } - } - if (f1) - break; - } - } - if (f1) - break; - } - - /* see if it sets the var */ - if (!f1) { - ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: Couldn't find an application call in this extension that sets the expression (%s) value!\n", - item->filename, item->startline, item->endline, item->u1.str); - warns++; - } - } -#else - pval *t,*tl=0,*p2; - int def= 0; - - /* first of all, does this switch have a default case ? */ - for (t=item->u2.statements; t; t=t->next) { - if (t->type == PV_DEFAULT) { - def =1; - break; - } - tl = t; - } - if (def) /* nothing to check. All cases accounted for! */ - return; - /* if no default, warn and insert a default case at the end */ - p2 = tl->next = calloc(1, sizeof(struct pval)); - - p2->type = PV_DEFAULT; - p2->startline = tl->startline; - p2->endline = tl->endline; - p2->startcol = tl->startcol; - p2->endcol = tl->endcol; - p2->filename = strdup(tl->filename); - ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: A default case was automatically added to the switch.\n", - p2->filename, p2->startline, p2->endline); - warns++; - -#endif -} - -static void check_context_names(void) -{ - pval *i,*j; - for (i=current_db; i; i=i->next) { - if (i->type == PV_CONTEXT || i->type == PV_MACRO) { - for (j=i->next; j; j=j->next) { - if ( j->type == PV_CONTEXT || j->type == PV_MACRO ) { - if ( !strcmp(i->u1.str, j->u1.str) ) - { - ast_log(LOG_ERROR,"Error: file %s, line %d-%d: The context name (%s) is also declared in file %s, line %d-%d!\n", - i->filename, i->startline, i->endline, i->u1.str, j->filename, j->startline, j->endline); - errs++; - } - } - } - } - } -} - -static void check_abstract_reference(pval *abstract_context) -{ - pval *i,*j; - /* find some context includes that reference this context */ - - - /* otherwise, print out a warning */ - for (i=current_db; i; i=i->next) { - if (i->type == PV_CONTEXT) { - for (j=i->u2. statements; j; j=j->next) { - if ( j->type == PV_INCLUDES ) { - struct pval *p4; - for (p4=j->u1.list; p4; p4=p4->next) { - /* for each context pointed to, find it, then find a context/label that matches the - target here! */ - if ( !strcmp(p4->u1.str, abstract_context->u1.str) ) - return; /* found a match! */ - } - } - } - } - } - ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: Couldn't find a reference to this abstract context (%s) in any other context!\n", - abstract_context->filename, abstract_context->startline, abstract_context->endline, abstract_context->u1.str); - warns++; -} - - -void check_pval_item(pval *item, struct argapp *apps, int in_globals) -{ - pval *lp; -#ifdef AAL_ARGCHECK - struct argapp *app, *found; -#endif - struct pval *macro_def; - struct pval *app_def; - - char errmsg[4096]; - char *strp; - - switch (item->type) { - case PV_WORD: - /* fields: item->u1.str == string associated with this (word). - item->u2.arglist == pval list of 4 PV_WORD elements for time values (only in PV_INCLUDES) */ - break; - - case PV_MACRO: - /* fields: item->u1.str == name of macro - item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user - item->u2.arglist->u1.str == argument - item->u2.arglist->next == next arg - - item->u3.macro_statements == pval list of statements in macro body. - */ - in_abstract_context = 0; - current_context = item; - current_extension = 0; - - check_macro_returns(item); - - for (lp=item->u2.arglist; lp; lp=lp->next) { - - } - check_pval(item->u3.macro_statements, apps,in_globals); - break; - - case PV_CONTEXT: - /* fields: item->u1.str == name of context - item->u2.statements == pval list of statements in context body - item->u3.abstract == int 1 if an abstract keyword were present - */ - current_context = item; - current_extension = 0; - if ( item->u3.abstract ) { - in_abstract_context = 1; - check_abstract_reference(item); - } else - in_abstract_context = 0; - check_pval(item->u2.statements, apps,in_globals); - break; - - case PV_MACRO_CALL: - /* fields: item->u1.str == name of macro to call - item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user - item->u2.arglist->u1.str == argument - item->u2.arglist->next == next arg - */ - macro_def = find_macro(item->u1.str); - if (!macro_def) { - /* here is a good place to check to see if the definition is in extensions.conf! */ - ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: macro call to non-existent %s ! Hopefully it is present in extensions.conf! \n", - item->filename, item->startline, item->endline, item->u1.str); - warns++; - } else if (macro_def->type != PV_MACRO) { - ast_log(LOG_ERROR,"Error: file %s, line %d-%d: macro call to %s references a context, not a macro!\n", - item->filename, item->startline, item->endline, item->u1.str); - errs++; - } else { - /* macro_def is a MACRO, so do the args match in number? */ - int hereargs = 0; - int thereargs = 0; - - for (lp=item->u2.arglist; lp; lp=lp->next) { - hereargs++; - } - for (lp=macro_def->u2.arglist; lp; lp=lp->next) { - thereargs++; - } - if (hereargs != thereargs ) { - ast_log(LOG_ERROR, "Error: file %s, line %d-%d: The macro call to %s has %d arguments, but the macro definition has %d arguments\n", - item->filename, item->startline, item->endline, item->u1.str, hereargs, thereargs); - errs++; - } - } - break; - - case PV_APPLICATION_CALL: - /* fields: item->u1.str == name of application to call - item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user - item->u2.arglist->u1.str == argument - item->u2.arglist->next == next arg - */ - /* Need to check to see if the application is available! */ - app_def = find_context(item->u1.str); - if (app_def && app_def->type == PV_MACRO) { - ast_log(LOG_ERROR,"Error: file %s, line %d-%d: application call to %s references an existing macro, but had no & preceding it!\n", - item->filename, item->startline, item->endline, item->u1.str); - errs++; - } - if (strcasecmp(item->u1.str,"GotoIf") == 0 - || strcasecmp(item->u1.str,"GotoIfTime") == 0 - || strcasecmp(item->u1.str,"while") == 0 - || strcasecmp(item->u1.str,"endwhile") == 0 - || strcasecmp(item->u1.str,"random") == 0 - || strcasecmp(item->u1.str,"gosub") == 0 - || strcasecmp(item->u1.str,"return") == 0 - || strcasecmp(item->u1.str,"gosubif") == 0 - || strcasecmp(item->u1.str,"continuewhile") == 0 - || strcasecmp(item->u1.str,"endwhile") == 0 - || strcasecmp(item->u1.str,"execif") == 0 - || strcasecmp(item->u1.str,"execiftime") == 0 - || strcasecmp(item->u1.str,"exitwhile") == 0 - || strcasecmp(item->u1.str,"goto") == 0 - || strcasecmp(item->u1.str,"macro") == 0 - || strcasecmp(item->u1.str,"macroexclusive") == 0 - || strcasecmp(item->u1.str,"macroif") == 0 - || strcasecmp(item->u1.str,"stackpop") == 0 - || strcasecmp(item->u1.str,"execIf") == 0 ) { - ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: application call to %s affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead!\n", - item->filename, item->startline, item->endline, item->u1.str); - warns++; - } - if (strcasecmp(item->u1.str,"macroexit") == 0) { - ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: I am converting the MacroExit call here to a return statement.\n", - item->filename, item->startline, item->endline); - item->type = PV_RETURN; - free(item->u1.str); - item->u1.str = 0; - } - -#ifdef AAL_ARGCHECK - found = 0; - for (app=apps; app; app=app->next) { - if (strcasecmp(app->name, item->u1.str) == 0) { - found =app; - break; - } - } - if (!found) { - ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: application call to %s not listed in applist database!\n", - item->filename, item->startline, item->endline, item->u1.str); - warns++; - } else - check_app_args(item, item->u2.arglist, app); -#endif - break; - - case PV_CASE: - /* fields: item->u1.str == value of case - item->u2.statements == pval list of statements under the case - */ - /* Make sure sequence of statements under case is terminated with goto, return, or break */ - /* find the last statement */ - check_pval(item->u2.statements, apps,in_globals); - break; - - case PV_PATTERN: - /* fields: item->u1.str == value of case - item->u2.statements == pval list of statements under the case - */ - /* Make sure sequence of statements under case is terminated with goto, return, or break */ - /* find the last statement */ - - check_pval(item->u2.statements, apps,in_globals); - break; - - case PV_DEFAULT: - /* fields: - item->u2.statements == pval list of statements under the case - */ - - check_pval(item->u2.statements, apps,in_globals); - break; - - case PV_CATCH: - /* fields: item->u1.str == name of extension to catch - item->u2.statements == pval list of statements in context body - */ - check_pval(item->u2.statements, apps,in_globals); - break; - - case PV_SWITCHES: - /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list - */ - check_pval(item->u1.list, apps,in_globals); - break; - - case PV_ESWITCHES: - /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list - */ - check_pval(item->u1.list, apps,in_globals); - break; - - case PV_INCLUDES: - /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list - */ - check_pval(item->u1.list, apps,in_globals); - check_includes(item); - for (lp=item->u1.list; lp; lp=lp->next){ - char *incl_context = lp->u1.str; - struct pval *that_context = find_context(incl_context); - - if ( lp->u2.arglist ) { - check_timerange(lp->u2.arglist); - check_dow(lp->u2.arglist->next); - check_day(lp->u2.arglist->next->next); - check_month(lp->u2.arglist->next->next->next); - } - - if (that_context) { - find_pval_gotos(that_context->u2.statements,0); - - } - } - break; - - case PV_STATEMENTBLOCK: - /* fields: item->u1.list == pval list of statements in block, one per entry in the list - */ - check_pval(item->u1.list, apps,in_globals); - break; - - case PV_VARDEC: - case PV_LOCALVARDEC: - /* fields: item->u1.str == variable name - item->u2.val == variable value to assign - */ - /* the RHS of a vardec is encapsulated in a $[] expr. Is it legal? */ - if( !in_globals ) { /* don't check stuff inside the globals context; no wrapping in $[ ] there... */ - snprintf(errmsg,sizeof(errmsg), "file %s, line %d, columns %d-%d, variable declaration expr '%s':", config, item->startline, item->startcol, item->endcol, item->u2.val); - ast_expr_register_extra_error_info(errmsg); - ast_expr(item->u2.val, expr_output, sizeof(expr_output),NULL); - ast_expr_clear_extra_error_info(); - if ( strpbrk(item->u2.val,"~!-+<>=*/&^") && !strstr(item->u2.val,"${") ) { - ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n", - item->filename, item->startline, item->endline, item->u2.val); - warns++; - } - check_expr2_input(item,item->u2.val); - } - break; - - case PV_GOTO: - /* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user. - item->u1.list->u1.str == where the data on a PV_WORD will always be. - */ - /* don't check goto's in abstract contexts */ - if ( in_abstract_context ) - break; - - check_goto(item); - break; - - case PV_LABEL: - /* fields: item->u1.str == label name - */ - if ( strspn(item->u1.str, "0123456789") == strlen(item->u1.str) ) { - ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: label '%s' is numeric, this is bad practice!\n", - item->filename, item->startline, item->endline, item->u1.str); - warns++; - } - - check_label(item); - break; - - case PV_FOR: - /* fields: item->u1.for_init == a string containing the initalizer - item->u2.for_test == a string containing the loop test - item->u3.for_inc == a string containing the loop increment - - item->u4.for_statements == a pval list of statements in the for () - */ - snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, for test expr '%s':", config, item->startline, item->startcol, item->endcol, item->u2.for_test); - ast_expr_register_extra_error_info(errmsg); - - strp = strchr(item->u1.for_init, '='); - if (strp) { - ast_expr(strp+1, expr_output, sizeof(expr_output),NULL); - } - ast_expr(item->u2.for_test, expr_output, sizeof(expr_output),NULL); - strp = strchr(item->u3.for_inc, '='); - if (strp) { - ast_expr(strp+1, expr_output, sizeof(expr_output),NULL); - } - if ( strpbrk(item->u2.for_test,"~!-+<>=*/&^") && !strstr(item->u2.for_test,"${") ) { - ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n", - item->filename, item->startline, item->endline, item->u2.for_test); - warns++; - } - if ( strpbrk(item->u3.for_inc,"~!-+<>=*/&^") && !strstr(item->u3.for_inc,"${") ) { - ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n", - item->filename, item->startline, item->endline, item->u3.for_inc); - warns++; - } - check_expr2_input(item,item->u2.for_test); - check_expr2_input(item,item->u3.for_inc); - - ast_expr_clear_extra_error_info(); - check_pval(item->u4.for_statements, apps,in_globals); - break; - - case PV_WHILE: - /* fields: item->u1.str == the while conditional, as supplied by user - - item->u2.statements == a pval list of statements in the while () - */ - snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, while expr '%s':", config, item->startline, item->startcol, item->endcol, item->u1.str); - ast_expr_register_extra_error_info(errmsg); - ast_expr(item->u1.str, expr_output, sizeof(expr_output),NULL); - ast_expr_clear_extra_error_info(); - if ( strpbrk(item->u1.str,"~!-+<>=*/&^") && !strstr(item->u1.str,"${") ) { - ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n", - item->filename, item->startline, item->endline, item->u1.str); - warns++; - } - check_expr2_input(item,item->u1.str); - check_pval(item->u2.statements, apps,in_globals); - break; - - case PV_BREAK: - /* fields: none - */ - check_break(item); - break; - - case PV_RETURN: - /* fields: none - */ - break; - - case PV_CONTINUE: - /* fields: none - */ - check_continue(item); - break; - - case PV_RANDOM: - /* fields: item->u1.str == the random number expression, as supplied by user - - item->u2.statements == a pval list of statements in the if () - item->u3.else_statements == a pval list of statements in the else - (could be zero) - */ - snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, random expr '%s':", config, item->startline, item->startcol, item->endcol, item->u1.str); - ast_expr_register_extra_error_info(errmsg); - ast_expr(item->u1.str, expr_output, sizeof(expr_output),NULL); - ast_expr_clear_extra_error_info(); - if ( strpbrk(item->u1.str,"~!-+<>=*/&^") && !strstr(item->u1.str,"${") ) { - ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: random expression '%s' has operators, but no variables. Interesting...\n", - item->filename, item->startline, item->endline, item->u1.str); - warns++; - } - check_expr2_input(item,item->u1.str); - check_pval(item->u2.statements, apps,in_globals); - if (item->u3.else_statements) { - check_pval(item->u3.else_statements, apps,in_globals); - } - break; - - case PV_IFTIME: - /* fields: item->u1.list == the if time values, 4 of them, each in PV_WORD, linked list - - item->u2.statements == a pval list of statements in the if () - item->u3.else_statements == a pval list of statements in the else - (could be zero) - */ - if ( item->u2.arglist ) { - check_timerange(item->u1.list); - check_dow(item->u1.list->next); - check_day(item->u1.list->next->next); - check_month(item->u1.list->next->next->next); - } - - check_pval(item->u2.statements, apps,in_globals); - if (item->u3.else_statements) { - check_pval(item->u3.else_statements, apps,in_globals); - } - break; - - case PV_IF: - /* fields: item->u1.str == the if conditional, as supplied by user - - item->u2.statements == a pval list of statements in the if () - item->u3.else_statements == a pval list of statements in the else - (could be zero) - */ - snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, if expr '%s':", config, item->startline, item->startcol, item->endcol, item->u1.str); - ast_expr_register_extra_error_info(errmsg); - ast_expr(item->u1.str, expr_output, sizeof(expr_output),NULL); - ast_expr_clear_extra_error_info(); - if ( strpbrk(item->u1.str,"~!-+<>=*/&^") && !strstr(item->u1.str,"${") ) { - ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression '%s' has operators, but no variables. Interesting...\n", - item->filename, item->startline, item->endline, item->u1.str); - warns++; - } - check_expr2_input(item,item->u1.str); - check_pval(item->u2.statements, apps,in_globals); - if (item->u3.else_statements) { - check_pval(item->u3.else_statements, apps,in_globals); + struct pval *x3; + x3 = find_first_label_in_current_context(label, that_context); + if (x3) { + return x3; + } + } + } } - break; - - case PV_SWITCH: - /* fields: item->u1.str == the switch expression - - item->u2.statements == a pval list of statements in the switch, - (will be case statements, most likely!) - */ - /* we can check the switch expression, see if it matches any of the app variables... - if it does, then, are all the possible cases accounted for? */ - check_switch_expr(item, apps); - check_pval(item->u2.statements, apps,in_globals); - break; - - case PV_EXTENSION: - /* fields: item->u1.str == the extension name, label, whatever it's called - - item->u2.statements == a pval list of statements in the extension - item->u3.hints == a char * hint argument - item->u4.regexten == an int boolean. non-zero says that regexten was specified - */ - current_extension = item ; - - check_pval(item->u2.statements, apps,in_globals); - break; - - case PV_IGNOREPAT: - /* fields: item->u1.str == the ignorepat data - */ - break; - - case PV_GLOBALS: - /* fields: item->u1.statements == pval list of statements, usually vardecs - */ - in_abstract_context = 0; - check_pval(item->u1.statements, apps, 1); - break; - default: - break; } + return 0; } -void check_pval(pval *item, struct argapp *apps, int in_globals) +struct pval *find_label_in_current_context(char *exten, char *label, pval *curr_cont) { - pval *i; - - /* checks to do: - 1. Do goto's point to actual labels? - 2. Do macro calls reference a macro? - 3. Does the number of macro args match the definition? - 4. Is a macro call missing its & at the front? - 5. Application calls-- we could check syntax for existing applications, - but I need some some sort of universal description bnf for a general - sort of method for checking arguments, in number, maybe even type, at least. - Don't want to hand code checks for hundreds of applications. - */ + /* printf(" --- Got args %s, %s\n", exten, label); */ + struct pval *ret; + struct pval *p3; + struct pval *startpt; - for (i=item; i; i=i->next) { - check_pval_item(i,apps,in_globals); + count_labels = 0; + return_on_context_match = 0; + match_context = "*"; + match_exten = exten; + match_label = label; + if (curr_cont->type == PV_MACRO) + startpt = curr_cont->u3.macro_statements; + else + startpt = curr_cont->u2.statements; + + ret = match_pval(startpt); + if (ret) + return ret; + + /* the target of the goto could be in an included context!! Fancy that!! */ + /* look for includes in the current context */ + for (p3=startpt; p3; p3=p3->next) { + if (p3->type == PV_INCLUDES) { + struct pval *p4; + for (p4=p3->u1.list; p4; p4=p4->next) { + /* for each context pointed to, find it, then find a context/label that matches the + target here! */ + char *incl_context = p4->u1.str; + /* find a matching context name */ + struct pval *that_context = find_context(incl_context); + if (that_context) { + struct pval *x3; + x3 = find_label_in_current_context(exten, label, that_context); + if (x3) { + return x3; + } + } + } + } } + return 0; } -static void ael2_semantic_check(pval *item, int *arg_errs, int *arg_warns, int *arg_notes) +static struct pval *find_label_in_current_extension(const char *label, pval *curr_ext) { - -#ifdef AAL_ARGCHECK - int argapp_errs =0; - char *rfilename; -#endif - struct argapp *apps=0; + /* printf(" --- Got args %s\n", label); */ + count_labels = 0; + return_on_context_match = 0; + match_context = "*"; + match_exten = "*"; + match_label = label; + return match_pval(curr_ext); +} -#ifdef AAL_ARGCHECK - rfilename = alloca(10 + strlen(ast_config_AST_VAR_DIR)); - sprintf(rfilename, "%s/applist", ast_config_AST_VAR_DIR); - - apps = argdesc_parse(rfilename, &argapp_errs); /* giveth */ -#endif - current_db = item; - errs = warns = notes = 0; +static struct pval *find_label_in_current_db(const char *context, const char *exten, const char *label) +{ + /* printf(" --- Got args %s, %s, %s\n", context, exten, label); */ + count_labels = 0; + return_on_context_match = 0; - check_context_names(); - check_pval(item, apps, 0); + match_context = context; + match_exten = exten; + match_label = label; + + return match_pval(current_db); +} -#ifdef AAL_ARGCHECK - argdesc_destroy(apps); /* taketh away */ -#endif - current_db = 0; - *arg_errs = errs; - *arg_warns = warns; - *arg_notes = notes; -} /* =============================================================================================== */ /* "CODE" GENERATOR -- Convert the AEL representation to asterisk extension language */ @@ -2908,71 +950,6 @@ static void ael2_semantic_check(pval *item, int *arg_errs, int *arg_warns, int * static int control_statement_count = 0; -struct ael_priority *new_prio(void) -{ - struct ael_priority *x = (struct ael_priority *)calloc(sizeof(struct ael_priority),1); - return x; -} - -struct ael_extension *new_exten(void) -{ - struct ael_extension *x = (struct ael_extension *)calloc(sizeof(struct ael_extension),1); - return x; -} - -void linkprio(struct ael_extension *exten, struct ael_priority *prio) -{ - if (!exten->plist) { - exten->plist = prio; - exten->plist_last = prio; - } else { - exten->plist_last->next = prio; - exten->plist_last = prio; - } - if( !prio->exten ) - prio->exten = exten; /* don't override the switch value */ -} - -void destroy_extensions(struct ael_extension *exten) -{ - struct ael_extension *ne, *nen; - for (ne=exten; ne; ne=nen) { - struct ael_priority *pe, *pen; - - if (ne->name) - free(ne->name); - - /* cidmatch fields are allocated with name, and freed when - the name field is freed. Don't do a free for this field, - unless you LIKE to see a crash! */ - - if (ne->hints) - free(ne->hints); - - for (pe=ne->plist; pe; pe=pen) { - pen = pe->next; - if (pe->app) - free(pe->app); - pe->app = 0; - if (pe->appargs) - free(pe->appargs); - pe->appargs = 0; - pe->origin = 0; - pe->goto_true = 0; - pe->goto_false = 0; - free(pe); - } - nen = ne->next_exten; - ne->next_exten = 0; - ne->plist =0; - ne->plist_last = 0; - ne->next_exten = 0; - ne->loop_break = 0; - ne->loop_continue = 0; - free(ne); - } -} - static int label_inside_case(pval *label) { pval *p = label; @@ -3642,453 +1619,50 @@ static void gen_prios(struct ael_extension *exten, char *label, pval *statement, control_statement_count++; /* generate an extension with name of catch, put all catch stats into this exten! */ - switch_case = new_exten(); - switch_case->context = this_context; - linkexten(exten,switch_case); - switch_case->name = strdup(p->u1.str); - snprintf(new_label,sizeof(new_label),"catch-%s-%d",p->u1.str, control_statement_count); - - gen_prios(switch_case, new_label, p->u2.statements,mother_exten,this_context); /* this will link in all the catch body statements here */ - if (switch_case->return_needed) { /* returns now generate a Return() app call, no longer a goto to the end of the exten */ - char buf[2000]; - struct ael_priority *np2 = new_prio(); - np2->type = AEL_APPCALL; - np2->app = strdup("NoOp"); - snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name); - np2->appargs = strdup(buf); - linkprio(switch_case, np2); - switch_case-> return_target = np2; - } - - break; - default: - break; - } - } -} - -void set_priorities(struct ael_extension *exten) -{ - int i; - struct ael_priority *pr; - do { - if (exten->is_switch) - i = 10; - else if (exten->regexten) - i=2; - else - i=1; - - for (pr=exten->plist; pr; pr=pr->next) { - pr->priority_num = i; - - if (!pr->origin || (pr->origin && pr->origin->type != PV_LABEL) ) /* Labels don't show up in the dialplan, - but we want them to point to the right - priority, which would be the next line - after the label; */ - i++; - } - - exten = exten->next_exten; - } while ( exten ); -} - -void add_extensions(struct ael_extension *exten) -{ - struct ael_priority *pr; - char *label=0; - char realext[AST_MAX_EXTENSION]; - if (!exten) { - ast_log(LOG_WARNING, "This file is Empty!\n" ); - return; - } - do { - struct ael_priority *last = 0; - - memset(realext, '\0', sizeof(realext)); /* make sure this is properly initialized */ - pbx_substitute_variables_helper(NULL, exten->name, realext, sizeof(realext) - 1); - if (exten->hints) { - if (ast_add_extension2(exten->context, 0 /*no replace*/, realext, PRIORITY_HINT, NULL, exten->cidmatch, - exten->hints, NULL, ast_free, registrar)) { - ast_log(LOG_WARNING, "Unable to add step at priority 'hint' of extension '%s'\n", - exten->name); - } - } - - for (pr=exten->plist; pr; pr=pr->next) { - char app[2000]; - char appargs[2000]; - - /* before we can add the extension, we need to prep the app/appargs; - the CONTROL types need to be done after the priority numbers are calculated. - */ - if (pr->type == AEL_LABEL) /* don't try to put labels in the dialplan! */ { - last = pr; - continue; - } - - if (pr->app) - strcpy(app, pr->app); - else - app[0] = 0; - if (pr->appargs ) - strcpy(appargs, pr->appargs); - else - appargs[0] = 0; - switch( pr->type ) { - case AEL_APPCALL: - /* easy case. Everything is all set up */ - break; - - case AEL_CONTROL1: /* FOR loop, WHILE loop, BREAK, CONTINUE, IF, IFTIME */ - /* simple, unconditional goto. */ - strcpy(app,"Goto"); - if (pr->goto_true->origin && pr->goto_true->origin->type == PV_SWITCH ) { - snprintf(appargs,sizeof(appargs),"%s,%d", pr->goto_true->exten->name, pr->goto_true->priority_num); - } else if (pr->goto_true->origin && pr->goto_true->origin->type == PV_IFTIME && pr->goto_true->origin->u3.else_statements ) { - snprintf(appargs,sizeof(appargs),"%d", pr->goto_true->priority_num+1); - } else - snprintf(appargs,sizeof(appargs),"%d", pr->goto_true->priority_num); - break; - - case AEL_FOR_CONTROL: /* WHILE loop test, FOR loop test */ - strcpy(app,"GotoIf"); - snprintf(appargs,sizeof(appargs),"%s?%d:%d", pr->appargs, pr->priority_num+1, pr->goto_false->priority_num); - break; - - case AEL_IF_CONTROL: - strcpy(app,"GotoIf"); - if (pr->origin->u3.else_statements ) - snprintf(appargs,sizeof(appargs),"%s?%d:%d", pr->appargs, pr->priority_num+1, pr->goto_false->priority_num+1); - else - snprintf(appargs,sizeof(appargs),"%s?%d:%d", pr->appargs, pr->priority_num+1, pr->goto_false->priority_num); - break; - - case AEL_RAND_CONTROL: - strcpy(app,"Random"); - snprintf(appargs,sizeof(appargs),"%s:%d", pr->appargs, pr->goto_true->priority_num+1); - break; - - case AEL_IFTIME_CONTROL: - strcpy(app,"GotoIfTime"); - snprintf(appargs,sizeof(appargs),"%s?%d", pr->appargs, pr->priority_num+2); - break; - - case AEL_RETURN: - strcpy(app,"Return"); - appargs[0] = 0; - break; - - default: - break; - } - if (last && last->type == AEL_LABEL ) { - label = last->origin->u1.str; - } - else - label = 0; - - if (ast_add_extension2(exten->context, 0 /*no replace*/, realext, pr->priority_num, (label?label:NULL), exten->cidmatch, - app, strdup(appargs), ast_free, registrar)) { - ast_log(LOG_WARNING, "Unable to add step at priority '%d' of extension '%s'\n", pr->priority_num, - exten->name); - } - last = pr; - } - exten = exten->next_exten; - } while ( exten ); -} - -static void attach_exten(struct ael_extension **list, struct ael_extension *newmem) -{ - /* travel to the end of the list... */ - struct ael_extension *lptr; - if( !*list ) { - *list = newmem; - return; - } - lptr = *list; - - while( lptr->next_exten ) { - lptr = lptr->next_exten; - } - /* lptr should now pointing to the last element in the list; it has a null next_exten pointer */ - lptr->next_exten = newmem; -} - -static pval *get_extension_or_contxt(pval *p) -{ - while( p && p->type != PV_EXTENSION && p->type != PV_CONTEXT && p->type != PV_MACRO ) { - - p = p->dad; - } - - return p; -} - -static pval *get_contxt(pval *p) -{ - while( p && p->type != PV_CONTEXT && p->type != PV_MACRO ) { - - p = p->dad; - } - - return p; -} - -static void fix_gotos_in_extensions(struct ael_extension *exten) -{ - struct ael_extension *e; - for(e=exten;e;e=e->next_exten) { - - struct ael_priority *p; - for(p=e->plist;p;p=p->next) { - - if( p->origin && p->origin->type == PV_GOTO && p->origin->u3.goto_target_in_case ) { - - /* fix the extension of the goto target to the actual extension in the post-compiled dialplan */ - - pval *target = p->origin->u2.goto_target; - struct ael_extension *z = target->u3.compiled_label; - pval *pv2 = p->origin; - char buf1[500]; - char *apparg_save = p->appargs; - - p->appargs = 0; - if (!pv2->u1.list->next) /* just one -- it won't hurt to repeat the extension */ { - snprintf(buf1,sizeof(buf1),"%s,%s", z->name, pv2->u1.list->u1.str); - p->appargs = strdup(buf1); - - } else if (pv2->u1.list->next && !pv2->u1.list->next->next) /* two */ { - snprintf(buf1,sizeof(buf1),"%s,%s", z->name, pv2->u1.list->next->u1.str); - p->appargs = strdup(buf1); - } else if (pv2->u1.list->next && pv2->u1.list->next->next) { - snprintf(buf1,sizeof(buf1),"%s,%s,%s", pv2->u1.list->u1.str, - z->name, - pv2->u1.list->next->next->u1.str); - p->appargs = strdup(buf1); - } - else - printf("WHAT? The goto doesn't fall into one of three cases for GOTO????\n"); - - if( apparg_save ) { - free(apparg_save); - } - } - } - } -} - - -void ast_compile_ael2(struct ast_context **local_contexts, struct pval *root) -{ - pval *p,*p2; - struct ast_context *context; - char buf[2000]; - struct ael_extension *exten; - struct ael_extension *exten_list = 0; - - for (p=root; p; p=p->next ) { /* do the globals first, so they'll be there - when we try to eval them */ - switch (p->type) { - case PV_GLOBALS: - /* just VARDEC elements */ - for (p2=p->u1.list; p2; p2=p2->next) { - char buf2[2000]; - snprintf(buf2,sizeof(buf2),"%s=%s", p2->u1.str, p2->u2.val); - pbx_builtin_setvar(NULL, buf2); - } - break; - default: - break; - } - } - - for (p=root; p; p=p->next ) { - pval *lp; - int argc; - - switch (p->type) { - case PV_MACRO: - - context = ast_context_create(local_contexts, p->u1.str, registrar); - - exten = new_exten(); - exten->context = context; - exten->name = strdup("s"); - argc = 1; - for (lp=p->u2.arglist; lp; lp=lp->next) { - /* for each arg, set up a "Set" command */ - struct ael_priority *np2 = new_prio(); - np2->type = AEL_APPCALL; - np2->app = strdup("Set"); - snprintf(buf,sizeof(buf),"LOCAL(%s)=${ARG%d}", lp->u1.str, argc++); - remove_spaces_before_equals(buf); - np2->appargs = strdup(buf); - linkprio(exten, np2); - } - /* add any includes */ - for (p2=p->u3.macro_statements; p2; p2=p2->next) { - pval *p3; - - switch (p2->type) { - case PV_INCLUDES: - for (p3 = p2->u1.list; p3 ;p3=p3->next) { - if ( p3->u2.arglist ) { - snprintf(buf,sizeof(buf), "%s,%s,%s,%s,%s", - p3->u1.str, - p3->u2.arglist->u1.str, - p3->u2.arglist->next->u1.str, - p3->u2.arglist->next->next->u1.str, - p3->u2.arglist->next->next->next->u1.str); - ast_context_add_include2(context, buf, registrar); - } else - ast_context_add_include2(context, p3->u1.str, registrar); - } - break; - default: - break; - } - } - /* CONTAINS APPCALLS, CATCH, just like extensions... */ - gen_prios(exten, p->u1.str, p->u3.macro_statements, 0, context ); - if (exten->return_needed) { /* most likely, this will go away */ + switch_case = new_exten(); + switch_case->context = this_context; + linkexten(exten,switch_case); + switch_case->name = strdup(p->u1.str); + snprintf(new_label,sizeof(new_label),"catch-%s-%d",p->u1.str, control_statement_count); + + gen_prios(switch_case, new_label, p->u2.statements,mother_exten,this_context); /* this will link in all the catch body statements here */ + if (switch_case->return_needed) { /* returns now generate a Return() app call, no longer a goto to the end of the exten */ + char buf[2000]; struct ael_priority *np2 = new_prio(); np2->type = AEL_APPCALL; np2->app = strdup("NoOp"); - snprintf(buf,sizeof(buf),"End of Macro %s-%s",p->u1.str, exten->name); + snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name); np2->appargs = strdup(buf); - linkprio(exten, np2); - exten-> return_target = np2; + linkprio(switch_case, np2); + switch_case-> return_target = np2; } - - set_priorities(exten); - attach_exten(&exten_list, exten); - break; - - case PV_GLOBALS: - /* already done */ - break; - - case PV_CONTEXT: - context = ast_context_create(local_contexts, p->u1.str, registrar); - - /* contexts contain: ignorepat, includes, switches, eswitches, extensions, */ - for (p2=p->u2.statements; p2; p2=p2->next) { - pval *p3; - char *s3; - - switch (p2->type) { - case PV_EXTENSION: - exten = new_exten(); - exten->name = strdup(p2->u1.str); - exten->context = context; - - if( (s3=strchr(exten->name, '/') ) != 0 ) - { - *s3 = 0; - exten->cidmatch = s3+1; - } - - if ( p2->u3.hints ) - exten->hints = strdup(p2->u3.hints); - exten->regexten = p2->u4.regexten; - gen_prios(exten, p->u1.str, p2->u2.statements, 0, context ); - if (exten->return_needed) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */ - struct ael_priority *np2 = new_prio(); - np2->type = AEL_APPCALL; - np2->app = strdup("NoOp"); - snprintf(buf,sizeof(buf),"End of Extension %s", exten->name); - np2->appargs = strdup(buf); - linkprio(exten, np2); - exten-> return_target = np2; - } - /* is the last priority in the extension a label? Then add a trailing no-op */ - if( !exten->plist_last ) - { - ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: Empty Extension!\n", - p2->filename, p2->startline, p2->endline); - } - - if ( exten->plist_last && exten->plist_last->type == AEL_LABEL ) { - struct ael_priority *np2 = new_prio(); - np2->type = AEL_APPCALL; - np2->app = strdup("NoOp"); - snprintf(buf,sizeof(buf),"A NoOp to follow a trailing label %s", exten->plist_last->origin->u1.str); - np2->appargs = strdup(buf); - linkprio(exten, np2); - } - - set_priorities(exten); - attach_exten(&exten_list, exten); - break; - - case PV_IGNOREPAT: - ast_context_add_ignorepat2(context, p2->u1.str, registrar); - break; - - case PV_INCLUDES: - for (p3 = p2->u1.list; p3 ;p3=p3->next) { - if ( p3->u2.arglist ) { - snprintf(buf,sizeof(buf), "%s,%s,%s,%s,%s", - p3->u1.str, - p3->u2.arglist->u1.str, - p3->u2.arglist->next->u1.str, - p3->u2.arglist->next->next->u1.str, - p3->u2.arglist->next->next->next->u1.str); - ast_context_add_include2(context, buf, registrar); - } else - ast_context_add_include2(context, p3->u1.str, registrar); - } - break; - - case PV_SWITCHES: - for (p3 = p2->u1.list; p3 ;p3=p3->next) { - char *c = strchr(p3->u1.str, '/'); - if (c) { - *c = '\0'; - c++; - } else - c = ""; - - ast_context_add_switch2(context, p3->u1.str, c, 0, registrar); - } - break; - case PV_ESWITCHES: - for (p3 = p2->u1.list; p3 ;p3=p3->next) { - char *c = strchr(p3->u1.str, '/'); - if (c) { - *c = '\0'; - c++; - } else - c = ""; - - ast_context_add_switch2(context, p3->u1.str, c, 1, registrar); - } - break; - default: - break; - } - } - break; - default: - /* huh? what? */ break; - } } - /* moved these from being done after a macro or extension were processed, - to after all processing is done, for the sake of fixing gotos to labels inside cases... */ - /* I guess this would be considered 2nd pass of compiler now... */ - fix_gotos_in_extensions(exten_list); /* find and fix extension ref in gotos to labels that are in case statements */ - add_extensions(exten_list); /* actually makes calls to create priorities in ast_contexts -- feeds dialplan to asterisk */ - destroy_extensions(exten_list); /* all that remains is an empty husk, discard of it as is proper */ +} + +static pval *get_extension_or_contxt(pval *p) +{ + while( p && p->type != PV_EXTENSION && p->type != PV_CONTEXT && p->type != PV_MACRO ) { + + p = p->dad; + } + return p; } +static pval *get_contxt(pval *p) +{ + while( p && p->type != PV_CONTEXT && p->type != PV_MACRO ) { + + p = p->dad; + } + + return p; +} static int aeldebug = 0; @@ -4225,8 +1799,8 @@ static int reload(void) int ael_external_load_module(void); int ael_external_load_module(void) { - pbx_load_module(); - return 1; + pbx_load_module(); + return 1; } #endif @@ -4236,290 +1810,6 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Asterisk Extension Langu .reload = reload, ); - -/* DESTROY the PVAL tree ============================================================================ */ - - - -void destroy_pval_item(pval *item) -{ - if (item == NULL) { - ast_log(LOG_WARNING, "null item\n"); - return; - } - - if (item->filename) - free(item->filename); - - switch (item->type) { - case PV_WORD: - /* fields: item->u1.str == string associated with this (word). */ - if (item->u1.str ) - free(item->u1.str); - if ( item->u2.arglist ) - destroy_pval(item->u2.arglist); - break; - - case PV_MACRO: - /* fields: item->u1.str == name of macro - item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user - item->u2.arglist->u1.str == argument - item->u2.arglist->next == next arg - - item->u3.macro_statements == pval list of statements in macro body. - */ - destroy_pval(item->u2.arglist); - if (item->u1.str ) - free(item->u1.str); - destroy_pval(item->u3.macro_statements); - break; - - case PV_CONTEXT: - /* fields: item->u1.str == name of context - item->u2.statements == pval list of statements in context body - item->u3.abstract == int 1 if an abstract keyword were present - */ - if (item->u1.str) - free(item->u1.str); - destroy_pval(item->u2.statements); - break; - - case PV_MACRO_CALL: - /* fields: item->u1.str == name of macro to call - item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user - item->u2.arglist->u1.str == argument - item->u2.arglist->next == next arg - */ - if (item->u1.str) - free(item->u1.str); - destroy_pval(item->u2.arglist); - break; - - case PV_APPLICATION_CALL: - /* fields: item->u1.str == name of application to call - item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user - item->u2.arglist->u1.str == argument - item->u2.arglist->next == next arg - */ - if (item->u1.str) - free(item->u1.str); - destroy_pval(item->u2.arglist); - break; - - case PV_CASE: - /* fields: item->u1.str == value of case - item->u2.statements == pval list of statements under the case - */ - if (item->u1.str) - free(item->u1.str); - destroy_pval(item->u2.statements); - break; - - case PV_PATTERN: - /* fields: item->u1.str == value of case - item->u2.statements == pval list of statements under the case - */ - if (item->u1.str) - free(item->u1.str); - destroy_pval(item->u2.statements); - break; - - case PV_DEFAULT: - /* fields: - item->u2.statements == pval list of statements under the case - */ - destroy_pval(item->u2.statements); - break; - - case PV_CATCH: - /* fields: item->u1.str == name of extension to catch - item->u2.statements == pval list of statements in context body - */ - if (item->u1.str) - free(item->u1.str); - destroy_pval(item->u2.statements); - break; - - case PV_SWITCHES: - /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list - */ - destroy_pval(item->u1.list); - break; - - case PV_ESWITCHES: - /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list - */ - destroy_pval(item->u1.list); - break; - - case PV_INCLUDES: - /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list - item->u2.arglist == pval list of 4 PV_WORD elements for time values - */ - destroy_pval(item->u1.list); - break; - - case PV_STATEMENTBLOCK: - /* fields: item->u1.list == pval list of statements in block, one per entry in the list - */ - destroy_pval(item->u1.list); - break; - - case PV_VARDEC: - case PV_LOCALVARDEC: - /* fields: item->u1.str == variable name - item->u2.val == variable value to assign - */ - if (item->u1.str) - free(item->u1.str); - if (item->u2.val) - free(item->u2.val); - break; - - case PV_GOTO: - /* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user. - item->u1.list->u1.str == where the data on a PV_WORD will always be. - */ - - destroy_pval(item->u1.list); - break; - - case PV_LABEL: - /* fields: item->u1.str == label name - */ - if (item->u1.str) - free(item->u1.str); - break; - - case PV_FOR: - /* fields: item->u1.for_init == a string containing the initalizer - item->u2.for_test == a string containing the loop test - item->u3.for_inc == a string containing the loop increment - - item->u4.for_statements == a pval list of statements in the for () - */ - if (item->u1.for_init) - free(item->u1.for_init); - if (item->u2.for_test) - free(item->u2.for_test); - if (item->u3.for_inc) - free(item->u3.for_inc); - destroy_pval(item->u4.for_statements); - break; - - case PV_WHILE: - /* fields: item->u1.str == the while conditional, as supplied by user - - item->u2.statements == a pval list of statements in the while () - */ - if (item->u1.str) - free(item->u1.str); - destroy_pval(item->u2.statements); - break; - - case PV_BREAK: - /* fields: none - */ - break; - - case PV_RETURN: - /* fields: none - */ - break; - - case PV_CONTINUE: - /* fields: none - */ - break; - - case PV_IFTIME: - /* fields: item->u1.list == the 4 time values, in PV_WORD structs, linked list - - item->u2.statements == a pval list of statements in the if () - item->u3.else_statements == a pval list of statements in the else - (could be zero) - */ - destroy_pval(item->u1.list); - destroy_pval(item->u2.statements); - if (item->u3.else_statements) { - destroy_pval(item->u3.else_statements); - } - break; - - case PV_RANDOM: - /* fields: item->u1.str == the random percentage, as supplied by user - - item->u2.statements == a pval list of statements in the true part () - item->u3.else_statements == a pval list of statements in the else - (could be zero) - fall thru to If */ - case PV_IF: - /* fields: item->u1.str == the if conditional, as supplied by user - - item->u2.statements == a pval list of statements in the if () - item->u3.else_statements == a pval list of statements in the else - (could be zero) - */ - if (item->u1.str) - free(item->u1.str); - destroy_pval(item->u2.statements); - if (item->u3.else_statements) { - destroy_pval(item->u3.else_statements); - } - break; - - case PV_SWITCH: - /* fields: item->u1.str == the switch expression - - item->u2.statements == a pval list of statements in the switch, - (will be case statements, most likely!) - */ - if (item->u1.str) - free(item->u1.str); - destroy_pval(item->u2.statements); - break; - - case PV_EXTENSION: - /* fields: item->u1.str == the extension name, label, whatever it's called - - item->u2.statements == a pval list of statements in the extension - item->u3.hints == a char * hint argument - item->u4.regexten == an int boolean. non-zero says that regexten was specified - */ - if (item->u1.str) - free(item->u1.str); - if (item->u3.hints) - free(item->u3.hints); - destroy_pval(item->u2.statements); - break; - - case PV_IGNOREPAT: - /* fields: item->u1.str == the ignorepat data - */ - if (item->u1.str) - free(item->u1.str); - break; - - case PV_GLOBALS: - /* fields: item->u1.statements == pval list of statements, usually vardecs - */ - destroy_pval(item->u1.statements); - break; - } - free(item); -} - -void destroy_pval(pval *item) -{ - pval *i,*nxt; - - for (i=item; i; i=nxt) { - nxt = i->next; - - destroy_pval_item(i); - } -} - #ifdef AAL_ARGCHECK static char *ael_funclist[] = { diff --git a/res/Makefile b/res/Makefile index f2d50ebab4c6214c545420d3d92e0d4c3b7b557f..97b77fe4302706d280b514c43d3ac6e8f88e7a4c 100644 --- a/res/Makefile +++ b/res/Makefile @@ -25,7 +25,24 @@ all: _all include $(ASTTOPDIR)/Makefile.moddir_rules +ael/ael_lex.o: ael/ael_lex.c ../include/asterisk/ael_structs.h ael/ael.tab.h +ael/ael_lex.o: ASTCFLAGS+=-I. -Iael + +ael/ael.tab.o: ael/ael.tab.c ael/ael.tab.h ../include/asterisk/ael_structs.h +ael/ael.tab.o: ASTCFLAGS+=-I. -Iael -DYYENABLE_NLS=0 + $(if $(filter res_snmp,$(EMBEDDED_MODS)),modules.link,res_snmp.so): snmp/agent.o +$(if $(filter res_ael_share,$(EMBEDDED_MODS)),modules.link,res_ael_share.so): ael/ael_lex.o ael/ael.tab.o ael/pval.o + +ael/ael_lex.c: + (cd ael; flex ael.flex; sed -i -e "/begin standard C headers/i#include \"asterisk.h\"" ael_lex.c) + +ael/ael.tab.c ael/ael.tab.h: + (cd ael; bison -v -d ael.y) + +ael/pval.o: ael/pval.c + clean:: rm -f snmp/*.o + rm -f ael*.o diff --git a/pbx/ael/ael.flex b/res/ael/ael.flex similarity index 100% rename from pbx/ael/ael.flex rename to res/ael/ael.flex diff --git a/pbx/ael/ael.tab.c b/res/ael/ael.tab.c similarity index 96% rename from pbx/ael/ael.tab.c rename to res/ael/ael.tab.c index 145c1c4028e049eaa74db3ff406925cf40711110..3a478c53820c9efe16b9de9cbf65ab775fb3c3f6 100644 --- a/pbx/ael/ael.tab.c +++ b/res/ael/ael.tab.c @@ -1,7 +1,9 @@ -/* A Bison parser, made by GNU Bison 2.1a. */ +/* A Bison parser, made by GNU Bison 2.3. */ -/* Skeleton parser for Yacc-like parsing with Bison, - Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. +/* Skeleton implementation for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,10 +20,18 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -/* As a special exception, when this file is copied by Bison into a - Bison output file, you may use that output file without restriction. - This special exception was added by the Free Software Foundation - in version 1.24 of Bison. */ +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ /* C LALR(1) parser skeleton written by Richard Stallman, by simplifying the original so-called "semantic" parser. */ @@ -37,7 +47,7 @@ #define YYBISON 1 /* Bison version. */ -#define YYBISON_VERSION "2.1a" +#define YYBISON_VERSION "2.3" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" @@ -188,12 +198,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/logger.h" #include "asterisk/ael_structs.h" -static pval * linku1(pval *head, pval *tail); +pval * linku1(pval *head, pval *tail); static void set_dads(pval *dad, pval *child_list); void reset_parencount(yyscan_t yyscanner); void reset_semicount(yyscan_t yyscanner); void reset_argcount(yyscan_t yyscanner ); - + #define YYLEX_PARAM ((struct parse_io *)parseio)->scanner #define YYERROR_VERBOSE 1 @@ -231,8 +241,8 @@ typedef union YYSTYPE char *str; /* strings */ struct pval *pval; /* full objects */ } -/* Line 198 of yacc.c. */ -#line 236 "ael.tab.c" +/* Line 187 of yacc.c. */ +#line 246 "ael.tab.c" YYSTYPE; # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 @@ -261,7 +271,7 @@ void yyerror(YYLTYPE *locp, struct parse_io *parseio, char const *s); int ael_yylex (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , void * yyscanner); /* create a new object with start-end marker */ -static pval *npval(pvaltype type, int first_line, int last_line, +pval *npval(pvaltype type, int first_line, int last_line, int first_column, int last_column); /* create a new object with start-end marker, simplified interface. @@ -276,8 +286,8 @@ static pval *nword(char *string, YYLTYPE *pos); static pval *update_last(pval *, YYLTYPE *); -/* Line 221 of yacc.c. */ -#line 281 "ael.tab.c" +/* Line 216 of yacc.c. */ +#line 291 "ael.tab.c" #ifdef short # undef short @@ -407,8 +417,13 @@ YYID (i) # ifndef YYSTACK_ALLOC_MAXIMUM # define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM # endif -# ifdef __cplusplus -extern "C" { +# if (defined __cplusplus && ! defined _STDLIB_H \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif # endif # ifndef YYMALLOC # define YYMALLOC malloc @@ -424,9 +439,6 @@ void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ void free (void *); /* INFRINGES ON USER NAME SPACE */ # endif # endif -# ifdef __cplusplus -} -# endif # endif #endif /* ! defined yyoverflow || YYERROR_VERBOSE */ @@ -1039,14 +1051,14 @@ do { \ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void -yy_symbol_value_print (FILE *yyoutput, int yytype, const YYSTYPE * const yyvaluep, const YYLTYPE * const yylocationp, struct parse_io *parseio) +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, struct parse_io *parseio) #else static void yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, parseio) FILE *yyoutput; int yytype; - const YYSTYPE * const yyvaluep; - const YYLTYPE * const yylocationp; + YYSTYPE const * const yyvaluep; + YYLTYPE const * const yylocationp; struct parse_io *parseio; #endif { @@ -1075,14 +1087,14 @@ yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, parseio) #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void -yy_symbol_print (FILE *yyoutput, int yytype, const YYSTYPE * const yyvaluep, const YYLTYPE * const yylocationp, struct parse_io *parseio) +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, struct parse_io *parseio) #else static void yy_symbol_print (yyoutput, yytype, yyvaluep, yylocationp, parseio) FILE *yyoutput; int yytype; - const YYSTYPE * const yyvaluep; - const YYLTYPE * const yylocationp; + YYSTYPE const * const yyvaluep; + YYLTYPE const * const yylocationp; struct parse_io *parseio; #endif { @@ -1260,7 +1272,7 @@ yytnamerr (char *yyres, const char *yystr) { if (*yystr == '"') { - size_t yyn = 0; + YYSIZE_T yyn = 0; char const *yyp = yystr; for (;;) @@ -1307,7 +1319,7 @@ yysyntax_error (char *yyresult, int yystate, int yychar) { int yyn = yypact[yystate]; - if (! (YYPACT_NINF < yyn && yyn < YYLAST)) + if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) return 0; else { @@ -1345,7 +1357,7 @@ yysyntax_error (char *yyresult, int yystate, int yychar) int yyxbegin = yyn < 0 ? -yyn : 0; /* Stay within bounds of both yycheck and yytname. */ - int yychecklim = YYLAST - yyn; + int yychecklim = YYLAST - yyn + 1; int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; int yycount = 1; @@ -1437,7 +1449,7 @@ yydestruct (yymsg, yytype, yyvaluep, yylocationp, parseio) case 42: /* "word" */ #line 178 "ael.y" { free((yyvaluep->str));}; -#line 1441 "ael.tab.c" +#line 1453 "ael.tab.c" break; case 45: /* "objects" */ #line 165 "ael.y" @@ -1445,7 +1457,7 @@ yydestruct (yymsg, yytype, yyvaluep, yylocationp, parseio) destroy_pval((yyvaluep->pval)); prev_word=0; }; -#line 1449 "ael.tab.c" +#line 1461 "ael.tab.c" break; case 46: /* "object" */ #line 165 "ael.y" @@ -1453,12 +1465,12 @@ yydestruct (yymsg, yytype, yyvaluep, yylocationp, parseio) destroy_pval((yyvaluep->pval)); prev_word=0; }; -#line 1457 "ael.tab.c" +#line 1469 "ael.tab.c" break; case 47: /* "context_name" */ #line 178 "ael.y" { free((yyvaluep->str));}; -#line 1462 "ael.tab.c" +#line 1474 "ael.tab.c" break; case 48: /* "context" */ #line 165 "ael.y" @@ -1466,7 +1478,7 @@ yydestruct (yymsg, yytype, yyvaluep, yylocationp, parseio) destroy_pval((yyvaluep->pval)); prev_word=0; }; -#line 1470 "ael.tab.c" +#line 1482 "ael.tab.c" break; case 50: /* "macro" */ #line 165 "ael.y" @@ -1474,7 +1486,7 @@ yydestruct (yymsg, yytype, yyvaluep, yylocationp, parseio) destroy_pval((yyvaluep->pval)); prev_word=0; }; -#line 1478 "ael.tab.c" +#line 1490 "ael.tab.c" break; case 51: /* "globals" */ #line 165 "ael.y" @@ -1482,7 +1494,7 @@ yydestruct (yymsg, yytype, yyvaluep, yylocationp, parseio) destroy_pval((yyvaluep->pval)); prev_word=0; }; -#line 1486 "ael.tab.c" +#line 1498 "ael.tab.c" break; case 52: /* "global_statements" */ #line 165 "ael.y" @@ -1490,7 +1502,7 @@ yydestruct (yymsg, yytype, yyvaluep, yylocationp, parseio) destroy_pval((yyvaluep->pval)); prev_word=0; }; -#line 1494 "ael.tab.c" +#line 1506 "ael.tab.c" break; case 53: /* "assignment" */ #line 165 "ael.y" @@ -1498,7 +1510,7 @@ yydestruct (yymsg, yytype, yyvaluep, yylocationp, parseio) destroy_pval((yyvaluep->pval)); prev_word=0; }; -#line 1502 "ael.tab.c" +#line 1514 "ael.tab.c" break; case 55: /* "local_assignment" */ #line 165 "ael.y" @@ -1506,7 +1518,7 @@ yydestruct (yymsg, yytype, yyvaluep, yylocationp, parseio) destroy_pval((yyvaluep->pval)); prev_word=0; }; -#line 1510 "ael.tab.c" +#line 1522 "ael.tab.c" break; case 57: /* "arglist" */ #line 165 "ael.y" @@ -1514,7 +1526,7 @@ yydestruct (yymsg, yytype, yyvaluep, yylocationp, parseio) destroy_pval((yyvaluep->pval)); prev_word=0; }; -#line 1518 "ael.tab.c" +#line 1530 "ael.tab.c" break; case 58: /* "elements" */ #line 165 "ael.y" @@ -1522,7 +1534,7 @@ yydestruct (yymsg, yytype, yyvaluep, yylocationp, parseio) destroy_pval((yyvaluep->pval)); prev_word=0; }; -#line 1526 "ael.tab.c" +#line 1538 "ael.tab.c" break; case 59: /* "element" */ #line 165 "ael.y" @@ -1530,7 +1542,7 @@ yydestruct (yymsg, yytype, yyvaluep, yylocationp, parseio) destroy_pval((yyvaluep->pval)); prev_word=0; }; -#line 1534 "ael.tab.c" +#line 1546 "ael.tab.c" break; case 60: /* "ignorepat" */ #line 165 "ael.y" @@ -1538,7 +1550,7 @@ yydestruct (yymsg, yytype, yyvaluep, yylocationp, parseio) destroy_pval((yyvaluep->pval)); prev_word=0; }; -#line 1542 "ael.tab.c" +#line 1554 "ael.tab.c" break; case 61: /* "extension" */ #line 165 "ael.y" @@ -1546,7 +1558,7 @@ yydestruct (yymsg, yytype, yyvaluep, yylocationp, parseio) destroy_pval((yyvaluep->pval)); prev_word=0; }; -#line 1550 "ael.tab.c" +#line 1562 "ael.tab.c" break; case 62: /* "statements" */ #line 165 "ael.y" @@ -1554,12 +1566,12 @@ yydestruct (yymsg, yytype, yyvaluep, yylocationp, parseio) destroy_pval((yyvaluep->pval)); prev_word=0; }; -#line 1558 "ael.tab.c" +#line 1570 "ael.tab.c" break; case 63: /* "timerange" */ #line 178 "ael.y" { free((yyvaluep->str));}; -#line 1563 "ael.tab.c" +#line 1575 "ael.tab.c" break; case 64: /* "timespec" */ #line 165 "ael.y" @@ -1567,12 +1579,12 @@ yydestruct (yymsg, yytype, yyvaluep, yylocationp, parseio) destroy_pval((yyvaluep->pval)); prev_word=0; }; -#line 1571 "ael.tab.c" +#line 1583 "ael.tab.c" break; case 65: /* "test_expr" */ #line 178 "ael.y" { free((yyvaluep->str));}; -#line 1576 "ael.tab.c" +#line 1588 "ael.tab.c" break; case 67: /* "if_like_head" */ #line 165 "ael.y" @@ -1580,22 +1592,22 @@ yydestruct (yymsg, yytype, yyvaluep, yylocationp, parseio) destroy_pval((yyvaluep->pval)); prev_word=0; }; -#line 1584 "ael.tab.c" +#line 1596 "ael.tab.c" break; case 68: /* "word_list" */ #line 178 "ael.y" { free((yyvaluep->str));}; -#line 1589 "ael.tab.c" +#line 1601 "ael.tab.c" break; case 70: /* "word3_list" */ #line 178 "ael.y" { free((yyvaluep->str));}; -#line 1594 "ael.tab.c" +#line 1606 "ael.tab.c" break; case 71: /* "goto_word" */ #line 178 "ael.y" { free((yyvaluep->str));}; -#line 1599 "ael.tab.c" +#line 1611 "ael.tab.c" break; case 72: /* "switch_statement" */ #line 165 "ael.y" @@ -1603,7 +1615,7 @@ yydestruct (yymsg, yytype, yyvaluep, yylocationp, parseio) destroy_pval((yyvaluep->pval)); prev_word=0; }; -#line 1607 "ael.tab.c" +#line 1619 "ael.tab.c" break; case 73: /* "statement" */ #line 165 "ael.y" @@ -1611,7 +1623,7 @@ yydestruct (yymsg, yytype, yyvaluep, yylocationp, parseio) destroy_pval((yyvaluep->pval)); prev_word=0; }; -#line 1615 "ael.tab.c" +#line 1627 "ael.tab.c" break; case 78: /* "opt_else" */ #line 165 "ael.y" @@ -1619,7 +1631,7 @@ yydestruct (yymsg, yytype, yyvaluep, yylocationp, parseio) destroy_pval((yyvaluep->pval)); prev_word=0; }; -#line 1623 "ael.tab.c" +#line 1635 "ael.tab.c" break; case 79: /* "target" */ #line 165 "ael.y" @@ -1627,12 +1639,12 @@ yydestruct (yymsg, yytype, yyvaluep, yylocationp, parseio) destroy_pval((yyvaluep->pval)); prev_word=0; }; -#line 1631 "ael.tab.c" +#line 1643 "ael.tab.c" break; case 80: /* "opt_pri" */ #line 178 "ael.y" { free((yyvaluep->str));}; -#line 1636 "ael.tab.c" +#line 1648 "ael.tab.c" break; case 81: /* "jumptarget" */ #line 165 "ael.y" @@ -1640,7 +1652,7 @@ yydestruct (yymsg, yytype, yyvaluep, yylocationp, parseio) destroy_pval((yyvaluep->pval)); prev_word=0; }; -#line 1644 "ael.tab.c" +#line 1656 "ael.tab.c" break; case 82: /* "macro_call" */ #line 165 "ael.y" @@ -1648,7 +1660,7 @@ yydestruct (yymsg, yytype, yyvaluep, yylocationp, parseio) destroy_pval((yyvaluep->pval)); prev_word=0; }; -#line 1652 "ael.tab.c" +#line 1664 "ael.tab.c" break; case 84: /* "application_call_head" */ #line 165 "ael.y" @@ -1656,7 +1668,7 @@ yydestruct (yymsg, yytype, yyvaluep, yylocationp, parseio) destroy_pval((yyvaluep->pval)); prev_word=0; }; -#line 1660 "ael.tab.c" +#line 1672 "ael.tab.c" break; case 86: /* "application_call" */ #line 165 "ael.y" @@ -1664,12 +1676,12 @@ yydestruct (yymsg, yytype, yyvaluep, yylocationp, parseio) destroy_pval((yyvaluep->pval)); prev_word=0; }; -#line 1668 "ael.tab.c" +#line 1680 "ael.tab.c" break; case 87: /* "opt_word" */ #line 178 "ael.y" { free((yyvaluep->str));}; -#line 1673 "ael.tab.c" +#line 1685 "ael.tab.c" break; case 88: /* "eval_arglist" */ #line 165 "ael.y" @@ -1677,7 +1689,7 @@ yydestruct (yymsg, yytype, yyvaluep, yylocationp, parseio) destroy_pval((yyvaluep->pval)); prev_word=0; }; -#line 1681 "ael.tab.c" +#line 1693 "ael.tab.c" break; case 89: /* "case_statements" */ #line 165 "ael.y" @@ -1685,7 +1697,7 @@ yydestruct (yymsg, yytype, yyvaluep, yylocationp, parseio) destroy_pval((yyvaluep->pval)); prev_word=0; }; -#line 1689 "ael.tab.c" +#line 1701 "ael.tab.c" break; case 90: /* "case_statement" */ #line 165 "ael.y" @@ -1693,7 +1705,7 @@ yydestruct (yymsg, yytype, yyvaluep, yylocationp, parseio) destroy_pval((yyvaluep->pval)); prev_word=0; }; -#line 1697 "ael.tab.c" +#line 1709 "ael.tab.c" break; case 91: /* "macro_statements" */ #line 165 "ael.y" @@ -1701,7 +1713,7 @@ yydestruct (yymsg, yytype, yyvaluep, yylocationp, parseio) destroy_pval((yyvaluep->pval)); prev_word=0; }; -#line 1705 "ael.tab.c" +#line 1717 "ael.tab.c" break; case 92: /* "macro_statement" */ #line 165 "ael.y" @@ -1709,7 +1721,7 @@ yydestruct (yymsg, yytype, yyvaluep, yylocationp, parseio) destroy_pval((yyvaluep->pval)); prev_word=0; }; -#line 1713 "ael.tab.c" +#line 1725 "ael.tab.c" break; case 93: /* "switches" */ #line 165 "ael.y" @@ -1717,7 +1729,7 @@ yydestruct (yymsg, yytype, yyvaluep, yylocationp, parseio) destroy_pval((yyvaluep->pval)); prev_word=0; }; -#line 1721 "ael.tab.c" +#line 1733 "ael.tab.c" break; case 94: /* "eswitches" */ #line 165 "ael.y" @@ -1725,7 +1737,7 @@ yydestruct (yymsg, yytype, yyvaluep, yylocationp, parseio) destroy_pval((yyvaluep->pval)); prev_word=0; }; -#line 1729 "ael.tab.c" +#line 1741 "ael.tab.c" break; case 95: /* "switchlist" */ #line 165 "ael.y" @@ -1733,7 +1745,7 @@ yydestruct (yymsg, yytype, yyvaluep, yylocationp, parseio) destroy_pval((yyvaluep->pval)); prev_word=0; }; -#line 1737 "ael.tab.c" +#line 1749 "ael.tab.c" break; case 96: /* "included_entry" */ #line 165 "ael.y" @@ -1741,7 +1753,7 @@ yydestruct (yymsg, yytype, yyvaluep, yylocationp, parseio) destroy_pval((yyvaluep->pval)); prev_word=0; }; -#line 1745 "ael.tab.c" +#line 1757 "ael.tab.c" break; case 97: /* "includeslist" */ #line 165 "ael.y" @@ -1749,7 +1761,7 @@ yydestruct (yymsg, yytype, yyvaluep, yylocationp, parseio) destroy_pval((yyvaluep->pval)); prev_word=0; }; -#line 1753 "ael.tab.c" +#line 1765 "ael.tab.c" break; case 98: /* "includes" */ #line 165 "ael.y" @@ -1757,7 +1769,7 @@ yydestruct (yymsg, yytype, yyvaluep, yylocationp, parseio) destroy_pval((yyvaluep->pval)); prev_word=0; }; -#line 1761 "ael.tab.c" +#line 1773 "ael.tab.c" break; default: @@ -2963,8 +2975,8 @@ yyreduce: break; -/* Line 1270 of yacc.c. */ -#line 2968 "ael.tab.c" +/* Line 1267 of yacc.c. */ +#line 2980 "ael.tab.c" default: break; } YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); @@ -3179,7 +3191,8 @@ yyreturn: if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); #endif - return yyresult; + /* Make sure YYID is used. */ + return YYID (yyresult); } @@ -3322,7 +3335,7 @@ void yyerror(YYLTYPE *locp, struct parse_io *parseio, char const *s) parseio->syntax_error_count++; } -static struct pval *npval(pvaltype type, int first_line, int last_line, +struct pval *npval(pvaltype type, int first_line, int last_line, int first_column, int last_column) { pval *z = calloc(1, sizeof(struct pval)); @@ -3357,23 +3370,6 @@ static pval *nword(char *string, YYLTYPE *pos) return p; } -/* append second element to the list in the first one */ -static pval * linku1(pval *head, pval *tail) -{ - if (!head) - return tail; - if (tail) { - if (!head->next) { - head->next = tail; - } else { - head->u1_last->next = tail; - } - head->u1_last = tail; - tail->prev = head; /* the dad link only points to containers */ - } - return head; -} - /* this routine adds a dad ptr to each element in the list */ static void set_dads(struct pval *dad, struct pval *child_list) { diff --git a/pbx/ael/ael.tab.h b/res/ael/ael.tab.h similarity index 80% rename from pbx/ael/ael.tab.h rename to res/ael/ael.tab.h index 4036c07f7c83a39ecf34a45fd8952e23790a9fa9..5495a6447b675e3811e735475faf2e23a2d1c951 100644 --- a/pbx/ael/ael.tab.h +++ b/res/ael/ael.tab.h @@ -1,7 +1,9 @@ -/* A Bison parser, made by GNU Bison 2.1a. */ +/* A Bison parser, made by GNU Bison 2.3. */ -/* Skeleton parser for Yacc-like parsing with Bison, - Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. +/* Skeleton interface for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,10 +20,18 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -/* As a special exception, when this file is copied by Bison into a - Bison output file, you may use that output file without restriction. - This special exception was added by the Free Software Foundation - in version 1.24 of Bison. */ +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ /* Tokens. */ #ifndef YYTOKENTYPE @@ -124,8 +134,8 @@ typedef union YYSTYPE char *str; /* strings */ struct pval *pval; /* full objects */ } -/* Line 1536 of yacc.c. */ -#line 129 "ael.tab.h" +/* Line 1489 of yacc.c. */ +#line 139 "ael.tab.h" YYSTYPE; # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 @@ -148,5 +158,3 @@ typedef struct YYLTYPE #endif - - diff --git a/pbx/ael/ael.y b/res/ael/ael.y similarity index 97% rename from pbx/ael/ael.y rename to res/ael/ael.y index 9aee6668759a2fc84fd4240ef2f0956173a0f805..5d0a1c1a2bcb8810138e72cd41e1ecf0ecef8b42 100644 --- a/pbx/ael/ael.y +++ b/res/ael/ael.y @@ -33,12 +33,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/logger.h" #include "asterisk/ael_structs.h" -static pval * linku1(pval *head, pval *tail); +pval * linku1(pval *head, pval *tail); static void set_dads(pval *dad, pval *child_list); void reset_parencount(yyscan_t yyscanner); void reset_semicount(yyscan_t yyscanner); void reset_argcount(yyscan_t yyscanner ); - + #define YYLEX_PARAM ((struct parse_io *)parseio)->scanner #define YYERROR_VERBOSE 1 @@ -63,7 +63,7 @@ void yyerror(YYLTYPE *locp, struct parse_io *parseio, char const *s); int ael_yylex (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , void * yyscanner); /* create a new object with start-end marker */ -static pval *npval(pvaltype type, int first_line, int last_line, +pval *npval(pvaltype type, int first_line, int last_line, int first_column, int last_column); /* create a new object with start-end marker, simplified interface. @@ -769,7 +769,7 @@ void yyerror(YYLTYPE *locp, struct parse_io *parseio, char const *s) parseio->syntax_error_count++; } -static struct pval *npval(pvaltype type, int first_line, int last_line, +struct pval *npval(pvaltype type, int first_line, int last_line, int first_column, int last_column) { pval *z = calloc(1, sizeof(struct pval)); @@ -804,23 +804,6 @@ static pval *nword(char *string, YYLTYPE *pos) return p; } -/* append second element to the list in the first one */ -static pval * linku1(pval *head, pval *tail) -{ - if (!head) - return tail; - if (tail) { - if (!head->next) { - head->next = tail; - } else { - head->u1_last->next = tail; - } - head->u1_last = tail; - tail->prev = head; /* the dad link only points to containers */ - } - return head; -} - /* this routine adds a dad ptr to each element in the list */ static void set_dads(struct pval *dad, struct pval *child_list) { diff --git a/pbx/ael/ael_lex.c b/res/ael/ael_lex.c similarity index 99% rename from pbx/ael/ael_lex.c rename to res/ael/ael_lex.c index d277fbc42cfe2a003d4a9e9a930e790b4fffa6e1..0a6fc49dbe008318b02246f727ac30a9916535a9 100644 --- a/pbx/ael/ael_lex.c +++ b/res/ael/ael_lex.c @@ -891,13 +891,11 @@ static void pbcwhere(const char *text, int *line, int *col ) #define argg 3 #define comment 4 -#ifndef YY_NO_UNISTD_H /* Special case for "unistd.h", since it is non-ANSI. We include it way * down here because we want the user's section 1 to have been scanned first. * The user has a chance to override it with an option. */ #include <unistd.h> -#endif #ifndef YY_EXTRA_TYPE #define YY_EXTRA_TYPE void * @@ -941,8 +939,6 @@ struct yyguts_t }; /* end struct yyguts_t */ -static int yy_init_globals (yyscan_t yyscanner ); - /* This must go here because YYSTYPE and YYLTYPE are included * from bison output in section 1.*/ # define yylval yyg->yylval_r @@ -1093,11 +1089,9 @@ static int input (yyscan_t yyscanner ); #ifndef YY_DECL #define YY_DECL_IS_OURS 1 -extern int ael_yylex \ - (YYSTYPE * yylval_param,YYLTYPE * yylloc_param ,yyscan_t yyscanner); +extern int ael_yylex (YYSTYPE * yylval_param,YYLTYPE * yylloc_param ,yyscan_t yyscanner); -#define YY_DECL int ael_yylex \ - (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner) +#define YY_DECL int ael_yylex (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner) #endif /* !YY_DECL */ /* Code executed at the beginning of each rule, after yytext and yyleng @@ -1127,7 +1121,7 @@ YY_DECL #line 173 "ael.flex" -#line 1130 "ael_lex.c" +#line 1124 "ael_lex.c" yylval = yylval_param; @@ -1753,7 +1747,7 @@ YY_RULE_SETUP #line 461 "ael.flex" ECHO; YY_BREAK -#line 1756 "ael_lex.c" +#line 1750 "ael_lex.c" case YY_END_OF_BUFFER: { @@ -1939,7 +1933,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner) else { - int num_to_read = + size_t num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; while ( num_to_read <= 0 ) @@ -2504,10 +2498,10 @@ YY_BUFFER_STATE ael_yy_scan_buffer (char * base, yy_size_t size , yyscan_t yys * @note If you want to scan bytes that may contain NUL values, then use * ael_yy_scan_bytes() instead. */ -YY_BUFFER_STATE ael_yy_scan_string (yyconst char * yy_str , yyscan_t yyscanner) +YY_BUFFER_STATE ael_yy_scan_string (yyconst char * str , yyscan_t yyscanner) { - return ael_yy_scan_bytes(yy_str,strlen(yy_str) ,yyscanner); + return ael_yy_scan_bytes(str,strlen(str) ,yyscanner); } /** Setup the input buffer state to scan the given bytes. The next call to ael_yylex() will diff --git a/res/ael/pval.c b/res/ael/pval.c new file mode 100644 index 0000000000000000000000000000000000000000..d2c37149ec3fe514e3f8db1d5a01bfe1c78210c5 --- /dev/null +++ b/res/ael/pval.c @@ -0,0 +1,5344 @@ + +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2006, Digium, Inc. + * + * Steve Murphy <murf@parsetree.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief Compile symbolic Asterisk Extension Logic into Asterisk extensions, version 2. + * + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include <sys/types.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <regex.h> +#include <sys/stat.h> + +#include "asterisk/pbx.h" +#include "asterisk/config.h" +#include "asterisk/module.h" +#include "asterisk/logger.h" +#include "asterisk/cli.h" +#include "asterisk/app.h" +#include "asterisk/callerid.h" +#include "asterisk/pval.h" +#include "asterisk/ael_structs.h" +#ifdef AAL_ARGCHECK +#include "asterisk/argdesc.h" +#endif + +extern int localized_pbx_load_module(void); + +static char expr_output[2096]; +#define AST_PBX_MAX_STACK 128 + +/* these functions are in ../ast_expr2.fl */ + +static int errs, warns; +static int notes; +#ifdef STANDALONE +static int extensions_dot_conf_loaded = 0; +#endif +static char *registrar = "pbx_ael"; + +static pval *current_db; +static pval *current_context; +static pval *current_extension; + +static const char *match_context; +static const char *match_exten; +static const char *match_label; +static int in_abstract_context; +static int count_labels; /* true, put matcher in label counting mode */ +static int label_count; /* labels are only meant to be counted in a context or exten */ +static int return_on_context_match; +static pval *last_matched_label; +struct pval *match_pval(pval *item); +static void check_timerange(pval *p); +static void check_dow(pval *DOW); +static void check_day(pval *DAY); +static void check_month(pval *MON); +static void check_expr2_input(pval *expr, char *str); +static int extension_matches(pval *here, const char *exten, const char *pattern); +static void check_goto(pval *item); +static void find_pval_goto_item(pval *item, int lev); +static void find_pval_gotos(pval *item, int lev); +static int check_break(pval *item); +static int check_continue(pval *item); +static void check_label(pval *item); +static void check_macro_returns(pval *macro); + +static struct pval *find_label_in_current_context(char *exten, char *label, pval *curr_cont); +static struct pval *find_first_label_in_current_context(char *label, pval *curr_cont); +static void print_pval_list(FILE *fin, pval *item, int depth); + +static struct pval *find_label_in_current_extension(const char *label, pval *curr_ext); +static struct pval *find_label_in_current_db(const char *context, const char *exten, const char *label); +static pval *get_goto_target(pval *item); +static int label_inside_case(pval *label); +static void attach_exten(struct ael_extension **list, struct ael_extension *newmem); +static void fix_gotos_in_extensions(struct ael_extension *exten); +static pval *get_extension_or_contxt(pval *p); +static pval *get_contxt(pval *p); +static void remove_spaces_before_equals(char *str); + +/* PRETTY PRINTER FOR AEL: ============================================================================= */ + +static void print_pval(FILE *fin, pval *item, int depth) +{ + int i; + pval *lp; + + for (i=0; i<depth; i++) { + fprintf(fin, "\t"); /* depth == indentation */ + } + + switch ( item->type ) { + case PV_WORD: + fprintf(fin,"%s;\n", item->u1.str); /* usually, words are encapsulated in something else */ + break; + + case PV_MACRO: + fprintf(fin,"macro %s(", item->u1.str); + for (lp=item->u2.arglist; lp; lp=lp->next) { + if (lp != item->u2.arglist ) + fprintf(fin,", "); + fprintf(fin,"%s", lp->u1.str); + } + fprintf(fin,") {\n"); + print_pval_list(fin,item->u3.macro_statements,depth+1); + for (i=0; i<depth; i++) { + fprintf(fin,"\t"); /* depth == indentation */ + } + fprintf(fin,"};\n\n"); + break; + + case PV_CONTEXT: + if ( item->u3.abstract ) + fprintf(fin,"abstract context %s {\n", item->u1.str); + else + fprintf(fin,"context %s {\n", item->u1.str); + print_pval_list(fin,item->u2.statements,depth+1); + for (i=0; i<depth; i++) { + fprintf(fin,"\t"); /* depth == indentation */ + } + fprintf(fin,"};\n\n"); + break; + + case PV_MACRO_CALL: + fprintf(fin,"&%s(", item->u1.str); + for (lp=item->u2.arglist; lp; lp=lp->next) { + if ( lp != item->u2.arglist ) + fprintf(fin,", "); + fprintf(fin,"%s", lp->u1.str); + } + fprintf(fin,");\n"); + break; + + case PV_APPLICATION_CALL: + fprintf(fin,"%s(", item->u1.str); + for (lp=item->u2.arglist; lp; lp=lp->next) { + if ( lp != item->u2.arglist ) + fprintf(fin,","); + fprintf(fin,"%s", lp->u1.str); + } + fprintf(fin,");\n"); + break; + + case PV_CASE: + fprintf(fin,"case %s:\n", item->u1.str); + print_pval_list(fin,item->u2.statements, depth+1); + break; + + case PV_PATTERN: + fprintf(fin,"pattern %s:\n", item->u1.str); + print_pval_list(fin,item->u2.statements, depth+1); + break; + + case PV_DEFAULT: + fprintf(fin,"default:\n"); + print_pval_list(fin,item->u2.statements, depth+1); + break; + + case PV_CATCH: + fprintf(fin,"catch %s {\n", item->u1.str); + print_pval_list(fin,item->u2.statements, depth+1); + for (i=0; i<depth; i++) { + fprintf(fin,"\t"); /* depth == indentation */ + } + fprintf(fin,"};\n"); + break; + + case PV_SWITCHES: + fprintf(fin,"switches {\n"); + print_pval_list(fin,item->u1.list,depth+1); + for (i=0; i<depth; i++) { + fprintf(fin,"\t"); /* depth == indentation */ + } + fprintf(fin,"};\n"); + break; + + case PV_ESWITCHES: + fprintf(fin,"eswitches {\n"); + print_pval_list(fin,item->u1.list,depth+1); + for (i=0; i<depth; i++) { + fprintf(fin,"\t"); /* depth == indentation */ + } + fprintf(fin,"};\n"); + break; + + case PV_INCLUDES: + fprintf(fin,"includes {\n"); + for (lp=item->u1.list; lp; lp=lp->next) { + for (i=0; i<depth+1; i++) { + fprintf(fin,"\t"); /* depth == indentation */ + } + fprintf(fin,"%s", lp->u1.str); /* usually, words are encapsulated in something else */ + if (lp->u2.arglist) + fprintf(fin,"|%s|%s|%s|%s", + lp->u2.arglist->u1.str, + lp->u2.arglist->next->u1.str, + lp->u2.arglist->next->next->u1.str, + lp->u2.arglist->next->next->next->u1.str + ); + fprintf(fin,";\n"); /* usually, words are encapsulated in something else */ + } + + for (i=0; i<depth; i++) { + fprintf(fin,"\t"); /* depth == indentation */ + } + fprintf(fin,"};\n"); + break; + + case PV_STATEMENTBLOCK: + fprintf(fin,"{\n"); + print_pval_list(fin,item->u1.list, depth+1); + for (i=0; i<depth; i++) { + fprintf(fin,"\t"); /* depth == indentation */ + } + fprintf(fin,"}\n"); + break; + + case PV_VARDEC: + fprintf(fin,"%s=%s;\n", item->u1.str, item->u2.val); + break; + + case PV_LOCALVARDEC: + fprintf(fin,"local %s=%s;\n", item->u1.str, item->u2.val); + break; + + case PV_GOTO: + fprintf(fin,"goto %s", item->u1.list->u1.str); + if ( item->u1.list->next ) + fprintf(fin,",%s", item->u1.list->next->u1.str); + if ( item->u1.list->next && item->u1.list->next->next ) + fprintf(fin,",%s", item->u1.list->next->next->u1.str); + fprintf(fin,"\n"); + break; + + case PV_LABEL: + fprintf(fin,"%s:\n", item->u1.str); + break; + + case PV_FOR: + fprintf(fin,"for (%s; %s; %s)\n", item->u1.for_init, item->u2.for_test, item->u3.for_inc); + print_pval_list(fin,item->u4.for_statements,depth+1); + break; + + case PV_WHILE: + fprintf(fin,"while (%s)\n", item->u1.str); + print_pval_list(fin,item->u2.statements,depth+1); + break; + + case PV_BREAK: + fprintf(fin,"break;\n"); + break; + + case PV_RETURN: + fprintf(fin,"return;\n"); + break; + + case PV_CONTINUE: + fprintf(fin,"continue;\n"); + break; + + case PV_RANDOM: + case PV_IFTIME: + case PV_IF: + if ( item->type == PV_IFTIME ) { + + fprintf(fin,"ifTime ( %s|%s|%s|%s )\n", + item->u1.list->u1.str, + item->u1.list->next->u1.str, + item->u1.list->next->next->u1.str, + item->u1.list->next->next->next->u1.str + ); + } else if ( item->type == PV_RANDOM ) { + fprintf(fin,"random ( %s )\n", item->u1.str ); + } else + fprintf(fin,"if ( %s )\n", item->u1.str); + if ( item->u2.statements && item->u2.statements->next ) { + for (i=0; i<depth; i++) { + fprintf(fin,"\t"); /* depth == indentation */ + } + fprintf(fin,"{\n"); + print_pval_list(fin,item->u2.statements,depth+1); + for (i=0; i<depth; i++) { + fprintf(fin,"\t"); /* depth == indentation */ + } + if ( item->u3.else_statements ) + fprintf(fin,"}\n"); + else + fprintf(fin,"};\n"); + } else if (item->u2.statements ) { + print_pval_list(fin,item->u2.statements,depth+1); + } else { + if (item->u3.else_statements ) + fprintf(fin, " {} "); + else + fprintf(fin, " {}; "); + } + if ( item->u3.else_statements ) { + for (i=0; i<depth; i++) { + fprintf(fin,"\t"); /* depth == indentation */ + } + fprintf(fin,"else\n"); + print_pval_list(fin,item->u3.else_statements, depth); + } + break; + + case PV_SWITCH: + fprintf(fin,"switch( %s ) {\n", item->u1.str); + print_pval_list(fin,item->u2.statements,depth+1); + for (i=0; i<depth; i++) { + fprintf(fin,"\t"); /* depth == indentation */ + } + fprintf(fin,"}\n"); + break; + + case PV_EXTENSION: + if ( item->u4.regexten ) + fprintf(fin, "regexten "); + if ( item->u3.hints ) + fprintf(fin,"hints(%s) ", item->u3.hints); + + fprintf(fin,"%s => \n", item->u1.str); + print_pval_list(fin,item->u2.statements,depth+1); + break; + + case PV_IGNOREPAT: + fprintf(fin,"ignorepat => %s;\n", item->u1.str); + break; + + case PV_GLOBALS: + fprintf(fin,"globals {\n"); + print_pval_list(fin,item->u1.statements,depth+1); + for (i=0; i<depth; i++) { + fprintf(fin,"\t"); /* depth == indentation */ + } + fprintf(fin,"}\n"); + break; + } +} + +static void print_pval_list(FILE *fin, pval *item, int depth) +{ + pval *i; + + for (i=item; i; i=i->next) { + print_pval(fin, i, depth); + } +} + +void ael2_print(char *fname, pval *tree) +{ + FILE *fin = fopen(fname,"w"); + if ( !fin ) { + ast_log(LOG_ERROR, "Couldn't open %s for writing.\n", fname); + return; + } + print_pval_list(fin, tree, 0); + fclose(fin); +} + + +/* EMPTY TEMPLATE FUNCS FOR AEL TRAVERSAL: ============================================================================= */ + +void traverse_pval_template(pval *item, int depth); +void traverse_pval_item_template(pval *item, int depth); + + +void traverse_pval_item_template(pval *item, int depth)/* depth comes in handy for a pretty print (indentation), + but you may not need it */ +{ + pval *lp; + + switch ( item->type ) { + case PV_WORD: + /* fields: item->u1.str == string associated with this (word). */ + break; + + case PV_MACRO: + /* fields: item->u1.str == name of macro + item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user + item->u2.arglist->u1.str == argument + item->u2.arglist->next == next arg + + item->u3.macro_statements == pval list of statements in macro body. + */ + for (lp=item->u2.arglist; lp; lp=lp->next) { + + } + traverse_pval_item_template(item->u3.macro_statements,depth+1); + break; + + case PV_CONTEXT: + /* fields: item->u1.str == name of context + item->u2.statements == pval list of statements in context body + item->u3.abstract == int 1 if an abstract keyword were present + */ + traverse_pval_item_template(item->u2.statements,depth+1); + break; + + case PV_MACRO_CALL: + /* fields: item->u1.str == name of macro to call + item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user + item->u2.arglist->u1.str == argument + item->u2.arglist->next == next arg + */ + for (lp=item->u2.arglist; lp; lp=lp->next) { + } + break; + + case PV_APPLICATION_CALL: + /* fields: item->u1.str == name of application to call + item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user + item->u2.arglist->u1.str == argument + item->u2.arglist->next == next arg + */ + for (lp=item->u2.arglist; lp; lp=lp->next) { + } + break; + + case PV_CASE: + /* fields: item->u1.str == value of case + item->u2.statements == pval list of statements under the case + */ + traverse_pval_item_template(item->u2.statements,depth+1); + break; + + case PV_PATTERN: + /* fields: item->u1.str == value of case + item->u2.statements == pval list of statements under the case + */ + traverse_pval_item_template(item->u2.statements,depth+1); + break; + + case PV_DEFAULT: + /* fields: + item->u2.statements == pval list of statements under the case + */ + traverse_pval_item_template(item->u2.statements,depth+1); + break; + + case PV_CATCH: + /* fields: item->u1.str == name of extension to catch + item->u2.statements == pval list of statements in context body + */ + traverse_pval_item_template(item->u2.statements,depth+1); + break; + + case PV_SWITCHES: + /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list + */ + traverse_pval_item_template(item->u1.list,depth+1); + break; + + case PV_ESWITCHES: + /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list + */ + traverse_pval_item_template(item->u1.list,depth+1); + break; + + case PV_INCLUDES: + /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list + item->u2.arglist == pval list of 4 PV_WORD elements for time values + */ + traverse_pval_item_template(item->u1.list,depth+1); + traverse_pval_item_template(item->u2.arglist,depth+1); + break; + + case PV_STATEMENTBLOCK: + /* fields: item->u1.list == pval list of statements in block, one per entry in the list + */ + traverse_pval_item_template(item->u1.list,depth+1); + break; + + case PV_LOCALVARDEC: + case PV_VARDEC: + /* fields: item->u1.str == variable name + item->u2.val == variable value to assign + */ + break; + + case PV_GOTO: + /* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user. + item->u1.list->u1.str == where the data on a PV_WORD will always be. + */ + + if ( item->u1.list->next ) + ; + if ( item->u1.list->next && item->u1.list->next->next ) + ; + + break; + + case PV_LABEL: + /* fields: item->u1.str == label name + */ + break; + + case PV_FOR: + /* fields: item->u1.for_init == a string containing the initalizer + item->u2.for_test == a string containing the loop test + item->u3.for_inc == a string containing the loop increment + + item->u4.for_statements == a pval list of statements in the for () + */ + traverse_pval_item_template(item->u4.for_statements,depth+1); + break; + + case PV_WHILE: + /* fields: item->u1.str == the while conditional, as supplied by user + + item->u2.statements == a pval list of statements in the while () + */ + traverse_pval_item_template(item->u2.statements,depth+1); + break; + + case PV_BREAK: + /* fields: none + */ + break; + + case PV_RETURN: + /* fields: none + */ + break; + + case PV_CONTINUE: + /* fields: none + */ + break; + + case PV_IFTIME: + /* fields: item->u1.list == there are 4 linked PV_WORDs here. + + item->u2.statements == a pval list of statements in the if () + item->u3.else_statements == a pval list of statements in the else + (could be zero) + */ + traverse_pval_item_template(item->u2.statements,depth+1); + if ( item->u3.else_statements ) { + traverse_pval_item_template(item->u3.else_statements,depth+1); + } + break; + + case PV_RANDOM: + /* fields: item->u1.str == the random number expression, as supplied by user + + item->u2.statements == a pval list of statements in the if () + item->u3.else_statements == a pval list of statements in the else + (could be zero) + */ + traverse_pval_item_template(item->u2.statements,depth+1); + if ( item->u3.else_statements ) { + traverse_pval_item_template(item->u3.else_statements,depth+1); + } + break; + + case PV_IF: + /* fields: item->u1.str == the if conditional, as supplied by user + + item->u2.statements == a pval list of statements in the if () + item->u3.else_statements == a pval list of statements in the else + (could be zero) + */ + traverse_pval_item_template(item->u2.statements,depth+1); + if ( item->u3.else_statements ) { + traverse_pval_item_template(item->u3.else_statements,depth+1); + } + break; + + case PV_SWITCH: + /* fields: item->u1.str == the switch expression + + item->u2.statements == a pval list of statements in the switch, + (will be case statements, most likely!) + */ + traverse_pval_item_template(item->u2.statements,depth+1); + break; + + case PV_EXTENSION: + /* fields: item->u1.str == the extension name, label, whatever it's called + + item->u2.statements == a pval list of statements in the extension + item->u3.hints == a char * hint argument + item->u4.regexten == an int boolean. non-zero says that regexten was specified + */ + traverse_pval_item_template(item->u2.statements,depth+1); + break; + + case PV_IGNOREPAT: + /* fields: item->u1.str == the ignorepat data + */ + break; + + case PV_GLOBALS: + /* fields: item->u1.statements == pval list of statements, usually vardecs + */ + traverse_pval_item_template(item->u1.statements,depth+1); + break; + } +} + +void traverse_pval_template(pval *item, int depth) /* depth comes in handy for a pretty print (indentation), + but you may not need it */ +{ + pval *i; + + for (i=item; i; i=i->next) { + traverse_pval_item_template(i, depth); + } +} + + +/* SEMANTIC CHECKING FOR AEL: ============================================================================= */ + +/* (not all that is syntactically legal is good! */ + + +static void check_macro_returns(pval *macro) +{ + pval *i; + if (!macro->u3.macro_statements) + { + pval *z = calloc(1, sizeof(struct pval)); + ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The macro %s is empty! I will insert a return.\n", + macro->filename, macro->startline, macro->endline, macro->u1.str); + + z->type = PV_RETURN; + z->startline = macro->startline; + z->endline = macro->endline; + z->startcol = macro->startcol; + z->endcol = macro->endcol; + z->filename = strdup(macro->filename); + + macro->u3.macro_statements = z; + return; + } + for (i=macro->u3.macro_statements; i; i=i->next) { + /* if the last statement in the list is not return, then insert a return there */ + if (i->next == NULL) { + if (i->type != PV_RETURN) { + pval *z = calloc(1, sizeof(struct pval)); + ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The macro %s does not end with a return; I will insert one.\n", + macro->filename, macro->startline, macro->endline, macro->u1.str); + + z->type = PV_RETURN; + z->startline = macro->startline; + z->endline = macro->endline; + z->startcol = macro->startcol; + z->endcol = macro->endcol; + z->filename = strdup(macro->filename); + + i->next = z; + return; + } + } + } + return; +} + + + +static int extension_matches(pval *here, const char *exten, const char *pattern) +{ + int err1; + regex_t preg; + + /* simple case, they match exactly, the pattern and exten name */ + if (!strcmp(pattern,exten) == 0) + return 1; + + if (pattern[0] == '_') { + char reg1[2000]; + const char *p; + char *r = reg1; + + if ( strlen(pattern)*5 >= 2000 ) /* safety valve */ { + ast_log(LOG_ERROR,"Error: The pattern %s is way too big. Pattern matching cancelled.\n", + pattern); + return 0; + } + /* form a regular expression from the pattern, and then match it against exten */ + *r++ = '^'; /* what if the extension is a pattern ?? */ + *r++ = '_'; /* what if the extension is a pattern ?? */ + *r++ = '?'; + for (p=pattern+1; *p; p++) { + switch ( *p ) { + case 'X': + *r++ = '['; + *r++ = '0'; + *r++ = '-'; + *r++ = '9'; + *r++ = 'X'; + *r++ = ']'; + break; + + case 'Z': + *r++ = '['; + *r++ = '1'; + *r++ = '-'; + *r++ = '9'; + *r++ = 'Z'; + *r++ = ']'; + break; + + case 'N': + *r++ = '['; + *r++ = '2'; + *r++ = '-'; + *r++ = '9'; + *r++ = 'N'; + *r++ = ']'; + break; + + case '[': + while ( *p && *p != ']' ) { + *r++ = *p++; + } + if ( *p != ']') { + ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The extension pattern '%s' is missing a closing bracket \n", + here->filename, here->startline, here->endline, pattern); + } + break; + + case '.': + case '!': + *r++ = '.'; + *r++ = '*'; + break; + case '*': + *r++ = '\\'; + *r++ = '*'; + break; + default: + *r++ = *p; + break; + + } + } + *r++ = '$'; /* what if the extension is a pattern ?? */ + *r++ = *p++; /* put in the closing null */ + err1 = regcomp(&preg, reg1, REG_NOSUB|REG_EXTENDED); + if ( err1 ) { + char errmess[500]; + regerror(err1,&preg,errmess,sizeof(errmess)); + regfree(&preg); + ast_log(LOG_WARNING, "Regcomp of %s failed, error code %d\n", + reg1, err1); + return 0; + } + err1 = regexec(&preg, exten, 0, 0, 0); + regfree(&preg); + + if ( err1 ) { + /* ast_log(LOG_NOTICE,"*****************************[%d]Extension %s did not match %s(%s)\n", + err1,exten, pattern, reg1); */ + return 0; /* no match */ + } else { + /* ast_log(LOG_NOTICE,"*****************************Extension %s matched %s\n", + exten, pattern); */ + return 1; + } + + + } else { + if ( strcmp(exten,pattern) == 0 ) { + return 1; + } else + return 0; + } +} + + +static void check_expr2_input(pval *expr, char *str) +{ + int spaces = strspn(str,"\t \n"); + if ( !strncmp(str+spaces,"$[",2) ) { + ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The expression '%s' is redundantly wrapped in '$[ ]'. \n", + expr->filename, expr->startline, expr->endline, str); + warns++; + } +} + +static void check_includes(pval *includes) +{ + struct pval *p4; + for (p4=includes->u1.list; p4; p4=p4->next) { + /* for each context pointed to, find it, then find a context/label that matches the + target here! */ + char *incl_context = p4->u1.str; + /* find a matching context name */ + struct pval *that_other_context = find_context(incl_context); + if (!that_other_context && strcmp(incl_context, "parkedcalls") != 0) { + ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The included context '%s' cannot be found.\n", + includes->filename, includes->startline, includes->endline, incl_context); + warns++; + } + } +} + + +static void check_timerange(pval *p) +{ + char *times; + char *e; + int s1, s2; + int e1, e2; + + times = ast_strdupa(p->u1.str); + + /* Star is all times */ + if (ast_strlen_zero(times) || !strcmp(times, "*")) { + return; + } + /* Otherwise expect a range */ + e = strchr(times, '-'); + if (!e) { + ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The time range format (%s) requires a '-' surrounded by two 24-hour times of day!\n", + p->filename, p->startline, p->endline, times); + warns++; + return; + } + *e = '\0'; + e++; + while (*e && !isdigit(*e)) + e++; + if (!*e) { + ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The time range format (%s) is missing the end time!\n", + p->filename, p->startline, p->endline, p->u1.str); + warns++; + } + if (sscanf(times, "%d:%d", &s1, &s2) != 2) { + ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start time (%s) isn't quite right!\n", + p->filename, p->startline, p->endline, times); + warns++; + } + if (sscanf(e, "%d:%d", &e1, &e2) != 2) { + ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end time (%s) isn't quite right!\n", + p->filename, p->startline, p->endline, times); + warns++; + } + + s1 = s1 * 30 + s2/2; + if ((s1 < 0) || (s1 >= 24*30)) { + ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start time (%s) is out of range!\n", + p->filename, p->startline, p->endline, times); + warns++; + } + e1 = e1 * 30 + e2/2; + if ((e1 < 0) || (e1 >= 24*30)) { + ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end time (%s) is out of range!\n", + p->filename, p->startline, p->endline, e); + warns++; + } + return; +} + +static char *days[] = +{ + "sun", + "mon", + "tue", + "wed", + "thu", + "fri", + "sat", +}; + +/*! \brief get_dow: Get day of week */ +static void check_dow(pval *DOW) +{ + char *dow; + char *c; + /* The following line is coincidence, really! */ + int s, e; + + dow = ast_strdupa(DOW->u1.str); + + /* Check for all days */ + if (ast_strlen_zero(dow) || !strcmp(dow, "*")) + return; + /* Get start and ending days */ + c = strchr(dow, '-'); + if (c) { + *c = '\0'; + c++; + } else + c = NULL; + /* Find the start */ + s = 0; + while ((s < 7) && strcasecmp(dow, days[s])) s++; + if (s >= 7) { + ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The day (%s) must be one of 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', or 'sat'!\n", + DOW->filename, DOW->startline, DOW->endline, dow); + warns++; + } + if (c) { + e = 0; + while ((e < 7) && strcasecmp(c, days[e])) e++; + if (e >= 7) { + ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end day (%s) must be one of 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', or 'sat'!\n", + DOW->filename, DOW->startline, DOW->endline, c); + warns++; + } + } else + e = s; +} + +static void check_day(pval *DAY) +{ + char *day; + char *c; + /* The following line is coincidence, really! */ + int s, e; + + day = ast_strdupa(DAY->u1.str); + + /* Check for all days */ + if (ast_strlen_zero(day) || !strcmp(day, "*")) { + return; + } + /* Get start and ending days */ + c = strchr(day, '-'); + if (c) { + *c = '\0'; + c++; + } + /* Find the start */ + if (sscanf(day, "%d", &s) != 1) { + ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start day of month (%s) must be a number!\n", + DAY->filename, DAY->startline, DAY->endline, day); + warns++; + } + else if ((s < 1) || (s > 31)) { + ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start day of month (%s) must be a number in the range [1-31]!\n", + DAY->filename, DAY->startline, DAY->endline, day); + warns++; + } + s--; + if (c) { + if (sscanf(c, "%d", &e) != 1) { + ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end day of month (%s) must be a number!\n", + DAY->filename, DAY->startline, DAY->endline, c); + warns++; + } + else if ((e < 1) || (e > 31)) { + ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end day of month (%s) must be a number in the range [1-31]!\n", + DAY->filename, DAY->startline, DAY->endline, day); + warns++; + } + e--; + } else + e = s; +} + +static char *months[] = +{ + "jan", + "feb", + "mar", + "apr", + "may", + "jun", + "jul", + "aug", + "sep", + "oct", + "nov", + "dec", +}; + +static void check_month(pval *MON) +{ + char *mon; + char *c; + /* The following line is coincidence, really! */ + int s, e; + + mon = ast_strdupa(MON->u1.str); + + /* Check for all days */ + if (ast_strlen_zero(mon) || !strcmp(mon, "*")) + return ; + /* Get start and ending days */ + c = strchr(mon, '-'); + if (c) { + *c = '\0'; + c++; + } + /* Find the start */ + s = 0; + while ((s < 12) && strcasecmp(mon, months[s])) s++; + if (s >= 12) { + ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start month (%s) must be a one of: 'jan', 'feb', ..., 'dec'!\n", + MON->filename, MON->startline, MON->endline, mon); + warns++; + } + if (c) { + e = 0; + while ((e < 12) && strcasecmp(mon, months[e])) e++; + if (e >= 12) { + ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end month (%s) must be a one of: 'jan', 'feb', ..., 'dec'!\n", + MON->filename, MON->startline, MON->endline, c); + warns++; + } + } else + e = s; +} + +static int check_break(pval *item) +{ + pval *p = item; + + while( p && p->type != PV_MACRO && p->type != PV_CONTEXT ) /* early cutout, sort of */ { + /* a break is allowed in WHILE, FOR, CASE, DEFAULT, PATTERN; otherwise, it don't make + no sense */ + if( p->type == PV_CASE || p->type == PV_DEFAULT || p->type == PV_PATTERN + || p->type == PV_WHILE || p->type == PV_FOR ) { + return 1; + } + p = p->dad; + } + ast_log(LOG_ERROR,"Error: file %s, line %d-%d: 'break' not in switch, for, or while statement!\n", + item->filename, item->startline, item->endline); + errs++; + + return 0; +} + +static int check_continue(pval *item) +{ + pval *p = item; + + while( p && p->type != PV_MACRO && p->type != PV_CONTEXT ) /* early cutout, sort of */ { + /* a break is allowed in WHILE, FOR, CASE, DEFAULT, PATTERN; otherwise, it don't make + no sense */ + if( p->type == PV_WHILE || p->type == PV_FOR ) { + return 1; + } + p = p->dad; + } + ast_log(LOG_ERROR,"Error: file %s, line %d-%d: 'continue' not in 'for' or 'while' statement!\n", + item->filename, item->startline, item->endline); + errs++; + + return 0; +} + +static struct pval *in_macro(pval *item) +{ + struct pval *curr; + curr = item; + while( curr ) { + if( curr->type == PV_MACRO ) { + return curr; + } + curr = curr->dad; + } + return 0; +} + +static struct pval *in_context(pval *item) +{ + struct pval *curr; + curr = item; + while( curr ) { + if( curr->type == PV_MACRO || curr->type == PV_CONTEXT ) { + return curr; + } + curr = curr->dad; + } + return 0; +} + + +/* general purpose goto finder */ + +static void check_label(pval *item) +{ + struct pval *curr; + struct pval *x; + int alright = 0; + + /* A label outside an extension just plain does not make sense! */ + + curr = item; + + while( curr ) { + if( curr->type == PV_MACRO || curr->type == PV_EXTENSION ) { + alright = 1; + break; + } + curr = curr->dad; + } + if( !alright ) + { + ast_log(LOG_ERROR,"Error: file %s, line %d-%d: Label %s is not within an extension or macro!\n", + item->filename, item->startline, item->endline, item->u1.str); + errs++; + } + + + /* basically, ensure that a label is not repeated in a context. Period. + The method: well, for each label, find the first label in the context + with the same name. If it's not the current label, then throw an error. */ + + + /* printf("==== check_label: ====\n"); */ + if( !current_extension ) + curr = current_context; + else + curr = current_extension; + + x = find_first_label_in_current_context((char *)item->u1.str, curr); + /* printf("Hey, check_label found with item = %x, and x is %x, and currcont is %x, label name is %s\n", item,x, current_context, (char *)item->u1.str); */ + if( x && x != item ) + { + ast_log(LOG_ERROR,"Error: file %s, line %d-%d: Duplicate label %s! Previously defined at file %s, line %d.\n", + item->filename, item->startline, item->endline, item->u1.str, x->filename, x->startline); + errs++; + } + /* printf("<<<<< check_label: ====\n"); */ +} + +static pval *get_goto_target(pval *item) +{ + /* just one item-- the label should be in the current extension */ + pval *curr_ext = get_extension_or_contxt(item); /* containing exten, or macro */ + pval *curr_cont; + + if (item->u1.list && !item->u1.list->next && !strstr((item->u1.list)->u1.str,"${")) { + struct pval *x = find_label_in_current_extension((char*)((item->u1.list)->u1.str), curr_ext); + return x; + } + + curr_cont = get_contxt(item); + + /* TWO items */ + if (item->u1.list->next && !item->u1.list->next->next) { + if (!strstr((item->u1.list)->u1.str,"${") + && !strstr(item->u1.list->next->u1.str,"${") ) /* Don't try to match variables */ { + struct pval *x = find_label_in_current_context((char *)item->u1.list->u1.str, (char *)item->u1.list->next->u1.str, curr_cont); + return x; + } + } + + /* All 3 items! */ + if (item->u1.list->next && item->u1.list->next->next) { + /* all three */ + pval *first = item->u1.list; + pval *second = item->u1.list->next; + pval *third = item->u1.list->next->next; + + if (!strstr((item->u1.list)->u1.str,"${") + && !strstr(item->u1.list->next->u1.str,"${") + && !strstr(item->u1.list->next->next->u1.str,"${")) /* Don't try to match variables */ { + struct pval *x = find_label_in_current_db((char*)first->u1.str, (char*)second->u1.str, (char*)third->u1.str); + if (!x) { + + struct pval *p3; + struct pval *that_context = find_context(item->u1.list->u1.str); + + /* the target of the goto could be in an included context!! Fancy that!! */ + /* look for includes in the current context */ + if (that_context) { + for (p3=that_context->u2.statements; p3; p3=p3->next) { + if (p3->type == PV_INCLUDES) { + struct pval *p4; + for (p4=p3->u1.list; p4; p4=p4->next) { + /* for each context pointed to, find it, then find a context/label that matches the + target here! */ + char *incl_context = p4->u1.str; + /* find a matching context name */ + struct pval *that_other_context = find_context(incl_context); + if (that_other_context) { + struct pval *x3; + x3 = find_label_in_current_context((char *)item->u1.list->next->u1.str, (char *)item->u1.list->next->next->u1.str, that_other_context); + if (x3) { + return x3; + } + } + } + } + } + } + } + return x; + } + } + return 0; +} + +static void check_goto(pval *item) +{ + /* check for the target of the goto-- does it exist? */ + if ( !(item->u1.list)->next && !(item->u1.list)->u1.str ) { + ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto: empty label reference found!\n", + item->filename, item->startline, item->endline); + errs++; + } + + /* just one item-- the label should be in the current extension */ + + if (item->u1.list && !item->u1.list->next && !strstr((item->u1.list)->u1.str,"${")) { + struct pval *z = get_extension_or_contxt(item); + struct pval *x = 0; + if (z) + x = find_label_in_current_extension((char*)((item->u1.list)->u1.str), z); /* if in macro, use current context instead */ + /* printf("Called find_label_in_current_extension with arg %s; current_extension is %x: %d\n", + (char*)((item->u1.list)->u1.str), current_extension?current_extension:current_context, current_extension?current_extension->type:current_context->type); */ + if (!x) { + ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto: no label %s exists in the current extension!\n", + item->filename, item->startline, item->endline, item->u1.list->u1.str); + errs++; + } + else + return; + } + + /* TWO items */ + if (item->u1.list->next && !item->u1.list->next->next) { + /* two items */ + /* printf("Calling find_label_in_current_context with args %s, %s\n", + (char*)((item->u1.list)->u1.str), (char *)item->u1.list->next->u1.str); */ + if (!strstr((item->u1.list)->u1.str,"${") + && !strstr(item->u1.list->next->u1.str,"${") ) /* Don't try to match variables */ { + struct pval *z = get_contxt(item); + struct pval *x = 0; + + if (z) + x = find_label_in_current_context((char *)item->u1.list->u1.str, (char *)item->u1.list->next->u1.str, z); + + if (!x) { + ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto: no label '%s,%s' exists in the current context, or any of its inclusions!\n", + item->filename, item->startline, item->endline, item->u1.list->u1.str, item->u1.list->next->u1.str ); + errs++; + } + else + return; + } + } + + /* All 3 items! */ + if (item->u1.list->next && item->u1.list->next->next) { + /* all three */ + pval *first = item->u1.list; + pval *second = item->u1.list->next; + pval *third = item->u1.list->next->next; + + /* printf("Calling find_label_in_current_db with args %s, %s, %s\n", + (char*)first->u1.str, (char*)second->u1.str, (char*)third->u1.str); */ + if (!strstr((item->u1.list)->u1.str,"${") + && !strstr(item->u1.list->next->u1.str,"${") + && !strstr(item->u1.list->next->next->u1.str,"${")) /* Don't try to match variables */ { + struct pval *x = find_label_in_current_db((char*)first->u1.str, (char*)second->u1.str, (char*)third->u1.str); + if (!x) { + struct pval *p3; + struct pval *found = 0; + struct pval *that_context = find_context(item->u1.list->u1.str); + + /* the target of the goto could be in an included context!! Fancy that!! */ + /* look for includes in the current context */ + if (that_context) { + for (p3=that_context->u2.statements; p3; p3=p3->next) { + if (p3->type == PV_INCLUDES) { + struct pval *p4; + for (p4=p3->u1.list; p4; p4=p4->next) { + /* for each context pointed to, find it, then find a context/label that matches the + target here! */ + char *incl_context = p4->u1.str; + /* find a matching context name */ + struct pval *that_other_context = find_context(incl_context); + if (that_other_context) { + struct pval *x3; + x3 = find_label_in_current_context((char *)item->u1.list->next->u1.str, (char *)item->u1.list->next->next->u1.str, that_other_context); + if (x3) { + found = x3; + break; + } + } + } + } + } + if (!found) { + ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto: no label %s|%s exists in the context %s or its inclusions!\n", + item->filename, item->startline, item->endline, item->u1.list->next->u1.str, item->u1.list->next->next->u1.str, item->u1.list->u1.str ); + errs++; + } else { + struct pval *mac = in_macro(item); /* is this goto inside a macro? */ + if( mac ) { /* yes! */ + struct pval *targ = in_context(found); + if( mac != targ ) + { + ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: It's bad form to have a goto in a macro to a target outside the macro!\n", + item->filename, item->startline, item->endline); + warns++; + } + } + } + } else { + /* here is where code would go to check for target existence in extensions.conf files */ +#ifdef STANDALONE + struct pbx_find_info pfiq = {.stacklen = 0 }; + extern int localized_pbx_load_module(void); + /* if this is a standalone, we will need to make sure the + localized load of extensions.conf is done */ + if (!extensions_dot_conf_loaded) { + localized_pbx_load_module(); + extensions_dot_conf_loaded++; + } + + pbx_find_extension(NULL, NULL, &pfiq, first->u1.str, second->u1.str, atoi(third->u1.str), + atoi(third->u1.str) ? NULL : third->u1.str, NULL, + atoi(third->u1.str) ? E_MATCH : E_FINDLABEL); + + if (pfiq.status != STATUS_SUCCESS) { + ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: goto: Couldn't find goto target %s|%s|%s, not even in extensions.conf!\n", + item->filename, item->startline, item->endline, first->u1.str, second->u1.str, third->u1.str); + warns++; + } +#else + ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: goto: Couldn't find goto target %s|%s|%s in the AEL code!\n", + item->filename, item->startline, item->endline, first->u1.str, second->u1.str, third->u1.str); + warns++; +#endif + } + } else { + struct pval *mac = in_macro(item); /* is this goto inside a macro? */ + if( mac ) { /* yes! */ + struct pval *targ = in_context(x); + if( mac != targ ) + { + ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: It's bad form to have a goto in a macro to a target outside the macro!\n", + item->filename, item->startline, item->endline); + warns++; + } + } + } + } + } +} + + +static void find_pval_goto_item(pval *item, int lev) +{ + struct pval *p4; + if (lev>100) { + ast_log(LOG_ERROR,"find_pval_goto in infinite loop!\n\n"); + return; + } + + switch ( item->type ) { + case PV_MACRO: + /* fields: item->u1.str == name of macro + item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user + item->u2.arglist->u1.str == argument + item->u2.arglist->next == next arg + + item->u3.macro_statements == pval list of statements in macro body. + */ + + /* printf("Descending into matching macro %s\n", match_context); */ + find_pval_gotos(item->u3.macro_statements,lev+1); /* if we're just searching for a context, don't bother descending into them */ + + break; + + case PV_CONTEXT: + /* fields: item->u1.str == name of context + item->u2.statements == pval list of statements in context body + item->u3.abstract == int 1 if an abstract keyword were present + */ + break; + + case PV_CASE: + /* fields: item->u1.str == value of case + item->u2.statements == pval list of statements under the case + */ + find_pval_gotos(item->u2.statements,lev+1); + break; + + case PV_PATTERN: + /* fields: item->u1.str == value of case + item->u2.statements == pval list of statements under the case + */ + find_pval_gotos(item->u2.statements,lev+1); + break; + + case PV_DEFAULT: + /* fields: + item->u2.statements == pval list of statements under the case + */ + find_pval_gotos(item->u2.statements,lev+1); + break; + + case PV_CATCH: + /* fields: item->u1.str == name of extension to catch + item->u2.statements == pval list of statements in context body + */ + find_pval_gotos(item->u2.statements,lev+1); + break; + + case PV_STATEMENTBLOCK: + /* fields: item->u1.list == pval list of statements in block, one per entry in the list + */ + find_pval_gotos(item->u1.list,lev+1); + break; + + case PV_GOTO: + /* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user. + item->u1.list->u1.str == where the data on a PV_WORD will always be. + */ + check_goto(item); /* THE WHOLE FUNCTION OF THIS ENTIRE ROUTINE!!!! */ + break; + + case PV_INCLUDES: + /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list + */ + for (p4=item->u1.list; p4; p4=p4->next) { + /* for each context pointed to, find it, then find a context/label that matches the + target here! */ + char *incl_context = p4->u1.str; + /* find a matching context name */ + struct pval *that_context = find_context(incl_context); + if (that_context) { + find_pval_gotos(that_context,lev+1); /* keep working up the includes */ + } + } + break; + + case PV_FOR: + /* fields: item->u1.for_init == a string containing the initalizer + item->u2.for_test == a string containing the loop test + item->u3.for_inc == a string containing the loop increment + + item->u4.for_statements == a pval list of statements in the for () + */ + find_pval_gotos(item->u4.for_statements,lev+1); + break; + + case PV_WHILE: + /* fields: item->u1.str == the while conditional, as supplied by user + + item->u2.statements == a pval list of statements in the while () + */ + find_pval_gotos(item->u2.statements,lev+1); + break; + + case PV_RANDOM: + /* fields: item->u1.str == the random number expression, as supplied by user + + item->u2.statements == a pval list of statements in the if () + item->u3.else_statements == a pval list of statements in the else + (could be zero) + fall thru to PV_IF */ + + case PV_IFTIME: + /* fields: item->u1.list == the time values, 4 of them, as PV_WORD structs in a list + + item->u2.statements == a pval list of statements in the if () + item->u3.else_statements == a pval list of statements in the else + (could be zero) + fall thru to PV_IF*/ + case PV_IF: + /* fields: item->u1.str == the if conditional, as supplied by user + + item->u2.statements == a pval list of statements in the if () + item->u3.else_statements == a pval list of statements in the else + (could be zero) + */ + find_pval_gotos(item->u2.statements,lev+1); + + if (item->u3.else_statements) { + find_pval_gotos(item->u3.else_statements,lev+1); + } + break; + + case PV_SWITCH: + /* fields: item->u1.str == the switch expression + + item->u2.statements == a pval list of statements in the switch, + (will be case statements, most likely!) + */ + find_pval_gotos(item->u3.else_statements,lev+1); + break; + + case PV_EXTENSION: + /* fields: item->u1.str == the extension name, label, whatever it's called + + item->u2.statements == a pval list of statements in the extension + item->u3.hints == a char * hint argument + item->u4.regexten == an int boolean. non-zero says that regexten was specified + */ + + find_pval_gotos(item->u2.statements,lev+1); + break; + + default: + break; + } +} + +static void find_pval_gotos(pval *item,int lev) +{ + pval *i; + + for (i=item; i; i=i->next) { + + find_pval_goto_item(i, lev); + } +} + + + +/* general purpose label finder */ +static struct pval *match_pval_item(pval *item) +{ + pval *x; + + switch ( item->type ) { + case PV_MACRO: + /* fields: item->u1.str == name of macro + item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user + item->u2.arglist->u1.str == argument + item->u2.arglist->next == next arg + + item->u3.macro_statements == pval list of statements in macro body. + */ + /* printf(" matching in MACRO %s, match_context=%s; retoncontmtch=%d; \n", item->u1.str, match_context, return_on_context_match); */ + if (!strcmp(match_context,"*") || !strcmp(item->u1.str, match_context)) { + + /* printf("MACRO: match context is: %s\n", match_context); */ + + if (return_on_context_match && !strcmp(item->u1.str, match_context)) /* if we're just searching for a context, don't bother descending into them */ { + /* printf("Returning on matching macro %s\n", match_context); */ + return item; + } + + + if (!return_on_context_match) { + /* printf("Descending into matching macro %s/%s\n", match_context, item->u1.str); */ + if ((x=match_pval(item->u3.macro_statements))) { + /* printf("Responded with pval match %x\n", x); */ + return x; + } + } + } else { + /* printf("Skipping context/macro %s\n", item->u1.str); */ + } + + break; + + case PV_CONTEXT: + /* fields: item->u1.str == name of context + item->u2.statements == pval list of statements in context body + item->u3.abstract == int 1 if an abstract keyword were present + */ + /* printf(" matching in CONTEXT\n"); */ + if (!strcmp(match_context,"*") || !strcmp(item->u1.str, match_context)) { + if (return_on_context_match && !strcmp(item->u1.str, match_context)) { + /* printf("Returning on matching context %s\n", match_context); */ + /* printf("non-CONTEXT: Responded with pval match %x\n", x); */ + return item; + } + + if (!return_on_context_match ) { + /* printf("Descending into matching context %s\n", match_context); */ + if ((x=match_pval(item->u2.statements))) /* if we're just searching for a context, don't bother descending into them */ { + /* printf("CONTEXT: Responded with pval match %x\n", x); */ + return x; + } + } + } else { + /* printf("Skipping context/macro %s\n", item->u1.str); */ + } + break; + + case PV_CASE: + /* fields: item->u1.str == value of case + item->u2.statements == pval list of statements under the case + */ + /* printf(" matching in CASE\n"); */ + if ((x=match_pval(item->u2.statements))) { + /* printf("CASE: Responded with pval match %x\n", x); */ + return x; + } + break; + + case PV_PATTERN: + /* fields: item->u1.str == value of case + item->u2.statements == pval list of statements under the case + */ + /* printf(" matching in PATTERN\n"); */ + if ((x=match_pval(item->u2.statements))) { + /* printf("PATTERN: Responded with pval match %x\n", x); */ + return x; + } + break; + + case PV_DEFAULT: + /* fields: + item->u2.statements == pval list of statements under the case + */ + /* printf(" matching in DEFAULT\n"); */ + if ((x=match_pval(item->u2.statements))) { + /* printf("DEFAULT: Responded with pval match %x\n", x); */ + return x; + } + break; + + case PV_CATCH: + /* fields: item->u1.str == name of extension to catch + item->u2.statements == pval list of statements in context body + */ + /* printf(" matching in CATCH\n"); */ + if ((x=match_pval(item->u2.statements))) { + /* printf("CATCH: Responded with pval match %x\n", x); */ + return x; + } + break; + + case PV_STATEMENTBLOCK: + /* fields: item->u1.list == pval list of statements in block, one per entry in the list + */ + /* printf(" matching in STATEMENTBLOCK\n"); */ + if ((x=match_pval(item->u1.list))) { + /* printf("STATEMENTBLOCK: Responded with pval match %x\n", x); */ + return x; + } + break; + + case PV_LABEL: + /* fields: item->u1.str == label name + */ + /* printf("PV_LABEL %s (cont=%s, exten=%s\n", + item->u1.str, current_context->u1.str, (current_extension?current_extension->u1.str:"<macro>"));*/ + + if (count_labels) { + if (!strcmp(match_label, item->u1.str)) { + label_count++; + last_matched_label = item; + } + + } else { + if (!strcmp(match_label, item->u1.str)) { + /* printf("LABEL: Responded with pval match %x\n", x); */ + return item; + } + } + break; + + case PV_FOR: + /* fields: item->u1.for_init == a string containing the initalizer + item->u2.for_test == a string containing the loop test + item->u3.for_inc == a string containing the loop increment + + item->u4.for_statements == a pval list of statements in the for () + */ + /* printf(" matching in FOR\n"); */ + if ((x=match_pval(item->u4.for_statements))) { + /* printf("FOR: Responded with pval match %x\n", x);*/ + return x; + } + break; + + case PV_WHILE: + /* fields: item->u1.str == the while conditional, as supplied by user + + item->u2.statements == a pval list of statements in the while () + */ + /* printf(" matching in WHILE\n"); */ + if ((x=match_pval(item->u2.statements))) { + /* printf("WHILE: Responded with pval match %x\n", x); */ + return x; + } + break; + + case PV_RANDOM: + /* fields: item->u1.str == the random number expression, as supplied by user + + item->u2.statements == a pval list of statements in the if () + item->u3.else_statements == a pval list of statements in the else + (could be zero) + fall thru to PV_IF */ + + case PV_IFTIME: + /* fields: item->u1.list == the time values, 4 of them, as PV_WORD structs in a list + + item->u2.statements == a pval list of statements in the if () + item->u3.else_statements == a pval list of statements in the else + (could be zero) + fall thru to PV_IF*/ + case PV_IF: + /* fields: item->u1.str == the if conditional, as supplied by user + + item->u2.statements == a pval list of statements in the if () + item->u3.else_statements == a pval list of statements in the else + (could be zero) + */ + /* printf(" matching in IF/IFTIME/RANDOM\n"); */ + if ((x=match_pval(item->u2.statements))) { + return x; + } + if (item->u3.else_statements) { + if ((x=match_pval(item->u3.else_statements))) { + /* printf("IF/IFTIME/RANDOM: Responded with pval match %x\n", x); */ + return x; + } + } + break; + + case PV_SWITCH: + /* fields: item->u1.str == the switch expression + + item->u2.statements == a pval list of statements in the switch, + (will be case statements, most likely!) + */ + /* printf(" matching in SWITCH\n"); */ + if ((x=match_pval(item->u2.statements))) { + /* printf("SWITCH: Responded with pval match %x\n", x); */ + return x; + } + break; + + case PV_EXTENSION: + /* fields: item->u1.str == the extension name, label, whatever it's called + + item->u2.statements == a pval list of statements in the extension + item->u3.hints == a char * hint argument + item->u4.regexten == an int boolean. non-zero says that regexten was specified + */ + /* printf(" matching in EXTENSION\n"); */ + if (!strcmp(match_exten,"*") || extension_matches(item, match_exten, item->u1.str) ) { + /* printf("Descending into matching exten %s => %s\n", match_exten, item->u1.str); */ + if (strcmp(match_label,"1") == 0) { + if (item->u2.statements) { + struct pval *p5 = item->u2.statements; + while (p5 && p5->type == PV_LABEL) /* find the first non-label statement in this context. If it exists, there's a "1" */ + p5 = p5->next; + if (p5) + return p5; + else + return 0; + } + else + return 0; + } + + if ((x=match_pval(item->u2.statements))) { + /* printf("EXTENSION: Responded with pval match %x\n", x); */ + return x; + } + } else { + /* printf("Skipping exten %s\n", item->u1.str); */ + } + break; + default: + /* printf(" matching in default = %d\n", item->type); */ + break; + } + return 0; +} + +struct pval *match_pval(pval *item) +{ + pval *i; + + for (i=item; i; i=i->next) { + pval *x; + /* printf(" -- match pval: item %d\n", i->type); */ + + if ((x = match_pval_item(i))) { + /* printf("match_pval: returning x=%x\n", (int)x); */ + return x; /* cut the search short */ + } + } + return 0; +} + +#if 0 +int count_labels_in_current_context(char *label) +{ + label_count = 0; + count_labels = 1; + return_on_context_match = 0; + match_pval(current_context->u2.statements); + + return label_count; +} +#endif + +struct pval *find_first_label_in_current_context(char *label, pval *curr_cont) +{ + /* printf(" --- Got args %s, %s\n", exten, label); */ + struct pval *ret; + struct pval *p3; + + count_labels = 0; + return_on_context_match = 0; + match_context = "*"; + match_exten = "*"; + match_label = label; + + ret = match_pval(curr_cont); + if (ret) + return ret; + + /* the target of the goto could be in an included context!! Fancy that!! */ + /* look for includes in the current context */ + for (p3=curr_cont->u2.statements; p3; p3=p3->next) { + if (p3->type == PV_INCLUDES) { + struct pval *p4; + for (p4=p3->u1.list; p4; p4=p4->next) { + /* for each context pointed to, find it, then find a context/label that matches the + target here! */ + char *incl_context = p4->u1.str; + /* find a matching context name */ + struct pval *that_context = find_context(incl_context); + if (that_context) { + struct pval *x3; + x3 = find_first_label_in_current_context(label, that_context); + if (x3) { + return x3; + } + } + } + } + } + return 0; +} + +struct pval *find_label_in_current_context(char *exten, char *label, pval *curr_cont) +{ + /* printf(" --- Got args %s, %s\n", exten, label); */ + struct pval *ret; + struct pval *p3; + + count_labels = 0; + return_on_context_match = 0; + match_context = "*"; + match_exten = exten; + match_label = label; + ret = match_pval(curr_cont->u2.statements); + if (ret) + return ret; + + /* the target of the goto could be in an included context!! Fancy that!! */ + /* look for includes in the current context */ + for (p3=curr_cont->u2.statements; p3; p3=p3->next) { + if (p3->type == PV_INCLUDES) { + struct pval *p4; + for (p4=p3->u1.list; p4; p4=p4->next) { + /* for each context pointed to, find it, then find a context/label that matches the + target here! */ + char *incl_context = p4->u1.str; + /* find a matching context name */ + struct pval *that_context = find_context(incl_context); + if (that_context) { + struct pval *x3; + x3 = find_label_in_current_context(exten, label, that_context); + if (x3) { + return x3; + } + } + } + } + } + return 0; +} + +static struct pval *find_label_in_current_extension(const char *label, pval *curr_ext) +{ + /* printf(" --- Got args %s\n", label); */ + count_labels = 0; + return_on_context_match = 0; + match_context = "*"; + match_exten = "*"; + match_label = label; + return match_pval(curr_ext); +} + +static struct pval *find_label_in_current_db(const char *context, const char *exten, const char *label) +{ + /* printf(" --- Got args %s, %s, %s\n", context, exten, label); */ + count_labels = 0; + return_on_context_match = 0; + + match_context = context; + match_exten = exten; + match_label = label; + + return match_pval(current_db); +} + + +struct pval *find_macro(char *name) +{ + return_on_context_match = 1; + count_labels = 0; + match_context = name; + match_exten = "*"; /* don't really need to set these, shouldn't be reached */ + match_label = "*"; + return match_pval(current_db); +} + +struct pval *find_context(char *name) +{ + return_on_context_match = 1; + count_labels = 0; + match_context = name; + match_exten = "*"; /* don't really need to set these, shouldn't be reached */ + match_label = "*"; + return match_pval(current_db); +} + +int is_float(char *arg ) +{ + char *s; + for (s=arg; *s; s++) { + if (*s != '.' && (*s < '0' || *s > '9')) + return 0; + } + return 1; +} +int is_int(char *arg ) +{ + char *s; + for (s=arg; *s; s++) { + if (*s < '0' || *s > '9') + return 0; + } + return 1; +} +int is_empty(char *arg) +{ + if (!arg) + return 1; + if (*arg == 0) + return 1; + while (*arg) { + if (*arg != ' ' && *arg != '\t') + return 0; + arg++; + } + return 1; +} + +#ifdef AAL_ARGCHECK +int option_matches_j( struct argdesc *should, pval *is, struct argapp *app) +{ + struct argchoice *ac; + char *opcop,*q,*p; + + switch (should->dtype) { + case ARGD_OPTIONSET: + if ( strstr(is->u1.str,"${") ) + return 0; /* no checking anything if there's a var reference in there! */ + + opcop = ast_strdupa(is->u1.str); + + for (q=opcop;*q;q++) { /* erase the innards of X(innard) type arguments, so we don't get confused later */ + if ( *q == '(' ) { + p = q+1; + while (*p && *p != ')' ) + *p++ = '+'; + q = p+1; + } + } + + for (ac=app->opts; ac; ac=ac->next) { + if (strlen(ac->name)>1 && strchr(ac->name,'(') == 0 && strcmp(ac->name,is->u1.str) == 0) /* multichar option, no parens, and a match? */ + return 0; + } + for (ac=app->opts; ac; ac=ac->next) { + if (strlen(ac->name)==1 || strchr(ac->name,'(')) { + char *p = strchr(opcop,ac->name[0]); /* wipe out all matched options in the user-supplied string */ + + if (p && *p == 'j') { + ast_log(LOG_ERROR, "Error: file %s, line %d-%d: The j option in the %s application call is not appropriate for AEL!\n", + is->filename, is->startline, is->endline, app->name); + errs++; + } + + if (p) { + *p = '+'; + if (ac->name[1] == '(') { + if (*(p+1) != '(') { + ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The %c option in the %s application call should have an (argument), but doesn't!\n", + is->filename, is->startline, is->endline, ac->name[0], app->name); + warns++; + } + } + } + } + } + for (q=opcop; *q; q++) { + if ( *q != '+' && *q != '(' && *q != ')') { + ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The %c option in the %s application call is not available as an option!\n", + is->filename, is->startline, is->endline, *q, app->name); + warns++; + } + } + return 1; + break; + default: + return 0; + } + +} + +int option_matches( struct argdesc *should, pval *is, struct argapp *app) +{ + struct argchoice *ac; + char *opcop; + + switch (should->dtype) { + case ARGD_STRING: + if (is_empty(is->u1.str) && should->type == ARGD_REQUIRED) + return 0; + if (is->u1.str && strlen(is->u1.str) > 0) /* most will match */ + return 1; + break; + + case ARGD_INT: + if (is_int(is->u1.str)) + return 1; + else + return 0; + break; + + case ARGD_FLOAT: + if (is_float(is->u1.str)) + return 1; + else + return 0; + break; + + case ARGD_ENUM: + if( !is->u1.str || strlen(is->u1.str) == 0 ) + return 1; /* a null arg in the call will match an enum, I guess! */ + for (ac=should->choices; ac; ac=ac->next) { + if (strcmp(ac->name,is->u1.str) == 0) + return 1; + } + return 0; + break; + + case ARGD_OPTIONSET: + opcop = ast_strdupa(is->u1.str); + + for (ac=app->opts; ac; ac=ac->next) { + if (strlen(ac->name)>1 && strchr(ac->name,'(') == 0 && strcmp(ac->name,is->u1.str) == 0) /* multichar option, no parens, and a match? */ + return 1; + } + for (ac=app->opts; ac; ac=ac->next) { + if (strlen(ac->name)==1 || strchr(ac->name,'(')) { + char *p = strchr(opcop,ac->name[0]); /* wipe out all matched options in the user-supplied string */ + + if (p) { + *p = '+'; + if (ac->name[1] == '(') { + if (*(p+1) == '(') { + char *q = p+1; + while (*q && *q != ')') { + *q++ = '+'; + } + *q = '+'; + } + } + } + } + } + return 1; + break; + case ARGD_VARARG: + return 1; /* matches anything */ + break; + } + return 1; /* unless some for-sure match or non-match returns, then it must be close enough ... */ +} +#endif + +int check_app_args(pval* appcall, pval *arglist, struct argapp *app) +{ +#ifdef AAL_ARGCHECK + struct argdesc *ad = app->args; + pval *pa; + int z; + + for (pa = arglist; pa; pa=pa->next) { + if (!ad) { + ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: Extra argument %s not in application call to %s !\n", + arglist->filename, arglist->startline, arglist->endline, pa->u1.str, app->name); + warns++; + return 1; + } else { + /* find the first entry in the ad list that will match */ + do { + if ( ad->dtype == ARGD_VARARG ) /* once we hit the VARARG, all bets are off. Discontinue the comparisons */ + break; + + z= option_matches( ad, pa, app); + if (!z) { + if ( !arglist ) + arglist=appcall; + + if (ad->type == ARGD_REQUIRED) { + ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: Required argument %s not in application call to %s !\n", + arglist->filename, arglist->startline, arglist->endline, ad->dtype==ARGD_OPTIONSET?"options":ad->name, app->name); + warns++; + return 1; + } + } else if (z && ad->dtype == ARGD_OPTIONSET) { + option_matches_j( ad, pa, app); + } + ad = ad->next; + } while (ad && !z); + } + } + /* any app nodes left, that are not optional? */ + for ( ; ad; ad=ad->next) { + if (ad->type == ARGD_REQUIRED && ad->dtype != ARGD_VARARG) { + if ( !arglist ) + arglist=appcall; + ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: Required argument %s not in application call to %s !\n", + arglist->filename, arglist->startline, arglist->endline, ad->dtype==ARGD_OPTIONSET?"options":ad->name, app->name); + warns++; + return 1; + } + } + return 0; +#else + return 0; +#endif +} + +void check_switch_expr(pval *item, struct argapp *apps) +{ +#ifdef AAL_ARGCHECK + /* get and clean the variable name */ + char *buff1, *p; + struct argapp *a,*a2; + struct appsetvar *v,*v2; + struct argchoice *c; + pval *t; + + p = item->u1.str; + while (p && *p && (*p == ' ' || *p == '\t' || *p == '$' || *p == '{' ) ) + p++; + + buff1 = ast_strdupa(p); + + while (strlen(buff1) > 0 && ( buff1[strlen(buff1)-1] == '}' || buff1[strlen(buff1)-1] == ' ' || buff1[strlen(buff1)-1] == '\t')) + buff1[strlen(buff1)-1] = 0; + /* buff1 now contains the variable name */ + v = 0; + for (a=apps; a; a=a->next) { + for (v=a->setvars;v;v=v->next) { + if (strcmp(v->name,buff1) == 0) { + break; + } + } + if ( v ) + break; + } + if (v && v->vals) { + /* we have a match, to a variable that has a set of determined values */ + int def= 0; + int pat = 0; + int f1 = 0; + + /* first of all, does this switch have a default case ? */ + for (t=item->u2.statements; t; t=t->next) { + if (t->type == PV_DEFAULT) { + def =1; + break; + } + if (t->type == PV_PATTERN) { + pat++; + } + } + if (def || pat) /* nothing to check. All cases accounted for! */ + return; + for (c=v->vals; c; c=c->next) { + f1 = 0; + for (t=item->u2.statements; t; t=t->next) { + if (t->type == PV_CASE || t->type == PV_PATTERN) { + if (!strcmp(t->u1.str,c->name)) { + f1 = 1; + break; + } + } + } + if (!f1) { + ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: switch with expression(%s) does not handle the case of %s !\n", + item->filename, item->startline, item->endline, item->u1.str, c->name); + warns++; + } + } + /* next, is there an app call in the current exten, that would set this var? */ + f1 = 0; + t = current_extension->u2.statements; + if ( t && t->type == PV_STATEMENTBLOCK ) + t = t->u1.statements; + for (; t && t != item; t=t->next) { + if (t->type == PV_APPLICATION_CALL) { + /* find the application that matches the u1.str */ + for (a2=apps; a2; a2=a2->next) { + if (strcasecmp(a2->name, t->u1.str)==0) { + for (v2=a2->setvars; v2; v2=v2->next) { + if (strcmp(v2->name, buff1) == 0) { + /* found an app that sets the var */ + f1 = 1; + break; + } + } + } + if (f1) + break; + } + } + if (f1) + break; + } + + /* see if it sets the var */ + if (!f1) { + ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: Couldn't find an application call in this extension that sets the expression (%s) value!\n", + item->filename, item->startline, item->endline, item->u1.str); + warns++; + } + } +#else + pval *t,*tl=0,*p2; + int def= 0; + + /* first of all, does this switch have a default case ? */ + for (t=item->u2.statements; t; t=t->next) { + if (t->type == PV_DEFAULT) { + def =1; + break; + } + tl = t; + } + if (def) /* nothing to check. All cases accounted for! */ + return; + /* if no default, warn and insert a default case at the end */ + p2 = tl->next = calloc(1, sizeof(struct pval)); + + p2->type = PV_DEFAULT; + p2->startline = tl->startline; + p2->endline = tl->endline; + p2->startcol = tl->startcol; + p2->endcol = tl->endcol; + p2->filename = strdup(tl->filename); + ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: A default case was automatically added to the switch.\n", + p2->filename, p2->startline, p2->endline); + warns++; + +#endif +} + +static void check_context_names(void) +{ + pval *i,*j; + for (i=current_db; i; i=i->next) { + if (i->type == PV_CONTEXT || i->type == PV_MACRO) { + for (j=i->next; j; j=j->next) { + if ( j->type == PV_CONTEXT || j->type == PV_MACRO ) { + if ( !strcmp(i->u1.str, j->u1.str) ) + { + ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: The context name (%s) is also declared in file %s, line %d-%d!\n", + i->filename, i->startline, i->endline, i->u1.str, j->filename, j->startline, j->endline); + warns++; + } + } + } + } + } +} + +static void check_abstract_reference(pval *abstract_context) +{ + pval *i,*j; + /* find some context includes that reference this context */ + + + /* otherwise, print out a warning */ + for (i=current_db; i; i=i->next) { + if (i->type == PV_CONTEXT) { + for (j=i->u2. statements; j; j=j->next) { + if ( j->type == PV_INCLUDES ) { + struct pval *p4; + for (p4=j->u1.list; p4; p4=p4->next) { + /* for each context pointed to, find it, then find a context/label that matches the + target here! */ + if ( !strcmp(p4->u1.str, abstract_context->u1.str) ) + return; /* found a match! */ + } + } + } + } + } + ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: Couldn't find a reference to this abstract context (%s) in any other context!\n", + abstract_context->filename, abstract_context->startline, abstract_context->endline, abstract_context->u1.str); + warns++; +} + + +void check_pval_item(pval *item, struct argapp *apps, int in_globals) +{ + pval *lp; +#ifdef AAL_ARGCHECK + struct argapp *app, *found; +#endif + struct pval *macro_def; + struct pval *app_def; + + char errmsg[4096]; + char *strp; + + switch (item->type) { + case PV_WORD: + /* fields: item->u1.str == string associated with this (word). + item->u2.arglist == pval list of 4 PV_WORD elements for time values (only in PV_INCLUDES) */ + break; + + case PV_MACRO: + /* fields: item->u1.str == name of macro + item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user + item->u2.arglist->u1.str == argument + item->u2.arglist->next == next arg + + item->u3.macro_statements == pval list of statements in macro body. + */ + in_abstract_context = 0; + current_context = item; + current_extension = 0; + + check_macro_returns(item); + + for (lp=item->u2.arglist; lp; lp=lp->next) { + + } + check_pval(item->u3.macro_statements, apps,in_globals); + break; + + case PV_CONTEXT: + /* fields: item->u1.str == name of context + item->u2.statements == pval list of statements in context body + item->u3.abstract == int 1 if an abstract keyword were present + */ + current_context = item; + current_extension = 0; + if ( item->u3.abstract ) { + in_abstract_context = 1; + check_abstract_reference(item); + } else + in_abstract_context = 0; + check_pval(item->u2.statements, apps,in_globals); + break; + + case PV_MACRO_CALL: + /* fields: item->u1.str == name of macro to call + item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user + item->u2.arglist->u1.str == argument + item->u2.arglist->next == next arg + */ +#ifdef STANDALONE + /* if this is a standalone, we will need to make sure the + localized load of extensions.conf is done */ + if (!extensions_dot_conf_loaded) { + localized_pbx_load_module(); + extensions_dot_conf_loaded++; + } +#endif + macro_def = find_macro(item->u1.str); + if (!macro_def) { +#ifdef STANDALONE + struct pbx_find_info pfiq = {.stacklen = 0 }; + struct pbx_find_info pfiq2 = {.stacklen = 0 }; + + /* look for the macro in the extensions.conf world */ + pbx_find_extension(NULL, NULL, &pfiq, item->u1.str, "s", 1, NULL, NULL, E_MATCH); + + if (pfiq.status != STATUS_SUCCESS) { + char namebuf2[256]; + snprintf(namebuf2, 256, "macro-%s", item->u1.str); + + /* look for the macro in the extensions.conf world */ + pbx_find_extension(NULL, NULL, &pfiq2, namebuf2, "s", 1, NULL, NULL, E_MATCH); + + if (pfiq2.status == STATUS_SUCCESS) { + ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: macro call to non-existent %s! (macro-%s was found in the extensions.conf stuff, but we are using gosubs!)\n", + item->filename, item->startline, item->endline, item->u1.str, item->u1.str); + warns++; + } else { + ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: macro call to non-existent %s! (Not even in the extensions.conf stuff!)\n", + item->filename, item->startline, item->endline, item->u1.str); + warns++; + } + } +#else + ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: macro call to %s cannot be found in the AEL code!\n", + item->filename, item->startline, item->endline, item->u1.str); + warns++; + +#endif +#ifdef THIS_IS_1DOT4 + char namebuf2[256]; + snprintf(namebuf2, 256, "macro-%s", item->u1.str); + + /* look for the macro in the extensions.conf world */ + pbx_find_extension(NULL, NULL, &pfiq, namebuf2, "s", 1, NULL, NULL, E_MATCH); + + if (pfiq.status != STATUS_SUCCESS) { + ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: macro call to %s was not found in the AEL, nor the extensions.conf !\n", + item->filename, item->startline, item->endline, item->u1.str); + warns++; + } + +#endif + + } else if (macro_def->type != PV_MACRO) { + ast_log(LOG_ERROR,"Error: file %s, line %d-%d: macro call to %s references a context, not a macro!\n", + item->filename, item->startline, item->endline, item->u1.str); + errs++; + } else { + /* macro_def is a MACRO, so do the args match in number? */ + int hereargs = 0; + int thereargs = 0; + + for (lp=item->u2.arglist; lp; lp=lp->next) { + hereargs++; + } + for (lp=macro_def->u2.arglist; lp; lp=lp->next) { + thereargs++; + } + if (hereargs != thereargs ) { + ast_log(LOG_ERROR, "Error: file %s, line %d-%d: The macro call to %s has %d arguments, but the macro definition has %d arguments\n", + item->filename, item->startline, item->endline, item->u1.str, hereargs, thereargs); + errs++; + } + } + break; + + case PV_APPLICATION_CALL: + /* fields: item->u1.str == name of application to call + item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user + item->u2.arglist->u1.str == argument + item->u2.arglist->next == next arg + */ + /* Need to check to see if the application is available! */ + app_def = find_context(item->u1.str); + if (app_def && app_def->type == PV_MACRO) { + ast_log(LOG_ERROR,"Error: file %s, line %d-%d: application call to %s references an existing macro, but had no & preceding it!\n", + item->filename, item->startline, item->endline, item->u1.str); + errs++; + } + if (strcasecmp(item->u1.str,"GotoIf") == 0 + || strcasecmp(item->u1.str,"GotoIfTime") == 0 + || strcasecmp(item->u1.str,"while") == 0 + || strcasecmp(item->u1.str,"endwhile") == 0 + || strcasecmp(item->u1.str,"random") == 0 + || strcasecmp(item->u1.str,"gosub") == 0 + || strcasecmp(item->u1.str,"return") == 0 + || strcasecmp(item->u1.str,"gosubif") == 0 + || strcasecmp(item->u1.str,"continuewhile") == 0 + || strcasecmp(item->u1.str,"endwhile") == 0 + || strcasecmp(item->u1.str,"execif") == 0 + || strcasecmp(item->u1.str,"execiftime") == 0 + || strcasecmp(item->u1.str,"exitwhile") == 0 + || strcasecmp(item->u1.str,"goto") == 0 + || strcasecmp(item->u1.str,"macro") == 0 + || strcasecmp(item->u1.str,"macroexclusive") == 0 + || strcasecmp(item->u1.str,"macroif") == 0 + || strcasecmp(item->u1.str,"stackpop") == 0 + || strcasecmp(item->u1.str,"execIf") == 0 ) { + ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: application call to %s affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead!\n", + item->filename, item->startline, item->endline, item->u1.str); + warns++; + } + if (strcasecmp(item->u1.str,"macroexit") == 0) { + ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: I am converting the MacroExit call here to a return statement.\n", + item->filename, item->startline, item->endline); + item->type = PV_RETURN; + free(item->u1.str); + item->u1.str = 0; + } + +#ifdef AAL_ARGCHECK + found = 0; + for (app=apps; app; app=app->next) { + if (strcasecmp(app->name, item->u1.str) == 0) { + found =app; + break; + } + } + if (!found) { + ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: application call to %s not listed in applist database!\n", + item->filename, item->startline, item->endline, item->u1.str); + warns++; + } else + check_app_args(item, item->u2.arglist, app); +#endif + break; + + case PV_CASE: + /* fields: item->u1.str == value of case + item->u2.statements == pval list of statements under the case + */ + /* Make sure sequence of statements under case is terminated with goto, return, or break */ + /* find the last statement */ + check_pval(item->u2.statements, apps,in_globals); + break; + + case PV_PATTERN: + /* fields: item->u1.str == value of case + item->u2.statements == pval list of statements under the case + */ + /* Make sure sequence of statements under case is terminated with goto, return, or break */ + /* find the last statement */ + + check_pval(item->u2.statements, apps,in_globals); + break; + + case PV_DEFAULT: + /* fields: + item->u2.statements == pval list of statements under the case + */ + + check_pval(item->u2.statements, apps,in_globals); + break; + + case PV_CATCH: + /* fields: item->u1.str == name of extension to catch + item->u2.statements == pval list of statements in context body + */ + check_pval(item->u2.statements, apps,in_globals); + break; + + case PV_SWITCHES: + /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list + */ + check_pval(item->u1.list, apps,in_globals); + break; + + case PV_ESWITCHES: + /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list + */ + check_pval(item->u1.list, apps,in_globals); + break; + + case PV_INCLUDES: + /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list + */ + check_pval(item->u1.list, apps,in_globals); + check_includes(item); + for (lp=item->u1.list; lp; lp=lp->next){ + char *incl_context = lp->u1.str; + struct pval *that_context = find_context(incl_context); + + if ( lp->u2.arglist ) { + check_timerange(lp->u2.arglist); + check_dow(lp->u2.arglist->next); + check_day(lp->u2.arglist->next->next); + check_month(lp->u2.arglist->next->next->next); + } + + if (that_context) { + find_pval_gotos(that_context->u2.statements,0); + + } + } + break; + + case PV_STATEMENTBLOCK: + /* fields: item->u1.list == pval list of statements in block, one per entry in the list + */ + check_pval(item->u1.list, apps,in_globals); + break; + + case PV_VARDEC: + /* fields: item->u1.str == variable name + item->u2.val == variable value to assign + */ + /* the RHS of a vardec is encapsulated in a $[] expr. Is it legal? */ + if( !in_globals ) { /* don't check stuff inside the globals context; no wrapping in $[ ] there... */ + snprintf(errmsg,sizeof(errmsg), "file %s, line %d, columns %d-%d, variable declaration expr '%s':", item->filename, item->startline, item->startcol, item->endcol, item->u2.val); + ast_expr_register_extra_error_info(errmsg); + ast_expr(item->u2.val, expr_output, sizeof(expr_output),NULL); + ast_expr_clear_extra_error_info(); + if ( strpbrk(item->u2.val,"~!-+<>=*/&^") && !strstr(item->u2.val,"${") ) { + ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n", + item->filename, item->startline, item->endline, item->u2.val); + warns++; + } + check_expr2_input(item,item->u2.val); + } + break; + + case PV_LOCALVARDEC: + /* fields: item->u1.str == variable name + item->u2.val == variable value to assign + */ + /* the RHS of a vardec is encapsulated in a $[] expr. Is it legal? */ + snprintf(errmsg,sizeof(errmsg), "file %s, line %d, columns %d-%d, variable declaration expr '%s':", item->filename, item->startline, item->startcol, item->endcol, item->u2.val); + ast_expr_register_extra_error_info(errmsg); + ast_expr(item->u2.val, expr_output, sizeof(expr_output),NULL); + ast_expr_clear_extra_error_info(); + if ( strpbrk(item->u2.val,"~!-+<>=*/&^") && !strstr(item->u2.val,"${") ) { + ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n", + item->filename, item->startline, item->endline, item->u2.val); + warns++; + } + check_expr2_input(item,item->u2.val); + break; + + case PV_GOTO: + /* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user. + item->u1.list->u1.str == where the data on a PV_WORD will always be. + */ + /* don't check goto's in abstract contexts */ + if ( in_abstract_context ) + break; + + check_goto(item); + break; + + case PV_LABEL: + /* fields: item->u1.str == label name + */ + if ( strspn(item->u1.str, "0123456789") == strlen(item->u1.str) ) { + ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: label '%s' is numeric, this is bad practice!\n", + item->filename, item->startline, item->endline, item->u1.str); + warns++; + } + + check_label(item); + break; + + case PV_FOR: + /* fields: item->u1.for_init == a string containing the initalizer + item->u2.for_test == a string containing the loop test + item->u3.for_inc == a string containing the loop increment + + item->u4.for_statements == a pval list of statements in the for () + */ + snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, for test expr '%s':", item->filename, item->startline, item->startcol, item->endcol, item->u2.for_test); + ast_expr_register_extra_error_info(errmsg); + + strp = strchr(item->u1.for_init, '='); + if (strp) { + ast_expr(strp+1, expr_output, sizeof(expr_output),NULL); + } + ast_expr(item->u2.for_test, expr_output, sizeof(expr_output),NULL); + strp = strchr(item->u3.for_inc, '='); + if (strp) { + ast_expr(strp+1, expr_output, sizeof(expr_output),NULL); + } + if ( strpbrk(item->u2.for_test,"~!-+<>=*/&^") && !strstr(item->u2.for_test,"${") ) { + ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n", + item->filename, item->startline, item->endline, item->u2.for_test); + warns++; + } + if ( strpbrk(item->u3.for_inc,"~!-+<>=*/&^") && !strstr(item->u3.for_inc,"${") ) { + ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n", + item->filename, item->startline, item->endline, item->u3.for_inc); + warns++; + } + check_expr2_input(item,item->u2.for_test); + check_expr2_input(item,item->u3.for_inc); + + ast_expr_clear_extra_error_info(); + check_pval(item->u4.for_statements, apps,in_globals); + break; + + case PV_WHILE: + /* fields: item->u1.str == the while conditional, as supplied by user + + item->u2.statements == a pval list of statements in the while () + */ + snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, while expr '%s':", item->filename, item->startline, item->startcol, item->endcol, item->u1.str); + ast_expr_register_extra_error_info(errmsg); + ast_expr(item->u1.str, expr_output, sizeof(expr_output),NULL); + ast_expr_clear_extra_error_info(); + if ( strpbrk(item->u1.str,"~!-+<>=*/&^") && !strstr(item->u1.str,"${") ) { + ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n", + item->filename, item->startline, item->endline, item->u1.str); + warns++; + } + check_expr2_input(item,item->u1.str); + check_pval(item->u2.statements, apps,in_globals); + break; + + case PV_BREAK: + /* fields: none + */ + check_break(item); + break; + + case PV_RETURN: + /* fields: none + */ + break; + + case PV_CONTINUE: + /* fields: none + */ + check_continue(item); + break; + + case PV_RANDOM: + /* fields: item->u1.str == the random number expression, as supplied by user + + item->u2.statements == a pval list of statements in the if () + item->u3.else_statements == a pval list of statements in the else + (could be zero) + */ + snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, random expr '%s':", item->filename, item->startline, item->startcol, item->endcol, item->u1.str); + ast_expr_register_extra_error_info(errmsg); + ast_expr(item->u1.str, expr_output, sizeof(expr_output),NULL); + ast_expr_clear_extra_error_info(); + if ( strpbrk(item->u1.str,"~!-+<>=*/&^") && !strstr(item->u1.str,"${") ) { + ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: random expression '%s' has operators, but no variables. Interesting...\n", + item->filename, item->startline, item->endline, item->u1.str); + warns++; + } + check_expr2_input(item,item->u1.str); + check_pval(item->u2.statements, apps,in_globals); + if (item->u3.else_statements) { + check_pval(item->u3.else_statements, apps,in_globals); + } + break; + + case PV_IFTIME: + /* fields: item->u1.list == the if time values, 4 of them, each in PV_WORD, linked list + + item->u2.statements == a pval list of statements in the if () + item->u3.else_statements == a pval list of statements in the else + (could be zero) + */ + if ( item->u2.arglist ) { + check_timerange(item->u1.list); + check_dow(item->u1.list->next); + check_day(item->u1.list->next->next); + check_month(item->u1.list->next->next->next); + } + + check_pval(item->u2.statements, apps,in_globals); + if (item->u3.else_statements) { + check_pval(item->u3.else_statements, apps,in_globals); + } + break; + + case PV_IF: + /* fields: item->u1.str == the if conditional, as supplied by user + + item->u2.statements == a pval list of statements in the if () + item->u3.else_statements == a pval list of statements in the else + (could be zero) + */ + snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, if expr '%s':", item->filename, item->startline, item->startcol, item->endcol, item->u1.str); + ast_expr_register_extra_error_info(errmsg); + ast_expr(item->u1.str, expr_output, sizeof(expr_output),NULL); + ast_expr_clear_extra_error_info(); + if ( strpbrk(item->u1.str,"~!-+<>=*/&^") && !strstr(item->u1.str,"${") ) { + ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression '%s' has operators, but no variables. Interesting...\n", + item->filename, item->startline, item->endline, item->u1.str); + warns++; + } + check_expr2_input(item,item->u1.str); + check_pval(item->u2.statements, apps,in_globals); + if (item->u3.else_statements) { + check_pval(item->u3.else_statements, apps,in_globals); + } + break; + + case PV_SWITCH: + /* fields: item->u1.str == the switch expression + + item->u2.statements == a pval list of statements in the switch, + (will be case statements, most likely!) + */ + /* we can check the switch expression, see if it matches any of the app variables... + if it does, then, are all the possible cases accounted for? */ + check_switch_expr(item, apps); + check_pval(item->u2.statements, apps,in_globals); + break; + + case PV_EXTENSION: + /* fields: item->u1.str == the extension name, label, whatever it's called + + item->u2.statements == a pval list of statements in the extension + item->u3.hints == a char * hint argument + item->u4.regexten == an int boolean. non-zero says that regexten was specified + */ + current_extension = item ; + + check_pval(item->u2.statements, apps,in_globals); + break; + + case PV_IGNOREPAT: + /* fields: item->u1.str == the ignorepat data + */ + break; + + case PV_GLOBALS: + /* fields: item->u1.statements == pval list of statements, usually vardecs + */ + in_abstract_context = 0; + check_pval(item->u1.statements, apps, 1); + break; + default: + break; + } +} + +void check_pval(pval *item, struct argapp *apps, int in_globals) +{ + pval *i; + + /* checks to do: + 1. Do goto's point to actual labels? + 2. Do macro calls reference a macro? + 3. Does the number of macro args match the definition? + 4. Is a macro call missing its & at the front? + 5. Application calls-- we could check syntax for existing applications, + but I need some some sort of universal description bnf for a general + sort of method for checking arguments, in number, maybe even type, at least. + Don't want to hand code checks for hundreds of applications. + */ + + for (i=item; i; i=i->next) { + check_pval_item(i,apps,in_globals); + } +} + +void ael2_semantic_check(pval *item, int *arg_errs, int *arg_warns, int *arg_notes) +{ + +#ifdef AAL_ARGCHECK + int argapp_errs =0; + char *rfilename; +#endif + struct argapp *apps=0; + +#ifdef AAL_ARGCHECK + rfilename = alloca(10 + strlen(ast_config_AST_VAR_DIR)); + sprintf(rfilename, "%s/applist", ast_config_AST_VAR_DIR); + + apps = argdesc_parse(rfilename, &argapp_errs); /* giveth */ +#endif + current_db = item; + errs = warns = notes = 0; + + check_context_names(); + check_pval(item, apps, 0); + +#ifdef AAL_ARGCHECK + argdesc_destroy(apps); /* taketh away */ +#endif + current_db = 0; + + *arg_errs = errs; + *arg_warns = warns; + *arg_notes = notes; +} + +/* =============================================================================================== */ +/* "CODE" GENERATOR -- Convert the AEL representation to asterisk extension language */ +/* =============================================================================================== */ + +static int control_statement_count = 0; + +struct ael_priority *new_prio(void) +{ + struct ael_priority *x = (struct ael_priority *)calloc(sizeof(struct ael_priority),1); + return x; +} + +struct ael_extension *new_exten(void) +{ + struct ael_extension *x = (struct ael_extension *)calloc(sizeof(struct ael_extension),1); + return x; +} + +void linkprio(struct ael_extension *exten, struct ael_priority *prio) +{ + if (!exten->plist) { + exten->plist = prio; + exten->plist_last = prio; + } else { + exten->plist_last->next = prio; + exten->plist_last = prio; + } + if( !prio->exten ) + prio->exten = exten; /* don't override the switch value */ +} + +void destroy_extensions(struct ael_extension *exten) +{ + struct ael_extension *ne, *nen; + for (ne=exten; ne; ne=nen) { + struct ael_priority *pe, *pen; + + if (ne->name) + free(ne->name); + + /* cidmatch fields are allocated with name, and freed when + the name field is freed. Don't do a free for this field, + unless you LIKE to see a crash! */ + + if (ne->hints) + free(ne->hints); + + for (pe=ne->plist; pe; pe=pen) { + pen = pe->next; + if (pe->app) + free(pe->app); + pe->app = 0; + if (pe->appargs) + free(pe->appargs); + pe->appargs = 0; + pe->origin = 0; + pe->goto_true = 0; + pe->goto_false = 0; + free(pe); + } + nen = ne->next_exten; + ne->next_exten = 0; + ne->plist =0; + ne->plist_last = 0; + ne->next_exten = 0; + ne->loop_break = 0; + ne->loop_continue = 0; + free(ne); + } +} + +static int label_inside_case(pval *label) +{ + pval *p = label; + + while( p && p->type != PV_MACRO && p->type != PV_CONTEXT ) /* early cutout, sort of */ { + if( p->type == PV_CASE || p->type == PV_DEFAULT || p->type == PV_PATTERN ) { + return 1; + } + + p = p->dad; + } + return 0; +} + +static void linkexten(struct ael_extension *exten, struct ael_extension *add) +{ + add->next_exten = exten->next_exten; /* this will reverse the order. Big deal. */ + exten->next_exten = add; +} + +static void remove_spaces_before_equals(char *str) +{ + char *p; + while( str && *str && *str != '=' ) + { + if( *str == ' ' || *str == '\n' || *str == '\r' || *str == '\t' ) + { + p = str; + while( *p ) + { + *p = *(p+1); + p++; + } + } + else + str++; + } +} + +/* =============================================================================================== */ +/* "CODE" GENERATOR -- Convert the AEL representation to asterisk extension language */ +/* =============================================================================================== */ + +static void gen_match_to_pattern(char *pattern, char *result) +{ + /* the result will be a string that will be matched by pattern */ + char *p=pattern, *t=result; + while (*p) { + if (*p == 'x' || *p == 'n' || *p == 'z' || *p == 'X' || *p == 'N' || *p == 'Z') + *t++ = '9'; + else if (*p == '[') { + char *z = p+1; + while (*z != ']') + z++; + if (*(z+1)== ']') + z++; + *t++=*(p+1); /* use the first char in the set */ + p = z; + } else { + *t++ = *p; + } + p++; + } + *t++ = 0; /* cap it off */ +} + +static void gen_prios(struct ael_extension *exten, char *label, pval *statement, struct ael_extension *mother_exten, struct ast_context *this_context ) +{ + pval *p,*p2,*p3; + struct ael_priority *pr; + struct ael_priority *for_init, *for_test, *for_inc, *for_loop, *for_end; + struct ael_priority *while_test, *while_loop, *while_end; + struct ael_priority *switch_test, *switch_end, *fall_thru; + struct ael_priority *if_test, *if_end, *if_skip, *if_false; +#ifdef OLD_RAND_ACTION + struct ael_priority *rand_test, *rand_end, *rand_skip; +#endif + char buf1[2000]; + char buf2[2000]; + char *strp, *strp2; + char new_label[2000]; + int default_exists; + int local_control_statement_count; + int first; + struct ael_priority *loop_break_save; + struct ael_priority *loop_continue_save; + struct ael_extension *switch_case; + + for (p=statement; p; p=p->next) { + switch (p->type) { + case PV_VARDEC: + pr = new_prio(); + pr->type = AEL_APPCALL; + snprintf(buf1,sizeof(buf1),"%s=$[%s]", p->u1.str, p->u2.val); + pr->app = strdup("Set"); + remove_spaces_before_equals(buf1); + pr->appargs = strdup(buf1); + pr->origin = p; + linkprio(exten, pr); + break; + + case PV_LOCALVARDEC: + pr = new_prio(); + pr->type = AEL_APPCALL; + snprintf(buf1,sizeof(buf1),"LOCAL(%s)=$[%s]", p->u1.str, p->u2.val); + pr->app = strdup("Set"); + remove_spaces_before_equals(buf1); + pr->appargs = strdup(buf1); + pr->origin = p; + linkprio(exten, pr); + break; + + case PV_GOTO: + pr = new_prio(); + pr->type = AEL_APPCALL; + p->u2.goto_target = get_goto_target(p); + if( p->u2.goto_target ) { + p->u3.goto_target_in_case = p->u2.goto_target->u2.label_in_case = label_inside_case(p->u2.goto_target); + } + + if (!p->u1.list->next) /* just one */ { + pr->app = strdup("Goto"); + if (!mother_exten) + pr->appargs = strdup(p->u1.list->u1.str); + else { /* for the case of simple within-extension gotos in case/pattern/default statement blocks: */ + snprintf(buf1,sizeof(buf1),"%s,%s", mother_exten->name, p->u1.list->u1.str); + pr->appargs = strdup(buf1); + } + + } else if (p->u1.list->next && !p->u1.list->next->next) /* two */ { + snprintf(buf1,sizeof(buf1),"%s,%s", p->u1.list->u1.str, p->u1.list->next->u1.str); + pr->app = strdup("Goto"); + pr->appargs = strdup(buf1); + } else if (p->u1.list->next && p->u1.list->next->next) { + snprintf(buf1,sizeof(buf1),"%s,%s,%s", p->u1.list->u1.str, + p->u1.list->next->u1.str, + p->u1.list->next->next->u1.str); + pr->app = strdup("Goto"); + pr->appargs = strdup(buf1); + } + pr->origin = p; + linkprio(exten, pr); + break; + + case PV_LABEL: + pr = new_prio(); + pr->type = AEL_LABEL; + pr->origin = p; + p->u3.compiled_label = exten; + linkprio(exten, pr); + break; + + case PV_FOR: + control_statement_count++; + loop_break_save = exten->loop_break; /* save them, then restore before leaving */ + loop_continue_save = exten->loop_continue; + snprintf(new_label,sizeof(new_label),"for-%s-%d", label, control_statement_count); + for_init = new_prio(); + for_inc = new_prio(); + for_test = new_prio(); + for_loop = new_prio(); + for_end = new_prio(); + for_init->type = AEL_APPCALL; + for_inc->type = AEL_APPCALL; + for_test->type = AEL_FOR_CONTROL; + for_test->goto_false = for_end; + for_loop->type = AEL_CONTROL1; /* simple goto */ + for_end->type = AEL_APPCALL; + for_init->app = strdup("Set"); + + strcpy(buf2,p->u1.for_init); + remove_spaces_before_equals(buf2); + strp = strchr(buf2, '='); + strp2 = strchr(p->u1.for_init, '='); + if (strp) { + *(strp+1) = 0; + strcat(buf2,"$["); + strncat(buf2,strp2+1, sizeof(buf2)-strlen(strp2+1)-2); + strcat(buf2,"]"); + for_init->appargs = strdup(buf2); + } else + for_init->appargs = strdup(p->u1.for_init); + + for_inc->app = strdup("Set"); + + strcpy(buf2,p->u3.for_inc); + remove_spaces_before_equals(buf2); + strp = strchr(buf2, '='); + strp2 = strchr(p->u3.for_inc, '='); + if (strp) { + *(strp+1) = 0; + strcat(buf2,"$["); + strncat(buf2,strp2+1, sizeof(buf2)-strlen(strp2+1)-2); + strcat(buf2,"]"); + for_inc->appargs = strdup(buf2); + } else + for_inc->appargs = strdup(p->u3.for_inc); + snprintf(buf1,sizeof(buf1),"$[%s]",p->u2.for_test); + for_test->app = 0; + for_test->appargs = strdup(buf1); + for_loop->goto_true = for_test; + snprintf(buf1,sizeof(buf1),"Finish for-%s-%d", label, control_statement_count); + for_end->app = strdup("NoOp"); + for_end->appargs = strdup(buf1); + /* link & load! */ + linkprio(exten, for_init); + linkprio(exten, for_test); + + /* now, put the body of the for loop here */ + exten->loop_break = for_end; + exten->loop_continue = for_inc; + + gen_prios(exten, new_label, p->u4.for_statements, mother_exten, this_context); /* this will link in all the statements here */ + + linkprio(exten, for_inc); + linkprio(exten, for_loop); + linkprio(exten, for_end); + + + exten->loop_break = loop_break_save; + exten->loop_continue = loop_continue_save; + for_loop->origin = p; + break; + + case PV_WHILE: + control_statement_count++; + loop_break_save = exten->loop_break; /* save them, then restore before leaving */ + loop_continue_save = exten->loop_continue; + snprintf(new_label,sizeof(new_label),"while-%s-%d", label, control_statement_count); + while_test = new_prio(); + while_loop = new_prio(); + while_end = new_prio(); + while_test->type = AEL_FOR_CONTROL; + while_test->goto_false = while_end; + while_loop->type = AEL_CONTROL1; /* simple goto */ + while_end->type = AEL_APPCALL; + snprintf(buf1,sizeof(buf1),"$[%s]",p->u1.str); + while_test->app = 0; + while_test->appargs = strdup(buf1); + while_loop->goto_true = while_test; + snprintf(buf1,sizeof(buf1),"Finish while-%s-%d", label, control_statement_count); + while_end->app = strdup("NoOp"); + while_end->appargs = strdup(buf1); + + linkprio(exten, while_test); + + /* now, put the body of the for loop here */ + exten->loop_break = while_end; + exten->loop_continue = while_test; + + gen_prios(exten, new_label, p->u2.statements, mother_exten, this_context); /* this will link in all the while body statements here */ + + linkprio(exten, while_loop); + linkprio(exten, while_end); + + + exten->loop_break = loop_break_save; + exten->loop_continue = loop_continue_save; + while_loop->origin = p; + break; + + case PV_SWITCH: + control_statement_count++; + local_control_statement_count = control_statement_count; + loop_break_save = exten->loop_break; /* save them, then restore before leaving */ + loop_continue_save = exten->loop_continue; + snprintf(new_label,sizeof(new_label),"sw-%s-%d", label, control_statement_count); + + switch_test = new_prio(); + switch_end = new_prio(); + switch_test->type = AEL_APPCALL; + switch_end->type = AEL_APPCALL; + snprintf(buf1,sizeof(buf1),"sw-%d-%s,10",control_statement_count, p->u1.str); + switch_test->app = strdup("Goto"); + switch_test->appargs = strdup(buf1); + snprintf(buf1,sizeof(buf1),"Finish switch-%s-%d", label, control_statement_count); + switch_end->app = strdup("NoOp"); + switch_end->appargs = strdup(buf1); + switch_end->origin = p; + switch_end->exten = exten; + + linkprio(exten, switch_test); + linkprio(exten, switch_end); + + exten->loop_break = switch_end; + exten->loop_continue = 0; + default_exists = 0; + + for (p2=p->u2.statements; p2; p2=p2->next) { + /* now, for each case/default put the body of the for loop here */ + if (p2->type == PV_CASE) { + /* ok, generate a extension and link it in */ + switch_case = new_exten(); + switch_case->context = this_context; + switch_case->is_switch = 1; + /* the break/continue locations are inherited from parent */ + switch_case->loop_break = exten->loop_break; + switch_case->loop_continue = exten->loop_continue; + + linkexten(exten,switch_case); + snprintf(buf1,sizeof(buf1),"sw-%d-%s", local_control_statement_count, p2->u1.str); + switch_case->name = strdup(buf1); + snprintf(new_label,sizeof(new_label),"sw-%s-%s-%d", label, p2->u1.str, local_control_statement_count); + + gen_prios(switch_case, new_label, p2->u2.statements, exten, this_context); /* this will link in all the case body statements here */ + + /* here is where we write code to "fall thru" to the next case... if there is one... */ + for (p3=p2->u2.statements; p3; p3=p3->next) { + if (!p3->next) + break; + } + /* p3 now points the last statement... */ + if (!p3 || ( p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN) ) { + /* is there a following CASE/PATTERN/DEFAULT? */ + if (p2->next && p2->next->type == PV_CASE) { + fall_thru = new_prio(); + fall_thru->type = AEL_APPCALL; + fall_thru->app = strdup("Goto"); + snprintf(buf1,sizeof(buf1),"sw-%d-%s,10",local_control_statement_count, p2->next->u1.str); + fall_thru->appargs = strdup(buf1); + linkprio(switch_case, fall_thru); + } else if (p2->next && p2->next->type == PV_PATTERN) { + fall_thru = new_prio(); + fall_thru->type = AEL_APPCALL; + fall_thru->app = strdup("Goto"); + gen_match_to_pattern(p2->next->u1.str, buf2); + snprintf(buf1,sizeof(buf1),"sw-%d-%s,10", local_control_statement_count, buf2); + fall_thru->appargs = strdup(buf1); + linkprio(switch_case, fall_thru); + } else if (p2->next && p2->next->type == PV_DEFAULT) { + fall_thru = new_prio(); + fall_thru->type = AEL_APPCALL; + fall_thru->app = strdup("Goto"); + snprintf(buf1,sizeof(buf1),"sw-%d-.,10",local_control_statement_count); + fall_thru->appargs = strdup(buf1); + linkprio(switch_case, fall_thru); + } else if (!p2->next) { + fall_thru = new_prio(); + fall_thru->type = AEL_CONTROL1; + fall_thru->goto_true = switch_end; + fall_thru->app = strdup("Goto"); + linkprio(switch_case, fall_thru); + } + } + if (switch_case->return_needed) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */ + char buf[2000]; + struct ael_priority *np2 = new_prio(); + np2->type = AEL_APPCALL; + np2->app = strdup("NoOp"); + snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name); + np2->appargs = strdup(buf); + linkprio(switch_case, np2); + switch_case-> return_target = np2; + } + } else if (p2->type == PV_PATTERN) { + /* ok, generate a extension and link it in */ + switch_case = new_exten(); + switch_case->context = this_context; + switch_case->is_switch = 1; + /* the break/continue locations are inherited from parent */ + switch_case->loop_break = exten->loop_break; + switch_case->loop_continue = exten->loop_continue; + + linkexten(exten,switch_case); + snprintf(buf1,sizeof(buf1),"_sw-%d-%s", local_control_statement_count, p2->u1.str); + switch_case->name = strdup(buf1); + snprintf(new_label,sizeof(new_label),"sw-%s-%s-%d", label, p2->u1.str, local_control_statement_count); + + gen_prios(switch_case, new_label, p2->u2.statements, exten, this_context); /* this will link in all the while body statements here */ + /* here is where we write code to "fall thru" to the next case... if there is one... */ + for (p3=p2->u2.statements; p3; p3=p3->next) { + if (!p3->next) + break; + } + /* p3 now points the last statement... */ + if (!p3 || ( p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN)) { + /* is there a following CASE/PATTERN/DEFAULT? */ + if (p2->next && p2->next->type == PV_CASE) { + fall_thru = new_prio(); + fall_thru->type = AEL_APPCALL; + fall_thru->app = strdup("Goto"); + snprintf(buf1,sizeof(buf1),"sw-%d-%s,10",local_control_statement_count, p2->next->u1.str); + fall_thru->appargs = strdup(buf1); + linkprio(switch_case, fall_thru); + } else if (p2->next && p2->next->type == PV_PATTERN) { + fall_thru = new_prio(); + fall_thru->type = AEL_APPCALL; + fall_thru->app = strdup("Goto"); + gen_match_to_pattern(p2->next->u1.str, buf2); + snprintf(buf1,sizeof(buf1),"sw-%d-%s,10",local_control_statement_count, buf2); + fall_thru->appargs = strdup(buf1); + linkprio(switch_case, fall_thru); + } else if (p2->next && p2->next->type == PV_DEFAULT) { + fall_thru = new_prio(); + fall_thru->type = AEL_APPCALL; + fall_thru->app = strdup("Goto"); + snprintf(buf1,sizeof(buf1),"sw-%d-.,10",local_control_statement_count); + fall_thru->appargs = strdup(buf1); + linkprio(switch_case, fall_thru); + } else if (!p2->next) { + fall_thru = new_prio(); + fall_thru->type = AEL_CONTROL1; + fall_thru->goto_true = switch_end; + fall_thru->app = strdup("Goto"); + linkprio(switch_case, fall_thru); + } + } + if (switch_case->return_needed) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */ + char buf[2000]; + struct ael_priority *np2 = new_prio(); + np2->type = AEL_APPCALL; + np2->app = strdup("NoOp"); + snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name); + np2->appargs = strdup(buf); + linkprio(switch_case, np2); + switch_case-> return_target = np2; + } + } else if (p2->type == PV_DEFAULT) { + default_exists++; + /* ok, generate a extension and link it in */ + switch_case = new_exten(); + switch_case->context = this_context; + switch_case->is_switch = 1; + /* the break/continue locations are inherited from parent */ + switch_case->loop_break = exten->loop_break; + switch_case->loop_continue = exten->loop_continue; + linkexten(exten,switch_case); + snprintf(buf1,sizeof(buf1),"_sw-%d-.", local_control_statement_count); + switch_case->name = strdup(buf1); + + snprintf(new_label,sizeof(new_label),"sw-%s-default-%d", label, local_control_statement_count); + + gen_prios(switch_case, new_label, p2->u2.statements, exten, this_context); /* this will link in all the while body statements here */ + + /* here is where we write code to "fall thru" to the next case... if there is one... */ + for (p3=p2->u2.statements; p3; p3=p3->next) { + if (!p3->next) + break; + } + /* p3 now points the last statement... */ + if (!p3 || (p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN)) { + /* is there a following CASE/PATTERN/DEFAULT? */ + if (p2->next && p2->next->type == PV_CASE) { + fall_thru = new_prio(); + fall_thru->type = AEL_APPCALL; + fall_thru->app = strdup("Goto"); + snprintf(buf1,sizeof(buf1),"sw-%d-%s,10",local_control_statement_count, p2->next->u1.str); + fall_thru->appargs = strdup(buf1); + linkprio(switch_case, fall_thru); + } else if (p2->next && p2->next->type == PV_PATTERN) { + fall_thru = new_prio(); + fall_thru->type = AEL_APPCALL; + fall_thru->app = strdup("Goto"); + gen_match_to_pattern(p2->next->u1.str, buf2); + snprintf(buf1,sizeof(buf1),"sw-%d-%s,10",local_control_statement_count, buf2); + fall_thru->appargs = strdup(buf1); + linkprio(switch_case, fall_thru); + } else if (p2->next && p2->next->type == PV_DEFAULT) { + fall_thru = new_prio(); + fall_thru->type = AEL_APPCALL; + fall_thru->app = strdup("Goto"); + snprintf(buf1,sizeof(buf1),"sw-%d-.,10",local_control_statement_count); + fall_thru->appargs = strdup(buf1); + linkprio(switch_case, fall_thru); + } else if (!p2->next) { + fall_thru = new_prio(); + fall_thru->type = AEL_CONTROL1; + fall_thru->goto_true = switch_end; + fall_thru->app = strdup("Goto"); + linkprio(switch_case, fall_thru); + } + } + if (switch_case->return_needed) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */ + char buf[2000]; + struct ael_priority *np2 = new_prio(); + np2->type = AEL_APPCALL; + np2->app = strdup("NoOp"); + snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name); + np2->appargs = strdup(buf); + linkprio(switch_case, np2); + switch_case-> return_target = np2; + } + } else { + /* what could it be??? */ + } + } + + exten->loop_break = loop_break_save; + exten->loop_continue = loop_continue_save; + switch_test->origin = p; + switch_end->origin = p; + break; + + case PV_MACRO_CALL: + pr = new_prio(); + pr->type = AEL_APPCALL; + snprintf(buf1,sizeof(buf1),"%s,s,1", p->u1.str); + first = 1; + for (p2 = p->u2.arglist; p2; p2 = p2->next) { + if (first) + { + strcat(buf1,"("); + first = 0; + } + else + strcat(buf1,","); + strcat(buf1,p2->u1.str); + } + if (!first) + strcat(buf1,")"); + + pr->app = strdup("Gosub"); + pr->appargs = strdup(buf1); + pr->origin = p; + linkprio(exten, pr); + break; + + case PV_APPLICATION_CALL: + pr = new_prio(); + pr->type = AEL_APPCALL; + buf1[0] = 0; + for (p2 = p->u2.arglist; p2; p2 = p2->next) { + if (p2 != p->u2.arglist ) + strcat(buf1,","); + strcat(buf1,p2->u1.str); + } + pr->app = strdup(p->u1.str); + pr->appargs = strdup(buf1); + pr->origin = p; + linkprio(exten, pr); + break; + + case PV_BREAK: + pr = new_prio(); + pr->type = AEL_CONTROL1; /* simple goto */ + pr->goto_true = exten->loop_break; + pr->origin = p; + linkprio(exten, pr); + break; + + case PV_RETURN: /* hmmmm */ + pr = new_prio(); + pr->type = AEL_RETURN; /* simple Return */ + /* exten->return_needed++; */ + pr->app = strdup("Return"); + pr->appargs = strdup(""); + pr->origin = p; + linkprio(exten, pr); + break; + + case PV_CONTINUE: + pr = new_prio(); + pr->type = AEL_CONTROL1; /* simple goto */ + pr->goto_true = exten->loop_continue; + pr->origin = p; + linkprio(exten, pr); + break; + + case PV_IFTIME: + control_statement_count++; + snprintf(new_label,sizeof(new_label),"iftime-%s-%d", label, control_statement_count); + + if_test = new_prio(); + if_test->type = AEL_IFTIME_CONTROL; + snprintf(buf1,sizeof(buf1),"%s,%s,%s,%s", + p->u1.list->u1.str, + p->u1.list->next->u1.str, + p->u1.list->next->next->u1.str, + p->u1.list->next->next->next->u1.str); + if_test->app = 0; + if_test->appargs = strdup(buf1); + if_test->origin = p; + + if_end = new_prio(); + if_end->type = AEL_APPCALL; + snprintf(buf1,sizeof(buf1),"Finish iftime-%s-%d", label, control_statement_count); + if_end->app = strdup("NoOp"); + if_end->appargs = strdup(buf1); + + if (p->u3.else_statements) { + if_skip = new_prio(); + if_skip->type = AEL_CONTROL1; /* simple goto */ + if_skip->goto_true = if_end; + if_skip->origin = p; + + } else { + if_skip = 0; + + if_test->goto_false = if_end; + } + + if_false = new_prio(); + if_false->type = AEL_CONTROL1; + if (p->u3.else_statements) { + if_false->goto_true = if_skip; /* +1 */ + } else { + if_false->goto_true = if_end; + } + + /* link & load! */ + linkprio(exten, if_test); + linkprio(exten, if_false); + + /* now, put the body of the if here */ + + gen_prios(exten, new_label, p->u2.statements, mother_exten, this_context); /* this will link in all the statements here */ + + if (p->u3.else_statements) { + linkprio(exten, if_skip); + gen_prios(exten, new_label, p->u3.else_statements, mother_exten, this_context); /* this will link in all the statements here */ + + } + + linkprio(exten, if_end); + + break; + + case PV_RANDOM: + case PV_IF: + control_statement_count++; + snprintf(new_label,sizeof(new_label),"if-%s-%d", label, control_statement_count); + + if_test = new_prio(); + if_end = new_prio(); + if_test->type = AEL_IF_CONTROL; + if_end->type = AEL_APPCALL; + if ( p->type == PV_RANDOM ) + snprintf(buf1,sizeof(buf1),"$[${RAND(0,99)} < (%s)]",p->u1.str); + else + snprintf(buf1,sizeof(buf1),"$[%s]",p->u1.str); + if_test->app = 0; + if_test->appargs = strdup(buf1); + snprintf(buf1,sizeof(buf1),"Finish if-%s-%d", label, control_statement_count); + if_end->app = strdup("NoOp"); + if_end->appargs = strdup(buf1); + if_test->origin = p; + + if (p->u3.else_statements) { + if_skip = new_prio(); + if_skip->type = AEL_CONTROL1; /* simple goto */ + if_skip->goto_true = if_end; + if_test->goto_false = if_skip;; + } else { + if_skip = 0; + if_test->goto_false = if_end;; + } + + /* link & load! */ + linkprio(exten, if_test); + + /* now, put the body of the if here */ + + gen_prios(exten, new_label, p->u2.statements, mother_exten, this_context); /* this will link in all the statements here */ + + if (p->u3.else_statements) { + linkprio(exten, if_skip); + gen_prios(exten, new_label, p->u3.else_statements, mother_exten, this_context); /* this will link in all the statements here */ + + } + + linkprio(exten, if_end); + + break; + + case PV_STATEMENTBLOCK: + gen_prios(exten, label, p->u1.list, mother_exten, this_context ); /* recurse into the block */ + break; + + case PV_CATCH: + control_statement_count++; + /* generate an extension with name of catch, put all catch stats + into this exten! */ + switch_case = new_exten(); + switch_case->context = this_context; + linkexten(exten,switch_case); + switch_case->name = strdup(p->u1.str); + snprintf(new_label,sizeof(new_label),"catch-%s-%d",p->u1.str, control_statement_count); + + gen_prios(switch_case, new_label, p->u2.statements,mother_exten,this_context); /* this will link in all the catch body statements here */ + if (switch_case->return_needed) { /* returns now generate a Return() app call, no longer a goto to the end of the exten */ + char buf[2000]; + struct ael_priority *np2 = new_prio(); + np2->type = AEL_APPCALL; + np2->app = strdup("NoOp"); + snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name); + np2->appargs = strdup(buf); + linkprio(switch_case, np2); + switch_case-> return_target = np2; + } + + break; + default: + break; + } + } +} + +void set_priorities(struct ael_extension *exten) +{ + int i; + struct ael_priority *pr; + do { + if (exten->is_switch) + i = 10; + else if (exten->regexten) + i=2; + else + i=1; + + for (pr=exten->plist; pr; pr=pr->next) { + pr->priority_num = i; + + if (!pr->origin || (pr->origin && pr->origin->type != PV_LABEL) ) /* Labels don't show up in the dialplan, + but we want them to point to the right + priority, which would be the next line + after the label; */ + i++; + } + + exten = exten->next_exten; + } while ( exten ); +} + +void add_extensions(struct ael_extension *exten) +{ + struct ael_priority *pr; + char *label=0; + char realext[AST_MAX_EXTENSION]; + if (!exten) { + ast_log(LOG_WARNING, "This file is Empty!\n" ); + return; + } + do { + struct ael_priority *last = 0; + + memset(realext, '\0', sizeof(realext)); /* make sure this is properly initialized */ + pbx_substitute_variables_helper(NULL, exten->name, realext, sizeof(realext) - 1); + if (exten->hints) { + if (ast_add_extension2(exten->context, 0 /*no replace*/, realext, PRIORITY_HINT, NULL, exten->cidmatch, + exten->hints, NULL, ast_free, registrar)) { + ast_log(LOG_WARNING, "Unable to add step at priority 'hint' of extension '%s'\n", + exten->name); + } + } + + for (pr=exten->plist; pr; pr=pr->next) { + char app[2000]; + char appargs[2000]; + + /* before we can add the extension, we need to prep the app/appargs; + the CONTROL types need to be done after the priority numbers are calculated. + */ + if (pr->type == AEL_LABEL) /* don't try to put labels in the dialplan! */ { + last = pr; + continue; + } + + if (pr->app) + strcpy(app, pr->app); + else + app[0] = 0; + if (pr->appargs ) + strcpy(appargs, pr->appargs); + else + appargs[0] = 0; + switch( pr->type ) { + case AEL_APPCALL: + /* easy case. Everything is all set up */ + break; + + case AEL_CONTROL1: /* FOR loop, WHILE loop, BREAK, CONTINUE, IF, IFTIME */ + /* simple, unconditional goto. */ + strcpy(app,"Goto"); + if (pr->goto_true->origin && pr->goto_true->origin->type == PV_SWITCH ) { + snprintf(appargs,sizeof(appargs),"%s,%d", pr->goto_true->exten->name, pr->goto_true->priority_num); + } else if (pr->goto_true->origin && pr->goto_true->origin->type == PV_IFTIME && pr->goto_true->origin->u3.else_statements ) { + snprintf(appargs,sizeof(appargs),"%d", pr->goto_true->priority_num+1); + } else + snprintf(appargs,sizeof(appargs),"%d", pr->goto_true->priority_num); + break; + + case AEL_FOR_CONTROL: /* WHILE loop test, FOR loop test */ + strcpy(app,"GotoIf"); + snprintf(appargs,sizeof(appargs),"%s?%d:%d", pr->appargs, pr->priority_num+1, pr->goto_false->priority_num); + break; + + case AEL_IF_CONTROL: + strcpy(app,"GotoIf"); + if (pr->origin->u3.else_statements ) + snprintf(appargs,sizeof(appargs),"%s?%d:%d", pr->appargs, pr->priority_num+1, pr->goto_false->priority_num+1); + else + snprintf(appargs,sizeof(appargs),"%s?%d:%d", pr->appargs, pr->priority_num+1, pr->goto_false->priority_num); + break; + + case AEL_RAND_CONTROL: + strcpy(app,"Random"); + snprintf(appargs,sizeof(appargs),"%s:%d", pr->appargs, pr->goto_true->priority_num+1); + break; + + case AEL_IFTIME_CONTROL: + strcpy(app,"GotoIfTime"); + snprintf(appargs,sizeof(appargs),"%s?%d", pr->appargs, pr->priority_num+2); + break; + + case AEL_RETURN: + strcpy(app,"Return"); + appargs[0] = 0; + break; + + default: + break; + } + if (last && last->type == AEL_LABEL ) { + label = last->origin->u1.str; + } + else + label = 0; + + if (ast_add_extension2(exten->context, 0 /*no replace*/, realext, pr->priority_num, (label?label:NULL), exten->cidmatch, + app, strdup(appargs), ast_free, registrar)) { + ast_log(LOG_WARNING, "Unable to add step at priority '%d' of extension '%s'\n", pr->priority_num, + exten->name); + } + last = pr; + } + exten = exten->next_exten; + } while ( exten ); +} + +static void attach_exten(struct ael_extension **list, struct ael_extension *newmem) +{ + /* travel to the end of the list... */ + struct ael_extension *lptr; + if( !*list ) { + *list = newmem; + return; + } + lptr = *list; + + while( lptr->next_exten ) { + lptr = lptr->next_exten; + } + /* lptr should now pointing to the last element in the list; it has a null next_exten pointer */ + lptr->next_exten = newmem; +} + +static pval *get_extension_or_contxt(pval *p) +{ + while( p && p->type != PV_EXTENSION && p->type != PV_CONTEXT && p->type != PV_MACRO ) { + + p = p->dad; + } + + return p; +} + +static pval *get_contxt(pval *p) +{ + while( p && p->type != PV_CONTEXT && p->type != PV_MACRO ) { + + p = p->dad; + } + + return p; +} + +static void fix_gotos_in_extensions(struct ael_extension *exten) +{ + struct ael_extension *e; + for(e=exten;e;e=e->next_exten) { + + struct ael_priority *p; + for(p=e->plist;p;p=p->next) { + + if( p->origin && p->origin->type == PV_GOTO && p->origin->u3.goto_target_in_case ) { + + /* fix the extension of the goto target to the actual extension in the post-compiled dialplan */ + + pval *target = p->origin->u2.goto_target; + struct ael_extension *z = target->u3.compiled_label; + pval *pv2 = p->origin; + char buf1[500]; + char *apparg_save = p->appargs; + + p->appargs = 0; + if (!pv2->u1.list->next) /* just one -- it won't hurt to repeat the extension */ { + snprintf(buf1,sizeof(buf1),"%s,%s", z->name, pv2->u1.list->u1.str); + p->appargs = strdup(buf1); + + } else if (pv2->u1.list->next && !pv2->u1.list->next->next) /* two */ { + snprintf(buf1,sizeof(buf1),"%s,%s", z->name, pv2->u1.list->next->u1.str); + p->appargs = strdup(buf1); + } else if (pv2->u1.list->next && pv2->u1.list->next->next) { + snprintf(buf1,sizeof(buf1),"%s,%s,%s", pv2->u1.list->u1.str, + z->name, + pv2->u1.list->next->next->u1.str); + p->appargs = strdup(buf1); + } + else + printf("WHAT? The goto doesn't fall into one of three cases for GOTO????\n"); + + if( apparg_save ) { + free(apparg_save); + } + } + } + } +} + + +void ast_compile_ael2(struct ast_context **local_contexts, struct pval *root) +{ + pval *p,*p2; + struct ast_context *context; + char buf[2000]; + struct ael_extension *exten; + struct ael_extension *exten_list = 0; + + for (p=root; p; p=p->next ) { /* do the globals first, so they'll be there + when we try to eval them */ + switch (p->type) { + case PV_GLOBALS: + /* just VARDEC elements */ + for (p2=p->u1.list; p2; p2=p2->next) { + char buf2[2000]; + snprintf(buf2,sizeof(buf2),"%s=%s", p2->u1.str, p2->u2.val); + pbx_builtin_setvar(NULL, buf2); + } + break; + default: + break; + } + } + + for (p=root; p; p=p->next ) { + pval *lp; + int argc; + + switch (p->type) { + case PV_MACRO: + + context = ast_context_create(local_contexts, p->u1.str, registrar); + + exten = new_exten(); + exten->context = context; + exten->name = strdup("s"); + argc = 1; + for (lp=p->u2.arglist; lp; lp=lp->next) { + /* for each arg, set up a "Set" command */ + struct ael_priority *np2 = new_prio(); + np2->type = AEL_APPCALL; + np2->app = strdup("Set"); + snprintf(buf,sizeof(buf),"LOCAL(%s)=${ARG%d}", lp->u1.str, argc++); + remove_spaces_before_equals(buf); + np2->appargs = strdup(buf); + linkprio(exten, np2); + } + + /* CONTAINS APPCALLS, CATCH, just like extensions... */ + gen_prios(exten, p->u1.str, p->u3.macro_statements, 0, context ); + if (exten->return_needed) { /* most likely, this will go away */ + struct ael_priority *np2 = new_prio(); + np2->type = AEL_APPCALL; + np2->app = strdup("NoOp"); + snprintf(buf,sizeof(buf),"End of Macro %s-%s",p->u1.str, exten->name); + np2->appargs = strdup(buf); + linkprio(exten, np2); + exten-> return_target = np2; + } + + set_priorities(exten); + attach_exten(&exten_list, exten); + break; + + case PV_GLOBALS: + /* already done */ + break; + + case PV_CONTEXT: + context = ast_context_create(local_contexts, p->u1.str, registrar); + + /* contexts contain: ignorepat, includes, switches, eswitches, extensions, */ + for (p2=p->u2.statements; p2; p2=p2->next) { + pval *p3; + char *s3; + + switch (p2->type) { + case PV_EXTENSION: + exten = new_exten(); + exten->name = strdup(p2->u1.str); + exten->context = context; + + if( (s3=strchr(exten->name, '/') ) != 0 ) + { + *s3 = 0; + exten->cidmatch = s3+1; + } + + if ( p2->u3.hints ) + exten->hints = strdup(p2->u3.hints); + exten->regexten = p2->u4.regexten; + gen_prios(exten, p->u1.str, p2->u2.statements, 0, context ); + if (exten->return_needed) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */ + struct ael_priority *np2 = new_prio(); + np2->type = AEL_APPCALL; + np2->app = strdup("NoOp"); + snprintf(buf,sizeof(buf),"End of Extension %s", exten->name); + np2->appargs = strdup(buf); + linkprio(exten, np2); + exten-> return_target = np2; + } + /* is the last priority in the extension a label? Then add a trailing no-op */ + if( !exten->plist_last ) + { + ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: Empty Extension!\n", + p2->filename, p2->startline, p2->endline); + } + + if ( exten->plist_last && exten->plist_last->type == AEL_LABEL ) { + struct ael_priority *np2 = new_prio(); + np2->type = AEL_APPCALL; + np2->app = strdup("NoOp"); + snprintf(buf,sizeof(buf),"A NoOp to follow a trailing label %s", exten->plist_last->origin->u1.str); + np2->appargs = strdup(buf); + linkprio(exten, np2); + } + + set_priorities(exten); + attach_exten(&exten_list, exten); + break; + + case PV_IGNOREPAT: + ast_context_add_ignorepat2(context, p2->u1.str, registrar); + break; + + case PV_INCLUDES: + for (p3 = p2->u1.list; p3 ;p3=p3->next) { + if ( p3->u2.arglist ) { + snprintf(buf,sizeof(buf), "%s,%s,%s,%s,%s", + p3->u1.str, + p3->u2.arglist->u1.str, + p3->u2.arglist->next->u1.str, + p3->u2.arglist->next->next->u1.str, + p3->u2.arglist->next->next->next->u1.str); + ast_context_add_include2(context, buf, registrar); + } else + ast_context_add_include2(context, p3->u1.str, registrar); + } + break; + + case PV_SWITCHES: + for (p3 = p2->u1.list; p3 ;p3=p3->next) { + char *c = strchr(p3->u1.str, '/'); + if (c) { + *c = '\0'; + c++; + } else + c = ""; + + ast_context_add_switch2(context, p3->u1.str, c, 0, registrar); + } + break; + + case PV_ESWITCHES: + for (p3 = p2->u1.list; p3 ;p3=p3->next) { + char *c = strchr(p3->u1.str, '/'); + if (c) { + *c = '\0'; + c++; + } else + c = ""; + + ast_context_add_switch2(context, p3->u1.str, c, 1, registrar); + } + break; + default: + break; + } + } + + break; + + default: + /* huh? what? */ + break; + + } + } + /* moved these from being done after a macro or extension were processed, + to after all processing is done, for the sake of fixing gotos to labels inside cases... */ + /* I guess this would be considered 2nd pass of compiler now... */ + fix_gotos_in_extensions(exten_list); /* find and fix extension ref in gotos to labels that are in case statements */ + add_extensions(exten_list); /* actually makes calls to create priorities in ast_contexts -- feeds dialplan to asterisk */ + destroy_extensions(exten_list); /* all that remains is an empty husk, discard of it as is proper */ + +} + + +/* DESTROY the PVAL tree ============================================================================ */ + + + +void destroy_pval_item(pval *item) +{ + if (item == NULL) { + ast_log(LOG_WARNING, "null item\n"); + return; + } + + if (item->filename) + free(item->filename); + + switch (item->type) { + case PV_WORD: + /* fields: item->u1.str == string associated with this (word). */ + if (item->u1.str ) + free(item->u1.str); + if ( item->u2.arglist ) + destroy_pval(item->u2.arglist); + break; + + case PV_MACRO: + /* fields: item->u1.str == name of macro + item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user + item->u2.arglist->u1.str == argument + item->u2.arglist->next == next arg + + item->u3.macro_statements == pval list of statements in macro body. + */ + destroy_pval(item->u2.arglist); + if (item->u1.str ) + free(item->u1.str); + destroy_pval(item->u3.macro_statements); + break; + + case PV_CONTEXT: + /* fields: item->u1.str == name of context + item->u2.statements == pval list of statements in context body + item->u3.abstract == int 1 if an abstract keyword were present + */ + if (item->u1.str) + free(item->u1.str); + destroy_pval(item->u2.statements); + break; + + case PV_MACRO_CALL: + /* fields: item->u1.str == name of macro to call + item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user + item->u2.arglist->u1.str == argument + item->u2.arglist->next == next arg + */ + if (item->u1.str) + free(item->u1.str); + destroy_pval(item->u2.arglist); + break; + + case PV_APPLICATION_CALL: + /* fields: item->u1.str == name of application to call + item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user + item->u2.arglist->u1.str == argument + item->u2.arglist->next == next arg + */ + if (item->u1.str) + free(item->u1.str); + destroy_pval(item->u2.arglist); + break; + + case PV_CASE: + /* fields: item->u1.str == value of case + item->u2.statements == pval list of statements under the case + */ + if (item->u1.str) + free(item->u1.str); + destroy_pval(item->u2.statements); + break; + + case PV_PATTERN: + /* fields: item->u1.str == value of case + item->u2.statements == pval list of statements under the case + */ + if (item->u1.str) + free(item->u1.str); + destroy_pval(item->u2.statements); + break; + + case PV_DEFAULT: + /* fields: + item->u2.statements == pval list of statements under the case + */ + destroy_pval(item->u2.statements); + break; + + case PV_CATCH: + /* fields: item->u1.str == name of extension to catch + item->u2.statements == pval list of statements in context body + */ + if (item->u1.str) + free(item->u1.str); + destroy_pval(item->u2.statements); + break; + + case PV_SWITCHES: + /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list + */ + destroy_pval(item->u1.list); + break; + + case PV_ESWITCHES: + /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list + */ + destroy_pval(item->u1.list); + break; + + case PV_INCLUDES: + /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list + item->u2.arglist == pval list of 4 PV_WORD elements for time values + */ + destroy_pval(item->u1.list); + break; + + case PV_STATEMENTBLOCK: + /* fields: item->u1.list == pval list of statements in block, one per entry in the list + */ + destroy_pval(item->u1.list); + break; + + case PV_LOCALVARDEC: + case PV_VARDEC: + /* fields: item->u1.str == variable name + item->u2.val == variable value to assign + */ + if (item->u1.str) + free(item->u1.str); + if (item->u2.val) + free(item->u2.val); + break; + + case PV_GOTO: + /* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user. + item->u1.list->u1.str == where the data on a PV_WORD will always be. + */ + + destroy_pval(item->u1.list); + break; + + case PV_LABEL: + /* fields: item->u1.str == label name + */ + if (item->u1.str) + free(item->u1.str); + break; + + case PV_FOR: + /* fields: item->u1.for_init == a string containing the initalizer + item->u2.for_test == a string containing the loop test + item->u3.for_inc == a string containing the loop increment + + item->u4.for_statements == a pval list of statements in the for () + */ + if (item->u1.for_init) + free(item->u1.for_init); + if (item->u2.for_test) + free(item->u2.for_test); + if (item->u3.for_inc) + free(item->u3.for_inc); + destroy_pval(item->u4.for_statements); + break; + + case PV_WHILE: + /* fields: item->u1.str == the while conditional, as supplied by user + + item->u2.statements == a pval list of statements in the while () + */ + if (item->u1.str) + free(item->u1.str); + destroy_pval(item->u2.statements); + break; + + case PV_BREAK: + /* fields: none + */ + break; + + case PV_RETURN: + /* fields: none + */ + break; + + case PV_CONTINUE: + /* fields: none + */ + break; + + case PV_IFTIME: + /* fields: item->u1.list == the 4 time values, in PV_WORD structs, linked list + + item->u2.statements == a pval list of statements in the if () + item->u3.else_statements == a pval list of statements in the else + (could be zero) + */ + destroy_pval(item->u1.list); + destroy_pval(item->u2.statements); + if (item->u3.else_statements) { + destroy_pval(item->u3.else_statements); + } + break; + + case PV_RANDOM: + /* fields: item->u1.str == the random percentage, as supplied by user + + item->u2.statements == a pval list of statements in the true part () + item->u3.else_statements == a pval list of statements in the else + (could be zero) + fall thru to If */ + case PV_IF: + /* fields: item->u1.str == the if conditional, as supplied by user + + item->u2.statements == a pval list of statements in the if () + item->u3.else_statements == a pval list of statements in the else + (could be zero) + */ + if (item->u1.str) + free(item->u1.str); + destroy_pval(item->u2.statements); + if (item->u3.else_statements) { + destroy_pval(item->u3.else_statements); + } + break; + + case PV_SWITCH: + /* fields: item->u1.str == the switch expression + + item->u2.statements == a pval list of statements in the switch, + (will be case statements, most likely!) + */ + if (item->u1.str) + free(item->u1.str); + destroy_pval(item->u2.statements); + break; + + case PV_EXTENSION: + /* fields: item->u1.str == the extension name, label, whatever it's called + + item->u2.statements == a pval list of statements in the extension + item->u3.hints == a char * hint argument + item->u4.regexten == an int boolean. non-zero says that regexten was specified + */ + if (item->u1.str) + free(item->u1.str); + if (item->u3.hints) + free(item->u3.hints); + destroy_pval(item->u2.statements); + break; + + case PV_IGNOREPAT: + /* fields: item->u1.str == the ignorepat data + */ + if (item->u1.str) + free(item->u1.str); + break; + + case PV_GLOBALS: + /* fields: item->u1.statements == pval list of statements, usually vardecs + */ + destroy_pval(item->u1.statements); + break; + } + free(item); +} + +void destroy_pval(pval *item) +{ + pval *i,*nxt; + + for (i=item; i; i=nxt) { + nxt = i->next; + + destroy_pval_item(i); + } +} + +#ifdef AAL_ARGCHECK +static char *ael_funclist[] = +{ + "AGENT", + "ARRAY", + "BASE64_DECODE", + "BASE64_ENCODE", + "CALLERID", + "CDR", + "CHANNEL", + "CHECKSIPDOMAIN", + "CHECK_MD5", + "CURL", + "CUT", + "DB", + "DB_EXISTS", + "DUNDILOOKUP", + "ENUMLOOKUP", + "ENV", + "EVAL", + "EXISTS", + "FIELDQTY", + "FILTER", + "GROUP", + "GROUP_COUNT", + "GROUP_LIST", + "GROUP_MATCH_COUNT", + "IAXPEER", + "IF", + "IFTIME", + "ISNULL", + "KEYPADHASH", + "LANGUAGE", + "LEN", + "MATH", + "MD5", + "MUSICCLASS", + "QUEUEAGENTCOUNT", + "QUEUE_MEMBER_COUNT", + "QUEUE_MEMBER_LIST", + "QUOTE", + "RAND", + "REGEX", + "SET", + "SHA1", + "SIPCHANINFO", + "SIPPEER", + "SIP_HEADER", + "SORT", + "STAT", + "STRFTIME", + "STRPTIME", + "TIMEOUT", + "TXTCIDNAME", + "URIDECODE", + "URIENCODE", + "VMCOUNT" +}; + + +int ael_is_funcname(char *name) +{ + int s,t; + t = sizeof(ael_funclist)/sizeof(char*); + s = 0; + while ((s < t) && strcasecmp(name, ael_funclist[s])) + s++; + if ( s < t ) + return 1; + else + return 0; +} +#endif + + +/* PVAL PI */ + +/* ----------------- implementation ------------------- */ + + +int pvalCheckType( pval *p, char *funcname, pvaltype type ) +{ + if (p->type != type) + { + ast_log(LOG_ERROR, "Func: %s the pval passed is not appropriate for this function!\n", funcname); + return 0; + } + return 1; +} + + +pval *pvalCreateNode( pvaltype type ) +{ + pval *p = calloc(1,sizeof(pval)); /* why, oh why, don't I use ast_calloc? Way, way, way too messy if I do! */ + p->type = type; /* remember, this can be used externally or internally to asterisk */ + return p; +} + +pvaltype pvalObjectGetType( pval *p ) +{ + return p->type; +} + + +void pvalWordSetString( pval *p, char *str) +{ + if (!pvalCheckType(p, "pvalWordSetString", PV_WORD)) + return; + p->u1.str = str; +} + +char *pvalWordGetString( pval *p ) +{ + if (!pvalCheckType(p, "pvalWordGetString", PV_WORD)) + return 0; + return p->u1.str; +} + + +void pvalMacroSetName( pval *p, char *name) +{ + if (!pvalCheckType(p, "pvalMacroSetName", PV_MACRO)) + return; + p->u1.str = name; +} + +char *pvalMacroGetName( pval *p ) +{ + if (!pvalCheckType(p, "pvalMacroGetName", PV_MACRO)) + return 0; + return p->u1.str; +} + +void pvalMacroSetArglist( pval *p, pval *arglist ) +{ + if (!pvalCheckType(p, "pvalMacroSetArglist", PV_MACRO)) + return; + p->u2.arglist = arglist; +} + +void pvalMacroAddArg( pval *p, pval *arg ) /* single arg only! */ +{ + if (!pvalCheckType(p, "pvalMacroAddArg", PV_MACRO)) + return; + if (!p->u2.arglist) + p->u2.arglist = arg; + else + linku1(p->u2.arglist, arg); + +} + +pval *pvalMacroWalkArgs( pval *p, pval **arg ) +{ + if (!pvalCheckType(p, "pvalMacroWalkArgs", PV_MACRO)) + return 0; + if (!(*arg)) + *arg = p->u2.arglist; + else { + *arg = (*arg)->next; + } + return *arg; +} + +void pvalMacroAddStatement( pval *p, pval *statement ) +{ + if (!pvalCheckType(p, "pvalMacroAddStatement", PV_MACRO)) + return; + if (!p->u3.macro_statements) + p->u3.macro_statements = statement; + else + linku1(p->u3.macro_statements, statement); + + +} + +pval *pvalMacroWalkStatements( pval *p, pval **next_statement ) +{ + if (!pvalCheckType(p, "pvalMacroWalkStatements", PV_MACRO)) + return 0; + if (!(*next_statement)) + *next_statement = p->u3.macro_statements; + else { + *next_statement = (*next_statement)->next; + } + return *next_statement; +} + + + +void pvalContextSetName( pval *p, char *name) +{ + if (!pvalCheckType(p, "pvalContextSetName", PV_CONTEXT)) + return; + p->u1.str = name; +} + +char *pvalContextGetName( pval *p ) +{ + if (!pvalCheckType(p, "pvalContextGetName", PV_CONTEXT)) + return 0; + return p->u1.str; +} + +void pvalContextSetAbstract( pval *p ) +{ + if (!pvalCheckType(p, "pvalContextSetAbstract", PV_CONTEXT)) + return; + p->u3.abstract = 1; +} + +void pvalContextUnsetAbstract( pval *p ) +{ + if (!pvalCheckType(p, "pvalContextUnsetAbstract", PV_CONTEXT)) + return; + p->u3.abstract = 0; +} + +int pvalContextGetAbstract( pval *p ) +{ + if (!pvalCheckType(p, "pvalContextGetAbstract", PV_CONTEXT)) + return 0; + return p->u3.abstract; +} + + + +void pvalContextAddStatement( pval *p, pval *statement) /* this includes SWITCHES, INCLUDES, IGNOREPAT, etc */ +{ + if (!pvalCheckType(p, "pvalContextAddStatement", PV_CONTEXT)) + return; + if (!p->u2.statements) + p->u2.statements = statement; + else + linku1(p->u2.statements, statement); +} + +pval *pvalContextWalkStatements( pval *p, pval **statements ) +{ + if (!pvalCheckType(p, "pvalContextWalkStatements", PV_CONTEXT)) + return 0; + if (!(*statements)) + *statements = p->u2.statements; + else { + *statements = (*statements)->next; + } + return *statements; +} + + +void pvalMacroCallSetMacroName( pval *p, char *name ) +{ + if (!pvalCheckType(p, "pvalMacroCallSetMacroName", PV_MACRO_CALL)) + return; + p->u1.str = name; +} + +char* pvalMacroCallGetMacroName( pval *p ) +{ + if (!pvalCheckType(p, "pvalMacroCallGetMacroName", PV_MACRO_CALL)) + return 0; + return p->u1.str; +} + +void pvalMacroCallSetArglist( pval *p, pval *arglist ) +{ + if (!pvalCheckType(p, "pvalMacroCallSetArglist", PV_MACRO_CALL)) + return; + p->u2.arglist = arglist; +} + +void pvalMacroCallAddArg( pval *p, pval *arg ) +{ + if (!pvalCheckType(p, "pvalMacroCallGetAddArg", PV_MACRO_CALL)) + return; + if (!p->u2.arglist) + p->u2.arglist = arg; + else + linku1(p->u2.arglist, arg); +} + +pval *pvalMacroCallWalkArgs( pval *p, pval **args ) +{ + if (!pvalCheckType(p, "pvalMacroCallWalkArgs", PV_MACRO_CALL)) + return 0; + if (!(*args)) + *args = p->u2.arglist; + else { + *args = (*args)->next; + } + return *args; +} + + +void pvalAppCallSetAppName( pval *p, char *name ) +{ + if (!pvalCheckType(p, "pvalAppCallSetAppName", PV_APPLICATION_CALL)) + return; + p->u1.str = name; +} + +char* pvalAppCallGetAppName( pval *p ) +{ + if (!pvalCheckType(p, "pvalAppCallGetAppName", PV_APPLICATION_CALL)) + return 0; + return p->u1.str; +} + +void pvalAppCallSetArglist( pval *p, pval *arglist ) +{ + if (!pvalCheckType(p, "pvalAppCallSetArglist", PV_APPLICATION_CALL)) + return; + p->u2.arglist = arglist; +} + +void pvalAppCallAddArg( pval *p, pval *arg ) +{ + if (!pvalCheckType(p, "pvalAppCallAddArg", PV_APPLICATION_CALL)) + return; + if (!p->u2.arglist) + p->u2.arglist = arg; + else + linku1(p->u2.arglist, arg); +} + +pval *pvalAppCallWalkArgs( pval *p, pval **args ) +{ + if (!pvalCheckType(p, "pvalAppCallWalkArgs", PV_APPLICATION_CALL)) + return 0; + if (!(*args)) + *args = p->u2.arglist; + else { + *args = (*args)->next; + } + return *args; +} + + +void pvalCasePatSetVal( pval *p, char *val ) +{ + if (!pvalCheckType(p, "pvalAppCallWalkArgs", PV_APPLICATION_CALL)) + return; + p->u1.str = val; +} + +char* pvalCasePatGetVal( pval *p ) +{ + return p->u1.str; +} + +void pvalCasePatDefAddStatement( pval *p, pval *statement ) +{ + if (!p->u2.arglist) + p->u2.statements = statement; + else + linku1(p->u2.statements, statement); +} + +pval *pvalCasePatDefWalkStatements( pval *p, pval **statement ) +{ + if (!(*statement)) + *statement = p->u2.statements; + else { + *statement = (*statement)->next; + } + return *statement; +} + + +void pvalCatchSetExtName( pval *p, char *name ) +{ + if (!pvalCheckType(p, "pvalCatchSetExtName", PV_CATCH)) + return; + p->u1.str = name; +} + +char* pvalCatchGetExtName( pval *p ) +{ + if (!pvalCheckType(p, "pvalCatchGetExtName", PV_CATCH)) + return 0; + return p->u1.str; +} + +void pvalCatchSetStatement( pval *p, pval *statement ) +{ + if (!pvalCheckType(p, "pvalCatchSetStatement", PV_CATCH)) + return; + p->u2.statements = statement; +} + +pval *pvalCatchGetStatement( pval *p ) +{ + if (!pvalCheckType(p, "pvalCatchGetStatement", PV_CATCH)) + return 0; + return p->u2.statements; +} + + +void pvalSwitchesAddSwitch( pval *p, char *name ) +{ + pval *s; + if (!pvalCheckType(p, "pvalSwitchesAddSwitch", PV_SWITCHES)) + return; + s = pvalCreateNode(PV_WORD); + s->u1.str = name; + p->u1.list = linku1(p->u1.list, s); +} + +char* pvalSwitchesWalkNames( pval *p, pval **next_item ) +{ + if (!pvalCheckType(p, "pvalSwitchesWalkNames", PV_SWITCHES)) + return 0; + if (!(*next_item)) + *next_item = p->u1.list; + else { + *next_item = (*next_item)->next; + } + return (*next_item)->u1.str; +} + +void pvalESwitchesAddSwitch( pval *p, char *name ) +{ + pval *s; + if (!pvalCheckType(p, "pvalESwitchesAddSwitch", PV_ESWITCHES)) + return; + s = pvalCreateNode(PV_WORD); + s->u1.str = name; + p->u1.list = linku1(p->u1.list, s); +} + +char* pvalESwitchesWalkNames( pval *p, pval **next_item ) +{ + if (!pvalCheckType(p, "pvalESwitchesWalkNames", PV_ESWITCHES)) + return 0; + if (!(*next_item)) + *next_item = p->u1.list; + else { + *next_item = (*next_item)->next; + } + return (*next_item)->u1.str; +} + + +void pvalIncludesAddInclude( pval *p, const char *include ) +{ + pval *s; + if (!pvalCheckType(p, "pvalIncludesAddSwitch", PV_INCLUDES)) + return; + s = pvalCreateNode(PV_WORD); + s->u1.str = (char *)include; + p->u1.list = linku1(p->u1.list, s); +} + /* an include is a WORD with string set to path */ + +void pvalIncludesAddIncludeWithTimeConstraints( pval *p, const char *include, char *hour_range, char *dom_range, char *dow_range, char *month_range ) +{ + pval *hr = pvalCreateNode(PV_WORD); + pval *dom = pvalCreateNode(PV_WORD); + pval *dow = pvalCreateNode(PV_WORD); + pval *mon = pvalCreateNode(PV_WORD); + pval *s = pvalCreateNode(PV_WORD); + + if (!pvalCheckType(p, "pvalIncludeAddIncludeWithTimeConstraints", PV_INCLUDES)) + return; + + s->u1.str = (char *)include; + p->u1.list = linku1(p->u1.list, s); + + hr->u1.str = hour_range; + dom->u1.str = dom_range; + dow->u1.str = dow_range; + mon->u1.str = month_range; + + s->u2.arglist = hr; + + hr->next = dom; + dom->next = dow; + dow->next = mon; + mon->next = 0; +} + /* is this right??? come back and correct it */ /*the ptr is to the WORD */ +void pvalIncludeGetTimeConstraints( pval *p, char **hour_range, char **dom_range, char **dow_range, char **month_range ) +{ + if (!pvalCheckType(p, "pvalIncludeGetTimeConstraints", PV_WORD)) + return; + if (p->u2.arglist) { + *hour_range = p->u2.arglist->u1.str; + *dom_range = p->u2.arglist->next->u1.str; + *dow_range = p->u2.arglist->next->next->u1.str; + *month_range = p->u2.arglist->next->next->next->u1.str; + } else { + *hour_range = 0; + *dom_range = 0; + *dow_range = 0; + *month_range = 0; + } +} + /* is this right??? come back and correct it */ /*the ptr is to the WORD */ +char* pvalIncludesWalk( pval *p, pval **next_item ) +{ + if (!pvalCheckType(p, "pvalIncludesWalk", PV_INCLUDES)) + return 0; + if (!(*next_item)) + *next_item = p->u1.list; + else { + *next_item = (*next_item)->next; + } + return (*next_item)->u1.str; +} + + +void pvalStatementBlockAddStatement( pval *p, pval *statement) +{ + if (!pvalCheckType(p, "pvalStatementBlockAddStatement", PV_STATEMENTBLOCK)) + return; + p->u1.list = linku1(p->u1.list, statement); +} + +pval *pvalStatementBlockWalkStatements( pval *p, pval **next_statement) +{ + if (!pvalCheckType(p, "pvalStatementBlockWalkStatements", PV_STATEMENTBLOCK)) + return 0; + if (!(*next_statement)) + *next_statement = p->u1.list; + else { + *next_statement = (*next_statement)->next; + } + return *next_statement; +} + +void pvalVarDecSetVarname( pval *p, char *name ) +{ + if (!pvalCheckType(p, "pvalVarDecSetVarname", PV_VARDEC)) + return; + p->u1.str = name; +} + +void pvalVarDecSetValue( pval *p, char *value ) +{ + if (!pvalCheckType(p, "pvalVarDecSetValue", PV_VARDEC)) + return; + p->u2.val = value; +} + +char* pvalVarDecGetVarname( pval *p ) +{ + if (!pvalCheckType(p, "pvalVarDecGetVarname", PV_VARDEC)) + return 0; + return p->u1.str; +} + +char* pvalVarDecGetValue( pval *p ) +{ + if (!pvalCheckType(p, "pvalVarDecGetValue", PV_VARDEC)) + return 0; + return p->u2.val; +} + +void pvalGotoSetTarget( pval *p, char *context, char *exten, char *label ) +{ + pval *con, *ext, *pri; + + if (!pvalCheckType(p, "pvalGotoSetTarget", PV_GOTO)) + return; + if (context && strlen(context)) { + con = pvalCreateNode(PV_WORD); + ext = pvalCreateNode(PV_WORD); + pri = pvalCreateNode(PV_WORD); + + con->u1.str = context; + ext->u1.str = exten; + pri->u1.str = label; + + con->next = ext; + ext->next = pri; + p->u1.list = con; + } else if (exten && strlen(exten)) { + ext = pvalCreateNode(PV_WORD); + pri = pvalCreateNode(PV_WORD); + + ext->u1.str = exten; + pri->u1.str = label; + + ext->next = pri; + p->u1.list = ext; + } else { + pri = pvalCreateNode(PV_WORD); + + pri->u1.str = label; + + p->u1.list = pri; + } +} + +void pvalGotoGetTarget( pval *p, char **context, char **exten, char **label ) +{ + if (!pvalCheckType(p, "pvalGotoGetTarget", PV_GOTO)) + return; + if (p->u1.list && p->u1.list->next && p->u1.list->next->next) { + *context = p->u1.list->u1.str; + *exten = p->u1.list->next->u1.str; + *label = p->u1.list->next->next->u1.str; + + } else if (p->u1.list && p->u1.list->next ) { + *exten = p->u1.list->u1.str; + *label = p->u1.list->next->u1.str; + *context = 0; + + } else if (p->u1.list) { + *label = p->u1.list->u1.str; + *context = 0; + *exten = 0; + + } else { + *context = 0; + *exten = 0; + *label = 0; + } +} + + +void pvalLabelSetName( pval *p, char *name ) +{ + if (!pvalCheckType(p, "pvalLabelSetName", PV_LABEL)) + return; + p->u1.str = name; +} + +char* pvalLabelGetName( pval *p ) +{ + if (!pvalCheckType(p, "pvalLabelGetName", PV_LABEL)) + return 0; + return p->u1.str; +} + + +void pvalForSetInit( pval *p, char *init ) +{ + if (!pvalCheckType(p, "pvalForSetInit", PV_FOR)) + return; + p->u1.for_init = init; +} + +void pvalForSetTest( pval *p, char *test ) +{ + if (!pvalCheckType(p, "pvalForSetTest", PV_FOR)) + return; + p->u2.for_test = test; +} + +void pvalForSetInc( pval *p, char *inc ) +{ + if (!pvalCheckType(p, "pvalForSetInc", PV_FOR)) + return; + p->u3.for_inc = inc; +} + +void pvalForSetStatement( pval *p, pval *statement ) +{ + if (!pvalCheckType(p, "pvalForSetStatement", PV_FOR)) + return; + p->u4.for_statements = statement; +} + +char* pvalForGetInit( pval *p ) +{ + if (!pvalCheckType(p, "pvalForGetInit", PV_FOR)) + return 0; + return p->u1.for_init; +} + +char* pvalForGetTest( pval *p ) +{ + if (!pvalCheckType(p, "pvalForGetTest", PV_FOR)) + return 0; + return p->u2.for_test; +} + +char* pvalForGetInc( pval *p ) +{ + if (!pvalCheckType(p, "pvalForGetInc", PV_FOR)) + return 0; + return p->u3.for_inc; +} + +pval* pvalForGetStatement( pval *p ) +{ + if (!pvalCheckType(p, "pvalForGetStatement", PV_FOR)) + return 0; + return p->u4.for_statements; +} + + + +void pvalIfSetCondition( pval *p, char *expr ) +{ + if (!pvalCheckType(p, "pvalIfSetCondition", PV_IF)) + return; + p->u1.str = expr; +} + +char* pvalIfGetCondition( pval *p ) +{ + if (!pvalCheckType(p, "pvalIfGetCondition", PV_IFTIME)) + return 0; + return p->u1.str; +} + +void pvalIfTimeSetCondition( pval *p, char *hour_range, char *dow_range, char *dom_range, char *mon_range ) /* time range format: 24-hour format begin-end|dow range|dom range|month range */ +{ + pval *hr = pvalCreateNode(PV_WORD); + pval *dow = pvalCreateNode(PV_WORD); + pval *dom = pvalCreateNode(PV_WORD); + pval *mon = pvalCreateNode(PV_WORD); + if (!pvalCheckType(p, "pvalIfTimeSetCondition", PV_IFTIME)) + return; + pvalWordSetString(hr, hour_range); + pvalWordSetString(dow, dow_range); + pvalWordSetString(dom, dom_range); + pvalWordSetString(mon, mon_range); + dom->next = mon; + dow->next = dom; + hr->next = dow; + p->u1.list = hr; +} + + /* is this right??? come back and correct it */ +void pvalIfTimeGetCondition( pval *p, char **hour_range, char **dow_range, char **dom_range, char **month_range ) +{ + if (!pvalCheckType(p, "pvalIfTimeGetCondition", PV_IFTIME)) + return; + *hour_range = p->u1.list->u1.str; + *dow_range = p->u1.list->next->u1.str; + *dom_range = p->u1.list->next->next->u1.str; + *month_range = p->u1.list->next->next->next->u1.str; +} + +void pvalRandomSetCondition( pval *p, char *percent ) +{ + if (!pvalCheckType(p, "pvalRandomSetCondition", PV_RANDOM)) + return; + p->u1.str = percent; +} + +char* pvalRandomGetCondition( pval *p ) +{ + if (!pvalCheckType(p, "pvalRandomGetCondition", PV_RANDOM)) + return 0; + return p->u1.str; +} + +void pvalConditionalSetThenStatement( pval *p, pval *statement ) +{ + p->u2.statements = statement; +} + +void pvalConditionalSetElseStatement( pval *p, pval *statement ) +{ + p->u3.else_statements = statement; +} + +pval* pvalConditionalGetThenStatement( pval *p ) +{ + return p->u2.statements; +} + +pval* pvalConditionalGetElseStatement( pval *p ) +{ + return p->u3.else_statements; +} + +void pvalSwitchSetTestexpr( pval *p, char *expr ) +{ + if (!pvalCheckType(p, "pvalSwitchSetTestexpr", PV_SWITCH)) + return; + p->u1.str = expr; +} + +char* pvalSwitchGetTestexpr( pval *p ) +{ + if (!pvalCheckType(p, "pvalSwitchGetTestexpr", PV_SWITCH)) + return 0; + return p->u1.str; +} + +void pvalSwitchAddCase( pval *p, pval *Case ) +{ + if (!pvalCheckType(p, "pvalSwitchAddCase", PV_SWITCH)) + return; + if (!pvalCheckType(Case, "pvalSwitchAddCase", PV_CASE)) + return; + if (!p->u2.statements) + p->u2.statements = Case; + else + linku1(p->u2.statements, Case); +} + +pval* pvalSwitchWalkCases( pval *p, pval **next_case ) +{ + if (!pvalCheckType(p, "pvalSwitchWalkCases", PV_SWITCH)) + return 0; + if (!(*next_case)) + *next_case = p->u2.statements; + else { + *next_case = (*next_case)->next; + } + return *next_case; +} + + +void pvalExtenSetName( pval *p, char *name ) +{ + if (!pvalCheckType(p, "pvalExtenSetName", PV_EXTENSION)) + return; + p->u1.str = name; +} + +char* pvalExtenGetName( pval *p ) +{ + if (!pvalCheckType(p, "pvalExtenGetName", PV_EXTENSION)) + return 0; + return p->u1.str; +} + +void pvalExtenSetRegexten( pval *p ) +{ + if (!pvalCheckType(p, "pvalExtenSetRegexten", PV_EXTENSION)) + return; + p->u4.regexten = 1; +} + +void pvalExtenUnSetRegexten( pval *p ) +{ + if (!pvalCheckType(p, "pvalExtenUnSetRegexten", PV_EXTENSION)) + return; + p->u4.regexten = 0; +} + +int pvalExtenGetRegexten( pval *p ) +{ + if (!pvalCheckType(p, "pvalExtenGetRegexten", PV_EXTENSION)) + return 0; + return p->u4.regexten; +} + +void pvalExtenSetHints( pval *p, char *hints ) +{ + if (!pvalCheckType(p, "pvalExtenSetHints", PV_EXTENSION)) + return; + p->u3.hints = hints; +} + +char* pvalExtenGetHints( pval *p ) +{ + if (!pvalCheckType(p, "pvalExtenGetHints", PV_EXTENSION)) + return 0; + return p->u3.hints; +} + +void pvalExtenSetStatement( pval *p, pval *statement ) +{ + if (!pvalCheckType(p, "pvalExtenSetStatement", PV_EXTENSION)) + return; + p->u2.statements = statement; +} + +pval* pvalExtenGetStatement( pval *p ) +{ + if (!pvalCheckType(p, "pvalExtenGetStatement", PV_EXTENSION)) + return 0; + return p->u2.statements; +} + + +void pvalIgnorePatSetPattern( pval *p, char *pat ) +{ + if (!pvalCheckType(p, "pvalIgnorePatSetPattern", PV_IGNOREPAT)) + return; + p->u1.str = pat; +} + +char* pvalIgnorePatGetPattern( pval *p ) +{ + if (!pvalCheckType(p, "pvalIgnorePatGetPattern", PV_IGNOREPAT)) + return 0; + return p->u1.str; +} + + +void pvalGlobalsAddStatement( pval *p, pval *statement ) +{ + if (p->type != PV_GLOBALS) { + ast_log(LOG_ERROR, "pvalGlobalsAddStatement called where first arg is not a Globals!\n"); + } else { + if (!p->u1.statements) { + p->u1.statements = statement; + } else { + p->u1.statements = linku1(p->u1.statements,statement); + } + } +} + +pval* pvalGlobalsWalkStatements( pval *p, pval **next_statement ) +{ + if (!pvalCheckType(p, "pvalGlobalsWalkStatements", PV_GLOBALS)) + return 0; + if (!next_statement) { + *next_statement = p; + return p; + } else { + *next_statement = (*next_statement)->next; + return (*next_statement)->next; + } +} + + +void pvalTopLevAddObject( pval *p, pval *contextOrObj ) +{ + if (p) { + linku1(p,contextOrObj); + } else { + ast_log(LOG_ERROR, "First arg to pvalTopLevel is NULL!\n"); + } +} + +pval *pvalTopLevWalkObjects(pval *p, pval **next_obj ) +{ + if (!next_obj) { + *next_obj = p; + return p; + } else { + *next_obj = (*next_obj)->next; + return (*next_obj)->next; + } +} + +/* append second element to the list in the first one via next pointers */ +pval * linku1(pval *head, pval *tail) +{ + if (!head) + return tail; + if (tail) { + if (!head->next) { + head->next = tail; + } else { + head->u1_last->next = tail; + } + head->u1_last = tail; + tail->prev = head; /* the dad link only points to containers */ + } + return head; +} + +#ifdef HERE_BY_MISTAKE_I_THINK +static char *config = "extensions.ael"; +int do_pbx_load_module(void) +{ + int errs, sem_err, sem_warn, sem_note; + char *rfilename; + struct ast_context *local_contexts=NULL, *con; + struct pval *parse_tree; + + ast_log(LOG_NOTICE, "Starting AEL load process.\n"); + if (config[0] == '/') + rfilename = (char *)config; + else { + rfilename = alloca(strlen(config) + strlen(ast_config_AST_CONFIG_DIR) + 2); + sprintf(rfilename, "%s/%s", ast_config_AST_CONFIG_DIR, config); + } + ast_log(LOG_NOTICE, "AEL load process: calculated config file name '%s'.\n", rfilename); + + if (access(rfilename,R_OK) != 0) { + ast_log(LOG_NOTICE, "File %s not found; AEL declining load\n", rfilename); + return AST_MODULE_LOAD_DECLINE; + } + + parse_tree = ael2_parse(rfilename, &errs); + ast_log(LOG_NOTICE, "AEL load process: parsed config file name '%s'.\n", rfilename); + ael2_semantic_check(parse_tree, &sem_err, &sem_warn, &sem_note); + if (errs == 0 && sem_err == 0) { + ast_log(LOG_NOTICE, "AEL load process: checked config file name '%s'.\n", rfilename); + ast_compile_ael2(&local_contexts, parse_tree); + ast_log(LOG_NOTICE, "AEL load process: compiled config file name '%s'.\n", rfilename); + + ast_merge_contexts_and_delete(&local_contexts, registrar); + ast_log(LOG_NOTICE, "AEL load process: merged config file name '%s'.\n", rfilename); + for (con = ast_walk_contexts(NULL); con; con = ast_walk_contexts(con)) + ast_context_verify_includes(con); + ast_log(LOG_NOTICE, "AEL load process: verified config file name '%s'.\n", rfilename); + } else { + ast_log(LOG_ERROR, "Sorry, but %d syntax errors and %d semantic errors were detected. It doesn't make sense to compile.\n", errs, sem_err); + destroy_pval(parse_tree); /* free up the memory */ + return AST_MODULE_LOAD_FAILURE; + } + destroy_pval(parse_tree); /* free up the memory */ + + return AST_MODULE_LOAD_SUCCESS; +} +#endif + diff --git a/res/res_ael_share.c b/res/res_ael_share.c new file mode 100644 index 0000000000000000000000000000000000000000..7c4dc70a8e795f255f309f4483cf627a38177c53 --- /dev/null +++ b/res/res_ael_share.c @@ -0,0 +1,61 @@ +/* + * 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. + */ + +/*! \file + * + * \brief Shareable AEL code -- mainly between internal and external modules + * + * \author Steve Murphy <murf@digium.com> + * + * \ingroup applications + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include "asterisk/file.h" +#include "asterisk/logger.h" +#include "asterisk/channel.h" +#include "asterisk/options.h" +#include "asterisk/pbx.h" +#include "asterisk/config.h" +#include "asterisk/module.h" +#include "asterisk/lock.h" +#include "asterisk/cli.h" + + +static int unload_module(void) +{ + return 0; +} + +static int load_module(void) +{ + return 0; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "share-able code for AEL", + .load = load_module, + .unload = unload_module + ); diff --git a/utils/Makefile b/utils/Makefile index fb6fb60a352c330e5d14faa3c182b9328ab0b188..246eac266531c765afb040d2aa9d1ce68cabe627 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -16,7 +16,7 @@ .PHONY: clean all uninstall # to get check_expr, add it to the ALL_UTILS list -ALL_UTILS:=astman smsq stereorize streamplayer aelparse muted check_expr +ALL_UTILS:=astman smsq stereorize streamplayer aelparse muted check_expr conf2ael UTILS:=$(ALL_UTILS) include $(ASTTOPDIR)/Makefile.rules @@ -40,6 +40,7 @@ endif ifneq ($(filter pbx_ael,$(MENUSELECT_PBX)),) UTILS:=$(filter-out aelparse,$(UTILS)) + UTILS:=$(filter-out conf2ael,$(UTILS)) endif all: $(UTILS) @@ -57,8 +58,8 @@ uninstall: clean: rm -f *.o $(ALL_UTILS) check_expr rm -f .*.o.d .*.oo.d - rm -f md5.c strcompat.c ast_expr2.c ast_expr2f.c pbx_ael.c - rm -f aelparse.c aelbison.c + rm -f md5.c strcompat.c ast_expr2.c ast_expr2f.c pbx_ael.c pval.c + rm -f aelparse.c aelbison.c conf2ael md5.c: ../main/md5.c @cp $< $@ @@ -80,6 +81,9 @@ strcompat.c: ../main/strcompat.c @echo " [FLEX] ../main/ast_expr2.fl -> $@" @flex -o $@ --full ../main/ast_expr2.fl +pval.c: ../res/ael/pval.c + @cp $< $@ + ast_expr2.c: ../main/ast_expr2.c @cp $< $@ @@ -88,12 +92,14 @@ ast_expr2f.c: ../main/ast_expr2f.c ast_expr2f.o: ASTCFLAGS+=-DSTANDALONE_AEL -I../main +pval.o : ASTCFLAGS+=-DSTANDALONE + check_expr: check_expr.o ast_expr2.o ast_expr2f.o -aelbison.c: ../pbx/ael/ael.tab.c +aelbison.c: ../res/ael/ael.tab.c @cp $< $@ -aelbison.o: aelbison.c ../pbx/ael/ael.tab.h ../include/asterisk/ael_structs.h -aelbison.o: ASTCFLAGS+=-I../pbx/ael -DYYENABLE_NLS=0 +aelbison.o: aelbison.c ../res/ael/ael.tab.h ../include/asterisk/ael_structs.h +aelbison.o: ASTCFLAGS+=-I../res/ael -DYYENABLE_NLS=0 pbx_ael.c: ../pbx/pbx_ael.c @cp $< $@ @@ -101,12 +107,17 @@ pbx_ael.o: ASTCFLAGS+=-DSTANDALONE_AEL ael_main.o: ael_main.c ../include/asterisk/ael_structs.h -aelparse.c: ../pbx/ael/ael_lex.c +aelparse.c: ../res/ael/ael_lex.c @cp $< $@ -aelparse.o: aelparse.c ../include/asterisk/ael_structs.h ../pbx/ael/ael.tab.h -aelparse.o: ASTCFLAGS+=-I../pbx -DSTANDALONE_AEL +aelparse.o: aelparse.c ../include/asterisk/ael_structs.h ../res/ael/ael.tab.h +aelparse.o: ASTCFLAGS+=-I../res -DSTANDALONE_AEL + +aelparse: aelparse.o aelbison.o pbx_ael.o ael_main.o ast_expr2f.o ast_expr2.o strcompat.o pval.o extconf.o + +extconf.o : extconf.c + +conf2ael: conf2ael.o ast_expr2f.o ast_expr2.o aelbison.o aelparse.o pbx_ael.o pval.o extconf.o -aelparse: aelparse.o aelbison.o pbx_ael.o ael_main.o ast_expr2f.o ast_expr2.o strcompat.o testexpr2s: ../main/ast_expr2f.c ../main/ast_expr2.c ../main/ast_expr2.h $(CC) -g -c -I../include -DSTANDALONE_AEL ../main/ast_expr2f.c -o ast_expr2f.o diff --git a/utils/ael_main.c b/utils/ael_main.c index 76562c3812c3140f743687487136b28a644aed21..6c166922f037bc047d33236e643ad2b25123300e 100644 --- a/utils/ael_main.c +++ b/utils/ael_main.c @@ -10,11 +10,13 @@ #include <limits.h> #include "asterisk/compat.h" -#include "asterisk/ast_expr.h" #include "asterisk/channel.h" +#include "asterisk/ast_expr.h" +#include "asterisk/ast_expr.h" #include "asterisk/module.h" #include "asterisk/app.h" #include "asterisk/ael_structs.h" +#include "asterisk/extconf.h" struct namelist { @@ -106,6 +108,41 @@ static int dump_extensions = 0; static int FIRST_TIME = 0; static FILE *dumpfile; +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); +} + +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); +} + struct ast_app *pbx_findapp(const char *app) { return (struct ast_app*)1; /* so as not to trigger an error */ @@ -390,56 +427,6 @@ void ast_context_destroy(void) printf("Executed ast_context_destroy();\n"); } -void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...) -{ - va_list vars; - va_start(vars,fmt); - if( !quiet || level > 2 ) { - printf("LOG: lev:%d file:%s line:%d func: %s ", - level, file, line, function); - vprintf(fmt, vars); - fflush(stdout); - va_end(vars); - } -} - -void ast_verbose(const char *fmt, ...) -{ - va_list vars; - va_start(vars,fmt); - - printf("VERBOSE: "); - vprintf(fmt, vars); - fflush(stdout); - va_end(vars); -} - -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; -} - void filter_leading_space_from_exprs(char *str) { /* Mainly for aesthetics */ @@ -478,7 +465,8 @@ void filter_newlines(char *str) extern struct module_symbols mod_data; -extern int ael_external_load_module(void); +int ael_external_load_module(void); + int main(int argc, char **argv) { @@ -511,9 +499,11 @@ int main(int argc, char **argv) if( use_curr_dir ) { strcpy(ast_config_AST_CONFIG_DIR, "."); + localized_use_local_dir(); } else { strcpy(ast_config_AST_CONFIG_DIR, "/etc/asterisk"); + localized_use_conf_dir(); } strcpy(ast_config_AST_VAR_DIR, "/var/lib/asterisk"); diff --git a/utils/check_expr.c b/utils/check_expr.c index f64147242d5d5b0cd2f1a12b6189c9cb3987bd77..db787753962fc8c5f4bfdae98fb8bc6e602c8055 100644 --- a/utils/check_expr.c +++ b/utils/check_expr.c @@ -21,8 +21,67 @@ #include <stdarg.h> #include <string.h> #include <stdlib.h> + +struct ast_channel +{ + char x; /* basically empty! */ +}; + #include <../include/asterisk/compat.h> #include <../include/asterisk/ast_expr.h> +#define AST_API_MODULE 1 +#include "asterisk/inline_api.h" +#include "asterisk/strings.h" + +/* I included this from utils.c, so as not to have everything in that .c + file included */ +/*! + * core handler for dynamic strings. + * This is not meant to be called directly, but rather through the + * various wrapper macros + * ast_str_set(...) + * ast_str_append(...) + * ast_str_set_va(...) + * ast_str_append_va(...) + */ +int __ast_str_helper(struct ast_str **buf, size_t max_len, + int append, const char *fmt, va_list ap) +{ + int res, need; + int offset = (append && (*buf)->len) ? (*buf)->used : 0; + + if (max_len < 0) + max_len = (*buf)->len; /* don't exceed the allocated space */ + /* + * Ask vsnprintf how much space we need. Remember that vsnprintf + * does not count the final '\0' so we must add 1. + */ + res = vsnprintf((*buf)->str + offset, (*buf)->len - offset, fmt, ap); + + need = res + offset + 1; + /* + * If there is not enough space and we are below the max length, + * reallocate the buffer and return a message telling to retry. + */ + if (need > (*buf)->len && (max_len == 0 || (*buf)->len < max_len) ) { + if (max_len && max_len < need) /* truncate as needed */ + need = max_len; + else if (max_len == 0) /* if unbounded, give more room for next time */ + need += 16 + need/4; + if (ast_str_make_space(buf, need)) { + return AST_DYNSTR_BUILD_FAILED; + } + (*buf)->str[offset] = '\0'; /* Truncate the partial write. */ + + /* va_end() and va_start() must be done before calling + * vsnprintf() again. */ + return AST_DYNSTR_BUILD_RETRY; + } + /* update space used, keep in mind the truncation */ + (*buf)->used = (res + offset > (*buf)->len) ? (*buf)->len : res + offset; + + return res; +} static int global_lineno = 1; static int global_expr_count=0; @@ -83,6 +142,8 @@ char *find_var(const char *varname) /* the list should be pretty short, if there return 0; } +void set_var(const char *varname, const char *varval); + void set_var(const char *varname, const char *varval) { struct varz *t = (struct varz*)calloc(1,sizeof(struct varz)); @@ -162,6 +223,8 @@ unsigned int check_expr(char* buffer, char* error_report) return warn_found; } +int check_eval(char *buffer, char *error_report); + struct ast_custom_function *ast_custom_function_find(const char *name); struct ast_custom_function *ast_custom_function_find(const char *name) @@ -240,6 +303,8 @@ int check_eval(char *buffer, char *error_report) } +void parse_file(const char *fname); + void parse_file(const char *fname) { FILE *f = fopen(fname,"r");