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