0110c5e37e30386e53c47a9136ca405d9ce6380c
[asterisk/asterisk.git] / pbx / pbx_ael.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2006, Digium, Inc.
5  *
6  * Steve Murphy <murf@parsetree.com>
7  *
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.
13  *
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.
17  */
18
19 /*! \file
20  *
21  * \brief Compile symbolic Asterisk Extension Logic into Asterisk extensions, version 2.
22  * 
23  */
24
25 #include "asterisk.h"
26
27 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
28
29 #include <sys/types.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <ctype.h>
34 #include <errno.h>
35 #include <regex.h>
36 #include <sys/stat.h>
37
38 #include "asterisk/pbx.h"
39 #include "asterisk/config.h"
40 #include "asterisk/module.h"
41 #include "asterisk/logger.h"
42 #include "asterisk/cli.h"
43 #include "asterisk/app.h"
44 #include "asterisk/callerid.h"
45 #include "asterisk/ael_structs.h"
46 #ifdef AAL_ARGCHECK
47 #include "asterisk/argdesc.h"
48 #endif
49
50 static char expr_output[2096];
51
52 /* these functions are in ../ast_expr2.fl */
53
54 #define DEBUG_READ   (1 << 0)
55 #define DEBUG_TOKENS (1 << 1)
56 #define DEBUG_MACROS (1 << 2)
57 #define DEBUG_CONTEXTS (1 << 3)
58
59 static char *config = "extensions.ael";
60 static char *registrar = "pbx_ael";
61
62 static int errs, warns;
63 #ifndef STANDALONE_AEL
64 static int notes;
65 #endif
66
67 #ifndef AAL_ARGCHECK
68 /* for the time being, short circuit all the AAL related structures
69    without permanently removing the code; after/during the AAL 
70    development, this code can be properly re-instated 
71 */
72
73 /* null definitions for structs passed down the infrastructure */
74 struct argapp
75 {
76         struct argapp *next;
77 };
78
79 #endif
80
81 #ifdef AAL_ARGCHECK
82 int option_matches_j( struct argdesc *should, pval *is, struct argapp *app);
83 int option_matches( struct argdesc *should, pval *is, struct argapp *app);
84 int ael_is_funcname(char *name);
85 #endif
86
87 int check_app_args(pval *appcall, pval *arglist, struct argapp *app);
88 void check_pval(pval *item, struct argapp *apps, int in_globals);
89 void check_pval_item(pval *item, struct argapp *apps, int in_globals);
90 void check_switch_expr(pval *item, struct argapp *apps);
91 void ast_expr_register_extra_error_info(char *errmsg);
92 void ast_expr_clear_extra_error_info(void);
93 int  ast_expr(char *expr, char *buf, int length);
94 struct pval *find_macro(char *name);
95 struct pval *find_context(char *name);
96 struct pval *find_context(char *name);
97 struct pval *find_macro(char *name);
98 struct ael_priority *new_prio(void);
99 struct ael_extension *new_exten(void);
100 void linkprio(struct ael_extension *exten, struct ael_priority *prio);
101 void destroy_extensions(struct ael_extension *exten);
102 static void linkexten(struct ael_extension *exten, struct ael_extension *add);
103 static void gen_prios(struct ael_extension *exten, char *label, pval *statement, struct ael_extension *mother_exten, struct ast_context *context );
104 void set_priorities(struct ael_extension *exten);
105 void add_extensions(struct ael_extension *exten);
106 void ast_compile_ael2(struct ast_context **local_contexts, struct pval *root);
107 void destroy_pval(pval *item);
108 void destroy_pval_item(pval *item);
109 int is_float(char *arg );
110 int is_int(char *arg );
111 int is_empty(char *arg);
112 static pval *current_db;
113 static pval *current_context;
114 static pval *current_extension;
115
116 static const char *match_context;
117 static const char *match_exten;
118 static const char *match_label;
119 static int in_abstract_context;
120 static int count_labels; /* true, put matcher in label counting mode */
121 static int label_count;  /* labels are only meant to be counted in a context or exten */
122 static int return_on_context_match;
123 static pval *last_matched_label;
124 struct pval *match_pval(pval *item);
125 static void check_timerange(pval *p);
126 static void check_dow(pval *DOW);
127 static void check_day(pval *DAY);
128 static void check_month(pval *MON);
129 static void check_expr2_input(pval *expr, char *str);
130 static int extension_matches(pval *here, const char *exten, const char *pattern);
131 static void check_goto(pval *item);
132 static void find_pval_goto_item(pval *item, int lev);
133 static void find_pval_gotos(pval *item, int lev);
134
135 static struct pval *find_label_in_current_context(char *exten, char *label, pval *curr_cont);
136 static struct pval *find_first_label_in_current_context(char *label, pval *curr_cont);
137 static void print_pval_list(FILE *fin, pval *item, int depth);
138
139 static struct pval *find_label_in_current_extension(const char *label, pval *curr_ext);
140 static struct pval *find_label_in_current_db(const char *context, const char *exten, const char *label);
141 static pval *get_goto_target(pval *item);
142 static int label_inside_case(pval *label);
143 static void attach_exten(struct ael_extension **list, struct ael_extension *newmem);
144 static void fix_gotos_in_extensions(struct ael_extension *exten);
145 static pval *get_extension_or_contxt(pval *p);
146 static pval *get_contxt(pval *p);
147 static void remove_spaces_before_equals(char *str);
148
149
150 /* PRETTY PRINTER FOR AEL:  ============================================================================= */
151
152 static void print_pval(FILE *fin, pval *item, int depth)
153 {
154         int i;
155         pval *lp;
156         
157         for (i=0; i<depth; i++) {
158                 fprintf(fin, "\t"); /* depth == indentation */
159         }
160         
161         switch ( item->type ) {
162         case PV_WORD:
163                 fprintf(fin,"%s;\n", item->u1.str); /* usually, words are encapsulated in something else */
164                 break;
165                 
166         case PV_MACRO:
167                 fprintf(fin,"macro %s(", item->u1.str);
168                 for (lp=item->u2.arglist; lp; lp=lp->next) {
169                         if (lp != item->u2.arglist )
170                                 fprintf(fin,", ");
171                         fprintf(fin,"%s", lp->u1.str);
172                 }
173                 fprintf(fin,") {\n");
174                 print_pval_list(fin,item->u3.macro_statements,depth+1);
175                 for (i=0; i<depth; i++) {
176                         fprintf(fin,"\t"); /* depth == indentation */
177                 }
178                 fprintf(fin,"};\n\n");
179                 break;
180                         
181         case PV_CONTEXT:
182                 if ( item->u3.abstract )
183                         fprintf(fin,"abstract context %s {\n", item->u1.str);
184                 else
185                         fprintf(fin,"context %s {\n", item->u1.str);
186                 print_pval_list(fin,item->u2.statements,depth+1);
187                 for (i=0; i<depth; i++) {
188                         fprintf(fin,"\t"); /* depth == indentation */
189                 }
190                 fprintf(fin,"};\n\n");
191                 break;
192                         
193         case PV_MACRO_CALL:
194                 fprintf(fin,"&%s(", item->u1.str);
195                 for (lp=item->u2.arglist; lp; lp=lp->next) {
196                         if ( lp != item->u2.arglist )
197                                 fprintf(fin,", ");
198                         fprintf(fin,"%s", lp->u1.str);
199                 }
200                 fprintf(fin,");\n");
201                 break;
202                         
203         case PV_APPLICATION_CALL:
204                 fprintf(fin,"%s(", item->u1.str);
205                 for (lp=item->u2.arglist; lp; lp=lp->next) {
206                         if ( lp != item->u2.arglist )
207                                 fprintf(fin,", ");
208                         fprintf(fin,"%s", lp->u1.str);
209                 }
210                 fprintf(fin,");\n");
211                 break;
212                         
213         case PV_CASE:
214                 fprintf(fin,"case %s:\n", item->u1.str);
215                 print_pval_list(fin,item->u2.statements, depth+1);
216                 break;
217                         
218         case PV_PATTERN:
219                 fprintf(fin,"pattern %s:\n", item->u1.str);
220                 print_pval_list(fin,item->u2.statements, depth+1);
221                 break;
222                         
223         case PV_DEFAULT:
224                 fprintf(fin,"default:\n");
225                 print_pval_list(fin,item->u2.statements, depth+1);
226                 break;
227                         
228         case PV_CATCH:
229                 fprintf(fin,"catch %s {\n", item->u1.str);
230                 print_pval_list(fin,item->u2.statements, depth+1);
231                 for (i=0; i<depth; i++) {
232                         fprintf(fin,"\t"); /* depth == indentation */
233                 }
234                 fprintf(fin,"};\n");
235                 break;
236                         
237         case PV_SWITCHES:
238                 fprintf(fin,"switches {\n");
239                 print_pval_list(fin,item->u1.list,depth+1);
240                 for (i=0; i<depth; i++) {
241                         fprintf(fin,"\t"); /* depth == indentation */
242                 }
243                 fprintf(fin,"};\n");
244                 break;
245                         
246         case PV_ESWITCHES:
247                 fprintf(fin,"eswitches {\n");
248                 print_pval_list(fin,item->u1.list,depth+1);
249                 for (i=0; i<depth; i++) {
250                         fprintf(fin,"\t"); /* depth == indentation */
251                 }
252                 fprintf(fin,"};\n");
253                 break;
254                         
255         case PV_INCLUDES:
256                 fprintf(fin,"includes {\n");
257                 for (lp=item->u1.list; lp; lp=lp->next) {
258                         for (i=0; i<depth+1; i++) {
259                                 fprintf(fin,"\t"); /* depth == indentation */
260                         }
261                         fprintf(fin,"%s", lp->u1.str); /* usually, words are encapsulated in something else */
262                         if ( lp->u2.arglist )
263                                 fprintf(fin,"|%s|%s|%s|%s", 
264                                                 lp->u2.arglist->u1.str,
265                                                 lp->u2.arglist->next->u1.str,
266                                                 lp->u2.arglist->next->next->u1.str,
267                                                 lp->u2.arglist->next->next->next->u1.str
268                                         );
269                         fprintf(fin,";\n"); /* usually, words are encapsulated in something else */
270                 }
271                 
272                 print_pval_list(fin,item->u1.list,depth+1);
273                 for (i=0; i<depth; i++) {
274                         fprintf(fin,"\t"); /* depth == indentation */
275                 }
276                 fprintf(fin,"};\n");
277                 break;
278                         
279         case PV_STATEMENTBLOCK:
280                 fprintf(fin,"{\n");
281                 print_pval_list(fin,item->u1.list, depth+1);
282                 for (i=0; i<depth; i++) {
283                         fprintf(fin,"\t"); /* depth == indentation */
284                 }
285                 fprintf(fin,"};\n");
286                 break;
287                         
288         case PV_VARDEC:
289                 fprintf(fin,"%s=%s;\n", item->u1.str, item->u2.val);
290                 break;
291                         
292         case PV_GOTO:
293                 fprintf(fin,"goto %s", item->u1.list->u1.str);
294                 if ( item->u1.list->next )
295                         fprintf(fin,"|%s", item->u1.list->next->u1.str);
296                 if ( item->u1.list->next && item->u1.list->next->next )
297                         fprintf(fin,"|%s", item->u1.list->next->next->u1.str);
298                 fprintf(fin,"\n");
299                 break;
300                         
301         case PV_LABEL:
302                 fprintf(fin,"%s:\n", item->u1.str);
303                 break;
304                         
305         case PV_FOR:
306                 fprintf(fin,"for (%s; %s; %s)\n", item->u1.for_init, item->u2.for_test, item->u3.for_inc);
307                 print_pval_list(fin,item->u4.for_statements,depth+1);
308                 break;
309                         
310         case PV_WHILE:
311                 fprintf(fin,"while (%s)\n", item->u1.str);
312                 print_pval_list(fin,item->u2.statements,depth+1);
313                 break;
314                         
315         case PV_BREAK:
316                 fprintf(fin,"break;\n");
317                 break;
318                         
319         case PV_RETURN:
320                 fprintf(fin,"return;\n");
321                 break;
322                         
323         case PV_CONTINUE:
324                 fprintf(fin,"continue;\n");
325                 break;
326                         
327         case PV_RANDOM:
328         case PV_IFTIME:
329         case PV_IF:
330                 if ( item->type == PV_IFTIME ) {
331                         
332                         fprintf(fin,"ifTime ( %s|%s|%s|%s )\n", 
333                                         item->u1.list->u1.str, 
334                                         item->u1.list->next->u1.str, 
335                                         item->u1.list->next->next->u1.str, 
336                                         item->u1.list->next->next->next->u1.str
337                                         );
338                 } else if ( item->type == PV_RANDOM ) {
339                         fprintf(fin,"random ( %s )\n", item->u1.str );
340                 } else
341                         fprintf(fin,"if ( %s )\n", item->u1.str);
342                 if ( item->u2.statements && item->u2.statements->next ) {
343                         for (i=0; i<depth; i++) {
344                                 fprintf(fin,"\t"); /* depth == indentation */
345                         }
346                         fprintf(fin,"{\n");
347                         print_pval_list(fin,item->u2.statements,depth+1);
348                         for (i=0; i<depth; i++) {
349                                 fprintf(fin,"\t"); /* depth == indentation */
350                         }
351                         if ( item->u3.else_statements )
352                                 fprintf(fin,"}\n");
353                         else
354                                 fprintf(fin,"};\n");
355                 } else if (item->u2.statements ) {
356                         print_pval_list(fin,item->u2.statements,depth+1);
357                 } else {
358                         if (item->u3.else_statements )
359                                 fprintf(fin, " {} ");
360                         else
361                                 fprintf(fin, " {}; ");
362                 }
363                 if ( item->u3.else_statements ) {
364                         for (i=0; i<depth; i++) {
365                                 fprintf(fin,"\t"); /* depth == indentation */
366                         }
367                         fprintf(fin,"else\n");
368                         print_pval_list(fin,item->u3.else_statements, depth);
369                 }
370                 break;
371                         
372         case PV_SWITCH:
373                 fprintf(fin,"switch( %s ) {\n", item->u1.str);
374                 print_pval_list(fin,item->u2.statements,depth+1);
375                 for (i=0; i<depth; i++) {
376                         fprintf(fin,"\t"); /* depth == indentation */
377                 }
378                 fprintf(fin,"}\n");
379                 break;
380                         
381         case PV_EXTENSION:
382                 if ( item->u4.regexten )
383                         fprintf(fin, "regexten ");
384                 if ( item->u3.hints )
385                         fprintf(fin,"hints(%s) ", item->u3.hints);
386                 
387                 fprintf(fin,"%s => \n", item->u1.str);
388                 print_pval_list(fin,item->u2.statements,depth+1);
389                 break;
390                         
391         case PV_IGNOREPAT:
392                 fprintf(fin,"ignorepat => %s\n", item->u1.str);
393                 break;
394                         
395         case PV_GLOBALS:
396                 fprintf(fin,"globals {\n");
397                 print_pval_list(fin,item->u1.statements,depth+1);
398                 for (i=0; i<depth; i++) {
399                         fprintf(fin,"\t"); /* depth == indentation */
400                 }
401                 fprintf(fin,"}\n");
402                 break;
403         }
404 }
405
406 static void print_pval_list(FILE *fin, pval *item, int depth)
407 {
408         pval *i;
409         
410         for (i=item; i; i=i->next) {
411                 print_pval(fin, i, depth);
412         }
413 }
414
415 #if 0
416 static void ael2_print(char *fname, pval *tree)
417 {
418         FILE *fin = fopen(fname,"w");
419         if ( !fin ) {
420                 ast_log(LOG_ERROR, "Couldn't open %s for writing.\n", fname);
421                 return;
422         }
423         print_pval_list(fin, tree, 0);
424         fclose(fin);
425 }
426 #endif
427
428
429 /* EMPTY TEMPLATE FUNCS FOR AEL TRAVERSAL:  ============================================================================= */
430
431 void traverse_pval_template(pval *item, int depth);
432 void traverse_pval_item_template(pval *item, int depth);
433
434
435 void traverse_pval_item_template(pval *item, int depth)/* depth comes in handy for a pretty print (indentation),
436                                                                                                                   but you may not need it */
437 {
438         pval *lp;
439         
440         switch ( item->type ) {
441         case PV_WORD:
442                 /* fields: item->u1.str == string associated with this (word). */
443                 break;
444                 
445         case PV_MACRO:
446                 /* fields: item->u1.str     == name of macro
447                            item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
448                                    item->u2.arglist->u1.str  == argument
449                                    item->u2.arglist->next   == next arg
450
451                                    item->u3.macro_statements == pval list of statements in macro body.
452                 */
453                 for (lp=item->u2.arglist; lp; lp=lp->next) {
454                 
455                 }
456                 traverse_pval_item_template(item->u3.macro_statements,depth+1);
457                 break;
458                         
459         case PV_CONTEXT:
460                 /* fields: item->u1.str     == name of context
461                            item->u2.statements == pval list of statements in context body
462                                    item->u3.abstract == int 1 if an abstract keyword were present
463                 */
464                 traverse_pval_item_template(item->u2.statements,depth+1);
465                 break;
466                         
467         case PV_MACRO_CALL:
468                 /* fields: item->u1.str     == name of macro to call
469                            item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
470                                    item->u2.arglist->u1.str  == argument
471                                    item->u2.arglist->next   == next arg
472                 */
473                 for (lp=item->u2.arglist; lp; lp=lp->next) {
474                 }
475                 break;
476                         
477         case PV_APPLICATION_CALL:
478                 /* fields: item->u1.str     == name of application to call
479                            item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
480                                    item->u2.arglist->u1.str  == argument
481                                    item->u2.arglist->next   == next arg
482                 */
483                 for (lp=item->u2.arglist; lp; lp=lp->next) {
484                 }
485                 break;
486                         
487         case PV_CASE:
488                 /* fields: item->u1.str     == value of case
489                            item->u2.statements == pval list of statements under the case
490                 */
491                 traverse_pval_item_template(item->u2.statements,depth+1);
492                 break;
493                         
494         case PV_PATTERN:
495                 /* fields: item->u1.str     == value of case
496                            item->u2.statements == pval list of statements under the case
497                 */
498                 traverse_pval_item_template(item->u2.statements,depth+1);
499                 break;
500                         
501         case PV_DEFAULT:
502                 /* fields: 
503                            item->u2.statements == pval list of statements under the case
504                 */
505                 traverse_pval_item_template(item->u2.statements,depth+1);
506                 break;
507                         
508         case PV_CATCH:
509                 /* fields: item->u1.str     == name of extension to catch
510                            item->u2.statements == pval list of statements in context body
511                 */
512                 traverse_pval_item_template(item->u2.statements,depth+1);
513                 break;
514                         
515         case PV_SWITCHES:
516                 /* fields: item->u1.list     == pval list of PV_WORD elements, one per entry in the list
517                 */
518                 traverse_pval_item_template(item->u1.list,depth+1);
519                 break;
520                         
521         case PV_ESWITCHES:
522                 /* fields: item->u1.list     == pval list of PV_WORD elements, one per entry in the list
523                 */
524                 traverse_pval_item_template(item->u1.list,depth+1);
525                 break;
526                         
527         case PV_INCLUDES:
528                 /* fields: item->u1.list     == pval list of PV_WORD elements, one per entry in the list
529                            item->u2.arglist  == pval list of 4 PV_WORD elements for time values
530                 */
531                 traverse_pval_item_template(item->u1.list,depth+1);
532                 traverse_pval_item_template(item->u2.arglist,depth+1);
533                 break;
534                         
535         case PV_STATEMENTBLOCK:
536                 /* fields: item->u1.list     == pval list of statements in block, one per entry in the list
537                 */
538                 traverse_pval_item_template(item->u1.list,depth+1);
539                 break;
540                         
541         case PV_VARDEC:
542                 /* fields: item->u1.str     == variable name
543                            item->u2.val     == variable value to assign
544                 */
545                 break;
546                         
547         case PV_GOTO:
548                 /* fields: item->u1.list     == pval list of PV_WORD target names, up to 3, in order as given by user.
549                            item->u1.list->u1.str  == where the data on a PV_WORD will always be.
550                 */
551                 
552                 if ( item->u1.list->next )
553                         ;
554                 if ( item->u1.list->next && item->u1.list->next->next )
555                         ;
556                 
557                 break;
558                         
559         case PV_LABEL:
560                 /* fields: item->u1.str     == label name
561                 */
562                 break;
563                         
564         case PV_FOR:
565                 /* fields: item->u1.for_init     == a string containing the initalizer
566                            item->u2.for_test     == a string containing the loop test
567                            item->u3.for_inc      == a string containing the loop increment
568
569                                    item->u4.for_statements == a pval list of statements in the for ()
570                 */
571                 traverse_pval_item_template(item->u4.for_statements,depth+1);
572                 break;
573                         
574         case PV_WHILE:
575                 /* fields: item->u1.str        == the while conditional, as supplied by user
576
577                                    item->u2.statements == a pval list of statements in the while ()
578                 */
579                 traverse_pval_item_template(item->u2.statements,depth+1);
580                 break;
581                         
582         case PV_BREAK:
583                 /* fields: none
584                 */
585                 break;
586                         
587         case PV_RETURN:
588                 /* fields: none
589                 */
590                 break;
591                         
592         case PV_CONTINUE:
593                 /* fields: none
594                 */
595                 break;
596                         
597         case PV_IFTIME:
598                 /* fields: item->u1.list        == there are 4 linked PV_WORDs here.
599
600                                    item->u2.statements == a pval list of statements in the if ()
601                                    item->u3.else_statements == a pval list of statements in the else
602                                                                                            (could be zero)
603                 */
604                 traverse_pval_item_template(item->u2.statements,depth+1);
605                 if ( item->u3.else_statements ) {
606                         traverse_pval_item_template(item->u3.else_statements,depth+1);
607                 }
608                 break;
609                         
610         case PV_RANDOM:
611                 /* fields: item->u1.str        == the random number expression, as supplied by user
612
613                                    item->u2.statements == a pval list of statements in the if ()
614                                    item->u3.else_statements == a pval list of statements in the else
615                                                                                            (could be zero)
616                 */
617                 traverse_pval_item_template(item->u2.statements,depth+1);
618                 if ( item->u3.else_statements ) {
619                         traverse_pval_item_template(item->u3.else_statements,depth+1);
620                 }
621                 break;
622                         
623         case PV_IF:
624                 /* fields: item->u1.str        == the if conditional, as supplied by user
625
626                                    item->u2.statements == a pval list of statements in the if ()
627                                    item->u3.else_statements == a pval list of statements in the else
628                                                                                            (could be zero)
629                 */
630                 traverse_pval_item_template(item->u2.statements,depth+1);
631                 if ( item->u3.else_statements ) {
632                         traverse_pval_item_template(item->u3.else_statements,depth+1);
633                 }
634                 break;
635                         
636         case PV_SWITCH:
637                 /* fields: item->u1.str        == the switch expression
638
639                                    item->u2.statements == a pval list of statements in the switch, 
640                                                                                         (will be case statements, most likely!)
641                 */
642                 traverse_pval_item_template(item->u2.statements,depth+1);
643                 break;
644                         
645         case PV_EXTENSION:
646                 /* fields: item->u1.str        == the extension name, label, whatever it's called
647
648                                    item->u2.statements == a pval list of statements in the extension
649                                    item->u3.hints      == a char * hint argument
650                                    item->u4.regexten   == an int boolean. non-zero says that regexten was specified
651                 */
652                 traverse_pval_item_template(item->u2.statements,depth+1);
653                 break;
654                         
655         case PV_IGNOREPAT:
656                 /* fields: item->u1.str        == the ignorepat data
657                 */
658                 break;
659                         
660         case PV_GLOBALS:
661                 /* fields: item->u1.statements     == pval list of statements, usually vardecs
662                 */
663                 traverse_pval_item_template(item->u1.statements,depth+1);
664                 break;
665         }
666 }
667
668 void traverse_pval_template(pval *item, int depth) /* depth comes in handy for a pretty print (indentation),
669                                                                                                           but you may not need it */
670 {
671         pval *i;
672         
673         for (i=item; i; i=i->next) {
674                 traverse_pval_item_template(i, depth);
675         }
676 }
677
678
679 /* SEMANTIC CHECKING FOR AEL:  ============================================================================= */
680
681 /*   (not all that is syntactically legal is good! */
682
683
684
685 static int extension_matches(pval *here, const char *exten, const char *pattern)
686 {
687         int err1;
688         regex_t preg;
689         
690         /* simple case, they match exactly, the pattern and exten name */
691         if( !strcmp(pattern,exten) == 0 )
692                 return 1;
693         
694         if ( pattern[0] == '_' ) {
695                 char reg1[2000];
696                 const char *p;
697                 char *r = reg1;
698                 
699                 if ( strlen(pattern)*5 >= 2000 ) /* safety valve */ {
700                         ast_log(LOG_ERROR,"Error: The pattern %s is way too big. Pattern matching cancelled.\n",
701                                         pattern);
702                         return 0;
703                 }
704                 /* form a regular expression from the pattern, and then match it against exten */
705                 *r++ = '^'; /* what if the extension is a pattern ?? */
706                 *r++ = '_'; /* what if the extension is a pattern ?? */
707                 *r++ = '?';
708                 for (p=pattern+1; *p; p++) {
709                         switch ( *p ) {
710                         case 'X':
711                                 *r++ = '[';
712                                 *r++ = '0';
713                                 *r++ = '-';
714                                 *r++ = '9';
715                                 *r++ = 'X';
716                                 *r++ = ']';
717                                 break;
718                                 
719                         case 'Z':
720                                 *r++ = '[';
721                                 *r++ = '1';
722                                 *r++ = '-';
723                                 *r++ = '9';
724                                 *r++ = 'Z';
725                                 *r++ = ']';
726                                 break;
727                                 
728                         case 'N':
729                                 *r++ = '[';
730                                 *r++ = '2';
731                                 *r++ = '-';
732                                 *r++ = '9';
733                                 *r++ = 'N';
734                                 *r++ = ']';
735                                 break;
736                                 
737                         case '[':
738                                 while ( *p && *p != ']' ) {
739                                         *r++ = *p++;
740                                 }
741                                 if ( *p != ']') {
742                                         ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The extension pattern '%s' is missing a closing bracket \n",
743                                                         here->filename, here->startline, here->endline, pattern);
744                                 }
745                                 break;
746                                 
747                         case '.':
748                         case '!':
749                                 *r++ = '.';
750                                 *r++ = '*';
751                                 break;
752                         case '*':
753                                 *r++ = '\\';
754                                 *r++ = '*';
755                                 break;
756                         default:
757                                 *r++ = *p;
758                                 break;
759                                 
760                         }
761                 }
762                 *r++ = '$'; /* what if the extension is a pattern ?? */
763                 *r++ = *p++; /* put in the closing null */
764                 err1 = regcomp(&preg, reg1, REG_NOSUB|REG_EXTENDED);
765                 if ( err1 ) {
766                         char errmess[500];
767                         regerror(err1,&preg,errmess,sizeof(errmess));
768                         regfree(&preg);
769                         ast_log(LOG_WARNING, "Regcomp of %s failed, error code %d\n",
770                                         reg1, err1);
771                         return 0;
772                 }
773                 err1 = regexec(&preg, exten, 0, 0, 0);
774                 regfree(&preg);
775                 
776                 if ( err1 ) {
777                         /* ast_log(LOG_NOTICE,"*****************************[%d]Extension %s did not match %s(%s)\n",
778                            err1,exten, pattern, reg1); */
779                         return 0; /* no match */
780                 } else {
781                         /* ast_log(LOG_NOTICE,"*****************************Extension %s matched %s\n",
782                            exten, pattern); */
783                         return 1;
784                 }
785                 
786                 
787         } else {
788                 if ( strcmp(exten,pattern) == 0 ) {
789                         return 1;
790                 } else
791                         return 0;
792         }
793 }
794
795
796 static void check_expr2_input(pval *expr, char *str)
797 {
798         int spaces = strspn(str,"\t \n");
799         if ( !strncmp(str+spaces,"$[",2) ) {
800                 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The expression '%s' is redundantly wrapped in '$[ ]'. \n",
801                                 expr->filename, expr->startline, expr->endline, str);
802                 warns++;
803         }
804 }
805
806 static void check_timerange(pval *p)
807 {
808         char times[200];
809         char *e;
810         int s1, s2;
811         int e1, e2;
812
813
814         strncpy(times, p->u1.str, sizeof(times));
815         /* Star is all times */
816         if (ast_strlen_zero(times) || !strcmp(times, "*")) {
817                 return;
818         }
819         /* Otherwise expect a range */
820         e = strchr(times, '-');
821         if (!e) {
822                 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",
823                                 p->filename, p->startline, p->endline, times);
824                 warns++;
825                 return;
826         }
827         *e = '\0';
828         e++;
829         while (*e && !isdigit(*e)) 
830                 e++;
831         if (!*e) {
832                 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The time range format (%s) is missing the end time!\n",
833                                 p->filename, p->startline, p->endline, p->u1.str);
834                 warns++;
835         }
836         if (sscanf(times, "%d:%d", &s1, &s2) != 2) {
837                 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start time (%s) isn't quite right!\n",
838                                 p->filename, p->startline, p->endline, times);
839                 warns++;
840         }
841         if (sscanf(e, "%d:%d", &e1, &e2) != 2) {
842                 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end time (%s) isn't quite right!\n",
843                                 p->filename, p->startline, p->endline, times);
844                 warns++;
845         }
846
847         s1 = s1 * 30 + s2/2;
848         if ((s1 < 0) || (s1 >= 24*30)) {
849                 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start time (%s) is out of range!\n",
850                                 p->filename, p->startline, p->endline, times);
851                 warns++;
852         }
853         e1 = e1 * 30 + e2/2;
854         if ((e1 < 0) || (e1 >= 24*30)) {
855                 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end time (%s) is out of range!\n",
856                                 p->filename, p->startline, p->endline, e);
857                 warns++;
858         }
859         return;
860 }
861
862 static char *days[] =
863 {
864         "sun",
865         "mon",
866         "tue",
867         "wed",
868         "thu",
869         "fri",
870         "sat",
871 };
872
873 /*! \brief  get_dow: Get day of week */
874 static void check_dow(pval *DOW)
875 {
876         char dow[200];
877         char *c;
878         /* The following line is coincidence, really! */
879         int s, e;
880         
881         strncpy(dow,DOW->u1.str,sizeof(dow));
882  
883         /* Check for all days */
884         if (ast_strlen_zero(dow) || !strcmp(dow, "*"))
885                 return;
886         /* Get start and ending days */
887         c = strchr(dow, '-');
888         if (c) {
889                 *c = '\0';
890                 c++;
891         } else
892                 c = NULL;
893         /* Find the start */
894         s = 0;
895         while ((s < 7) && strcasecmp(dow, days[s])) s++;
896         if (s >= 7) {
897                 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",
898                                 DOW->filename, DOW->startline, DOW->endline, dow);
899                 warns++;
900         }
901         if (c) {
902                 e = 0;
903                 while ((e < 7) && strcasecmp(c, days[e])) e++;
904                 if (e >= 7) {
905                         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",
906                                         DOW->filename, DOW->startline, DOW->endline, c);
907                         warns++;
908                 }
909         } else
910                 e = s;
911 }
912
913 static void check_day(pval *DAY)
914 {
915         char day[200];
916         char *c;
917         /* The following line is coincidence, really! */
918         int s, e;
919
920         strncpy(day,DAY->u1.str,sizeof(day));
921         /* Check for all days */
922         if (ast_strlen_zero(day) || !strcmp(day, "*")) {
923                 return;
924         }
925         /* Get start and ending days */
926         c = strchr(day, '-');
927         if (c) {
928                 *c = '\0';
929                 c++;
930         }
931         /* Find the start */
932         if (sscanf(day, "%d", &s) != 1) {
933                 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start day of month (%s) must be a number!\n",
934                                 DAY->filename, DAY->startline, DAY->endline, day);
935                 warns++;
936         }
937         else if ((s < 1) || (s > 31)) {
938                 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",
939                                 DAY->filename, DAY->startline, DAY->endline, day);
940                 warns++;
941         }
942         s--;
943         if (c) {
944                 if (sscanf(c, "%d", &e) != 1) {
945                         ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end day of month (%s) must be a number!\n",
946                                         DAY->filename, DAY->startline, DAY->endline, c);
947                         warns++;
948                 }
949                 else if ((e < 1) || (e > 31)) {
950                         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",
951                                         DAY->filename, DAY->startline, DAY->endline, day);
952                         warns++;
953                 }
954                 e--;
955         } else
956                 e = s;
957 }
958
959 static char *months[] =
960 {
961         "jan",
962         "feb",
963         "mar",
964         "apr",
965         "may",
966         "jun",
967         "jul",
968         "aug",
969         "sep",
970         "oct",
971         "nov",
972         "dec",
973 };
974
975 static void check_month(pval *MON)
976 {
977         char mon[200];
978         char *c;
979         /* The following line is coincidence, really! */
980         int s, e;
981
982         strncpy(mon,MON->u1.str,sizeof(mon));
983         /* Check for all days */
984         if (ast_strlen_zero(mon) || !strcmp(mon, "*")) 
985                 return ;
986         /* Get start and ending days */
987         c = strchr(mon, '-');
988         if (c) {
989                 *c = '\0';
990                 c++;
991         }
992         /* Find the start */
993         s = 0;
994         while ((s < 12) && strcasecmp(mon, months[s])) s++;
995         if (s >= 12) {
996                 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start month (%s) must be a one of: 'jan', 'feb', ..., 'dec'!\n",
997                                 MON->filename, MON->startline, MON->endline, mon);
998                 warns++;
999         }
1000         if (c) {
1001                 e = 0;
1002                 while ((e < 12) && strcasecmp(mon, months[e])) e++;
1003                 if (e >= 12) {
1004                         ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end month (%s) must be a one of: 'jan', 'feb', ..., 'dec'!\n",
1005                                         MON->filename, MON->startline, MON->endline, c);
1006                         warns++;
1007                 }
1008         } else
1009                 e = s;
1010 }
1011
1012 /* general purpose goto finder */
1013
1014 static void check_label(pval *item)
1015 {
1016         /* basically, ensure that a label is not repeated in a context. Period.
1017            The method:  well, for each label, find the first label in the context
1018            with the same name. If it's not the current label, then throw an error. */
1019         struct pval *curr;
1020         
1021         /* printf("==== check_label:   ====\n"); */
1022         if( !current_extension )
1023                 curr = current_context;
1024         else
1025                 curr = current_extension;
1026         
1027         struct pval *x = find_first_label_in_current_context((char *)item->u1.str, curr);
1028         /* 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); */
1029         if( x && x != item )
1030         {
1031                 ast_log(LOG_ERROR,"Error: file %s, line %d-%d: Duplicate label %s! Previously defined at file %s, line %d.\n",
1032                                 item->filename, item->startline, item->endline, item->u1.str, x->filename, x->startline);
1033                 errs++;
1034         }
1035         /* printf("<<<<< check_label:   ====\n"); */
1036 }
1037
1038 static pval *get_goto_target(pval *item)
1039 {
1040         /* just one item-- the label should be in the current extension */
1041         pval *curr_ext = get_extension_or_contxt(item); /* containing exten, or macro */
1042         pval *curr_cont;
1043         
1044         if (item->u1.list && !item->u1.list->next && !strstr((item->u1.list)->u1.str,"${")) {
1045                 struct pval *x = find_label_in_current_extension((char*)((item->u1.list)->u1.str), curr_ext);
1046                         return x;
1047         }
1048
1049         curr_cont = get_contxt(item);
1050
1051         /* TWO items */
1052         if (item->u1.list->next && !item->u1.list->next->next) {
1053                 if (!strstr((item->u1.list)->u1.str,"${") 
1054                         && !strstr(item->u1.list->next->u1.str,"${") ) /* Don't try to match variables */ {
1055                         struct pval *x = find_label_in_current_context((char *)item->u1.list->u1.str, (char *)item->u1.list->next->u1.str, curr_cont);
1056                                 return x;
1057                 }
1058         }
1059         
1060         /* All 3 items! */
1061         if (item->u1.list->next && item->u1.list->next->next) {
1062                 /* all three */
1063                 pval *first = item->u1.list;
1064                 pval *second = item->u1.list->next;
1065                 pval *third = item->u1.list->next->next;
1066                 
1067                 if (!strstr((item->u1.list)->u1.str,"${") 
1068                         && !strstr(item->u1.list->next->u1.str,"${")
1069                         && !strstr(item->u1.list->next->next->u1.str,"${")) /* Don't try to match variables */ {
1070                         struct pval *x = find_label_in_current_db((char*)first->u1.str, (char*)second->u1.str, (char*)third->u1.str);
1071                         if (!x) {
1072
1073                                 struct pval *p3;
1074                                 struct pval *that_context = find_context(item->u1.list->u1.str);
1075                                 
1076                                 /* the target of the goto could be in an included context!! Fancy that!! */
1077                                 /* look for includes in the current context */
1078                                 if (that_context) {
1079                                         for (p3=that_context->u2.statements; p3; p3=p3->next) {
1080                                                 if (p3->type == PV_INCLUDES) {
1081                                                         struct pval *p4;
1082                                                         for (p4=p3->u1.list; p4; p4=p4->next) {
1083                                                                 /* for each context pointed to, find it, then find a context/label that matches the
1084                                                                    target here! */
1085                                                                 char *incl_context = p4->u1.str;
1086                                                                 /* find a matching context name */
1087                                                                 struct pval *that_other_context = find_context(incl_context);
1088                                                                 if (that_other_context) {
1089                                                                         struct pval *x3;
1090                                                                         x3 = find_label_in_current_context((char *)item->u1.list->next->u1.str, (char *)item->u1.list->next->next->u1.str, that_other_context);
1091                                                                         if (x3) {
1092                                                                                 return x3;
1093                                                                         }
1094                                                                 }
1095                                                         }
1096                                                 }
1097                                         }
1098                                 }
1099                         }
1100                         return x;
1101                 }
1102         }
1103         return 0;
1104 }
1105
1106 static void check_goto(pval *item)
1107 {
1108         /* check for the target of the goto-- does it exist? */
1109         if ( !(item->u1.list)->next && !(item->u1.list)->u1.str ) {
1110                 ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto:  empty label reference found!\n",
1111                                 item->filename, item->startline, item->endline);
1112                 errs++;
1113         }
1114         
1115         /* just one item-- the label should be in the current extension */
1116         
1117         if (item->u1.list && !item->u1.list->next && !strstr((item->u1.list)->u1.str,"${")) {
1118                 struct pval *x = find_label_in_current_extension((char*)((item->u1.list)->u1.str), current_extension? current_extension:current_context); /* if in macro, use current context instead */
1119                 /* printf("Called find_label_in_current_extension with arg %s; current_extension is %x: %d\n",
1120                    (char*)((item->u1.list)->u1.str), current_extension?current_extension:current_context, current_extension?current_extension->type:current_context->type); */
1121                 if (!x) {
1122                         ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto:  no label %s exists in the current extension!\n",
1123                                         item->filename, item->startline, item->endline, item->u1.list->u1.str);
1124                         errs++;
1125                 }
1126                 else
1127                         return;
1128         }
1129         
1130         /* TWO items */
1131         if (item->u1.list->next && !item->u1.list->next->next) {
1132                 /* two items */
1133                 /* printf("Calling find_label_in_current_context with args %s, %s\n",
1134                    (char*)((item->u1.list)->u1.str), (char *)item->u1.list->next->u1.str); */
1135                 if (!strstr((item->u1.list)->u1.str,"${") 
1136                         && !strstr(item->u1.list->next->u1.str,"${") ) /* Don't try to match variables */ {
1137                         struct pval *x = find_label_in_current_context((char *)item->u1.list->u1.str, (char *)item->u1.list->next->u1.str, current_context);
1138                         if (!x) {
1139                                 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",
1140                                                 item->filename, item->startline, item->endline, item->u1.list->u1.str, item->u1.list->next->u1.str );
1141                                 errs++;
1142                         }
1143                         else
1144                                 return;
1145                 }
1146         }
1147         
1148         /* All 3 items! */
1149         if (item->u1.list->next && item->u1.list->next->next) {
1150                 /* all three */
1151                 pval *first = item->u1.list;
1152                 pval *second = item->u1.list->next;
1153                 pval *third = item->u1.list->next->next;
1154                 
1155                 /* printf("Calling find_label_in_current_db with args %s, %s, %s\n",
1156                    (char*)first->u1.str, (char*)second->u1.str, (char*)third->u1.str); */
1157                 if (!strstr((item->u1.list)->u1.str,"${") 
1158                         && !strstr(item->u1.list->next->u1.str,"${")
1159                         && !strstr(item->u1.list->next->next->u1.str,"${")) /* Don't try to match variables */ {
1160                         struct pval *x = find_label_in_current_db((char*)first->u1.str, (char*)second->u1.str, (char*)third->u1.str);
1161                         if (!x) {
1162                                 struct pval *p3;
1163                                 struct pval *found = 0;
1164                                 struct pval *that_context = find_context(item->u1.list->u1.str);
1165                                 
1166                                 /* the target of the goto could be in an included context!! Fancy that!! */
1167                                 /* look for includes in the current context */
1168                                 if (that_context) {
1169                                         for (p3=that_context->u2.statements; p3; p3=p3->next) {
1170                                                 if (p3->type == PV_INCLUDES) {
1171                                                         struct pval *p4;
1172                                                         for (p4=p3->u1.list; p4; p4=p4->next) {
1173                                                                 /* for each context pointed to, find it, then find a context/label that matches the
1174                                                                    target here! */
1175                                                                 char *incl_context = p4->u1.str;
1176                                                                 /* find a matching context name */
1177                                                                 struct pval *that_other_context = find_context(incl_context);
1178                                                                 if (that_other_context) {
1179                                                                         struct pval *x3;
1180                                                                         x3 = find_label_in_current_context((char *)item->u1.list->next->u1.str, (char *)item->u1.list->next->next->u1.str, that_other_context);
1181                                                                         if (x3) {
1182                                                                                 found = x3;
1183                                                                                 break;
1184                                                                         }
1185                                                                 }
1186                                                         }
1187                                                 }
1188                                         }
1189                                         if (!found) {
1190                                                 ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto:  no label %s|%s exists in the context %s or its inclusions!\n",
1191                                                                 item->filename, item->startline, item->endline, item->u1.list->next->u1.str, item->u1.list->next->next->u1.str, item->u1.list->u1.str );
1192                                                 errs++;
1193                                         }
1194                                 } else {
1195                                         ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto:  no context %s could be found that matches the goto target!\n",
1196                                                         item->filename, item->startline, item->endline, item->u1.list->u1.str);
1197                                         errs++;
1198                                 }
1199                         }
1200                 }
1201         }
1202 }
1203         
1204
1205 static void find_pval_goto_item(pval *item, int lev)
1206 {
1207         struct pval *p4;
1208         if (lev>100) {
1209                 ast_log(LOG_ERROR,"find_pval_goto in infinite loop!\n\n");
1210                 return;
1211         }
1212         
1213         switch ( item->type ) {
1214         case PV_MACRO:
1215                 /* fields: item->u1.str     == name of macro
1216                            item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
1217                                    item->u2.arglist->u1.str  == argument
1218                                    item->u2.arglist->next   == next arg
1219
1220                                    item->u3.macro_statements == pval list of statements in macro body.
1221                 */
1222                         
1223                 /* printf("Descending into matching macro %s\n", match_context); */
1224                 find_pval_gotos(item->u3.macro_statements,lev+1); /* if we're just searching for a context, don't bother descending into them */
1225                 
1226                 break;
1227                         
1228         case PV_CONTEXT:
1229                 /* fields: item->u1.str     == name of context
1230                            item->u2.statements == pval list of statements in context body
1231                                    item->u3.abstract == int 1 if an abstract keyword were present
1232                 */
1233                 break;
1234
1235         case PV_CASE:
1236                 /* fields: item->u1.str     == value of case
1237                            item->u2.statements == pval list of statements under the case
1238                 */
1239                 find_pval_gotos(item->u2.statements,lev+1);
1240                 break;
1241                         
1242         case PV_PATTERN:
1243                 /* fields: item->u1.str     == value of case
1244                            item->u2.statements == pval list of statements under the case
1245                 */
1246                 find_pval_gotos(item->u2.statements,lev+1);
1247                 break;
1248                         
1249         case PV_DEFAULT:
1250                 /* fields: 
1251                            item->u2.statements == pval list of statements under the case
1252                 */
1253                 find_pval_gotos(item->u2.statements,lev+1);
1254                 break;
1255                         
1256         case PV_CATCH:
1257                 /* fields: item->u1.str     == name of extension to catch
1258                            item->u2.statements == pval list of statements in context body
1259                 */
1260                 find_pval_gotos(item->u2.statements,lev+1);
1261                 break;
1262                         
1263         case PV_STATEMENTBLOCK:
1264                 /* fields: item->u1.list     == pval list of statements in block, one per entry in the list
1265                 */
1266                 find_pval_gotos(item->u1.list,lev+1);
1267                 break;
1268                         
1269         case PV_GOTO:
1270                 /* fields: item->u1.list     == pval list of PV_WORD target names, up to 3, in order as given by user.
1271                            item->u1.list->u1.str  == where the data on a PV_WORD will always be.
1272                 */
1273                 check_goto(item);  /* THE WHOLE FUNCTION OF THIS ENTIRE ROUTINE!!!! */
1274                 break;
1275                         
1276         case PV_INCLUDES:
1277                 /* fields: item->u1.list     == pval list of PV_WORD elements, one per entry in the list
1278                 */
1279                 for (p4=item->u1.list; p4; p4=p4->next) {
1280                         /* for each context pointed to, find it, then find a context/label that matches the
1281                            target here! */
1282                         char *incl_context = p4->u1.str;
1283                         /* find a matching context name */
1284                         struct pval *that_context = find_context(incl_context);
1285                         if (that_context) {
1286                                 find_pval_gotos(that_context,lev+1); /* keep working up the includes */
1287                         }
1288                 }
1289                 break;
1290                 
1291         case PV_FOR:
1292                 /* fields: item->u1.for_init     == a string containing the initalizer
1293                            item->u2.for_test     == a string containing the loop test
1294                            item->u3.for_inc      == a string containing the loop increment
1295
1296                                    item->u4.for_statements == a pval list of statements in the for ()
1297                 */
1298                 find_pval_gotos(item->u4.for_statements,lev+1);
1299                 break;
1300                         
1301         case PV_WHILE:
1302                 /* fields: item->u1.str        == the while conditional, as supplied by user
1303
1304                                    item->u2.statements == a pval list of statements in the while ()
1305                 */
1306                 find_pval_gotos(item->u2.statements,lev+1);
1307                 break;
1308                         
1309         case PV_RANDOM:
1310                 /* fields: item->u1.str        == the random number expression, as supplied by user
1311
1312                                    item->u2.statements == a pval list of statements in the if ()
1313                                    item->u3.else_statements == a pval list of statements in the else
1314                                                                                            (could be zero)
1315                  fall thru to PV_IF */
1316                 
1317         case PV_IFTIME:
1318                 /* fields: item->u1.list        == the time values, 4 of them, as PV_WORD structs in a list
1319
1320                                    item->u2.statements == a pval list of statements in the if ()
1321                                    item->u3.else_statements == a pval list of statements in the else
1322                                                                                            (could be zero)
1323                 fall thru to PV_IF*/
1324         case PV_IF:
1325                 /* fields: item->u1.str        == the if conditional, as supplied by user
1326
1327                                    item->u2.statements == a pval list of statements in the if ()
1328                                    item->u3.else_statements == a pval list of statements in the else
1329                                                                                            (could be zero)
1330                 */
1331                 find_pval_gotos(item->u2.statements,lev+1);
1332
1333                 if (item->u3.else_statements) {
1334                         find_pval_gotos(item->u3.else_statements,lev+1);
1335                 }
1336                 break;
1337                         
1338         case PV_SWITCH:
1339                 /* fields: item->u1.str        == the switch expression
1340
1341                                    item->u2.statements == a pval list of statements in the switch, 
1342                                                                                         (will be case statements, most likely!)
1343                 */
1344                 find_pval_gotos(item->u3.else_statements,lev+1);
1345                 break;
1346                         
1347         case PV_EXTENSION:
1348                 /* fields: item->u1.str        == the extension name, label, whatever it's called
1349
1350                                    item->u2.statements == a pval list of statements in the extension
1351                                    item->u3.hints      == a char * hint argument
1352                                    item->u4.regexten   == an int boolean. non-zero says that regexten was specified
1353                 */
1354
1355                 find_pval_gotos(item->u2.statements,lev+1);
1356                 break;
1357
1358         default:
1359                 break;
1360         }
1361 }
1362
1363 static void find_pval_gotos(pval *item,int lev)
1364 {
1365         pval *i;
1366
1367         for (i=item; i; i=i->next) {
1368                 
1369                 find_pval_goto_item(i, lev);
1370         }
1371 }
1372
1373
1374
1375 /* general purpose label finder */
1376 static struct pval *match_pval_item(pval *item)
1377 {
1378         pval *x;
1379         
1380         switch ( item->type ) {
1381         case PV_MACRO:
1382                 /* fields: item->u1.str     == name of macro
1383                            item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
1384                                    item->u2.arglist->u1.str  == argument
1385                                    item->u2.arglist->next   == next arg
1386
1387                                    item->u3.macro_statements == pval list of statements in macro body.
1388                 */
1389                 /* printf("    matching in MACRO %s, match_context=%s; retoncontmtch=%d; \n", item->u1.str, match_context, return_on_context_match); */
1390                 if (!strcmp(match_context,"*") || !strcmp(item->u1.str, match_context)) {
1391                         
1392                         /* printf("MACRO: match context is: %s\n", match_context); */
1393                         
1394                         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 */ {
1395                                 /* printf("Returning on matching macro %s\n", match_context); */
1396                                 return item;
1397                         }
1398                         
1399                         
1400                         if (!return_on_context_match) {
1401                                 /* printf("Descending into matching macro %s/%s\n", match_context, item->u1.str); */
1402                                 if ((x=match_pval(item->u3.macro_statements)))  {
1403                                         /* printf("Responded with pval match %x\n", x); */
1404                                         return x;
1405                                 }
1406                         }
1407                 } else {
1408                         /* printf("Skipping context/macro %s\n", item->u1.str); */
1409                 }
1410                 
1411                 break;
1412                         
1413         case PV_CONTEXT:
1414                 /* fields: item->u1.str     == name of context
1415                            item->u2.statements == pval list of statements in context body
1416                                    item->u3.abstract == int 1 if an abstract keyword were present
1417                 */
1418                 /* printf("    matching in CONTEXT\n"); */
1419                 if (!strcmp(match_context,"*") || !strcmp(item->u1.str, match_context)) {
1420                         if (return_on_context_match && !strcmp(item->u1.str, match_context)) {
1421                                 /* printf("Returning on matching context %s\n", match_context); */
1422                                 /* printf("non-CONTEXT: Responded with pval match %x\n", x); */
1423                                 return item;
1424                         }
1425                         
1426                         if (!return_on_context_match ) {
1427                                 /* printf("Descending into matching context %s\n", match_context); */
1428                                 if ((x=match_pval(item->u2.statements))) /* if we're just searching for a context, don't bother descending into them */ {
1429                                         /* printf("CONTEXT: Responded with pval match %x\n", x); */
1430                                         return x;
1431                                 }
1432                         }
1433                 } else {
1434                         /* printf("Skipping context/macro %s\n", item->u1.str); */
1435                 }
1436                 break;
1437
1438         case PV_CASE:
1439                 /* fields: item->u1.str     == value of case
1440                            item->u2.statements == pval list of statements under the case
1441                 */
1442                 /* printf("    matching in CASE\n"); */
1443                 if ((x=match_pval(item->u2.statements))) {
1444                         /* printf("CASE: Responded with pval match %x\n", x); */
1445                         return x;
1446                 }
1447                 break;
1448                         
1449         case PV_PATTERN:
1450                 /* fields: item->u1.str     == value of case
1451                            item->u2.statements == pval list of statements under the case
1452                 */
1453                 /* printf("    matching in PATTERN\n"); */
1454                 if ((x=match_pval(item->u2.statements))) {
1455                         /* printf("PATTERN: Responded with pval match %x\n", x); */
1456                         return x;
1457                 }
1458                 break;
1459                         
1460         case PV_DEFAULT:
1461                 /* fields: 
1462                            item->u2.statements == pval list of statements under the case
1463                 */
1464                 /* printf("    matching in DEFAULT\n"); */
1465                 if ((x=match_pval(item->u2.statements))) {
1466                         /* printf("DEFAULT: Responded with pval match %x\n", x); */
1467                         return x;
1468                 }
1469                 break;
1470                         
1471         case PV_CATCH:
1472                 /* fields: item->u1.str     == name of extension to catch
1473                            item->u2.statements == pval list of statements in context body
1474                 */
1475                 /* printf("    matching in CATCH\n"); */
1476                 if ((x=match_pval(item->u2.statements))) {
1477                         /* printf("CATCH: Responded with pval match %x\n", x); */
1478                         return x;
1479                 }
1480                 break;
1481                         
1482         case PV_STATEMENTBLOCK:
1483                 /* fields: item->u1.list     == pval list of statements in block, one per entry in the list
1484                 */
1485                 /* printf("    matching in STATEMENTBLOCK\n"); */
1486                 if ((x=match_pval(item->u1.list))) {
1487                         /* printf("STATEMENTBLOCK: Responded with pval match %x\n", x); */
1488                         return x;
1489                 }
1490                 break;
1491                         
1492         case PV_LABEL:
1493                 /* fields: item->u1.str     == label name
1494                 */
1495                 /* printf("PV_LABEL %s (cont=%s, exten=%s\n", 
1496                    item->u1.str, current_context->u1.str, (current_extension?current_extension->u1.str:"<macro>"));*/
1497                 
1498                 if (count_labels) {
1499                         if (!strcmp(match_label, item->u1.str)) {
1500                                 label_count++;
1501                                 last_matched_label = item;
1502                         }
1503                         
1504                 } else {
1505                         if (!strcmp(match_label, item->u1.str)) {
1506                                 /* printf("LABEL: Responded with pval match %x\n", x); */
1507                                 return item;
1508                         }
1509                 }
1510                 break;
1511                         
1512         case PV_FOR:
1513                 /* fields: item->u1.for_init     == a string containing the initalizer
1514                            item->u2.for_test     == a string containing the loop test
1515                            item->u3.for_inc      == a string containing the loop increment
1516
1517                                    item->u4.for_statements == a pval list of statements in the for ()
1518                 */
1519                 /* printf("    matching in FOR\n"); */
1520                 if ((x=match_pval(item->u4.for_statements))) {
1521                         /* printf("FOR: Responded with pval match %x\n", x);*/
1522                         return x;
1523                 }
1524                 break;
1525                         
1526         case PV_WHILE:
1527                 /* fields: item->u1.str        == the while conditional, as supplied by user
1528
1529                                    item->u2.statements == a pval list of statements in the while ()
1530                 */
1531                 /* printf("    matching in WHILE\n"); */
1532                 if ((x=match_pval(item->u2.statements))) {
1533                         /* printf("WHILE: Responded with pval match %x\n", x); */
1534                         return x;
1535                 }
1536                 break;
1537                         
1538         case PV_RANDOM:
1539                 /* fields: item->u1.str        == the random number expression, as supplied by user
1540
1541                                    item->u2.statements == a pval list of statements in the if ()
1542                                    item->u3.else_statements == a pval list of statements in the else
1543                                                                                            (could be zero)
1544                  fall thru to PV_IF */
1545                 
1546         case PV_IFTIME:
1547                 /* fields: item->u1.list        == the time values, 4 of them, as PV_WORD structs in a list
1548
1549                                    item->u2.statements == a pval list of statements in the if ()
1550                                    item->u3.else_statements == a pval list of statements in the else
1551                                                                                            (could be zero)
1552                 fall thru to PV_IF*/
1553         case PV_IF:
1554                 /* fields: item->u1.str        == the if conditional, as supplied by user
1555
1556                                    item->u2.statements == a pval list of statements in the if ()
1557                                    item->u3.else_statements == a pval list of statements in the else
1558                                                                                            (could be zero)
1559                 */
1560                 /* printf("    matching in IF/IFTIME/RANDOM\n"); */
1561                 if ((x=match_pval(item->u2.statements))) {
1562                         return x;
1563                 }
1564                 if (item->u3.else_statements) {
1565                         if ((x=match_pval(item->u3.else_statements))) {
1566                                 /* printf("IF/IFTIME/RANDOM: Responded with pval match %x\n", x); */
1567                                 return x;
1568                         }
1569                 }
1570                 break;
1571                         
1572         case PV_SWITCH:
1573                 /* fields: item->u1.str        == the switch expression
1574
1575                                    item->u2.statements == a pval list of statements in the switch, 
1576                                                                                         (will be case statements, most likely!)
1577                 */
1578                 /* printf("    matching in SWITCH\n"); */
1579                 if ((x=match_pval(item->u2.statements))) {
1580                         /* printf("SWITCH: Responded with pval match %x\n", x); */
1581                         return x;
1582                 }
1583                 break;
1584                         
1585         case PV_EXTENSION:
1586                 /* fields: item->u1.str        == the extension name, label, whatever it's called
1587
1588                                    item->u2.statements == a pval list of statements in the extension
1589                                    item->u3.hints      == a char * hint argument
1590                                    item->u4.regexten   == an int boolean. non-zero says that regexten was specified
1591                 */
1592                 /* printf("    matching in EXTENSION\n"); */
1593                 if (!strcmp(match_exten,"*") || extension_matches(item, match_exten, item->u1.str) ) {
1594                         /* printf("Descending into matching exten %s => %s\n", match_exten, item->u1.str); */
1595                         if (strcmp(match_label,"1") == 0) {
1596                                 if (item->u2.statements) {
1597                                         struct pval *p5 = item->u2.statements;
1598                                         while (p5 && p5->type == PV_LABEL)  /* find the first non-label statement in this context. If it exists, there's a "1" */
1599                                                 p5 = p5->next;
1600                                         if (p5)
1601                                                 return p5;
1602                                         else
1603                                                 return 0;
1604                                 }
1605                                 else
1606                                         return 0;
1607                         }
1608
1609                         if ((x=match_pval(item->u2.statements))) {
1610                                 /* printf("EXTENSION: Responded with pval match %x\n", x); */
1611                                 return x;
1612                         }
1613                 } else {
1614                         /* printf("Skipping exten %s\n", item->u1.str); */
1615                 }
1616                 break;
1617         default:
1618                 /* printf("    matching in default = %d\n", item->type); */
1619                 break;
1620         }
1621         return 0;
1622 }
1623
1624 struct pval *match_pval(pval *item)
1625 {
1626         pval *i;
1627
1628         for (i=item; i; i=i->next) {
1629                 pval *x;
1630                 /* printf("   -- match pval: item %d\n", i->type); */
1631                 
1632                 if ((x = match_pval_item(i))) {
1633                         /* printf("match_pval: returning x=%x\n", (int)x); */
1634                         return x; /* cut the search short */
1635                 }
1636         }
1637         return 0;
1638 }
1639
1640 #if 0
1641 int count_labels_in_current_context(char *label)
1642 {
1643         label_count = 0;
1644         count_labels = 1;
1645         return_on_context_match = 0;
1646         match_pval(current_context->u2.statements);
1647         
1648         return label_count;
1649 }
1650 #endif
1651
1652 struct pval *find_first_label_in_current_context(char *label, pval *curr_cont)
1653 {
1654         /* printf("  --- Got args %s, %s\n", exten, label); */
1655         struct pval *ret;
1656         struct pval *p3;
1657         
1658         count_labels = 0;
1659         return_on_context_match = 0;
1660         match_context = "*";
1661         match_exten = "*";
1662         match_label = label;
1663         
1664         ret =  match_pval(curr_cont);
1665         if (ret)
1666                 return ret;
1667                                         
1668         /* the target of the goto could be in an included context!! Fancy that!! */
1669         /* look for includes in the current context */
1670         for (p3=curr_cont->u2.statements; p3; p3=p3->next) {
1671                 if (p3->type == PV_INCLUDES) {
1672                         struct pval *p4;
1673                         for (p4=p3->u1.list; p4; p4=p4->next) {
1674                                 /* for each context pointed to, find it, then find a context/label that matches the
1675                                    target here! */
1676                                 char *incl_context = p4->u1.str;
1677                                 /* find a matching context name */
1678                                 struct pval *that_context = find_context(incl_context);
1679                                 if (that_context) {
1680                                         struct pval *x3;
1681                                         x3 = find_first_label_in_current_context(label, that_context);
1682                                         if (x3) {
1683                                                 return x3;
1684                                         }
1685                                 }
1686                         }
1687                 }
1688         }
1689         return 0;
1690 }
1691
1692 struct pval *find_label_in_current_context(char *exten, char *label, pval *curr_cont)
1693 {
1694         /* printf("  --- Got args %s, %s\n", exten, label); */
1695         struct pval *ret;
1696         struct pval *p3;
1697         
1698         count_labels = 0;
1699         return_on_context_match = 0;
1700         match_context = "*";
1701         match_exten = exten;
1702         match_label = label;
1703         ret =  match_pval(curr_cont->u2.statements);
1704         if (ret)
1705                 return ret;
1706                                         
1707         /* the target of the goto could be in an included context!! Fancy that!! */
1708         /* look for includes in the current context */
1709         for (p3=curr_cont->u2.statements; p3; p3=p3->next) {
1710                 if (p3->type == PV_INCLUDES) {
1711                         struct pval *p4;
1712                         for (p4=p3->u1.list; p4; p4=p4->next) {
1713                                 /* for each context pointed to, find it, then find a context/label that matches the
1714                                    target here! */
1715                                 char *incl_context = p4->u1.str;
1716                                 /* find a matching context name */
1717                                 struct pval *that_context = find_context(incl_context);
1718                                 if (that_context) {
1719                                         struct pval *x3;
1720                                         x3 = find_label_in_current_context(exten, label, that_context);
1721                                         if (x3) {
1722                                                 return x3;
1723                                         }
1724                                 }
1725                         }
1726                 }
1727         }
1728         return 0;
1729 }
1730
1731 static struct pval *find_label_in_current_extension(const char *label, pval *curr_ext)
1732 {
1733         /* printf("  --- Got args %s\n", label); */
1734         count_labels = 0;
1735         return_on_context_match = 0;
1736         match_context = "*";
1737         match_exten = "*";
1738         match_label = label;
1739         return match_pval(curr_ext);
1740 }
1741
1742 static struct pval *find_label_in_current_db(const char *context, const char *exten, const char *label)
1743 {
1744         /* printf("  --- Got args %s, %s, %s\n", context, exten, label); */
1745         count_labels = 0;
1746         return_on_context_match = 0;
1747
1748         match_context = context;
1749         match_exten = exten;
1750         match_label = label;
1751         
1752         return match_pval(current_db);
1753 }
1754
1755
1756 struct pval *find_macro(char *name)
1757 {
1758         return_on_context_match = 1;
1759         count_labels = 0;
1760         match_context = name;
1761         match_exten = "*";  /* don't really need to set these, shouldn't be reached */
1762         match_label = "*";
1763         return match_pval(current_db);
1764 }
1765
1766 struct pval *find_context(char *name)
1767 {
1768         return_on_context_match = 1;
1769         count_labels = 0;
1770         match_context = name;
1771         match_exten = "*";  /* don't really need to set these, shouldn't be reached */
1772         match_label = "*";
1773         return match_pval(current_db);
1774 }
1775
1776 int is_float(char *arg )
1777 {
1778         char *s;
1779         for (s=arg; *s; s++) {
1780                 if (*s != '.' && (*s < '0' || *s > '9'))
1781                         return 0;
1782         }
1783         return 1;
1784 }
1785 int is_int(char *arg )
1786 {
1787         char *s;
1788         for (s=arg; *s; s++) {
1789                 if (*s < '0' || *s > '9')
1790                         return 0;
1791         }
1792         return 1;
1793 }
1794 int is_empty(char *arg)
1795 {
1796         if (!arg)
1797                 return 1;
1798         if (*arg == 0)
1799                 return 1;
1800         while (*arg) {
1801                 if (*arg != ' ' && *arg != '\t')
1802                         return 0;
1803                 arg++;
1804         }
1805         return 1;
1806 }
1807
1808 #ifdef AAL_ARGCHECK
1809 int option_matches_j( struct argdesc *should, pval *is, struct argapp *app)
1810 {
1811         struct argchoice *ac;
1812         char opcop[400],*q,*p;
1813         
1814         switch (should->dtype) {
1815         case ARGD_OPTIONSET:
1816                 if ( strstr(is->u1.str,"${") )
1817                         return 0;  /* no checking anything if there's a var reference in there! */
1818                         
1819                 strncpy(opcop,is->u1.str,sizeof(opcop));
1820
1821                 for (q=opcop;*q;q++) { /* erase the innards of X(innard) type arguments, so we don't get confused later */
1822                         if ( *q == '(' ) {
1823                                 p = q+1;
1824                                 while (*p && *p != ')' )
1825                                         *p++ = '+';
1826                                 q = p+1;
1827                         }
1828                 }
1829                 
1830                 for (ac=app->opts; ac; ac=ac->next) {
1831                         if (strlen(ac->name)>1  && strchr(ac->name,'(') == 0 && strcmp(ac->name,is->u1.str) == 0) /* multichar option, no parens, and a match? */
1832                                 return 0;
1833                 }
1834                 for (ac=app->opts; ac; ac=ac->next) {
1835                         if (strlen(ac->name)==1  ||  strchr(ac->name,'(')) {
1836                                 char *p = strchr(opcop,ac->name[0]);  /* wipe out all matched options in the user-supplied string */
1837                                 
1838                                 if (p && *p == 'j') {
1839                                         ast_log(LOG_ERROR, "Error: file %s, line %d-%d: The j option in the %s application call is not appropriate for AEL!\n",
1840                                                         is->filename, is->startline, is->endline, app->name);
1841                                         errs++;
1842                                 }
1843                                 
1844                                 if (p) {
1845                                         *p = '+';
1846                                         if (ac->name[1] == '(') {
1847                                                 if (*(p+1) != '(') {
1848                                                         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",
1849                                                                         is->filename, is->startline, is->endline, ac->name[0], app->name);
1850                                                         warns++;
1851                                                 }
1852                                         }
1853                                 }
1854                         }
1855                 }
1856                 for (q=opcop; *q; q++) {
1857                         if ( *q != '+' && *q != '(' && *q != ')') {
1858                                 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",
1859                                                 is->filename, is->startline, is->endline, *q, app->name);
1860                                 warns++;
1861                         }
1862                 }
1863                 return 1;
1864                 break;
1865         default:
1866                 return 0;
1867         }
1868         
1869 }
1870
1871 int option_matches( struct argdesc *should, pval *is, struct argapp *app)
1872 {
1873         struct argchoice *ac;
1874         char opcop[400];
1875         
1876         switch (should->dtype) {
1877         case ARGD_STRING:
1878                 if (is_empty(is->u1.str) && should->type == ARGD_REQUIRED)
1879                         return 0;
1880                 if (is->u1.str && strlen(is->u1.str) > 0) /* most will match */
1881                         return 1;
1882                 break;
1883                 
1884         case ARGD_INT:
1885                 if (is_int(is->u1.str))
1886                         return 1;
1887                 else
1888                         return 0;
1889                 break;
1890                 
1891         case ARGD_FLOAT:
1892                 if (is_float(is->u1.str))
1893                         return 1;
1894                 else
1895                         return 0;
1896                 break;
1897                 
1898         case ARGD_ENUM:
1899                 if( !is->u1.str || strlen(is->u1.str) == 0 )
1900                         return 1; /* a null arg in the call will match an enum, I guess! */
1901                 for (ac=should->choices; ac; ac=ac->next) {
1902                         if (strcmp(ac->name,is->u1.str) == 0)
1903                                 return 1;
1904                 }
1905                 return 0;
1906                 break;
1907                 
1908         case ARGD_OPTIONSET:
1909                 strncpy(opcop,is->u1.str,sizeof(opcop));
1910                 
1911                 for (ac=app->opts; ac; ac=ac->next) {
1912                         if (strlen(ac->name)>1  && strchr(ac->name,'(') == 0 && strcmp(ac->name,is->u1.str) == 0) /* multichar option, no parens, and a match? */
1913                                 return 1;
1914                 }
1915                 for (ac=app->opts; ac; ac=ac->next) {
1916                         if (strlen(ac->name)==1  ||  strchr(ac->name,'(')) {
1917                                 char *p = strchr(opcop,ac->name[0]);  /* wipe out all matched options in the user-supplied string */
1918                                 
1919                                 if (p) {
1920                                         *p = '+';
1921                                         if (ac->name[1] == '(') {
1922                                                 if (*(p+1) == '(') {
1923                                                         char *q = p+1;
1924                                                         while (*q && *q != ')') {
1925                                                                 *q++ = '+';
1926                                                         }
1927                                                         *q = '+';
1928                                                 }
1929                                         }
1930                                 }
1931                         }
1932                 }
1933                 return 1;
1934                 break;
1935         case ARGD_VARARG:
1936                 return 1; /* matches anything */
1937                 break;
1938         }
1939         return 1; /* unless some for-sure match or non-match returns, then it must be close enough ... */
1940 }
1941 #endif
1942
1943 int check_app_args(pval* appcall, pval *arglist, struct argapp *app)
1944 {
1945 #ifdef AAL_ARGCHECK
1946         struct argdesc *ad = app->args;
1947         pval *pa;
1948         int z;
1949         
1950         for (pa = arglist; pa; pa=pa->next) {
1951                 if (!ad) {
1952                         ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: Extra argument %s not in application call to %s !\n",
1953                                         arglist->filename, arglist->startline, arglist->endline, pa->u1.str, app->name);
1954                         warns++;
1955                         return 1;
1956                 } else {
1957                         /* find the first entry in the ad list that will match */
1958                         do {
1959                                 if ( ad->dtype == ARGD_VARARG ) /* once we hit the VARARG, all bets are off. Discontinue the comparisons */
1960                                         break;
1961                                 
1962                                 z= option_matches( ad, pa, app);
1963                                 if (!z) {
1964                                         if ( !arglist )
1965                                                 arglist=appcall;
1966                                         
1967                                         if (ad->type == ARGD_REQUIRED) {
1968                                                 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: Required argument %s not in application call to %s !\n",
1969                                                                 arglist->filename, arglist->startline, arglist->endline, ad->dtype==ARGD_OPTIONSET?"options":ad->name, app->name);
1970                                                 warns++;
1971                                                 return 1;
1972                                         }
1973                                 } else if (z && ad->dtype == ARGD_OPTIONSET) {
1974                                         option_matches_j( ad, pa, app);
1975                                 }
1976                                 ad = ad->next;
1977                         } while (ad && !z);
1978                 }
1979         }
1980         /* any app nodes left, that are not optional? */
1981         for ( ; ad; ad=ad->next) {
1982                 if (ad->type == ARGD_REQUIRED && ad->dtype != ARGD_VARARG) {
1983                         if ( !arglist ) 
1984                                 arglist=appcall;
1985                         ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: Required argument %s not in application call to %s !\n",
1986                                         arglist->filename, arglist->startline, arglist->endline, ad->dtype==ARGD_OPTIONSET?"options":ad->name, app->name);
1987                         warns++;
1988                         return 1;
1989                 }
1990         }
1991         return 0;
1992 #else
1993         return 0;
1994 #endif
1995 }
1996
1997 void check_switch_expr(pval *item, struct argapp *apps)
1998 {
1999 #ifdef AAL_ARGCHECK
2000         /* get and clean the variable name */
2001         char buff1[1024],*p;
2002         struct argapp *a,*a2;
2003         struct appsetvar *v,*v2;
2004         struct argchoice *c;
2005         pval *t;
2006         
2007         p = item->u1.str;
2008         while (p && *p && (*p == ' ' || *p == '\t' || *p == '$' || *p == '{' ) )
2009                 p++;
2010         
2011         strncpy(buff1,p,sizeof(buff1));
2012         while (strlen(buff1) > 0 && ( buff1[strlen(buff1)-1] == '}' || buff1[strlen(buff1)-1] == ' ' || buff1[strlen(buff1)-1] == '\t'))
2013                 buff1[strlen(buff1)-1] = 0;
2014         /* buff1 now contains the variable name */
2015         v = 0;
2016         for (a=apps; a; a=a->next) {
2017                 for (v=a->setvars;v;v=v->next) {
2018                         if (strcmp(v->name,buff1) == 0) {
2019                                 break;
2020                         }
2021                 }
2022                 if ( v )
2023                         break;
2024         }
2025         if (v && v->vals) {
2026                 /* we have a match, to a variable that has a set of determined values */
2027                 int def= 0;
2028                 int pat = 0;
2029                 int f1 = 0;
2030                 
2031                 /* first of all, does this switch have a default case ? */
2032                 for (t=item->u2.statements; t; t=t->next) {
2033                         if (t->type == PV_DEFAULT) {
2034                                 def =1;
2035                                 break;
2036                         }
2037                         if (t->type == PV_PATTERN) {
2038                                 pat++;
2039                         }
2040                 }
2041                 if (def || pat) /* nothing to check. All cases accounted for! */
2042                         return;
2043                 for (c=v->vals; c; c=c->next) {
2044                         f1 = 0;
2045                         for (t=item->u2.statements; t; t=t->next) {
2046                                 if (t->type == PV_CASE || t->type == PV_PATTERN) {
2047                                         if (!strcmp(t->u1.str,c->name)) {
2048                                                 f1 = 1;
2049                                                 break;
2050                                         }
2051                                 }
2052                         }
2053                         if (!f1) {
2054                                 ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: switch with expression(%s) does not handle the case of %s !\n",
2055                                                 item->filename, item->startline, item->endline, item->u1.str, c->name);
2056                                 warns++;
2057                         }
2058                 }
2059                 /* next, is there an app call in the current exten, that would set this var? */
2060                 f1 = 0;
2061                 t = current_extension->u2.statements;
2062                 if ( t && t->type == PV_STATEMENTBLOCK )
2063                         t = t->u1.statements;
2064                 for (; t && t != item; t=t->next) {
2065                         if (t->type == PV_APPLICATION_CALL) {
2066                                 /* find the application that matches the u1.str */
2067                                 for (a2=apps; a2; a2=a2->next) {
2068                                         if (strcasecmp(a2->name, t->u1.str)==0) {
2069                                                 for (v2=a2->setvars; v2; v2=v2->next) {
2070                                                         if (strcmp(v2->name, buff1) == 0) {
2071                                                                 /* found an app that sets the var */
2072                                                                 f1 = 1;
2073                                                                 break;
2074                                                         }
2075                                                 }
2076                                         }
2077                                         if (f1)
2078                                                 break;
2079                                 }
2080                         }
2081                         if (f1)
2082                                 break;
2083                 }
2084                                 
2085                 /* see if it sets the var */
2086                 if (!f1) {
2087                         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",
2088                                         item->filename, item->startline, item->endline, item->u1.str);
2089                         warns++;
2090                 }
2091         }
2092 #endif
2093 }
2094
2095 #ifndef STANDALONE_AEL
2096 static void check_context_names(void)
2097 {
2098         pval *i,*j;
2099         for (i=current_db; i; i=i->next) {
2100                 if (i->type == PV_CONTEXT || i->type == PV_MACRO) {
2101                         for (j=i->next; j; j=j->next) {
2102                                 if ( j->type == PV_CONTEXT || j->type == PV_MACRO ) {
2103                                         if ( !strcmp(i->u1.str, j->u1.str) )
2104                                         {
2105                                                 ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: The context name (%s) is also declared in file %s, line %d-%d!\n",
2106                                                                 i->filename, i->startline, i->endline, i->u1.str,  j->filename, j->startline, j->endline);
2107                                                 warns++;
2108                                         }
2109                                 }
2110                         }
2111                 }
2112         }
2113 }
2114 #endif
2115
2116 static void check_abstract_reference(pval *abstract_context)
2117 {
2118         pval *i,*j;
2119         /* find some context includes that reference this context */
2120         
2121
2122         /* otherwise, print out a warning */
2123         for (i=current_db; i; i=i->next) {
2124                 if (i->type == PV_CONTEXT) {
2125                         for (j=i->u2. statements; j; j=j->next) {
2126                                 if ( j->type == PV_INCLUDES ) {
2127                                         struct pval *p4;
2128                                         for (p4=j->u1.list; p4; p4=p4->next) {
2129                                                 /* for each context pointed to, find it, then find a context/label that matches the
2130                                                    target here! */
2131                                                 if ( !strcmp(p4->u1.str, abstract_context->u1.str) )
2132                                                         return; /* found a match! */
2133                                         }
2134                                 }
2135                         }
2136                 }
2137         }
2138         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",
2139                         abstract_context->filename, abstract_context->startline, abstract_context->endline, abstract_context->u1.str);
2140         warns++;
2141 }
2142
2143
2144 void check_pval_item(pval *item, struct argapp *apps, int in_globals)
2145 {
2146         pval *lp;
2147 #ifdef AAL_ARGCHECK
2148         struct argapp *app, *found;
2149 #endif
2150         struct pval *macro_def;
2151         struct pval *app_def;
2152         
2153         char errmsg[4096];
2154         char *strp;
2155         
2156         switch (item->type) {
2157         case PV_WORD:
2158                 /* fields: item->u1.str == string associated with this (word).
2159                            item->u2.arglist  == pval list of 4 PV_WORD elements for time values (only in PV_INCLUDES) */
2160                 break;
2161                 
2162         case PV_MACRO:
2163                 /* fields: item->u1.str     == name of macro
2164                            item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
2165                                    item->u2.arglist->u1.str  == argument
2166                                    item->u2.arglist->next   == next arg
2167
2168                                    item->u3.macro_statements == pval list of statements in macro body.
2169                 */
2170                 in_abstract_context = 0;
2171                 current_context = item;
2172                 current_extension = 0;
2173                 for (lp=item->u2.arglist; lp; lp=lp->next) {
2174                 
2175                 }
2176                 check_pval(item->u3.macro_statements, apps,in_globals);
2177                 break;
2178                         
2179         case PV_CONTEXT:
2180                 /* fields: item->u1.str     == name of context
2181                            item->u2.statements == pval list of statements in context body
2182                                    item->u3.abstract == int 1 if an abstract keyword were present
2183                 */
2184                 current_context = item;
2185                 current_extension = 0;
2186                 if ( item->u3.abstract ) {
2187                         in_abstract_context = 1;
2188                         check_abstract_reference(item);
2189                 } else
2190                         in_abstract_context = 0;
2191                 check_pval(item->u2.statements, apps,in_globals);
2192                 break;
2193                         
2194         case PV_MACRO_CALL:
2195                 /* fields: item->u1.str     == name of macro to call
2196                            item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
2197                                    item->u2.arglist->u1.str  == argument
2198                                    item->u2.arglist->next   == next arg
2199                 */
2200                 macro_def = find_macro(item->u1.str);
2201                 if (!macro_def) {
2202                         ast_log(LOG_ERROR, "Error: file %s, line %d-%d: macro call to non-existent %s !\n",
2203                                         item->filename, item->startline, item->endline, item->u1.str);
2204                         errs++;
2205                 } else if (macro_def->type != PV_MACRO) {
2206                         ast_log(LOG_ERROR,"Error: file %s, line %d-%d: macro call to %s references a context, not a macro!\n",
2207                                         item->filename, item->startline, item->endline, item->u1.str);
2208                         errs++;
2209                 } else {
2210                         /* macro_def is a MACRO, so do the args match in number? */
2211                         int hereargs = 0;
2212                         int thereargs = 0;
2213                         
2214                         for (lp=item->u2.arglist; lp; lp=lp->next) {
2215                                 hereargs++;
2216                         }
2217                         for (lp=macro_def->u2.arglist; lp; lp=lp->next) {
2218                                 thereargs++;
2219                         }
2220                         if (hereargs != thereargs ) {
2221                                 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",
2222                                                 item->filename, item->startline, item->endline, item->u1.str, hereargs, thereargs);
2223                                 errs++;
2224                         }
2225                 }
2226                 break;
2227                         
2228         case PV_APPLICATION_CALL:
2229                 /* fields: item->u1.str     == name of application to call
2230                            item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
2231                                    item->u2.arglist->u1.str  == argument
2232                                    item->u2.arglist->next   == next arg
2233                 */
2234                 /* Need to check to see if the application is available! */
2235                 app_def = find_context(item->u1.str);
2236                 if (app_def && app_def->type == PV_MACRO) {
2237                         ast_log(LOG_ERROR,"Error: file %s, line %d-%d: application call to %s references an existing macro, but had no & preceding it!\n",
2238                                         item->filename, item->startline, item->endline, item->u1.str);
2239                         errs++;
2240                 }
2241                 if (strcasecmp(item->u1.str,"GotoIf") == 0
2242                         || strcasecmp(item->u1.str,"GotoIfTime") == 0
2243                         || strcasecmp(item->u1.str,"while") == 0
2244                         || strcasecmp(item->u1.str,"endwhile") == 0
2245                         || strcasecmp(item->u1.str,"random") == 0
2246                         || strcasecmp(item->u1.str,"execIf") == 0 ) {
2247                         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",
2248                                         item->filename, item->startline, item->endline, item->u1.str);
2249                         warns++;
2250                 }
2251 #ifdef AAL_ARGCHECK
2252                 found = 0;
2253                 for (app=apps; app; app=app->next) {
2254                         if (strcasecmp(app->name, item->u1.str) == 0) {
2255                                 found =app;
2256                                 break;
2257                         }
2258                 }
2259                 if (!found) {
2260                         ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: application call to %s not listed in applist database!\n",
2261                                         item->filename, item->startline, item->endline, item->u1.str);
2262                         warns++;
2263                 } else
2264                         check_app_args(item, item->u2.arglist, app);
2265 #endif
2266                 break;
2267                 
2268         case PV_CASE:
2269                 /* fields: item->u1.str     == value of case
2270                            item->u2.statements == pval list of statements under the case
2271                 */
2272                 /* Make sure sequence of statements under case is terminated with  goto, return, or break */
2273                 /* find the last statement */
2274                 check_pval(item->u2.statements, apps,in_globals);
2275                 break;
2276                         
2277         case PV_PATTERN:
2278                 /* fields: item->u1.str     == value of case
2279                            item->u2.statements == pval list of statements under the case
2280                 */
2281                 /* Make sure sequence of statements under case is terminated with  goto, return, or break */
2282                 /* find the last statement */
2283                 
2284                 check_pval(item->u2.statements, apps,in_globals);
2285                 break;
2286                         
2287         case PV_DEFAULT:
2288                 /* fields: 
2289                            item->u2.statements == pval list of statements under the case
2290                 */
2291
2292                 check_pval(item->u2.statements, apps,in_globals);
2293                 break;
2294                         
2295         case PV_CATCH:
2296                 /* fields: item->u1.str     == name of extension to catch
2297                            item->u2.statements == pval list of statements in context body
2298                 */
2299                 check_pval(item->u2.statements, apps,in_globals);
2300                 break;
2301                         
2302         case PV_SWITCHES:
2303                 /* fields: item->u1.list     == pval list of PV_WORD elements, one per entry in the list
2304                 */
2305                 check_pval(item->u1.list, apps,in_globals);
2306                 break;
2307                         
2308         case PV_ESWITCHES:
2309                 /* fields: item->u1.list     == pval list of PV_WORD elements, one per entry in the list
2310                 */
2311                 check_pval(item->u1.list, apps,in_globals);
2312                 break;
2313                         
2314         case PV_INCLUDES:
2315                 /* fields: item->u1.list     == pval list of PV_WORD elements, one per entry in the list
2316                 */
2317                 check_pval(item->u1.list, apps,in_globals);
2318                 for (lp=item->u1.list; lp; lp=lp->next){
2319                         char *incl_context = lp->u1.str;
2320                         struct pval *that_context = find_context(incl_context);
2321
2322                         if ( lp->u2.arglist ) {
2323                                 check_timerange(lp->u2.arglist);
2324                                 check_dow(lp->u2.arglist->next);
2325                                 check_day(lp->u2.arglist->next->next);
2326                                 check_month(lp->u2.arglist->next->next->next);
2327                         }
2328                         
2329                         if (that_context) {
2330                                 find_pval_gotos(that_context->u2.statements,0);
2331                                 
2332                         }
2333                 }
2334                 break;
2335                         
2336         case PV_STATEMENTBLOCK:
2337                 /* fields: item->u1.list     == pval list of statements in block, one per entry in the list
2338                 */
2339                 check_pval(item->u1.list, apps,in_globals);
2340                 break;
2341                         
2342         case PV_VARDEC:
2343                 /* fields: item->u1.str     == variable name
2344                            item->u2.val     == variable value to assign
2345                 */
2346                 /* the RHS of a vardec is encapsulated in a $[] expr. Is it legal? */
2347                 if( !in_globals ) { /* don't check stuff inside the globals context; no wrapping in $[ ] there... */
2348                         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);
2349                         ast_expr_register_extra_error_info(errmsg);
2350                         ast_expr(item->u2.val, expr_output, sizeof(expr_output));
2351                         ast_expr_clear_extra_error_info();
2352                         if ( strpbrk(item->u2.val,"~!-+<>=*/&^") && !strstr(item->u2.val,"${") ) {
2353                                 ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n",
2354                                                 item->filename, item->startline, item->endline, item->u2.val);
2355                                 warns++;
2356                         }
2357                         check_expr2_input(item,item->u2.val);
2358                 }
2359                 break;
2360                         
2361         case PV_GOTO:
2362                 /* fields: item->u1.list     == pval list of PV_WORD target names, up to 3, in order as given by user.
2363                            item->u1.list->u1.str  == where the data on a PV_WORD will always be.
2364                 */
2365                 /* don't check goto's in abstract contexts */
2366                 if ( in_abstract_context )
2367                         break;
2368                 
2369                 check_goto(item);
2370                 break;
2371                         
2372         case PV_LABEL:
2373                 /* fields: item->u1.str     == label name
2374                 */
2375                 if ( strspn(item->u1.str, "0123456789") == strlen(item->u1.str) ) {
2376                         ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: label '%s' is numeric, this is bad practice!\n",
2377                                         item->filename, item->startline, item->endline, item->u1.str);
2378                         warns++;
2379                 }
2380
2381                 check_label(item);
2382                 break;
2383                         
2384         case PV_FOR:
2385                 /* fields: item->u1.for_init     == a string containing the initalizer
2386                            item->u2.for_test     == a string containing the loop test
2387                            item->u3.for_inc      == a string containing the loop increment
2388
2389                                    item->u4.for_statements == a pval list of statements in the for ()
2390                 */
2391                 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);
2392                 ast_expr_register_extra_error_info(errmsg);
2393
2394                 strp = strchr(item->u1.for_init, '=');
2395                 if (strp) {
2396                         ast_expr(strp+1, expr_output, sizeof(expr_output));
2397                 }
2398                 ast_expr(item->u2.for_test, expr_output, sizeof(expr_output));
2399                 strp = strchr(item->u3.for_inc, '=');
2400                 if (strp) {
2401                         ast_expr(strp+1, expr_output, sizeof(expr_output));
2402                 }
2403                 if ( strpbrk(item->u2.for_test,"~!-+<>=*/&^") && !strstr(item->u2.for_test,"${") ) {
2404                         ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n",
2405                                         item->filename, item->startline, item->endline, item->u2.for_test);
2406                         warns++;
2407                 }
2408                 if ( strpbrk(item->u3.for_inc,"~!-+<>=*/&^") && !strstr(item->u3.for_inc,"${") ) {
2409                         ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n",
2410                                         item->filename, item->startline, item->endline, item->u3.for_inc);
2411                         warns++;
2412                 }
2413                 check_expr2_input(item,item->u2.for_test);
2414                 check_expr2_input(item,item->u3.for_inc);
2415                 
2416                 ast_expr_clear_extra_error_info();
2417                 check_pval(item->u4.for_statements, apps,in_globals);
2418                 break;
2419                         
2420         case PV_WHILE:
2421                 /* fields: item->u1.str        == the while conditional, as supplied by user
2422
2423                                    item->u2.statements == a pval list of statements in the while ()
2424                 */
2425                 snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, while expr '%s':", config, item->startline, item->startcol, item->endcol, item->u1.str);
2426                 ast_expr_register_extra_error_info(errmsg);
2427                 ast_expr(item->u1.str, expr_output, sizeof(expr_output));
2428                 ast_expr_clear_extra_error_info();
2429                 if ( strpbrk(item->u1.str,"~!-+<>=*/&^") && !strstr(item->u1.str,"${") ) {
2430                         ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n",
2431                                         item->filename, item->startline, item->endline, item->u1.str);
2432                         warns++;
2433                 }
2434                 check_expr2_input(item,item->u1.str);
2435                 check_pval(item->u2.statements, apps,in_globals);
2436                 break;
2437                         
2438         case PV_BREAK:
2439                 /* fields: none
2440                 */
2441                 break;
2442                         
2443         case PV_RETURN:
2444                 /* fields: none
2445                 */
2446                 break;
2447                         
2448         case PV_CONTINUE:
2449                 /* fields: none
2450                 */
2451                 break;
2452                         
2453         case PV_RANDOM:
2454                 /* fields: item->u1.str        == the random number expression, as supplied by user
2455
2456                                    item->u2.statements == a pval list of statements in the if ()
2457                                    item->u3.else_statements == a pval list of statements in the else
2458                                                                                            (could be zero)
2459                 */
2460                 snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, random expr '%s':", config, item->startline, item->startcol, item->endcol, item->u1.str);
2461                 ast_expr_register_extra_error_info(errmsg);
2462                 ast_expr(item->u1.str, expr_output, sizeof(expr_output));
2463                 ast_expr_clear_extra_error_info();
2464                 if ( strpbrk(item->u1.str,"~!-+<>=*/&^") && !strstr(item->u1.str,"${") ) {
2465                         ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: random expression '%s' has operators, but no variables. Interesting...\n",
2466                                         item->filename, item->startline, item->endline, item->u1.str);
2467                         warns++;
2468                 }
2469                 check_expr2_input(item,item->u1.str);
2470                 check_pval(item->u2.statements, apps,in_globals);
2471                 if (item->u3.else_statements) {
2472                         check_pval(item->u3.else_statements, apps,in_globals);
2473                 }
2474                 break;
2475
2476         case PV_IFTIME:
2477                 /* fields: item->u1.list        == the if time values, 4 of them, each in PV_WORD, linked list 
2478
2479                                    item->u2.statements == a pval list of statements in the if ()
2480                                    item->u3.else_statements == a pval list of statements in the else
2481                                                                                            (could be zero)
2482                 */
2483                 if ( item->u2.arglist ) {
2484                         check_timerange(item->u1.list);
2485                         check_dow(item->u1.list->next);
2486                         check_day(item->u1.list->next->next);
2487                         check_month(item->u1.list->next->next->next);
2488                 }
2489
2490                 check_pval(item->u2.statements, apps,in_globals);
2491                 if (item->u3.else_statements) {
2492                         check_pval(item->u3.else_statements, apps,in_globals);
2493                 }
2494                 break;
2495                         
2496         case PV_IF:
2497                 /* fields: item->u1.str        == the if conditional, as supplied by user
2498
2499                                    item->u2.statements == a pval list of statements in the if ()
2500                                    item->u3.else_statements == a pval list of statements in the else
2501                                                                                            (could be zero)
2502                 */
2503                 snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, if expr '%s':", config, item->startline, item->startcol, item->endcol, item->u1.str);
2504                 ast_expr_register_extra_error_info(errmsg);
2505                 ast_expr(item->u1.str, expr_output, sizeof(expr_output));
2506                 ast_expr_clear_extra_error_info();
2507                 if ( strpbrk(item->u1.str,"~!-+<>=*/&^") && !strstr(item->u1.str,"${") ) {
2508                         ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression '%s' has operators, but no variables. Interesting...\n",
2509                                         item->filename, item->startline, item->endline, item->u1.str);
2510                         warns++;
2511                 }
2512                 check_expr2_input(item,item->u1.str);
2513                 check_pval(item->u2.statements, apps,in_globals);
2514                 if (item->u3.else_statements) {
2515                         check_pval(item->u3.else_statements, apps,in_globals);
2516                 }
2517                 break;
2518                         
2519         case PV_SWITCH:
2520                 /* fields: item->u1.str        == the switch expression
2521
2522                                    item->u2.statements == a pval list of statements in the switch, 
2523                                                                                         (will be case statements, most likely!)
2524                 */
2525                 /* we can check the switch expression, see if it matches any of the app variables...
2526            if it does, then, are all the possible cases accounted for? */
2527                 check_switch_expr(item, apps);
2528                 check_pval(item->u2.statements, apps,in_globals);
2529                 break;
2530                         
2531         case PV_EXTENSION:
2532                 /* fields: item->u1.str        == the extension name, label, whatever it's called
2533
2534                                    item->u2.statements == a pval list of statements in the extension
2535                                    item->u3.hints      == a char * hint argument
2536                                    item->u4.regexten   == an int boolean. non-zero says that regexten was specified
2537                 */
2538                 current_extension = item ;
2539                 
2540                 check_pval(item->u2.statements, apps,in_globals);
2541                 break;
2542                         
2543         case PV_IGNOREPAT:
2544                 /* fields: item->u1.str        == the ignorepat data
2545                 */
2546                 break;
2547                         
2548         case PV_GLOBALS:
2549                 /* fields: item->u1.statements     == pval list of statements, usually vardecs
2550                 */
2551                 in_abstract_context = 0;
2552                 check_pval(item->u1.statements, apps, 1);
2553                 break;
2554         default:
2555                 break;
2556         }
2557 }
2558
2559 void check_pval(pval *item, struct argapp *apps, int in_globals)
2560 {
2561         pval *i;
2562
2563         /* checks to do:
2564            1. Do goto's point to actual labels? 
2565            2. Do macro calls reference a macro?
2566            3. Does the number of macro args match the definition?
2567            4. Is a macro call missing its & at the front?
2568            5. Application calls-- we could check syntax for existing applications,
2569               but I need some some sort of universal description bnf for a general
2570                   sort of method for checking arguments, in number, maybe even type, at least. 
2571                   Don't want to hand code checks for hundreds of applications.
2572         */
2573         
2574         for (i=item; i; i=i->next) {
2575                 check_pval_item(i,apps,in_globals);
2576         }
2577 }
2578
2579 #ifndef STANDALONE_AEL
2580 static void ael2_semantic_check(pval *item, int *arg_errs, int *arg_warns, int *arg_notes)
2581 {
2582         
2583 #ifdef AAL_ARGCHECK
2584         int argapp_errs =0;
2585         char *rfilename;
2586 #endif
2587         struct argapp *apps=0;
2588
2589 #ifdef AAL_ARGCHECK
2590         rfilename = alloca(10 + strlen(ast_config_AST_VAR_DIR));
2591         sprintf(rfilename, "%s/applist", ast_config_AST_VAR_DIR);
2592         
2593         apps = argdesc_parse(rfilename, &argapp_errs); /* giveth */
2594 #endif
2595         current_db = item;
2596         errs = warns = notes = 0;
2597
2598         check_context_names();
2599         check_pval(item, apps, 0);
2600
2601 #ifdef AAL_ARGCHECK
2602         argdesc_destroy(apps);  /* taketh away */
2603 #endif
2604         current_db = 0;
2605
2606         *arg_errs = errs;
2607         *arg_warns = warns;
2608         *arg_notes = notes;
2609 }
2610 #endif
2611
2612 /* =============================================================================================== */
2613 /* "CODE" GENERATOR -- Convert the AEL representation to asterisk extension language */
2614 /* =============================================================================================== */
2615
2616 static int control_statement_count = 0;
2617
2618 struct ael_priority *new_prio(void)
2619 {
2620         struct ael_priority *x = (struct ael_priority *)calloc(sizeof(struct ael_priority),1);
2621         return x;
2622 }
2623
2624 struct ael_extension *new_exten(void)
2625 {
2626         struct ael_extension *x = (struct ael_extension *)calloc(sizeof(struct ael_extension),1);
2627         return x;
2628 }
2629
2630 void linkprio(struct ael_extension *exten, struct ael_priority *prio)
2631 {
2632         if (!exten->plist) {
2633                 exten->plist = prio;
2634                 exten->plist_last = prio;
2635         } else {
2636                 exten->plist_last->next = prio;
2637                 exten->plist_last = prio;
2638         }
2639         if( !prio->exten )
2640                 prio->exten = exten; /* don't override the switch value */
2641 }
2642
2643 void destroy_extensions(struct ael_extension *exten)
2644 {
2645         struct ael_extension *ne, *nen;
2646         for (ne=exten; ne; ne=nen) {
2647                 struct ael_priority *pe, *pen;
2648                 
2649                 if (ne->name)
2650                         free(ne->name);
2651                 
2652                 /* cidmatch fields are allocated with name, and freed when
2653                    the name field is freed. Don't do a free for this field,
2654                    unless you LIKE to see a crash! */
2655
2656                 if (ne->hints)
2657                         free(ne->hints);
2658                 
2659                 for (pe=ne->plist; pe; pe=pen) {
2660                         pen = pe->next;
2661                         if (pe->app)
2662                                 free(pe->app);
2663                         pe->app = 0;
2664                         if (pe->appargs)
2665                                 free(pe->appargs);
2666                         pe->appargs = 0;
2667                         pe->origin = 0;
2668                         pe->goto_true = 0;
2669                         pe->goto_false = 0;
2670                         free(pe);
2671                 }
2672                 nen = ne->next_exten;
2673                 ne->next_exten = 0;
2674                 ne->plist =0;
2675                 ne->plist_last = 0;
2676                 ne->next_exten = 0;
2677                 ne->loop_break = 0;
2678                 ne->loop_continue = 0;
2679                 free(ne);
2680         }
2681 }
2682
2683 static int label_inside_case(pval *label)
2684 {
2685         pval *p = label;
2686         
2687         while( p && p->type != PV_MACRO && p->type != PV_CONTEXT ) /* early cutout, sort of */ {
2688                 if( p->type == PV_CASE || p->type == PV_DEFAULT || p->type == PV_PATTERN ) {
2689                         return 1;
2690                 }
2691
2692                 p = p->dad;
2693         }
2694         return 0;
2695 }
2696
2697 static void linkexten(struct ael_extension *exten, struct ael_extension *add)
2698 {
2699         add->next_exten = exten->next_exten; /* this will reverse the order. Big deal. */
2700         exten->next_exten = add;
2701 }
2702
2703 static void remove_spaces_before_equals(char *str)
2704 {
2705         char *p;
2706         while( str && *str && *str != '=' )
2707         {
2708                 if( *str == ' ' || *str == '\n' || *str == '\r' || *str == '\t' )
2709                 {
2710                         p = str;
2711                         while( *p )
2712                         {
2713                                 *p = *(p+1);
2714                                 p++;
2715                         }
2716                 }
2717                 else
2718                         str++;
2719         }
2720 }
2721
2722 static void gen_prios(struct ael_extension *exten, char *label, pval *statement, struct ael_extension *mother_exten, struct ast_context *this_context )
2723 {
2724         pval *p,*p2,*p3;
2725         struct ael_priority *pr;
2726         struct ael_priority *for_init, *for_test, *for_inc, *for_loop, *for_end;
2727         struct ael_priority *while_test, *while_loop, *while_end;
2728         struct ael_priority *switch_test, *switch_end, *fall_thru;
2729         struct ael_priority *if_test, *if_end, *if_skip, *if_false;
2730 #ifdef OLD_RAND_ACTION
2731         struct ael_priority *rand_test, *rand_end, *rand_skip;
2732 #endif
2733         char buf1[2000];
2734         char buf2[2000];
2735         char *strp, *strp2;
2736         char new_label[2000];
2737         int default_exists;
2738         int local_control_statement_count;
2739         struct ael_priority *loop_break_save;
2740         struct ael_priority *loop_continue_save;
2741         struct ael_extension *switch_case;
2742         
2743         for (p=statement; p; p=p->next) {
2744                 switch (p->type) {
2745                 case PV_VARDEC:
2746                         pr = new_prio();
2747                         pr->type = AEL_APPCALL;
2748                         snprintf(buf1,sizeof(buf1),"%s=$[%s]", p->u1.str, p->u2.val);
2749                         pr->app = strdup("Set");
2750                         remove_spaces_before_equals(buf1);
2751                         pr->appargs = strdup(buf1);
2752                         pr->origin = p;
2753                         linkprio(exten, pr);
2754                         break;
2755
2756                 case PV_GOTO:
2757                         pr = new_prio();
2758                         pr->type = AEL_APPCALL;
2759                         p->u2.goto_target = get_goto_target(p);
2760                         if( p->u2.goto_target ) {
2761                                 p->u3.goto_target_in_case = p->u2.goto_target->u2.label_in_case = label_inside_case(p->u2.goto_target);
2762                         }
2763                         
2764                         if (!p->u1.list->next) /* just one */ {
2765                                 pr->app = strdup("Goto");
2766                                 if (!mother_exten)
2767                                         pr->appargs = strdup(p->u1.list->u1.str);
2768                                 else {  /* for the case of simple within-extension gotos in case/pattern/default statement blocks: */ 
2769                                         snprintf(buf1,sizeof(buf1),"%s|%s", mother_exten->name, p->u1.list->u1.str);
2770                                         pr->appargs = strdup(buf1);
2771                                 }
2772                                 
2773                         } else if (p->u1.list->next && !p->u1.list->next->next) /* two */ {
2774                                 snprintf(buf1,sizeof(buf1),"%s|%s", p->u1.list->u1.str, p->u1.list->next->u1.str);
2775                                 pr->app = strdup("Goto");
2776                                 pr->appargs = strdup(buf1);
2777                         } else if (p->u1.list->next && p->u1.list->next->next) {
2778                                 snprintf(buf1,sizeof(buf1),"%s|%s|%s", p->u1.list->u1.str, 
2779                                                 p->u1.list->next->u1.str,
2780                                                 p->u1.list->next->next->u1.str);
2781                                 pr->app = strdup("Goto");
2782                                 pr->appargs = strdup(buf1);
2783                         }
2784                         pr->origin = p;
2785                         linkprio(exten, pr);
2786                         break;
2787
2788                 case PV_LABEL:
2789                         pr = new_prio();
2790                         pr->type = AEL_LABEL;
2791                         pr->origin = p;
2792                         p->u3.compiled_label = exten;
2793                         linkprio(exten, pr);
2794                         break;
2795
2796                 case PV_FOR:
2797                         control_statement_count++;
2798                         loop_break_save = exten->loop_break; /* save them, then restore before leaving */
2799                         loop_continue_save = exten->loop_continue;
2800                         snprintf(new_label,sizeof(new_label),"for-%s-%d", label, control_statement_count);
2801                         for_init = new_prio();
2802                         for_inc = new_prio();
2803                         for_test = new_prio();
2804                         for_loop = new_prio();
2805                         for_end = new_prio();
2806                         for_init->type = AEL_APPCALL;
2807                         for_inc->type = AEL_APPCALL;
2808                         for_test->type = AEL_FOR_CONTROL;
2809                         for_test->goto_false = for_end;
2810                         for_loop->type = AEL_CONTROL1; /* simple goto */
2811                         for_end->type = AEL_APPCALL;
2812                         for_init->app = strdup("Set");
2813                         
2814                         strcpy(buf2,p->u1.for_init);
2815                         remove_spaces_before_equals(buf2);
2816                         strp = strchr(buf2, '=');
2817                         strp2 = strchr(p->u1.for_init, '=');
2818                         if (strp) {
2819                                 *(strp+1) = 0;
2820                                 strcat(buf2,"$[");
2821                                 strncat(buf2,strp2+1, sizeof(buf2)-strlen(strp2+1)-2);
2822                                 strcat(buf2,"]");
2823                                 for_init->appargs = strdup(buf2);
2824                         } else
2825                                 for_init->appargs = strdup(p->u1.for_init);
2826
2827                         for_inc->app = strdup("Set");
2828
2829                         strcpy(buf2,p->u3.for_inc);
2830                         remove_spaces_before_equals(buf2);
2831                         strp = strchr(buf2, '=');
2832                         strp2 = strchr(p->u3.for_inc, '=');
2833                         if (strp) {
2834                                 *(strp+1) = 0;
2835                                 strcat(buf2,"$[");
2836                                 strncat(buf2,strp2+1, sizeof(buf2)-strlen(strp2+1)-2);
2837                                 strcat(buf2,"]");
2838                                 for_inc->appargs = strdup(buf2);
2839                         } else
2840                                 for_inc->appargs = strdup(p->u3.for_inc);
2841                         snprintf(buf1,sizeof(buf1),"$[%s]",p->u2.for_test);
2842                         for_test->app = 0;
2843                         for_test->appargs = strdup(buf1);
2844                         for_loop->goto_true = for_test;
2845                         snprintf(buf1,sizeof(buf1),"Finish for-%s-%d", label, control_statement_count);
2846                         for_end->app = strdup("NoOp");
2847                         for_end->appargs = strdup(buf1);
2848                         /* link & load! */
2849                         linkprio(exten, for_init);
2850                         linkprio(exten, for_test);
2851                         
2852                         /* now, put the body of the for loop here */
2853                         exten->loop_break = for_end;
2854                         exten->loop_continue = for_test;
2855                         
2856                         gen_prios(exten, new_label, p->u4.for_statements, mother_exten, this_context); /* this will link in all the statements here */
2857                         
2858                         linkprio(exten, for_inc);
2859                         linkprio(exten, for_loop);
2860                         linkprio(exten, for_end);
2861                         
2862                         
2863                         exten->loop_break = loop_break_save;
2864                         exten->loop_continue = loop_continue_save;
2865                         for_loop->origin = p;
2866                         break;
2867
2868                 case PV_WHILE:
2869                         control_statement_count++;
2870                         loop_break_save = exten->loop_break; /* save them, then restore before leaving */
2871                         loop_continue_save = exten->loop_continue;
2872                         snprintf(new_label,sizeof(new_label),"while-%s-%d", label, control_statement_count);
2873                         while_test = new_prio();
2874                         while_loop = new_prio();
2875                         while_end = new_prio();
2876                         while_test->type = AEL_FOR_CONTROL;
2877                         while_test->goto_false = while_end;
2878                         while_loop->type = AEL_CONTROL1; /* simple goto */
2879                         while_end->type = AEL_APPCALL;
2880                         snprintf(buf1,sizeof(buf1),"$[%s]",p->u1.str);
2881                         while_test->app = 0;
2882                         while_test->appargs = strdup(buf1);
2883                         while_loop->goto_true = while_test;
2884                         snprintf(buf1,sizeof(buf1),"Finish while-%s-%d", label, control_statement_count);
2885                         while_end->app = strdup("NoOp");
2886                         while_end->appargs = strdup(buf1);
2887
2888                         linkprio(exten, while_test);
2889                         
2890                         /* now, put the body of the for loop here */
2891                         exten->loop_break = while_end;
2892                         exten->loop_continue = while_test;
2893                         
2894                         gen_prios(exten, new_label, p->u2.statements, mother_exten, this_context); /* this will link in all the while body statements here */
2895
2896                         linkprio(exten, while_loop);
2897                         linkprio(exten, while_end);
2898                         
2899                         
2900                         exten->loop_break = loop_break_save;
2901                         exten->loop_continue = loop_continue_save;
2902                         while_loop->origin = p;
2903                         break;
2904
2905                 case PV_SWITCH:
2906                         control_statement_count++;
2907                         local_control_statement_count = control_statement_count;
2908                         loop_break_save = exten->loop_break; /* save them, then restore before leaving */
2909                         loop_continue_save = exten->loop_continue;
2910                         snprintf(new_label,sizeof(new_label),"sw-%s-%d", label, control_statement_count);
2911
2912                         switch_test = new_prio();
2913                         switch_end = new_prio();
2914                         switch_test->type = AEL_APPCALL;
2915                         switch_end->type = AEL_APPCALL;
2916                         snprintf(buf1,sizeof(buf1),"sw-%d-%s|1",control_statement_count, p->u1.str);
2917                         switch_test->app = strdup("Goto");
2918                         switch_test->appargs = strdup(buf1);
2919                         snprintf(buf1,sizeof(buf1),"Finish switch-%s-%d", label, control_statement_count);
2920                         switch_end->app = strdup("NoOp");
2921                         switch_end->appargs = strdup(buf1);
2922                         switch_end->origin = p;
2923                         switch_end->exten = exten;
2924
2925                         linkprio(exten, switch_test);
2926                         linkprio(exten, switch_end);
2927                         
2928                         exten->loop_break = switch_end;
2929                         exten->loop_continue = 0;
2930                         default_exists = 0;
2931                         
2932                         for (p2=p->u2.statements; p2; p2=p2->next) {
2933                                 /* now, for each case/default put the body of the for loop here */
2934                                 if (p2->type == PV_CASE) {
2935                                         /* ok, generate a extension and link it in */
2936                                         switch_case = new_exten();
2937                                         switch_case->context = this_context;
2938                                         /* the break/continue locations are inherited from parent */
2939                                         switch_case->loop_break = exten->loop_break;
2940                                         switch_case->loop_continue = exten->loop_continue;
2941                                         
2942                                         linkexten(exten,switch_case);
2943                                         snprintf(buf1,sizeof(buf1),"sw-%d-%s", local_control_statement_count, p2->u1.str);
2944                                         switch_case->name = strdup(buf1);
2945                                         snprintf(new_label,sizeof(new_label),"sw-%s-%s-%d", label, p2->u1.str, local_control_statement_count);
2946                                         
2947                                         gen_prios(switch_case, new_label, p2->u2.statements, exten, this_context); /* this will link in all the case body statements here */
2948
2949                                         /* here is where we write code to "fall thru" to the next case... if there is one... */
2950                                         for (p3=p2->u2.statements; p3; p3=p3->next) {
2951                                                 if (!p3->next)
2952                                                         break;
2953                                         }
2954                                         /* p3 now points the last statement... */
2955                                         if (!p3 || ( p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN) ) {
2956                                                 /* is there a following CASE/PATTERN/DEFAULT? */
2957                                                 if (p2->next && p2->next->type == PV_CASE) {
2958                                                         fall_thru = new_prio();
2959                                                         fall_thru->type = AEL_APPCALL;
2960                                                         fall_thru->app = strdup("Goto");
2961                                                         snprintf(buf1,sizeof(buf1),"sw-%d-%s|1",local_control_statement_count, p2->next->u1.str);
2962                                                         fall_thru->appargs = strdup(buf1);
2963                                                         linkprio(switch_case, fall_thru);
2964                                                 } else if (p2->next && p2->next->type == PV_PATTERN) {
2965                                                         fall_thru = new_prio();
2966                                                         fall_thru->type = AEL_APPCALL;
2967                                                         fall_thru->app = strdup("Goto");
2968                                                         snprintf(buf1,sizeof(buf1),"_sw-%d-%s|1",local_control_statement_count, p2->next->u1.str);
2969                                                         fall_thru->appargs = strdup(buf1);
2970                                                         linkprio(switch_case, fall_thru);
2971                                                 } else if (p2->next && p2->next->type == PV_DEFAULT) {
2972                                                         fall_thru = new_prio();
2973                                                         fall_thru->type = AEL_APPCALL;
2974                                                         fall_thru->app = strdup("Goto");
2975                                                         snprintf(buf1,sizeof(buf1),"_sw-%d-.|1",local_control_statement_count);
2976                                                         fall_thru->appargs = strdup(buf1);
2977                                                         linkprio(switch_case, fall_thru);
2978                                                 } else if (!p2->next) {
2979                                                         fall_thru = new_prio();
2980                                                         fall_thru->type = AEL_CONTROL1;
2981                                                         fall_thru->goto_true = switch_end;
2982                                                         fall_thru->app = strdup("Goto");
2983                                                         linkprio(switch_case, fall_thru);
2984                                                 }
2985                                         }
2986                                         if (switch_case->return_needed) {
2987                                                 char buf[2000];
2988                                                 struct ael_priority *np2 = new_prio();
2989                                                 np2->type = AEL_APPCALL;
2990                                                 np2->app = strdup("NoOp");
2991                                                 snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name);
2992                                                 np2->appargs = strdup(buf);
2993                                                 linkprio(switch_case, np2);
2994                                                 switch_case-> return_target = np2;
2995                                         }
2996                                 } else if (p2->type == PV_PATTERN) {
2997                                         /* ok, generate a extension and link it in */
2998                                         switch_case = new_exten();
2999                                         switch_case->context = this_context;
3000                                         /* the break/continue locations are inherited from parent */
3001                                         switch_case->loop_break = exten->loop_break;
3002                                         switch_case->loop_continue = exten->loop_continue;
3003                                         
3004                                         linkexten(exten,switch_case);
3005                                         snprintf(buf1,sizeof(buf1),"_sw-%d-%s", local_control_statement_count, p2->u1.str);
3006                                         switch_case->name = strdup(buf1);
3007                                         snprintf(new_label,sizeof(new_label),"sw-%s-%s-%d", label, p2->u1.str, local_control_statement_count);
3008                                         
3009                                         gen_prios(switch_case, new_label, p2->u2.statements, exten, this_context); /* this will link in all the while body statements here */
3010                                         /* here is where we write code to "fall thru" to the next case... if there is one... */
3011                                         for (p3=p2->u2.statements; p3; p3=p3->next) {
3012                                                 if (!p3->next)
3013                                                         break;
3014                                         }
3015                                         /* p3 now points the last statement... */
3016                                         if (!p3 || ( p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN)) {
3017                                                 /* is there a following CASE/PATTERN/DEFAULT? */
3018                                                 if (p2->next && p2->next->type == PV_CASE) {
3019                                                         fall_thru = new_prio();
3020                                                         fall_thru->type = AEL_APPCALL;
3021                                                         fall_thru->app = strdup("Goto");
3022                                                         snprintf(buf1,sizeof(buf1),"sw-%d-%s|1",local_control_statement_count, p2->next->u1.str);
3023                                                         fall_thru->appargs = strdup(buf1);
3024                                                         linkprio(switch_case, fall_thru);
3025                                                 } else if (p2->next && p2->next->type == PV_PATTERN) {
3026                                                         fall_thru = new_prio();
3027                                                         fall_thru->type = AEL_APPCALL;
3028                                                         fall_thru->app = strdup("Goto");
3029                                                         snprintf(buf1,sizeof(buf1),"_sw-%d-%s|1",local_control_statement_count, p2->next->u1.str);
3030                                                         fall_thru->appargs = strdup(buf1);
3031                                                         linkprio(switch_case, fall_thru);
3032                                                 } else if (p2->next && p2->next->type == PV_DEFAULT) {
3033                                                         fall_thru = new_prio();
3034                                                         fall_thru->type = AEL_APPCALL;
3035                                                         fall_thru->app = strdup("Goto");
3036                                                         snprintf(buf1,sizeof(buf1),"_sw-%d-.|1",local_control_statement_count);
3037                                                         fall_thru->appargs = strdup(buf1);
3038                                                         linkprio(switch_case, fall_thru);
3039                                                 } else if (!p2->next) {
3040                                                         fall_thru = new_prio();
3041                                                         fall_thru->type = AEL_CONTROL1;
3042                                                         fall_thru->goto_true = switch_end;
3043                                                         fall_thru->app = strdup("Goto");
3044                                                         linkprio(switch_case, fall_thru);
3045                                                 }
3046                                         }
3047                                         if (switch_case->return_needed) {
3048                                                 char buf[2000];
3049                                                 struct ael_priority *np2 = new_prio();
3050                                                 np2->type = AEL_APPCALL;
3051                                                 np2->app = strdup("NoOp");
3052                                                 snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name);
3053                                                 np2->appargs = strdup(buf);
3054                                                 linkprio(switch_case, np2);
3055                                                 switch_case-> return_target = np2;
3056                                         }
3057                                 } else if (p2->type == PV_DEFAULT) {
3058                                         default_exists++;
3059                                         /* ok, generate a extension and link it in */
3060                                         switch_case = new_exten();
3061                                         switch_case->context = this_context;
3062                                         /* the break/continue locations are inherited from parent */
3063                                         switch_case->loop_break = exten->loop_break;
3064                                         switch_case->loop_continue = exten->loop_continue;
3065                                         linkexten(exten,switch_case);
3066                                         snprintf(buf1,sizeof(buf1),"_sw-%d-.", local_control_statement_count);
3067                                         switch_case->name = strdup(buf1);
3068                                         
3069                                         snprintf(new_label,sizeof(new_label),"sw-%s-default-%d", label, local_control_statement_count);
3070                                         
3071                                         gen_prios(switch_case, new_label, p2->u2.statements, exten, this_context); /* this will link in all the while body statements here */
3072                                         
3073                                         /* here is where we write code to "fall thru" to the next case... if there is one... */
3074                                         for (p3=p2->u2.statements; p3; p3=p3->next) {
3075                                                 if (!p3->next)
3076                                                         break;
3077                                         }
3078                                         /* p3 now points the last statement... */
3079                                         if (!p3 || (p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN)) {
3080                                                 /* is there a following CASE/PATTERN/DEFAULT? */
3081                                                 if (p2->next && p2->next->type == PV_CASE) {
3082                                                         fall_thru = new_prio();
3083                                                         fall_thru->type = AEL_APPCALL;
3084                                                         fall_thru->app = strdup("Goto");
3085                                                         snprintf(buf1,sizeof(buf1),"sw-%d-%s|1",local_control_statement_count, p2->next->u1.str);
3086                                                         fall_thru->appargs = strdup(buf1);
3087                                                         linkprio(switch_case, fall_thru);
3088                                                 } else if (p2->next && p2->next->type == PV_PATTERN) {
3089                                                         fall_thru = new_prio();
3090                                                         fall_thru->type = AEL_APPCALL;
3091                                                         fall_thru->app = strdup("Goto");
3092                                                         snprintf(buf1,sizeof(buf1),"_sw-%d-%s|1",local_control_statement_count, p2->next->u1.str);
3093                                                         fall_thru->appargs = strdup(buf1);
3094                                                         linkprio(switch_case, fall_thru);
3095                                                 } else if (p2->next && p2->next->type == PV_DEFAULT) {
3096                                                         fall_thru = new_prio();
3097                                                         fall_thru->type = AEL_APPCALL;
3098                                                         fall_thru->app = strdup("Goto");
3099                                                         snprintf(buf1,sizeof(buf1),"_sw-%d-.|1",local_control_statement_count);
3100                                                         fall_thru->appargs = strdup(buf1);
3101                                                         linkprio(switch_case, fall_thru);
3102                                                 } else if (!p2->next) {
3103                                                         fall_thru = new_prio();
3104                                                         fall_thru->type = AEL_CONTROL1;
3105                                                         fall_thru->goto_true = switch_end;
3106                                                         fall_thru->app = strdup("Goto");
3107                                                         linkprio(switch_case, fall_thru);
3108                                                 }
3109                                         }
3110                                         if (switch_case->return_needed) {
3111                                                 char buf[2000];
3112                                                 struct ael_priority *np2 = new_prio();
3113                                                 np2->type = AEL_APPCALL;
3114                                                 np2->app = strdup("NoOp");
3115                                                 snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name);
3116                                                 np2->appargs = strdup(buf);
3117                                                 linkprio(switch_case, np2);
3118                                                 switch_case-> return_target = np2;
3119                                         }
3120                                 } else {
3121                                         /* what could it be??? */
3122                                 }
3123                         }
3124                         
3125                         exten->loop_break = loop_break_save;
3126                         exten->loop_continue = loop_continue_save;
3127                         switch_test->origin&