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