This commit closes bug 7605, and half-closes 7638. The AEL code has been redistribute...
[asterisk/asterisk.git] / pbx / pbx_ael.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2006, Digium, Inc.
5  *
6  * Steve Murphy <murf@parsetree.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Compile symbolic Asterisk Extension Logic into Asterisk extensions, version 2.
22  * 
23  */
24
25 /*** MODULEINFO
26         <depend>res_ael_share</depend>
27  ***/
28
29 #include "asterisk.h"
30
31 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
32
33 #include <sys/types.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <ctype.h>
39 #include <errno.h>
40 #include <regex.h>
41 #include <sys/stat.h>
42
43 #include "asterisk/pbx.h"
44 #include "asterisk/config.h"
45 #include "asterisk/module.h"
46 #include "asterisk/logger.h"
47 #include "asterisk/cli.h"
48 #include "asterisk/app.h"
49 #include "asterisk/callerid.h"
50 #include "asterisk/ael_structs.h"
51 #include "asterisk/pval.h"
52 #ifdef AAL_ARGCHECK
53 #include "asterisk/argdesc.h"
54 #endif
55
56 /* these functions are in ../ast_expr2.fl */
57
58 #define DEBUG_READ   (1 << 0)
59 #define DEBUG_TOKENS (1 << 1)
60 #define DEBUG_MACROS (1 << 2)
61 #define DEBUG_CONTEXTS (1 << 3)
62
63 static char *config = "extensions.ael";
64 static char *registrar = "pbx_ael";
65 static int pbx_load_module(void);
66 static int warns, errs;
67 static struct pval *current_db;
68
69 #ifndef AAL_ARGCHECK
70 /* for the time being, short circuit all the AAL related structures
71    without permanently removing the code; after/during the AAL 
72    development, this code can be properly re-instated 
73 */
74
75 #endif
76
77 #ifdef AAL_ARGCHECK
78 int option_matches_j( struct argdesc *should, pval *is, struct argapp *app);
79 int option_matches( struct argdesc *should, pval *is, struct argapp *app);
80 int ael_is_funcname(char *name);
81 #endif
82
83 int check_app_args(pval *appcall, pval *arglist, struct argapp *app);
84 void check_pval(pval *item, struct argapp *apps, int in_globals);
85 void check_pval_item(pval *item, struct argapp *apps, int in_globals);
86 void check_switch_expr(pval *item, struct argapp *apps);
87 void ast_expr_register_extra_error_info(char *errmsg);
88 void ast_expr_clear_extra_error_info(void);
89 struct pval *find_macro(char *name);
90 struct pval *find_context(char *name);
91 struct pval *find_context(char *name);
92 struct pval *find_macro(char *name);
93 struct ael_priority *new_prio(void);
94 struct ael_extension *new_exten(void);
95 void linkprio(struct ael_extension *exten, struct ael_priority *prio);
96 void destroy_extensions(struct ael_extension *exten);
97 void set_priorities(struct ael_extension *exten);
98 void add_extensions(struct ael_extension *exten);
99 void ast_compile_ael2(struct ast_context **local_contexts, struct pval *root);
100 void destroy_pval(pval *item);
101 void destroy_pval_item(pval *item);
102 int is_float(char *arg );
103 int is_int(char *arg );
104 int is_empty(char *arg);
105
106 static const char *match_context;
107 static const char *match_exten;
108 static const char *match_label;
109 static int count_labels; /* true, put matcher in label counting mode */
110 static int return_on_context_match;
111 struct pval *match_pval(pval *item);
112 static void check_goto(pval *item);
113 static void find_pval_goto_item(pval *item, int lev);
114 static void find_pval_gotos(pval *item, int lev);
115
116 static struct pval *find_label_in_current_context(char *exten, char *label, pval *curr_cont);
117 static struct pval *find_first_label_in_current_context(char *label, pval *curr_cont);
118 static void print_pval_list(FILE *fin, pval *item, int depth);
119
120 static struct pval *find_label_in_current_extension(const char *label, pval *curr_ext);
121 static struct pval *find_label_in_current_db(const char *context, const char *exten, const char *label);
122 static pval *get_goto_target(pval *item);
123 static int label_inside_case(pval *label);
124 static pval *get_extension_or_contxt(pval *p);
125 static pval *get_contxt(pval *p);
126 static void remove_spaces_before_equals(char *str);
127 /* static void substitute_commas(char *str); */
128 #ifdef NOMORE
129 /*! \brief I am adding this code to substitute commas with vertbars in the args to apps */
130 static void substitute_commas(char *str)
131 {
132         char *p = str;
133         
134         while (p && *p)
135         {
136                 if (*p == ',' && ((p != str && *(p-1) != '\\')
137                                 || p == str))
138                         *p = '|';
139                 if (*p == '\\' && *(p+1) == ',') { /* learning experience: the '\,' is turned into just ',' by pbx_config; So we need to do the same */
140                         char *q = p;
141                         while (*q) {  /* move the ',' and everything after it up 1 char */
142                                 *q = *(q+1);
143                                 q++;
144                         }
145                 }
146                 p++;
147         }
148 }
149 #endif
150
151 /* PRETTY PRINTER FOR AEL:  ============================================================================= */
152
153 static void print_pval(FILE *fin, pval *item, int depth)
154 {
155         int i;
156         pval *lp;
157         
158         for (i=0; i<depth; i++) {
159                 fprintf(fin, "\t"); /* depth == indentation */
160         }
161         
162         switch ( item->type ) {
163         case PV_WORD:
164                 fprintf(fin,"%s;\n", item->u1.str); /* usually, words are encapsulated in something else */
165                 break;
166                 
167         case PV_MACRO:
168                 fprintf(fin,"macro %s(", item->u1.str);
169                 for (lp=item->u2.arglist; lp; lp=lp->next) {
170                         if (lp != item->u2.arglist )
171                                 fprintf(fin,", ");
172                         fprintf(fin,"%s", lp->u1.str);
173                 }
174                 fprintf(fin,") {\n");
175                 print_pval_list(fin,item->u3.macro_statements,depth+1);
176                 for (i=0; i<depth; i++) {
177                         fprintf(fin,"\t"); /* depth == indentation */
178                 }
179                 fprintf(fin,"};\n\n");
180                 break;
181                         
182         case PV_CONTEXT:
183                 if ( item->u3.abstract )
184                         fprintf(fin,"abstract context %s {\n", item->u1.str);
185                 else
186                         fprintf(fin,"context %s {\n", item->u1.str);
187                 print_pval_list(fin,item->u2.statements,depth+1);
188                 for (i=0; i<depth; i++) {
189                         fprintf(fin,"\t"); /* depth == indentation */
190                 }
191                 fprintf(fin,"};\n\n");
192                 break;
193                         
194         case PV_MACRO_CALL:
195                 fprintf(fin,"&%s(", item->u1.str);
196                 for (lp=item->u2.arglist; lp; lp=lp->next) {
197                         if ( lp != item->u2.arglist )
198                                 fprintf(fin,", ");
199                         fprintf(fin,"%s", lp->u1.str);
200                 }
201                 fprintf(fin,");\n");
202                 break;
203                         
204         case PV_APPLICATION_CALL:
205                 fprintf(fin,"%s(", item->u1.str);
206                 for (lp=item->u2.arglist; lp; lp=lp->next) {
207                         if ( lp != item->u2.arglist )
208                                 fprintf(fin,",");
209                         fprintf(fin,"%s", lp->u1.str);
210                 }
211                 fprintf(fin,");\n");
212                 break;
213                         
214         case PV_CASE:
215                 fprintf(fin,"case %s:\n", item->u1.str);
216                 print_pval_list(fin,item->u2.statements, depth+1);
217                 break;
218                         
219         case PV_PATTERN:
220                 fprintf(fin,"pattern %s:\n", item->u1.str);
221                 print_pval_list(fin,item->u2.statements, depth+1);
222                 break;
223                         
224         case PV_DEFAULT:
225                 fprintf(fin,"default:\n");
226                 print_pval_list(fin,item->u2.statements, depth+1);
227                 break;
228                         
229         case PV_CATCH:
230                 fprintf(fin,"catch %s {\n", item->u1.str);
231                 print_pval_list(fin,item->u2.statements, depth+1);
232                 for (i=0; i<depth; i++) {
233                         fprintf(fin,"\t"); /* depth == indentation */
234                 }
235                 fprintf(fin,"};\n");
236                 break;
237                         
238         case PV_SWITCHES:
239                 fprintf(fin,"switches {\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_ESWITCHES:
248                 fprintf(fin,"eswitches {\n");
249                 print_pval_list(fin,item->u1.list,depth+1);
250                 for (i=0; i<depth; i++) {
251                         fprintf(fin,"\t"); /* depth == indentation */
252                 }
253                 fprintf(fin,"};\n");
254                 break;
255                         
256         case PV_INCLUDES:
257                 fprintf(fin,"includes {\n");
258                 for (lp=item->u1.list; lp; lp=lp->next) {
259                         for (i=0; i<depth+1; i++) {
260                                 fprintf(fin,"\t"); /* depth == indentation */
261                         }
262                         fprintf(fin,"%s", lp->u1.str); /* usually, words are encapsulated in something else */
263                         if ( lp->u2.arglist )
264                                 fprintf(fin,"|%s|%s|%s|%s", 
265                                                 lp->u2.arglist->u1.str,
266                                                 lp->u2.arglist->next->u1.str,
267                                                 lp->u2.arglist->next->next->u1.str,
268                                                 lp->u2.arglist->next->next->next->u1.str
269                                         );
270                         fprintf(fin,";\n"); /* usually, words are encapsulated in something else */
271                 }
272                 
273                 print_pval_list(fin,item->u1.list,depth+1);
274                 for (i=0; i<depth; i++) {
275                         fprintf(fin,"\t"); /* depth == indentation */
276                 }
277                 fprintf(fin,"};\n");
278                 break;
279                         
280         case PV_STATEMENTBLOCK:
281                 fprintf(fin,"{\n");
282                 print_pval_list(fin,item->u1.list, depth+1);
283                 for (i=0; i<depth; i++) {
284                         fprintf(fin,"\t"); /* depth == indentation */
285                 }
286                 fprintf(fin,"};\n");
287                 break;
288                         
289         case PV_VARDEC:
290                 fprintf(fin,"%s=%s;\n", item->u1.str, item->u2.val);
291                 break;
292                         
293         case PV_LOCALVARDEC:
294                 fprintf(fin,"local %s=%s;\n", item->u1.str, item->u2.val);
295                 break;
296                         
297         case PV_GOTO:
298                 fprintf(fin,"goto %s", item->u1.list->u1.str);
299                 if ( item->u1.list->next )
300                         fprintf(fin,",%s", item->u1.list->next->u1.str);
301                 if ( item->u1.list->next && item->u1.list->next->next )
302                         fprintf(fin,",%s", item->u1.list->next->next->u1.str);
303                 fprintf(fin,"\n");
304                 break;
305                         
306         case PV_LABEL:
307                 fprintf(fin,"%s:\n", item->u1.str);
308                 break;
309                         
310         case PV_FOR:
311                 fprintf(fin,"for (%s; %s; %s)\n", item->u1.for_init, item->u2.for_test, item->u3.for_inc);
312                 print_pval_list(fin,item->u4.for_statements,depth+1);
313                 break;
314                         
315         case PV_WHILE:
316                 fprintf(fin,"while (%s)\n", item->u1.str);
317                 print_pval_list(fin,item->u2.statements,depth+1);
318                 break;
319                         
320         case PV_BREAK:
321                 fprintf(fin,"break;\n");
322                 break;
323                         
324         case PV_RETURN:
325                 fprintf(fin,"return;\n");
326                 break;
327                         
328         case PV_CONTINUE:
329                 fprintf(fin,"continue;\n");
330                 break;
331                         
332         case PV_RANDOM:
333         case PV_IFTIME:
334         case PV_IF:
335                 if ( item->type == PV_IFTIME ) {
336                         
337                         fprintf(fin,"ifTime ( %s|%s|%s|%s )\n", 
338                                         item->u1.list->u1.str, 
339                                         item->u1.list->next->u1.str, 
340                                         item->u1.list->next->next->u1.str, 
341                                         item->u1.list->next->next->next->u1.str
342                                         );
343                 } else if ( item->type == PV_RANDOM ) {
344                         fprintf(fin,"random ( %s )\n", item->u1.str );
345                 } else
346                         fprintf(fin,"if ( %s )\n", item->u1.str);
347                 if ( item->u2.statements && item->u2.statements->next ) {
348                         for (i=0; i<depth; i++) {
349                                 fprintf(fin,"\t"); /* depth == indentation */
350                         }
351                         fprintf(fin,"{\n");
352                         print_pval_list(fin,item->u2.statements,depth+1);
353                         for (i=0; i<depth; i++) {
354                                 fprintf(fin,"\t"); /* depth == indentation */
355                         }
356                         if ( item->u3.else_statements )
357                                 fprintf(fin,"}\n");
358                         else
359                                 fprintf(fin,"};\n");
360                 } else if (item->u2.statements ) {
361                         print_pval_list(fin,item->u2.statements,depth+1);
362                 } else {
363                         if (item->u3.else_statements )
364                                 fprintf(fin, " {} ");
365                         else
366                                 fprintf(fin, " {}; ");
367                 }
368                 if ( item->u3.else_statements ) {
369                         for (i=0; i<depth; i++) {
370                                 fprintf(fin,"\t"); /* depth == indentation */
371                         }
372                         fprintf(fin,"else\n");
373                         print_pval_list(fin,item->u3.else_statements, depth);
374                 }
375                 break;
376                         
377         case PV_SWITCH:
378                 fprintf(fin,"switch( %s ) {\n", item->u1.str);
379                 print_pval_list(fin,item->u2.statements,depth+1);
380                 for (i=0; i<depth; i++) {
381                         fprintf(fin,"\t"); /* depth == indentation */
382                 }
383                 fprintf(fin,"}\n");
384                 break;
385                         
386         case PV_EXTENSION:
387                 if ( item->u4.regexten )
388                         fprintf(fin, "regexten ");
389                 if ( item->u3.hints )
390                         fprintf(fin,"hints(%s) ", item->u3.hints);
391                 
392                 fprintf(fin,"%s => \n", item->u1.str);
393                 print_pval_list(fin,item->u2.statements,depth+1);
394                 break;
395                         
396         case PV_IGNOREPAT:
397                 fprintf(fin,"ignorepat => %s\n", item->u1.str);
398                 break;
399                         
400         case PV_GLOBALS:
401                 fprintf(fin,"globals {\n");
402                 print_pval_list(fin,item->u1.statements,depth+1);
403                 for (i=0; i<depth; i++) {
404                         fprintf(fin,"\t"); /* depth == indentation */
405                 }
406                 fprintf(fin,"}\n");
407                 break;
408         }
409 }
410
411 static void print_pval_list(FILE *fin, pval *item, int depth)
412 {
413         pval *i;
414         
415         for (i=item; i; i=i->next) {
416                 print_pval(fin, i, depth);
417         }
418 }
419
420 #if 0
421 static void ael2_print(char *fname, pval *tree)
422 {
423         FILE *fin = fopen(fname,"w");
424         if ( !fin ) {
425                 ast_log(LOG_ERROR, "Couldn't open %s for writing.\n", fname);
426                 return;
427         }
428         print_pval_list(fin, tree, 0);
429         fclose(fin);
430 }
431 #endif
432
433
434
435 /* SEMANTIC CHECKING FOR AEL:  ============================================================================= */
436
437 /*   (not all that is syntactically legal is good! */
438
439
440 static struct pval *in_macro(pval *item)
441 {
442         struct pval *curr;
443         curr = item;    
444         while( curr ) {
445                 if( curr->type == PV_MACRO  ) {
446                         return curr;
447                 }
448                 curr = curr->dad;
449         }
450         return 0;
451 }
452
453 static struct pval *in_context(pval *item)
454 {
455         struct pval *curr;
456         curr = item;    
457         while( curr ) {
458                 if( curr->type == PV_MACRO || curr->type == PV_CONTEXT ) {
459                         return curr;
460                 }
461                 curr = curr->dad;
462         }
463         return 0;
464 }
465
466
467 static pval *get_goto_target(pval *item)
468 {
469         /* just one item-- the label should be in the current extension */
470         pval *curr_ext = get_extension_or_contxt(item); /* containing exten, or macro */
471         pval *curr_cont;
472         
473         if (item->u1.list && !item->u1.list->next && !strstr((item->u1.list)->u1.str,"${")) {
474                 struct pval *x = find_label_in_current_extension((char*)((item->u1.list)->u1.str), curr_ext);
475                         return x;
476         }
477
478         curr_cont = get_contxt(item);
479
480         /* TWO items */
481         if (item->u1.list->next && !item->u1.list->next->next) {
482                 if (!strstr((item->u1.list)->u1.str,"${") 
483                         && !strstr(item->u1.list->next->u1.str,"${") ) /* Don't try to match variables */ {
484                         struct pval *x = find_label_in_current_context((char *)item->u1.list->u1.str, (char *)item->u1.list->next->u1.str, curr_cont);
485                                 return x;
486                 }
487         }
488         
489         /* All 3 items! */
490         if (item->u1.list->next && item->u1.list->next->next) {
491                 /* all three */
492                 pval *first = item->u1.list;
493                 pval *second = item->u1.list->next;
494                 pval *third = item->u1.list->next->next;
495                 
496                 if (!strstr((item->u1.list)->u1.str,"${") 
497                         && !strstr(item->u1.list->next->u1.str,"${")
498                         && !strstr(item->u1.list->next->next->u1.str,"${")) /* Don't try to match variables */ {
499                         struct pval *x = find_label_in_current_db((char*)first->u1.str, (char*)second->u1.str, (char*)third->u1.str);
500                         if (!x) {
501
502                                 struct pval *p3;
503                                 struct pval *that_context = find_context(item->u1.list->u1.str);
504                                 
505                                 /* the target of the goto could be in an included context!! Fancy that!! */
506                                 /* look for includes in the current context */
507                                 if (that_context) {
508                                         for (p3=that_context->u2.statements; p3; p3=p3->next) {
509                                                 if (p3->type == PV_INCLUDES) {
510                                                         struct pval *p4;
511                                                         for (p4=p3->u1.list; p4; p4=p4->next) {
512                                                                 /* for each context pointed to, find it, then find a context/label that matches the
513                                                                    target here! */
514                                                                 char *incl_context = p4->u1.str;
515                                                                 /* find a matching context name */
516                                                                 struct pval *that_other_context = find_context(incl_context);
517                                                                 if (that_other_context) {
518                                                                         struct pval *x3;
519                                                                         x3 = find_label_in_current_context((char *)item->u1.list->next->u1.str, (char *)item->u1.list->next->next->u1.str, that_other_context);
520                                                                         if (x3) {
521                                                                                 return x3;
522                                                                         }
523                                                                 }
524                                                         }
525                                                 }
526                                         }
527                                 }
528                         }
529                         return x;
530                 }
531         }
532         return 0;
533 }
534
535 static void check_goto(pval *item)
536 {
537         /* check for the target of the goto-- does it exist? */
538         if ( !(item->u1.list)->next && !(item->u1.list)->u1.str ) {
539                 ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto:  empty label reference found!\n",
540                                 item->filename, item->startline, item->endline);
541                 errs++;
542         }
543         
544         /* just one item-- the label should be in the current extension */
545         
546         if (item->u1.list && !item->u1.list->next && !strstr((item->u1.list)->u1.str,"${")) {
547                 struct pval *z = get_extension_or_contxt(item);
548                 struct pval *x = 0;
549                 if (z)
550                         x = find_label_in_current_extension((char*)((item->u1.list)->u1.str), z); /* if in macro, use current context instead */
551                 /* printf("Called find_label_in_current_extension with arg %s; current_extension is %x: %d\n",
552                    (char*)((item->u1.list)->u1.str), current_extension?current_extension:current_context, current_extension?current_extension->type:current_context->type); */
553                 if (!x) {
554                         ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto:  no label %s exists in the current extension!\n",
555                                         item->filename, item->startline, item->endline, item->u1.list->u1.str);
556                         errs++;
557                 }
558                 else
559                         return;
560         }
561         
562         /* TWO items */
563         if (item->u1.list->next && !item->u1.list->next->next) {
564                 /* two items */
565                 /* printf("Calling find_label_in_current_context with args %s, %s\n",
566                    (char*)((item->u1.list)->u1.str), (char *)item->u1.list->next->u1.str); */
567                 if (!strstr((item->u1.list)->u1.str,"${") 
568                         && !strstr(item->u1.list->next->u1.str,"${") ) /* Don't try to match variables */ {
569                         struct pval *z = get_contxt(item);
570                         struct pval *x = 0;
571                         
572                         if (z)
573                                 x = find_label_in_current_context((char *)item->u1.list->u1.str, (char *)item->u1.list->next->u1.str, z);
574
575                         if (!x) {
576                                 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",
577                                                 item->filename, item->startline, item->endline, item->u1.list->u1.str, item->u1.list->next->u1.str );
578                                 errs++;
579                         }
580                         else
581                                 return;
582                 }
583         }
584         
585         /* All 3 items! */
586         if (item->u1.list->next && item->u1.list->next->next) {
587                 /* all three */
588                 pval *first = item->u1.list;
589                 pval *second = item->u1.list->next;
590                 pval *third = item->u1.list->next->next;
591                 
592                 /* printf("Calling find_label_in_current_db with args %s, %s, %s\n",
593                    (char*)first->u1.str, (char*)second->u1.str, (char*)third->u1.str); */
594                 if (!strstr((item->u1.list)->u1.str,"${") 
595                         && !strstr(item->u1.list->next->u1.str,"${")
596                         && !strstr(item->u1.list->next->next->u1.str,"${")) /* Don't try to match variables */ {
597                         struct pval *x = find_label_in_current_db((char*)first->u1.str, (char*)second->u1.str, (char*)third->u1.str);
598                         if (!x) {
599                                 struct pval *p3;
600                                 struct pval *found = 0;
601                                 struct pval *that_context = find_context(item->u1.list->u1.str);
602                                 
603                                 /* the target of the goto could be in an included context!! Fancy that!! */
604                                 /* look for includes in the current context */
605                                 if (that_context) {
606                                         for (p3=that_context->u2.statements; p3; p3=p3->next) {
607                                                 if (p3->type == PV_INCLUDES) {
608                                                         struct pval *p4;
609                                                         for (p4=p3->u1.list; p4; p4=p4->next) {
610                                                                 /* for each context pointed to, find it, then find a context/label that matches the
611                                                                    target here! */
612                                                                 char *incl_context = p4->u1.str;
613                                                                 /* find a matching context name */
614                                                                 struct pval *that_other_context = find_context(incl_context);
615                                                                 if (that_other_context) {
616                                                                         struct pval *x3;
617                                                                         x3 = find_label_in_current_context((char *)item->u1.list->next->u1.str, (char *)item->u1.list->next->next->u1.str, that_other_context);
618                                                                         if (x3) {
619                                                                                 found = x3;
620                                                                                 break;
621                                                                         }
622                                                                 }
623                                                         }
624                                                 }
625                                         }
626                                         if (!found) {
627                                                 ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto:  no label %s|%s exists in the context %s or its inclusions!\n",
628                                                                 item->filename, item->startline, item->endline, item->u1.list->next->u1.str, item->u1.list->next->next->u1.str, item->u1.list->u1.str );
629                                                 errs++;
630                                         } else {
631                                                 struct pval *mac = in_macro(item); /* is this goto inside a macro? */
632                                                 if( mac ) {    /* yes! */
633                                                         struct pval *targ = in_context(found);
634                                                         if( mac != targ )
635                                                         {
636                                                                 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",
637                                                                                 item->filename, item->startline, item->endline);
638                                                                 warns++;                                                                
639                                                         }
640                                                 }
641                                         }
642                                 } else {
643                                         /* here is where code would go to check for target existence in extensions.conf files */
644                                         ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: goto:  no context %s could be found that matches the goto target!\n",
645                                                         item->filename, item->startline, item->endline, item->u1.list->u1.str);
646                                         warns++; /* this is just a warning, because this context could be in extensions.conf or somewhere */
647                                 }
648                         } else {
649                                 struct pval *mac = in_macro(item); /* is this goto inside a macro? */
650                                 if( mac ) {    /* yes! */
651                                         struct pval *targ = in_context(x);
652                                         if( mac != targ )
653                                         {
654                                                 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",
655                                                                 item->filename, item->startline, item->endline);
656                                                 warns++;                                                                
657                                         }
658                                 }
659                         }
660                 }
661         }
662 }
663         
664
665 static void find_pval_goto_item(pval *item, int lev)
666 {
667         struct pval *p4;
668         if (lev>100) {
669                 ast_log(LOG_ERROR,"find_pval_goto in infinite loop!\n\n");
670                 return;
671         }
672         
673         switch ( item->type ) {
674         case PV_MACRO:
675                 /* fields: item->u1.str     == name of macro
676                            item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
677                                    item->u2.arglist->u1.str  == argument
678                                    item->u2.arglist->next   == next arg
679
680                                    item->u3.macro_statements == pval list of statements in macro body.
681                 */
682                         
683                 /* printf("Descending into matching macro %s\n", match_context); */
684                 find_pval_gotos(item->u3.macro_statements,lev+1); /* if we're just searching for a context, don't bother descending into them */
685                 
686                 break;
687                         
688         case PV_CONTEXT:
689                 /* fields: item->u1.str     == name of context
690                            item->u2.statements == pval list of statements in context body
691                                    item->u3.abstract == int 1 if an abstract keyword were present
692                 */
693                 break;
694
695         case PV_CASE:
696                 /* fields: item->u1.str     == value of case
697                            item->u2.statements == pval list of statements under the case
698                 */
699                 find_pval_gotos(item->u2.statements,lev+1);
700                 break;
701                         
702         case PV_PATTERN:
703                 /* fields: item->u1.str     == value of case
704                            item->u2.statements == pval list of statements under the case
705                 */
706                 find_pval_gotos(item->u2.statements,lev+1);
707                 break;
708                         
709         case PV_DEFAULT:
710                 /* fields: 
711                            item->u2.statements == pval list of statements under the case
712                 */
713                 find_pval_gotos(item->u2.statements,lev+1);
714                 break;
715                         
716         case PV_CATCH:
717                 /* fields: item->u1.str     == name of extension to catch
718                            item->u2.statements == pval list of statements in context body
719                 */
720                 find_pval_gotos(item->u2.statements,lev+1);
721                 break;
722                         
723         case PV_STATEMENTBLOCK:
724                 /* fields: item->u1.list     == pval list of statements in block, one per entry in the list
725                 */
726                 find_pval_gotos(item->u1.list,lev+1);
727                 break;
728                         
729         case PV_GOTO:
730                 /* fields: item->u1.list     == pval list of PV_WORD target names, up to 3, in order as given by user.
731                            item->u1.list->u1.str  == where the data on a PV_WORD will always be.
732                 */
733                 check_goto(item);  /* THE WHOLE FUNCTION OF THIS ENTIRE ROUTINE!!!! */
734                 break;
735                         
736         case PV_INCLUDES:
737                 /* fields: item->u1.list     == pval list of PV_WORD elements, one per entry in the list
738                 */
739                 for (p4=item->u1.list; p4; p4=p4->next) {
740                         /* for each context pointed to, find it, then find a context/label that matches the
741                            target here! */
742                         char *incl_context = p4->u1.str;
743                         /* find a matching context name */
744                         struct pval *that_context = find_context(incl_context);
745                         if (that_context) {
746                                 find_pval_gotos(that_context,lev+1); /* keep working up the includes */
747                         }
748                 }
749                 break;
750                 
751         case PV_FOR:
752                 /* fields: item->u1.for_init     == a string containing the initalizer
753                            item->u2.for_test     == a string containing the loop test
754                            item->u3.for_inc      == a string containing the loop increment
755
756                                    item->u4.for_statements == a pval list of statements in the for ()
757                 */
758                 find_pval_gotos(item->u4.for_statements,lev+1);
759                 break;
760                         
761         case PV_WHILE:
762                 /* fields: item->u1.str        == the while conditional, as supplied by user
763
764                                    item->u2.statements == a pval list of statements in the while ()
765                 */
766                 find_pval_gotos(item->u2.statements,lev+1);
767                 break;
768                         
769         case PV_RANDOM:
770                 /* fields: item->u1.str        == the random number expression, as supplied by user
771
772                                    item->u2.statements == a pval list of statements in the if ()
773                                    item->u3.else_statements == a pval list of statements in the else
774                                                                                            (could be zero)
775                  fall thru to PV_IF */
776                 
777         case PV_IFTIME:
778                 /* fields: item->u1.list        == the time values, 4 of them, as PV_WORD structs in a list
779
780                                    item->u2.statements == a pval list of statements in the if ()
781                                    item->u3.else_statements == a pval list of statements in the else
782                                                                                            (could be zero)
783                 fall thru to PV_IF*/
784         case PV_IF:
785                 /* fields: item->u1.str        == the if conditional, as supplied by user
786
787                                    item->u2.statements == a pval list of statements in the if ()
788                                    item->u3.else_statements == a pval list of statements in the else
789                                                                                            (could be zero)
790                 */
791                 find_pval_gotos(item->u2.statements,lev+1);
792
793                 if (item->u3.else_statements) {
794                         find_pval_gotos(item->u3.else_statements,lev+1);
795                 }
796                 break;
797                         
798         case PV_SWITCH:
799                 /* fields: item->u1.str        == the switch expression
800
801                                    item->u2.statements == a pval list of statements in the switch, 
802                                                                                         (will be case statements, most likely!)
803                 */
804                 find_pval_gotos(item->u3.else_statements,lev+1);
805                 break;
806                         
807         case PV_EXTENSION:
808                 /* fields: item->u1.str        == the extension name, label, whatever it's called
809
810                                    item->u2.statements == a pval list of statements in the extension
811                                    item->u3.hints      == a char * hint argument
812                                    item->u4.regexten   == an int boolean. non-zero says that regexten was specified
813                 */
814
815                 find_pval_gotos(item->u2.statements,lev+1);
816                 break;
817
818         default:
819                 break;
820         }
821 }
822
823 static void find_pval_gotos(pval *item,int lev)
824 {
825         pval *i;
826
827         for (i=item; i; i=i->next) {
828                 
829                 find_pval_goto_item(i, lev);
830         }
831 }
832
833
834
835 struct pval *find_first_label_in_current_context(char *label, pval *curr_cont)
836 {
837         /* printf("  --- Got args %s, %s\n", exten, label); */
838         struct pval *ret;
839         struct pval *p3;
840         struct pval *startpt = ((curr_cont->type==PV_MACRO)?curr_cont->u3.macro_statements: curr_cont->u2.statements);
841         
842         count_labels = 0;
843         return_on_context_match = 0;
844         match_context = "*";
845         match_exten = "*";
846         match_label = label;
847         
848         ret =  match_pval(curr_cont);
849         if (ret)
850                 return ret;
851                                         
852         /* the target of the goto could be in an included context!! Fancy that!! */
853         /* look for includes in the current context */
854         for (p3=startpt; p3; p3=p3->next) {
855                 if (p3->type == PV_INCLUDES) {
856                         struct pval *p4;
857                         for (p4=p3->u1.list; p4; p4=p4->next) {
858                                 /* for each context pointed to, find it, then find a context/label that matches the
859                                    target here! */
860                                 char *incl_context = p4->u1.str;
861                                 /* find a matching context name */
862                                 struct pval *that_context = find_context(incl_context);
863                                 if (that_context) {
864                                         struct pval *x3;
865                                         x3 = find_first_label_in_current_context(label, that_context);
866                                         if (x3) {
867                                                 return x3;
868                                         }
869                                 }
870                         }
871                 }
872         }
873         return 0;
874 }
875
876 struct pval *find_label_in_current_context(char *exten, char *label, pval *curr_cont)
877 {
878         /* printf("  --- Got args %s, %s\n", exten, label); */
879         struct pval *ret;
880         struct pval *p3;
881         struct pval *startpt;
882         
883         count_labels = 0;
884         return_on_context_match = 0;
885         match_context = "*";
886         match_exten = exten;
887         match_label = label;
888         if (curr_cont->type == PV_MACRO)
889                 startpt = curr_cont->u3.macro_statements;
890         else
891                 startpt = curr_cont->u2.statements;
892
893         ret =  match_pval(startpt);
894         if (ret)
895                 return ret;
896                                         
897         /* the target of the goto could be in an included context!! Fancy that!! */
898         /* look for includes in the current context */
899         for (p3=startpt; p3; p3=p3->next) {
900                 if (p3->type == PV_INCLUDES) {
901                         struct pval *p4;
902                         for (p4=p3->u1.list; p4; p4=p4->next) {
903                                 /* for each context pointed to, find it, then find a context/label that matches the
904                                    target here! */
905                                 char *incl_context = p4->u1.str;
906                                 /* find a matching context name */
907                                 struct pval *that_context = find_context(incl_context);
908                                 if (that_context) {
909                                         struct pval *x3;
910                                         x3 = find_label_in_current_context(exten, label, that_context);
911                                         if (x3) {
912                                                 return x3;
913                                         }
914                                 }
915                         }
916                 }
917         }
918         return 0;
919 }
920
921 static struct pval *find_label_in_current_extension(const char *label, pval *curr_ext)
922 {
923         /* printf("  --- Got args %s\n", label); */
924         count_labels = 0;
925         return_on_context_match = 0;
926         match_context = "*";
927         match_exten = "*";
928         match_label = label;
929         return match_pval(curr_ext);
930 }
931
932 static struct pval *find_label_in_current_db(const char *context, const char *exten, const char *label)
933 {
934         /* printf("  --- Got args %s, %s, %s\n", context, exten, label); */
935         count_labels = 0;
936         return_on_context_match = 0;
937
938         match_context = context;
939         match_exten = exten;
940         match_label = label;
941         
942         return match_pval(current_db);
943 }
944
945
946
947 /* =============================================================================================== */
948 /* "CODE" GENERATOR -- Convert the AEL representation to asterisk extension language */
949 /* =============================================================================================== */
950
951 static int control_statement_count = 0;
952
953 static int label_inside_case(pval *label)
954 {
955         pval *p = label;
956         
957         while( p && p->type != PV_MACRO && p->type != PV_CONTEXT ) /* early cutout, sort of */ {
958                 if( p->type == PV_CASE || p->type == PV_DEFAULT || p->type == PV_PATTERN ) {
959                         return 1;
960                 }
961
962                 p = p->dad;
963         }
964         return 0;
965 }
966
967 static void linkexten(struct ael_extension *exten, struct ael_extension *add)
968 {
969         add->next_exten = exten->next_exten; /* this will reverse the order. Big deal. */
970         exten->next_exten = add;
971 }
972
973 static void remove_spaces_before_equals(char *str)
974 {
975         char *p;
976         while( str && *str && *str != '=' )
977         {
978                 if( *str == ' ' || *str == '\n' || *str == '\r' || *str == '\t' )
979                 {
980                         p = str;
981                         while( *p )
982                         {
983                                 *p = *(p+1);
984                                 p++;
985                         }
986                 }
987                 else
988                         str++;
989         }
990 }
991
992 static void gen_match_to_pattern(char *pattern, char *result)
993 {
994         /* the result will be a string that will be matched by pattern */
995         char *p=pattern, *t=result;
996         while (*p) {
997                 if (*p == 'x' || *p == 'n' || *p == 'z' || *p == 'X' || *p == 'N' || *p == 'Z')
998                         *t++ = '9';
999                 else if (*p == '[') {
1000                         char *z = p+1;
1001                         while (*z != ']')
1002                                 z++;
1003                         if (*(z+1)== ']')
1004                                 z++;
1005                         *t++=*(p+1); /* use the first char in the set */
1006                         p = z;
1007                 } else {
1008                         *t++ = *p;
1009                 }
1010                 p++;
1011         }
1012         *t++ = 0; /* cap it off */
1013 }
1014
1015 static void gen_prios(struct ael_extension *exten, char *label, pval *statement, struct ael_extension *mother_exten, struct ast_context *this_context )
1016 {
1017         pval *p,*p2,*p3;
1018         struct ael_priority *pr;
1019         struct ael_priority *for_init, *for_test, *for_inc, *for_loop, *for_end;
1020         struct ael_priority *while_test, *while_loop, *while_end;
1021         struct ael_priority *switch_test, *switch_end, *fall_thru;
1022         struct ael_priority *if_test, *if_end, *if_skip, *if_false;
1023 #ifdef OLD_RAND_ACTION
1024         struct ael_priority *rand_test, *rand_end, *rand_skip;
1025 #endif
1026         char buf1[2000];
1027         char buf2[2000];
1028         char *strp, *strp2;
1029         char new_label[2000];
1030         int default_exists;
1031         int local_control_statement_count;
1032         int first;
1033         struct ael_priority *loop_break_save;
1034         struct ael_priority *loop_continue_save;
1035         struct ael_extension *switch_case;
1036         
1037         for (p=statement; p; p=p->next) {
1038                 switch (p->type) {
1039                 case PV_VARDEC:
1040                         pr = new_prio();
1041                         pr->type = AEL_APPCALL;
1042                         snprintf(buf1,sizeof(buf1),"%s=$[%s]", p->u1.str, p->u2.val);
1043                         pr->app = strdup("Set");
1044                         remove_spaces_before_equals(buf1);
1045                         pr->appargs = strdup(buf1);
1046                         pr->origin = p;
1047                         linkprio(exten, pr);
1048                         break;
1049
1050                 case PV_LOCALVARDEC:
1051                         pr = new_prio();
1052                         pr->type = AEL_APPCALL;
1053                         snprintf(buf1,sizeof(buf1),"LOCAL(%s)=$[%s]", p->u1.str, p->u2.val);
1054                         pr->app = strdup("Set");
1055                         remove_spaces_before_equals(buf1);
1056                         pr->appargs = strdup(buf1);
1057                         pr->origin = p;
1058                         linkprio(exten, pr);
1059                         break;
1060
1061                 case PV_GOTO:
1062                         pr = new_prio();
1063                         pr->type = AEL_APPCALL;
1064                         p->u2.goto_target = get_goto_target(p);
1065                         if( p->u2.goto_target ) {
1066                                 p->u3.goto_target_in_case = p->u2.goto_target->u2.label_in_case = label_inside_case(p->u2.goto_target);
1067                         }
1068                         
1069                         if (!p->u1.list->next) /* just one */ {
1070                                 pr->app = strdup("Goto");
1071                                 if (!mother_exten)
1072                                         pr->appargs = strdup(p->u1.list->u1.str);
1073                                 else {  /* for the case of simple within-extension gotos in case/pattern/default statement blocks: */ 
1074                                         snprintf(buf1,sizeof(buf1),"%s,%s", mother_exten->name, p->u1.list->u1.str);
1075                                         pr->appargs = strdup(buf1);
1076                                 }
1077                                 
1078                         } else if (p->u1.list->next && !p->u1.list->next->next) /* two */ {
1079                                 snprintf(buf1,sizeof(buf1),"%s,%s", p->u1.list->u1.str, p->u1.list->next->u1.str);
1080                                 pr->app = strdup("Goto");
1081                                 pr->appargs = strdup(buf1);
1082                         } else if (p->u1.list->next && p->u1.list->next->next) {
1083                                 snprintf(buf1,sizeof(buf1),"%s,%s,%s", p->u1.list->u1.str, 
1084                                                 p->u1.list->next->u1.str,
1085                                                 p->u1.list->next->next->u1.str);
1086                                 pr->app = strdup("Goto");
1087                                 pr->appargs = strdup(buf1);
1088                         }
1089                         pr->origin = p;
1090                         linkprio(exten, pr);
1091                         break;
1092
1093                 case PV_LABEL:
1094                         pr = new_prio();
1095                         pr->type = AEL_LABEL;
1096                         pr->origin = p;
1097                         p->u3.compiled_label = exten;
1098                         linkprio(exten, pr);
1099                         break;
1100
1101                 case PV_FOR:
1102                         control_statement_count++;
1103                         loop_break_save = exten->loop_break; /* save them, then restore before leaving */
1104                         loop_continue_save = exten->loop_continue;
1105                         snprintf(new_label,sizeof(new_label),"for-%s-%d", label, control_statement_count);
1106                         for_init = new_prio();
1107                         for_inc = new_prio();
1108                         for_test = new_prio();
1109                         for_loop = new_prio();
1110                         for_end = new_prio();
1111                         for_init->type = AEL_APPCALL;
1112                         for_inc->type = AEL_APPCALL;
1113                         for_test->type = AEL_FOR_CONTROL;
1114                         for_test->goto_false = for_end;
1115                         for_loop->type = AEL_CONTROL1; /* simple goto */
1116                         for_end->type = AEL_APPCALL;
1117                         for_init->app = strdup("Set");
1118                         
1119                         strcpy(buf2,p->u1.for_init);
1120                         remove_spaces_before_equals(buf2);
1121                         strp = strchr(buf2, '=');
1122                         strp2 = strchr(p->u1.for_init, '=');
1123                         if (strp) {
1124                                 *(strp+1) = 0;
1125                                 strcat(buf2,"$[");
1126                                 strncat(buf2,strp2+1, sizeof(buf2)-strlen(strp2+1)-2);
1127                                 strcat(buf2,"]");
1128                                 for_init->appargs = strdup(buf2);
1129                         } else
1130                                 for_init->appargs = strdup(p->u1.for_init);
1131
1132                         for_inc->app = strdup("Set");
1133
1134                         strcpy(buf2,p->u3.for_inc);
1135                         remove_spaces_before_equals(buf2);
1136                         strp = strchr(buf2, '=');
1137                         strp2 = strchr(p->u3.for_inc, '=');
1138                         if (strp) {
1139                                 *(strp+1) = 0;
1140                                 strcat(buf2,"$[");
1141                                 strncat(buf2,strp2+1, sizeof(buf2)-strlen(strp2+1)-2);
1142                                 strcat(buf2,"]");
1143                                 for_inc->appargs = strdup(buf2);
1144                         } else
1145                                 for_inc->appargs = strdup(p->u3.for_inc);
1146                         snprintf(buf1,sizeof(buf1),"$[%s]",p->u2.for_test);
1147                         for_test->app = 0;
1148                         for_test->appargs = strdup(buf1);
1149                         for_loop->goto_true = for_test;
1150                         snprintf(buf1,sizeof(buf1),"Finish for-%s-%d", label, control_statement_count);
1151                         for_end->app = strdup("NoOp");
1152                         for_end->appargs = strdup(buf1);
1153                         /* link & load! */
1154                         linkprio(exten, for_init);
1155                         linkprio(exten, for_test);
1156                         
1157                         /* now, put the body of the for loop here */
1158                         exten->loop_break = for_end;
1159                         exten->loop_continue = for_inc;
1160                         
1161                         gen_prios(exten, new_label, p->u4.for_statements, mother_exten, this_context); /* this will link in all the statements here */
1162                         
1163                         linkprio(exten, for_inc);
1164                         linkprio(exten, for_loop);
1165                         linkprio(exten, for_end);
1166                         
1167                         
1168                         exten->loop_break = loop_break_save;
1169                         exten->loop_continue = loop_continue_save;
1170                         for_loop->origin = p;
1171                         break;
1172
1173                 case PV_WHILE:
1174                         control_statement_count++;
1175                         loop_break_save = exten->loop_break; /* save them, then restore before leaving */
1176                         loop_continue_save = exten->loop_continue;
1177                         snprintf(new_label,sizeof(new_label),"while-%s-%d", label, control_statement_count);
1178                         while_test = new_prio();
1179                         while_loop = new_prio();
1180                         while_end = new_prio();
1181                         while_test->type = AEL_FOR_CONTROL;
1182                         while_test->goto_false = while_end;
1183                         while_loop->type = AEL_CONTROL1; /* simple goto */
1184                         while_end->type = AEL_APPCALL;
1185                         snprintf(buf1,sizeof(buf1),"$[%s]",p->u1.str);
1186                         while_test->app = 0;
1187                         while_test->appargs = strdup(buf1);
1188                         while_loop->goto_true = while_test;
1189                         snprintf(buf1,sizeof(buf1),"Finish while-%s-%d", label, control_statement_count);
1190                         while_end->app = strdup("NoOp");
1191                         while_end->appargs = strdup(buf1);
1192
1193                         linkprio(exten, while_test);
1194                         
1195                         /* now, put the body of the for loop here */
1196                         exten->loop_break = while_end;
1197                         exten->loop_continue = while_test;
1198                         
1199                         gen_prios(exten, new_label, p->u2.statements, mother_exten, this_context); /* this will link in all the while body statements here */
1200
1201                         linkprio(exten, while_loop);
1202                         linkprio(exten, while_end);
1203                         
1204                         
1205                         exten->loop_break = loop_break_save;
1206                         exten->loop_continue = loop_continue_save;
1207                         while_loop->origin = p;
1208                         break;
1209
1210                 case PV_SWITCH:
1211                         control_statement_count++;
1212                         local_control_statement_count = control_statement_count;
1213                         loop_break_save = exten->loop_break; /* save them, then restore before leaving */
1214                         loop_continue_save = exten->loop_continue;
1215                         snprintf(new_label,sizeof(new_label),"sw-%s-%d", label, control_statement_count);
1216
1217                         switch_test = new_prio();
1218                         switch_end = new_prio();
1219                         switch_test->type = AEL_APPCALL;
1220                         switch_end->type = AEL_APPCALL;
1221                         snprintf(buf1,sizeof(buf1),"sw-%d-%s,10",control_statement_count, p->u1.str);
1222                         switch_test->app = strdup("Goto");
1223                         switch_test->appargs = strdup(buf1);
1224                         snprintf(buf1,sizeof(buf1),"Finish switch-%s-%d", label, control_statement_count);
1225                         switch_end->app = strdup("NoOp");
1226                         switch_end->appargs = strdup(buf1);
1227                         switch_end->origin = p;
1228                         switch_end->exten = exten;
1229
1230                         linkprio(exten, switch_test);
1231                         linkprio(exten, switch_end);
1232                         
1233                         exten->loop_break = switch_end;
1234                         exten->loop_continue = 0;
1235                         default_exists = 0;
1236                         
1237                         for (p2=p->u2.statements; p2; p2=p2->next) {
1238                                 /* now, for each case/default put the body of the for loop here */
1239                                 if (p2->type == PV_CASE) {
1240                                         /* ok, generate a extension and link it in */
1241                                         switch_case = new_exten();
1242                                         switch_case->context = this_context;
1243                                         switch_case->is_switch = 1;
1244                                         /* the break/continue locations are inherited from parent */
1245                                         switch_case->loop_break = exten->loop_break;
1246                                         switch_case->loop_continue = exten->loop_continue;
1247                                         
1248                                         linkexten(exten,switch_case);
1249                                         snprintf(buf1,sizeof(buf1),"sw-%d-%s", local_control_statement_count, p2->u1.str);
1250                                         switch_case->name = strdup(buf1);
1251                                         snprintf(new_label,sizeof(new_label),"sw-%s-%s-%d", label, p2->u1.str, local_control_statement_count);
1252                                         
1253                                         gen_prios(switch_case, new_label, p2->u2.statements, exten, this_context); /* this will link in all the case body statements here */
1254
1255                                         /* here is where we write code to "fall thru" to the next case... if there is one... */
1256                                         for (p3=p2->u2.statements; p3; p3=p3->next) {
1257                                                 if (!p3->next)
1258                                                         break;
1259                                         }
1260                                         /* p3 now points the last statement... */
1261                                         if (!p3 || ( p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN) ) {
1262                                                 /* is there a following CASE/PATTERN/DEFAULT? */
1263                                                 if (p2->next && p2->next->type == PV_CASE) {
1264                                                         fall_thru = new_prio();
1265                                                         fall_thru->type = AEL_APPCALL;
1266                                                         fall_thru->app = strdup("Goto");
1267                                                         snprintf(buf1,sizeof(buf1),"sw-%d-%s,10",local_control_statement_count, p2->next->u1.str);
1268                                                         fall_thru->appargs = strdup(buf1);
1269                                                         linkprio(switch_case, fall_thru);
1270                                                 } else if (p2->next && p2->next->type == PV_PATTERN) {
1271                                                         fall_thru = new_prio();
1272                                                         fall_thru->type = AEL_APPCALL;
1273                                                         fall_thru->app = strdup("Goto");
1274                                                         gen_match_to_pattern(p2->next->u1.str, buf2);
1275                                                         snprintf(buf1,sizeof(buf1),"sw-%d-%s,10", local_control_statement_count, buf2);
1276                                                         fall_thru->appargs = strdup(buf1);
1277                                                         linkprio(switch_case, fall_thru);
1278                                                 } else if (p2->next && p2->next->type == PV_DEFAULT) {
1279                                                         fall_thru = new_prio();
1280                                                         fall_thru->type = AEL_APPCALL;
1281                                                         fall_thru->app = strdup("Goto");
1282                                                         snprintf(buf1,sizeof(buf1),"sw-%d-.,10",local_control_statement_count);
1283                                                         fall_thru->appargs = strdup(buf1);
1284                                                         linkprio(switch_case, fall_thru);
1285                                                 } else if (!p2->next) {
1286                                                         fall_thru = new_prio();
1287                                                         fall_thru->type = AEL_CONTROL1;
1288                                                         fall_thru->goto_true = switch_end;
1289                                                         fall_thru->app = strdup("Goto");
1290                                                         linkprio(switch_case, fall_thru);
1291                                                 }
1292                                         }
1293                                         if (switch_case->return_needed) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */
1294                                                 char buf[2000];
1295                                                 struct ael_priority *np2 = new_prio();
1296                                                 np2->type = AEL_APPCALL;
1297                                                 np2->app = strdup("NoOp");
1298                                                 snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name);
1299                                                 np2->appargs = strdup(buf);
1300                                                 linkprio(switch_case, np2);
1301                                                 switch_case-> return_target = np2;
1302                                         }
1303                                 } else if (p2->type == PV_PATTERN) {
1304                                         /* ok, generate a extension and link it in */
1305                                         switch_case = new_exten();
1306                                         switch_case->context = this_context;
1307                                         switch_case->is_switch = 1;
1308                                         /* the break/continue locations are inherited from parent */
1309                                         switch_case->loop_break = exten->loop_break;
1310                                         switch_case->loop_continue = exten->loop_continue;
1311                                         
1312                                         linkexten(exten,switch_case);
1313                                         snprintf(buf1,sizeof(buf1),"_sw-%d-%s", local_control_statement_count, p2->u1.str);
1314                                         switch_case->name = strdup(buf1);
1315                                         snprintf(new_label,sizeof(new_label),"sw-%s-%s-%d", label, p2->u1.str, local_control_statement_count);
1316                                         
1317                                         gen_prios(switch_case, new_label, p2->u2.statements, exten, this_context); /* this will link in all the while body statements here */
1318                                         /* here is where we write code to "fall thru" to the next case... if there is one... */
1319                                         for (p3=p2->u2.statements; p3; p3=p3->next) {
1320                                                 if (!p3->next)
1321                                                         break;
1322                                         }
1323                                         /* p3 now points the last statement... */
1324                                         if (!p3 || ( p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN)) {
1325                                                 /* is there a following CASE/PATTERN/DEFAULT? */
1326                                                 if (p2->next && p2->next->type == PV_CASE) {
1327                                                         fall_thru = new_prio();
1328                                                         fall_thru->type = AEL_APPCALL;
1329                                                         fall_thru->app = strdup("Goto");
1330                                                         snprintf(buf1,sizeof(buf1),"sw-%d-%s,10",local_control_statement_count, p2->next->u1.str);
1331                                                         fall_thru->appargs = strdup(buf1);
1332                                                         linkprio(switch_case, fall_thru);
1333                                                 } else if (p2->next && p2->next->type == PV_PATTERN) {
1334                                                         fall_thru = new_prio();
1335                                                         fall_thru->type = AEL_APPCALL;
1336                                                         fall_thru->app = strdup("Goto");
1337                                                         gen_match_to_pattern(p2->next->u1.str, buf2);
1338                                                         snprintf(buf1,sizeof(buf1),"sw-%d-%s,10",local_control_statement_count, buf2);
1339                                                         fall_thru->appargs = strdup(buf1);
1340                                                         linkprio(switch_case, fall_thru);
1341                                                 } else if (p2->next && p2->next->type == PV_DEFAULT) {
1342                                                         fall_thru = new_prio();
1343                                                         fall_thru->type = AEL_APPCALL;
1344                                                         fall_thru->app = strdup("Goto");
1345                                                         snprintf(buf1,sizeof(buf1),"sw-%d-.,10",local_control_statement_count);
1346                                                         fall_thru->appargs = strdup(buf1);
1347                                                         linkprio(switch_case, fall_thru);
1348                                                 } else if (!p2->next) {
1349                                                         fall_thru = new_prio();
1350                                                         fall_thru->type = AEL_CONTROL1;
1351                                                         fall_thru->goto_true = switch_end;
1352                                                         fall_thru->app = strdup("Goto");
1353                                                         linkprio(switch_case, fall_thru);
1354                                                 }
1355                                         }
1356                                         if (switch_case->return_needed) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */
1357                                                 char buf[2000];
1358                                                 struct ael_priority *np2 = new_prio();
1359                                                 np2->type = AEL_APPCALL;
1360                                                 np2->app = strdup("NoOp");
1361                                                 snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name);
1362                                                 np2->appargs = strdup(buf);
1363                                                 linkprio(switch_case, np2);
1364                                                 switch_case-> return_target = np2;
1365                                         }
1366                                 } else if (p2->type == PV_DEFAULT) {
1367                                         default_exists++;
1368                                         /* ok, generate a extension and link it in */
1369                                         switch_case = new_exten();
1370                                         switch_case->context = this_context;
1371                                         switch_case->is_switch = 1;
1372                                         /* the break/continue locations are inherited from parent */
1373                                         switch_case->loop_break = exten->loop_break;
1374                                         switch_case->loop_continue = exten->loop_continue;
1375                                         linkexten(exten,switch_case);
1376                                         snprintf(buf1,sizeof(buf1),"_sw-%d-.", local_control_statement_count);
1377                                         switch_case->name = strdup(buf1);
1378                                         
1379                                         snprintf(new_label,sizeof(new_label),"sw-%s-default-%d", label, local_control_statement_count);
1380                                         
1381                                         gen_prios(switch_case, new_label, p2->u2.statements, exten, this_context); /* this will link in all the while body statements here */
1382                                         
1383                                         /* here is where we write code to "fall thru" to the next case... if there is one... */
1384                                         for (p3=p2->u2.statements; p3; p3=p3->next) {
1385                                                 if (!p3->next)
1386                                                         break;
1387                                         }
1388                                         /* p3 now points the last statement... */
1389                                         if (!p3 || (p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN)) {
1390                                                 /* is there a following CASE/PATTERN/DEFAULT? */
1391                                                 if (p2->next && p2->next->type == PV_CASE) {
1392                                                         fall_thru = new_prio();
1393                                                         fall_thru->type = AEL_APPCALL;
1394                                                         fall_thru->app = strdup("Goto");
1395                                                         snprintf(buf1,sizeof(buf1),"sw-%d-%s,10",local_control_statement_count, p2->next->u1.str);
1396                                                         fall_thru->appargs = strdup(buf1);
1397                                                         linkprio(switch_case, fall_thru);
1398                                                 } else if (p2->next && p2->next->type == PV_PATTERN) {
1399                                                         fall_thru = new_prio();
1400                                                         fall_thru->type = AEL_APPCALL;
1401                                                         fall_thru->app = strdup("Goto");
1402                                                         gen_match_to_pattern(p2->next->u1.str, buf2);
1403                                                         snprintf(buf1,sizeof(buf1),"sw-%d-%s,10",local_control_statement_count, buf2);
1404                                                         fall_thru->appargs = strdup(buf1);
1405                                                         linkprio(switch_case, fall_thru);
1406                                                 } else if (p2->next && p2->next->type == PV_DEFAULT) {
1407                                                         fall_thru = new_prio();
1408                                                         fall_thru->type = AEL_APPCALL;
1409                                                         fall_thru->app = strdup("Goto");
1410                                                         snprintf(buf1,sizeof(buf1),"sw-%d-.,10",local_control_statement_count);
1411                                                         fall_thru->appargs = strdup(buf1);
1412                                                         linkprio(switch_case, fall_thru);
1413                                                 } else if (!p2->next) {
1414                                                         fall_thru = new_prio();
1415                                                         fall_thru->type = AEL_CONTROL1;
1416                                                         fall_thru->goto_true = switch_end;
1417                                                         fall_thru->app = strdup("Goto");
1418                                                         linkprio(switch_case, fall_thru);
1419                                                 }
1420                                         }
1421                                         if (switch_case->return_needed) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */
1422                                                 char buf[2000];
1423                                                 struct ael_priority *np2 = new_prio();
1424                                                 np2->type = AEL_APPCALL;
1425                                                 np2->app = strdup("NoOp");
1426                                                 snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name);
1427                                                 np2->appargs = strdup(buf);
1428                                                 linkprio(switch_case, np2);
1429                                                 switch_case-> return_target = np2;
1430                                         }
1431                                 } else {
1432                                         /* what could it be??? */
1433                                 }
1434                         }
1435                         
1436                         exten->loop_break = loop_break_save;
1437                         exten->loop_continue = loop_continue_save;
1438                         switch_test->origin = p;
1439                         switch_end->origin = p;
1440                         break;
1441
1442                 case PV_MACRO_CALL:
1443                         pr = new_prio();
1444                         pr->type = AEL_APPCALL;
1445                         snprintf(buf1,sizeof(buf1),"%s,s,1", p->u1.str);
1446                         first = 1;
1447                         for (p2 = p->u2.arglist; p2; p2 = p2->next) {
1448                                 if (first)
1449                                 {
1450                                         strcat(buf1,"(");
1451                                         first = 0;
1452                                 }
1453                                 else
1454                                         strcat(buf1,",");
1455                                 strcat(buf1,p2->u1.str);
1456                         }
1457                         if (!first)
1458                                 strcat(buf1,")");
1459
1460                         pr->app = strdup("Gosub");
1461                         pr->appargs = strdup(buf1);
1462                         pr->origin = p;
1463                         linkprio(exten, pr);
1464                         break;
1465
1466                 case PV_APPLICATION_CALL:
1467                         pr = new_prio();
1468                         pr->type = AEL_APPCALL;
1469                         buf1[0] = 0;
1470                         for (p2 = p->u2.arglist; p2; p2 = p2->next) {
1471                                 if (p2 != p->u2.arglist )
1472                                         strcat(buf1,",");
1473                                 /*substitute_commas(p2->u1.str); */
1474                                 strcat(buf1,p2->u1.str);
1475                         }
1476                         pr->app = strdup(p->u1.str);
1477                         pr->appargs = strdup(buf1);
1478                         pr->origin = p;
1479                         linkprio(exten, pr);
1480                         break;
1481
1482                 case PV_BREAK:
1483                         pr = new_prio();
1484                         pr->type = AEL_CONTROL1; /* simple goto */
1485                         pr->goto_true = exten->loop_break;
1486                         pr->origin = p;
1487                         linkprio(exten, pr);
1488                         break;
1489
1490                 case PV_RETURN: /* hmmmm */
1491                         pr = new_prio();
1492                         pr->type = AEL_RETURN; /* simple Return */
1493                         /* exten->return_needed++; */
1494                         pr->app = strdup("Return");
1495                         pr->appargs = strdup("");
1496                         pr->origin = p;
1497                         linkprio(exten, pr);
1498                         break;
1499
1500                 case PV_CONTINUE:
1501                         pr = new_prio();
1502                         pr->type = AEL_CONTROL1; /* simple goto */
1503                         pr->goto_true = exten->loop_continue;
1504                         pr->origin = p;
1505                         linkprio(exten, pr);
1506                         break;
1507
1508                 case PV_IFTIME:
1509                         control_statement_count++;
1510                         snprintf(new_label,sizeof(new_label),"iftime-%s-%d", label, control_statement_count);
1511                         
1512                         if_test = new_prio();
1513                         if_test->type = AEL_IFTIME_CONTROL;
1514                         snprintf(buf1,sizeof(buf1),"%s,%s,%s,%s",
1515                                          p->u1.list->u1.str, 
1516                                          p->u1.list->next->u1.str, 
1517                                          p->u1.list->next->next->u1.str, 
1518                                          p->u1.list->next->next->next->u1.str);
1519                         if_test->app = 0;
1520                         if_test->appargs = strdup(buf1);
1521                         if_test->origin = p;
1522
1523                         if_end = new_prio();
1524                         if_end->type = AEL_APPCALL;
1525                         snprintf(buf1,sizeof(buf1),"Finish iftime-%s-%d", label, control_statement_count);
1526                         if_end->app = strdup("NoOp");
1527                         if_end->appargs = strdup(buf1);
1528
1529                         if (p->u3.else_statements) {
1530                                 if_skip = new_prio();
1531                                 if_skip->type = AEL_CONTROL1; /* simple goto */
1532                                 if_skip->goto_true = if_end;
1533                                 if_skip->origin  = p;
1534
1535                         } else {
1536                                 if_skip = 0;
1537
1538                                 if_test->goto_false = if_end;
1539                         }
1540
1541                         if_false = new_prio();
1542                         if_false->type = AEL_CONTROL1;
1543                         if (p->u3.else_statements) {
1544                                 if_false->goto_true = if_skip; /* +1 */
1545                         } else {
1546                                 if_false->goto_true = if_end;
1547                         }
1548                         
1549                         /* link & load! */
1550                         linkprio(exten, if_test);
1551                         linkprio(exten, if_false);
1552                         
1553                         /* now, put the body of the if here */
1554                         
1555                         gen_prios(exten, new_label, p->u2.statements, mother_exten, this_context); /* this will link in all the statements here */
1556                         
1557                         if (p->u3.else_statements) {
1558                                 linkprio(exten, if_skip);
1559                                 gen_prios(exten, new_label, p->u3.else_statements, mother_exten, this_context); /* this will link in all the statements here */
1560
1561                         }
1562                         
1563                         linkprio(exten, if_end);
1564                         
1565                         break;
1566
1567                 case PV_RANDOM:
1568                 case PV_IF:
1569                         control_statement_count++;
1570                         snprintf(new_label,sizeof(new_label),"if-%s-%d", label, control_statement_count);
1571                         
1572                         if_test = new_prio();
1573                         if_end = new_prio();
1574                         if_test->type = AEL_IF_CONTROL;
1575                         if_end->type = AEL_APPCALL;
1576                         if ( p->type == PV_RANDOM )
1577                                 snprintf(buf1,sizeof(buf1),"$[${RAND(0,99)} < (%s)]",p->u1.str);
1578                         else
1579                                 snprintf(buf1,sizeof(buf1),"$[%s]",p->u1.str);
1580                         if_test->app = 0;
1581                         if_test->appargs = strdup(buf1);
1582                         snprintf(buf1,sizeof(buf1),"Finish if-%s-%d", label, control_statement_count);
1583                         if_end->app = strdup("NoOp");
1584                         if_end->appargs = strdup(buf1);
1585                         if_test->origin = p;
1586                         
1587                         if (p->u3.else_statements) {
1588                                 if_skip = new_prio();
1589                                 if_skip->type = AEL_CONTROL1; /* simple goto */
1590                                 if_skip->goto_true = if_end;
1591                                 if_test->goto_false = if_skip;;
1592                         } else {
1593                                 if_skip = 0;
1594                                 if_test->goto_false = if_end;;
1595                         }
1596                         
1597                         /* link & load! */
1598                         linkprio(exten, if_test);
1599                         
1600                         /* now, put the body of the if here */
1601                         
1602                         gen_prios(exten, new_label, p->u2.statements, mother_exten, this_context); /* this will link in all the statements here */
1603                         
1604                         if (p->u3.else_statements) {
1605                                 linkprio(exten, if_skip);
1606                                 gen_prios(exten, new_label, p->u3.else_statements, mother_exten, this_context); /* this will link in all the statements here */
1607
1608                         }
1609                         
1610                         linkprio(exten, if_end);
1611                         
1612                         break;
1613
1614                 case PV_STATEMENTBLOCK:
1615                         gen_prios(exten, label, p->u1.list, mother_exten, this_context ); /* recurse into the block */
1616                         break;
1617
1618                 case PV_CATCH:
1619                         control_statement_count++;
1620                         /* generate an extension with name of catch, put all catch stats
1621                            into this exten! */
1622                         switch_case = new_exten();
1623                         switch_case->context = this_context;
1624                         linkexten(exten,switch_case);
1625                         switch_case->name = strdup(p->u1.str);
1626                         snprintf(new_label,sizeof(new_label),"catch-%s-%d",p->u1.str, control_statement_count);
1627                         
1628                         gen_prios(switch_case, new_label, p->u2.statements,mother_exten,this_context); /* this will link in all the catch body statements here */
1629                         if (switch_case->return_needed) { /* returns now generate a Return() app call, no longer a goto to the end of the exten */
1630                                 char buf[2000];
1631                                 struct ael_priority *np2 = new_prio();
1632                                 np2->type = AEL_APPCALL;
1633                                 np2->app = strdup("NoOp");
1634                                 snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name);
1635                                 np2->appargs = strdup(buf);
1636                                 linkprio(switch_case, np2);
1637                                 switch_case-> return_target = np2;
1638                         }
1639
1640                         break;
1641                 default:
1642                         break;
1643                 }
1644         }
1645 }
1646
1647 static pval *get_extension_or_contxt(pval *p)
1648 {
1649         while( p && p->type != PV_EXTENSION && p->type != PV_CONTEXT && p->type != PV_MACRO ) {
1650                 
1651                 p = p->dad;
1652         }
1653         
1654         return p;
1655 }
1656
1657 static pval *get_contxt(pval *p)
1658 {
1659         while( p && p->type != PV_CONTEXT && p->type != PV_MACRO ) {
1660                 
1661                 p = p->dad;
1662         }
1663         
1664         return p;
1665 }
1666
1667 static int aeldebug = 0;
1668
1669 /* interface stuff */
1670
1671 /* if all the below are static, who cares if they are present? */
1672
1673 static int pbx_load_module(void)
1674 {
1675         int errs, sem_err, sem_warn, sem_note;
1676         char *rfilename;
1677         struct ast_context *local_contexts=NULL, *con;
1678         struct pval *parse_tree;
1679
1680         ast_log(LOG_NOTICE, "Starting AEL load process.\n");
1681         if (config[0] == '/')
1682                 rfilename = (char *)config;
1683         else {
1684                 rfilename = alloca(strlen(config) + strlen(ast_config_AST_CONFIG_DIR) + 2);
1685                 sprintf(rfilename, "%s/%s", ast_config_AST_CONFIG_DIR, config);
1686         }
1687         ast_log(LOG_NOTICE, "AEL load process: calculated config file name '%s'.\n", rfilename);
1688
1689         if (access(rfilename,R_OK) != 0) {
1690                 ast_log(LOG_NOTICE, "File %s not found; AEL declining load\n", rfilename);
1691                 return AST_MODULE_LOAD_DECLINE;
1692         }
1693         
1694         parse_tree = ael2_parse(rfilename, &errs);
1695         ast_log(LOG_NOTICE, "AEL load process: parsed config file name '%s'.\n", rfilename);
1696         ael2_semantic_check(parse_tree, &sem_err, &sem_warn, &sem_note);
1697         if (errs == 0 && sem_err == 0) {
1698                 ast_log(LOG_NOTICE, "AEL load process: checked config file name '%s'.\n", rfilename);
1699                 ast_compile_ael2(&local_contexts, parse_tree);
1700                 ast_log(LOG_NOTICE, "AEL load process: compiled config file name '%s'.\n", rfilename);
1701                 
1702                 ast_merge_contexts_and_delete(&local_contexts, registrar);
1703                 ast_log(LOG_NOTICE, "AEL load process: merged config file name '%s'.\n", rfilename);
1704                 for (con = ast_walk_contexts(NULL); con; con = ast_walk_contexts(con))
1705                         ast_context_verify_includes(con);
1706                 ast_log(LOG_NOTICE, "AEL load process: verified config file name '%s'.\n", rfilename);
1707         } else {
1708                 ast_log(LOG_ERROR, "Sorry, but %d syntax errors and %d semantic errors were detected. It doesn't make sense to compile.\n", errs, sem_err);
1709                 destroy_pval(parse_tree); /* free up the memory */
1710                 return AST_MODULE_LOAD_DECLINE;
1711         }
1712         destroy_pval(parse_tree); /* free up the memory */
1713         
1714         return AST_MODULE_LOAD_SUCCESS;
1715 }
1716
1717 /* CLI interface */
1718 static int ael2_debug_read(int fd, int argc, char *argv[])
1719 {
1720         aeldebug |= DEBUG_READ;
1721         return 0;
1722 }
1723
1724 static int ael2_debug_tokens(int fd, int argc, char *argv[])
1725 {
1726         aeldebug |= DEBUG_TOKENS;
1727         return 0;
1728 }
1729
1730 static int ael2_debug_macros(int fd, int argc, char *argv[])
1731 {
1732         aeldebug |= DEBUG_MACROS;
1733         return 0;
1734 }
1735
1736 static int ael2_debug_contexts(int fd, int argc, char *argv[])
1737 {
1738         aeldebug |= DEBUG_CONTEXTS;
1739         return 0;
1740 }
1741
1742 static int ael2_no_debug(int fd, int argc, char *argv[])
1743 {
1744         aeldebug = 0;
1745         return 0;
1746 }
1747
1748 static int ael2_reload(int fd, int argc, char *argv[])
1749 {
1750         return (pbx_load_module());
1751 }
1752
1753 static struct ast_cli_entry cli_ael_no_debug = {
1754         { "ael", "no", "debug", NULL },
1755         ael2_no_debug, NULL,
1756         NULL };
1757
1758 static struct ast_cli_entry cli_ael[] = {
1759         { { "ael", "reload", NULL },
1760         ael2_reload, "Reload AEL configuration" },
1761
1762         { { "ael", "debug", "read", NULL },
1763         ael2_debug_read, "Enable AEL read debug (does nothing)" },
1764
1765         { { "ael", "debug", "tokens", NULL },
1766         ael2_debug_tokens, "Enable AEL tokens debug (does nothing)" },
1767
1768         { { "ael", "debug", "macros", NULL },
1769         ael2_debug_macros, "Enable AEL macros debug (does nothing)" },
1770
1771         { { "ael", "debug", "contexts", NULL },
1772         ael2_debug_contexts, "Enable AEL contexts debug (does nothing)" },
1773
1774         { { "ael", "nodebug", NULL },
1775         ael2_no_debug, "Disable AEL debug messages",
1776         NULL, NULL, &cli_ael_no_debug },
1777 };
1778
1779 static int unload_module(void)
1780 {
1781         ast_context_destroy(NULL, registrar);
1782         ast_cli_unregister_multiple(cli_ael, sizeof(cli_ael) / sizeof(struct ast_cli_entry));
1783         return 0;
1784 }
1785
1786 static int load_module(void)
1787 {
1788         ast_cli_register_multiple(cli_ael, sizeof(cli_ael) / sizeof(struct ast_cli_entry));
1789         return (pbx_load_module());
1790 }
1791
1792 static int reload(void)
1793 {
1794         return pbx_load_module();
1795 }
1796
1797 #ifdef STANDALONE_AEL
1798 #define AST_MODULE "ael"
1799 int ael_external_load_module(void);
1800 int ael_external_load_module(void)
1801 {
1802         pbx_load_module();
1803         return 1;
1804 }
1805 #endif
1806
1807 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Asterisk Extension Language Compiler",
1808                 .load = load_module,
1809                 .unload = unload_module,
1810                 .reload = reload,
1811                );
1812
1813 #ifdef AAL_ARGCHECK
1814 static char *ael_funclist[] =
1815 {
1816         "AGENT",
1817         "ARRAY",
1818         "BASE64_DECODE",
1819         "BASE64_ENCODE",
1820         "CALLERID",
1821         "CDR",
1822         "CHANNEL",
1823         "CHECKSIPDOMAIN",
1824         "CHECK_MD5",
1825         "CURL",
1826         "CUT",
1827         "DB",
1828         "DB_EXISTS",
1829         "DUNDILOOKUP",
1830         "ENUMLOOKUP",
1831         "ENV",
1832         "EVAL",
1833         "EXISTS",
1834         "FIELDQTY",
1835         "FILTER",
1836         "GROUP",
1837         "GROUP_COUNT",
1838         "GROUP_LIST",
1839         "GROUP_MATCH_COUNT",
1840         "IAXPEER",
1841         "IF",
1842         "IFTIME",
1843         "ISNULL",
1844         "KEYPADHASH",
1845         "LANGUAGE",
1846         "LEN",
1847         "MATH",
1848         "MD5",
1849         "MUSICCLASS",
1850         "QUEUEAGENTCOUNT",
1851         "QUEUE_MEMBER_COUNT",
1852         "QUEUE_MEMBER_LIST",
1853         "QUOTE",
1854         "RAND",
1855         "REGEX",
1856         "SET",
1857         "SHA1",
1858         "SIPCHANINFO",
1859         "SIPPEER",
1860         "SIP_HEADER",
1861         "SORT",
1862         "STAT",
1863         "STRFTIME",
1864         "STRPTIME",
1865         "TIMEOUT",
1866         "TXTCIDNAME",
1867         "URIDECODE",
1868         "URIENCODE",
1869         "VMCOUNT"
1870 };
1871
1872
1873 int ael_is_funcname(char *name)
1874 {
1875         int s,t;
1876         t = sizeof(ael_funclist)/sizeof(char*);
1877         s = 0;
1878         while ((s < t) && strcasecmp(name, ael_funclist[s])) 
1879                 s++;
1880         if ( s < t )
1881                 return 1;
1882         else
1883                 return 0;
1884 }
1885 #endif