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