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