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.
27 #if !defined(STANDALONE_AEL)
28 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
31 #include <sys/types.h>
41 #include "asterisk/pbx.h"
42 #include "asterisk/config.h"
43 #include "asterisk/module.h"
44 #include "asterisk/logger.h"
45 #include "asterisk/cli.h"
46 #include "asterisk/app.h"
47 #include "asterisk/callerid.h"
48 #include "asterisk/ael_structs.h"
50 #include "asterisk/argdesc.h"
53 static char expr_output
[2096];
55 /* these functions are in ../ast_expr2.fl */
57 #define DEBUG_READ (1 << 0)
58 #define DEBUG_TOKENS (1 << 1)
59 #define DEBUG_MACROS (1 << 2)
60 #define DEBUG_CONTEXTS (1 << 3)
62 static char *config
= "extensions.ael";
63 static char *registrar
= "pbx_ael";
64 static int pbx_load_module(void);
66 static int errs
, warns
;
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
75 /* null definitions for structs passed down the infrastructure */
84 int option_matches_j( struct argdesc
*should
, pval
*is
, struct argapp
*app
);
85 int option_matches( struct argdesc
*should
, pval
*is
, struct argapp
*app
);
86 int ael_is_funcname(char *name
);
89 int check_app_args(pval
*appcall
, pval
*arglist
, struct argapp
*app
);
90 void check_pval(pval
*item
, struct argapp
*apps
, int in_globals
);
91 void check_pval_item(pval
*item
, struct argapp
*apps
, int in_globals
);
92 void check_switch_expr(pval
*item
, struct argapp
*apps
);
93 void ast_expr_register_extra_error_info(char *errmsg
);
94 void ast_expr_clear_extra_error_info(void);
95 int ast_expr(char *expr
, char *buf
, int length
);
96 struct pval
*find_macro(char *name
);
97 struct pval
*find_context(char *name
);
98 struct pval
*find_context(char *name
);
99 struct pval
*find_macro(char *name
);
100 struct ael_priority
*new_prio(void);
101 struct ael_extension
*new_exten(void);
102 void linkprio(struct ael_extension
*exten
, struct ael_priority
*prio
, struct ael_extension
*mother_exten
);
103 void destroy_extensions(struct ael_extension
*exten
);
104 static void linkexten(struct ael_extension
*exten
, struct ael_extension
*add
);
105 static void gen_prios(struct ael_extension
*exten
, char *label
, pval
*statement
, struct ael_extension
*mother_exten
, struct ast_context
*context
);
106 void set_priorities(struct ael_extension
*exten
);
107 void add_extensions(struct ael_extension
*exten
);
108 void ast_compile_ael2(struct ast_context
**local_contexts
, struct pval
*root
);
109 void destroy_pval(pval
*item
);
110 void destroy_pval_item(pval
*item
);
111 int is_float(char *arg
);
112 int is_int(char *arg
);
113 int is_empty(char *arg
);
114 static pval
*current_db
=0;
115 static pval
*current_context
=0;
116 static pval
*current_extension
=0;
118 static const char *match_context
;
119 static const char *match_exten
;
120 static const char *match_label
;
121 static int in_abstract_context
;
122 static int count_labels
; /* true, put matcher in label counting mode */
123 static int label_count
; /* labels are only meant to be counted in a context or exten */
124 static int return_on_context_match
;
125 static pval
*last_matched_label
;
126 struct pval
*match_pval(pval
*item
);
127 static void check_timerange(pval
*p
);
128 static void check_dow(pval
*DOW
);
129 static void check_day(pval
*DAY
);
130 static void check_month(pval
*MON
);
131 static void check_expr2_input(pval
*expr
, char *str
);
132 static int extension_matches(pval
*here
, const char *exten
, const char *pattern
);
133 static void check_goto(pval
*item
);
134 static void find_pval_goto_item(pval
*item
, int lev
);
135 static void find_pval_gotos(pval
*item
, int lev
);
137 static struct pval
*find_label_in_current_context(char *exten
, char *label
, pval
*curr_cont
);
138 static struct pval
*find_first_label_in_current_context(char *label
, pval
*curr_cont
);
139 static void print_pval_list(FILE *fin
, pval
*item
, int depth
);
141 static struct pval
*find_label_in_current_extension(const char *label
, pval
*curr_ext
);
142 static struct pval
*find_label_in_current_db(const char *context
, const char *exten
, const char *label
);
143 static pval
*get_goto_target(pval
*item
);
144 static int label_inside_case(pval
*label
);
145 static void attach_exten(struct ael_extension
**list
, struct ael_extension
*newmem
);
146 static void fix_gotos_in_extensions(struct ael_extension
*exten
);
147 static pval
*get_extension_or_contxt(pval
*p
);
148 static pval
*get_contxt(pval
*p
);
149 static void remove_spaces_before_equals(char *str
);
150 static void substitute_commas(char *str
);
152 /* I am adding this code to substitute commas with vertbars in the args to apps */
153 static void substitute_commas(char *str
)
159 if (*p
== ',' && ((p
!= str
&& *(p
-1) != '\\')
162 if (*p
== '\\' && *(p
+1) == ',') { /* learning experience: the '\,' is turned into just ',' by pbx_config; So we need to do the same */
164 while (*q
) { /* move the ',' and everything after it up 1 char */
174 /* PRETTY PRINTER FOR AEL: ============================================================================= */
176 static void print_pval(FILE *fin
, pval
*item
, int depth
)
181 for (i
=0; i
<depth
; i
++) {
182 fprintf(fin
, "\t"); /* depth == indentation */
185 switch ( item
->type
) {
187 fprintf(fin
,"%s;\n", item
->u1
.str
); /* usually, words are encapsulated in something else */
191 fprintf(fin
,"macro %s(", item
->u1
.str
);
192 for (lp
=item
->u2
.arglist
; lp
; lp
=lp
->next
) {
193 if (lp
!= item
->u2
.arglist
)
195 fprintf(fin
,"%s", lp
->u1
.str
);
197 fprintf(fin
,") {\n");
198 print_pval_list(fin
,item
->u3
.macro_statements
,depth
+1);
199 for (i
=0; i
<depth
; i
++) {
200 fprintf(fin
,"\t"); /* depth == indentation */
202 fprintf(fin
,"};\n\n");
206 if ( item
->u3
.abstract
)
207 fprintf(fin
,"abstract context %s {\n", item
->u1
.str
);
209 fprintf(fin
,"context %s {\n", item
->u1
.str
);
210 print_pval_list(fin
,item
->u2
.statements
,depth
+1);
211 for (i
=0; i
<depth
; i
++) {
212 fprintf(fin
,"\t"); /* depth == indentation */
214 fprintf(fin
,"};\n\n");
218 fprintf(fin
,"&%s(", item
->u1
.str
);
219 for (lp
=item
->u2
.arglist
; lp
; lp
=lp
->next
) {
220 if ( lp
!= item
->u2
.arglist
)
222 fprintf(fin
,"%s", lp
->u1
.str
);
227 case PV_APPLICATION_CALL
:
228 fprintf(fin
,"%s(", item
->u1
.str
);
229 for (lp
=item
->u2
.arglist
; lp
; lp
=lp
->next
) {
230 if ( lp
!= item
->u2
.arglist
)
232 fprintf(fin
,"%s", lp
->u1
.str
);
238 fprintf(fin
,"case %s:\n", item
->u1
.str
);
239 print_pval_list(fin
,item
->u2
.statements
, depth
+1);
243 fprintf(fin
,"pattern %s:\n", item
->u1
.str
);
244 print_pval_list(fin
,item
->u2
.statements
, depth
+1);
248 fprintf(fin
,"default:\n");
249 print_pval_list(fin
,item
->u2
.statements
, depth
+1);
253 fprintf(fin
,"catch %s {\n", item
->u1
.str
);
254 print_pval_list(fin
,item
->u2
.statements
, depth
+1);
255 for (i
=0; i
<depth
; i
++) {
256 fprintf(fin
,"\t"); /* depth == indentation */
262 fprintf(fin
,"switches {\n");
263 print_pval_list(fin
,item
->u1
.list
,depth
+1);
264 for (i
=0; i
<depth
; i
++) {
265 fprintf(fin
,"\t"); /* depth == indentation */
271 fprintf(fin
,"eswitches {\n");
272 print_pval_list(fin
,item
->u1
.list
,depth
+1);
273 for (i
=0; i
<depth
; i
++) {
274 fprintf(fin
,"\t"); /* depth == indentation */
280 fprintf(fin
,"includes {\n");
281 for (lp
=item
->u1
.list
; lp
; lp
=lp
->next
) {
282 for (i
=0; i
<depth
+1; i
++) {
283 fprintf(fin
,"\t"); /* depth == indentation */
285 fprintf(fin
,"%s", lp
->u1
.str
); /* usually, words are encapsulated in something else */
286 if ( lp
->u2
.arglist
)
287 fprintf(fin
,"|%s|%s|%s|%s",
288 lp
->u2
.arglist
->u1
.str
,
289 lp
->u2
.arglist
->next
->u1
.str
,
290 lp
->u2
.arglist
->next
->next
->u1
.str
,
291 lp
->u2
.arglist
->next
->next
->next
->u1
.str
293 fprintf(fin
,";\n"); /* usually, words are encapsulated in something else */
296 print_pval_list(fin
,item
->u1
.list
,depth
+1);
297 for (i
=0; i
<depth
; i
++) {
298 fprintf(fin
,"\t"); /* depth == indentation */
303 case PV_STATEMENTBLOCK
:
305 print_pval_list(fin
,item
->u1
.list
, depth
+1);
306 for (i
=0; i
<depth
; i
++) {
307 fprintf(fin
,"\t"); /* depth == indentation */
313 fprintf(fin
,"%s=%s;\n", item
->u1
.str
, item
->u2
.val
);
317 fprintf(fin
,"goto %s", item
->u1
.list
->u1
.str
);
318 if ( item
->u1
.list
->next
)
319 fprintf(fin
,"|%s", item
->u1
.list
->next
->u1
.str
);
320 if ( item
->u1
.list
->next
&& item
->u1
.list
->next
->next
)
321 fprintf(fin
,"|%s", item
->u1
.list
->next
->next
->u1
.str
);
326 fprintf(fin
,"%s:\n", item
->u1
.str
);
330 fprintf(fin
,"for (%s; %s; %s)\n", item
->u1
.for_init
, item
->u2
.for_test
, item
->u3
.for_inc
);
331 print_pval_list(fin
,item
->u4
.for_statements
,depth
+1);
335 fprintf(fin
,"while (%s)\n", item
->u1
.str
);
336 print_pval_list(fin
,item
->u2
.statements
,depth
+1);
340 fprintf(fin
,"break;\n");
344 fprintf(fin
,"return;\n");
348 fprintf(fin
,"continue;\n");
354 if ( item
->type
== PV_IFTIME
) {
356 fprintf(fin
,"ifTime ( %s|%s|%s|%s )\n",
357 item
->u1
.list
->u1
.str
,
358 item
->u1
.list
->next
->u1
.str
,
359 item
->u1
.list
->next
->next
->u1
.str
,
360 item
->u1
.list
->next
->next
->next
->u1
.str
362 } else if ( item
->type
== PV_RANDOM
) {
363 fprintf(fin
,"random ( %s )\n", item
->u1
.str
);
365 fprintf(fin
,"if ( %s )\n", item
->u1
.str
);
366 if ( item
->u2
.statements
&& item
->u2
.statements
->next
) {
367 for (i
=0; i
<depth
; i
++) {
368 fprintf(fin
,"\t"); /* depth == indentation */
371 print_pval_list(fin
,item
->u2
.statements
,depth
+1);
372 for (i
=0; i
<depth
; i
++) {
373 fprintf(fin
,"\t"); /* depth == indentation */
375 if ( item
->u3
.else_statements
)
379 } else if (item
->u2
.statements
) {
380 print_pval_list(fin
,item
->u2
.statements
,depth
+1);
382 if (item
->u3
.else_statements
)
383 fprintf(fin
, " {} ");
385 fprintf(fin
, " {}; ");
387 if ( item
->u3
.else_statements
) {
388 for (i
=0; i
<depth
; i
++) {
389 fprintf(fin
,"\t"); /* depth == indentation */
391 fprintf(fin
,"else\n");
392 print_pval_list(fin
,item
->u3
.else_statements
, depth
);
397 fprintf(fin
,"switch( %s ) {\n", item
->u1
.str
);
398 print_pval_list(fin
,item
->u2
.statements
,depth
+1);
399 for (i
=0; i
<depth
; i
++) {
400 fprintf(fin
,"\t"); /* depth == indentation */
406 if ( item
->u4
.regexten
)
407 fprintf(fin
, "regexten ");
408 if ( item
->u3
.hints
)
409 fprintf(fin
,"hints(%s) ", item
->u3
.hints
);
411 fprintf(fin
,"%s => \n", item
->u1
.str
);
412 print_pval_list(fin
,item
->u2
.statements
,depth
+1);
416 fprintf(fin
,"ignorepat => %s\n", item
->u1
.str
);
420 fprintf(fin
,"globals {\n");
421 print_pval_list(fin
,item
->u1
.statements
,depth
+1);
422 for (i
=0; i
<depth
; i
++) {
423 fprintf(fin
,"\t"); /* depth == indentation */
430 static void print_pval_list(FILE *fin
, pval
*item
, int depth
)
434 for (i
=item
; i
; i
=i
->next
) {
435 print_pval(fin
, i
, depth
);
440 static void ael2_print(char *fname
, pval
*tree
)
442 FILE *fin
= fopen(fname
,"w");
444 ast_log(LOG_ERROR
, "Couldn't open %s for writing.\n", fname
);
447 print_pval_list(fin
, tree
, 0);
453 /* EMPTY TEMPLATE FUNCS FOR AEL TRAVERSAL: ============================================================================= */
455 void traverse_pval_template(pval
*item
, int depth
);
456 void traverse_pval_item_template(pval
*item
, int depth
);
459 void traverse_pval_item_template(pval
*item
, int depth
)/* depth comes in handy for a pretty print (indentation),
460 but you may not need it */
464 switch ( item
->type
) {
466 /* fields: item->u1.str == string associated with this (word). */
470 /* fields: item->u1.str == name of macro
471 item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
472 item->u2.arglist->u1.str == argument
473 item->u2.arglist->next == next arg
475 item->u3.macro_statements == pval list of statements in macro body.
477 for (lp
=item
->u2
.arglist
; lp
; lp
=lp
->next
) {
480 traverse_pval_item_template(item
->u3
.macro_statements
,depth
+1);
484 /* fields: item->u1.str == name of context
485 item->u2.statements == pval list of statements in context body
486 item->u3.abstract == int 1 if an abstract keyword were present
488 traverse_pval_item_template(item
->u2
.statements
,depth
+1);
492 /* fields: item->u1.str == name of macro to call
493 item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
494 item->u2.arglist->u1.str == argument
495 item->u2.arglist->next == next arg
497 for (lp
=item
->u2
.arglist
; lp
; lp
=lp
->next
) {
501 case PV_APPLICATION_CALL
:
502 /* fields: item->u1.str == name of application to call
503 item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
504 item->u2.arglist->u1.str == argument
505 item->u2.arglist->next == next arg
507 for (lp
=item
->u2
.arglist
; lp
; lp
=lp
->next
) {
512 /* fields: item->u1.str == value of case
513 item->u2.statements == pval list of statements under the case
515 traverse_pval_item_template(item
->u2
.statements
,depth
+1);
519 /* fields: item->u1.str == value of case
520 item->u2.statements == pval list of statements under the case
522 traverse_pval_item_template(item
->u2
.statements
,depth
+1);
527 item->u2.statements == pval list of statements under the case
529 traverse_pval_item_template(item
->u2
.statements
,depth
+1);
533 /* fields: item->u1.str == name of extension to catch
534 item->u2.statements == pval list of statements in context body
536 traverse_pval_item_template(item
->u2
.statements
,depth
+1);
540 /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
542 traverse_pval_item_template(item
->u1
.list
,depth
+1);
546 /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
548 traverse_pval_item_template(item
->u1
.list
,depth
+1);
552 /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
553 item->u2.arglist == pval list of 4 PV_WORD elements for time values
555 traverse_pval_item_template(item
->u1
.list
,depth
+1);
556 traverse_pval_item_template(item
->u2
.arglist
,depth
+1);
559 case PV_STATEMENTBLOCK
:
560 /* fields: item->u1.list == pval list of statements in block, one per entry in the list
562 traverse_pval_item_template(item
->u1
.list
,depth
+1);
566 /* fields: item->u1.str == variable name
567 item->u2.val == variable value to assign
572 /* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user.
573 item->u1.list->u1.str == where the data on a PV_WORD will always be.
576 if ( item
->u1
.list
->next
)
578 if ( item
->u1
.list
->next
&& item
->u1
.list
->next
->next
)
584 /* fields: item->u1.str == label name
589 /* fields: item->u1.for_init == a string containing the initalizer
590 item->u2.for_test == a string containing the loop test
591 item->u3.for_inc == a string containing the loop increment
593 item->u4.for_statements == a pval list of statements in the for ()
595 traverse_pval_item_template(item
->u4
.for_statements
,depth
+1);
599 /* fields: item->u1.str == the while conditional, as supplied by user
601 item->u2.statements == a pval list of statements in the while ()
603 traverse_pval_item_template(item
->u2
.statements
,depth
+1);
622 /* fields: item->u1.list == there are 4 linked PV_WORDs here.
624 item->u2.statements == a pval list of statements in the if ()
625 item->u3.else_statements == a pval list of statements in the else
628 traverse_pval_item_template(item
->u2
.statements
,depth
+1);
629 if ( item
->u3
.else_statements
) {
630 traverse_pval_item_template(item
->u3
.else_statements
,depth
+1);
635 /* fields: item->u1.str == the random number expression, as supplied by user
637 item->u2.statements == a pval list of statements in the if ()
638 item->u3.else_statements == a pval list of statements in the else
641 traverse_pval_item_template(item
->u2
.statements
,depth
+1);
642 if ( item
->u3
.else_statements
) {
643 traverse_pval_item_template(item
->u3
.else_statements
,depth
+1);
648 /* fields: item->u1.str == the if conditional, as supplied by user
650 item->u2.statements == a pval list of statements in the if ()
651 item->u3.else_statements == a pval list of statements in the else
654 traverse_pval_item_template(item
->u2
.statements
,depth
+1);
655 if ( item
->u3
.else_statements
) {
656 traverse_pval_item_template(item
->u3
.else_statements
,depth
+1);
661 /* fields: item->u1.str == the switch expression
663 item->u2.statements == a pval list of statements in the switch,
664 (will be case statements, most likely!)
666 traverse_pval_item_template(item
->u2
.statements
,depth
+1);
670 /* fields: item->u1.str == the extension name, label, whatever it's called
672 item->u2.statements == a pval list of statements in the extension
673 item->u3.hints == a char * hint argument
674 item->u4.regexten == an int boolean. non-zero says that regexten was specified
676 traverse_pval_item_template(item
->u2
.statements
,depth
+1);
680 /* fields: item->u1.str == the ignorepat data
685 /* fields: item->u1.statements == pval list of statements, usually vardecs
687 traverse_pval_item_template(item
->u1
.statements
,depth
+1);
692 void traverse_pval_template(pval
*item
, int depth
) /* depth comes in handy for a pretty print (indentation),
693 but you may not need it */
697 for (i
=item
; i
; i
=i
->next
) {
698 traverse_pval_item_template(i
, depth
);
703 /* SEMANTIC CHECKING FOR AEL: ============================================================================= */
705 /* (not all that is syntactically legal is good! */
709 static int extension_matches(pval
*here
, const char *exten
, const char *pattern
)
714 /* simple case, they match exactly, the pattern and exten name */
715 if( !strcmp(pattern
,exten
) == 0 )
718 if ( pattern
[0] == '_' ) {
723 if ( strlen(pattern
)*5 >= 2000 ) /* safety valve */ {
724 ast_log(LOG_ERROR
,"Error: The pattern %s is way too big. Pattern matching cancelled.\n",
728 /* form a regular expression from the pattern, and then match it against exten */
729 *r
++ = '^'; /* what if the extension is a pattern ?? */
730 *r
++ = '_'; /* what if the extension is a pattern ?? */
732 for (p
=pattern
+1; *p
; p
++) {
762 while ( *p
&& *p
!= ']' ) {
766 ast_log(LOG_WARNING
, "Warning: file %s, line %d-%d: The extension pattern '%s' is missing a closing bracket \n",
767 here
->filename
, here
->startline
, here
->endline
, pattern
);
786 *r
++ = '$'; /* what if the extension is a pattern ?? */
787 *r
++ = *p
++; /* put in the closing null */
788 err1
= regcomp(&preg
, reg1
, REG_NOSUB
|REG_EXTENDED
);
791 regerror(err1
,&preg
,errmess
,sizeof(errmess
));
793 ast_log(LOG_WARNING
, "Regcomp of %s failed, error code %d\n",
797 err1
= regexec(&preg
, exten
, 0, 0, 0);
801 /* ast_log(LOG_NOTICE,"*****************************[%d]Extension %s did not match %s(%s)\n",
802 err1,exten, pattern, reg1); */
803 return 0; /* no match */
805 /* ast_log(LOG_NOTICE,"*****************************Extension %s matched %s\n",
812 if ( strcmp(exten
,pattern
) == 0 ) {
820 static void check_expr2_input(pval
*expr
, char *str
)
822 int spaces
= strspn(str
,"\t \n");
823 if ( !strncmp(str
+spaces
,"$[",2) ) {
824 ast_log(LOG_WARNING
, "Warning: file %s, line %d-%d: The expression '%s' is redundantly wrapped in '$[ ]'. \n",
825 expr
->filename
, expr
->startline
, expr
->endline
, str
);
830 static void check_includes(pval
*includes
)
833 for (p4
=includes
->u1
.list
; p4
; p4
=p4
->next
) {
834 /* for each context pointed to, find it, then find a context/label that matches the
836 char *incl_context
= p4
->u1
.str
;
837 /* find a matching context name */
838 struct pval
*that_other_context
= find_context(incl_context
);
839 if (!that_other_context
&& strcmp(incl_context
, "parkedcalls") != 0) {
840 ast_log(LOG_WARNING
, "Warning: file %s, line %d-%d: The included context '%s' cannot be found.\n\
841 (You may ignore this warning if '%s' exists in extensions.conf, or is created by another module. I cannot check for those.)\n",
842 includes
->filename
, includes
->startline
, includes
->endline
, incl_context
, incl_context
);
849 static void check_timerange(pval
*p
)
856 times
= ast_strdupa(p
->u1
.str
);
858 /* Star is all times */
859 if (ast_strlen_zero(times
) || !strcmp(times
, "*")) {
862 /* Otherwise expect a range */
863 e
= strchr(times
, '-');
865 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",
866 p
->filename
, p
->startline
, p
->endline
, times
);
872 while (*e
&& !isdigit(*e
))
875 ast_log(LOG_WARNING
, "Warning: file %s, line %d-%d: The time range format (%s) is missing the end time!\n",
876 p
->filename
, p
->startline
, p
->endline
, p
->u1
.str
);
879 if (sscanf(times
, "%d:%d", &s1
, &s2
) != 2) {
880 ast_log(LOG_WARNING
, "Warning: file %s, line %d-%d: The start time (%s) isn't quite right!\n",
881 p
->filename
, p
->startline
, p
->endline
, times
);
884 if (sscanf(e
, "%d:%d", &e1
, &e2
) != 2) {
885 ast_log(LOG_WARNING
, "Warning: file %s, line %d-%d: The end time (%s) isn't quite right!\n",
886 p
->filename
, p
->startline
, p
->endline
, times
);
891 if ((s1
< 0) || (s1
>= 24*30)) {
892 ast_log(LOG_WARNING
, "Warning: file %s, line %d-%d: The start time (%s) is out of range!\n",
893 p
->filename
, p
->startline
, p
->endline
, times
);
897 if ((e1
< 0) || (e1
>= 24*30)) {
898 ast_log(LOG_WARNING
, "Warning: file %s, line %d-%d: The end time (%s) is out of range!\n",
899 p
->filename
, p
->startline
, p
->endline
, e
);
905 static char *days
[] =
916 /*! \brief get_dow: Get day of week */
917 static void check_dow(pval
*DOW
)
921 /* The following line is coincidence, really! */
924 dow
= ast_strdupa(DOW
->u1
.str
);
926 /* Check for all days */
927 if (ast_strlen_zero(dow
) || !strcmp(dow
, "*"))
929 /* Get start and ending days */
930 c
= strchr(dow
, '-');
938 while ((s
< 7) && strcasecmp(dow
, days
[s
])) s
++;
940 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",
941 DOW
->filename
, DOW
->startline
, DOW
->endline
, dow
);
946 while ((e
< 7) && strcasecmp(c
, days
[e
])) e
++;
948 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",
949 DOW
->filename
, DOW
->startline
, DOW
->endline
, c
);
956 static void check_day(pval
*DAY
)
960 /* The following line is coincidence, really! */
963 day
= ast_strdupa(DAY
->u1
.str
);
965 /* Check for all days */
966 if (ast_strlen_zero(day
) || !strcmp(day
, "*")) {
969 /* Get start and ending days */
970 c
= strchr(day
, '-');
976 if (sscanf(day
, "%d", &s
) != 1) {
977 ast_log(LOG_WARNING
, "Warning: file %s, line %d-%d: The start day of month (%s) must be a number!\n",
978 DAY
->filename
, DAY
->startline
, DAY
->endline
, day
);
981 else if ((s
< 1) || (s
> 31)) {
982 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",
983 DAY
->filename
, DAY
->startline
, DAY
->endline
, day
);
988 if (sscanf(c
, "%d", &e
) != 1) {
989 ast_log(LOG_WARNING
, "Warning: file %s, line %d-%d: The end day of month (%s) must be a number!\n",
990 DAY
->filename
, DAY
->startline
, DAY
->endline
, c
);
993 else if ((e
< 1) || (e
> 31)) {
994 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",
995 DAY
->filename
, DAY
->startline
, DAY
->endline
, day
);
1003 static char *months
[] =
1019 static void check_month(pval
*MON
)
1023 /* The following line is coincidence, really! */
1026 mon
= ast_strdupa(MON
->u1
.str
);
1028 /* Check for all days */
1029 if (ast_strlen_zero(mon
) || !strcmp(mon
, "*"))
1031 /* Get start and ending days */
1032 c
= strchr(mon
, '-');
1037 /* Find the start */
1039 while ((s
< 12) && strcasecmp(mon
, months
[s
])) s
++;
1041 ast_log(LOG_WARNING
, "Warning: file %s, line %d-%d: The start month (%s) must be a one of: 'jan', 'feb', ..., 'dec'!\n",
1042 MON
->filename
, MON
->startline
, MON
->endline
, mon
);
1047 while ((e
< 12) && strcasecmp(mon
, months
[e
])) e
++;
1049 ast_log(LOG_WARNING
, "Warning: file %s, line %d-%d: The end month (%s) must be a one of: 'jan', 'feb', ..., 'dec'!\n",
1050 MON
->filename
, MON
->startline
, MON
->endline
, c
);
1057 static int check_break(pval
*item
)
1061 while( p
&& p
->type
!= PV_MACRO
&& p
->type
!= PV_CONTEXT
) /* early cutout, sort of */ {
1062 /* a break is allowed in WHILE, FOR, CASE, DEFAULT, PATTERN; otherwise, it don't make
1064 if( p
->type
== PV_CASE
|| p
->type
== PV_DEFAULT
|| p
->type
== PV_PATTERN
1065 || p
->type
== PV_WHILE
|| p
->type
== PV_FOR
) {
1070 ast_log(LOG_ERROR
,"Error: file %s, line %d-%d: 'break' not in switch, for, or while statement!\n",
1071 item
->filename
, item
->startline
, item
->endline
);
1077 static int check_continue(pval
*item
)
1081 while( p
&& p
->type
!= PV_MACRO
&& p
->type
!= PV_CONTEXT
) /* early cutout, sort of */ {
1082 /* a break is allowed in WHILE, FOR, CASE, DEFAULT, PATTERN; otherwise, it don't make
1084 if( p
->type
== PV_WHILE
|| p
->type
== PV_FOR
) {
1089 ast_log(LOG_ERROR
,"Error: file %s, line %d-%d: 'continue' not in 'for' or 'while' statement!\n",
1090 item
->filename
, item
->startline
, item
->endline
);
1097 /* general purpose goto finder */
1099 static void check_label(pval
*item
)
1101 /* basically, ensure that a label is not repeated in a context. Period.
1102 The method: well, for each label, find the first label in the context
1103 with the same name. If it's not the current label, then throw an error. */
1107 /* printf("==== check_label: ====\n"); */
1108 if( !current_extension
)
1109 curr
= current_context
;
1111 curr
= current_extension
;
1113 x
= find_first_label_in_current_context((char *)item
->u1
.str
, curr
);
1114 /* 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); */
1115 if( x
&& x
!= item
)
1117 ast_log(LOG_ERROR
,"Error: file %s, line %d-%d: Duplicate label %s! Previously defined at file %s, line %d.\n",
1118 item
->filename
, item
->startline
, item
->endline
, item
->u1
.str
, x
->filename
, x
->startline
);
1121 /* printf("<<<<< check_label: ====\n"); */
1124 static pval
*get_goto_target(pval
*item
)
1126 /* just one item-- the label should be in the current extension */
1127 pval
*curr_ext
= get_extension_or_contxt(item
); /* containing exten, or macro */
1130 if (item
->u1
.list
&& !item
->u1
.list
->next
&& !strstr((item
->u1
.list
)->u1
.str
,"${")) {
1131 struct pval
*x
= find_label_in_current_extension((char*)((item
->u1
.list
)->u1
.str
), curr_ext
);
1135 curr_cont
= get_contxt(item
);
1138 if (item
->u1
.list
->next
&& !item
->u1
.list
->next
->next
) {
1139 if (!strstr((item
->u1
.list
)->u1
.str
,"${")
1140 && !strstr(item
->u1
.list
->next
->u1
.str
,"${") ) /* Don't try to match variables */ {
1141 struct pval
*x
= find_label_in_current_context((char *)item
->u1
.list
->u1
.str
, (char *)item
->u1
.list
->next
->u1
.str
, curr_cont
);
1147 if (item
->u1
.list
->next
&& item
->u1
.list
->next
->next
) {
1149 pval
*first
= item
->u1
.list
;
1150 pval
*second
= item
->u1
.list
->next
;
1151 pval
*third
= item
->u1
.list
->next
->next
;
1153 if (!strstr((item
->u1
.list
)->u1
.str
,"${")
1154 && !strstr(item
->u1
.list
->next
->u1
.str
,"${")
1155 && !strstr(item
->u1
.list
->next
->next
->u1
.str
,"${")) /* Don't try to match variables */ {
1156 struct pval
*x
= find_label_in_current_db((char*)first
->u1
.str
, (char*)second
->u1
.str
, (char*)third
->u1
.str
);
1160 struct pval
*that_context
= find_context(item
->u1
.list
->u1
.str
);
1162 /* the target of the goto could be in an included context!! Fancy that!! */
1163 /* look for includes in the current context */
1165 for (p3
=that_context
->u2
.statements
; p3
; p3
=p3
->next
) {
1166 if (p3
->type
== PV_INCLUDES
) {
1168 for (p4
=p3
->u1
.list
; p4
; p4
=p4
->next
) {
1169 /* for each context pointed to, find it, then find a context/label that matches the
1171 char *incl_context
= p4
->u1
.str
;
1172 /* find a matching context name */
1173 struct pval
*that_other_context
= find_context(incl_context
);
1174 if (that_other_context
) {
1176 x3
= find_label_in_current_context((char *)item
->u1
.list
->next
->u1
.str
, (char *)item
->u1
.list
->next
->next
->u1
.str
, that_other_context
);
1192 static void check_goto(pval
*item
)
1194 /* check for the target of the goto-- does it exist? */
1195 if ( !(item
->u1
.list
)->next
&& !(item
->u1
.list
)->u1
.str
) {
1196 ast_log(LOG_ERROR
,"Error: file %s, line %d-%d: goto: empty label reference found!\n",
1197 item
->filename
, item
->startline
, item
->endline
);
1201 /* just one item-- the label should be in the current extension */
1203 if (item
->u1
.list
&& !item
->u1
.list
->next
&& !strstr((item
->u1
.list
)->u1
.str
,"${")) {
1204 struct pval
*z
= get_extension_or_contxt(item
);
1207 x
= find_label_in_current_extension((char*)((item
->u1
.list
)->u1
.str
), z
); /* if in macro, use current context instead */
1208 /* printf("Called find_label_in_current_extension with arg %s; current_extension is %x: %d\n",
1209 (char*)((item->u1.list)->u1.str), current_extension?current_extension:current_context, current_extension?current_extension->type:current_context->type); */
1211 ast_log(LOG_ERROR
,"Error: file %s, line %d-%d: goto: no label %s exists in the current extension!\n",
1212 item
->filename
, item
->startline
, item
->endline
, item
->u1
.list
->u1
.str
);
1220 if (item
->u1
.list
->next
&& !item
->u1
.list
->next
->next
) {
1222 /* printf("Calling find_label_in_current_context with args %s, %s\n",
1223 (char*)((item->u1.list)->u1.str), (char *)item->u1.list->next->u1.str); */
1224 if (!strstr((item
->u1
.list
)->u1
.str
,"${")
1225 && !strstr(item
->u1
.list
->next
->u1
.str
,"${") ) /* Don't try to match variables */ {
1226 struct pval
*z
= get_contxt(item
);
1230 x
= find_label_in_current_context((char *)item
->u1
.list
->u1
.str
, (char *)item
->u1
.list
->next
->u1
.str
, z
);
1233 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",
1234 item
->filename
, item
->startline
, item
->endline
, item
->u1
.list
->u1
.str
, item
->u1
.list
->next
->u1
.str
);
1243 if (item
->u1
.list
->next
&& item
->u1
.list
->next
->next
) {
1245 pval
*first
= item
->u1
.list
;
1246 pval
*second
= item
->u1
.list
->next
;
1247 pval
*third
= item
->u1
.list
->next
->next
;
1249 /* printf("Calling find_label_in_current_db with args %s, %s, %s\n",
1250 (char*)first->u1.str, (char*)second->u1.str, (char*)third->u1.str); */
1251 if (!strstr((item
->u1
.list
)->u1
.str
,"${")
1252 && !strstr(item
->u1
.list
->next
->u1
.str
,"${")
1253 && !strstr(item
->u1
.list
->next
->next
->u1
.str
,"${")) /* Don't try to match variables */ {
1254 struct pval
*x
= find_label_in_current_db((char*)first
->u1
.str
, (char*)second
->u1
.str
, (char*)third
->u1
.str
);
1257 struct pval
*found
= 0;
1258 struct pval
*that_context
= find_context(item
->u1
.list
->u1
.str
);
1260 /* the target of the goto could be in an included context!! Fancy that!! */
1261 /* look for includes in the current context */
1263 for (p3
=that_context
->u2
.statements
; p3
; p3
=p3
->next
) {
1264 if (p3
->type
== PV_INCLUDES
) {
1266 for (p4
=p3
->u1
.list
; p4
; p4
=p4
->next
) {
1267 /* for each context pointed to, find it, then find a context/label that matches the
1269 char *incl_context
= p4
->u1
.str
;
1270 /* find a matching context name */
1271 struct pval
*that_other_context
= find_context(incl_context
);
1272 if (that_other_context
) {
1274 x3
= find_label_in_current_context((char *)item
->u1
.list
->next
->u1
.str
, (char *)item
->u1
.list
->next
->next
->u1
.str
, that_other_context
);
1284 ast_log(LOG_ERROR
,"Error: file %s, line %d-%d: goto: no label %s|%s exists in the context %s or its inclusions!\n",
1285 item
->filename
, item
->startline
, item
->endline
, item
->u1
.list
->next
->u1
.str
, item
->u1
.list
->next
->next
->u1
.str
, item
->u1
.list
->u1
.str
);
1289 /* here is where code would go to check for target existence in extensions.conf files */
1290 ast_log(LOG_WARNING
,"Warning: file %s, line %d-%d: goto: no context %s could be found that matches the goto target!\n",
1291 item
->filename
, item
->startline
, item
->endline
, item
->u1
.list
->u1
.str
);
1292 warns
++; /* this is just a warning, because this context could be in extensions.conf or somewhere */
1300 static void find_pval_goto_item(pval
*item
, int lev
)
1305 ast_log(LOG_ERROR
,"find_pval_goto in infinite loop! item_type: %d\n\n", item
->type
);
1309 switch ( item
->type
) {
1311 /* fields: item->u1.str == name of macro
1312 item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
1313 item->u2.arglist->u1.str == argument
1314 item->u2.arglist->next == next arg
1316 item->u3.macro_statements == pval list of statements in macro body.
1319 /* printf("Descending into macro %s at line %d\n", item->u1.str, item->startline); */
1320 find_pval_gotos(item
->u3
.macro_statements
,lev
+1); /* if we're just searching for a context, don't bother descending into them */
1325 /* fields: item->u1.str == name of context
1326 item->u2.statements == pval list of statements in context body
1327 item->u3.abstract == int 1 if an abstract keyword were present
1332 /* fields: item->u1.str == value of case
1333 item->u2.statements == pval list of statements under the case
1335 /* printf("Descending into Case of %s\n", item->u1.str); */
1336 find_pval_gotos(item
->u2
.statements
,lev
+1);
1340 /* fields: item->u1.str == value of case
1341 item->u2.statements == pval list of statements under the case
1343 /* printf("Descending into Pattern of %s\n", item->u1.str); */
1344 find_pval_gotos(item
->u2
.statements
,lev
+1);
1349 item->u2.statements == pval list of statements under the case
1351 /* printf("Descending into default\n"); */
1352 find_pval_gotos(item
->u2
.statements
,lev
+1);
1356 /* fields: item->u1.str == name of extension to catch
1357 item->u2.statements == pval list of statements in context body
1359 /* printf("Descending into catch of %s\n", item->u1.str); */
1360 find_pval_gotos(item
->u2
.statements
,lev
+1);
1363 case PV_STATEMENTBLOCK
:
1364 /* fields: item->u1.list == pval list of statements in block, one per entry in the list
1366 /* printf("Descending into statement block\n"); */
1367 find_pval_gotos(item
->u1
.list
,lev
+1);
1371 /* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user.
1372 item->u1.list->u1.str == where the data on a PV_WORD will always be.
1374 check_goto(item
); /* THE WHOLE FUNCTION OF THIS ENTIRE ROUTINE!!!! */
1378 /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
1380 for (p4
=item
->u1
.list
; p4
; p4
=p4
->next
) {
1381 /* for each context pointed to, find it, then find a context/label that matches the
1383 char *incl_context
= p4
->u1
.str
;
1384 /* find a matching context name */
1385 struct pval
*that_context
= find_context(incl_context
);
1386 if (that_context
&& that_context
->u2
.statements
) {
1387 /* printf("Descending into include of '%s' at line %d; that_context=%s, that_context type=%d\n", incl_context, item->startline, that_context->u1.str, that_context->type); */
1388 find_pval_gotos(that_context
->u2
.statements
,lev
+1); /* keep working up the includes */
1394 /* fields: item->u1.for_init == a string containing the initalizer
1395 item->u2.for_test == a string containing the loop test
1396 item->u3.for_inc == a string containing the loop increment
1398 item->u4.for_statements == a pval list of statements in the for ()
1400 /* printf("Descending into for at line %d\n", item->startline); */
1401 find_pval_gotos(item
->u4
.for_statements
,lev
+1);
1405 /* fields: item->u1.str == the while conditional, as supplied by user
1407 item->u2.statements == a pval list of statements in the while ()
1409 /* printf("Descending into while at line %d\n", item->startline); */
1410 find_pval_gotos(item
->u2
.statements
,lev
+1);
1414 /* fields: item->u1.str == the random number expression, as supplied by user
1416 item->u2.statements == a pval list of statements in the if ()
1417 item->u3.else_statements == a pval list of statements in the else
1419 fall thru to PV_IF */
1422 /* fields: item->u1.list == the time values, 4 of them, as PV_WORD structs in a list
1424 item->u2.statements == a pval list of statements in the if ()
1425 item->u3.else_statements == a pval list of statements in the else
1427 fall thru to PV_IF*/
1429 /* fields: item->u1.str == the if conditional, as supplied by user
1431 item->u2.statements == a pval list of statements in the if ()
1432 item->u3.else_statements == a pval list of statements in the else
1435 /* printf("Descending into random/iftime/if at line %d\n", item->startline); */
1436 find_pval_gotos(item
->u2
.statements
,lev
+1);
1438 if (item
->u3
.else_statements
) {
1439 /* printf("Descending into random/iftime/if's ELSE at line %d\n", item->startline); */
1440 find_pval_gotos(item
->u3
.else_statements
,lev
+1);
1445 /* fields: item->u1.str == the switch expression
1447 item->u2.statements == a pval list of statements in the switch,
1448 (will be case statements, most likely!)
1450 /* printf("Descending into switch at line %d\n", item->startline); */
1451 find_pval_gotos(item
->u3
.else_statements
,lev
+1);
1455 /* fields: item->u1.str == the extension name, label, whatever it's called
1457 item->u2.statements == a pval list of statements in the extension
1458 item->u3.hints == a char * hint argument
1459 item->u4.regexten == an int boolean. non-zero says that regexten was specified
1462 /* printf("Descending into extension %s at line %d\n", item->u1.str, item->startline); */
1463 find_pval_gotos(item
->u2
.statements
,lev
+1);
1471 static void find_pval_gotos(pval
*item
,int lev
)
1475 for (i
=item
; i
; i
=i
->next
) {
1476 /* printf("About to call pval_goto_item, itemcount=%d, itemtype=%d\n", item_count, i->type); */
1477 find_pval_goto_item(i
, lev
);
1483 /* general purpose label finder */
1484 static struct pval
*match_pval_item(pval
*item
)
1488 switch ( item
->type
) {
1490 /* fields: item->u1.str == name of macro
1491 item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
1492 item->u2.arglist->u1.str == argument
1493 item->u2.arglist->next == next arg
1495 item->u3.macro_statements == pval list of statements in macro body.
1497 /* printf(" matching in MACRO %s, match_context=%s; retoncontmtch=%d; \n", item->u1.str, match_context, return_on_context_match); */
1498 if (!strcmp(match_context
,"*") || !strcmp(item
->u1
.str
, match_context
)) {
1500 /* printf("MACRO: match context is: %s\n", match_context); */
1502 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 */ {
1503 /* printf("Returning on matching macro %s\n", match_context); */
1508 if (!return_on_context_match
) {
1509 /* printf("Descending into matching macro %s/%s\n", match_context, item->u1.str); */
1510 if ((x
=match_pval(item
->u3
.macro_statements
))) {
1511 /* printf("Responded with pval match %x\n", x); */
1516 /* printf("Skipping context/macro %s\n", item->u1.str); */
1522 /* fields: item->u1.str == name of context
1523 item->u2.statements == pval list of statements in context body
1524 item->u3.abstract == int 1 if an abstract keyword were present
1526 /* printf(" matching in CONTEXT\n"); */
1527 if (!strcmp(match_context
,"*") || !strcmp(item
->u1
.str
, match_context
)) {
1528 if (return_on_context_match
&& !strcmp(item
->u1
.str
, match_context
)) {
1529 /* printf("Returning on matching context %s\n", match_context); */
1530 /* printf("non-CONTEXT: Responded with pval match %x\n", x); */
1534 if (!return_on_context_match
) {
1535 /* printf("Descending into matching context %s\n", match_context); */
1536 if ((x
=match_pval(item
->u2
.statements
))) /* if we're just searching for a context, don't bother descending into them */ {
1537 /* printf("CONTEXT: Responded with pval match %x\n", x); */
1542 /* printf("Skipping context/macro %s\n", item->u1.str); */
1547 /* fields: item->u1.str == value of case
1548 item->u2.statements == pval list of statements under the case
1550 /* printf(" matching in CASE\n"); */
1551 if ((x
=match_pval(item
->u2
.statements
))) {
1552 /* printf("CASE: Responded with pval match %x\n", x); */
1558 /* fields: item->u1.str == value of case
1559 item->u2.statements == pval list of statements under the case
1561 /* printf(" matching in PATTERN\n"); */
1562 if ((x
=match_pval(item
->u2
.statements
))) {
1563 /* printf("PATTERN: Responded with pval match %x\n", x); */
1570 item->u2.statements == pval list of statements under the case
1572 /* printf(" matching in DEFAULT\n"); */
1573 if ((x
=match_pval(item
->u2
.statements
))) {
1574 /* printf("DEFAULT: Responded with pval match %x\n", x); */
1580 /* fields: item->u1.str == name of extension to catch
1581 item->u2.statements == pval list of statements in context body
1583 /* printf(" matching in CATCH\n"); */
1584 if (!strcmp(match_exten
,"*") || extension_matches(item
, match_exten
, item
->u1
.str
) ) {
1585 /* printf("Descending into matching catch %s => %s\n", match_exten, item->u1.str); */
1586 if (strcmp(match_label
,"1") == 0) {
1587 if (item
->u2
.statements
) {
1588 struct pval
*p5
= item
->u2
.statements
;
1589 while (p5
&& p5
->type
== PV_LABEL
) /* find the first non-label statement in this context. If it exists, there's a "1" */
1600 if ((x
=match_pval(item
->u2
.statements
))) {
1601 /* printf("CATCH: Responded with pval match %x\n", (unsigned int)x); */
1605 /* printf("Skipping catch %s\n", item->u1.str); */
1609 case PV_STATEMENTBLOCK
:
1610 /* fields: item->u1.list == pval list of statements in block, one per entry in the list
1612 /* printf(" matching in STATEMENTBLOCK\n"); */
1613 if ((x
=match_pval(item
->u1
.list
))) {
1614 /* printf("STATEMENTBLOCK: Responded with pval match %x\n", x); */
1620 /* fields: item->u1.str == label name
1622 /* printf("PV_LABEL %s (cont=%s, exten=%s\n",
1623 item->u1.str, current_context->u1.str, (current_extension?current_extension->u1.str:"<macro>"));*/
1626 if (!strcmp(match_label
, item
->u1
.str
)) {
1628 last_matched_label
= item
;
1632 if (!strcmp(match_label
, item
->u1
.str
)) {
1633 /* printf("LABEL: Responded with pval match %x\n", x); */
1640 /* fields: item->u1.for_init == a string containing the initalizer
1641 item->u2.for_test == a string containing the loop test
1642 item->u3.for_inc == a string containing the loop increment
1644 item->u4.for_statements == a pval list of statements in the for ()
1646 /* printf(" matching in FOR\n"); */
1647 if ((x
=match_pval(item
->u4
.for_statements
))) {
1648 /* printf("FOR: Responded with pval match %x\n", x);*/
1654 /* fields: item->u1.str == the while conditional, as supplied by user
1656 item->u2.statements == a pval list of statements in the while ()
1658 /* printf(" matching in WHILE\n"); */
1659 if ((x
=match_pval(item
->u2
.statements
))) {
1660 /* printf("WHILE: Responded with pval match %x\n", x); */
1666 /* fields: item->u1.str == the random number expression, as supplied by user
1668 item->u2.statements == a pval list of statements in the if ()
1669 item->u3.else_statements == a pval list of statements in the else
1671 fall thru to PV_IF */
1674 /* fields: item->u1.list == the time values, 4 of them, as PV_WORD structs in a list
1676 item->u2.statements == a pval list of statements in the if ()
1677 item->u3.else_statements == a pval list of statements in the else
1679 fall thru to PV_IF*/
1681 /* fields: item->u1.str == the if conditional, as supplied by user
1683 item->u2.statements == a pval list of statements in the if ()
1684 item->u3.else_statements == a pval list of statements in the else
1687 /* printf(" matching in IF/IFTIME/RANDOM\n"); */
1688 if ((x
=match_pval(item
->u2
.statements
))) {
1691 if (item
->u3
.else_statements
) {
1692 if ((x
=match_pval(item
->u3
.else_statements
))) {
1693 /* printf("IF/IFTIME/RANDOM: Responded with pval match %x\n", x); */
1700 /* fields: item->u1.str == the switch expression
1702 item->u2.statements == a pval list of statements in the switch,
1703 (will be case statements, most likely!)
1705 /* printf(" matching in SWITCH\n"); */
1706 if ((x
=match_pval(item
->u2
.statements
))) {
1707 /* printf("SWITCH: Responded with pval match %x\n", x); */
1713 /* fields: item->u1.str == the extension name, label, whatever it's called
1715 item->u2.statements == a pval list of statements in the extension
1716 item->u3.hints == a char * hint argument
1717 item->u4.regexten == an int boolean. non-zero says that regexten was specified
1719 /* printf(" matching in EXTENSION\n"); */
1720 if (!strcmp(match_exten
,"*") || extension_matches(item
, match_exten
, item
->u1
.str
) ) {
1721 /* printf("Descending into matching exten %s => %s\n", match_exten, item->u1.str); */
1722 if (strcmp(match_label
,"1") == 0) {
1723 if (item
->u2
.statements
) {
1724 struct pval
*p5
= item
->u2
.statements
;
1725 while (p5
&& p5
->type
== PV_LABEL
) /* find the first non-label statement in this context. If it exists, there's a "1" */
1736 if ((x
=match_pval(item
->u2
.statements
))) {
1737 /* printf("EXTENSION: Responded with pval match %x\n", x); */
1741 /* printf("Skipping exten %s\n", item->u1.str); */
1745 /* printf(" matching in default = %d\n", item->type); */
1751 struct pval
*match_pval(pval
*item
)
1755 for (i
=item
; i
; i
=i
->next
) {
1757 /* printf(" -- match pval: item %d\n", i->type); */
1759 if ((x
= match_pval_item(i
))) {
1760 /* printf("match_pval: returning x=%x\n", (int)x); */
1761 return x
; /* cut the search short */
1768 int count_labels_in_current_context(char *label
)
1772 return_on_context_match
= 0;
1773 match_pval(current_context
->u2
.statements
);
1779 struct pval
*find_first_label_in_current_context(char *label
, pval
*curr_cont
)
1781 /* printf(" --- Got args %s, %s\n", exten, label); */
1784 struct pval
*startpt
= ((curr_cont
->type
==PV_MACRO
)?curr_cont
->u3
.macro_statements
: curr_cont
->u2
.statements
);
1787 return_on_context_match
= 0;
1788 match_context
= "*";
1790 match_label
= label
;
1792 ret
= match_pval(curr_cont
);
1796 /* the target of the goto could be in an included context!! Fancy that!! */
1797 /* look for includes in the current context */
1798 for (p3
=startpt
; p3
; p3
=p3
->next
) {
1799 if (p3
->type
== PV_INCLUDES
) {
1801 for (p4
=p3
->u1
.list
; p4
; p4
=p4
->next
) {
1802 /* for each context pointed to, find it, then find a context/label that matches the
1804 char *incl_context
= p4
->u1
.str
;
1805 /* find a matching context name */
1806 struct pval
*that_context
= find_context(incl_context
);
1809 x3
= find_first_label_in_current_context(label
, that_context
);
1820 struct pval
*find_label_in_current_context(char *exten
, char *label
, pval
*curr_cont
)
1822 /* printf(" --- Got args %s, %s\n", exten, label); */
1825 struct pval
*startpt
;
1828 return_on_context_match
= 0;
1829 match_context
= "*";
1830 match_exten
= exten
;
1831 match_label
= label
;
1832 if (curr_cont
->type
== PV_MACRO
)
1833 startpt
= curr_cont
->u3
.macro_statements
;
1835 startpt
= curr_cont
->u2
.statements
;
1837 ret
= match_pval(startpt
);
1841 /* the target of the goto could be in an included context!! Fancy that!! */
1842 /* look for includes in the current context */
1843 for (p3
=startpt
; p3
; p3
=p3
->next
) {
1844 if (p3
->type
== PV_INCLUDES
) {
1846 for (p4
=p3
->u1
.list
; p4
; p4
=p4
->next
) {
1847 /* for each context pointed to, find it, then find a context/label that matches the
1849 char *incl_context
= p4
->u1
.str
;
1850 /* find a matching context name */
1851 struct pval
*that_context
= find_context(incl_context
);
1854 x3
= find_label_in_current_context(exten
, label
, that_context
);
1865 static struct pval
*find_label_in_current_extension(const char *label
, pval
*curr_ext
)
1867 /* printf(" --- Got args %s\n", label); */
1869 return_on_context_match
= 0;
1870 match_context
= "*";
1872 match_label
= label
;
1873 return match_pval(curr_ext
);
1876 static struct pval
*find_label_in_current_db(const char *context
, const char *exten
, const char *label
)
1878 /* printf(" --- Got args %s, %s, %s\n", context, exten, label); */
1880 return_on_context_match
= 0;
1882 match_context
= context
;
1883 match_exten
= exten
;
1884 match_label
= label
;
1886 return match_pval(current_db
);
1890 struct pval
*find_macro(char *name
)
1892 return_on_context_match
= 1;
1894 match_context
= name
;
1895 match_exten
= "*"; /* don't really need to set these, shouldn't be reached */
1897 return match_pval(current_db
);
1900 struct pval
*find_context(char *name
)
1902 return_on_context_match
= 1;
1904 match_context
= name
;
1905 match_exten
= "*"; /* don't really need to set these, shouldn't be reached */
1907 return match_pval(current_db
);
1910 int is_float(char *arg
)
1913 for (s
=arg
; *s
; s
++) {
1914 if (*s
!= '.' && (*s
< '0' || *s
> '9'))
1919 int is_int(char *arg
)
1922 for (s
=arg
; *s
; s
++) {
1923 if (*s
< '0' || *s
> '9')
1928 int is_empty(char *arg
)
1935 if (*arg
!= ' ' && *arg
!= '\t')
1943 int option_matches_j( struct argdesc
*should
, pval
*is
, struct argapp
*app
)
1945 struct argchoice
*ac
;
1948 switch (should
->dtype
) {
1949 case ARGD_OPTIONSET
:
1950 if ( strstr(is
->u1
.str
,"${") )
1951 return 0; /* no checking anything if there's a var reference in there! */
1953 opcop
= ast_strdupa(is
->u1
.str
);
1955 for (q
=opcop
;*q
;q
++) { /* erase the innards of X(innard) type arguments, so we don't get confused later */
1958 while (*p
&& *p
!= ')' )
1964 for (ac
=app
->opts
; ac
; ac
=ac
->next
) {
1965 if (strlen(ac
->name
)>1 && strchr(ac
->name
,'(') == 0 && strcmp(ac
->name
,is
->u1
.str
) == 0) /* multichar option, no parens, and a match? */
1968 for (ac
=app
->opts
; ac
; ac
=ac
->next
) {
1969 if (strlen(ac
->name
)==1 || strchr(ac
->name
,'(')) {
1970 char *p
= strchr(opcop
,ac
->name
[0]); /* wipe out all matched options in the user-supplied string */
1972 if (p
&& *p
== 'j') {
1973 ast_log(LOG_ERROR
, "Error: file %s, line %d-%d: The j option in the %s application call is not appropriate for AEL!\n",
1974 is
->filename
, is
->startline
, is
->endline
, app
->name
);
1980 if (ac
->name
[1] == '(') {
1981 if (*(p
+1) != '(') {
1982 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",
1983 is
->filename
, is
->startline
, is
->endline
, ac
->name
[0], app
->name
);
1990 for (q
=opcop
; *q
; q
++) {
1991 if ( *q
!= '+' && *q
!= '(' && *q
!= ')') {
1992 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",
1993 is
->filename
, is
->startline
, is
->endline
, *q
, app
->name
);
2005 int option_matches( struct argdesc
*should
, pval
*is
, struct argapp
*app
)
2007 struct argchoice
*ac
;
2010 switch (should
->dtype
) {
2012 if (is_empty(is
->u1
.str
) && should
->type
== ARGD_REQUIRED
)
2014 if (is
->u1
.str
&& strlen(is
->u1
.str
) > 0) /* most will match */
2019 if (is_int(is
->u1
.str
))
2026 if (is_float(is
->u1
.str
))
2033 if( !is
->u1
.str
|| strlen(is
->u1
.str
) == 0 )
2034 return 1; /* a null arg in the call will match an enum, I guess! */
2035 for (ac
=should
->choices
; ac
; ac
=ac
->next
) {
2036 if (strcmp(ac
->name
,is
->u1
.str
) == 0)
2042 case ARGD_OPTIONSET
:
2043 opcop
= ast_strdupa(is
->u1
.str
);
2045 for (ac
=app
->opts
; ac
; ac
=ac
->next
) {
2046 if (strlen(ac
->name
)>1 && strchr(ac
->name
,'(') == 0 && strcmp(ac
->name
,is
->u1
.str
) == 0) /* multichar option, no parens, and a match? */
2049 for (ac
=app
->opts
; ac
; ac
=ac
->next
) {
2050 if (strlen(ac
->name
)==1 || strchr(ac
->name
,'(')) {
2051 char *p
= strchr(opcop
,ac
->name
[0]); /* wipe out all matched options in the user-supplied string */
2055 if (ac
->name
[1] == '(') {
2056 if (*(p
+1) == '(') {
2058 while (*q
&& *q
!= ')') {
2070 return 1; /* matches anything */
2073 return 1; /* unless some for-sure match or non-match returns, then it must be close enough ... */
2077 int check_app_args(pval
* appcall
, pval
*arglist
, struct argapp
*app
)
2080 struct argdesc
*ad
= app
->args
;
2084 for (pa
= arglist
; pa
; pa
=pa
->next
) {
2086 ast_log(LOG_WARNING
, "Warning: file %s, line %d-%d: Extra argument %s not in application call to %s !\n",
2087 arglist
->filename
, arglist
->startline
, arglist
->endline
, pa
->u1
.str
, app
->name
);
2091 /* find the first entry in the ad list that will match */
2093 if ( ad
->dtype
== ARGD_VARARG
) /* once we hit the VARARG, all bets are off. Discontinue the comparisons */
2096 z
= option_matches( ad
, pa
, app
);
2101 if (ad
->type
== ARGD_REQUIRED
) {
2102 ast_log(LOG_WARNING
, "Warning: file %s, line %d-%d: Required argument %s not in application call to %s !\n",
2103 arglist
->filename
, arglist
->startline
, arglist
->endline
, ad
->dtype
==ARGD_OPTIONSET
?"options":ad
->name
, app
->name
);
2107 } else if (z
&& ad
->dtype
== ARGD_OPTIONSET
) {
2108 option_matches_j( ad
, pa
, app
);
2114 /* any app nodes left, that are not optional? */
2115 for ( ; ad
; ad
=ad
->next
) {
2116 if (ad
->type
== ARGD_REQUIRED
&& ad
->dtype
!= ARGD_VARARG
) {
2119 ast_log(LOG_WARNING
, "Warning: file %s, line %d-%d: Required argument %s not in application call to %s !\n",
2120 arglist
->filename
, arglist
->startline
, arglist
->endline
, ad
->dtype
==ARGD_OPTIONSET
?"options":ad
->name
, app
->name
);
2131 void check_switch_expr(pval
*item
, struct argapp
*apps
)
2134 /* get and clean the variable name */
2136 struct argapp
*a
,*a2
;
2137 struct appsetvar
*v
,*v2
;
2138 struct argchoice
*c
;
2142 while (p
&& *p
&& (*p
== ' ' || *p
== '\t' || *p
== '$' || *p
== '{' ) )
2145 buff1
= ast_strdupa(p
);
2147 while (strlen(buff1
) > 0 && ( buff1
[strlen(buff1
)-1] == '}' || buff1
[strlen(buff1
)-1] == ' ' || buff1
[strlen(buff1
)-1] == '\t'))
2148 buff1
[strlen(buff1
)-1] = 0;
2149 /* buff1 now contains the variable name */
2151 for (a
=apps
; a
; a
=a
->next
) {
2152 for (v
=a
->setvars
;v
;v
=v
->next
) {
2153 if (strcmp(v
->name
,buff1
) == 0) {
2161 /* we have a match, to a variable that has a set of determined values */
2166 /* first of all, does this switch have a default case ? */
2167 for (t
=item
->u2
.statements
; t
; t
=t
->next
) {
2168 if (t
->type
== PV_DEFAULT
) {
2172 if (t
->type
== PV_PATTERN
) {
2176 if (def
|| pat
) /* nothing to check. All cases accounted for! */
2178 for (c
=v
->vals
; c
; c
=c
->next
) {
2180 for (t
=item
->u2
.statements
; t
; t
=t
->next
) {
2181 if (t
->type
== PV_CASE
|| t
->type
== PV_PATTERN
) {
2182 if (!strcmp(t
->u1
.str
,c
->name
)) {
2189 ast_log(LOG_WARNING
,"Warning: file %s, line %d-%d: switch with expression(%s) does not handle the case of %s !\n",
2190 item
->filename
, item
->startline
, item
->endline
, item
->u1
.str
, c
->name
);
2194 /* next, is there an app call in the current exten, that would set this var? */
2196 t
= current_extension
->u2
.statements
;
2197 if ( t
&& t
->type
== PV_STATEMENTBLOCK
)
2198 t
= t
->u1
.statements
;
2199 for (; t
&& t
!= item
; t
=t
->next
) {
2200 if (t
->type
== PV_APPLICATION_CALL
) {
2201 /* find the application that matches the u1.str */
2202 for (a2
=apps
; a2
; a2
=a2
->next
) {
2203 if (strcasecmp(a2
->name
, t
->u1
.str
)==0) {
2204 for (v2
=a2
->setvars
; v2
; v2
=v2
->next
) {
2205 if (strcmp(v2
->name
, buff1
) == 0) {
2206 /* found an app that sets the var */
2220 /* see if it sets the var */
2222 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",
2223 item
->filename
, item
->startline
, item
->endline
, item
->u1
.str
);
2231 /* first of all, does this switch have a default case ? */
2232 for (t
=item
->u2
.statements
; t
; t
=t
->next
) {
2233 if (t
->type
== PV_DEFAULT
) {
2239 if (def
) /* nothing to check. All cases accounted for! */
2241 /* if no default, warn and insert a default case at the end */
2242 p2
= tl
->next
= calloc(1, sizeof(struct pval
));
2244 p2
->type
= PV_DEFAULT
;
2245 p2
->startline
= tl
->startline
;
2246 p2
->endline
= tl
->endline
;
2247 p2
->startcol
= tl
->startcol
;
2248 p2
->endcol
= tl
->endcol
;
2249 p2
->filename
= strdup(tl
->filename
);
2250 ast_log(LOG_WARNING
,"Warning: file %s, line %d-%d: A default case was automatically added to the switch.\n",
2251 p2
->filename
, p2
->startline
, p2
->endline
);
2257 static void check_context_names(void)
2260 for (i
=current_db
; i
; i
=i
->next
) {
2261 if (i
->type
== PV_CONTEXT
|| i
->type
== PV_MACRO
) {
2262 for (j
=i
->next
; j
; j
=j
->next
) {
2263 if ( j
->type
== PV_CONTEXT
|| j
->type
== PV_MACRO
) {
2264 if ( !strcmp(i
->u1
.str
, j
->u1
.str
) && !(i
->u3
.abstract
&2) && !(j
->u3
.abstract
&2) )
2266 ast_log(LOG_ERROR
,"Error: file %s, line %d-%d: The context name (%s) is also declared in file %s, line %d-%d! (and neither is marked 'extend')\n",
2267 i
->filename
, i
->startline
, i
->endline
, i
->u1
.str
, j
->filename
, j
->startline
, j
->endline
);
2276 static void check_abstract_reference(pval
*abstract_context
)
2279 /* find some context includes that reference this context */
2282 /* otherwise, print out a warning */
2283 for (i
=current_db
; i
; i
=i
->next
) {
2284 if (i
->type
== PV_CONTEXT
) {
2285 for (j
=i
->u2
. statements
; j
; j
=j
->next
) {
2286 if ( j
->type
== PV_INCLUDES
) {
2288 for (p4
=j
->u1
.list
; p4
; p4
=p4
->next
) {
2289 /* for each context pointed to, find it, then find a context/label that matches the
2291 if ( !strcmp(p4
->u1
.str
, abstract_context
->u1
.str
) )
2292 return; /* found a match! */
2298 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",
2299 abstract_context
->filename
, abstract_context
->startline
, abstract_context
->endline
, abstract_context
->u1
.str
);
2304 void check_pval_item(pval
*item
, struct argapp
*apps
, int in_globals
)
2308 struct argapp
*app
, *found
;
2310 struct pval
*macro_def
;
2311 struct pval
*app_def
;
2316 switch (item
->type
) {
2318 /* fields: item->u1.str == string associated with this (word).
2319 item->u2.arglist == pval list of 4 PV_WORD elements for time values (only in PV_INCLUDES) */
2323 /* fields: item->u1.str == name of macro
2324 item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
2325 item->u2.arglist->u1.str == argument
2326 item->u2.arglist->next == next arg
2328 item->u3.macro_statements == pval list of statements in macro body.
2330 in_abstract_context
= 0;
2331 current_context
= item
;
2332 current_extension
= 0;
2333 for (lp
=item
->u2
.arglist
; lp
; lp
=lp
->next
) {
2336 check_pval(item
->u3
.macro_statements
, apps
,in_globals
);
2340 /* fields: item->u1.str == name of context
2341 item->u2.statements == pval list of statements in context body
2342 item->u3.abstract == int 1 if an abstract keyword were present
2344 current_context
= item
;
2345 current_extension
= 0;
2346 if ( item
->u3
.abstract
) {
2347 in_abstract_context
= 1;
2348 check_abstract_reference(item
);
2350 in_abstract_context
= 0;
2351 check_pval(item
->u2
.statements
, apps
,in_globals
);
2355 /* fields: item->u1.str == name of macro to call
2356 item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
2357 item->u2.arglist->u1.str == argument
2358 item->u2.arglist->next == next arg
2360 macro_def
= find_macro(item
->u1
.str
);
2362 /* here is a good place to check to see if the definition is in extensions.conf! */
2363 ast_log(LOG_WARNING
, "Warning: file %s, line %d-%d: macro call to non-existent %s ! Hopefully it is present in extensions.conf! \n",
2364 item
->filename
, item
->startline
, item
->endline
, item
->u1
.str
);
2366 } else if (macro_def
->type
!= PV_MACRO
) {
2367 ast_log(LOG_ERROR
,"Error: file %s, line %d-%d: macro call to %s references a context, not a macro!\n",
2368 item
->filename
, item
->startline
, item
->endline
, item
->u1
.str
);
2371 /* macro_def is a MACRO, so do the args match in number? */
2375 for (lp
=item
->u2
.arglist
; lp
; lp
=lp
->next
) {
2378 for (lp
=macro_def
->u2
.arglist
; lp
; lp
=lp
->next
) {
2381 if (hereargs
!= thereargs
) {
2382 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",
2383 item
->filename
, item
->startline
, item
->endline
, item
->u1
.str
, hereargs
, thereargs
);
2389 case PV_APPLICATION_CALL
:
2390 /* fields: item->u1.str == name of application to call
2391 item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
2392 item->u2.arglist->u1.str == argument
2393 item->u2.arglist->next == next arg
2395 /* Need to check to see if the application is available! */
2396 app_def
= find_context(item
->u1
.str
);
2397 if (app_def
&& app_def
->type
== PV_MACRO
) {
2398 ast_log(LOG_ERROR
,"Error: file %s, line %d-%d: application call to %s references an existing macro, but had no & preceding it!\n",
2399 item
->filename
, item
->startline
, item
->endline
, item
->u1
.str
);
2402 if (strcasecmp(item
->u1
.str
,"GotoIf") == 0
2403 || strcasecmp(item
->u1
.str
,"GotoIfTime") == 0
2404 || strcasecmp(item
->u1
.str
,"while") == 0
2405 || strcasecmp(item
->u1
.str
,"endwhile") == 0
2406 || strcasecmp(item
->u1
.str
,"random") == 0
2407 || strcasecmp(item
->u1
.str
,"execIf") == 0 ) {
2408 ast_log(LOG_WARNING
,"Warning: file %s, line %d-%d: application call to %s needs to be re-written using AEL if, while, goto, etc. keywords instead!\n",
2409 item
->filename
, item
->startline
, item
->endline
, item
->u1
.str
);
2414 for (app
=apps
; app
; app
=app
->next
) {
2415 if (strcasecmp(app
->name
, item
->u1
.str
) == 0) {
2421 ast_log(LOG_WARNING
,"Warning: file %s, line %d-%d: application call to %s not listed in applist database!\n",
2422 item
->filename
, item
->startline
, item
->endline
, item
->u1
.str
);
2425 check_app_args(item
, item
->u2
.arglist
, app
);
2430 /* fields: item->u1.str == value of case
2431 item->u2.statements == pval list of statements under the case
2433 /* Make sure sequence of statements under case is terminated with goto, return, or break */
2434 /* find the last statement */
2435 check_pval(item
->u2
.statements
, apps
,in_globals
);
2439 /* fields: item->u1.str == value of case
2440 item->u2.statements == pval list of statements under the case
2442 /* Make sure sequence of statements under case is terminated with goto, return, or break */
2443 /* find the last statement */
2445 check_pval(item
->u2
.statements
, apps
,in_globals
);
2450 item->u2.statements == pval list of statements under the case
2453 check_pval(item
->u2
.statements
, apps
,in_globals
);
2457 /* fields: item->u1.str == name of extension to catch
2458 item->u2.statements == pval list of statements in context body
2460 check_pval(item
->u2
.statements
, apps
,in_globals
);
2464 /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
2466 check_pval(item
->u1
.list
, apps
,in_globals
);
2470 /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
2472 check_pval(item
->u1
.list
, apps
,in_globals
);
2476 /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
2478 check_pval(item
->u1
.list
, apps
,in_globals
);
2479 check_includes(item
);
2480 for (lp
=item
->u1
.list
; lp
; lp
=lp
->next
){
2481 char *incl_context
= lp
->u1
.str
;
2482 struct pval
*that_context
= find_context(incl_context
);
2484 if ( lp
->u2
.arglist
) {
2485 check_timerange(lp
->u2
.arglist
);
2486 check_dow(lp
->u2
.arglist
->next
);
2487 check_day(lp
->u2
.arglist
->next
->next
);
2488 check_month(lp
->u2
.arglist
->next
->next
->next
);
2492 find_pval_gotos(that_context
->u2
.statements
,0);
2498 case PV_STATEMENTBLOCK
:
2499 /* fields: item->u1.list == pval list of statements in block, one per entry in the list
2501 check_pval(item
->u1
.list
, apps
,in_globals
);
2505 /* fields: item->u1.str == variable name
2506 item->u2.val == variable value to assign
2508 /* the RHS of a vardec is encapsulated in a $[] expr. Is it legal? */
2509 if( !in_globals
) { /* don't check stuff inside the globals context; no wrapping in $[ ] there... */
2510 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
);
2511 ast_expr_register_extra_error_info(errmsg
);
2512 ast_expr(item
->u2
.val
, expr_output
, sizeof(expr_output
));
2513 ast_expr_clear_extra_error_info();
2514 if ( strpbrk(item
->u2
.val
,"~!-+<>=*/&^") && !strstr(item
->u2
.val
,"${") ) {
2515 ast_log(LOG_WARNING
,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n",
2516 item
->filename
, item
->startline
, item
->endline
, item
->u2
.val
);
2519 check_expr2_input(item
,item
->u2
.val
);
2524 /* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user.
2525 item->u1.list->u1.str == where the data on a PV_WORD will always be.
2527 /* don't check goto's in abstract contexts */
2528 if ( in_abstract_context
)
2535 /* fields: item->u1.str == label name
2537 if ( strspn(item
->u1
.str
, "0123456789") == strlen(item
->u1
.str
) ) {
2538 ast_log(LOG_WARNING
,"Warning: file %s, line %d-%d: label '%s' is numeric, this is bad practice!\n",
2539 item
->filename
, item
->startline
, item
->endline
, item
->u1
.str
);
2547 /* fields: item->u1.for_init == a string containing the initalizer
2548 item->u2.for_test == a string containing the loop test
2549 item->u3.for_inc == a string containing the loop increment
2551 item->u4.for_statements == a pval list of statements in the for ()
2553 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
);
2554 ast_expr_register_extra_error_info(errmsg
);
2556 strp
= strchr(item
->u1
.for_init
, '=');
2558 ast_expr(strp
+1, expr_output
, sizeof(expr_output
));
2560 ast_expr(item
->u2
.for_test
, expr_output
, sizeof(expr_output
));
2561 strp
= strchr(item
->u3
.for_inc
, '=');
2563 ast_expr(strp
+1, expr_output
, sizeof(expr_output
));
2565 if ( strpbrk(item
->u2
.for_test
,"~!-+<>=*/&^") && !strstr(item
->u2
.for_test
,"${") ) {
2566 ast_log(LOG_WARNING
,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n",
2567 item
->filename
, item
->startline
, item
->endline
, item
->u2
.for_test
);
2570 if ( strpbrk(item
->u3
.for_inc
,"~!-+<>=*/&^") && !strstr(item
->u3
.for_inc
,"${") ) {
2571 ast_log(LOG_WARNING
,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n",
2572 item
->filename
, item
->startline
, item
->endline
, item
->u3
.for_inc
);
2575 check_expr2_input(item
,item
->u2
.for_test
);
2576 check_expr2_input(item
,item
->u3
.for_inc
);
2578 ast_expr_clear_extra_error_info();
2579 check_pval(item
->u4
.for_statements
, apps
,in_globals
);
2583 /* fields: item->u1.str == the while conditional, as supplied by user
2585 item->u2.statements == a pval list of statements in the while ()
2587 snprintf(errmsg
,sizeof(errmsg
),"file %s, line %d, columns %d-%d, while expr '%s':", config
, item
->startline
, item
->startcol
, item
->endcol
, item
->u1
.str
);
2588 ast_expr_register_extra_error_info(errmsg
);
2589 ast_expr(item
->u1
.str
, expr_output
, sizeof(expr_output
));
2590 ast_expr_clear_extra_error_info();
2591 if ( strpbrk(item
->u1
.str
,"~!-+<>=*/&^") && !strstr(item
->u1
.str
,"${") ) {
2592 ast_log(LOG_WARNING
,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n",
2593 item
->filename
, item
->startline
, item
->endline
, item
->u1
.str
);
2596 check_expr2_input(item
,item
->u1
.str
);
2597 check_pval(item
->u2
.statements
, apps
,in_globals
);
2614 check_continue(item
);
2618 /* fields: item->u1.str == the random number expression, as supplied by user
2620 item->u2.statements == a pval list of statements in the if ()
2621 item->u3.else_statements == a pval list of statements in the else
2624 snprintf(errmsg
,sizeof(errmsg
),"file %s, line %d, columns %d-%d, random expr '%s':", config
, item
->startline
, item
->startcol
, item
->endcol
, item
->u1
.str
);
2625 ast_expr_register_extra_error_info(errmsg
);
2626 ast_expr(item
->u1
.str
, expr_output
, sizeof(expr_output
));
2627 ast_expr_clear_extra_error_info();
2628 if ( strpbrk(item
->u1
.str
,"~!-+<>=*/&^") && !strstr(item
->u1
.str
,"${") ) {
2629 ast_log(LOG_WARNING
,"Warning: file %s, line %d-%d: random expression '%s' has operators, but no variables. Interesting...\n",
2630 item
->filename
, item
->startline
, item
->endline
, item
->u1
.str
);
2633 check_expr2_input(item
,item
->u1
.str
);
2634 check_pval(item
->u2
.statements
, apps
,in_globals
);
2635 if (item
->u3
.else_statements
) {
2636 check_pval(item
->u3
.else_statements
, apps
,in_globals
);
2641 /* fields: item->u1.list == the if time values, 4 of them, each in PV_WORD, linked list
2643 item->u2.statements == a pval list of statements in the if ()
2644 item->u3.else_statements == a pval list of statements in the else
2647 if ( item
->u2
.arglist
) {
2648 check_timerange(item
->u1
.list
);
2649 check_dow(item
->u1
.list
->next
);
2650 check_day(item
->u1
.list
->next
->next
);
2651 check_month(item
->u1
.list
->next
->next
->next
);
2654 check_pval(item
->u2
.statements
, apps
,in_globals
);
2655 if (item
->u3
.else_statements
) {
2656 check_pval(item
->u3
.else_statements
, apps
,in_globals
);
2661 /* fields: item->u1.str == the if conditional, as supplied by user
2663 item->u2.statements == a pval list of statements in the if ()
2664 item->u3.else_statements == a pval list of statements in the else
2667 snprintf(errmsg
,sizeof(errmsg
),"file %s, line %d, columns %d-%d, if expr '%s':", config
, item
->startline
, item
->startcol
, item
->endcol
, item
->u1
.str
);
2668 ast_expr_register_extra_error_info(errmsg
);
2669 ast_expr(item
->u1
.str
, expr_output
, sizeof(expr_output
));
2670 ast_expr_clear_extra_error_info();
2671 if ( strpbrk(item
->u1
.str
,"~!-+<>=*/&^") && !strstr(item
->u1
.str
,"${") ) {
2672 ast_log(LOG_WARNING
,"Warning: file %s, line %d-%d: expression '%s' has operators, but no variables. Interesting...\n",
2673 item
->filename
, item
->startline
, item
->endline
, item
->u1
.str
);
2676 check_expr2_input(item
,item
->u1
.str
);
2677 check_pval(item
->u2
.statements
, apps
,in_globals
);
2678 if (item
->u3
.else_statements
) {
2679 check_pval(item
->u3
.else_statements
, apps
,in_globals
);
2684 /* fields: item->u1.str == the switch expression
2686 item->u2.statements == a pval list of statements in the switch,
2687 (will be case statements, most likely!)
2689 /* we can check the switch expression, see if it matches any of the app variables...
2690 if it does, then, are all the possible cases accounted for? */
2691 check_switch_expr(item
, apps
);
2692 check_pval(item
->u2
.statements
, apps
,in_globals
);
2696 /* fields: item->u1.str == the extension name, label, whatever it's called
2698 item->u2.statements == a pval list of statements in the extension
2699 item->u3.hints == a char * hint argument
2700 item->u4.regexten == an int boolean. non-zero says that regexten was specified
2702 current_extension
= item
;
2704 check_pval(item
->u2
.statements
, apps
,in_globals
);
2708 /* fields: item->u1.str == the ignorepat data
2713 /* fields: item->u1.statements == pval list of statements, usually vardecs
2715 in_abstract_context
= 0;
2716 check_pval(item
->u1
.statements
, apps
, 1);
2723 void check_pval(pval
*item
, struct argapp
*apps
, int in_globals
)
2728 1. Do goto's point to actual labels?
2729 2. Do macro calls reference a macro?
2730 3. Does the number of macro args match the definition?
2731 4. Is a macro call missing its & at the front?
2732 5. Application calls-- we could check syntax for existing applications,
2733 but I need some some sort of universal description bnf for a general
2734 sort of method for checking arguments, in number, maybe even type, at least.
2735 Don't want to hand code checks for hundreds of applications.
2738 for (i
=item
; i
; i
=i
->next
) {
2739 check_pval_item(i
,apps
,in_globals
);
2743 static void ael2_semantic_check(pval
*item
, int *arg_errs
, int *arg_warns
, int *arg_notes
)
2750 struct argapp
*apps
=0;
2753 return; /* don't check an empty tree */
2755 rfilename
= alloca(10 + strlen(ast_config_AST_VAR_DIR
));
2756 sprintf(rfilename
, "%s/applist", ast_config_AST_VAR_DIR
);
2758 apps
= argdesc_parse(rfilename
, &argapp_errs
); /* giveth */
2761 errs
= warns
= notes
= 0;
2763 check_context_names();
2764 check_pval(item
, apps
, 0);
2767 argdesc_destroy(apps
); /* taketh away */
2776 /* =============================================================================================== */
2777 /* "CODE" GENERATOR -- Convert the AEL representation to asterisk extension language */
2778 /* =============================================================================================== */
2780 static int control_statement_count
= 0;
2782 struct ael_priority
*new_prio(void)
2784 struct ael_priority
*x
= (struct ael_priority
*)calloc(sizeof(struct ael_priority
),1);
2788 struct ael_extension
*new_exten(void)
2790 struct ael_extension
*x
= (struct ael_extension
*)calloc(sizeof(struct ael_extension
),1);
2794 void linkprio(struct ael_extension
*exten
, struct ael_priority
*prio
, struct ael_extension
*mother_exten
)
2798 if (!exten
->plist
) {
2799 exten
->plist
= prio
;
2800 exten
->plist_last
= prio
;
2802 exten
->plist_last
->next
= prio
;
2803 exten
->plist_last
= prio
;
2806 prio
->exten
= exten
; /* don't override the switch value */
2807 /* The following code will cause all priorities within an extension
2808 to have ${EXTEN} or ${EXTEN: replaced with ~~EXTEN~~, which is
2809 set just before the first switch in an exten. The switches
2810 will muck up the original ${EXTEN} value, so we save it away
2811 and the user accesses this copy instead. */
2812 if (prio
->appargs
&& ((mother_exten
&& mother_exten
->has_switch
) || exten
->has_switch
) ) {
2813 while ((p1
= strstr(prio
->appargs
, "${EXTEN}"))) {
2814 p2
= malloc(strlen(prio
->appargs
)+5);
2816 strcpy(p2
, prio
->appargs
);
2817 strcat(p2
, "${~~EXTEN~~}");
2820 free(prio
->appargs
);
2823 while ((p1
= strstr(prio
->appargs
, "${EXTEN:"))) {
2824 p2
= malloc(strlen(prio
->appargs
)+5);
2826 strcpy(p2
, prio
->appargs
);
2827 strcat(p2
, "${~~EXTEN~~:");
2830 free(prio
->appargs
);
2836 void destroy_extensions(struct ael_extension
*exten
)
2838 struct ael_extension
*ne
, *nen
;
2839 for (ne
=exten
; ne
; ne
=nen
) {
2840 struct ael_priority
*pe
, *pen
;
2845 /* cidmatch fields are allocated with name, and freed when
2846 the name field is freed. Don't do a free for this field,
2847 unless you LIKE to see a crash! */
2852 for (pe
=ne
->plist
; pe
; pe
=pen
) {
2865 nen
= ne
->next_exten
;
2871 ne
->loop_continue
= 0;
2876 static int label_inside_case(pval
*label
)
2880 while( p
&& p
->type
!= PV_MACRO
&& p
->type
!= PV_CONTEXT
) /* early cutout, sort of */ {
2881 if( p
->type
== PV_CASE
|| p
->type
== PV_DEFAULT
|| p
->type
== PV_PATTERN
) {
2890 static void linkexten(struct ael_extension
*exten
, struct ael_extension
*add
)
2892 add
->next_exten
= exten
->next_exten
; /* this will reverse the order. Big deal. */
2893 exten
->next_exten
= add
;
2896 static void remove_spaces_before_equals(char *str
)
2899 while( str
&& *str
&& *str
!= '=' )
2901 if( *str
== ' ' || *str
== '\n' || *str
== '\r' || *str
== '\t' )
2915 static void gen_match_to_pattern(char *pattern
, char *result
)
2917 /* the result will be a string that will be matched by pattern */
2918 char *p
=pattern
, *t
=result
;
2920 if (*p
== 'x' || *p
== 'n' || *p
== 'z' || *p
== 'X' || *p
== 'N' || *p
== 'Z')
2922 else if (*p
== '[') {
2928 *t
++=*(p
+1); /* use the first char in the set */
2935 *t
++ = 0; /* cap it off */
2938 /* ==== a set of routines to search for a switch statement contained in the pval description */
2940 int find_switch_item(pval
*item
);
2941 int contains_switch(pval
*item
);
2944 int find_switch_item(pval
*item
)
2946 switch ( item
->type
) {
2948 /* fields: item->u1.str == string associated with this (word). */
2952 /* fields: item->u1.str == name of macro
2953 item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
2954 item->u2.arglist->u1.str == argument
2955 item->u2.arglist->next == next arg
2957 item->u3.macro_statements == pval list of statements in macro body.
2959 /* had better not see this */
2960 if (contains_switch(item
->u3
.macro_statements
))
2965 /* fields: item->u1.str == name of context
2966 item->u2.statements == pval list of statements in context body
2967 item->u3.abstract == int 1 if an abstract keyword were present
2969 /* had better not see this */
2970 if (contains_switch(item
->u2
.statements
))
2975 /* fields: item->u1.str == name of macro to call
2976 item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
2977 item->u2.arglist->u1.str == argument
2978 item->u2.arglist->next == next arg
2982 case PV_APPLICATION_CALL
:
2983 /* fields: item->u1.str == name of application to call
2984 item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
2985 item->u2.arglist->u1.str == argument
2986 item->u2.arglist->next == next arg
2991 /* fields: item->u1.str == value of case
2992 item->u2.statements == pval list of statements under the case
2994 /* had better not see this */
2995 if (contains_switch(item
->u2
.statements
))
3000 /* fields: item->u1.str == value of case
3001 item->u2.statements == pval list of statements under the case
3003 /* had better not see this */
3004 if (contains_switch(item
->u2
.statements
))
3010 item->u2.statements == pval list of statements under the case
3012 /* had better not see this */
3013 if (contains_switch(item
->u2
.statements
))
3018 /* fields: item->u1.str == name of extension to catch
3019 item->u2.statements == pval list of statements in context body
3021 /* had better not see this */
3022 if (contains_switch(item
->u2
.statements
))
3027 /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
3032 /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
3037 /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
3038 item->u2.arglist == pval list of 4 PV_WORD elements for time values
3042 case PV_STATEMENTBLOCK
:
3043 /* fields: item->u1.list == pval list of statements in block, one per entry in the list
3045 if (contains_switch(item
->u1
.list
) )
3050 /* fields: item->u1.str == variable name
3051 item->u2.val == variable value to assign
3056 /* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user.
3057 item->u1.list->u1.str == where the data on a PV_WORD will always be.
3062 /* fields: item->u1.str == label name
3067 /* fields: item->u1.for_init == a string containing the initalizer
3068 item->u2.for_test == a string containing the loop test
3069 item->u3.for_inc == a string containing the loop increment
3071 item->u4.for_statements == a pval list of statements in the for ()
3073 if (contains_switch(item
->u4
.for_statements
))
3078 /* fields: item->u1.str == the while conditional, as supplied by user
3080 item->u2.statements == a pval list of statements in the while ()
3082 if (contains_switch(item
->u2
.statements
))
3102 /* fields: item->u1.list == there are 4 linked PV_WORDs here.
3104 item->u2.statements == a pval list of statements in the if ()
3105 item->u3.else_statements == a pval list of statements in the else
3108 if (contains_switch(item
->u2
.statements
))
3110 if ( item
->u3
.else_statements
) {
3111 if (contains_switch(item
->u3
.else_statements
))
3117 /* fields: item->u1.str == the random number expression, as supplied by user
3119 item->u2.statements == a pval list of statements in the if ()
3120 item->u3.else_statements == a pval list of statements in the else
3123 if (contains_switch(item
->u2
.statements
))
3125 if ( item
->u3
.else_statements
) {
3126 if (contains_switch(item
->u3
.else_statements
))
3132 /* fields: item->u1.str == the if conditional, as supplied by user
3134 item->u2.statements == a pval list of statements in the if ()
3135 item->u3.else_statements == a pval list of statements in the else
3138 if (contains_switch(item
->u2
.statements
))
3140 if ( item
->u3
.else_statements
) {
3141 if (contains_switch(item
->u3
.else_statements
))
3147 /* fields: item->u1.str == the switch expression
3149 item->u2.statements == a pval list of statements in the switch,
3150 (will be case statements, most likely!)
3152 return 1; /* JACKPOT */
3156 /* fields: item->u1.str == the extension name, label, whatever it's called
3158 item->u2.statements == a pval list of statements in the extension
3159 item->u3.hints == a char * hint argument
3160 item->u4.regexten == an int boolean. non-zero says that regexten was specified
3162 if (contains_switch(item
->u2
.statements
))
3167 /* fields: item->u1.str == the ignorepat data
3172 /* fields: item->u1.statements == pval list of statements, usually vardecs
3179 int contains_switch(pval
*item
)
3183 for (i
=item
; i
; i
=i
->next
) {
3184 if (find_switch_item(i
))
3191 static void gen_prios(struct ael_extension
*exten
, char *label
, pval
*statement
, struct ael_extension
*mother_exten
, struct ast_context
*this_context
)
3194 struct ael_priority
*pr
;
3195 struct ael_priority
*for_init
, *for_test
, *for_inc
, *for_loop
, *for_end
;
3196 struct ael_priority
*while_test
, *while_loop
, *while_end
;
3197 struct ael_priority
*switch_set
, *switch_test
, *switch_end
, *fall_thru
, *switch_empty
;
3198 struct ael_priority
*if_test
, *if_end
, *if_skip
, *if_false
;
3199 #ifdef OLD_RAND_ACTION
3200 struct ael_priority
*rand_test
, *rand_end
, *rand_skip
;
3205 char new_label
[2000];
3207 int local_control_statement_count
;
3208 struct ael_priority
*loop_break_save
;
3209 struct ael_priority
*loop_continue_save
;
3210 struct ael_extension
*switch_case
,*switch_null
;
3212 if ((mother_exten
&& !mother_exten
->checked_switch
) || (exten
&& !exten
->checked_switch
)) {
3213 if (contains_switch(statement
)) { /* only run contains_switch if you haven't checked before */
3215 if (!mother_exten
->has_switch
) {
3216 switch_set
= new_prio();
3217 switch_set
->type
= AEL_APPCALL
;
3218 switch_set
->app
= strdup("Set");
3219 switch_set
->appargs
= strdup("~~EXTEN~~=${EXTEN}");
3220 linkprio(exten
, switch_set
, mother_exten
);
3221 mother_exten
->has_switch
= 1;
3222 mother_exten
->checked_switch
= 1;
3224 exten
->has_switch
= 1;
3225 exten
->checked_switch
= 1;
3229 if (!exten
->has_switch
) {
3230 switch_set
= new_prio();
3231 switch_set
->type
= AEL_APPCALL
;
3232 switch_set
->app
= strdup("Set");
3233 switch_set
->appargs
= strdup("~~EXTEN~~=${EXTEN}");
3234 linkprio(exten
, switch_set
, mother_exten
);
3235 exten
->has_switch
= 1;
3236 exten
->checked_switch
= 1;
3238 mother_exten
->has_switch
= 1;
3239 mother_exten
->checked_switch
= 1;
3245 mother_exten
->checked_switch
= 1;
3248 exten
->checked_switch
= 1;
3252 for (p
=statement
; p
; p
=p
->next
) {
3256 pr
->type
= AEL_APPCALL
;
3257 snprintf(buf1
,sizeof(buf1
),"%s=$[%s]", p
->u1
.str
, p
->u2
.val
);
3258 pr
->app
= strdup("Set");
3259 remove_spaces_before_equals(buf1
);
3260 pr
->appargs
= strdup(buf1
);
3262 linkprio(exten
, pr
, mother_exten
);
3267 pr
->type
= AEL_APPCALL
;
3268 p
->u2
.goto_target
= get_goto_target(p
);
3269 if( p
->u2
.goto_target
) {
3270 p
->u3
.goto_target_in_case
= p
->u2
.goto_target
->u2
.label_in_case
= label_inside_case(p
->u2
.goto_target
);
3273 if (!p
->u1
.list
->next
) /* just one */ {
3274 pr
->app
= strdup("Goto");
3276 pr
->appargs
= strdup(p
->u1
.list
->u1
.str
);
3277 else { /* for the case of simple within-extension gotos in case/pattern/default statement blocks: */
3278 snprintf(buf1
,sizeof(buf1
),"%s|%s", mother_exten
->name
, p
->u1
.list
->u1
.str
);
3279 pr
->appargs
= strdup(buf1
);
3282 } else if (p
->u1
.list
->next
&& !p
->u1
.list
->next
->next
) /* two */ {
3283 snprintf(buf1
,sizeof(buf1
),"%s|%s", p
->u1
.list
->u1
.str
, p
->u1
.list
->next
->u1
.str
);
3284 pr
->app
= strdup("Goto");
3285 pr
->appargs
= strdup(buf1
);
3286 } else if (p
->u1
.list
->next
&& p
->u1
.list
->next
->next
) {
3287 snprintf(buf1
,sizeof(buf1
),"%s|%s|%s", p
->u1
.list
->u1
.str
,
3288 p
->u1
.list
->next
->u1
.str
,
3289 p
->u1
.list
->next
->next
->u1
.str
);
3290 pr
->app
= strdup("Goto");
3291 pr
->appargs
= strdup(buf1
);
3294 linkprio(exten
, pr
, mother_exten
);
3299 pr
->type
= AEL_LABEL
;
3301 p
->u3
.compiled_label
= exten
;
3302 linkprio(exten
, pr
, mother_exten
);
3306 control_statement_count
++;
3307 loop_break_save
= exten
->loop_break
; /* save them, then restore before leaving */
3308 loop_continue_save
= exten
->loop_continue
;
3309 snprintf(new_label
,sizeof(new_label
),"for-%s-%d", label
, control_statement_count
);
3310 for_init
= new_prio();
3311 for_inc
= new_prio();
3312 for_test
= new_prio();
3313 for_loop
= new_prio();
3314 for_end
= new_prio();
3315 for_init
->type
= AEL_APPCALL
;
3316 for_inc
->type
= AEL_APPCALL
;
3317 for_test
->type
= AEL_FOR_CONTROL
;
3318 for_test
->goto_false
= for_end
;
3319 for_loop
->type
= AEL_CONTROL1
; /* simple goto */
3320 for_end
->type
= AEL_APPCALL
;
3321 for_init
->app
= strdup("Set");
3323 strcpy(buf2
,p
->u1
.for_init
);
3324 remove_spaces_before_equals(buf2
);
3325 strp
= strchr(buf2
, '=');
3327 strp2
= strchr(p
->u1
.for_init
, '=');
3330 strncat(buf2
,strp2
+1, sizeof(buf2
)-strlen(strp2
+1)-2);
3332 for_init
->appargs
= strdup(buf2
);
3333 /* for_init->app = strdup("Set"); just set! */
3335 strp2
= p
->u1
.for_init
;
3336 while (*strp2
&& isspace(*strp2
))
3338 if (*strp2
== '&') { /* itsa macro call */
3339 char *strp3
= strp2
+1;
3340 while (*strp3
&& isspace(*strp3
))
3342 strcpy(buf2
, strp3
);
3343 strp3
= strchr(buf2
,'(');
3347 while ((strp3
=strchr(buf2
,','))) {
3350 strp3
= strrchr(buf2
, ')');
3352 *strp3
= 0; /* remove the closing paren */
3354 for_init
->appargs
= strdup(buf2
);
3356 free(for_init
->app
);
3357 for_init
->app
= strdup("Macro");
3358 } else { /* must be a regular app call */
3360 strcpy(buf2
, strp2
);
3361 strp3
= strchr(buf2
,'(');
3365 free(for_init
->app
);
3366 for_init
->app
= strdup(buf2
);
3367 for_init
->appargs
= strdup(strp3
+1);
3368 strp3
= strrchr(for_init
->appargs
, ')');
3370 *strp3
= 0; /* remove the closing paren */
3375 strcpy(buf2
,p
->u3
.for_inc
);
3376 remove_spaces_before_equals(buf2
);
3377 strp
= strchr(buf2
, '=');
3378 if (strp
) { /* there's an = in this part; that means an assignment. set it up */
3379 strp2
= strchr(p
->u3
.for_inc
, '=');
3382 strncat(buf2
,strp2
+1, sizeof(buf2
)-strlen(strp2
+1)-2);
3384 for_inc
->appargs
= strdup(buf2
);
3385 for_inc
->app
= strdup("Set");
3387 strp2
= p
->u3
.for_inc
;
3388 while (*strp2
&& isspace(*strp2
))
3390 if (*strp2
== '&') { /* itsa macro call */
3391 char *strp3
= strp2
+1;
3392 while (*strp3
&& isspace(*strp3
))
3394 strcpy(buf2
, strp3
);
3395 strp3
= strchr(buf2
,'(');
3399 while ((strp3
=strchr(buf2
,','))) {
3402 strp3
= strrchr(buf2
, ')');
3404 *strp3
= 0; /* remove the closing paren */
3406 for_inc
->appargs
= strdup(buf2
);
3408 for_inc
->app
= strdup("Macro");
3409 } else { /* must be a regular app call */
3411 strcpy(buf2
, strp2
);
3412 strp3
= strchr(buf2
,'(');
3415 for_inc
->app
= strdup(buf2
);
3416 for_inc
->appargs
= strdup(strp3
+1);
3417 strp3
= strrchr(for_inc
->appargs
, ')');
3419 *strp3
= 0; /* remove the closing paren */
3423 snprintf(buf1
,sizeof(buf1
),"$[%s]",p
->u2
.for_test
);
3425 for_test
->appargs
= strdup(buf1
);
3426 for_loop
->goto_true
= for_test
;
3427 snprintf(buf1
,sizeof(buf1
),"Finish for-%s-%d", label
, control_statement_count
);
3428 for_end
->app
= strdup("NoOp");
3429 for_end
->appargs
= strdup(buf1
);
3431 linkprio(exten
, for_init
, mother_exten
);
3432 linkprio(exten
, for_test
, mother_exten
);
3434 /* now, put the body of the for loop here */
3435 exten
->loop_break
= for_end
;
3436 exten
->loop_continue
= for_inc
;
3438 gen_prios(exten
, new_label
, p
->u4
.for_statements
, mother_exten
, this_context
); /* this will link in all the statements here */
3440 linkprio(exten
, for_inc
, mother_exten
);
3441 linkprio(exten
, for_loop
, mother_exten
);
3442 linkprio(exten
, for_end
, mother_exten
);
3445 exten
->loop_break
= loop_break_save
;
3446 exten
->loop_continue
= loop_continue_save
;
3447 for_loop
->origin
= p
;
3451 control_statement_count
++;
3452 loop_break_save
= exten
->loop_break
; /* save them, then restore before leaving */
3453 loop_continue_save
= exten
->loop_continue
;
3454 snprintf(new_label
,sizeof(new_label
),"while-%s-%d", label
, control_statement_count
);
3455 while_test
= new_prio();
3456 while_loop
= new_prio();
3457 while_end
= new_prio();
3458 while_test
->type
= AEL_FOR_CONTROL
;
3459 while_test
->goto_false
= while_end
;
3460 while_loop
->type
= AEL_CONTROL1
; /* simple goto */
3461 while_end
->type
= AEL_APPCALL
;
3462 snprintf(buf1
,sizeof(buf1
),"$[%s]",p
->u1
.str
);
3463 while_test
->app
= 0;
3464 while_test
->appargs
= strdup(buf1
);
3465 while_loop
->goto_true
= while_test
;
3466 snprintf(buf1
,sizeof(buf1
),"Finish while-%s-%d", label
, control_statement_count
);
3467 while_end
->app
= strdup("NoOp");
3468 while_end
->appargs
= strdup(buf1
);
3470 linkprio(exten
, while_test
, mother_exten
);
3472 /* now, put the body of the for loop here */
3473 exten
->loop_break
= while_end
;
3474 exten
->loop_continue
= while_test
;
3476 gen_prios(exten
, new_label
, p
->u2
.statements
, mother_exten
, this_context
); /* this will link in all the while body statements here */
3478 linkprio(exten
, while_loop
, mother_exten
);
3479 linkprio(exten
, while_end
, mother_exten
);
3482 exten
->loop_break
= loop_break_save
;
3483 exten
->loop_continue
= loop_continue_save
;
3484 while_loop
->origin
= p
;
3488 control_statement_count
++;
3489 local_control_statement_count
= control_statement_count
;
3490 loop_break_save
= exten
->loop_break
; /* save them, then restore before leaving */
3491 loop_continue_save
= exten
->loop_continue
;
3492 snprintf(new_label
,sizeof(new_label
),"sw-%s-%d", label
, control_statement_count
);
3493 switch_test
= new_prio();
3494 switch_end
= new_prio();
3495 switch_test
->type
= AEL_APPCALL
;
3496 switch_end
->type
= AEL_APPCALL
;
3497 strncpy(buf2
,p
->u1
.str
,sizeof(buf2
));
3498 buf2
[sizeof(buf2
)-1] = 0; /* just in case */
3499 substitute_commas(buf2
);
3500 snprintf(buf1
,sizeof(buf1
),"sw-%d-%s|10",control_statement_count
, buf2
);
3501 switch_test
->app
= strdup("Goto");
3502 switch_test
->appargs
= strdup(buf1
);
3503 snprintf(buf1
,sizeof(buf1
),"Finish switch-%s-%d", label
, control_statement_count
);
3504 switch_end
->app
= strdup("NoOp");
3505 switch_end
->appargs
= strdup(buf1
);
3506 switch_end
->origin
= p
;
3507 switch_end
->exten
= exten
;
3509 linkprio(exten
, switch_test
, mother_exten
);
3510 linkprio(exten
, switch_end
, mother_exten
);
3512 exten
->loop_break
= switch_end
;
3513 exten
->loop_continue
= 0;
3516 for (p2
=p
->u2
.statements
; p2
; p2
=p2
->next
) {
3517 /* now, for each case/default put the body of the for loop here */
3518 if (p2
->type
== PV_CASE
) {
3519 /* ok, generate a extension and link it in */
3520 switch_case
= new_exten();
3521 if (mother_exten
&& mother_exten
->checked_switch
) {
3522 switch_case
->has_switch
= mother_exten
->has_switch
;
3523 switch_case
->checked_switch
= mother_exten
->checked_switch
;
3525 if (exten
&& exten
->checked_switch
) {
3526 switch_case
->has_switch
= exten
->has_switch
;
3527 switch_case
->checked_switch
= exten
->checked_switch
;
3529 switch_case
->context
= this_context
;
3530 switch_case
->is_switch
= 1;
3531 /* the break/continue locations are inherited from parent */
3532 switch_case
->loop_break
= exten
->loop_break
;
3533 switch_case
->loop_continue
= exten
->loop_continue
;
3535 linkexten(exten
,switch_case
);
3536 strncpy(buf2
,p2
->u1
.str
,sizeof(buf2
));
3537 buf2
[sizeof(buf2
)-1] = 0; /* just in case */
3538 substitute_commas(buf2
);
3539 snprintf(buf1
,sizeof(buf1
),"sw-%d-%s", local_control_statement_count
, buf2
);
3540 switch_case
->name
= strdup(buf1
);
3541 snprintf(new_label
,sizeof(new_label
),"sw-%s-%s-%d", label
, buf2
, local_control_statement_count
);
3543 gen_prios(switch_case
, new_label
, p2
->u2
.statements
, exten
, this_context
); /* this will link in all the case body statements here */
3545 /* here is where we write code to "fall thru" to the next case... if there is one... */
3546 for (p3
=p2
->u2
.statements
; p3
; p3
=p3
->next
) {
3550 /* p3 now points the last statement... */
3551 if (!p3
|| ( p3
->type
!= PV_GOTO
&& p3
->type
!= PV_BREAK
&& p3
->type
!= PV_RETURN
) ) {
3552 /* is there a following CASE/PATTERN/DEFAULT? */
3553 if (p2
->next
&& p2
->next
->type
== PV_CASE
) {
3554 fall_thru
= new_prio();
3555 fall_thru
->type
= AEL_APPCALL
;
3556 fall_thru
->app
= strdup("Goto");
3557 strncpy(buf2
,p2
->next
->u1
.str
,sizeof(buf2
));
3558 buf2
[sizeof(buf2
)-1] = 0; /* just in case */
3559 substitute_commas(buf2
);
3560 snprintf(buf1
,sizeof(buf1
),"sw-%d-%s|10",local_control_statement_count
, buf2
);
3561 fall_thru
->appargs
= strdup(buf1
);
3562 linkprio(switch_case
, fall_thru
, mother_exten
);
3563 } else if (p2
->next
&& p2
->next
->type
== PV_PATTERN
) {
3564 fall_thru
= new_prio();
3565 fall_thru
->type
= AEL_APPCALL
;
3566 fall_thru
->app
= strdup("Goto");
3567 gen_match_to_pattern(p2
->next
->u1
.str
, buf2
);
3568 substitute_commas(buf2
);
3569 snprintf(buf1
,sizeof(buf1
),"sw-%d-%s|10", local_control_statement_count
, buf2
);
3570 fall_thru
->appargs
= strdup(buf1
);
3571 linkprio(switch_case
, fall_thru
, mother_exten
);
3572 } else if (p2
->next
&& p2
->next
->type
== PV_DEFAULT
) {
3573 fall_thru
= new_prio();
3574 fall_thru
->type
= AEL_APPCALL
;
3575 fall_thru
->app
= strdup("Goto");
3576 snprintf(buf1
,sizeof(buf1
),"sw-%d-.|10",local_control_statement_count
);
3577 fall_thru
->appargs
= strdup(buf1
);
3578 linkprio(switch_case
, fall_thru
, mother_exten
);
3579 } else if (!p2
->next
) {
3580 fall_thru
= new_prio();
3581 fall_thru
->type
= AEL_CONTROL1
;
3582 fall_thru
->goto_true
= switch_end
;
3583 fall_thru
->app
= strdup("Goto");
3584 linkprio(switch_case
, fall_thru
, mother_exten
);
3587 if (switch_case
->return_needed
) {
3589 struct ael_priority
*np2
= new_prio();
3590 np2
->type
= AEL_APPCALL
;
3591 np2
->app
= strdup("NoOp");
3592 snprintf(buf
,sizeof(buf
),"End of Extension %s", switch_case
->name
);
3593 np2
->appargs
= strdup(buf
);
3594 linkprio(switch_case
, np2
, mother_exten
);
3595 switch_case
-> return_target
= np2
;
3597 } else if (p2
->type
== PV_PATTERN
) {
3598 /* ok, generate a extension and link it in */
3599 switch_case
= new_exten();
3600 if (mother_exten
&& mother_exten
->checked_switch
) {
3601 switch_case
->has_switch
= mother_exten
->has_switch
;
3602 switch_case
->checked_switch
= mother_exten
->checked_switch
;
3604 if (exten
&& exten
->checked_switch
) {
3605 switch_case
->has_switch
= exten
->has_switch
;
3606 switch_case
->checked_switch
= exten
->checked_switch
;
3608 switch_case
->context
= this_context
;
3609 switch_case
->is_switch
= 1;
3610 /* the break/continue locations are inherited from parent */
3611 switch_case
->loop_break
= exten
->loop_break
;
3612 switch_case
->loop_continue
= exten
->loop_continue
;
3614 linkexten(exten
,switch_case
);
3615 strncpy(buf2
,p2
->u1
.str
,sizeof(buf2
));
3616 buf2
[sizeof(buf2
)-1] = 0; /* just in case */
3617 substitute_commas(buf2
);
3618 snprintf(buf1
,sizeof(buf1
),"_sw-%d-%s", local_control_statement_count
, buf2
);
3619 switch_case
->name
= strdup(buf1
);
3620 snprintf(new_label
,sizeof(new_label
),"sw-%s-%s-%d", label
, buf2
, local_control_statement_count
);
3622 gen_prios(switch_case
, new_label
, p2
->u2
.statements
, exten
, this_context
); /* this will link in all the while body statements here */
3623 /* here is where we write code to "fall thru" to the next case... if there is one... */
3624 for (p3
=p2
->u2
.statements
; p3
; p3
=p3
->next
) {
3628 /* p3 now points the last statement... */
3629 if (!p3
|| ( p3
->type
!= PV_GOTO
&& p3
->type
!= PV_BREAK
&& p3
->type
!= PV_RETURN
)) {
3630 /* is there a following CASE/PATTERN/DEFAULT? */
3631 if (p2
->next
&& p2
->next
->type
== PV_CASE
) {
3632 fall_thru
= new_prio();
3633 fall_thru
->type
= AEL_APPCALL
;
3634 fall_thru
->app
= strdup("Goto");
3635 strncpy(buf2
,p2
->next
->u1
.str
,sizeof(buf2
));
3636 buf2
[sizeof(buf2
)-1] = 0; /* just in case */
3637 substitute_commas(buf2
);
3638 snprintf(buf1
,sizeof(buf1
),"sw-%d-%s|10",local_control_statement_count
, buf2
);
3639 fall_thru
->appargs
= strdup(buf1
);
3640 linkprio(switch_case
, fall_thru
, mother_exten
);
3641 } else if (p2
->next
&& p2
->next
->type
== PV_PATTERN
) {
3642 fall_thru
= new_prio();
3643 fall_thru
->type
= AEL_APPCALL
;
3644 fall_thru
->app
= strdup("Goto");
3645 gen_match_to_pattern(p2
->next
->u1
.str
, buf2
);
3646 substitute_commas(buf2
);
3647 snprintf(buf1
,sizeof(buf1
),"sw-%d-%s|10",local_control_statement_count
, buf2
);
3648 fall_thru
->appargs
= strdup(buf1
);
3649 linkprio(switch_case
, fall_thru
, mother_exten
);
3650 } else if (p2
->next
&& p2
->next
->type
== PV_DEFAULT
) {
3651 fall_thru
= new_prio();
3652 fall_thru
->type
= AEL_APPCALL
;
3653 fall_thru
->app
= strdup("Goto");
3654 snprintf(buf1
,sizeof(buf1
),"sw-%d-.|10",local_control_statement_count
);
3655 fall_thru
->appargs
= strdup(buf1
);
3656 linkprio(switch_case
, fall_thru
, mother_exten
);
3657 } else if (!p2
->next
) {
3658 fall_thru
= new_prio();
3659 fall_thru
->type
= AEL_CONTROL1
;
3660 fall_thru
->goto_true
= switch_end
;
3661 fall_thru
->app
= strdup("Goto");
3662 linkprio(switch_case
, fall_thru
, mother_exten
);
3665 if (switch_case
->return_needed
) {
3667 struct ael_priority
*np2
= new_prio();
3668 np2
->type
= AEL_APPCALL
;
3669 np2
->app
= strdup("NoOp");
3670 snprintf(buf
,sizeof(buf
),"End of Extension %s", switch_case
->name
);
3671 np2
->appargs
= strdup(buf
);
3672 linkprio(switch_case
, np2
, mother_exten
);
3673 switch_case
-> return_target
= np2
;
3675 } else if (p2
->type
== PV_DEFAULT
) {
3676 /* ok, generate a extension and link it in */
3677 switch_case
= new_exten();
3678 if (mother_exten
&& mother_exten
->checked_switch
) {
3679 switch_case
->has_switch
= mother_exten
->has_switch
;
3680 switch_case
->checked_switch
= mother_exten
->checked_switch
;
3682 if (exten
&& exten
->checked_switch
) {
3683 switch_case
->has_switch
= exten
->has_switch
;
3684 switch_case
->checked_switch
= exten
->checked_switch
;
3686 switch_case
->context
= this_context
;
3687 switch_case
->is_switch
= 1;
3689 /* new: the default case intros a pattern with ., which covers ALMOST everything.
3690 but it doesn't cover a NULL pattern. So, we'll define a null extension to match
3691 that goto's the default extension. */
3694 switch_null
= new_exten();
3695 if (mother_exten
&& mother_exten
->checked_switch
) {
3696 switch_null
->has_switch
= mother_exten
->has_switch
;
3697 switch_null
->checked_switch
= mother_exten
->checked_switch
;
3699 if (exten
&& exten
->checked_switch
) {
3700 switch_null
->has_switch
= exten
->has_switch
;
3701 switch_null
->checked_switch
= exten
->checked_switch
;
3703 switch_null
->context
= this_context
;
3704 switch_null
->is_switch
= 1;
3705 switch_empty
= new_prio();
3706 snprintf(buf1
,sizeof(buf1
),"sw-%d-.|10",local_control_statement_count
);
3707 switch_empty
->app
= strdup("Goto");
3708 switch_empty
->appargs
= strdup(buf1
);
3709 linkprio(switch_null
, switch_empty
, mother_exten
);
3710 snprintf(buf1
,sizeof(buf1
),"sw-%d-", local_control_statement_count
);
3711 switch_null
->name
= strdup(buf1
);
3712 switch_null
->loop_break
= exten
->loop_break
;
3713 switch_null
->loop_continue
= exten
->loop_continue
;
3714 linkexten(exten
,switch_null
);
3716 /* the break/continue locations are inherited from parent */
3717 switch_case
->loop_break
= exten
->loop_break
;
3718 switch_case
->loop_continue
= exten
->loop_continue
;
3719 linkexten(exten
,switch_case
);
3720 snprintf(buf1
,sizeof(buf1
),"_sw-%d-.", local_control_statement_count
);
3721 switch_case
->name
= strdup(buf1
);
3723 snprintf(new_label
,sizeof(new_label
),"sw-%s-default-%d", label
, local_control_statement_count
);
3725 gen_prios(switch_case
, new_label
, p2
->u2
.statements
, exten
, this_context
); /* this will link in all the default: body statements here */
3727 /* here is where we write code to "fall thru" to the next case... if there is one... */
3728 for (p3
=p2
->u2
.statements
; p3
; p3
=p3
->next
) {
3732 /* p3 now points the last statement... */
3733 if (!p3
|| (p3
->type
!= PV_GOTO
&& p3
->type
!= PV_BREAK
&& p3
->type
!= PV_RETURN
)) {
3734 /* is there a following CASE/PATTERN/DEFAULT? */
3735 if (p2
->next
&& p2
->next
->type
== PV_CASE
) {
3736 fall_thru
= new_prio();
3737 fall_thru
->type
= AEL_APPCALL
;
3738 fall_thru
->app
= strdup("Goto");
3739 strncpy(buf2
,p2
->next
->u1
.str
,sizeof(buf2
));
3740 buf2
[sizeof(buf2
)-1] = 0; /* just in case */
3741 substitute_commas(buf2
);
3742 snprintf(buf1
,sizeof(buf1
),"sw-%d-%s|10",local_control_statement_count
, buf2
);
3743 fall_thru
->appargs
= strdup(buf1
);
3744 linkprio(switch_case
, fall_thru
, mother_exten
);
3745 } else if (p2
->next
&& p2
->next
->type
== PV_PATTERN
) {
3746 fall_thru
= new_prio();
3747 fall_thru
->type
= AEL_APPCALL
;
3748 fall_thru
->app
= strdup("Goto");
3749 gen_match_to_pattern(p2
->next
->u1
.str
, buf2
);
3750 substitute_commas(buf2
);
3751 snprintf(buf1
,sizeof(buf1
),"sw-%d-%s|10",local_control_statement_count
, buf2
);
3752 fall_thru
->appargs
= strdup(buf1
);
3753 linkprio(switch_case
, fall_thru
, mother_exten
);
3754 } else if (p2
->next
&& p2
->next
->type
== PV_DEFAULT
) {
3755 fall_thru
= new_prio();
3756 fall_thru
->type
= AEL_APPCALL
;
3757 fall_thru
->app
= strdup("Goto");
3758 snprintf(buf1
,sizeof(buf1
),"sw-%d-.|10",local_control_statement_count
);
3759 fall_thru
->appargs
= strdup(buf1
);
3760 linkprio(switch_case
, fall_thru
, mother_exten
);
3761 } else if (!p2
->next
) {
3762 fall_thru
= new_prio();
3763 fall_thru
->type
= AEL_CONTROL1
;
3764 fall_thru
->goto_true
= switch_end
;
3765 fall_thru
->app
= strdup("Goto");
3766 linkprio(switch_case
, fall_thru
, mother_exten
);
3769 if (switch_case
->return_needed
) {
3771 struct ael_priority
*np2
= new_prio();
3772 np2
->type
= AEL_APPCALL
;
3773 np2
->app
= strdup("NoOp");
3774 snprintf(buf
,sizeof(buf
),"End of Extension %s", switch_case
->name
);
3775 np2
->appargs
= strdup(buf
);
3776 linkprio(switch_case
, np2
, mother_exten
);
3777 switch_case
-> return_target
= np2
;
3780 /* what could it be??? */
3784 exten
->loop_break
= loop_break_save
;
3785 exten
->loop_continue
= loop_continue_save
;
3786 switch_test
->origin
= p
;
3787 switch_end
->origin
= p
;
3792 pr
->type
= AEL_APPCALL
;
3793 snprintf(buf1
,sizeof(buf1
),"%s", p
->u1
.str
);
3794 for (p2
= p
->u2
.arglist
; p2
; p2
= p2
->next
) {
3796 strcat(buf1
,p2
->u1
.str
);
3798 pr
->app
= strdup("Macro");
3799 pr
->appargs
= strdup(buf1
);
3801 linkprio(exten
, pr
, mother_exten
);
3804 case PV_APPLICATION_CALL
:
3806 pr
->type
= AEL_APPCALL
;
3808 for (p2
= p
->u2
.arglist
; p2
; p2
= p2
->next
) {
3809 if (p2
!= p
->u2
.arglist
)
3811 substitute_commas(p2
->u1
.str
);
3812 strcat(buf1
,p2
->u1
.str
);
3814 pr
->app
= strdup(p
->u1
.str
);
3815 pr
->appargs
= strdup(buf1
);
3817 linkprio(exten
, pr
, mother_exten
);
3822 pr
->type
= AEL_CONTROL1
; /* simple goto */
3823 pr
->goto_true
= exten
->loop_break
;
3825 linkprio(exten
, pr
, mother_exten
);
3828 case PV_RETURN
: /* hmmmm */
3830 pr
->type
= AEL_RETURN
; /* simple goto */
3831 exten
->return_needed
++;
3832 pr
->app
= strdup("Goto");
3833 pr
->appargs
= strdup("");
3835 linkprio(exten
, pr
, mother_exten
);
3840 pr
->type
= AEL_CONTROL1
; /* simple goto */
3841 pr
->goto_true
= exten
->loop_continue
;
3843 linkprio(exten
, pr
, mother_exten
);
3846 #ifdef OLD_RAND_ACTION
3848 control_statement_count
++;
3849 snprintf(new_label
,sizeof(new_label
),"rand-%s-%d", label
, control_statement_count
);
3850 rand_test
= new_prio();
3851 rand_test
->type
= AEL_RAND_CONTROL
;
3852 snprintf(buf1
,sizeof(buf1
),"$[%s]",
3855 rand_test
->appargs
= strdup(buf1
);
3856 rand_test
->origin
= p
;
3858 rand_end
= new_prio();
3859 rand_end
->type
= AEL_APPCALL
;
3860 snprintf(buf1
,sizeof(buf1
),"Finish rand-%s-%d", label
, control_statement_count
);
3861 rand_end
->app
= strdup("NoOp");
3862 rand_end
->appargs
= strdup(buf1
);
3864 rand_skip
= new_prio();
3865 rand_skip
->type
= AEL_CONTROL1
; /* simple goto */
3866 rand_skip
->goto_true
= rand_end
;
3867 rand_skip
->origin
= p
;
3869 rand_test
->goto_true
= rand_skip
; /* +1, really */
3871 linkprio(exten
, rand_test
, mother_exten
);
3873 if (p
->u3
.else_statements
) {
3874 gen_prios(exten
, new_label
, p
->u3
.else_statements
, mother_exten
, this_context
); /* this will link in all the else statements here */
3877 linkprio(exten
, rand_skip
, mother_exten
);
3879 gen_prios(exten
, new_label
, p
->u2
.statements
, mother_exten
, this_context
); /* this will link in all the "true" statements here */
3881 linkprio(exten
, rand_end
, mother_exten
);
3887 control_statement_count
++;
3888 snprintf(new_label
,sizeof(new_label
),"iftime-%s-%d", label
, control_statement_count
);
3890 if_test
= new_prio();
3891 if_test
->type
= AEL_IFTIME_CONTROL
;
3892 snprintf(buf1
,sizeof(buf1
),"%s|%s|%s|%s",
3894 p
->u1
.list
->next
->u1
.str
,
3895 p
->u1
.list
->next
->next
->u1
.str
,
3896 p
->u1
.list
->next
->next
->next
->u1
.str
);
3898 if_test
->appargs
= strdup(buf1
);
3899 if_test
->origin
= p
;
3901 if_end
= new_prio();
3902 if_end
->type
= AEL_APPCALL
;
3903 snprintf(buf1
,sizeof(buf1
),"Finish iftime-%s-%d", label
, control_statement_count
);
3904 if_end
->app
= strdup("NoOp");
3905 if_end
->appargs
= strdup(buf1
);
3907 if (p
->u3
.else_statements
) {
3908 if_skip
= new_prio();
3909 if_skip
->type
= AEL_CONTROL1
; /* simple goto */
3910 if_skip
->goto_true
= if_end
;
3911 if_skip
->origin
= p
;
3916 if_test
->goto_false
= if_end
;
3919 if_false
= new_prio();
3920 if_false
->type
= AEL_CONTROL1
;
3921 if (p
->u3
.else_statements
) {
3922 if_false
->goto_true
= if_skip
; /* +1 */
3924 if_false
->goto_true
= if_end
;
3928 linkprio(exten
, if_test
, mother_exten
);
3929 linkprio(exten
, if_false
, mother_exten
);
3931 /* now, put the body of the if here */
3933 gen_prios(exten
, new_label
, p
->u2
.statements
, mother_exten
, this_context
); /* this will link in all the statements here */
3935 if (p
->u3
.else_statements
) {
3936 linkprio(exten
, if_skip
, mother_exten
);
3937 gen_prios(exten
, new_label
, p
->u3
.else_statements
, mother_exten
, this_context
); /* this will link in all the statements here */
3941 linkprio(exten
, if_end
, mother_exten
);
3947 control_statement_count
++;
3948 snprintf(new_label
,sizeof(new_label
),"if-%s-%d", label
, control_statement_count
);
3950 if_test
= new_prio();
3951 if_end
= new_prio();
3952 if_test
->type
= AEL_IF_CONTROL
;
3953 if_end
->type
= AEL_APPCALL
;
3954 if ( p
->type
== PV_RANDOM
)
3955 snprintf(buf1
,sizeof(buf1
),"$[${RAND(0|99)} < (%s)]",p
->u1
.str
);
3958 strcpy(buf
,p
->u1
.str
);
3959 substitute_commas(buf
);
3960 snprintf(buf1
,sizeof(buf1
),"$[%s]",buf
);
3964 if_test
->appargs
= strdup(buf1
);
3965 snprintf(buf1
,sizeof(buf1
),"Finish if-%s-%d", label
, control_statement_count
);
3966 if_end
->app
= strdup("NoOp");
3967 if_end
->appargs
= strdup(buf1
);
3968 if_test
->origin
= p
;
3970 if (p
->u3
.else_statements
) {
3971 if_skip
= new_prio();
3972 if_skip
->type
= AEL_CONTROL1
; /* simple goto */
3973 if_skip
->goto_true
= if_end
;
3974 if_test
->goto_false
= if_skip
;;
3977 if_test
->goto_false
= if_end
;;
3981 linkprio(exten
, if_test
, mother_exten
);
3983 /* now, put the body of the if here */
3985 gen_prios(exten
, new_label
, p
->u2
.statements
, mother_exten
, this_context
); /* this will link in all the statements here */
3987 if (p
->u3
.else_statements
) {
3988 linkprio(exten
, if_skip
, mother_exten
);
3989 gen_prios(exten
, new_label
, p
->u3
.else_statements
, mother_exten
, this_context
); /* this will link in all the statements here */
3993 linkprio(exten
, if_end
, mother_exten
);
3997 case PV_STATEMENTBLOCK
:
3998 gen_prios(exten
, label
, p
->u1
.list
, mother_exten
, this_context
); /* recurse into the block */
4002 control_statement_count
++;
4003 /* generate an extension with name of catch, put all catch stats
4005 switch_case
= new_exten();
4006 if (mother_exten
&& mother_exten
->checked_switch
) {
4007 switch_case
->has_switch
= mother_exten
->has_switch
;
4008 switch_case
->checked_switch
= mother_exten
->checked_switch
;
4010 if (exten
&& exten
->checked_switch
) {
4011 switch_case
->has_switch
= exten
->has_switch
;
4012 switch_case
->checked_switch
= exten
->checked_switch
;
4015 switch_case
->context
= this_context
;
4016 linkexten(exten
,switch_case
);
4017 switch_case
->name
= strdup(p
->u1
.str
);
4018 snprintf(new_label
,sizeof(new_label
),"catch-%s-%d",p
->u1
.str
, control_statement_count
);
4020 gen_prios(switch_case
, new_label
, p
->u2
.statements
,mother_exten
,this_context
); /* this will link in all the catch body statements here */
4021 if (switch_case
->return_needed
) {
4023 struct ael_priority
*np2
= new_prio();
4024 np2
->type
= AEL_APPCALL
;
4025 np2
->app
= strdup("NoOp");
4026 snprintf(buf
,sizeof(buf
),"End of Extension %s", switch_case
->name
);
4027 np2
->appargs
= strdup(buf
);
4028 linkprio(switch_case
, np2
, mother_exten
);
4029 switch_case
-> return_target
= np2
;
4039 void set_priorities(struct ael_extension
*exten
)
4042 struct ael_priority
*pr
;
4044 if (exten
->is_switch
)
4046 else if (exten
->regexten
)
4051 for (pr
=exten
->plist
; pr
; pr
=pr
->next
) {
4052 pr
->priority_num
= i
;
4054 if (!pr
->origin
|| (pr
->origin
&& pr
->origin
->type
!= PV_LABEL
) ) /* Labels don't show up in the dialplan,
4055 but we want them to point to the right
4056 priority, which would be the next line
4061 exten
= exten
->next_exten
;
4065 void add_extensions(struct ael_extension
*exten
)
4067 struct ael_priority
*pr
;
4069 char realext
[AST_MAX_EXTENSION
];
4071 ast_log(LOG_WARNING
, "This file is Empty!\n" );
4075 struct ael_priority
*last
= 0;
4077 memset(realext
, '\0', sizeof(realext
));
4078 pbx_substitute_variables_helper(NULL
, exten
->name
, realext
, sizeof(realext
) - 1);
4080 if (ast_add_extension2(exten
->context
, 0 /*no replace*/, realext
, PRIORITY_HINT
, NULL
, exten
->cidmatch
,
4081 exten
->hints
, NULL
, ast_free
, registrar
)) {
4082 ast_log(LOG_WARNING
, "Unable to add step at priority 'hint' of extension '%s'\n",
4087 for (pr
=exten
->plist
; pr
; pr
=pr
->next
) {
4091 /* before we can add the extension, we need to prep the app/appargs;
4092 the CONTROL types need to be done after the priority numbers are calculated.
4094 if (pr
->type
== AEL_LABEL
) /* don't try to put labels in the dialplan! */ {
4100 strcpy(app
, pr
->app
);
4104 strcpy(appargs
, pr
->appargs
);
4107 switch( pr
->type
) {
4109 /* easy case. Everything is all set up */
4112 case AEL_CONTROL1
: /* FOR loop, WHILE loop, BREAK, CONTINUE, IF, IFTIME */
4113 /* simple, unconditional goto. */
4115 if (pr
->goto_true
->origin
&& pr
->goto_true
->origin
->type
== PV_SWITCH
) {
4116 snprintf(appargs
,sizeof(appargs
),"%s|%d", pr
->goto_true
->exten
->name
, pr
->goto_true
->priority_num
);
4117 } else if (pr
->goto_true
->origin
&& pr
->goto_true
->origin
->type
== PV_IFTIME
&& pr
->goto_true
->origin
->u3
.else_statements
) {
4118 snprintf(appargs
,sizeof(appargs
),"%d", pr
->goto_true
->priority_num
+1);
4120 snprintf(appargs
,sizeof(appargs
),"%d", pr
->goto_true
->priority_num
);
4123 case AEL_FOR_CONTROL
: /* WHILE loop test, FOR loop test */
4124 strcpy(app
,"GotoIf");
4125 snprintf(appargs
,sizeof(appargs
),"%s?%d:%d", pr
->appargs
, pr
->priority_num
+1, pr
->goto_false
->priority_num
);
4128 case AEL_IF_CONTROL
:
4129 strcpy(app
,"GotoIf");
4130 if (pr
->origin
->u3
.else_statements
)
4131 snprintf(appargs
,sizeof(appargs
),"%s?%d:%d", pr
->appargs
, pr
->priority_num
+1, pr
->goto_false
->priority_num
+1);
4133 snprintf(appargs
,sizeof(appargs
),"%s?%d:%d", pr
->appargs
, pr
->priority_num
+1, pr
->goto_false
->priority_num
);
4136 case AEL_RAND_CONTROL
:
4137 strcpy(app
,"Random");
4138 snprintf(appargs
,sizeof(appargs
),"%s:%d", pr
->appargs
, pr
->goto_true
->priority_num
+1);
4141 case AEL_IFTIME_CONTROL
:
4142 strcpy(app
,"GotoIfTime");
4143 snprintf(appargs
,sizeof(appargs
),"%s?%d", pr
->appargs
, pr
->priority_num
+2);
4148 snprintf(appargs
,sizeof(appargs
), "%d", exten
->return_target
->priority_num
);
4154 if (last
&& last
->type
== AEL_LABEL
) {
4155 label
= last
->origin
->u1
.str
;
4160 if (ast_add_extension2(exten
->context
, 0 /*no replace*/, realext
, pr
->priority_num
, (label
?label
:NULL
), exten
->cidmatch
,
4161 app
, strdup(appargs
), ast_free
, registrar
)) {
4162 ast_log(LOG_WARNING
, "Unable to add step at priority '%d' of extension '%s'\n", pr
->priority_num
,
4167 exten
= exten
->next_exten
;
4171 static void attach_exten(struct ael_extension
**list
, struct ael_extension
*newmem
)
4173 /* travel to the end of the list... */
4174 struct ael_extension
*lptr
;
4181 while( lptr
->next_exten
) {
4182 lptr
= lptr
->next_exten
;
4184 /* lptr should now pointing to the last element in the list; it has a null next_exten pointer */
4185 lptr
->next_exten
= newmem
;
4188 static pval
*get_extension_or_contxt(pval
*p
)
4190 while( p
&& p
->type
!= PV_EXTENSION
&& p
->type
!= PV_CONTEXT
&& p
->type
!= PV_MACRO
) {
4198 static pval
*get_contxt(pval
*p
)
4200 while( p
&& p
->type
!= PV_CONTEXT
&& p
->type
!= PV_MACRO
) {
4208 static void fix_gotos_in_extensions(struct ael_extension
*exten
)
4210 struct ael_extension
*e
;
4211 for(e
=exten
;e
;e
=e
->next_exten
) {
4213 struct ael_priority
*p
;
4214 for(p
=e
->plist
;p
;p
=p
->next
) {
4216 if( p
->origin
&& p
->origin
->type
== PV_GOTO
&& p
->origin
->u3
.goto_target_in_case
) {
4218 /* fix the extension of the goto target to the actual extension in the post-compiled dialplan */
4220 pval
*target
= p
->origin
->u2
.goto_target
;
4221 struct ael_extension
*z
= target
->u3
.compiled_label
;
4222 pval
*pv2
= p
->origin
;
4224 char *apparg_save
= p
->appargs
;
4227 if (!pv2
->u1
.list
->next
) /* just one -- it won't hurt to repeat the extension */ {
4228 snprintf(buf1
,sizeof(buf1
),"%s|%s", z
->name
, pv2
->u1
.list
->u1
.str
);
4229 p
->appargs
= strdup(buf1
);
4231 } else if (pv2
->u1
.list
->next
&& !pv2
->u1
.list
->next
->next
) /* two */ {
4232 snprintf(buf1
,sizeof(buf1
),"%s|%s", z
->name
, pv2
->u1
.list
->next
->u1
.str
);
4233 p
->appargs
= strdup(buf1
);
4234 } else if (pv2
->u1
.list
->next
&& pv2
->u1
.list
->next
->next
) {
4235 snprintf(buf1
,sizeof(buf1
),"%s|%s|%s", pv2
->u1
.list
->u1
.str
,
4237 pv2
->u1
.list
->next
->next
->u1
.str
);
4238 p
->appargs
= strdup(buf1
);
4241 printf("WHAT? The goto doesn't fall into one of three cases for GOTO????\n");
4252 void ast_compile_ael2(struct ast_context
**local_contexts
, struct pval
*root
)
4255 struct ast_context
*context
;
4257 struct ael_extension
*exten
;
4258 struct ael_extension
*exten_list
= 0;
4260 for (p
=root
; p
; p
=p
->next
) { /* do the globals first, so they'll be there
4261 when we try to eval them */
4264 /* just VARDEC elements */
4265 for (p2
=p
->u1
.list
; p2
; p2
=p2
->next
) {
4267 snprintf(buf2
,sizeof(buf2
),"%s=%s", p2
->u1
.str
, p2
->u2
.val
);
4268 pbx_builtin_setvar(NULL
, buf2
);
4276 for (p
=root
; p
; p
=p
->next
) {
4282 strcpy(buf
,"macro-");
4283 strcat(buf
,p
->u1
.str
);
4284 context
= ast_context_create(local_contexts
, buf
, registrar
);
4286 exten
= new_exten();
4287 exten
->context
= context
;
4288 exten
->name
= strdup("s");
4290 for (lp
=p
->u2
.arglist
; lp
; lp
=lp
->next
) {
4291 /* for each arg, set up a "Set" command */
4292 struct ael_priority
*np2
= new_prio();
4293 np2
->type
= AEL_APPCALL
;
4294 np2
->app
= strdup("Set");
4295 snprintf(buf
,sizeof(buf
),"%s=${ARG%d}", lp
->u1
.str
, argc
++);
4296 remove_spaces_before_equals(buf
);
4297 np2
->appargs
= strdup(buf
);
4298 linkprio(exten
, np2
, NULL
);
4300 /* add any includes */
4301 for (p2
=p
->u3
.macro_statements
; p2
; p2
=p2
->next
) {
4306 for (p3
= p2
->u1
.list
; p3
;p3
=p3
->next
) {
4307 if ( p3
->u2
.arglist
) {
4308 snprintf(buf
,sizeof(buf
), "%s|%s|%s|%s|%s",
4310 p3
->u2
.arglist
->u1
.str
,
4311 p3
->u2
.arglist
->next
->u1
.str
,
4312 p3
->u2
.arglist
->next
->next
->u1
.str
,
4313 p3
->u2
.arglist
->next
->next
->next
->u1
.str
);
4314 ast_context_add_include2(context
, buf
, registrar
);
4316 ast_context_add_include2(context
, p3
->u1
.str
, registrar
);
4323 /* CONTAINS APPCALLS, CATCH, just like extensions... */
4324 gen_prios(exten
, p
->u1
.str
, p
->u3
.macro_statements
, 0, context
);
4325 if (exten
->return_needed
) {
4326 struct ael_priority
*np2
= new_prio();
4327 np2
->type
= AEL_APPCALL
;
4328 np2
->app
= strdup("NoOp");
4329 snprintf(buf
,sizeof(buf
),"End of Macro %s-%s",p
->u1
.str
, exten
->name
);
4330 np2
->appargs
= strdup(buf
);
4331 linkprio(exten
, np2
, NULL
);
4332 exten
-> return_target
= np2
;
4335 set_priorities(exten
);
4336 attach_exten(&exten_list
, exten
);
4344 context
= ast_context_find_or_create(local_contexts
, p
->u1
.str
, registrar
);
4346 /* contexts contain: ignorepat, includes, switches, eswitches, extensions, */
4347 for (p2
=p
->u2
.statements
; p2
; p2
=p2
->next
) {
4353 exten
= new_exten();
4354 exten
->name
= strdup(p2
->u1
.str
);
4355 exten
->context
= context
;
4357 if( (s3
=strchr(exten
->name
, '/') ) != 0 )
4360 exten
->cidmatch
= s3
+1;
4364 exten
->hints
= strdup(p2
->u3
.hints
);
4365 exten
->regexten
= p2
->u4
.regexten
;
4366 gen_prios(exten
, p
->u1
.str
, p2
->u2
.statements
, 0, context
);
4367 if (exten
->return_needed
) {
4368 struct ael_priority
*np2
= new_prio();
4369 np2
->type
= AEL_APPCALL
;
4370 np2
->app
= strdup("NoOp");
4371 snprintf(buf
,sizeof(buf
),"End of Extension %s", exten
->name
);
4372 np2
->appargs
= strdup(buf
);
4373 linkprio(exten
, np2
, NULL
);
4374 exten
-> return_target
= np2
;
4376 /* is the last priority in the extension a label? Then add a trailing no-op */
4377 if ( exten
->plist_last
&& exten
->plist_last
->type
== AEL_LABEL
) {
4378 struct ael_priority
*np2
= new_prio();
4379 np2
->type
= AEL_APPCALL
;
4380 np2
->app
= strdup("NoOp");
4381 snprintf(buf
,sizeof(buf
),"A NoOp to follow a trailing label %s", exten
->plist_last
->origin
->u1
.str
);
4382 np2
->appargs
= strdup(buf
);
4383 linkprio(exten
, np2
, NULL
);
4386 set_priorities(exten
);
4387 attach_exten(&exten_list
, exten
);
4391 ast_context_add_ignorepat2(context
, p2
->u1
.str
, registrar
);
4395 for (p3
= p2
->u1
.list
; p3
;p3
=p3
->next
) {
4396 if ( p3
->u2
.arglist
) {
4397 snprintf(buf
,sizeof(buf
), "%s|%s|%s|%s|%s",
4399 p3
->u2
.arglist
->u1
.str
,
4400 p3
->u2
.arglist
->next
->u1
.str
,
4401 p3
->u2
.arglist
->next
->next
->u1
.str
,
4402 p3
->u2
.arglist
->next
->next
->next
->u1
.str
);
4403 ast_context_add_include2(context
, buf
, registrar
);
4405 ast_context_add_include2(context
, p3
->u1
.str
, registrar
);
4410 for (p3
= p2
->u1
.list
; p3
;p3
=p3
->next
) {
4411 char *c
= strchr(p3
->u1
.str
, '/');
4418 ast_context_add_switch2(context
, p3
->u1
.str
, c
, 0, registrar
);
4423 for (p3
= p2
->u1
.list
; p3
;p3
=p3
->next
) {
4424 char *c
= strchr(p3
->u1
.str
, '/');
4431 ast_context_add_switch2(context
, p3
->u1
.str
, c
, 1, registrar
);
4447 /* moved these from being done after a macro or extension were processed,
4448 to after all processing is done, for the sake of fixing gotos to labels inside cases... */
4449 /* I guess this would be considered 2nd pass of compiler now... */
4450 fix_gotos_in_extensions(exten_list
); /* find and fix extension ref in gotos to labels that are in case statements */
4451 add_extensions(exten_list
); /* actually makes calls to create priorities in ast_contexts -- feeds dialplan to asterisk */
4452 destroy_extensions(exten_list
); /* all that remains is an empty husk, discard of it as is proper */
4457 static int aeldebug
= 0;
4459 /* interface stuff */
4461 /* if all the below are static, who cares if they are present? */
4463 static int pbx_load_module(void)
4465 int errs
=0, sem_err
=0, sem_warn
=0, sem_note
=0;
4467 struct ast_context
*local_contexts
=NULL
, *con
;
4468 struct pval
*parse_tree
;
4470 ast_log(LOG_NOTICE
, "Starting AEL load process.\n");
4471 if (config
[0] == '/')
4472 rfilename
= (char *)config
;
4474 rfilename
= alloca(strlen(config
) + strlen(ast_config_AST_CONFIG_DIR
) + 2);
4475 sprintf(rfilename
, "%s/%s", ast_config_AST_CONFIG_DIR
, config
);
4477 ast_log(LOG_NOTICE
, "AEL load process: calculated config file name '%s'.\n", rfilename
);
4479 if (access(rfilename
,R_OK
) != 0) {
4480 ast_log(LOG_NOTICE
, "File %s not found; AEL declining load\n", rfilename
);
4481 return AST_MODULE_LOAD_DECLINE
;
4484 parse_tree
= ael2_parse(rfilename
, &errs
);
4485 ast_log(LOG_NOTICE
, "AEL load process: parsed config file name '%s'.\n", rfilename
);
4486 ael2_semantic_check(parse_tree
, &sem_err
, &sem_warn
, &sem_note
);
4487 if (errs
== 0 && sem_err
== 0) {
4488 ast_log(LOG_NOTICE
, "AEL load process: checked config file name '%s'.\n", rfilename
);
4489 ast_compile_ael2(&local_contexts
, parse_tree
);
4490 ast_log(LOG_NOTICE
, "AEL load process: compiled config file name '%s'.\n", rfilename
);
4492 ast_merge_contexts_and_delete(&local_contexts
, registrar
);
4493 ast_log(LOG_NOTICE
, "AEL load process: merged config file name '%s'.\n", rfilename
);
4494 for (con
= ast_walk_contexts(NULL
); con
; con
= ast_walk_contexts(con
))
4495 ast_context_verify_includes(con
);
4496 ast_log(LOG_NOTICE
, "AEL load process: verified config file name '%s'.\n", rfilename
);
4498 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
);
4499 destroy_pval(parse_tree
); /* free up the memory */
4500 return AST_MODULE_LOAD_DECLINE
;
4502 destroy_pval(parse_tree
); /* free up the memory */
4504 return AST_MODULE_LOAD_SUCCESS
;
4508 static int ael2_debug_read(int fd
, int argc
, char *argv
[])
4510 aeldebug
|= DEBUG_READ
;
4514 static int ael2_debug_tokens(int fd
, int argc
, char *argv
[])
4516 aeldebug
|= DEBUG_TOKENS
;
4520 static int ael2_debug_macros(int fd
, int argc
, char *argv
[])
4522 aeldebug
|= DEBUG_MACROS
;
4526 static int ael2_debug_contexts(int fd
, int argc
, char *argv
[])
4528 aeldebug
|= DEBUG_CONTEXTS
;
4532 static int ael2_no_debug(int fd
, int argc
, char *argv
[])
4538 static int ael2_reload(int fd
, int argc
, char *argv
[])
4540 return (pbx_load_module());
4543 static struct ast_cli_entry cli_ael_no_debug
= {
4544 { "ael", "no", "debug", NULL
},
4545 ael2_no_debug
, NULL
,
4548 static struct ast_cli_entry cli_ael
[] = {
4549 { { "ael", "reload", NULL
},
4550 ael2_reload
, "Reload AEL configuration" },
4552 { { "ael", "debug", "read", NULL
},
4553 ael2_debug_read
, "Enable AEL read debug (does nothing)" },
4555 { { "ael", "debug", "tokens", NULL
},
4556 ael2_debug_tokens
, "Enable AEL tokens debug (does nothing)" },
4558 { { "ael", "debug", "macros", NULL
},
4559 ael2_debug_macros
, "Enable AEL macros debug (does nothing)" },
4561 { { "ael", "debug", "contexts", NULL
},
4562 ael2_debug_contexts
, "Enable AEL contexts debug (does nothing)" },
4564 { { "ael", "nodebug", NULL
},
4565 ael2_no_debug
, "Disable AEL debug messages",
4566 NULL
, NULL
, &cli_ael_no_debug
},
4569 static int unload_module(void)
4571 ast_context_destroy(NULL
, registrar
);
4572 ast_cli_unregister_multiple(cli_ael
, sizeof(cli_ael
) / sizeof(struct ast_cli_entry
));
4576 static int load_module(void)
4578 ast_cli_register_multiple(cli_ael
, sizeof(cli_ael
) / sizeof(struct ast_cli_entry
));
4579 return (pbx_load_module());
4582 static int reload(void)
4584 return pbx_load_module();
4587 #ifdef STANDALONE_AEL
4588 #define AST_MODULE "ael"
4589 int ael_external_load_module(void);
4590 int ael_external_load_module(void)
4597 AST_MODULE_INFO(ASTERISK_GPL_KEY
, AST_MODFLAG_DEFAULT
, "Asterisk Extension Language Compiler",
4598 .load
= load_module
,
4599 .unload
= unload_module
,
4604 /* DESTROY the PVAL tree ============================================================================ */
4608 void destroy_pval_item(pval
*item
)
4611 ast_log(LOG_WARNING
, "null item\n");
4616 free(item
->filename
);
4618 switch (item
->type
) {
4620 /* fields: item->u1.str == string associated with this (word). */
4623 if ( item
->u2
.arglist
)
4624 destroy_pval(item
->u2
.arglist
);
4628 /* fields: item->u1.str == name of macro
4629 item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
4630 item->u2.arglist->u1.str == argument
4631 item->u2.arglist->next == next arg
4633 item->u3.macro_statements == pval list of statements in macro body.
4635 destroy_pval(item
->u2
.arglist
);
4638 destroy_pval(item
->u3
.macro_statements
);
4642 /* fields: item->u1.str == name of context
4643 item->u2.statements == pval list of statements in context body
4644 item->u3.abstract == int 1 if an abstract keyword were present
4648 destroy_pval(item
->u2
.statements
);
4652 /* fields: item->u1.str == name of macro to call
4653 item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
4654 item->u2.arglist->u1.str == argument
4655 item->u2.arglist->next == next arg
4659 destroy_pval(item
->u2
.arglist
);
4662 case PV_APPLICATION_CALL
:
4663 /* fields: item->u1.str == name of application to call
4664 item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
4665 item->u2.arglist->u1.str == argument
4666 item->u2.arglist->next == next arg
4670 destroy_pval(item
->u2
.arglist
);
4674 /* fields: item->u1.str == value of case
4675 item->u2.statements == pval list of statements under the case
4679 destroy_pval(item
->u2
.statements
);
4683 /* fields: item->u1.str == value of case
4684 item->u2.statements == pval list of statements under the case
4688 destroy_pval(item
->u2
.statements
);
4693 item->u2.statements == pval list of statements under the case
4695 destroy_pval(item
->u2
.statements
);
4699 /* fields: item->u1.str == name of extension to catch
4700 item->u2.statements == pval list of statements in context body
4704 destroy_pval(item
->u2
.statements
);
4708 /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
4710 destroy_pval(item
->u1
.list
);
4714 /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
4716 destroy_pval(item
->u1
.list
);
4720 /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
4721 item->u2.arglist == pval list of 4 PV_WORD elements for time values
4723 destroy_pval(item
->u1
.list
);
4726 case PV_STATEMENTBLOCK
:
4727 /* fields: item->u1.list == pval list of statements in block, one per entry in the list
4729 destroy_pval(item
->u1
.list
);
4733 /* fields: item->u1.str == variable name
4734 item->u2.val == variable value to assign
4743 /* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user.
4744 item->u1.list->u1.str == where the data on a PV_WORD will always be.
4747 destroy_pval(item
->u1
.list
);
4751 /* fields: item->u1.str == label name
4758 /* fields: item->u1.for_init == a string containing the initalizer
4759 item->u2.for_test == a string containing the loop test
4760 item->u3.for_inc == a string containing the loop increment
4762 item->u4.for_statements == a pval list of statements in the for ()
4764 if (item
->u1
.for_init
)
4765 free(item
->u1
.for_init
);
4766 if (item
->u2
.for_test
)
4767 free(item
->u2
.for_test
);
4768 if (item
->u3
.for_inc
)
4769 free(item
->u3
.for_inc
);
4770 destroy_pval(item
->u4
.for_statements
);
4774 /* fields: item->u1.str == the while conditional, as supplied by user
4776 item->u2.statements == a pval list of statements in the while ()
4780 destroy_pval(item
->u2
.statements
);
4799 /* fields: item->u1.list == the 4 time values, in PV_WORD structs, linked list
4801 item->u2.statements == a pval list of statements in the if ()
4802 item->u3.else_statements == a pval list of statements in the else
4805 destroy_pval(item
->u1
.list
);
4806 destroy_pval(item
->u2
.statements
);
4807 if (item
->u3
.else_statements
) {
4808 destroy_pval(item
->u3
.else_statements
);
4813 /* fields: item->u1.str == the random percentage, as supplied by user
4815 item->u2.statements == a pval list of statements in the true part ()
4816 item->u3.else_statements == a pval list of statements in the else
4820 /* fields: item->u1.str == the if conditional, as supplied by user
4822 item->u2.statements == a pval list of statements in the if ()
4823 item->u3.else_statements == a pval list of statements in the else
4828 destroy_pval(item
->u2
.statements
);
4829 if (item
->u3
.else_statements
) {
4830 destroy_pval(item
->u3
.else_statements
);
4835 /* fields: item->u1.str == the switch expression
4837 item->u2.statements == a pval list of statements in the switch,
4838 (will be case statements, most likely!)
4842 destroy_pval(item
->u2
.statements
);
4846 /* fields: item->u1.str == the extension name, label, whatever it's called
4848 item->u2.statements == a pval list of statements in the extension
4849 item->u3.hints == a char * hint argument
4850 item->u4.regexten == an int boolean. non-zero says that regexten was specified
4855 free(item
->u3
.hints
);
4856 destroy_pval(item
->u2
.statements
);
4860 /* fields: item->u1.str == the ignorepat data
4867 /* fields: item->u1.statements == pval list of statements, usually vardecs
4869 destroy_pval(item
->u1
.statements
);
4875 void destroy_pval(pval
*item
)
4879 for (i
=item
; i
; i
=nxt
) {
4882 destroy_pval_item(i
);
4887 static char *ael_funclist
[] =
4912 "GROUP_MATCH_COUNT",
4924 "QUEUE_MEMBER_COUNT",
4925 "QUEUE_MEMBER_LIST",
4946 int ael_is_funcname(char *name
)
4949 t
= sizeof(ael_funclist
)/sizeof(char*);
4951 while ((s
< t
) && strcasecmp(name
, ael_funclist
[s
]))