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