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