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