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