2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2006, Digium, Inc.
6 * Steve Murphy <murf@parsetree.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief Compile symbolic Asterisk Extension Logic into Asterisk extensions, version 2.
26 <depend>res_ael_share</depend>
31 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
33 #include <sys/types.h>
43 #include "asterisk/pbx.h"
44 #include "asterisk/config.h"
45 #include "asterisk/module.h"
46 #include "asterisk/logger.h"
47 #include "asterisk/cli.h"
48 #include "asterisk/app.h"
49 #include "asterisk/callerid.h"
50 #include "asterisk/ael_structs.h"
51 #include "asterisk/pval.h"
53 #include "asterisk/argdesc.h"
56 /* these functions are in ../ast_expr2.fl */
58 #define DEBUG_READ (1 << 0)
59 #define DEBUG_TOKENS (1 << 1)
60 #define DEBUG_MACROS (1 << 2)
61 #define DEBUG_CONTEXTS (1 << 3)
63 static char *config = "extensions.ael";
64 static char *registrar = "pbx_ael";
65 static int pbx_load_module(void);
66 static int warns, errs;
67 static struct pval *current_db;
70 /* for the time being, short circuit all the AAL related structures
71 without permanently removing the code; after/during the AAL
72 development, this code can be properly re-instated
78 int option_matches_j( struct argdesc *should, pval *is, struct argapp *app);
79 int option_matches( struct argdesc *should, pval *is, struct argapp *app);
80 int ael_is_funcname(char *name);
83 int check_app_args(pval *appcall, pval *arglist, struct argapp *app);
84 void check_pval(pval *item, struct argapp *apps, int in_globals);
85 void check_pval_item(pval *item, struct argapp *apps, int in_globals);
86 void check_switch_expr(pval *item, struct argapp *apps);
87 void ast_expr_register_extra_error_info(char *errmsg);
88 void ast_expr_clear_extra_error_info(void);
89 struct pval *find_macro(char *name);
90 struct pval *find_context(char *name);
91 struct pval *find_context(char *name);
92 struct pval *find_macro(char *name);
93 struct ael_priority *new_prio(void);
94 struct ael_extension *new_exten(void);
95 void linkprio(struct ael_extension *exten, struct ael_priority *prio);
96 void destroy_extensions(struct ael_extension *exten);
97 void set_priorities(struct ael_extension *exten);
98 void add_extensions(struct ael_extension *exten);
99 void ast_compile_ael2(struct ast_context **local_contexts, struct pval *root);
100 void destroy_pval(pval *item);
101 void destroy_pval_item(pval *item);
102 int is_float(char *arg );
103 int is_int(char *arg );
104 int is_empty(char *arg);
106 static const char *match_context;
107 static const char *match_exten;
108 static const char *match_label;
109 static int count_labels; /* true, put matcher in label counting mode */
110 static int return_on_context_match;
111 struct pval *match_pval(pval *item);
112 static void check_goto(pval *item);
113 static void find_pval_goto_item(pval *item, int lev);
114 static void find_pval_gotos(pval *item, int lev);
116 static struct pval *find_label_in_current_context(char *exten, char *label, pval *curr_cont);
117 static struct pval *find_first_label_in_current_context(char *label, pval *curr_cont);
118 static void print_pval_list(FILE *fin, pval *item, int depth);
120 static struct pval *find_label_in_current_extension(const char *label, pval *curr_ext);
121 static struct pval *find_label_in_current_db(const char *context, const char *exten, const char *label);
122 static pval *get_goto_target(pval *item);
123 static int label_inside_case(pval *label);
124 static pval *get_extension_or_contxt(pval *p);
125 static pval *get_contxt(pval *p);
126 static void remove_spaces_before_equals(char *str);
127 /* static void substitute_commas(char *str); */
129 /*! \brief I am adding this code to substitute commas with vertbars in the args to apps */
130 static void substitute_commas(char *str)
136 if (*p == ',' && ((p != str && *(p-1) != '\\')
139 if (*p == '\\' && *(p+1) == ',') { /* learning experience: the '\,' is turned into just ',' by pbx_config; So we need to do the same */
141 while (*q) { /* move the ',' and everything after it up 1 char */
151 /* PRETTY PRINTER FOR AEL: ============================================================================= */
153 static void print_pval(FILE *fin, pval *item, int depth)
158 for (i=0; i<depth; i++) {
159 fprintf(fin, "\t"); /* depth == indentation */
162 switch ( item->type ) {
164 fprintf(fin,"%s;\n", item->u1.str); /* usually, words are encapsulated in something else */
168 fprintf(fin,"macro %s(", item->u1.str);
169 for (lp=item->u2.arglist; lp; lp=lp->next) {
170 if (lp != item->u2.arglist )
172 fprintf(fin,"%s", lp->u1.str);
174 fprintf(fin,") {\n");
175 print_pval_list(fin,item->u3.macro_statements,depth+1);
176 for (i=0; i<depth; i++) {
177 fprintf(fin,"\t"); /* depth == indentation */
179 fprintf(fin,"};\n\n");
183 if ( item->u3.abstract )
184 fprintf(fin,"abstract context %s {\n", item->u1.str);
186 fprintf(fin,"context %s {\n", item->u1.str);
187 print_pval_list(fin,item->u2.statements,depth+1);
188 for (i=0; i<depth; i++) {
189 fprintf(fin,"\t"); /* depth == indentation */
191 fprintf(fin,"};\n\n");
195 fprintf(fin,"&%s(", item->u1.str);
196 for (lp=item->u2.arglist; lp; lp=lp->next) {
197 if ( lp != item->u2.arglist )
199 fprintf(fin,"%s", lp->u1.str);
204 case PV_APPLICATION_CALL:
205 fprintf(fin,"%s(", item->u1.str);
206 for (lp=item->u2.arglist; lp; lp=lp->next) {
207 if ( lp != item->u2.arglist )
209 fprintf(fin,"%s", lp->u1.str);
215 fprintf(fin,"case %s:\n", item->u1.str);
216 print_pval_list(fin,item->u2.statements, depth+1);
220 fprintf(fin,"pattern %s:\n", item->u1.str);
221 print_pval_list(fin,item->u2.statements, depth+1);
225 fprintf(fin,"default:\n");
226 print_pval_list(fin,item->u2.statements, depth+1);
230 fprintf(fin,"catch %s {\n", item->u1.str);
231 print_pval_list(fin,item->u2.statements, depth+1);
232 for (i=0; i<depth; i++) {
233 fprintf(fin,"\t"); /* depth == indentation */
239 fprintf(fin,"switches {\n");
240 print_pval_list(fin,item->u1.list,depth+1);
241 for (i=0; i<depth; i++) {
242 fprintf(fin,"\t"); /* depth == indentation */
248 fprintf(fin,"eswitches {\n");
249 print_pval_list(fin,item->u1.list,depth+1);
250 for (i=0; i<depth; i++) {
251 fprintf(fin,"\t"); /* depth == indentation */
257 fprintf(fin,"includes {\n");
258 for (lp=item->u1.list; lp; lp=lp->next) {
259 for (i=0; i<depth+1; i++) {
260 fprintf(fin,"\t"); /* depth == indentation */
262 fprintf(fin,"%s", lp->u1.str); /* usually, words are encapsulated in something else */
263 if ( lp->u2.arglist )
264 fprintf(fin,"|%s|%s|%s|%s",
265 lp->u2.arglist->u1.str,
266 lp->u2.arglist->next->u1.str,
267 lp->u2.arglist->next->next->u1.str,
268 lp->u2.arglist->next->next->next->u1.str
270 fprintf(fin,";\n"); /* usually, words are encapsulated in something else */
273 print_pval_list(fin,item->u1.list,depth+1);
274 for (i=0; i<depth; i++) {
275 fprintf(fin,"\t"); /* depth == indentation */
280 case PV_STATEMENTBLOCK:
282 print_pval_list(fin,item->u1.list, depth+1);
283 for (i=0; i<depth; i++) {
284 fprintf(fin,"\t"); /* depth == indentation */
290 fprintf(fin,"%s=%s;\n", item->u1.str, item->u2.val);
294 fprintf(fin,"local %s=%s;\n", item->u1.str, item->u2.val);
298 fprintf(fin,"goto %s", item->u1.list->u1.str);
299 if ( item->u1.list->next )
300 fprintf(fin,",%s", item->u1.list->next->u1.str);
301 if ( item->u1.list->next && item->u1.list->next->next )
302 fprintf(fin,",%s", item->u1.list->next->next->u1.str);
307 fprintf(fin,"%s:\n", item->u1.str);
311 fprintf(fin,"for (%s; %s; %s)\n", item->u1.for_init, item->u2.for_test, item->u3.for_inc);
312 print_pval_list(fin,item->u4.for_statements,depth+1);
316 fprintf(fin,"while (%s)\n", item->u1.str);
317 print_pval_list(fin,item->u2.statements,depth+1);
321 fprintf(fin,"break;\n");
325 fprintf(fin,"return;\n");
329 fprintf(fin,"continue;\n");
335 if ( item->type == PV_IFTIME ) {
337 fprintf(fin,"ifTime ( %s|%s|%s|%s )\n",
338 item->u1.list->u1.str,
339 item->u1.list->next->u1.str,
340 item->u1.list->next->next->u1.str,
341 item->u1.list->next->next->next->u1.str
343 } else if ( item->type == PV_RANDOM ) {
344 fprintf(fin,"random ( %s )\n", item->u1.str );
346 fprintf(fin,"if ( %s )\n", item->u1.str);
347 if ( item->u2.statements && item->u2.statements->next ) {
348 for (i=0; i<depth; i++) {
349 fprintf(fin,"\t"); /* depth == indentation */
352 print_pval_list(fin,item->u2.statements,depth+1);
353 for (i=0; i<depth; i++) {
354 fprintf(fin,"\t"); /* depth == indentation */
356 if ( item->u3.else_statements )
360 } else if (item->u2.statements ) {
361 print_pval_list(fin,item->u2.statements,depth+1);
363 if (item->u3.else_statements )
364 fprintf(fin, " {} ");
366 fprintf(fin, " {}; ");
368 if ( item->u3.else_statements ) {
369 for (i=0; i<depth; i++) {
370 fprintf(fin,"\t"); /* depth == indentation */
372 fprintf(fin,"else\n");
373 print_pval_list(fin,item->u3.else_statements, depth);
378 fprintf(fin,"switch( %s ) {\n", item->u1.str);
379 print_pval_list(fin,item->u2.statements,depth+1);
380 for (i=0; i<depth; i++) {
381 fprintf(fin,"\t"); /* depth == indentation */
387 if ( item->u4.regexten )
388 fprintf(fin, "regexten ");
389 if ( item->u3.hints )
390 fprintf(fin,"hints(%s) ", item->u3.hints);
392 fprintf(fin,"%s => \n", item->u1.str);
393 print_pval_list(fin,item->u2.statements,depth+1);
397 fprintf(fin,"ignorepat => %s\n", item->u1.str);
401 fprintf(fin,"globals {\n");
402 print_pval_list(fin,item->u1.statements,depth+1);
403 for (i=0; i<depth; i++) {
404 fprintf(fin,"\t"); /* depth == indentation */
411 static void print_pval_list(FILE *fin, pval *item, int depth)
415 for (i=item; i; i=i->next) {
416 print_pval(fin, i, depth);
421 static void ael2_print(char *fname, pval *tree)
423 FILE *fin = fopen(fname,"w");
425 ast_log(LOG_ERROR, "Couldn't open %s for writing.\n", fname);
428 print_pval_list(fin, tree, 0);
435 /* SEMANTIC CHECKING FOR AEL: ============================================================================= */
437 /* (not all that is syntactically legal is good! */
440 static struct pval *in_macro(pval *item)
445 if( curr->type == PV_MACRO ) {
453 static struct pval *in_context(pval *item)
458 if( curr->type == PV_MACRO || curr->type == PV_CONTEXT ) {
467 static pval *get_goto_target(pval *item)
469 /* just one item-- the label should be in the current extension */
470 pval *curr_ext = get_extension_or_contxt(item); /* containing exten, or macro */
473 if (item->u1.list && !item->u1.list->next && !strstr((item->u1.list)->u1.str,"${")) {
474 struct pval *x = find_label_in_current_extension((char*)((item->u1.list)->u1.str), curr_ext);
478 curr_cont = get_contxt(item);
481 if (item->u1.list->next && !item->u1.list->next->next) {
482 if (!strstr((item->u1.list)->u1.str,"${")
483 && !strstr(item->u1.list->next->u1.str,"${") ) /* Don't try to match variables */ {
484 struct pval *x = find_label_in_current_context((char *)item->u1.list->u1.str, (char *)item->u1.list->next->u1.str, curr_cont);
490 if (item->u1.list->next && item->u1.list->next->next) {
492 pval *first = item->u1.list;
493 pval *second = item->u1.list->next;
494 pval *third = item->u1.list->next->next;
496 if (!strstr((item->u1.list)->u1.str,"${")
497 && !strstr(item->u1.list->next->u1.str,"${")
498 && !strstr(item->u1.list->next->next->u1.str,"${")) /* Don't try to match variables */ {
499 struct pval *x = find_label_in_current_db((char*)first->u1.str, (char*)second->u1.str, (char*)third->u1.str);
503 struct pval *that_context = find_context(item->u1.list->u1.str);
505 /* the target of the goto could be in an included context!! Fancy that!! */
506 /* look for includes in the current context */
508 for (p3=that_context->u2.statements; p3; p3=p3->next) {
509 if (p3->type == PV_INCLUDES) {
511 for (p4=p3->u1.list; p4; p4=p4->next) {
512 /* for each context pointed to, find it, then find a context/label that matches the
514 char *incl_context = p4->u1.str;
515 /* find a matching context name */
516 struct pval *that_other_context = find_context(incl_context);
517 if (that_other_context) {
519 x3 = find_label_in_current_context((char *)item->u1.list->next->u1.str, (char *)item->u1.list->next->next->u1.str, that_other_context);
535 static void check_goto(pval *item)
537 /* check for the target of the goto-- does it exist? */
538 if ( !(item->u1.list)->next && !(item->u1.list)->u1.str ) {
539 ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto: empty label reference found!\n",
540 item->filename, item->startline, item->endline);
544 /* just one item-- the label should be in the current extension */
546 if (item->u1.list && !item->u1.list->next && !strstr((item->u1.list)->u1.str,"${")) {
547 struct pval *z = get_extension_or_contxt(item);
550 x = find_label_in_current_extension((char*)((item->u1.list)->u1.str), z); /* if in macro, use current context instead */
551 /* printf("Called find_label_in_current_extension with arg %s; current_extension is %x: %d\n",
552 (char*)((item->u1.list)->u1.str), current_extension?current_extension:current_context, current_extension?current_extension->type:current_context->type); */
554 ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto: no label %s exists in the current extension!\n",
555 item->filename, item->startline, item->endline, item->u1.list->u1.str);
563 if (item->u1.list->next && !item->u1.list->next->next) {
565 /* printf("Calling find_label_in_current_context with args %s, %s\n",
566 (char*)((item->u1.list)->u1.str), (char *)item->u1.list->next->u1.str); */
567 if (!strstr((item->u1.list)->u1.str,"${")
568 && !strstr(item->u1.list->next->u1.str,"${") ) /* Don't try to match variables */ {
569 struct pval *z = get_contxt(item);
573 x = find_label_in_current_context((char *)item->u1.list->u1.str, (char *)item->u1.list->next->u1.str, z);
576 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",
577 item->filename, item->startline, item->endline, item->u1.list->u1.str, item->u1.list->next->u1.str );
586 if (item->u1.list->next && item->u1.list->next->next) {
588 pval *first = item->u1.list;
589 pval *second = item->u1.list->next;
590 pval *third = item->u1.list->next->next;
592 /* printf("Calling find_label_in_current_db with args %s, %s, %s\n",
593 (char*)first->u1.str, (char*)second->u1.str, (char*)third->u1.str); */
594 if (!strstr((item->u1.list)->u1.str,"${")
595 && !strstr(item->u1.list->next->u1.str,"${")
596 && !strstr(item->u1.list->next->next->u1.str,"${")) /* Don't try to match variables */ {
597 struct pval *x = find_label_in_current_db((char*)first->u1.str, (char*)second->u1.str, (char*)third->u1.str);
600 struct pval *found = 0;
601 struct pval *that_context = find_context(item->u1.list->u1.str);
603 /* the target of the goto could be in an included context!! Fancy that!! */
604 /* look for includes in the current context */
606 for (p3=that_context->u2.statements; p3; p3=p3->next) {
607 if (p3->type == PV_INCLUDES) {
609 for (p4=p3->u1.list; p4; p4=p4->next) {
610 /* for each context pointed to, find it, then find a context/label that matches the
612 char *incl_context = p4->u1.str;
613 /* find a matching context name */
614 struct pval *that_other_context = find_context(incl_context);
615 if (that_other_context) {
617 x3 = find_label_in_current_context((char *)item->u1.list->next->u1.str, (char *)item->u1.list->next->next->u1.str, that_other_context);
627 ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto: no label %s|%s exists in the context %s or its inclusions!\n",
628 item->filename, item->startline, item->endline, item->u1.list->next->u1.str, item->u1.list->next->next->u1.str, item->u1.list->u1.str );
631 struct pval *mac = in_macro(item); /* is this goto inside a macro? */
632 if( mac ) { /* yes! */
633 struct pval *targ = in_context(found);
636 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",
637 item->filename, item->startline, item->endline);
643 /* here is where code would go to check for target existence in extensions.conf files */
644 ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: goto: no context %s could be found that matches the goto target!\n",
645 item->filename, item->startline, item->endline, item->u1.list->u1.str);
646 warns++; /* this is just a warning, because this context could be in extensions.conf or somewhere */
649 struct pval *mac = in_macro(item); /* is this goto inside a macro? */
650 if( mac ) { /* yes! */
651 struct pval *targ = in_context(x);
654 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",
655 item->filename, item->startline, item->endline);
665 static void find_pval_goto_item(pval *item, int lev)
669 ast_log(LOG_ERROR,"find_pval_goto in infinite loop!\n\n");
673 switch ( item->type ) {
675 /* fields: item->u1.str == name of macro
676 item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
677 item->u2.arglist->u1.str == argument
678 item->u2.arglist->next == next arg
680 item->u3.macro_statements == pval list of statements in macro body.
683 /* printf("Descending into matching macro %s\n", match_context); */
684 find_pval_gotos(item->u3.macro_statements,lev+1); /* if we're just searching for a context, don't bother descending into them */
689 /* fields: item->u1.str == name of context
690 item->u2.statements == pval list of statements in context body
691 item->u3.abstract == int 1 if an abstract keyword were present
696 /* fields: item->u1.str == value of case
697 item->u2.statements == pval list of statements under the case
699 find_pval_gotos(item->u2.statements,lev+1);
703 /* fields: item->u1.str == value of case
704 item->u2.statements == pval list of statements under the case
706 find_pval_gotos(item->u2.statements,lev+1);
711 item->u2.statements == pval list of statements under the case
713 find_pval_gotos(item->u2.statements,lev+1);
717 /* fields: item->u1.str == name of extension to catch
718 item->u2.statements == pval list of statements in context body
720 find_pval_gotos(item->u2.statements,lev+1);
723 case PV_STATEMENTBLOCK:
724 /* fields: item->u1.list == pval list of statements in block, one per entry in the list
726 find_pval_gotos(item->u1.list,lev+1);
730 /* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user.
731 item->u1.list->u1.str == where the data on a PV_WORD will always be.
733 check_goto(item); /* THE WHOLE FUNCTION OF THIS ENTIRE ROUTINE!!!! */
737 /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
739 for (p4=item->u1.list; p4; p4=p4->next) {
740 /* for each context pointed to, find it, then find a context/label that matches the
742 char *incl_context = p4->u1.str;
743 /* find a matching context name */
744 struct pval *that_context = find_context(incl_context);
746 find_pval_gotos(that_context,lev+1); /* keep working up the includes */
752 /* fields: item->u1.for_init == a string containing the initalizer
753 item->u2.for_test == a string containing the loop test
754 item->u3.for_inc == a string containing the loop increment
756 item->u4.for_statements == a pval list of statements in the for ()
758 find_pval_gotos(item->u4.for_statements,lev+1);
762 /* fields: item->u1.str == the while conditional, as supplied by user
764 item->u2.statements == a pval list of statements in the while ()
766 find_pval_gotos(item->u2.statements,lev+1);
770 /* fields: item->u1.str == the random number expression, as supplied by user
772 item->u2.statements == a pval list of statements in the if ()
773 item->u3.else_statements == a pval list of statements in the else
775 fall thru to PV_IF */
778 /* fields: item->u1.list == the time values, 4 of them, as PV_WORD structs in a list
780 item->u2.statements == a pval list of statements in the if ()
781 item->u3.else_statements == a pval list of statements in the else
785 /* fields: item->u1.str == the if conditional, as supplied by user
787 item->u2.statements == a pval list of statements in the if ()
788 item->u3.else_statements == a pval list of statements in the else
791 find_pval_gotos(item->u2.statements,lev+1);
793 if (item->u3.else_statements) {
794 find_pval_gotos(item->u3.else_statements,lev+1);
799 /* fields: item->u1.str == the switch expression
801 item->u2.statements == a pval list of statements in the switch,
802 (will be case statements, most likely!)
804 find_pval_gotos(item->u3.else_statements,lev+1);
808 /* fields: item->u1.str == the extension name, label, whatever it's called
810 item->u2.statements == a pval list of statements in the extension
811 item->u3.hints == a char * hint argument
812 item->u4.regexten == an int boolean. non-zero says that regexten was specified
815 find_pval_gotos(item->u2.statements,lev+1);
823 static void find_pval_gotos(pval *item,int lev)
827 for (i=item; i; i=i->next) {
829 find_pval_goto_item(i, lev);
835 struct pval *find_first_label_in_current_context(char *label, pval *curr_cont)
837 /* printf(" --- Got args %s, %s\n", exten, label); */
840 struct pval *startpt = ((curr_cont->type==PV_MACRO)?curr_cont->u3.macro_statements: curr_cont->u2.statements);
843 return_on_context_match = 0;
848 ret = match_pval(curr_cont);
852 /* the target of the goto could be in an included context!! Fancy that!! */
853 /* look for includes in the current context */
854 for (p3=startpt; p3; p3=p3->next) {
855 if (p3->type == PV_INCLUDES) {
857 for (p4=p3->u1.list; p4; p4=p4->next) {
858 /* for each context pointed to, find it, then find a context/label that matches the
860 char *incl_context = p4->u1.str;
861 /* find a matching context name */
862 struct pval *that_context = find_context(incl_context);
865 x3 = find_first_label_in_current_context(label, that_context);
876 struct pval *find_label_in_current_context(char *exten, char *label, pval *curr_cont)
878 /* printf(" --- Got args %s, %s\n", exten, label); */
881 struct pval *startpt;
884 return_on_context_match = 0;
888 if (curr_cont->type == PV_MACRO)
889 startpt = curr_cont->u3.macro_statements;
891 startpt = curr_cont->u2.statements;
893 ret = match_pval(startpt);
897 /* the target of the goto could be in an included context!! Fancy that!! */
898 /* look for includes in the current context */
899 for (p3=startpt; p3; p3=p3->next) {
900 if (p3->type == PV_INCLUDES) {
902 for (p4=p3->u1.list; p4; p4=p4->next) {
903 /* for each context pointed to, find it, then find a context/label that matches the
905 char *incl_context = p4->u1.str;
906 /* find a matching context name */
907 struct pval *that_context = find_context(incl_context);
910 x3 = find_label_in_current_context(exten, label, that_context);
921 static struct pval *find_label_in_current_extension(const char *label, pval *curr_ext)
923 /* printf(" --- Got args %s\n", label); */
925 return_on_context_match = 0;
929 return match_pval(curr_ext);
932 static struct pval *find_label_in_current_db(const char *context, const char *exten, const char *label)
934 /* printf(" --- Got args %s, %s, %s\n", context, exten, label); */
936 return_on_context_match = 0;
938 match_context = context;
942 return match_pval(current_db);
947 /* =============================================================================================== */
948 /* "CODE" GENERATOR -- Convert the AEL representation to asterisk extension language */
949 /* =============================================================================================== */
951 static int control_statement_count = 0;
953 static int label_inside_case(pval *label)
957 while( p && p->type != PV_MACRO && p->type != PV_CONTEXT ) /* early cutout, sort of */ {
958 if( p->type == PV_CASE || p->type == PV_DEFAULT || p->type == PV_PATTERN ) {
967 static void linkexten(struct ael_extension *exten, struct ael_extension *add)
969 add->next_exten = exten->next_exten; /* this will reverse the order. Big deal. */
970 exten->next_exten = add;
973 static void remove_spaces_before_equals(char *str)
976 while( str && *str && *str != '=' )
978 if( *str == ' ' || *str == '\n' || *str == '\r' || *str == '\t' )
992 static void gen_match_to_pattern(char *pattern, char *result)
994 /* the result will be a string that will be matched by pattern */
995 char *p=pattern, *t=result;
997 if (*p == 'x' || *p == 'n' || *p == 'z' || *p == 'X' || *p == 'N' || *p == 'Z')
999 else if (*p == '[') {
1005 *t++=*(p+1); /* use the first char in the set */
1012 *t++ = 0; /* cap it off */
1015 static void gen_prios(struct ael_extension *exten, char *label, pval *statement, struct ael_extension *mother_exten, struct ast_context *this_context )
1018 struct ael_priority *pr;
1019 struct ael_priority *for_init, *for_test, *for_inc, *for_loop, *for_end;
1020 struct ael_priority *while_test, *while_loop, *while_end;
1021 struct ael_priority *switch_test, *switch_end, *fall_thru;
1022 struct ael_priority *if_test, *if_end, *if_skip, *if_false;
1023 #ifdef OLD_RAND_ACTION
1024 struct ael_priority *rand_test, *rand_end, *rand_skip;
1029 char new_label[2000];
1031 int local_control_statement_count;
1033 struct ael_priority *loop_break_save;
1034 struct ael_priority *loop_continue_save;
1035 struct ael_extension *switch_case;
1037 for (p=statement; p; p=p->next) {
1041 pr->type = AEL_APPCALL;
1042 snprintf(buf1,sizeof(buf1),"%s=$[%s]", p->u1.str, p->u2.val);
1043 pr->app = strdup("Set");
1044 remove_spaces_before_equals(buf1);
1045 pr->appargs = strdup(buf1);
1047 linkprio(exten, pr);
1050 case PV_LOCALVARDEC:
1052 pr->type = AEL_APPCALL;
1053 snprintf(buf1,sizeof(buf1),"LOCAL(%s)=$[%s]", p->u1.str, p->u2.val);
1054 pr->app = strdup("Set");
1055 remove_spaces_before_equals(buf1);
1056 pr->appargs = strdup(buf1);
1058 linkprio(exten, pr);
1063 pr->type = AEL_APPCALL;
1064 p->u2.goto_target = get_goto_target(p);
1065 if( p->u2.goto_target ) {
1066 p->u3.goto_target_in_case = p->u2.goto_target->u2.label_in_case = label_inside_case(p->u2.goto_target);
1069 if (!p->u1.list->next) /* just one */ {
1070 pr->app = strdup("Goto");
1072 pr->appargs = strdup(p->u1.list->u1.str);
1073 else { /* for the case of simple within-extension gotos in case/pattern/default statement blocks: */
1074 snprintf(buf1,sizeof(buf1),"%s,%s", mother_exten->name, p->u1.list->u1.str);
1075 pr->appargs = strdup(buf1);
1078 } else if (p->u1.list->next && !p->u1.list->next->next) /* two */ {
1079 snprintf(buf1,sizeof(buf1),"%s,%s", p->u1.list->u1.str, p->u1.list->next->u1.str);
1080 pr->app = strdup("Goto");
1081 pr->appargs = strdup(buf1);
1082 } else if (p->u1.list->next && p->u1.list->next->next) {
1083 snprintf(buf1,sizeof(buf1),"%s,%s,%s", p->u1.list->u1.str,
1084 p->u1.list->next->u1.str,
1085 p->u1.list->next->next->u1.str);
1086 pr->app = strdup("Goto");
1087 pr->appargs = strdup(buf1);
1090 linkprio(exten, pr);
1095 pr->type = AEL_LABEL;
1097 p->u3.compiled_label = exten;
1098 linkprio(exten, pr);
1102 control_statement_count++;
1103 loop_break_save = exten->loop_break; /* save them, then restore before leaving */
1104 loop_continue_save = exten->loop_continue;
1105 snprintf(new_label,sizeof(new_label),"for-%s-%d", label, control_statement_count);
1106 for_init = new_prio();
1107 for_inc = new_prio();
1108 for_test = new_prio();
1109 for_loop = new_prio();
1110 for_end = new_prio();
1111 for_init->type = AEL_APPCALL;
1112 for_inc->type = AEL_APPCALL;
1113 for_test->type = AEL_FOR_CONTROL;
1114 for_test->goto_false = for_end;
1115 for_loop->type = AEL_CONTROL1; /* simple goto */
1116 for_end->type = AEL_APPCALL;
1117 for_init->app = strdup("Set");
1119 strcpy(buf2,p->u1.for_init);
1120 remove_spaces_before_equals(buf2);
1121 strp = strchr(buf2, '=');
1122 strp2 = strchr(p->u1.for_init, '=');
1126 strncat(buf2,strp2+1, sizeof(buf2)-strlen(strp2+1)-2);
1128 for_init->appargs = strdup(buf2);
1130 for_init->appargs = strdup(p->u1.for_init);
1132 for_inc->app = strdup("Set");
1134 strcpy(buf2,p->u3.for_inc);
1135 remove_spaces_before_equals(buf2);
1136 strp = strchr(buf2, '=');
1137 strp2 = strchr(p->u3.for_inc, '=');
1141 strncat(buf2,strp2+1, sizeof(buf2)-strlen(strp2+1)-2);
1143 for_inc->appargs = strdup(buf2);
1145 for_inc->appargs = strdup(p->u3.for_inc);
1146 snprintf(buf1,sizeof(buf1),"$[%s]",p->u2.for_test);
1148 for_test->appargs = strdup(buf1);
1149 for_loop->goto_true = for_test;
1150 snprintf(buf1,sizeof(buf1),"Finish for-%s-%d", label, control_statement_count);
1151 for_end->app = strdup("NoOp");
1152 for_end->appargs = strdup(buf1);
1154 linkprio(exten, for_init);
1155 linkprio(exten, for_test);
1157 /* now, put the body of the for loop here */
1158 exten->loop_break = for_end;
1159 exten->loop_continue = for_inc;
1161 gen_prios(exten, new_label, p->u4.for_statements, mother_exten, this_context); /* this will link in all the statements here */
1163 linkprio(exten, for_inc);
1164 linkprio(exten, for_loop);
1165 linkprio(exten, for_end);
1168 exten->loop_break = loop_break_save;
1169 exten->loop_continue = loop_continue_save;
1170 for_loop->origin = p;
1174 control_statement_count++;
1175 loop_break_save = exten->loop_break; /* save them, then restore before leaving */
1176 loop_continue_save = exten->loop_continue;
1177 snprintf(new_label,sizeof(new_label),"while-%s-%d", label, control_statement_count);
1178 while_test = new_prio();
1179 while_loop = new_prio();
1180 while_end = new_prio();
1181 while_test->type = AEL_FOR_CONTROL;
1182 while_test->goto_false = while_end;
1183 while_loop->type = AEL_CONTROL1; /* simple goto */
1184 while_end->type = AEL_APPCALL;
1185 snprintf(buf1,sizeof(buf1),"$[%s]",p->u1.str);
1186 while_test->app = 0;
1187 while_test->appargs = strdup(buf1);
1188 while_loop->goto_true = while_test;
1189 snprintf(buf1,sizeof(buf1),"Finish while-%s-%d", label, control_statement_count);
1190 while_end->app = strdup("NoOp");
1191 while_end->appargs = strdup(buf1);
1193 linkprio(exten, while_test);
1195 /* now, put the body of the for loop here */
1196 exten->loop_break = while_end;
1197 exten->loop_continue = while_test;
1199 gen_prios(exten, new_label, p->u2.statements, mother_exten, this_context); /* this will link in all the while body statements here */
1201 linkprio(exten, while_loop);
1202 linkprio(exten, while_end);
1205 exten->loop_break = loop_break_save;
1206 exten->loop_continue = loop_continue_save;
1207 while_loop->origin = p;
1211 control_statement_count++;
1212 local_control_statement_count = control_statement_count;
1213 loop_break_save = exten->loop_break; /* save them, then restore before leaving */
1214 loop_continue_save = exten->loop_continue;
1215 snprintf(new_label,sizeof(new_label),"sw-%s-%d", label, control_statement_count);
1217 switch_test = new_prio();
1218 switch_end = new_prio();
1219 switch_test->type = AEL_APPCALL;
1220 switch_end->type = AEL_APPCALL;
1221 snprintf(buf1,sizeof(buf1),"sw-%d-%s,10",control_statement_count, p->u1.str);
1222 switch_test->app = strdup("Goto");
1223 switch_test->appargs = strdup(buf1);
1224 snprintf(buf1,sizeof(buf1),"Finish switch-%s-%d", label, control_statement_count);
1225 switch_end->app = strdup("NoOp");
1226 switch_end->appargs = strdup(buf1);
1227 switch_end->origin = p;
1228 switch_end->exten = exten;
1230 linkprio(exten, switch_test);
1231 linkprio(exten, switch_end);
1233 exten->loop_break = switch_end;
1234 exten->loop_continue = 0;
1237 for (p2=p->u2.statements; p2; p2=p2->next) {
1238 /* now, for each case/default put the body of the for loop here */
1239 if (p2->type == PV_CASE) {
1240 /* ok, generate a extension and link it in */
1241 switch_case = new_exten();
1242 switch_case->context = this_context;
1243 switch_case->is_switch = 1;
1244 /* the break/continue locations are inherited from parent */
1245 switch_case->loop_break = exten->loop_break;
1246 switch_case->loop_continue = exten->loop_continue;
1248 linkexten(exten,switch_case);
1249 snprintf(buf1,sizeof(buf1),"sw-%d-%s", local_control_statement_count, p2->u1.str);
1250 switch_case->name = strdup(buf1);
1251 snprintf(new_label,sizeof(new_label),"sw-%s-%s-%d", label, p2->u1.str, local_control_statement_count);
1253 gen_prios(switch_case, new_label, p2->u2.statements, exten, this_context); /* this will link in all the case body statements here */
1255 /* here is where we write code to "fall thru" to the next case... if there is one... */
1256 for (p3=p2->u2.statements; p3; p3=p3->next) {
1260 /* p3 now points the last statement... */
1261 if (!p3 || ( p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN) ) {
1262 /* is there a following CASE/PATTERN/DEFAULT? */
1263 if (p2->next && p2->next->type == PV_CASE) {
1264 fall_thru = new_prio();
1265 fall_thru->type = AEL_APPCALL;
1266 fall_thru->app = strdup("Goto");
1267 snprintf(buf1,sizeof(buf1),"sw-%d-%s,10",local_control_statement_count, p2->next->u1.str);
1268 fall_thru->appargs = strdup(buf1);
1269 linkprio(switch_case, fall_thru);
1270 } else if (p2->next && p2->next->type == PV_PATTERN) {
1271 fall_thru = new_prio();
1272 fall_thru->type = AEL_APPCALL;
1273 fall_thru->app = strdup("Goto");
1274 gen_match_to_pattern(p2->next->u1.str, buf2);
1275 snprintf(buf1,sizeof(buf1),"sw-%d-%s,10", local_control_statement_count, buf2);
1276 fall_thru->appargs = strdup(buf1);
1277 linkprio(switch_case, fall_thru);
1278 } else if (p2->next && p2->next->type == PV_DEFAULT) {
1279 fall_thru = new_prio();
1280 fall_thru->type = AEL_APPCALL;
1281 fall_thru->app = strdup("Goto");
1282 snprintf(buf1,sizeof(buf1),"sw-%d-.,10",local_control_statement_count);
1283 fall_thru->appargs = strdup(buf1);
1284 linkprio(switch_case, fall_thru);
1285 } else if (!p2->next) {
1286 fall_thru = new_prio();
1287 fall_thru->type = AEL_CONTROL1;
1288 fall_thru->goto_true = switch_end;
1289 fall_thru->app = strdup("Goto");
1290 linkprio(switch_case, fall_thru);
1293 if (switch_case->return_needed) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */
1295 struct ael_priority *np2 = new_prio();
1296 np2->type = AEL_APPCALL;
1297 np2->app = strdup("NoOp");
1298 snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name);
1299 np2->appargs = strdup(buf);
1300 linkprio(switch_case, np2);
1301 switch_case-> return_target = np2;
1303 } else if (p2->type == PV_PATTERN) {
1304 /* ok, generate a extension and link it in */
1305 switch_case = new_exten();
1306 switch_case->context = this_context;
1307 switch_case->is_switch = 1;
1308 /* the break/continue locations are inherited from parent */
1309 switch_case->loop_break = exten->loop_break;
1310 switch_case->loop_continue = exten->loop_continue;
1312 linkexten(exten,switch_case);
1313 snprintf(buf1,sizeof(buf1),"_sw-%d-%s", local_control_statement_count, p2->u1.str);
1314 switch_case->name = strdup(buf1);
1315 snprintf(new_label,sizeof(new_label),"sw-%s-%s-%d", label, p2->u1.str, local_control_statement_count);
1317 gen_prios(switch_case, new_label, p2->u2.statements, exten, this_context); /* this will link in all the while body statements here */
1318 /* here is where we write code to "fall thru" to the next case... if there is one... */
1319 for (p3=p2->u2.statements; p3; p3=p3->next) {
1323 /* p3 now points the last statement... */
1324 if (!p3 || ( p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN)) {
1325 /* is there a following CASE/PATTERN/DEFAULT? */
1326 if (p2->next && p2->next->type == PV_CASE) {
1327 fall_thru = new_prio();
1328 fall_thru->type = AEL_APPCALL;
1329 fall_thru->app = strdup("Goto");
1330 snprintf(buf1,sizeof(buf1),"sw-%d-%s,10",local_control_statement_count, p2->next->u1.str);
1331 fall_thru->appargs = strdup(buf1);
1332 linkprio(switch_case, fall_thru);
1333 } else if (p2->next && p2->next->type == PV_PATTERN) {
1334 fall_thru = new_prio();
1335 fall_thru->type = AEL_APPCALL;
1336 fall_thru->app = strdup("Goto");
1337 gen_match_to_pattern(p2->next->u1.str, buf2);
1338 snprintf(buf1,sizeof(buf1),"sw-%d-%s,10",local_control_statement_count, buf2);
1339 fall_thru->appargs = strdup(buf1);
1340 linkprio(switch_case, fall_thru);
1341 } else if (p2->next && p2->next->type == PV_DEFAULT) {
1342 fall_thru = new_prio();
1343 fall_thru->type = AEL_APPCALL;
1344 fall_thru->app = strdup("Goto");
1345 snprintf(buf1,sizeof(buf1),"sw-%d-.,10",local_control_statement_count);
1346 fall_thru->appargs = strdup(buf1);
1347 linkprio(switch_case, fall_thru);
1348 } else if (!p2->next) {
1349 fall_thru = new_prio();
1350 fall_thru->type = AEL_CONTROL1;
1351 fall_thru->goto_true = switch_end;
1352 fall_thru->app = strdup("Goto");
1353 linkprio(switch_case, fall_thru);
1356 if (switch_case->return_needed) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */
1358 struct ael_priority *np2 = new_prio();
1359 np2->type = AEL_APPCALL;
1360 np2->app = strdup("NoOp");
1361 snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name);
1362 np2->appargs = strdup(buf);
1363 linkprio(switch_case, np2);
1364 switch_case-> return_target = np2;
1366 } else if (p2->type == PV_DEFAULT) {
1368 /* ok, generate a extension and link it in */
1369 switch_case = new_exten();
1370 switch_case->context = this_context;
1371 switch_case->is_switch = 1;
1372 /* the break/continue locations are inherited from parent */
1373 switch_case->loop_break = exten->loop_break;
1374 switch_case->loop_continue = exten->loop_continue;
1375 linkexten(exten,switch_case);
1376 snprintf(buf1,sizeof(buf1),"_sw-%d-.", local_control_statement_count);
1377 switch_case->name = strdup(buf1);
1379 snprintf(new_label,sizeof(new_label),"sw-%s-default-%d", label, local_control_statement_count);
1381 gen_prios(switch_case, new_label, p2->u2.statements, exten, this_context); /* this will link in all the while body statements here */
1383 /* here is where we write code to "fall thru" to the next case... if there is one... */
1384 for (p3=p2->u2.statements; p3; p3=p3->next) {
1388 /* p3 now points the last statement... */
1389 if (!p3 || (p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN)) {
1390 /* is there a following CASE/PATTERN/DEFAULT? */
1391 if (p2->next && p2->next->type == PV_CASE) {
1392 fall_thru = new_prio();
1393 fall_thru->type = AEL_APPCALL;
1394 fall_thru->app = strdup("Goto");
1395 snprintf(buf1,sizeof(buf1),"sw-%d-%s,10",local_control_statement_count, p2->next->u1.str);
1396 fall_thru->appargs = strdup(buf1);
1397 linkprio(switch_case, fall_thru);
1398 } else if (p2->next && p2->next->type == PV_PATTERN) {
1399 fall_thru = new_prio();
1400 fall_thru->type = AEL_APPCALL;
1401 fall_thru->app = strdup("Goto");
1402 gen_match_to_pattern(p2->next->u1.str, buf2);
1403 snprintf(buf1,sizeof(buf1),"sw-%d-%s,10",local_control_statement_count, buf2);
1404 fall_thru->appargs = strdup(buf1);
1405 linkprio(switch_case, fall_thru);
1406 } else if (p2->next && p2->next->type == PV_DEFAULT) {
1407 fall_thru = new_prio();
1408 fall_thru->type = AEL_APPCALL;
1409 fall_thru->app = strdup("Goto");
1410 snprintf(buf1,sizeof(buf1),"sw-%d-.,10",local_control_statement_count);
1411 fall_thru->appargs = strdup(buf1);
1412 linkprio(switch_case, fall_thru);
1413 } else if (!p2->next) {
1414 fall_thru = new_prio();
1415 fall_thru->type = AEL_CONTROL1;
1416 fall_thru->goto_true = switch_end;
1417 fall_thru->app = strdup("Goto");
1418 linkprio(switch_case, fall_thru);
1421 if (switch_case->return_needed) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */
1423 struct ael_priority *np2 = new_prio();
1424 np2->type = AEL_APPCALL;
1425 np2->app = strdup("NoOp");
1426 snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name);
1427 np2->appargs = strdup(buf);
1428 linkprio(switch_case, np2);
1429 switch_case-> return_target = np2;
1432 /* what could it be??? */
1436 exten->loop_break = loop_break_save;
1437 exten->loop_continue = loop_continue_save;
1438 switch_test->origin = p;
1439 switch_end->origin = p;
1444 pr->type = AEL_APPCALL;
1445 snprintf(buf1,sizeof(buf1),"%s,s,1", p->u1.str);
1447 for (p2 = p->u2.arglist; p2; p2 = p2->next) {
1455 strcat(buf1,p2->u1.str);
1460 pr->app = strdup("Gosub");
1461 pr->appargs = strdup(buf1);
1463 linkprio(exten, pr);
1466 case PV_APPLICATION_CALL:
1468 pr->type = AEL_APPCALL;
1470 for (p2 = p->u2.arglist; p2; p2 = p2->next) {
1471 if (p2 != p->u2.arglist )
1473 /*substitute_commas(p2->u1.str); */
1474 strcat(buf1,p2->u1.str);
1476 pr->app = strdup(p->u1.str);
1477 pr->appargs = strdup(buf1);
1479 linkprio(exten, pr);
1484 pr->type = AEL_CONTROL1; /* simple goto */
1485 pr->goto_true = exten->loop_break;
1487 linkprio(exten, pr);
1490 case PV_RETURN: /* hmmmm */
1492 pr->type = AEL_RETURN; /* simple Return */
1493 /* exten->return_needed++; */
1494 pr->app = strdup("Return");
1495 pr->appargs = strdup("");
1497 linkprio(exten, pr);
1502 pr->type = AEL_CONTROL1; /* simple goto */
1503 pr->goto_true = exten->loop_continue;
1505 linkprio(exten, pr);
1509 control_statement_count++;
1510 snprintf(new_label,sizeof(new_label),"iftime-%s-%d", label, control_statement_count);
1512 if_test = new_prio();
1513 if_test->type = AEL_IFTIME_CONTROL;
1514 snprintf(buf1,sizeof(buf1),"%s,%s,%s,%s",
1516 p->u1.list->next->u1.str,
1517 p->u1.list->next->next->u1.str,
1518 p->u1.list->next->next->next->u1.str);
1520 if_test->appargs = strdup(buf1);
1521 if_test->origin = p;
1523 if_end = new_prio();
1524 if_end->type = AEL_APPCALL;
1525 snprintf(buf1,sizeof(buf1),"Finish iftime-%s-%d", label, control_statement_count);
1526 if_end->app = strdup("NoOp");
1527 if_end->appargs = strdup(buf1);
1529 if (p->u3.else_statements) {
1530 if_skip = new_prio();
1531 if_skip->type = AEL_CONTROL1; /* simple goto */
1532 if_skip->goto_true = if_end;
1533 if_skip->origin = p;
1538 if_test->goto_false = if_end;
1541 if_false = new_prio();
1542 if_false->type = AEL_CONTROL1;
1543 if (p->u3.else_statements) {
1544 if_false->goto_true = if_skip; /* +1 */
1546 if_false->goto_true = if_end;
1550 linkprio(exten, if_test);
1551 linkprio(exten, if_false);
1553 /* now, put the body of the if here */
1555 gen_prios(exten, new_label, p->u2.statements, mother_exten, this_context); /* this will link in all the statements here */
1557 if (p->u3.else_statements) {
1558 linkprio(exten, if_skip);
1559 gen_prios(exten, new_label, p->u3.else_statements, mother_exten, this_context); /* this will link in all the statements here */
1563 linkprio(exten, if_end);
1569 control_statement_count++;
1570 snprintf(new_label,sizeof(new_label),"if-%s-%d", label, control_statement_count);
1572 if_test = new_prio();
1573 if_end = new_prio();
1574 if_test->type = AEL_IF_CONTROL;
1575 if_end->type = AEL_APPCALL;
1576 if ( p->type == PV_RANDOM )
1577 snprintf(buf1,sizeof(buf1),"$[${RAND(0,99)} < (%s)]",p->u1.str);
1579 snprintf(buf1,sizeof(buf1),"$[%s]",p->u1.str);
1581 if_test->appargs = strdup(buf1);
1582 snprintf(buf1,sizeof(buf1),"Finish if-%s-%d", label, control_statement_count);
1583 if_end->app = strdup("NoOp");
1584 if_end->appargs = strdup(buf1);
1585 if_test->origin = p;
1587 if (p->u3.else_statements) {
1588 if_skip = new_prio();
1589 if_skip->type = AEL_CONTROL1; /* simple goto */
1590 if_skip->goto_true = if_end;
1591 if_test->goto_false = if_skip;;
1594 if_test->goto_false = if_end;;
1598 linkprio(exten, if_test);
1600 /* now, put the body of the if here */
1602 gen_prios(exten, new_label, p->u2.statements, mother_exten, this_context); /* this will link in all the statements here */
1604 if (p->u3.else_statements) {
1605 linkprio(exten, if_skip);
1606 gen_prios(exten, new_label, p->u3.else_statements, mother_exten, this_context); /* this will link in all the statements here */
1610 linkprio(exten, if_end);
1614 case PV_STATEMENTBLOCK:
1615 gen_prios(exten, label, p->u1.list, mother_exten, this_context ); /* recurse into the block */
1619 control_statement_count++;
1620 /* generate an extension with name of catch, put all catch stats
1622 switch_case = new_exten();
1623 switch_case->context = this_context;
1624 linkexten(exten,switch_case);
1625 switch_case->name = strdup(p->u1.str);
1626 snprintf(new_label,sizeof(new_label),"catch-%s-%d",p->u1.str, control_statement_count);
1628 gen_prios(switch_case, new_label, p->u2.statements,mother_exten,this_context); /* this will link in all the catch body statements here */
1629 if (switch_case->return_needed) { /* returns now generate a Return() app call, no longer a goto to the end of the exten */
1631 struct ael_priority *np2 = new_prio();
1632 np2->type = AEL_APPCALL;
1633 np2->app = strdup("NoOp");
1634 snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name);
1635 np2->appargs = strdup(buf);
1636 linkprio(switch_case, np2);
1637 switch_case-> return_target = np2;
1647 static pval *get_extension_or_contxt(pval *p)
1649 while( p && p->type != PV_EXTENSION && p->type != PV_CONTEXT && p->type != PV_MACRO ) {
1657 static pval *get_contxt(pval *p)
1659 while( p && p->type != PV_CONTEXT && p->type != PV_MACRO ) {
1667 static int aeldebug = 0;
1669 /* interface stuff */
1671 /* if all the below are static, who cares if they are present? */
1673 static int pbx_load_module(void)
1675 int errs, sem_err, sem_warn, sem_note;
1677 struct ast_context *local_contexts=NULL, *con;
1678 struct pval *parse_tree;
1680 ast_log(LOG_NOTICE, "Starting AEL load process.\n");
1681 if (config[0] == '/')
1682 rfilename = (char *)config;
1684 rfilename = alloca(strlen(config) + strlen(ast_config_AST_CONFIG_DIR) + 2);
1685 sprintf(rfilename, "%s/%s", ast_config_AST_CONFIG_DIR, config);
1687 ast_log(LOG_NOTICE, "AEL load process: calculated config file name '%s'.\n", rfilename);
1689 if (access(rfilename,R_OK) != 0) {
1690 ast_log(LOG_NOTICE, "File %s not found; AEL declining load\n", rfilename);
1691 return AST_MODULE_LOAD_DECLINE;
1694 parse_tree = ael2_parse(rfilename, &errs);
1695 ast_log(LOG_NOTICE, "AEL load process: parsed config file name '%s'.\n", rfilename);
1696 ael2_semantic_check(parse_tree, &sem_err, &sem_warn, &sem_note);
1697 if (errs == 0 && sem_err == 0) {
1698 ast_log(LOG_NOTICE, "AEL load process: checked config file name '%s'.\n", rfilename);
1699 ast_compile_ael2(&local_contexts, parse_tree);
1700 ast_log(LOG_NOTICE, "AEL load process: compiled config file name '%s'.\n", rfilename);
1702 ast_merge_contexts_and_delete(&local_contexts, registrar);
1703 ast_log(LOG_NOTICE, "AEL load process: merged config file name '%s'.\n", rfilename);
1704 for (con = ast_walk_contexts(NULL); con; con = ast_walk_contexts(con))
1705 ast_context_verify_includes(con);
1706 ast_log(LOG_NOTICE, "AEL load process: verified config file name '%s'.\n", rfilename);
1708 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);
1709 destroy_pval(parse_tree); /* free up the memory */
1710 return AST_MODULE_LOAD_DECLINE;
1712 destroy_pval(parse_tree); /* free up the memory */
1714 return AST_MODULE_LOAD_SUCCESS;
1718 static int ael2_debug_read(int fd, int argc, char *argv[])
1720 aeldebug |= DEBUG_READ;
1724 static int ael2_debug_tokens(int fd, int argc, char *argv[])
1726 aeldebug |= DEBUG_TOKENS;
1730 static int ael2_debug_macros(int fd, int argc, char *argv[])
1732 aeldebug |= DEBUG_MACROS;
1736 static int ael2_debug_contexts(int fd, int argc, char *argv[])
1738 aeldebug |= DEBUG_CONTEXTS;
1742 static int ael2_no_debug(int fd, int argc, char *argv[])
1748 static int ael2_reload(int fd, int argc, char *argv[])
1750 return (pbx_load_module());
1753 static struct ast_cli_entry cli_ael_no_debug = {
1754 { "ael", "no", "debug", NULL },
1755 ael2_no_debug, NULL,
1758 static struct ast_cli_entry cli_ael[] = {
1759 { { "ael", "reload", NULL },
1760 ael2_reload, "Reload AEL configuration" },
1762 { { "ael", "debug", "read", NULL },
1763 ael2_debug_read, "Enable AEL read debug (does nothing)" },
1765 { { "ael", "debug", "tokens", NULL },
1766 ael2_debug_tokens, "Enable AEL tokens debug (does nothing)" },
1768 { { "ael", "debug", "macros", NULL },
1769 ael2_debug_macros, "Enable AEL macros debug (does nothing)" },
1771 { { "ael", "debug", "contexts", NULL },
1772 ael2_debug_contexts, "Enable AEL contexts debug (does nothing)" },
1774 { { "ael", "nodebug", NULL },
1775 ael2_no_debug, "Disable AEL debug messages",
1776 NULL, NULL, &cli_ael_no_debug },
1779 static int unload_module(void)
1781 ast_context_destroy(NULL, registrar);
1782 ast_cli_unregister_multiple(cli_ael, sizeof(cli_ael) / sizeof(struct ast_cli_entry));
1786 static int load_module(void)
1788 ast_cli_register_multiple(cli_ael, sizeof(cli_ael) / sizeof(struct ast_cli_entry));
1789 return (pbx_load_module());
1792 static int reload(void)
1794 return pbx_load_module();
1797 #ifdef STANDALONE_AEL
1798 #define AST_MODULE "ael"
1799 int ael_external_load_module(void);
1800 int ael_external_load_module(void)
1807 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Asterisk Extension Language Compiler",
1808 .load = load_module,
1809 .unload = unload_module,
1814 static char *ael_funclist[] =
1839 "GROUP_MATCH_COUNT",
1851 "QUEUE_MEMBER_COUNT",
1852 "QUEUE_MEMBER_LIST",
1873 int ael_is_funcname(char *name)
1876 t = sizeof(ael_funclist)/sizeof(char*);
1878 while ((s < t) && strcasecmp(name, ael_funclist[s]))