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