Mon Nov 24 15:34:19 2008

Asterisk developer's documentation


pbx_ael.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2006, Digium, Inc.
00005  *
00006  * Steve Murphy <murf@parsetree.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Compile symbolic Asterisk Extension Logic into Asterisk extensions, version 2.
00022  * 
00023  */
00024 
00025 #include "asterisk.h"
00026 
00027 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 136726 $")
00028 
00029 #include <sys/types.h>
00030 #include <stdlib.h>
00031 #include <unistd.h>
00032 #include <stdio.h>
00033 #include <string.h>
00034 #include <ctype.h>
00035 #include <errno.h>
00036 #include <regex.h>
00037 #include <sys/stat.h>
00038 
00039 #include "asterisk/pbx.h"
00040 #include "asterisk/config.h"
00041 #include "asterisk/module.h"
00042 #include "asterisk/logger.h"
00043 #include "asterisk/cli.h"
00044 #include "asterisk/app.h"
00045 #include "asterisk/callerid.h"
00046 #include "asterisk/ael_structs.h"
00047 #ifdef AAL_ARGCHECK
00048 #include "asterisk/argdesc.h"
00049 #include <dirent.h>
00050 #endif
00051 
00052 static char expr_output[2096];
00053 
00054 /* these functions are in ../ast_expr2.fl */
00055 
00056 #define DEBUG_READ   (1 << 0)
00057 #define DEBUG_TOKENS (1 << 1)
00058 #define DEBUG_MACROS (1 << 2)
00059 #define DEBUG_CONTEXTS (1 << 3)
00060 
00061 #define AST_MAX_FILENAME_LEN  256
00062 
00063 static char *config = "extensions.ael";
00064 static char *registrar = "pbx_ael";
00065 static int pbx_load_module(void);
00066 
00067 static int errs, warns;
00068 static int notes;
00069 
00070 #ifndef AAL_ARGCHECK
00071 /* for the time being, short circuit all the AAL related structures
00072    without permanently removing the code; after/during the AAL 
00073    development, this code can be properly re-instated 
00074 */
00075 
00076 /* null definitions for structs passed down the infrastructure */
00077 struct argapp
00078 {
00079    struct argapp *next;
00080 };
00081 
00082 #endif
00083 
00084 #ifdef AAL_ARGCHECK
00085 int option_matches_j( struct argdesc *should, pval *is, struct argapp *app);
00086 int option_matches( struct argdesc *should, pval *is, struct argapp *app);
00087 int ael_is_funcname(char *name);
00088 #endif
00089 
00090 int check_app_args(pval *appcall, pval *arglist, struct argapp *app);
00091 void check_pval(pval *item, struct argapp *apps, int in_globals);
00092 void check_pval_item(pval *item, struct argapp *apps, int in_globals);
00093 void check_switch_expr(pval *item, struct argapp *apps);
00094 void ast_expr_register_extra_error_info(char *errmsg);
00095 void ast_expr_clear_extra_error_info(void);
00096 int  ast_expr(char *expr, char *buf, int length);
00097 struct pval *find_macro(char *name);
00098 struct pval *find_context(char *name);
00099 struct pval *find_context(char *name);
00100 struct pval *find_macro(char *name);
00101 struct ael_priority *new_prio(void);
00102 struct ael_extension *new_exten(void);
00103 void linkprio(struct ael_extension *exten, struct ael_priority *prio, struct ael_extension *mother_exten);
00104 void destroy_extensions(struct ael_extension *exten);
00105 static void linkexten(struct ael_extension *exten, struct ael_extension *add);
00106 static void gen_prios(struct ael_extension *exten, char *label, pval *statement, struct ael_extension *mother_exten, struct ast_context *context );
00107 void set_priorities(struct ael_extension *exten);
00108 void add_extensions(struct ael_extension *exten);
00109 void ast_compile_ael2(struct ast_context **local_contexts, struct pval *root);
00110 void destroy_pval(pval *item);
00111 void destroy_pval_item(pval *item);
00112 int is_float(char *arg );
00113 int is_int(char *arg );
00114 int is_empty(char *arg);
00115 static pval *current_db=0;
00116 static pval *current_context=0;
00117 static pval *current_extension=0;
00118 
00119 static const char *match_context;
00120 static const char *match_exten;
00121 static const char *match_label;
00122 static int in_abstract_context;
00123 static int count_labels; /* true, put matcher in label counting mode */
00124 static int label_count;  /* labels are only meant to be counted in a context or exten */
00125 static int return_on_context_match;
00126 static pval *last_matched_label;
00127 struct pval *match_pval(pval *item);
00128 static void check_timerange(pval *p);
00129 static void check_dow(pval *DOW);
00130 static void check_day(pval *DAY);
00131 static void check_month(pval *MON);
00132 static void check_expr2_input(pval *expr, char *str);
00133 static int extension_matches(pval *here, const char *exten, const char *pattern);
00134 static void check_goto(pval *item);
00135 static void find_pval_goto_item(pval *item, int lev);
00136 static void find_pval_gotos(pval *item, int lev);
00137 
00138 static struct pval *find_label_in_current_context(char *exten, char *label, pval *curr_cont);
00139 static struct pval *find_first_label_in_current_context(char *label, pval *curr_cont);
00140 static void print_pval_list(FILE *fin, pval *item, int depth);
00141 
00142 static struct pval *find_label_in_current_extension(const char *label, pval *curr_ext);
00143 static struct pval *find_label_in_current_db(const char *context, const char *exten, const char *label);
00144 static pval *get_goto_target(pval *item);
00145 static int label_inside_case(pval *label);
00146 static void attach_exten(struct ael_extension **list, struct ael_extension *newmem);
00147 static void fix_gotos_in_extensions(struct ael_extension *exten);
00148 static pval *get_extension_or_contxt(pval *p);
00149 static pval *get_contxt(pval *p);
00150 static void remove_spaces_before_equals(char *str);
00151 static void substitute_commas(char *str);
00152 
00153 /* I am adding this code to substitute commas with vertbars in the args to apps */
00154 static void substitute_commas(char *str)
00155 {
00156    char *p = str;
00157    
00158    while (p && *p)
00159    {
00160       if (*p == ',' && ((p != str && *(p-1) != '\\')
00161             || p == str))
00162          *p = '|';
00163       if (*p == '\\' && *(p+1) == ',') { /* learning experience: the '\,' is turned into just ',' by pbx_config; So we need to do the same */
00164          char *q = p;
00165          while (*q) {  /* move the ',' and everything after it up 1 char */
00166             *q = *(q+1);
00167             q++;
00168          }
00169       }
00170       p++;
00171    }
00172 }
00173 
00174 
00175 /* PRETTY PRINTER FOR AEL:  ============================================================================= */
00176 
00177 static void print_pval(FILE *fin, pval *item, int depth)
00178 {
00179    int i;
00180    pval *lp;
00181    
00182    for (i=0; i<depth; i++) {
00183       fprintf(fin, "\t"); /* depth == indentation */
00184    }
00185    
00186    switch ( item->type ) {
00187    case PV_WORD:
00188       fprintf(fin,"%s;\n", item->u1.str); /* usually, words are encapsulated in something else */
00189       break;
00190       
00191    case PV_MACRO:
00192       fprintf(fin,"macro %s(", item->u1.str);
00193       for (lp=item->u2.arglist; lp; lp=lp->next) {
00194          if (lp != item->u2.arglist )
00195             fprintf(fin,", ");
00196          fprintf(fin,"%s", lp->u1.str);
00197       }
00198       fprintf(fin,") {\n");
00199       print_pval_list(fin,item->u3.macro_statements,depth+1);
00200       for (i=0; i<depth; i++) {
00201          fprintf(fin,"\t"); /* depth == indentation */
00202       }
00203       fprintf(fin,"};\n\n");
00204       break;
00205          
00206    case PV_CONTEXT:
00207       if ( item->u3.abstract )
00208          fprintf(fin,"abstract context %s {\n", item->u1.str);
00209       else
00210          fprintf(fin,"context %s {\n", item->u1.str);
00211       print_pval_list(fin,item->u2.statements,depth+1);
00212       for (i=0; i<depth; i++) {
00213          fprintf(fin,"\t"); /* depth == indentation */
00214       }
00215       fprintf(fin,"};\n\n");
00216       break;
00217          
00218    case PV_MACRO_CALL:
00219       fprintf(fin,"&%s(", item->u1.str);
00220       for (lp=item->u2.arglist; lp; lp=lp->next) {
00221          if ( lp != item->u2.arglist )
00222             fprintf(fin,", ");
00223          fprintf(fin,"%s", lp->u1.str);
00224       }
00225       fprintf(fin,");\n");
00226       break;
00227          
00228    case PV_APPLICATION_CALL:
00229       fprintf(fin,"%s(", item->u1.str);
00230       for (lp=item->u2.arglist; lp; lp=lp->next) {
00231          if ( lp != item->u2.arglist )
00232             fprintf(fin,",");
00233          fprintf(fin,"%s", lp->u1.str);
00234       }
00235       fprintf(fin,");\n");
00236       break;
00237          
00238    case PV_CASE:
00239       fprintf(fin,"case %s:\n", item->u1.str);
00240       print_pval_list(fin,item->u2.statements, depth+1);
00241       break;
00242          
00243    case PV_PATTERN:
00244       fprintf(fin,"pattern %s:\n", item->u1.str);
00245       print_pval_list(fin,item->u2.statements, depth+1);
00246       break;
00247          
00248    case PV_DEFAULT:
00249       fprintf(fin,"default:\n");
00250       print_pval_list(fin,item->u2.statements, depth+1);
00251       break;
00252          
00253    case PV_CATCH:
00254       fprintf(fin,"catch %s {\n", item->u1.str);
00255       print_pval_list(fin,item->u2.statements, depth+1);
00256       for (i=0; i<depth; i++) {
00257          fprintf(fin,"\t"); /* depth == indentation */
00258       }
00259       fprintf(fin,"};\n");
00260       break;
00261          
00262    case PV_SWITCHES:
00263       fprintf(fin,"switches {\n");
00264       print_pval_list(fin,item->u1.list,depth+1);
00265       for (i=0; i<depth; i++) {
00266          fprintf(fin,"\t"); /* depth == indentation */
00267       }
00268       fprintf(fin,"};\n");
00269       break;
00270          
00271    case PV_ESWITCHES:
00272       fprintf(fin,"eswitches {\n");
00273       print_pval_list(fin,item->u1.list,depth+1);
00274       for (i=0; i<depth; i++) {
00275          fprintf(fin,"\t"); /* depth == indentation */
00276       }
00277       fprintf(fin,"};\n");
00278       break;
00279          
00280    case PV_INCLUDES:
00281       fprintf(fin,"includes {\n");
00282       for (lp=item->u1.list; lp; lp=lp->next) {
00283          for (i=0; i<depth+1; i++) {
00284             fprintf(fin,"\t"); /* depth == indentation */
00285          }
00286          fprintf(fin,"%s", lp->u1.str); /* usually, words are encapsulated in something else */
00287          if ( lp->u2.arglist )
00288             fprintf(fin,"|%s|%s|%s|%s", 
00289                   lp->u2.arglist->u1.str,
00290                   lp->u2.arglist->next->u1.str,
00291                   lp->u2.arglist->next->next->u1.str,
00292                   lp->u2.arglist->next->next->next->u1.str
00293                );
00294          fprintf(fin,";\n"); /* usually, words are encapsulated in something else */
00295       }
00296       
00297       print_pval_list(fin,item->u1.list,depth+1);
00298       for (i=0; i<depth; i++) {
00299          fprintf(fin,"\t"); /* depth == indentation */
00300       }
00301       fprintf(fin,"};\n");
00302       break;
00303          
00304    case PV_STATEMENTBLOCK:
00305       fprintf(fin,"{\n");
00306       print_pval_list(fin,item->u1.list, depth+1);
00307       for (i=0; i<depth; i++) {
00308          fprintf(fin,"\t"); /* depth == indentation */
00309       }
00310       fprintf(fin,"};\n");
00311       break;
00312          
00313    case PV_VARDEC:
00314       fprintf(fin,"%s=%s;\n", item->u1.str, item->u2.val);
00315       break;
00316          
00317    case PV_GOTO:
00318       fprintf(fin,"goto %s", item->u1.list->u1.str);
00319       if ( item->u1.list->next )
00320          fprintf(fin,"|%s", item->u1.list->next->u1.str);
00321       if ( item->u1.list->next && item->u1.list->next->next )
00322          fprintf(fin,"|%s", item->u1.list->next->next->u1.str);
00323       fprintf(fin,"\n");
00324       break;
00325          
00326    case PV_LABEL:
00327       fprintf(fin,"%s:\n", item->u1.str);
00328       break;
00329          
00330    case PV_FOR:
00331       fprintf(fin,"for (%s; %s; %s)\n", item->u1.for_init, item->u2.for_test, item->u3.for_inc);
00332       print_pval_list(fin,item->u4.for_statements,depth+1);
00333       break;
00334          
00335    case PV_WHILE:
00336       fprintf(fin,"while (%s)\n", item->u1.str);
00337       print_pval_list(fin,item->u2.statements,depth+1);
00338       break;
00339          
00340    case PV_BREAK:
00341       fprintf(fin,"break;\n");
00342       break;
00343          
00344    case PV_RETURN:
00345       fprintf(fin,"return;\n");
00346       break;
00347          
00348    case PV_CONTINUE:
00349       fprintf(fin,"continue;\n");
00350       break;
00351          
00352    case PV_RANDOM:
00353    case PV_IFTIME:
00354    case PV_IF:
00355       if ( item->type == PV_IFTIME ) {
00356          
00357          fprintf(fin,"ifTime ( %s|%s|%s|%s )\n", 
00358                item->u1.list->u1.str, 
00359                item->u1.list->next->u1.str, 
00360                item->u1.list->next->next->u1.str, 
00361                item->u1.list->next->next->next->u1.str
00362                );
00363       } else if ( item->type == PV_RANDOM ) {
00364          fprintf(fin,"random ( %s )\n", item->u1.str );
00365       } else
00366          fprintf(fin,"if ( %s )\n", item->u1.str);
00367       if ( item->u2.statements && item->u2.statements->next ) {
00368          for (i=0; i<depth; i++) {
00369             fprintf(fin,"\t"); /* depth == indentation */
00370          }
00371          fprintf(fin,"{\n");
00372          print_pval_list(fin,item->u2.statements,depth+1);
00373          for (i=0; i<depth; i++) {
00374             fprintf(fin,"\t"); /* depth == indentation */
00375          }
00376          if ( item->u3.else_statements )
00377             fprintf(fin,"}\n");
00378          else
00379             fprintf(fin,"};\n");
00380       } else if (item->u2.statements ) {
00381          print_pval_list(fin,item->u2.statements,depth+1);
00382       } else {
00383          if (item->u3.else_statements )
00384             fprintf(fin, " {} ");
00385          else
00386             fprintf(fin, " {}; ");
00387       }
00388       if ( item->u3.else_statements ) {
00389          for (i=0; i<depth; i++) {
00390             fprintf(fin,"\t"); /* depth == indentation */
00391          }
00392          fprintf(fin,"else\n");
00393          print_pval_list(fin,item->u3.else_statements, depth);
00394       }
00395       break;
00396          
00397    case PV_SWITCH:
00398       fprintf(fin,"switch( %s ) {\n", item->u1.str);
00399       print_pval_list(fin,item->u2.statements,depth+1);
00400       for (i=0; i<depth; i++) {
00401          fprintf(fin,"\t"); /* depth == indentation */
00402       }
00403       fprintf(fin,"}\n");
00404       break;
00405          
00406    case PV_EXTENSION:
00407       if ( item->u4.regexten )
00408          fprintf(fin, "regexten ");
00409       if ( item->u3.hints )
00410          fprintf(fin,"hints(%s) ", item->u3.hints);
00411       
00412       fprintf(fin,"%s => \n", item->u1.str);
00413       print_pval_list(fin,item->u2.statements,depth+1);
00414       break;
00415          
00416    case PV_IGNOREPAT:
00417       fprintf(fin,"ignorepat => %s\n", item->u1.str);
00418       break;
00419          
00420    case PV_GLOBALS:
00421       fprintf(fin,"globals {\n");
00422       print_pval_list(fin,item->u1.statements,depth+1);
00423       for (i=0; i<depth; i++) {
00424          fprintf(fin,"\t"); /* depth == indentation */
00425       }
00426       fprintf(fin,"}\n");
00427       break;
00428    }
00429 }
00430 
00431 static void print_pval_list(FILE *fin, pval *item, int depth)
00432 {
00433    pval *i;
00434    
00435    for (i=item; i; i=i->next) {
00436       print_pval(fin, i, depth);
00437    }
00438 }
00439 
00440 #if 0
00441 static void ael2_print(char *fname, pval *tree)
00442 {
00443    FILE *fin = fopen(fname,"w");
00444    if ( !fin ) {
00445       ast_log(LOG_ERROR, "Couldn't open %s for writing.\n", fname);
00446       return;
00447    }
00448    print_pval_list(fin, tree, 0);
00449    fclose(fin);
00450 }
00451 #endif
00452 
00453 
00454 /* EMPTY TEMPLATE FUNCS FOR AEL TRAVERSAL:  ============================================================================= */
00455 
00456 void traverse_pval_template(pval *item, int depth);
00457 void traverse_pval_item_template(pval *item, int depth);
00458 
00459 
00460 void traverse_pval_item_template(pval *item, int depth)/* depth comes in handy for a pretty print (indentation),
00461                                             but you may not need it */
00462 {
00463    pval *lp;
00464    
00465    switch ( item->type ) {
00466    case PV_WORD:
00467       /* fields: item->u1.str == string associated with this (word). */
00468       break;
00469       
00470    case PV_MACRO:
00471       /* fields: item->u1.str     == name of macro
00472                  item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
00473                item->u2.arglist->u1.str  == argument
00474                item->u2.arglist->next   == next arg
00475 
00476                item->u3.macro_statements == pval list of statements in macro body.
00477       */
00478       for (lp=item->u2.arglist; lp; lp=lp->next) {
00479       
00480       }
00481       traverse_pval_item_template(item->u3.macro_statements,depth+1);
00482       break;
00483          
00484    case PV_CONTEXT:
00485       /* fields: item->u1.str     == name of context
00486                  item->u2.statements == pval list of statements in context body
00487                item->u3.abstract == int 1 if an abstract keyword were present
00488       */
00489       traverse_pval_item_template(item->u2.statements,depth+1);
00490       break;
00491          
00492    case PV_MACRO_CALL:
00493       /* fields: item->u1.str     == name of macro to call
00494                  item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
00495                item->u2.arglist->u1.str  == argument
00496                item->u2.arglist->next   == next arg
00497       */
00498       for (lp=item->u2.arglist; lp; lp=lp->next) {
00499       }
00500       break;
00501          
00502    case PV_APPLICATION_CALL:
00503       /* fields: item->u1.str     == name of application to call
00504                  item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
00505                item->u2.arglist->u1.str  == argument
00506                item->u2.arglist->next   == next arg
00507       */
00508       for (lp=item->u2.arglist; lp; lp=lp->next) {
00509       }
00510       break;
00511          
00512    case PV_CASE:
00513       /* fields: item->u1.str     == value of case
00514                  item->u2.statements == pval list of statements under the case
00515       */
00516       traverse_pval_item_template(item->u2.statements,depth+1);
00517       break;
00518          
00519    case PV_PATTERN:
00520       /* fields: item->u1.str     == value of case
00521                  item->u2.statements == pval list of statements under the case
00522       */
00523       traverse_pval_item_template(item->u2.statements,depth+1);
00524       break;
00525          
00526    case PV_DEFAULT:
00527       /* fields: 
00528                  item->u2.statements == pval list of statements under the case
00529       */
00530       traverse_pval_item_template(item->u2.statements,depth+1);
00531       break;
00532          
00533    case PV_CATCH:
00534       /* fields: item->u1.str     == name of extension to catch
00535                  item->u2.statements == pval list of statements in context body
00536       */
00537       traverse_pval_item_template(item->u2.statements,depth+1);
00538       break;
00539          
00540    case PV_SWITCHES:
00541       /* fields: item->u1.list     == pval list of PV_WORD elements, one per entry in the list
00542       */
00543       traverse_pval_item_template(item->u1.list,depth+1);
00544       break;
00545          
00546    case PV_ESWITCHES:
00547       /* fields: item->u1.list     == pval list of PV_WORD elements, one per entry in the list
00548       */
00549       traverse_pval_item_template(item->u1.list,depth+1);
00550       break;
00551          
00552    case PV_INCLUDES:
00553       /* fields: item->u1.list     == pval list of PV_WORD elements, one per entry in the list
00554                  item->u2.arglist  == pval list of 4 PV_WORD elements for time values
00555       */
00556       traverse_pval_item_template(item->u1.list,depth+1);
00557       traverse_pval_item_template(item->u2.arglist,depth+1);
00558       break;
00559          
00560    case PV_STATEMENTBLOCK:
00561       /* fields: item->u1.list     == pval list of statements in block, one per entry in the list
00562       */
00563       traverse_pval_item_template(item->u1.list,depth+1);
00564       break;
00565          
00566    case PV_VARDEC:
00567       /* fields: item->u1.str     == variable name
00568                  item->u2.val     == variable value to assign
00569       */
00570       break;
00571          
00572    case PV_GOTO:
00573       /* fields: item->u1.list     == pval list of PV_WORD target names, up to 3, in order as given by user.
00574                  item->u1.list->u1.str  == where the data on a PV_WORD will always be.
00575       */
00576       
00577       if ( item->u1.list->next )
00578          ;
00579       if ( item->u1.list->next && item->u1.list->next->next )
00580          ;
00581       
00582       break;
00583          
00584    case PV_LABEL:
00585       /* fields: item->u1.str     == label name
00586       */
00587       break;
00588          
00589    case PV_FOR:
00590       /* fields: item->u1.for_init     == a string containing the initalizer
00591                  item->u2.for_test     == a string containing the loop test
00592                  item->u3.for_inc      == a string containing the loop increment
00593 
00594                item->u4.for_statements == a pval list of statements in the for ()
00595       */
00596       traverse_pval_item_template(item->u4.for_statements,depth+1);
00597       break;
00598          
00599    case PV_WHILE:
00600       /* fields: item->u1.str        == the while conditional, as supplied by user
00601 
00602                item->u2.statements == a pval list of statements in the while ()
00603       */
00604       traverse_pval_item_template(item->u2.statements,depth+1);
00605       break;
00606          
00607    case PV_BREAK:
00608       /* fields: none
00609       */
00610       break;
00611          
00612    case PV_RETURN:
00613       /* fields: none
00614       */
00615       break;
00616          
00617    case PV_CONTINUE:
00618       /* fields: none
00619       */
00620       break;
00621          
00622    case PV_IFTIME:
00623       /* fields: item->u1.list        == there are 4 linked PV_WORDs here.
00624 
00625                item->u2.statements == a pval list of statements in the if ()
00626                item->u3.else_statements == a pval list of statements in the else
00627                                     (could be zero)
00628       */
00629       traverse_pval_item_template(item->u2.statements,depth+1);
00630       if ( item->u3.else_statements ) {
00631          traverse_pval_item_template(item->u3.else_statements,depth+1);
00632       }
00633       break;
00634          
00635    case PV_RANDOM:
00636       /* fields: item->u1.str        == the random number expression, as supplied by user
00637 
00638                item->u2.statements == a pval list of statements in the if ()
00639                item->u3.else_statements == a pval list of statements in the else
00640                                     (could be zero)
00641       */
00642       traverse_pval_item_template(item->u2.statements,depth+1);
00643       if ( item->u3.else_statements ) {
00644          traverse_pval_item_template(item->u3.else_statements,depth+1);
00645       }
00646       break;
00647          
00648    case PV_IF:
00649       /* fields: item->u1.str        == the if conditional, as supplied by user
00650 
00651                item->u2.statements == a pval list of statements in the if ()
00652                item->u3.else_statements == a pval list of statements in the else
00653                                     (could be zero)
00654       */
00655       traverse_pval_item_template(item->u2.statements,depth+1);
00656       if ( item->u3.else_statements ) {
00657          traverse_pval_item_template(item->u3.else_statements,depth+1);
00658       }
00659       break;
00660          
00661    case PV_SWITCH:
00662       /* fields: item->u1.str        == the switch expression
00663 
00664                item->u2.statements == a pval list of statements in the switch, 
00665                                     (will be case statements, most likely!)
00666       */
00667       traverse_pval_item_template(item->u2.statements,depth+1);
00668       break;
00669          
00670    case PV_EXTENSION:
00671       /* fields: item->u1.str        == the extension name, label, whatever it's called
00672 
00673                item->u2.statements == a pval list of statements in the extension
00674                item->u3.hints      == a char * hint argument
00675                item->u4.regexten   == an int boolean. non-zero says that regexten was specified
00676       */
00677       traverse_pval_item_template(item->u2.statements,depth+1);
00678       break;
00679          
00680    case PV_IGNOREPAT:
00681       /* fields: item->u1.str        == the ignorepat data
00682       */
00683       break;
00684          
00685    case PV_GLOBALS:
00686       /* fields: item->u1.statements     == pval list of statements, usually vardecs
00687       */
00688       traverse_pval_item_template(item->u1.statements,depth+1);
00689       break;
00690    }
00691 }
00692 
00693 void traverse_pval_template(pval *item, int depth) /* depth comes in handy for a pretty print (indentation),
00694                                          but you may not need it */
00695 {
00696    pval *i;
00697    
00698    for (i=item; i; i=i->next) {
00699       traverse_pval_item_template(i, depth);
00700    }
00701 }
00702 
00703 
00704 /* SEMANTIC CHECKING FOR AEL:  ============================================================================= */
00705 
00706 /*   (not all that is syntactically legal is good! */
00707 
00708 
00709 
00710 static int extension_matches(pval *here, const char *exten, const char *pattern)
00711 {
00712    int err1;
00713    regex_t preg;
00714    
00715    /* simple case, they match exactly, the pattern and exten name */
00716    if( !strcmp(pattern,exten) == 0 )
00717       return 1;
00718    
00719    if ( pattern[0] == '_' ) {
00720       char reg1[2000];
00721       const char *p;
00722       char *r = reg1;
00723       
00724       if ( strlen(pattern)*5 >= 2000 ) /* safety valve */ {
00725          ast_log(LOG_ERROR,"Error: The pattern %s is way too big. Pattern matching cancelled.\n",
00726                pattern);
00727          return 0;
00728       }
00729       /* form a regular expression from the pattern, and then match it against exten */
00730       *r++ = '^'; /* what if the extension is a pattern ?? */
00731       *r++ = '_'; /* what if the extension is a pattern ?? */
00732       *r++ = '?';
00733       for (p=pattern+1; *p; p++) {
00734          switch ( *p ) {
00735          case 'X':
00736             *r++ = '[';
00737             *r++ = '0';
00738             *r++ = '-';
00739             *r++ = '9';
00740             *r++ = 'X';
00741             *r++ = ']';
00742             break;
00743             
00744          case 'Z':
00745             *r++ = '[';
00746             *r++ = '1';
00747             *r++ = '-';
00748             *r++ = '9';
00749             *r++ = 'Z';
00750             *r++ = ']';
00751             break;
00752             
00753          case 'N':
00754             *r++ = '[';
00755             *r++ = '2';
00756             *r++ = '-';
00757             *r++ = '9';
00758             *r++ = 'N';
00759             *r++ = ']';
00760             break;
00761             
00762          case '[':
00763             while ( *p && *p != ']' ) {
00764                *r++ = *p++;
00765             }
00766             if ( *p != ']') {
00767                ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The extension pattern '%s' is missing a closing bracket \n",
00768                      here->filename, here->startline, here->endline, pattern);
00769             }
00770             break;
00771             
00772          case '.':
00773          case '!':
00774             *r++ = '.';
00775             *r++ = '*';
00776             break;
00777          case '*':
00778             *r++ = '\\';
00779             *r++ = '*';
00780             break;
00781          default:
00782             *r++ = *p;
00783             break;
00784             
00785          }
00786       }
00787       *r++ = '$'; /* what if the extension is a pattern ?? */
00788       *r++ = *p++; /* put in the closing null */
00789       err1 = regcomp(&preg, reg1, REG_NOSUB|REG_EXTENDED);
00790       if ( err1 ) {
00791          char errmess[500];
00792          regerror(err1,&preg,errmess,sizeof(errmess));
00793          regfree(&preg);
00794          ast_log(LOG_WARNING, "Regcomp of %s failed, error code %d\n",
00795                reg1, err1);
00796          return 0;
00797       }
00798       err1 = regexec(&preg, exten, 0, 0, 0);
00799       regfree(&preg);
00800       
00801       if ( err1 ) {
00802          /* ast_log(LOG_NOTICE,"*****************************[%d]Extension %s did not match %s(%s)\n",
00803             err1,exten, pattern, reg1); */
00804          return 0; /* no match */
00805       } else {
00806          /* ast_log(LOG_NOTICE,"*****************************Extension %s matched %s\n",
00807             exten, pattern); */
00808          return 1;
00809       }
00810       
00811       
00812    } else {
00813       if ( strcmp(exten,pattern) == 0 ) {
00814          return 1;
00815       } else
00816          return 0;
00817    }
00818 }
00819 
00820 
00821 static void check_expr2_input(pval *expr, char *str)
00822 {
00823    int spaces = strspn(str,"\t \n");
00824    if ( !strncmp(str+spaces,"$[",2) ) {
00825       ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The expression '%s' is redundantly wrapped in '$[ ]'. \n",
00826             expr->filename, expr->startline, expr->endline, str);
00827       warns++;
00828    }
00829 }
00830 
00831 static void check_includes(pval *includes)
00832 {
00833    struct pval *p4;
00834    for (p4=includes->u1.list; p4; p4=p4->next) {
00835       /* for each context pointed to, find it, then find a context/label that matches the
00836          target here! */
00837       char *incl_context = p4->u1.str;
00838       /* find a matching context name */
00839       struct pval *that_other_context = find_context(incl_context);
00840       if (!that_other_context && strcmp(incl_context, "parkedcalls") != 0) {
00841          ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The included context '%s' cannot be found.\n\
00842  (You may ignore this warning if '%s' exists in extensions.conf, or is created by another module. I cannot check for those.)\n",
00843                includes->filename, includes->startline, includes->endline, incl_context, incl_context);
00844          warns++;
00845       }
00846    }
00847 }
00848 
00849 
00850 static void check_timerange(pval *p)
00851 {
00852    char *times;
00853    char *e;
00854    int s1, s2;
00855    int e1, e2;
00856 
00857    times = ast_strdupa(p->u1.str);
00858 
00859    /* Star is all times */
00860    if (ast_strlen_zero(times) || !strcmp(times, "*")) {
00861       return;
00862    }
00863    /* Otherwise expect a range */
00864    e = strchr(times, '-');
00865    if (!e) {
00866       ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The time range format (%s) requires a '-' surrounded by two 24-hour times of day!\n",
00867             p->filename, p->startline, p->endline, times);
00868       warns++;
00869       return;
00870    }
00871    *e = '\0';
00872    e++;
00873    while (*e && !isdigit(*e)) 
00874       e++;
00875    if (!*e) {
00876       ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The time range format (%s) is missing the end time!\n",
00877             p->filename, p->startline, p->endline, p->u1.str);
00878       warns++;
00879    }
00880    if (sscanf(times, "%d:%d", &s1, &s2) != 2) {
00881       ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start time (%s) isn't quite right!\n",
00882             p->filename, p->startline, p->endline, times);
00883       warns++;
00884    }
00885    if (sscanf(e, "%d:%d", &e1, &e2) != 2) {
00886       ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end time (%s) isn't quite right!\n",
00887             p->filename, p->startline, p->endline, times);
00888       warns++;
00889    }
00890 
00891    s1 = s1 * 30 + s2/2;
00892    if ((s1 < 0) || (s1 >= 24*30)) {
00893       ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start time (%s) is out of range!\n",
00894             p->filename, p->startline, p->endline, times);
00895       warns++;
00896    }
00897    e1 = e1 * 30 + e2/2;
00898    if ((e1 < 0) || (e1 >= 24*30)) {
00899       ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end time (%s) is out of range!\n",
00900             p->filename, p->startline, p->endline, e);
00901       warns++;
00902    }
00903    return;
00904 }
00905 
00906 static char *days[] =
00907 {
00908    "sun",
00909    "mon",
00910    "tue",
00911    "wed",
00912    "thu",
00913    "fri",
00914    "sat",
00915 };
00916 
00917 /*! \brief  get_dow: Get day of week */
00918 static void check_dow(pval *DOW)
00919 {
00920    char *dow;
00921    char *c;
00922    /* The following line is coincidence, really! */
00923    int s, e;
00924    
00925    dow = ast_strdupa(DOW->u1.str);
00926 
00927    /* Check for all days */
00928    if (ast_strlen_zero(dow) || !strcmp(dow, "*"))
00929       return;
00930    /* Get start and ending days */
00931    c = strchr(dow, '-');
00932    if (c) {
00933       *c = '\0';
00934       c++;
00935    } else
00936       c = NULL;
00937    /* Find the start */
00938    s = 0;
00939    while ((s < 7) && strcasecmp(dow, days[s])) s++;
00940    if (s >= 7) {
00941       ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The day (%s) must be one of 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', or 'sat'!\n",
00942             DOW->filename, DOW->startline, DOW->endline, dow);
00943       warns++;
00944    }
00945    if (c) {
00946       e = 0;
00947       while ((e < 7) && strcasecmp(c, days[e])) e++;
00948       if (e >= 7) {
00949          ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end day (%s) must be one of 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', or 'sat'!\n",
00950                DOW->filename, DOW->startline, DOW->endline, c);
00951          warns++;
00952       }
00953    } else
00954       e = s;
00955 }
00956 
00957 static void check_day(pval *DAY)
00958 {
00959    char *day;
00960    char *c;
00961    /* The following line is coincidence, really! */
00962    int s, e;
00963 
00964    day = ast_strdupa(DAY->u1.str);
00965 
00966    /* Check for all days */
00967    if (ast_strlen_zero(day) || !strcmp(day, "*")) {
00968       return;
00969    }
00970    /* Get start and ending days */
00971    c = strchr(day, '-');
00972    if (c) {
00973       *c = '\0';
00974       c++;
00975    }
00976    /* Find the start */
00977    if (sscanf(day, "%d", &s) != 1) {
00978       ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start day of month (%s) must be a number!\n",
00979             DAY->filename, DAY->startline, DAY->endline, day);
00980       warns++;
00981    }
00982    else if ((s < 1) || (s > 31)) {
00983       ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start day of month (%s) must be a number in the range [1-31]!\n",
00984             DAY->filename, DAY->startline, DAY->endline, day);
00985       warns++;
00986    }
00987    s--;
00988    if (c) {
00989       if (sscanf(c, "%d", &e) != 1) {
00990          ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end day of month (%s) must be a number!\n",
00991                DAY->filename, DAY->startline, DAY->endline, c);
00992          warns++;
00993       }
00994       else if ((e < 1) || (e > 31)) {
00995          ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end day of month (%s) must be a number in the range [1-31]!\n",
00996                DAY->filename, DAY->startline, DAY->endline, day);
00997          warns++;
00998       }
00999       e--;
01000    } else
01001       e = s;
01002 }
01003 
01004 static char *months[] =
01005 {
01006    "jan",
01007    "feb",
01008    "mar",
01009    "apr",
01010    "may",
01011    "jun",
01012    "jul",
01013    "aug",
01014    "sep",
01015    "oct",
01016    "nov",
01017    "dec",
01018 };
01019 
01020 static void check_month(pval *MON)
01021 {
01022    char *mon;
01023    char *c;
01024    /* The following line is coincidence, really! */
01025    int s, e;
01026 
01027    mon = ast_strdupa(MON->u1.str);
01028 
01029    /* Check for all days */
01030    if (ast_strlen_zero(mon) || !strcmp(mon, "*")) 
01031       return ;
01032    /* Get start and ending days */
01033    c = strchr(mon, '-');
01034    if (c) {
01035       *c = '\0';
01036       c++;
01037    }
01038    /* Find the start */
01039    s = 0;
01040    while ((s < 12) && strcasecmp(mon, months[s])) s++;
01041    if (s >= 12) {
01042       ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start month (%s) must be a one of: 'jan', 'feb', ..., 'dec'!\n",
01043             MON->filename, MON->startline, MON->endline, mon);
01044       warns++;
01045    }
01046    if (c) {
01047       e = 0;
01048       while ((e < 12) && strcasecmp(mon, months[e])) e++;
01049       if (e >= 12) {
01050          ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end month (%s) must be a one of: 'jan', 'feb', ..., 'dec'!\n",
01051                MON->filename, MON->startline, MON->endline, c);
01052          warns++;
01053       }
01054    } else
01055       e = s;
01056 }
01057 
01058 static int check_break(pval *item)
01059 {
01060    pval *p = item;
01061    
01062    while( p && p->type != PV_MACRO && p->type != PV_CONTEXT ) /* early cutout, sort of */ {
01063       /* a break is allowed in WHILE, FOR, CASE, DEFAULT, PATTERN; otherwise, it don't make
01064          no sense */
01065       if( p->type == PV_CASE || p->type == PV_DEFAULT || p->type == PV_PATTERN 
01066          || p->type == PV_WHILE || p->type == PV_FOR   ) {
01067          return 1;
01068       }
01069       p = p->dad;
01070    }
01071    ast_log(LOG_ERROR,"Error: file %s, line %d-%d: 'break' not in switch, for, or while statement!\n",
01072          item->filename, item->startline, item->endline);
01073    errs++;
01074    
01075    return 0;
01076 }
01077 
01078 static int check_continue(pval *item)
01079 {
01080    pval *p = item;
01081    
01082    while( p && p->type != PV_MACRO && p->type != PV_CONTEXT ) /* early cutout, sort of */ {
01083       /* a break is allowed in WHILE, FOR, CASE, DEFAULT, PATTERN; otherwise, it don't make
01084          no sense */
01085       if( p->type == PV_WHILE || p->type == PV_FOR   ) {
01086          return 1;
01087       }
01088       p = p->dad;
01089    }
01090    ast_log(LOG_ERROR,"Error: file %s, line %d-%d: 'continue' not in 'for' or 'while' statement!\n",
01091          item->filename, item->startline, item->endline);
01092    errs++;
01093    
01094    return 0;
01095 }
01096 
01097 
01098 /* general purpose goto finder */
01099 
01100 static void check_label(pval *item)
01101 {
01102    /* basically, ensure that a label is not repeated in a context. Period.
01103       The method:  well, for each label, find the first label in the context
01104       with the same name. If it's not the current label, then throw an error. */
01105    struct pval *curr;
01106    struct pval *x;
01107    
01108    /* printf("==== check_label:   ====\n"); */
01109    if( !current_extension )
01110       curr = current_context;
01111    else
01112       curr = current_extension;
01113    
01114    x = find_first_label_in_current_context((char *)item->u1.str, curr);
01115    /* printf("Hey, check_label found with item = %x, and x is %x, and currcont is %x, label name is %s\n", item,x, current_context, (char *)item->u1.str); */
01116    if( x && x != item )
01117    {
01118       ast_log(LOG_ERROR,"Error: file %s, line %d-%d: Duplicate label %s! Previously defined at file %s, line %d.\n",
01119             item->filename, item->startline, item->endline, item->u1.str, x->filename, x->startline);
01120       errs++;
01121    }
01122    /* printf("<<<<< check_label:   ====\n"); */
01123 }
01124 
01125 static pval *get_goto_target(pval *item)
01126 {
01127    /* just one item-- the label should be in the current extension */
01128    pval *curr_ext = get_extension_or_contxt(item); /* containing exten, or macro */
01129    pval *curr_cont;
01130    
01131    if (item->u1.list && !item->u1.list->next && !strstr((item->u1.list)->u1.str,"${")) {
01132       struct pval *x = find_label_in_current_extension((char*)((item->u1.list)->u1.str), curr_ext);
01133          return x;
01134    }
01135 
01136    curr_cont = get_contxt(item);
01137 
01138    /* TWO items */
01139    if (item->u1.list->next && !item->u1.list->next->next) {
01140       if (!strstr((item->u1.list)->u1.str,"${") 
01141          && !strstr(item->u1.list->next->u1.str,"${") ) /* Don't try to match variables */ {
01142          struct pval *x = find_label_in_current_context((char *)item->u1.list->u1.str, (char *)item->u1.list->next->u1.str, curr_cont);
01143             return x;
01144       }
01145    }
01146    
01147    /* All 3 items! */
01148    if (item->u1.list->next && item->u1.list->next->next) {
01149       /* all three */
01150       pval *first = item->u1.list;
01151       pval *second = item->u1.list->next;
01152       pval *third = item->u1.list->next->next;
01153       
01154       if (!strstr((item->u1.list)->u1.str,"${") 
01155          && !strstr(item->u1.list->next->u1.str,"${")
01156          && !strstr(item->u1.list->next->next->u1.str,"${")) /* Don't try to match variables */ {
01157          struct pval *x = find_label_in_current_db((char*)first->u1.str, (char*)second->u1.str, (char*)third->u1.str);
01158          if (!x) {
01159 
01160             struct pval *p3;
01161             struct pval *that_context = find_context(item->u1.list->u1.str);
01162             
01163             /* the target of the goto could be in an included context!! Fancy that!! */
01164             /* look for includes in the current context */
01165             if (that_context) {
01166                for (p3=that_context->u2.statements; p3; p3=p3->next) {
01167                   if (p3->type == PV_INCLUDES) {
01168                      struct pval *p4;
01169                      for (p4=p3->u1.list; p4; p4=p4->next) {
01170                         /* for each context pointed to, find it, then find a context/label that matches the
01171                            target here! */
01172                         char *incl_context = p4->u1.str;
01173                         /* find a matching context name */
01174                         struct pval *that_other_context = find_context(incl_context);
01175                         if (that_other_context) {
01176                            struct pval *x3;
01177                            x3 = find_label_in_current_context((char *)item->u1.list->next->u1.str, (char *)item->u1.list->next->next->u1.str, that_other_context);
01178                            if (x3) {
01179                               return x3;
01180                            }
01181                         }
01182                      }
01183                   }
01184                }
01185             }
01186          }
01187          return x;
01188       }
01189    }
01190    return 0;
01191 }
01192 
01193 static void check_goto(pval *item)
01194 {
01195    /* check for the target of the goto-- does it exist? */
01196    if ( !(item->u1.list)->next && !(item->u1.list)->u1.str ) {
01197       ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto:  empty label reference found!\n",
01198             item->filename, item->startline, item->endline);
01199       errs++;
01200    }
01201    
01202    /* just one item-- the label should be in the current extension */
01203    
01204    if (item->u1.list && !item->u1.list->next && !strstr((item->u1.list)->u1.str,"${")) {
01205       struct pval *z = get_extension_or_contxt(item);
01206       struct pval *x = 0;
01207       if (z)
01208          x = find_label_in_current_extension((char*)((item->u1.list)->u1.str), z); /* if in macro, use current context instead */
01209       /* printf("Called find_label_in_current_extension with arg %s; current_extension is %x: %d\n",
01210          (char*)((item->u1.list)->u1.str), current_extension?current_extension:current_context, current_extension?current_extension->type:current_context->type); */
01211       if (!x) {
01212          ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto:  no label %s exists in the current extension!\n",
01213                item->filename, item->startline, item->endline, item->u1.list->u1.str);
01214          errs++;
01215       }
01216       else
01217          return;
01218    }
01219    
01220    /* TWO items */
01221    if (item->u1.list->next && !item->u1.list->next->next) {
01222       /* two items */
01223       /* printf("Calling find_label_in_current_context with args %s, %s\n",
01224          (char*)((item->u1.list)->u1.str), (char *)item->u1.list->next->u1.str); */
01225       if (!strstr((item->u1.list)->u1.str,"${") 
01226          && !strstr(item->u1.list->next->u1.str,"${") ) /* Don't try to match variables */ {
01227          struct pval *z = get_contxt(item);
01228          struct pval *x = 0;
01229          
01230          if (z)
01231             x = find_label_in_current_context((char *)item->u1.list->u1.str, (char *)item->u1.list->next->u1.str, z);
01232 
01233          if (!x) {
01234             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",
01235                   item->filename, item->startline, item->endline, item->u1.list->u1.str, item->u1.list->next->u1.str );
01236             errs++;
01237          }
01238          else
01239             return;
01240       }
01241    }
01242    
01243    /* All 3 items! */
01244    if (item->u1.list->next && item->u1.list->next->next) {
01245       /* all three */
01246       pval *first = item->u1.list;
01247       pval *second = item->u1.list->next;
01248       pval *third = item->u1.list->next->next;
01249       
01250       /* printf("Calling find_label_in_current_db with args %s, %s, %s\n",
01251          (char*)first->u1.str, (char*)second->u1.str, (char*)third->u1.str); */
01252       if (!strstr((item->u1.list)->u1.str,"${") 
01253          && !strstr(item->u1.list->next->u1.str,"${")
01254          && !strstr(item->u1.list->next->next->u1.str,"${")) /* Don't try to match variables */ {
01255          struct pval *x = find_label_in_current_db((char*)first->u1.str, (char*)second->u1.str, (char*)third->u1.str);
01256          if (!x) {
01257             struct pval *p3;
01258             struct pval *found = 0;
01259             struct pval *that_context = find_context(item->u1.list->u1.str);
01260             
01261             /* the target of the goto could be in an included context!! Fancy that!! */
01262             /* look for includes in the current context */
01263             if (that_context) {
01264                for (p3=that_context->u2.statements; p3; p3=p3->next) {
01265                   if (p3->type == PV_INCLUDES) {
01266                      struct pval *p4;
01267                      for (p4=p3->u1.list; p4; p4=p4->next) {
01268                         /* for each context pointed to, find it, then find a context/label that matches the
01269                            target here! */
01270                         char *incl_context = p4->u1.str;
01271                         /* find a matching context name */
01272                         struct pval *that_other_context = find_context(incl_context);
01273                         if (that_other_context) {
01274                            struct pval *x3;
01275                            x3 = find_label_in_current_context((char *)item->u1.list->next->u1.str, (char *)item->u1.list->next->next->u1.str, that_other_context);
01276                            if (x3) {
01277                               found = x3;
01278                               break;
01279                            }
01280                         }
01281                      }
01282                   }
01283                }
01284                if (!found) {
01285                   ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto:  no label %s|%s exists in the context %s or its inclusions!\n",
01286                         item->filename, item->startline, item->endline, item->u1.list->next->u1.str, item->u1.list->next->next->u1.str, item->u1.list->u1.str );
01287                   errs++;
01288                }
01289             } else {
01290                /* here is where code would go to check for target existence in extensions.conf files */
01291                ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: goto:  no context %s could be found that matches the goto target!\n",
01292                      item->filename, item->startline, item->endline, item->u1.list->u1.str);
01293                warns++; /* this is just a warning, because this context could be in extensions.conf or somewhere */
01294             }
01295          }
01296       }
01297    }
01298 }
01299    
01300 
01301 static void find_pval_goto_item(pval *item, int lev)
01302 {
01303    struct pval *p4;
01304    
01305    if (lev>100) {
01306       ast_log(LOG_ERROR,"find_pval_goto in infinite loop! item_type: %d\n\n", item->type);
01307       return;
01308    }
01309    
01310    switch ( item->type ) {
01311    case PV_MACRO:
01312       /* fields: item->u1.str     == name of macro
01313                  item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
01314                item->u2.arglist->u1.str  == argument
01315                item->u2.arglist->next   == next arg
01316 
01317                item->u3.macro_statements == pval list of statements in macro body.
01318       */
01319          
01320       /* printf("Descending into macro %s at line %d\n", item->u1.str, item->startline); */
01321       find_pval_gotos(item->u3.macro_statements,lev+1); /* if we're just searching for a context, don't bother descending into them */
01322       
01323       break;
01324          
01325    case PV_CONTEXT:
01326       /* fields: item->u1.str     == name of context
01327                  item->u2.statements == pval list of statements in context body
01328                item->u3.abstract == int 1 if an abstract keyword were present
01329       */
01330       break;
01331 
01332    case PV_CASE:
01333       /* fields: item->u1.str     == value of case
01334                  item->u2.statements == pval list of statements under the case
01335       */
01336       /* printf("Descending into Case of %s\n", item->u1.str); */
01337       find_pval_gotos(item->u2.statements,lev+1);
01338       break;
01339          
01340    case PV_PATTERN:
01341       /* fields: item->u1.str     == value of case
01342                  item->u2.statements == pval list of statements under the case
01343       */
01344       /* printf("Descending into Pattern of %s\n", item->u1.str); */
01345       find_pval_gotos(item->u2.statements,lev+1);
01346       break;
01347          
01348    case PV_DEFAULT:
01349       /* fields: 
01350                  item->u2.statements == pval list of statements under the case
01351       */
01352       /* printf("Descending into default\n"); */
01353       find_pval_gotos(item->u2.statements,lev+1);
01354       break;
01355          
01356    case PV_CATCH:
01357       /* fields: item->u1.str     == name of extension to catch
01358                  item->u2.statements == pval list of statements in context body
01359       */
01360       /* printf("Descending into catch of %s\n", item->u1.str); */
01361       find_pval_gotos(item->u2.statements,lev+1);
01362       break;
01363          
01364    case PV_STATEMENTBLOCK:
01365       /* fields: item->u1.list     == pval list of statements in block, one per entry in the list
01366       */
01367       /* printf("Descending into statement block\n"); */
01368       find_pval_gotos(item->u1.list,lev+1);
01369       break;
01370          
01371    case PV_GOTO:
01372       /* fields: item->u1.list     == pval list of PV_WORD target names, up to 3, in order as given by user.
01373                  item->u1.list->u1.str  == where the data on a PV_WORD will always be.
01374       */
01375       check_goto(item);  /* THE WHOLE FUNCTION OF THIS ENTIRE ROUTINE!!!! */
01376       break;
01377          
01378    case PV_INCLUDES:
01379       /* fields: item->u1.list     == pval list of PV_WORD elements, one per entry in the list
01380       */
01381       for (p4=item->u1.list; p4; p4=p4->next) {
01382          /* for each context pointed to, find it, then find a context/label that matches the
01383             target here! */
01384          char *incl_context = p4->u1.str;
01385          /* find a matching context name */
01386          struct pval *that_context = find_context(incl_context);
01387          if (that_context && that_context->u2.statements) {
01388             /* printf("Descending into include of '%s' at line %d; that_context=%s, that_context type=%d\n", incl_context, item->startline, that_context->u1.str, that_context->type); */
01389             find_pval_gotos(that_context->u2.statements,lev+1); /* keep working up the includes */
01390          }
01391       }
01392       break;
01393       
01394    case PV_FOR:
01395       /* fields: item->u1.for_init     == a string containing the initalizer
01396                  item->u2.for_test     == a string containing the loop test
01397                  item->u3.for_inc      == a string containing the loop increment
01398 
01399                item->u4.for_statements == a pval list of statements in the for ()
01400       */
01401       /* printf("Descending into for at line %d\n", item->startline); */
01402       find_pval_gotos(item->u4.for_statements,lev+1);
01403       break;
01404          
01405    case PV_WHILE:
01406       /* fields: item->u1.str        == the while conditional, as supplied by user
01407 
01408                item->u2.statements == a pval list of statements in the while ()
01409       */
01410       /* printf("Descending into while at line %d\n", item->startline); */
01411       find_pval_gotos(item->u2.statements,lev+1);
01412       break;
01413          
01414    case PV_RANDOM:
01415       /* fields: item->u1.str        == the random number expression, as supplied by user
01416 
01417                item->u2.statements == a pval list of statements in the if ()
01418                item->u3.else_statements == a pval list of statements in the else
01419                                     (could be zero)
01420        fall thru to PV_IF */
01421       
01422    case PV_IFTIME:
01423       /* fields: item->u1.list        == the time values, 4 of them, as PV_WORD structs in a list
01424 
01425                item->u2.statements == a pval list of statements in the if ()
01426                item->u3.else_statements == a pval list of statements in the else
01427                                     (could be zero)
01428       fall thru to PV_IF*/
01429    case PV_IF:
01430       /* fields: item->u1.str        == the if conditional, as supplied by user
01431 
01432                item->u2.statements == a pval list of statements in the if ()
01433                item->u3.else_statements == a pval list of statements in the else
01434                                     (could be zero)
01435       */
01436       /* printf("Descending into random/iftime/if at line %d\n", item->startline); */
01437       find_pval_gotos(item->u2.statements,lev+1);
01438 
01439       if (item->u3.else_statements) {
01440          /* printf("Descending into random/iftime/if's ELSE at line %d\n", item->startline); */
01441          find_pval_gotos(item->u3.else_statements,lev+1);
01442       }
01443       break;
01444          
01445    case PV_SWITCH:
01446       /* fields: item->u1.str        == the switch expression
01447 
01448                item->u2.statements == a pval list of statements in the switch, 
01449                                     (will be case statements, most likely!)
01450       */
01451       /* printf("Descending into switch at line %d\n", item->startline); */
01452       find_pval_gotos(item->u3.else_statements,lev+1);
01453       break;
01454          
01455    case PV_EXTENSION:
01456       /* fields: item->u1.str        == the extension name, label, whatever it's called
01457 
01458                item->u2.statements == a pval list of statements in the extension
01459                item->u3.hints      == a char * hint argument
01460                item->u4.regexten   == an int boolean. non-zero says that regexten was specified
01461       */
01462 
01463       /* printf("Descending into extension %s at line %d\n", item->u1.str, item->startline); */
01464       find_pval_gotos(item->u2.statements,lev+1);
01465       break;
01466 
01467    default:
01468       break;
01469    }
01470 }
01471 
01472 static void find_pval_gotos(pval *item,int lev)
01473 {
01474    pval *i;
01475    
01476    for (i=item; i; i=i->next) {
01477       /* printf("About to call pval_goto_item, itemcount=%d, itemtype=%d\n", item_count, i->type); */
01478       find_pval_goto_item(i, lev);
01479    }
01480 }
01481 
01482 
01483 
01484 /* general purpose label finder */
01485 static struct pval *match_pval_item(pval *item)
01486 {
01487    pval *x;
01488    
01489    switch ( item->type ) {
01490    case PV_MACRO:
01491       /* fields: item->u1.str     == name of macro
01492                  item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
01493                item->u2.arglist->u1.str  == argument
01494                item->u2.arglist->next   == next arg
01495 
01496                item->u3.macro_statements == pval list of statements in macro body.
01497       */
01498       /* printf("    matching in MACRO %s, match_context=%s; retoncontmtch=%d; \n", item->u1.str, match_context, return_on_context_match); */
01499       if (!strcmp(match_context,"*") || !strcmp(item->u1.str, match_context)) {
01500          
01501          /* printf("MACRO: match context is: %s\n", match_context); */
01502          
01503          if (return_on_context_match && !strcmp(item->u1.str, match_context)) /* if we're just searching for a context, don't bother descending into them */ {
01504             /* printf("Returning on matching macro %s\n", match_context); */
01505             return item;
01506          }
01507          
01508          
01509          if (!return_on_context_match) {
01510             /* printf("Descending into matching macro %s/%s\n", match_context, item->u1.str); */
01511             if ((x=match_pval(item->u3.macro_statements)))  {
01512                /* printf("Responded with pval match %x\n", x); */
01513                return x;
01514             }
01515          }
01516       } else {
01517          /* printf("Skipping context/macro %s\n", item->u1.str); */
01518       }
01519       
01520       break;
01521          
01522    case PV_CONTEXT:
01523       /* fields: item->u1.str     == name of context
01524                  item->u2.statements == pval list of statements in context body
01525                item->u3.abstract == int 1 if an abstract keyword were present
01526       */
01527       /* printf("    matching in CONTEXT\n"); */
01528       if (!strcmp(match_context,"*") || !strcmp(item->u1.str, match_context)) {
01529          if (return_on_context_match && !strcmp(item->u1.str, match_context)) {
01530             /* printf("Returning on matching context %s\n", match_context); */
01531             /* printf("non-CONTEXT: Responded with pval match %x\n", x); */
01532             return item;
01533          }
01534          
01535          if (!return_on_context_match ) {
01536             /* printf("Descending into matching context %s\n", match_context); */
01537             if ((x=match_pval(item->u2.statements))) /* if we're just searching for a context, don't bother descending into them */ {
01538                /* printf("CONTEXT: Responded with pval match %x\n", x); */
01539                return x;
01540             }
01541          }
01542       } else {
01543          /* printf("Skipping context/macro %s\n", item->u1.str); */
01544       }
01545       break;
01546 
01547    case PV_CASE:
01548       /* fields: item->u1.str     == value of case
01549                  item->u2.statements == pval list of statements under the case
01550       */
01551       /* printf("    matching in CASE\n"); */
01552       if ((x=match_pval(item->u2.statements))) {
01553          /* printf("CASE: Responded with pval match %x\n", x); */
01554          return x;
01555       }
01556       break;
01557          
01558    case PV_PATTERN:
01559       /* fields: item->u1.str     == value of case
01560                  item->u2.statements == pval list of statements under the case
01561       */
01562       /* printf("    matching in PATTERN\n"); */
01563       if ((x=match_pval(item->u2.statements))) {
01564          /* printf("PATTERN: Responded with pval match %x\n", x); */
01565          return x;
01566       }
01567       break;
01568          
01569    case PV_DEFAULT:
01570       /* fields: 
01571                  item->u2.statements == pval list of statements under the case
01572       */
01573       /* printf("    matching in DEFAULT\n"); */
01574       if ((x=match_pval(item->u2.statements))) {
01575          /* printf("DEFAULT: Responded with pval match %x\n", x); */
01576          return x;
01577       }
01578       break;
01579          
01580    case PV_CATCH:
01581       /* fields: item->u1.str     == name of extension to catch
01582                  item->u2.statements == pval list of statements in context body
01583       */
01584       /* printf("    matching in CATCH\n"); */
01585       if (!strcmp(match_exten,"*") || extension_matches(item, match_exten, item->u1.str) ) {
01586          /* printf("Descending into matching catch %s => %s\n", match_exten, item->u1.str); */
01587          if (strcmp(match_label,"1") == 0) {
01588             if (item->u2.statements) {
01589                struct pval *p5 = item->u2.statements;
01590                while (p5 && p5->type == PV_LABEL)  /* find the first non-label statement in this context. If it exists, there's a "1" */
01591                   p5 = p5->next;
01592                if (p5)
01593                   return p5;
01594                else
01595                   return 0;
01596             }
01597             else
01598                return 0;
01599          }
01600 
01601          if ((x=match_pval(item->u2.statements))) {
01602             /* printf("CATCH: Responded with pval match %x\n", (unsigned int)x); */
01603             return x;
01604          }
01605       } else {
01606          /* printf("Skipping catch %s\n", item->u1.str); */
01607       }
01608       break;
01609          
01610    case PV_STATEMENTBLOCK:
01611       /* fields: item->u1.list     == pval list of statements in block, one per entry in the list
01612       */
01613       /* printf("    matching in STATEMENTBLOCK\n"); */
01614       if ((x=match_pval(item->u1.list))) {
01615          /* printf("STATEMENTBLOCK: Responded with pval match %x\n", x); */
01616          return x;
01617       }
01618       break;
01619          
01620    case PV_LABEL:
01621       /* fields: item->u1.str     == label name
01622       */
01623       /* printf("PV_LABEL %s (cont=%s, exten=%s\n", 
01624          item->u1.str, current_context->u1.str, (current_extension?current_extension->u1.str:"<macro>"));*/
01625       
01626       if (count_labels) {
01627          if (!strcmp(match_label, item->u1.str)) {
01628             label_count++;
01629             last_matched_label = item;
01630          }
01631          
01632       } else {
01633          if (!strcmp(match_label, item->u1.str)) {
01634             /* printf("LABEL: Responded with pval match %x\n", x); */
01635             return item;
01636          }
01637       }
01638       break;
01639          
01640    case PV_FOR:
01641       /* fields: item->u1.for_init     == a string containing the initalizer
01642                  item->u2.for_test     == a string containing the loop test
01643                  item->u3.for_inc      == a string containing the loop increment
01644 
01645                item->u4.for_statements == a pval list of statements in the for ()
01646       */
01647       /* printf("    matching in FOR\n"); */
01648       if ((x=match_pval(item->u4.for_statements))) {
01649          /* printf("FOR: Responded with pval match %x\n", x);*/
01650          return x;
01651       }
01652       break;
01653          
01654    case PV_WHILE:
01655       /* fields: item->u1.str        == the while conditional, as supplied by user
01656 
01657                item->u2.statements == a pval list of statements in the while ()
01658       */
01659       /* printf("    matching in WHILE\n"); */
01660       if ((x=match_pval(item->u2.statements))) {
01661          /* printf("WHILE: Responded with pval match %x\n", x); */
01662          return x;
01663       }
01664       break;
01665          
01666    case PV_RANDOM:
01667       /* fields: item->u1.str        == the random number expression, as supplied by user
01668 
01669                item->u2.statements == a pval list of statements in the if ()
01670                item->u3.else_statements == a pval list of statements in the else
01671                                     (could be zero)
01672        fall thru to PV_IF */
01673       
01674    case PV_IFTIME:
01675       /* fields: item->u1.list        == the time values, 4 of them, as PV_WORD structs in a list
01676 
01677                item->u2.statements == a pval list of statements in the if ()
01678                item->u3.else_statements == a pval list of statements in the else
01679                                     (could be zero)
01680       fall thru to PV_IF*/
01681    case PV_IF:
01682       /* fields: item->u1.str        == the if conditional, as supplied by user
01683 
01684                item->u2.statements == a pval list of statements in the if ()
01685                item->u3.else_statements == a pval list of statements in the else
01686                                     (could be zero)
01687       */
01688       /* printf("    matching in IF/IFTIME/RANDOM\n"); */
01689       if ((x=match_pval(item->u2.statements))) {
01690          return x;
01691       }
01692       if (item->u3.else_statements) {
01693          if ((x=match_pval(item->u3.else_statements))) {
01694             /* printf("IF/IFTIME/RANDOM: Responded with pval match %x\n", x); */
01695             return x;
01696          }
01697       }
01698       break;
01699          
01700    case PV_SWITCH:
01701       /* fields: item->u1.str        == the switch expression
01702 
01703                item->u2.statements == a pval list of statements in the switch, 
01704                                     (will be case statements, most likely!)
01705       */
01706       /* printf("    matching in SWITCH\n"); */
01707       if ((x=match_pval(item->u2.statements))) {
01708          /* printf("SWITCH: Responded with pval match %x\n", x); */
01709          return x;
01710       }
01711       break;
01712          
01713    case PV_EXTENSION:
01714       /* fields: item->u1.str        == the extension name, label, whatever it's called
01715 
01716                item->u2.statements == a pval list of statements in the extension
01717                item->u3.hints      == a char * hint argument
01718                item->u4.regexten   == an int boolean. non-zero says that regexten was specified
01719       */
01720       /* printf("    matching in EXTENSION\n"); */
01721       if (!strcmp(match_exten,"*") || extension_matches(item, match_exten, item->u1.str) ) {
01722          /* printf("Descending into matching exten %s => %s\n", match_exten, item->u1.str); */
01723          if (strcmp(match_label,"1") == 0) {
01724             if (item->u2.statements) {
01725                struct pval *p5 = item->u2.statements;
01726                while (p5 && p5->type == PV_LABEL)  /* find the first non-label statement in this context. If it exists, there's a "1" */
01727                   p5 = p5->next;
01728                if (p5)
01729                   return p5;
01730                else
01731                   return 0;
01732             }
01733             else
01734                return 0;
01735          }
01736 
01737          if ((x=match_pval(item->u2.statements))) {
01738             /* printf("EXTENSION: Responded with pval match %x\n", x); */
01739             return x;
01740          }
01741       } else {
01742          /* printf("Skipping exten %s\n", item->u1.str); */
01743       }
01744       break;
01745    default:
01746       /* printf("    matching in default = %d\n", item->type); */
01747       break;
01748    }
01749    return 0;
01750 }
01751 
01752 struct pval *match_pval(pval *item)
01753 {
01754    pval *i;
01755 
01756    for (i=item; i; i=i->next) {
01757       pval *x;
01758       /* printf("   -- match pval: item %d\n", i->type); */
01759       
01760       if ((x = match_pval_item(i))) {
01761          /* printf("match_pval: returning x=%x\n", (int)x); */
01762          return x; /* cut the search short */
01763       }
01764    }
01765    return 0;
01766 }
01767 
01768 #if 0
01769 int count_labels_in_current_context(char *label)
01770 {
01771    label_count = 0;
01772    count_labels = 1;
01773    return_on_context_match = 0;
01774    match_pval(current_context->u2.statements);
01775    
01776    return label_count;
01777 }
01778 #endif
01779 
01780 struct pval *find_first_label_in_current_context(char *label, pval *curr_cont)
01781 {
01782    /* printf("  --- Got args %s, %s\n", exten, label); */
01783    struct pval *ret;
01784    struct pval *p3;
01785    struct pval *startpt = ((curr_cont->type==PV_MACRO)?curr_cont->u3.macro_statements: curr_cont->u2.statements);
01786    
01787    count_labels = 0;
01788    return_on_context_match = 0;
01789    match_context = "*";
01790    match_exten = "*";
01791    match_label = label;
01792    
01793    ret =  match_pval(curr_cont);
01794    if (ret)
01795       return ret;
01796                
01797    /* the target of the goto could be in an included context!! Fancy that!! */
01798    /* look for includes in the current context */
01799    for (p3=startpt; p3; p3=p3->next) {
01800       if (p3->type == PV_INCLUDES) {
01801          struct pval *p4;
01802          for (p4=p3->u1.list; p4; p4=p4->next) {
01803             /* for each context pointed to, find it, then find a context/label that matches the
01804                target here! */
01805             char *incl_context = p4->u1.str;
01806             /* find a matching context name */
01807             struct pval *that_context = find_context(incl_context);
01808             if (that_context) {
01809                struct pval *x3;
01810                x3 = find_first_label_in_current_context(label, that_context);
01811                if (x3) {
01812                   return x3;
01813                }
01814             }
01815          }
01816       }
01817    }
01818    return 0;
01819 }
01820 
01821 struct pval *find_label_in_current_context(char *exten, char *label, pval *curr_cont)
01822 {
01823    /* printf("  --- Got args %s, %s\n", exten, label); */
01824    struct pval *ret;
01825    struct pval *p3;
01826    struct pval *startpt;
01827    
01828    count_labels = 0;
01829    return_on_context_match = 0;
01830    match_context = "*";
01831    match_exten = exten;
01832    match_label = label;
01833    if (curr_cont->type == PV_MACRO)
01834       startpt = curr_cont->u3.macro_statements;
01835    else
01836       startpt = curr_cont->u2.statements;
01837 
01838    ret =  match_pval(startpt);
01839    if (ret)
01840       return ret;
01841                
01842    /* the target of the goto could be in an included context!! Fancy that!! */
01843    /* look for includes in the current context */
01844    for (p3=startpt; p3; p3=p3->next) {
01845       if (p3->type == PV_INCLUDES) {
01846          struct pval *p4;
01847          for (p4=p3->u1.list; p4; p4=p4->next) {
01848             /* for each context pointed to, find it, then find a context/label that matches the
01849                target here! */
01850             char *incl_context = p4->u1.str;
01851             /* find a matching context name */
01852             struct pval *that_context = find_context(incl_context);
01853             if (that_context) {
01854                struct pval *x3;
01855                x3 = find_label_in_current_context(exten, label, that_context);
01856                if (x3) {
01857                   return x3;
01858                }
01859             }
01860          }
01861       }
01862    }
01863    return 0;
01864 }
01865 
01866 static struct pval *find_label_in_current_extension(const char *label, pval *curr_ext)
01867 {
01868    /* printf("  --- Got args %s\n", label); */
01869    count_labels = 0;
01870    return_on_context_match = 0;
01871    match_context = "*";
01872    match_exten = "*";
01873    match_label = label;
01874    return match_pval(curr_ext);
01875 }
01876 
01877 static struct pval *find_label_in_current_db(const char *context, const char *exten, const char *label)
01878 {
01879    /* printf("  --- Got args %s, %s, %s\n", context, exten, label); */
01880    count_labels = 0;
01881    return_on_context_match = 0;
01882 
01883    match_context = context;
01884    match_exten = exten;
01885    match_label = label;
01886    
01887    return match_pval(current_db);
01888 }
01889 
01890 
01891 struct pval *find_macro(char *name)
01892 {
01893    return_on_context_match = 1;
01894    count_labels = 0;
01895    match_context = name;
01896    match_exten = "*";  /* don't really need to set these, shouldn't be reached */
01897    match_label = "*";
01898    return match_pval(current_db);
01899 }
01900 
01901 struct pval *find_context(char *name)
01902 {
01903    return_on_context_match = 1;
01904    count_labels = 0;
01905    match_context = name;
01906    match_exten = "*";  /* don't really need to set these, shouldn't be reached */
01907    match_label = "*";
01908    return match_pval(current_db);
01909 }
01910 
01911 int is_float(char *arg )
01912 {
01913    char *s;
01914    for (s=arg; *s; s++) {
01915       if (*s != '.' && (*s < '0' || *s > '9'))
01916          return 0;
01917    }
01918    return 1;
01919 }
01920 int is_int(char *arg )
01921 {
01922    char *s;
01923    for (s=arg; *s; s++) {
01924       if (*s < '0' || *s > '9')
01925          return 0;
01926    }
01927    return 1;
01928 }
01929 int is_empty(char *arg)
01930 {
01931    if (!arg)
01932       return 1;
01933    if (*arg == 0)
01934       return 1;
01935    while (*arg) {
01936       if (*arg != ' ' && *arg != '\t')
01937          return 0;
01938       arg++;
01939    }
01940    return 1;
01941 }
01942 
01943 #ifdef AAL_ARGCHECK
01944 int option_matches_j( struct argdesc *should, pval *is, struct argapp *app)
01945 {
01946    struct argchoice *ac;
01947    char *opcop,*q,*p;
01948    
01949    switch (should->dtype) {
01950    case ARGD_OPTIONSET:
01951       if ( strstr(is->u1.str,"${") )
01952          return 0;  /* no checking anything if there's a var reference in there! */
01953          
01954       opcop = ast_strdupa(is->u1.str);
01955 
01956       for (q=opcop;*q;q++) { /* erase the innards of X(innard) type arguments, so we don't get confused later */
01957          if ( *q == '(' ) {
01958             p = q+1;
01959             while (*p && *p != ')' )
01960                *p++ = '+';
01961             q = p+1;
01962          }
01963       }
01964       
01965       for (ac=app->opts; ac; ac=ac->next) {
01966          if (strlen(ac->name)>1  && strchr(ac->name,'(') == 0 && strcmp(ac->name,is->u1.str) == 0) /* multichar option, no parens, and a match? */
01967             return 0;
01968       }
01969       for (ac=app->opts; ac; ac=ac->next) {
01970          if (strlen(ac->name)==1  ||  strchr(ac->name,'(')) {
01971             char *p = strchr(opcop,ac->name[0]);  /* wipe out all matched options in the user-supplied string */
01972             
01973             if (p && *p == 'j') {
01974                ast_log(LOG_ERROR, "Error: file %s, line %d-%d: The j option in the %s application call is not appropriate for AEL!\n",
01975                      is->filename, is->startline, is->endline, app->name);
01976                errs++;
01977             }
01978             
01979             if (p) {
01980                *p = '+';
01981                if (ac->name[1] == '(') {
01982                   if (*(p+1) != '(') {
01983                      ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The %c option in the %s application call should have an (argument), but doesn't!\n",
01984                            is->filename, is->startline, is->endline, ac->name[0], app->name);
01985                      warns++;
01986                   }
01987                }
01988             }
01989          }
01990       }
01991       for (q=opcop; *q; q++) {
01992          if ( *q != '+' && *q != '(' && *q != ')') {
01993             ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The %c option in the %s application call is not available as an option!\n",
01994                   is->filename, is->startline, is->endline, *q, app->name);
01995             warns++;
01996          }
01997       }
01998       return 1;
01999       break;
02000    default:
02001       return 0;
02002    }
02003    
02004 }
02005 
02006 int option_matches( struct argdesc *should, pval *is, struct argapp *app)
02007 {
02008    struct argchoice *ac;
02009    char *opcop;
02010    
02011    switch (should->dtype) {
02012    case ARGD_STRING:
02013       if (is_empty(is->u1.str) && should->type == ARGD_REQUIRED)
02014          return 0;
02015       if (is->u1.str && strlen(is->u1.str) > 0) /* most will match */
02016          return 1;
02017       break;
02018       
02019    case ARGD_INT:
02020       if (is_int(is->u1.str))
02021          return 1;
02022       else
02023          return 0;
02024       break;
02025       
02026    case ARGD_FLOAT:
02027       if (is_float(is->u1.str))
02028          return 1;
02029       else
02030          return 0;
02031       break;
02032       
02033    case ARGD_ENUM:
02034       if( !is->u1.str || strlen(is->u1.str) == 0 )
02035          return 1; /* a null arg in the call will match an enum, I guess! */
02036       for (ac=should->choices; ac; ac=ac->next) {
02037          if (strcmp(ac->name,is->u1.str) == 0)
02038             return 1;
02039       }
02040       return 0;
02041       break;
02042       
02043    case ARGD_OPTIONSET:
02044       opcop = ast_strdupa(is->u1.str);
02045       
02046       for (ac=app->opts; ac; ac=ac->next) {
02047          if (strlen(ac->name)>1  && strchr(ac->name,'(') == 0 && strcmp(ac->name,is->u1.str) == 0) /* multichar option, no parens, and a match? */
02048             return 1;
02049       }
02050       for (ac=app->opts; ac; ac=ac->next) {
02051          if (strlen(ac->name)==1  ||  strchr(ac->name,'(')) {
02052             char *p = strchr(opcop,ac->name[0]);  /* wipe out all matched options in the user-supplied string */
02053             
02054             if (p) {
02055                *p = '+';
02056                if (ac->name[1] == '(') {
02057                   if (*(p+1) == '(') {
02058                      char *q = p+1;
02059                      while (*q && *q != ')') {
02060                         *q++ = '+';
02061                      }
02062                      *q = '+';
02063                   }
02064                }
02065             }
02066          }
02067       }
02068       return 1;
02069       break;
02070    case ARGD_VARARG:
02071       return 1; /* matches anything */
02072       break;
02073    }
02074    return 1; /* unless some for-sure match or non-match returns, then it must be close enough ... */
02075 }
02076 #endif
02077 
02078 int check_app_args(pval* appcall, pval *arglist, struct argapp *app)
02079 {
02080 #ifdef AAL_ARGCHECK
02081    struct argdesc *ad = app->args;
02082    pval *pa;
02083    int z;
02084    
02085    for (pa = arglist; pa; pa=pa->next) {
02086       if (!ad) {
02087          ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: Extra argument %s not in application call to %s !\n",
02088                arglist->filename, arglist->startline, arglist->endline, pa->u1.str, app->name);
02089          warns++;
02090          return 1;
02091       } else {
02092          /* find the first entry in the ad list that will match */
02093          do {
02094             if ( ad->dtype == ARGD_VARARG ) /* once we hit the VARARG, all bets are off. Discontinue the comparisons */
02095                break;
02096             
02097             z= option_matches( ad, pa, app);
02098             if (!z) {
02099                if ( !arglist )
02100                   arglist=appcall;
02101                
02102                if (ad->type == ARGD_REQUIRED) {
02103                   ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: Required argument %s not in application call to %s !\n",
02104                         arglist->filename, arglist->startline, arglist->endline, ad->dtype==ARGD_OPTIONSET?"options":ad->name, app->name);
02105                   warns++;
02106                   return 1;
02107                }
02108             } else if (z && ad->dtype == ARGD_OPTIONSET) {
02109                option_matches_j( ad, pa, app);
02110             }
02111             ad = ad->next;
02112          } while (ad && !z);
02113       }
02114    }
02115    /* any app nodes left, that are not optional? */
02116    for ( ; ad; ad=ad->next) {
02117       if (ad->type == ARGD_REQUIRED && ad->dtype != ARGD_VARARG) {
02118          if ( !arglist ) 
02119             arglist=appcall;
02120          ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: Required argument %s not in application call to %s !\n",
02121                arglist->filename, arglist->startline, arglist->endline, ad->dtype==ARGD_OPTIONSET?"options":ad->name, app->name);
02122          warns++;
02123          return 1;
02124       }
02125    }
02126    return 0;
02127 #else
02128    return 0;
02129 #endif
02130 }
02131 
02132 void check_switch_expr(pval *item, struct argapp *apps)
02133 {
02134 #ifdef AAL_ARGCHECK
02135    /* get and clean the variable name */
02136    char *buff1, *p;
02137    struct argapp *a,*a2;
02138    struct appsetvar *v,*v2;
02139    struct argchoice *c;
02140    pval *t;
02141    
02142    p = item->u1.str;
02143    while (p && *p && (*p == ' ' || *p == '\t' || *p == '$' || *p == '{' ) )
02144       p++;
02145    
02146    buff1 = ast_strdupa(p);
02147 
02148    while (strlen(buff1) > 0 && ( buff1[strlen(buff1)-1] == '}' || buff1[strlen(buff1)-1] == ' ' || buff1[strlen(buff1)-1] == '\t'))
02149       buff1[strlen(buff1)-1] = 0;
02150    /* buff1 now contains the variable name */
02151    v = 0;
02152    for (a=apps; a; a=a->next) {
02153       for (v=a->setvars;v;v=v->next) {
02154          if (strcmp(v->name,buff1) == 0) {
02155             break;
02156          }
02157       }
02158       if ( v )
02159          break;
02160    }
02161    if (v && v->vals) {
02162       /* we have a match, to a variable that has a set of determined values */
02163       int def= 0;
02164       int pat = 0;
02165       int f1 = 0;
02166       
02167       /* first of all, does this switch have a default case ? */
02168       for (t=item->u2.statements; t; t=t->next) {
02169          if (t->type == PV_DEFAULT) {
02170             def =1;
02171             break;
02172          }
02173          if (t->type == PV_PATTERN) {
02174             pat++;
02175          }
02176       }
02177       if (def || pat) /* nothing to check. All cases accounted for! */
02178          return;
02179       for (c=v->vals; c; c=c->next) {
02180          f1 = 0;
02181          for (t=item->u2.statements; t; t=t->next) {
02182             if (t->type == PV_CASE || t->type == PV_PATTERN) {
02183                if (!strcmp(t->u1.str,c->name)) {
02184                   f1 = 1;
02185                   break;
02186                }
02187             }
02188          }
02189          if (!f1) {
02190             ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: switch with expression(%s) does not handle the case of %s !\n",
02191                   item->filename, item->startline, item->endline, item->u1.str, c->name);
02192             warns++;
02193          }
02194       }
02195       /* next, is there an app call in the current exten, that would set this var? */
02196       f1 = 0;
02197       t = current_extension->u2.statements;
02198       if ( t && t->type == PV_STATEMENTBLOCK )
02199          t = t->u1.statements;
02200       for (; t && t != item; t=t->next) {
02201          if (t->type == PV_APPLICATION_CALL) {
02202             /* find the application that matches the u1.str */
02203             for (a2=apps; a2; a2=a2->next) {
02204                if (strcasecmp(a2->name, t->u1.str)==0) {
02205                   for (v2=a2->setvars; v2; v2=v2->next) {
02206                      if (strcmp(v2->name, buff1) == 0) {
02207                         /* found an app that sets the var */
02208                         f1 = 1;
02209                         break;
02210                      }
02211                   }
02212                }
02213                if (f1)
02214                   break;
02215             }
02216          }
02217          if (f1)
02218             break;
02219       }
02220             
02221       /* see if it sets the var */
02222       if (!f1) {
02223          ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: Couldn't find an application call in this extension that sets the  expression (%s) value!\n",
02224                item->filename, item->startline, item->endline, item->u1.str);
02225          warns++;
02226       }
02227    }
02228 #else
02229    pval *t,*tl=0,*p2;
02230    int def= 0;
02231    
02232    /* first of all, does this switch have a default case ? */
02233    for (t=item->u2.statements; t; t=t->next) {
02234       if (t->type == PV_DEFAULT) {
02235          def =1;
02236          break;
02237       }
02238       tl = t;
02239    }
02240    if (def) /* nothing to check. All cases accounted for! */
02241       return;
02242    /* if no default, warn and insert a default case at the end */
02243    p2 = tl->next = calloc(1, sizeof(struct pval));
02244    
02245    p2->type = PV_DEFAULT;
02246    p2->startline = tl->startline;
02247    p2->endline = tl->endline;
02248    p2->startcol = tl->startcol;
02249    p2->endcol = tl->endcol;
02250    p2->filename = strdup(tl->filename);
02251    ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: A default case was automatically added to the switch.\n",
02252          p2->filename, p2->startline, p2->endline);
02253    warns++;
02254 
02255 #endif
02256 }
02257 
02258 static void check_context_names(void)
02259 {
02260    pval *i,*j;
02261    for (i=current_db; i; i=i->next) {
02262       if (i->type == PV_CONTEXT || i->type == PV_MACRO) {
02263          for (j=i->next; j; j=j->next) {
02264             if ( j->type == PV_CONTEXT || j->type == PV_MACRO ) {
02265                if ( !strcmp(i->u1.str, j->u1.str) && !(i->u3.abstract&2) && !(j->u3.abstract&2) )
02266                {
02267                   ast_log(LOG_ERROR,"Error: file %s, line %d-%d: The context name (%s) is also declared in file %s, line %d-%d! (and neither is marked 'extend')\n",
02268                         i->filename, i->startline, i->endline, i->u1.str,  j->filename, j->startline, j->endline);
02269                   errs++;
02270                }
02271             }
02272          }
02273       }
02274    }
02275 }
02276 
02277 static void check_abstract_reference(pval *abstract_context)
02278 {
02279    pval *i,*j;
02280    /* find some context includes that reference this context */
02281    
02282 
02283    /* otherwise, print out a warning */
02284    for (i=current_db; i; i=i->next) {
02285       if (i->type == PV_CONTEXT) {
02286          for (j=i->u2. statements; j; j=j->next) {
02287             if ( j->type == PV_INCLUDES ) {
02288                struct pval *p4;
02289                for (p4=j->u1.list; p4; p4=p4->next) {
02290                   /* for each context pointed to, find it, then find a context/label that matches the
02291                      target here! */
02292                   if ( !strcmp(p4->u1.str, abstract_context->u1.str) )
02293                      return; /* found a match! */
02294                }
02295             }
02296          }
02297       }
02298    }
02299    ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: Couldn't find a reference to this abstract context (%s) in any other context!\n",
02300          abstract_context->filename, abstract_context->startline, abstract_context->endline, abstract_context->u1.str);
02301    warns++;
02302 }
02303 
02304 
02305 void check_pval_item(pval *item, struct argapp *apps, int in_globals)
02306 {
02307    pval *lp;
02308 #ifdef AAL_ARGCHECK
02309    struct argapp *app, *found;
02310 #endif
02311    struct pval *macro_def;
02312    struct pval *app_def;
02313    
02314    char errmsg[4096];
02315    char *strp;
02316    
02317    switch (item->type) {
02318    case PV_WORD:
02319       /* fields: item->u1.str == string associated with this (word).
02320                  item->u2.arglist  == pval list of 4 PV_WORD elements for time values (only in PV_INCLUDES) */
02321       break;
02322       
02323    case PV_MACRO:
02324       /* fields: item->u1.str     == name of macro
02325                  item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
02326                item->u2.arglist->u1.str  == argument
02327                item->u2.arglist->next   == next arg
02328 
02329                item->u3.macro_statements == pval list of statements in macro body.
02330       */
02331       in_abstract_context = 0;
02332       current_context = item;
02333       current_extension = 0;
02334       for (lp=item->u2.arglist; lp; lp=lp->next) {
02335       
02336       }
02337       check_pval(item->u3.macro_statements, apps,in_globals);
02338       break;
02339          
02340    case PV_CONTEXT:
02341       /* fields: item->u1.str     == name of context
02342                  item->u2.statements == pval list of statements in context body
02343                item->u3.abstract == int 1 if an abstract keyword were present
02344       */
02345       current_context = item;
02346       current_extension = 0;
02347       if ( item->u3.abstract ) {
02348          in_abstract_context = 1;
02349          check_abstract_reference(item);
02350       } else
02351          in_abstract_context = 0;
02352       check_pval(item->u2.statements, apps,in_globals);
02353       break;
02354          
02355    case PV_MACRO_CALL:
02356       /* fields: item->u1.str     == name of macro to call
02357                  item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
02358                item->u2.arglist->u1.str  == argument
02359                item->u2.arglist->next   == next arg
02360       */
02361       macro_def = find_macro(item->u1.str);
02362       if (!macro_def) {
02363          /* here is a good place to check to see if the definition is in extensions.conf! */
02364          ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: macro call to non-existent %s ! Hopefully it is present in extensions.conf! \n",
02365                item->filename, item->startline, item->endline, item->u1.str);
02366          warns++;
02367       } else if (macro_def->type != PV_MACRO) {
02368          ast_log(LOG_ERROR,"Error: file %s, line %d-%d: macro call to %s references a context, not a macro!\n",
02369                item->filename, item->startline, item->endline, item->u1.str);
02370          errs++;
02371       } else {
02372          /* macro_def is a MACRO, so do the args match in number? */
02373          int hereargs = 0;
02374          int thereargs = 0;
02375          
02376          for (lp=item->u2.arglist; lp; lp=lp->next) {
02377             hereargs++;
02378          }
02379          for (lp=macro_def->u2.arglist; lp; lp=lp->next) {
02380             thereargs++;
02381          }
02382          if (hereargs != thereargs ) {
02383             ast_log(LOG_ERROR, "Error: file %s, line %d-%d: The macro call to %s has %d arguments, but the macro definition has %d arguments\n",
02384                   item->filename, item->startline, item->endline, item->u1.str, hereargs, thereargs);
02385             errs++;
02386          }
02387       }
02388       break;
02389          
02390    case PV_APPLICATION_CALL:
02391       /* fields: item->u1.str     == name of application to call
02392                  item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
02393                item->u2.arglist->u1.str  == argument
02394                item->u2.arglist->next   == next arg
02395       */
02396       /* Need to check to see if the application is available! */
02397       app_def = find_context(item->u1.str);
02398       if (app_def && app_def->type == PV_MACRO) {
02399          ast_log(LOG_ERROR,"Error: file %s, line %d-%d: application call to %s references an existing macro, but had no & preceding it!\n",
02400                item->filename, item->startline, item->endline, item->u1.str);
02401          errs++;
02402       }
02403       if (strcasecmp(item->u1.str,"GotoIf") == 0
02404          || strcasecmp(item->u1.str,"GotoIfTime") == 0
02405          || strcasecmp(item->u1.str,"while") == 0
02406          || strcasecmp(item->u1.str,"endwhile") == 0
02407          || strcasecmp(item->u1.str,"random") == 0
02408          || strcasecmp(item->u1.str,"execIf") == 0 ) {
02409          ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: application call to %s needs to be re-written using AEL if, while, goto, etc. keywords instead!\n",
02410                item->filename, item->startline, item->endline, item->u1.str);
02411          warns++;
02412       }
02413 #ifdef AAL_ARGCHECK
02414       found = 0;
02415       for (app=apps; app; app=app->next) {
02416          if (strcasecmp(app->name, item->u1.str) == 0) {
02417             found =app;
02418             break;
02419          }
02420       }
02421       if (!found) {
02422          ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: application call to %s not listed in applist database!\n",
02423                item->filename, item->startline, item->endline, item->u1.str);
02424          warns++;
02425       } else
02426          check_app_args(item, item->u2.arglist, app);
02427 #endif
02428       break;
02429       
02430    case PV_CASE:
02431       /* fields: item->u1.str     == value of case
02432                  item->u2.statements == pval list of statements under the case
02433       */
02434       /* Make sure sequence of statements under case is terminated with  goto, return, or break */
02435       /* find the last statement */
02436       check_pval(item->u2.statements, apps,in_globals);
02437       break;
02438          
02439    case PV_PATTERN:
02440       /* fields: item->u1.str     == value of case
02441                  item->u2.statements == pval list of statements under the case
02442       */
02443       /* Make sure sequence of statements under case is terminated with  goto, return, or break */
02444       /* find the last statement */
02445       
02446       check_pval(item->u2.statements, apps,in_globals);
02447       break;
02448          
02449    case PV_DEFAULT:
02450       /* fields: 
02451                  item->u2.statements == pval list of statements under the case
02452       */
02453 
02454       check_pval(item->u2.statements, apps,in_globals);
02455       break;
02456          
02457    case PV_CATCH:
02458       /* fields: item->u1.str     == name of extension to catch
02459                  item->u2.statements == pval list of statements in context body
02460       */
02461       check_pval(item->u2.statements, apps,in_globals);
02462       break;
02463          
02464    case PV_SWITCHES:
02465       /* fields: item->u1.list     == pval list of PV_WORD elements, one per entry in the list
02466       */
02467       check_pval(item->u1.list, apps,in_globals);
02468       break;
02469          
02470    case PV_ESWITCHES:
02471       /* fields: item->u1.list     == pval list of PV_WORD elements, one per entry in the list
02472       */
02473       check_pval(item->u1.list, apps,in_globals);
02474       break;
02475          
02476    case PV_INCLUDES:
02477       /* fields: item->u1.list     == pval list of PV_WORD elements, one per entry in the list
02478       */
02479       check_pval(item->u1.list, apps,in_globals);
02480       check_includes(item);
02481       for (lp=item->u1.list; lp; lp=lp->next){
02482          char *incl_context = lp->u1.str;
02483          struct pval *that_context = find_context(incl_context);
02484 
02485          if ( lp->u2.arglist ) {
02486             check_timerange(lp->u2.arglist);
02487             check_dow(lp->u2.arglist->next);
02488             check_day(lp->u2.arglist->next->next);
02489             check_month(lp->u2.arglist->next->next->next);
02490          }
02491          
02492          if (that_context) {
02493             find_pval_gotos(that_context->u2.statements,0);
02494             
02495          }
02496       }
02497       break;
02498          
02499    case PV_STATEMENTBLOCK:
02500       /* fields: item->u1.list     == pval list of statements in block, one per entry in the list
02501       */
02502       check_pval(item->u1.list, apps,in_globals);
02503       break;
02504          
02505    case PV_VARDEC:
02506       /* fields: item->u1.str     == variable name
02507                  item->u2.val     == variable value to assign
02508       */
02509       /* the RHS of a vardec is encapsulated in a $[] expr. Is it legal? */
02510       if( !in_globals ) { /* don't check stuff inside the globals context; no wrapping in $[ ] there... */
02511          snprintf(errmsg,sizeof(errmsg), "file %s, line %d, columns %d-%d, variable declaration expr '%s':", config, item->startline, item->startcol, item->endcol, item->u2.val);
02512          ast_expr_register_extra_error_info(errmsg);
02513          ast_expr(item->u2.val, expr_output, sizeof(expr_output));
02514          ast_expr_clear_extra_error_info();
02515          if ( strpbrk(item->u2.val,"~!-+<>=*/&^") && !strstr(item->u2.val,"${") ) {
02516             ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n",
02517                   item->filename, item->startline, item->endline, item->u2.val);
02518             warns++;
02519          }
02520          check_expr2_input(item,item->u2.val);
02521       }
02522       break;
02523          
02524    case PV_GOTO:
02525       /* fields: item->u1.list     == pval list of PV_WORD target names, up to 3, in order as given by user.
02526                  item->u1.list->u1.str  == where the data on a PV_WORD will always be.
02527       */
02528       /* don't check goto's in abstract contexts */
02529       if ( in_abstract_context )
02530          break;
02531       
02532       check_goto(item);
02533       break;
02534          
02535    case PV_LABEL:
02536       /* fields: item->u1.str     == label name
02537       */
02538       if ( strspn(item->u1.str, "0123456789") == strlen(item->u1.str) ) {
02539          ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: label '%s' is numeric, this is bad practice!\n",
02540                item->filename, item->startline, item->endline, item->u1.str);
02541          warns++;
02542       }
02543 
02544       check_label(item);
02545       break;
02546          
02547    case PV_FOR:
02548       /* fields: item->u1.for_init     == a string containing the initalizer
02549                  item->u2.for_test     == a string containing the loop test
02550                  item->u3.for_inc      == a string containing the loop increment
02551 
02552                item->u4.for_statements == a pval list of statements in the for ()
02553       */
02554       snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, for test expr '%s':", config, item->startline, item->startcol, item->endcol, item->u2.for_test);
02555       ast_expr_register_extra_error_info(errmsg);
02556 
02557       strp = strchr(item->u1.for_init, '=');
02558       if (strp) {
02559          ast_expr(strp+1, expr_output, sizeof(expr_output));
02560       }
02561       ast_expr(item->u2.for_test, expr_output, sizeof(expr_output));
02562       strp = strchr(item->u3.for_inc, '=');
02563       if (strp) {
02564          ast_expr(strp+1, expr_output, sizeof(expr_output));
02565       }
02566       if ( strpbrk(item->u2.for_test,"~!-+<>=*/&^") && !strstr(item->u2.for_test,"${") ) {
02567          ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n",
02568                item->filename, item->startline, item->endline, item->u2.for_test);
02569          warns++;
02570       }
02571       if ( strpbrk(item->u3.for_inc,"~!-+<>=*/&^") && !strstr(item->u3.for_inc,"${") ) {
02572          ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n",
02573                item->filename, item->startline, item->endline, item->u3.for_inc);
02574          warns++;
02575       }
02576       check_expr2_input(item,item->u2.for_test);
02577       check_expr2_input(item,item->u3.for_inc);
02578       
02579       ast_expr_clear_extra_error_info();
02580       check_pval(item->u4.for_statements, apps,in_globals);
02581       break;
02582          
02583    case PV_WHILE:
02584       /* fields: item->u1.str        == the while conditional, as supplied by user
02585 
02586                item->u2.statements == a pval list of statements in the while ()
02587       */
02588       snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, while expr '%s':", config, item->startline, item->startcol, item->endcol, item->u1.str);
02589       ast_expr_register_extra_error_info(errmsg);
02590       ast_expr(item->u1.str, expr_output, sizeof(expr_output));
02591       ast_expr_clear_extra_error_info();
02592       if ( strpbrk(item->u1.str,"~!-+<>=*/&^") && !strstr(item->u1.str,"${") ) {
02593          ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n",
02594                item->filename, item->startline, item->endline, item->u1.str);
02595          warns++;
02596       }
02597       check_expr2_input(item,item->u1.str);
02598       check_pval(item->u2.statements, apps,in_globals);
02599       break;
02600          
02601    case PV_BREAK:
02602       /* fields: none
02603       */
02604       check_break(item);
02605       break;
02606          
02607    case PV_RETURN:
02608       /* fields: none
02609       */
02610       break;
02611          
02612    case PV_CONTINUE:
02613       /* fields: none
02614       */
02615       check_continue(item);
02616       break;
02617          
02618    case PV_RANDOM:
02619       /* fields: item->u1.str        == the random number expression, as supplied by user
02620 
02621                item->u2.statements == a pval list of statements in the if ()
02622                item->u3.else_statements == a pval list of statements in the else
02623                                     (could be zero)
02624       */
02625       snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, random expr '%s':", config, item->startline, item->startcol, item->endcol, item->u1.str);
02626       ast_expr_register_extra_error_info(errmsg);
02627       ast_expr(item->u1.str, expr_output, sizeof(expr_output));
02628       ast_expr_clear_extra_error_info();
02629       if ( strpbrk(item->u1.str,"~!-+<>=*/&^") && !strstr(item->u1.str,"${") ) {
02630          ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: random expression '%s' has operators, but no variables. Interesting...\n",
02631                item->filename, item->startline, item->endline, item->u1.str);
02632          warns++;
02633       }
02634       check_expr2_input(item,item->u1.str);
02635       check_pval(item->u2.statements, apps,in_globals);
02636       if (item->u3.else_statements) {
02637          check_pval(item->u3.else_statements, apps,in_globals);
02638       }
02639       break;
02640 
02641    case PV_IFTIME:
02642       /* fields: item->u1.list        == the if time values, 4 of them, each in PV_WORD, linked list 
02643 
02644                item->u2.statements == a pval list of statements in the if ()
02645                item->u3.else_statements == a pval list of statements in the else
02646                                     (could be zero)
02647       */
02648       if ( item->u2.arglist ) {
02649          check_timerange(item->u1.list);
02650          check_dow(item->u1.list->next);
02651          check_day(item->u1.list->next->next);
02652          check_month(item->u1.list->next->next->next);
02653       }
02654 
02655       check_pval(item->u2.statements, apps,in_globals);
02656       if (item->u3.else_statements) {
02657          check_pval(item->u3.else_statements, apps,in_globals);
02658       }
02659       break;
02660          
02661    case PV_IF:
02662       /* fields: item->u1.str        == the if conditional, as supplied by user
02663 
02664                item->u2.statements == a pval list of statements in the if ()
02665                item->u3.else_statements == a pval list of statements in the else
02666                                     (could be zero)
02667       */
02668       snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, if expr '%s':", config, item->startline, item->startcol, item->endcol, item->u1.str);
02669       ast_expr_register_extra_error_info(errmsg);
02670       ast_expr(item->u1.str, expr_output, sizeof(expr_output));
02671       ast_expr_clear_extra_error_info();
02672       if ( strpbrk(item->u1.str,"~!-+<>=*/&^") && !strstr(item->u1.str,"${") ) {
02673          ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression '%s' has operators, but no variables. Interesting...\n",
02674                item->filename, item->startline, item->endline, item->u1.str);
02675          warns++;
02676       }
02677       check_expr2_input(item,item->u1.str);
02678       check_pval(item->u2.statements, apps,in_globals);
02679       if (item->u3.else_statements) {
02680          check_pval(item->u3.else_statements, apps,in_globals);
02681       }
02682       break;
02683          
02684    case PV_SWITCH:
02685       /* fields: item->u1.str        == the switch expression
02686 
02687                item->u2.statements == a pval list of statements in the switch, 
02688                                     (will be case statements, most likely!)
02689       */
02690       /* we can check the switch expression, see if it matches any of the app variables...
02691            if it does, then, are all the possible cases accounted for? */
02692       check_switch_expr(item, apps);
02693       check_pval(item->u2.statements, apps,in_globals);
02694       break;
02695          
02696    case PV_EXTENSION:
02697       /* fields: item->u1.str        == the extension name, label, whatever it's called
02698 
02699                item->u2.statements == a pval list of statements in the extension
02700                item->u3.hints      == a char * hint argument
02701                item->u4.regexten   == an int boolean. non-zero says that regexten was specified
02702       */
02703       current_extension = item ;
02704       
02705       check_pval(item->u2.statements, apps,in_globals);
02706       break;
02707          
02708    case PV_IGNOREPAT:
02709       /* fields: item->u1.str        == the ignorepat data
02710       */
02711       break;
02712          
02713    case PV_GLOBALS:
02714       /* fields: item->u1.statements     == pval list of statements, usually vardecs
02715       */
02716       in_abstract_context = 0;
02717       check_pval(item->u1.statements, apps, 1);
02718       break;
02719    default:
02720       break;
02721    }
02722 }
02723 
02724 void check_pval(pval *item, struct argapp *apps, int in_globals)
02725 {
02726    pval *i;
02727 
02728    /* checks to do:
02729       1. Do goto's point to actual labels? 
02730       2. Do macro calls reference a macro?
02731       3. Does the number of macro args match the definition?
02732       4. Is a macro call missing its & at the front?
02733       5. Application calls-- we could check syntax for existing applications,
02734          but I need some some sort of universal description bnf for a general
02735         sort of method for checking arguments, in number, maybe even type, at least. 
02736         Don't want to hand code checks for hundreds of applications.
02737    */
02738    
02739    for (i=item; i; i=i->next) {
02740       check_pval_item(i,apps,in_globals);
02741    }
02742 }
02743 
02744 static void ael2_semantic_check(pval *item, int *arg_errs, int *arg_warns, int *arg_notes)
02745 {
02746    
02747 #ifdef AAL_ARGCHECK
02748    int argapp_errs =0;
02749    char *rfilename, *rdirname, *xappsfilename;
02750    DIR *dir = NULL;
02751    struct dirent *de;
02752 #endif
02753    struct argapp *apps=0, *xapps=0, *app;
02754 
02755    if (!item)
02756       return; /* don't check an empty tree */
02757 #ifdef AAL_ARGCHECK
02758    rfilename = alloca(10 + strlen(ast_config_AST_VAR_DIR));
02759    sprintf(rfilename, "%s/applist", ast_config_AST_VAR_DIR);
02760    
02761    apps = argdesc_parse(rfilename, &argapp_errs); /* giveth */
02762 
02763    if (argapp_errs == 0) {
02764       ast_log(LOG_NOTICE, "AEL load process: parsed default apps desc file '%s'.\n", rfilename);
02765    } else {
02766       ast_log(LOG_ERROR, "AEL load process: %d errors found parsing apps file '%s'.\n", argapp_errs, rfilename);  
02767    }
02768 
02769    // Open applist.d/ for extra applists
02770    rdirname = alloca(16 + strlen(ast_config_AST_VAR_DIR));
02771    sprintf(rdirname, "%s/applist.d", ast_config_AST_VAR_DIR);
02772    dir = opendir(rdirname);
02773    if (!dir) {
02774       ast_log(LOG_WARNING, "Unable to open ael2 extra apps dir. %s is not a valid directory\n", rdirname);
02775    } else {
02776       // Reset errors count
02777       argapp_errs = 0;
02778       // Load apps from each file named *.apps into AST_VAR_DIR/applist.d/
02779       xappsfilename = alloca(AST_MAX_FILENAME_LEN + strlen(rdirname));
02780       while ((de = readdir(dir))) {
02781          xappsfilename[0] = 0;
02782          if ((strlen(de->d_name) > 4) && !strcasecmp(de->d_name + strlen(de->d_name) - 5, ".apps")) {
02783             sprintf(xappsfilename, "%s/%s", rdirname, de->d_name);
02784             xapps = argdesc_parse(xappsfilename, &argapp_errs);
02785             // If xapps list is not empty prepend it to apps
02786             if(xapps != NULL) {
02787                if (argapp_errs == 0) {
02788                   ast_log(LOG_NOTICE, "AEL load process: parsed extra apps desc file '%s'.\n", xappsfilename);
02789                } else {
02790                   ast_log(LOG_ERROR, "AEL load process: %d errors found parsing extra apps file '%s'.\n", argapp_errs, xappsfilename);
02791                }
02792                app=xapps;
02793                while (app->next != NULL) {
02794                   app=app->next;
02795                }
02796                app->next = apps;
02797                apps = xapps;
02798             } else {
02799                ast_log(LOG_WARNING, "AEL load process: no apps desc found into file '%s'.\n", xappsfilename);
02800             }
02801          }
02802       }
02803    }
02804 #endif
02805    current_db = item;
02806    errs = warns = notes = 0;
02807 
02808    check_context_names();
02809    check_pval(item, apps, 0);
02810 
02811 #ifdef AAL_ARGCHECK
02812    argdesc_destroy(apps);  /* taketh away */
02813 #endif
02814    current_db = 0;
02815 
02816    *arg_errs = errs;
02817    *arg_warns = warns;
02818    *arg_notes = notes;
02819 }
02820 
02821 /* =============================================================================================== */
02822 /* "CODE" GENERATOR -- Convert the AEL representation to asterisk extension language */
02823 /* =============================================================================================== */
02824 
02825 static int control_statement_count = 0;
02826 
02827 struct ael_priority *new_prio(void)
02828 {
02829    struct ael_priority *x = (struct ael_priority *)calloc(sizeof(struct ael_priority),1);
02830    return x;
02831 }
02832 
02833 struct ael_extension *new_exten(void)
02834 {
02835    struct ael_extension *x = (struct ael_extension *)calloc(sizeof(struct ael_extension),1);
02836    return x;
02837 }
02838 
02839 void linkprio(struct ael_extension *exten, struct ael_priority *prio, struct ael_extension *mother_exten)
02840 {
02841    char *p1, *p2;
02842    
02843    if (!exten->plist) {
02844       exten->plist = prio;
02845       exten->plist_last = prio;
02846    } else {
02847       exten->plist_last->next = prio;
02848       exten->plist_last = prio;
02849    }
02850    if( !prio->exten )
02851       prio->exten = exten; /* don't override the switch value */
02852    /* The following code will cause all priorities within an extension 
02853       to have ${EXTEN} or ${EXTEN: replaced with ~~EXTEN~~, which is
02854       set just before the first switch in an exten. The switches
02855       will muck up the original ${EXTEN} value, so we save it away
02856       and the user accesses this copy instead. */
02857    if (prio->appargs && ((mother_exten && mother_exten->has_switch) || exten->has_switch) ) {
02858       while ((p1 = strstr(prio->appargs, "${EXTEN}"))) {
02859          p2 = malloc(strlen(prio->appargs)+5);
02860          *p1 = 0;
02861          strcpy(p2, prio->appargs);
02862          strcat(p2, "${~~EXTEN~~}");
02863          if (*(p1+8))
02864             strcat(p2, p1+8);
02865          free(prio->appargs);
02866          prio->appargs = p2;
02867       }
02868       while ((p1 = strstr(prio->appargs, "${EXTEN:"))) {
02869          p2 = malloc(strlen(prio->appargs)+5);
02870          *p1 = 0;
02871          strcpy(p2, prio->appargs);
02872          strcat(p2, "${~~EXTEN~~:");
02873          if (*(p1+8))
02874             strcat(p2, p1+8);
02875          free(prio->appargs);
02876          prio->appargs = p2;
02877       }
02878    }
02879 }
02880 
02881 void destroy_extensions(struct ael_extension *exten)
02882 {
02883    struct ael_extension *ne, *nen;
02884    for (ne=exten; ne; ne=nen) {
02885       struct ael_priority *pe, *pen;
02886       
02887       if (ne->name)
02888          free(ne->name);
02889       
02890       /* cidmatch fields are allocated with name, and freed when
02891          the name field is freed. Don't do a free for this field,
02892          unless you LIKE to see a crash! */
02893 
02894       if (ne->hints)
02895          free(ne->hints);
02896       
02897       for (pe=ne->plist; pe; pe=pen) {
02898          pen = pe->next;
02899          if (pe->app)
02900             free(pe->app);
02901          pe->app = 0;
02902          if (pe->appargs)
02903             free(pe->appargs);
02904          pe->appargs = 0;
02905          pe->origin = 0;
02906          pe->goto_true = 0;
02907          pe->goto_false = 0;
02908          free(pe);
02909       }
02910       nen = ne->next_exten;
02911       ne->next_exten = 0;
02912       ne->plist =0;
02913       ne->plist_last = 0;
02914       ne->next_exten = 0;
02915       ne->loop_break = 0;
02916       ne->loop_continue = 0;
02917       free(ne);
02918    }
02919 }
02920 
02921 static int label_inside_case(pval *label)
02922 {
02923    pval *p = label;
02924    
02925    while( p && p->type != PV_MACRO && p->type != PV_CONTEXT ) /* early cutout, sort of */ {
02926       if( p->type == PV_CASE || p->type == PV_DEFAULT || p->type == PV_PATTERN ) {
02927          return 1;
02928       }
02929 
02930       p = p->dad;
02931    }
02932    return 0;
02933 }
02934 
02935 static void linkexten(struct ael_extension *exten, struct ael_extension *add)
02936 {
02937    add->next_exten = exten->next_exten; /* this will reverse the order. Big deal. */
02938    exten->next_exten = add;
02939 }
02940 
02941 static void remove_spaces_before_equals(char *str)
02942 {
02943    char *p;
02944    while( str && *str && *str != '=' )
02945    {
02946       if( *str == ' ' || *str == '\n' || *str == '\r' || *str == '\t' )
02947       {
02948          p = str;
02949          while( *p )
02950          {
02951             *p = *(p+1);
02952             p++;
02953          }
02954       }
02955       else
02956          str++;
02957    }
02958 }
02959 
02960 static void gen_match_to_pattern(char *pattern, char *result)
02961 {
02962    /* the result will be a string that will be matched by pattern */
02963    char *p=pattern, *t=result;
02964    while (*p) {
02965       if (*p == 'x' || *p == 'n' || *p == 'z' || *p == 'X' || *p == 'N' || *p == 'Z')
02966          *t++ = '9';
02967       else if (*p == '[') {
02968          char *z = p+1;
02969          while (*z != ']')
02970             z++;
02971          if (*(z+1)== ']')
02972             z++;
02973          *t++=*(p+1); /* use the first char in the set */
02974          p = z;
02975       } else {
02976          *t++ = *p;
02977       }
02978       p++;
02979    }
02980    *t++ = 0; /* cap it off */
02981 }
02982 
02983 /* ==== a set of routines to search for a switch statement contained in the pval description */
02984 
02985 int find_switch_item(pval *item);
02986 int contains_switch(pval *item);
02987 
02988 
02989 int find_switch_item(pval *item)
02990 {
02991    switch ( item->type ) {
02992    case PV_WORD:
02993       /* fields: item->u1.str == string associated with this (word). */
02994       break;
02995       
02996    case PV_MACRO:
02997       /* fields: item->u1.str     == name of macro
02998                  item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
02999                item->u2.arglist->u1.str  == argument
03000                item->u2.arglist->next   == next arg
03001 
03002                item->u3.macro_statements == pval list of statements in macro body.
03003       */
03004       /* had better not see this */
03005       if (contains_switch(item->u3.macro_statements))
03006          return 1;
03007       break;
03008          
03009    case PV_CONTEXT:
03010       /* fields: item->u1.str     == name of context
03011                  item->u2.statements == pval list of statements in context body
03012                item->u3.abstract == int 1 if an abstract keyword were present
03013       */
03014       /* had better not see this */
03015       if (contains_switch(item->u2.statements))
03016          return 1;
03017       break;
03018          
03019    case PV_MACRO_CALL:
03020       /* fields: item->u1.str     == name of macro to call
03021                  item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
03022                item->u2.arglist->u1.str  == argument
03023                item->u2.arglist->next   == next arg
03024       */
03025       break;
03026          
03027    case PV_APPLICATION_CALL:
03028       /* fields: item->u1.str     == name of application to call
03029                  item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
03030                item->u2.arglist->u1.str  == argument
03031                item->u2.arglist->next   == next arg
03032       */
03033       break;
03034          
03035    case PV_CASE:
03036       /* fields: item->u1.str     == value of case
03037                  item->u2.statements == pval list of statements under the case
03038       */
03039       /* had better not see this */
03040       if (contains_switch(item->u2.statements))
03041          return 1;
03042       break;
03043          
03044    case PV_PATTERN:
03045       /* fields: item->u1.str     == value of case
03046                  item->u2.statements == pval list of statements under the case
03047       */
03048       /* had better not see this */
03049       if (contains_switch(item->u2.statements))
03050          return 1;
03051       break;
03052          
03053    case PV_DEFAULT:
03054       /* fields: 
03055                  item->u2.statements == pval list of statements under the case
03056       */
03057       /* had better not see this */
03058       if (contains_switch(item->u2.statements))
03059          return 1;
03060       break;
03061          
03062    case PV_CATCH:
03063       /* fields: item->u1.str     == name of extension to catch
03064                  item->u2.statements == pval list of statements in context body
03065       */
03066       /* had better not see this */
03067       if (contains_switch(item->u2.statements))
03068          return 1;
03069       break;
03070          
03071    case PV_SWITCHES:
03072       /* fields: item->u1.list     == pval list of PV_WORD elements, one per entry in the list
03073       */
03074       break;
03075          
03076    case PV_ESWITCHES:
03077       /* fields: item->u1.list     == pval list of PV_WORD elements, one per entry in the list
03078       */
03079       break;
03080          
03081    case PV_INCLUDES:
03082       /* fields: item->u1.list     == pval list of PV_WORD elements, one per entry in the list
03083                  item->u2.arglist  == pval list of 4 PV_WORD elements for time values
03084       */
03085       break;
03086          
03087    case PV_STATEMENTBLOCK:
03088       /* fields: item->u1.list     == pval list of statements in block, one per entry in the list
03089       */
03090       if (contains_switch(item->u1.list) )
03091          return 1;
03092       break;
03093          
03094    case PV_VARDEC:
03095       /* fields: item->u1.str     == variable name
03096                  item->u2.val     == variable value to assign
03097       */
03098       break;
03099          
03100    case PV_GOTO:
03101       /* fields: item->u1.list     == pval list of PV_WORD target names, up to 3, in order as given by user.
03102                  item->u1.list->u1.str  == where the data on a PV_WORD will always be.
03103       */
03104       break;
03105          
03106    case PV_LABEL:
03107       /* fields: item->u1.str     == label name
03108       */
03109       break;
03110          
03111    case PV_FOR:
03112       /* fields: item->u1.for_init     == a string containing the initalizer
03113                  item->u2.for_test     == a string containing the loop test
03114                  item->u3.for_inc      == a string containing the loop increment
03115 
03116                item->u4.for_statements == a pval list of statements in the for ()
03117       */
03118       if (contains_switch(item->u4.for_statements))
03119          return 1;
03120       break;
03121          
03122    case PV_WHILE:
03123       /* fields: item->u1.str        == the while conditional, as supplied by user
03124 
03125                item->u2.statements == a pval list of statements in the while ()
03126       */
03127       if (contains_switch(item->u2.statements))
03128          return 1;
03129       break;
03130          
03131    case PV_BREAK:
03132       /* fields: none
03133       */
03134       break;
03135          
03136    case PV_RETURN:
03137       /* fields: none
03138       */
03139       break;
03140          
03141    case PV_CONTINUE:
03142       /* fields: none
03143       */
03144       break;
03145          
03146    case PV_IFTIME:
03147       /* fields: item->u1.list        == there are 4 linked PV_WORDs here.
03148 
03149                item->u2.statements == a pval list of statements in the if ()
03150                item->u3.else_statements == a pval list of statements in the else
03151                                     (could be zero)
03152       */
03153       if (contains_switch(item->u2.statements))
03154          return 1;
03155       if ( item->u3.else_statements ) {
03156          if (contains_switch(item->u3.else_statements))
03157             return 1;
03158       }
03159       break;
03160          
03161    case PV_RANDOM:
03162       /* fields: item->u1.str        == the random number expression, as supplied by user
03163 
03164                item->u2.statements == a pval list of statements in the if ()
03165                item->u3.else_statements == a pval list of statements in the else
03166                                     (could be zero)
03167       */
03168       if (contains_switch(item->u2.statements))
03169          return 1;
03170       if ( item->u3.else_statements ) {
03171          if (contains_switch(item->u3.else_statements))
03172             return 1;
03173       }
03174       break;
03175          
03176    case PV_IF:
03177       /* fields: item->u1.str        == the if conditional, as supplied by user
03178 
03179                item->u2.statements == a pval list of statements in the if ()
03180                item->u3.else_statements == a pval list of statements in the else
03181                                     (could be zero)
03182       */
03183       if (contains_switch(item->u2.statements))
03184          return 1;
03185       if ( item->u3.else_statements ) {
03186          if (contains_switch(item->u3.else_statements))
03187             return 1;
03188       }
03189       break;
03190          
03191    case PV_SWITCH:
03192       /* fields: item->u1.str        == the switch expression
03193 
03194                item->u2.statements == a pval list of statements in the switch, 
03195                                     (will be case statements, most likely!)
03196       */
03197       return 1; /* JACKPOT */
03198       break;
03199          
03200    case PV_EXTENSION:
03201       /* fields: item->u1.str        == the extension name, label, whatever it's called
03202 
03203                item->u2.statements == a pval list of statements in the extension
03204                item->u3.hints      == a char * hint argument
03205                item->u4.regexten   == an int boolean. non-zero says that regexten was specified
03206       */
03207       if (contains_switch(item->u2.statements))
03208          return 1;
03209       break;
03210          
03211    case PV_IGNOREPAT:
03212       /* fields: item->u1.str        == the ignorepat data
03213       */
03214       break;
03215          
03216    case PV_GLOBALS:
03217       /* fields: item->u1.statements     == pval list of statements, usually vardecs
03218       */
03219       break;
03220    }
03221    return 0;
03222 }
03223 
03224 int contains_switch(pval *item)
03225 {
03226    pval *i;
03227    
03228    for (i=item; i; i=i->next) {
03229       if (find_switch_item(i))
03230          return 1;
03231    }
03232    return 0;
03233 }
03234 
03235 
03236 static void gen_prios(struct ael_extension *exten, char *label, pval *statement, struct ael_extension *mother_exten, struct ast_context *this_context )
03237 {
03238    pval *p,*p2,*p3;
03239    struct ael_priority *pr;
03240    struct ael_priority *for_init, *for_test, *for_inc, *for_loop, *for_end;
03241    struct ael_priority *while_test, *while_loop, *while_end;
03242    struct ael_priority *switch_set, *switch_test, *switch_end, *fall_thru, *switch_empty;
03243    struct ael_priority *if_test, *if_end, *if_skip, *if_false;
03244 #ifdef OLD_RAND_ACTION
03245    struct ael_priority *rand_test, *rand_end, *rand_skip;
03246 #endif
03247    char buf1[2000];
03248    char buf2[2000];
03249    char *strp, *strp2;
03250    char new_label[2000];
03251    int default_exists;
03252    int local_control_statement_count;
03253    struct ael_priority *loop_break_save;
03254    struct ael_priority *loop_continue_save;
03255    struct ael_extension *switch_case,*switch_null;
03256    
03257    if ((mother_exten && !mother_exten->checked_switch) || (exten && !exten->checked_switch)) {
03258       if (contains_switch(statement)) { /* only run contains_switch if you haven't checked before */
03259          if (mother_exten) {
03260             if (!mother_exten->has_switch) {
03261                switch_set = new_prio();
03262                switch_set->type = AEL_APPCALL;
03263                switch_set->app = strdup("Set");
03264                switch_set->appargs = strdup("~~EXTEN~~=${EXTEN}");
03265                linkprio(exten, switch_set, mother_exten);
03266                mother_exten->has_switch = 1;
03267                mother_exten->checked_switch = 1;
03268                if (exten) {
03269                   exten->has_switch = 1;
03270                   exten->checked_switch = 1;
03271                }
03272             }
03273          } else if (exten) {
03274             if (!exten->has_switch) {
03275                switch_set = new_prio();
03276                switch_set->type = AEL_APPCALL;
03277                switch_set->app = strdup("Set");
03278                switch_set->appargs = strdup("~~EXTEN~~=${EXTEN}");
03279                linkprio(exten, switch_set, mother_exten);
03280                exten->has_switch = 1;
03281                exten->checked_switch = 1;
03282                if (mother_exten) {
03283                   mother_exten->has_switch = 1;
03284                   mother_exten->checked_switch = 1;
03285                }
03286             }
03287          }
03288       } else {
03289          if (mother_exten) {
03290             mother_exten->checked_switch = 1;
03291          }
03292          if (exten) {
03293             exten->checked_switch = 1;
03294          }
03295       }
03296    }
03297    for (p=statement; p; p=p->next) {
03298       switch (p->type) {
03299       case PV_VARDEC:
03300          pr = new_prio();
03301          pr->type = AEL_APPCALL;
03302          snprintf(buf1,sizeof(buf1),"%s=$[%s]", p->u1.str, p->u2.val);
03303          pr->app = strdup("Set");
03304          remove_spaces_before_equals(buf1);
03305          pr->appargs = strdup(buf1);
03306          pr->origin = p;
03307          linkprio(exten, pr, mother_exten);
03308          break;
03309          
03310       case PV_GOTO:
03311          pr = new_prio();
03312          pr->type = AEL_APPCALL;
03313          p->u2.goto_target = get_goto_target(p);
03314          if( p->u2.goto_target ) {
03315             p->u3.goto_target_in_case = p->u2.goto_target->u2.label_in_case = label_inside_case(p->u2.goto_target);
03316          }
03317          
03318          if (!p->u1.list->next) /* just one */ {
03319             pr->app = strdup("Goto");
03320             if (!mother_exten)
03321                pr->appargs = strdup(p->u1.list->u1.str);
03322             else {  /* for the case of simple within-extension gotos in case/pattern/default statement blocks: */ 
03323                snprintf(buf1,sizeof(buf1),"%s|%s", mother_exten->name, p->u1.list->u1.str);
03324                pr->appargs = strdup(buf1);
03325             }
03326             
03327          } else if (p->u1.list->next && !p->u1.list->next->next) /* two */ {
03328             snprintf(buf1,sizeof(buf1),"%s|%s", p->u1.list->u1.str, p->u1.list->next->u1.str);
03329             pr->app = strdup("Goto");
03330             pr->appargs = strdup(buf1);
03331          } else if (p->u1.list->next && p->u1.list->next->next) {
03332             snprintf(buf1,sizeof(buf1),"%s|%s|%s", p->u1.list->u1.str, 
03333                   p->u1.list->next->u1.str,
03334                   p->u1.list->next->next->u1.str);
03335             pr->app = strdup("Goto");
03336             pr->appargs = strdup(buf1);
03337          }
03338          pr->origin = p;
03339          linkprio(exten, pr, mother_exten);
03340          break;
03341 
03342       case PV_LABEL:
03343          pr = new_prio();
03344          pr->type = AEL_LABEL;
03345          pr->origin = p;
03346          p->u3.compiled_label = exten;
03347          linkprio(exten, pr, mother_exten);
03348          break;
03349 
03350       case PV_FOR:
03351          control_statement_count++;
03352          loop_break_save = exten->loop_break; /* save them, then restore before leaving */
03353          loop_continue_save = exten->loop_continue;
03354          snprintf(new_label,sizeof(new_label),"for-%s-%d", label, control_statement_count);
03355          for_init = new_prio();
03356          for_inc = new_prio();
03357          for_test = new_prio();
03358          for_loop = new_prio();
03359          for_end = new_prio();
03360          for_init->type = AEL_APPCALL;
03361          for_inc->type = AEL_APPCALL;
03362          for_test->type = AEL_FOR_CONTROL;
03363          for_test->goto_false = for_end;
03364          for_loop->type = AEL_CONTROL1; /* simple goto */
03365          for_end->type = AEL_APPCALL;
03366          for_init->app = strdup("Set");
03367          
03368          strcpy(buf2,p->u1.for_init);
03369          remove_spaces_before_equals(buf2);
03370          strp = strchr(buf2, '=');
03371          if (strp) {
03372             strp2 = strchr(p->u1.for_init, '=');
03373             *(strp+1) = 0;
03374             strcat(buf2,"$[");
03375             strncat(buf2,strp2+1, sizeof(buf2)-strlen(strp2+1)-2);
03376             strcat(buf2,"]");
03377             for_init->appargs = strdup(buf2);
03378             /* for_init->app = strdup("Set"); just set! */
03379          } else {
03380             strp2 = p->u1.for_init;
03381             while (*strp2 && isspace(*strp2))
03382                strp2++;
03383             if (*strp2 == '&') { /* itsa macro call */
03384                char *strp3 = strp2+1;
03385                while (*strp3 && isspace(*strp3))
03386                   strp3++;
03387                strcpy(buf2, strp3);
03388                strp3 = strchr(buf2,'(');
03389                if (strp3) {
03390                   *strp3 = '|';
03391                }
03392                while ((strp3=strchr(buf2,','))) {
03393                   *strp3 = '|';
03394                }
03395                strp3 = strrchr(buf2, ')');
03396                if (strp3)
03397                   *strp3 = 0; /* remove the closing paren */
03398 
03399                for_init->appargs = strdup(buf2);
03400                if (for_init->app)
03401                   free(for_init->app);
03402                for_init->app = strdup("Macro");
03403             } else {  /* must be a regular app call */
03404                char *strp3;
03405                strcpy(buf2, strp2);
03406                strp3 = strchr(buf2,'(');
03407                if (strp3) {
03408                   *strp3 = 0;
03409                   if (for_init->app)
03410                      free(for_init->app);
03411                   for_init->app = strdup(buf2);
03412                   for_init->appargs = strdup(strp3+1);
03413                   strp3 = strrchr(for_init->appargs, ')');
03414                   if (strp3)
03415                      *strp3 = 0; /* remove the closing paren */
03416                }
03417             }
03418          }
03419 
03420          strcpy(buf2,p->u3.for_inc);
03421          remove_spaces_before_equals(buf2);
03422          strp = strchr(buf2, '=');
03423          if (strp) {  /* there's an = in this part; that means an assignment. set it up */
03424             strp2 = strchr(p->u3.for_inc, '=');
03425             *(strp+1) = 0;
03426             strcat(buf2,"$[");
03427             strncat(buf2,strp2+1, sizeof(buf2)-strlen(strp2+1)-2);
03428             strcat(buf2,"]");
03429             for_inc->appargs = strdup(buf2);
03430             for_inc->app = strdup("Set");
03431          } else {
03432             strp2 = p->u3.for_inc;
03433             while (*strp2 && isspace(*strp2))
03434                strp2++;
03435             if (*strp2 == '&') { /* itsa macro call */
03436                char *strp3 = strp2+1;
03437                while (*strp3 && isspace(*strp3))
03438                   strp3++;
03439                strcpy(buf2, strp3);
03440                strp3 = strchr(buf2,'(');
03441                if (strp3) {
03442                   *strp3 = '|';
03443                }
03444                while ((strp3=strchr(buf2,','))) {
03445                   *strp3 = '|';
03446                }
03447                strp3 = strrchr(buf2, ')');
03448                if (strp3)
03449                   *strp3 = 0; /* remove the closing paren */
03450 
03451                for_inc->appargs = strdup(buf2);
03452 
03453                for_inc->app = strdup("Macro");
03454             } else {  /* must be a regular app call */
03455                char *strp3;
03456                strcpy(buf2, strp2);
03457                strp3 = strchr(buf2,'(');
03458                if (strp3) {
03459                   *strp3 = 0;
03460                   for_inc->app = strdup(buf2);
03461                   for_inc->appargs = strdup(strp3+1);
03462                   strp3 = strrchr(for_inc->appargs, ')');
03463                   if (strp3)
03464                      *strp3 = 0; /* remove the closing paren */
03465                }
03466             }
03467          }
03468          snprintf(buf1,sizeof(buf1),"$[%s]",p->u2.for_test);
03469          for_test->app = 0;
03470          for_test->appargs = strdup(buf1);
03471          for_loop->goto_true = for_test;
03472          snprintf(buf1,sizeof(buf1),"Finish for-%s-%d", label, control_statement_count);
03473          for_end->app = strdup("NoOp");
03474          for_end->appargs = strdup(buf1);
03475          /* link & load! */
03476          linkprio(exten, for_init, mother_exten);
03477          linkprio(exten, for_test, mother_exten);
03478          
03479          /* now, put the body of the for loop here */
03480          exten->loop_break = for_end;
03481          exten->loop_continue = for_inc;
03482          
03483          gen_prios(exten, new_label, p->u4.for_statements, mother_exten, this_context); /* this will link in all the statements here */
03484          
03485          linkprio(exten, for_inc, mother_exten);
03486          linkprio(exten, for_loop, mother_exten);
03487          linkprio(exten, for_end, mother_exten);
03488          
03489          
03490          exten->loop_break = loop_break_save;
03491          exten->loop_continue = loop_continue_save;
03492          for_loop->origin = p;
03493          break;
03494 
03495       case PV_WHILE:
03496          control_statement_count++;
03497          loop_break_save = exten->loop_break; /* save them, then restore before leaving */
03498          loop_continue_save = exten->loop_continue;
03499          snprintf(new_label,sizeof(new_label),"while-%s-%d", label, control_statement_count);
03500          while_test = new_prio();
03501          while_loop = new_prio();
03502          while_end = new_prio();
03503          while_test->type = AEL_FOR_CONTROL;
03504          while_test->goto_false = while_end;
03505          while_loop->type = AEL_CONTROL1; /* simple goto */
03506          while_end->type = AEL_APPCALL;
03507          snprintf(buf1,sizeof(buf1),"$[%s]",p->u1.str);
03508          while_test->app = 0;
03509          while_test->appargs = strdup(buf1);
03510          while_loop->goto_true = while_test;
03511          snprintf(buf1,sizeof(buf1),"Finish while-%s-%d", label, control_statement_count);
03512          while_end->app = strdup("NoOp");
03513          while_end->appargs = strdup(buf1);
03514 
03515          linkprio(exten, while_test, mother_exten);
03516          
03517          /* now, put the body of the for loop here */
03518          exten->loop_break = while_end;
03519          exten->loop_continue = while_test;
03520          
03521          gen_prios(exten, new_label, p->u2.statements, mother_exten, this_context); /* this will link in all the while body statements here */
03522 
03523          linkprio(exten, while_loop, mother_exten);
03524          linkprio(exten, while_end, mother_exten);
03525          
03526          
03527          exten->loop_break = loop_break_save;
03528          exten->loop_continue = loop_continue_save;
03529          while_loop->origin = p;
03530          break;
03531 
03532       case PV_SWITCH:
03533          control_statement_count++;
03534          local_control_statement_count = control_statement_count;
03535          loop_break_save = exten->loop_break; /* save them, then restore before leaving */
03536          loop_continue_save = exten->loop_continue;
03537          snprintf(new_label,sizeof(new_label),"sw-%s-%d", label, control_statement_count);
03538          switch_test = new_prio();
03539          switch_end = new_prio();
03540          switch_test->type = AEL_APPCALL;
03541          switch_end->type = AEL_APPCALL;
03542          strncpy(buf2,p->u1.str,sizeof(buf2));
03543          buf2[sizeof(buf2)-1] = 0; /* just in case */
03544          substitute_commas(buf2);
03545          snprintf(buf1,sizeof(buf1),"sw-%d-%s|10",control_statement_count, buf2);
03546          switch_test->app = strdup("Goto");
03547          switch_test->appargs = strdup(buf1);
03548          snprintf(buf1,sizeof(buf1),"Finish switch-%s-%d", label, control_statement_count);
03549          switch_end->app = strdup("NoOp");
03550          switch_end->appargs = strdup(buf1);
03551          switch_end->origin = p;
03552          switch_end->exten = exten;
03553 
03554          linkprio(exten, switch_test, mother_exten);
03555          linkprio(exten, switch_end, mother_exten);
03556          
03557          exten->loop_break = switch_end;
03558          exten->loop_continue = 0;
03559          default_exists = 0;
03560          
03561          for (p2=p->u2.statements; p2; p2=p2->next) {
03562             /* now, for each case/default put the body of the for loop here */
03563             if (p2->type == PV_CASE) {
03564                /* ok, generate a extension and link it in */
03565                switch_case = new_exten();
03566                if (mother_exten && mother_exten->checked_switch) {
03567                   switch_case->has_switch = mother_exten->has_switch;
03568                   switch_case->checked_switch = mother_exten->checked_switch;
03569                }
03570                if (exten && exten->checked_switch) {
03571                   switch_case->has_switch = exten->has_switch;
03572                   switch_case->checked_switch = exten->checked_switch;
03573                }
03574                switch_case->context = this_context;
03575                switch_case->is_switch = 1;
03576                /* the break/continue locations are inherited from parent */
03577                switch_case->loop_break = exten->loop_break;
03578                switch_case->loop_continue = exten->loop_continue;
03579                
03580                linkexten(exten,switch_case);
03581                strncpy(buf2,p2->u1.str,sizeof(buf2));
03582                buf2[sizeof(buf2)-1] = 0; /* just in case */
03583                substitute_commas(buf2);
03584                snprintf(buf1,sizeof(buf1),"sw-%d-%s", local_control_statement_count, buf2);
03585                switch_case->name = strdup(buf1);
03586                snprintf(new_label,sizeof(new_label),"sw-%s-%s-%d", label, buf2, local_control_statement_count);
03587                
03588                gen_prios(switch_case, new_label, p2->u2.statements, exten, this_context); /* this will link in all the case body statements here */
03589 
03590                /* here is where we write code to "fall thru" to the next case... if there is one... */
03591                for (p3=p2->u2.statements; p3; p3=p3->next) {
03592                   if (!p3->next)
03593                      break;
03594                }
03595                /* p3 now points the last statement... */
03596                if (!p3 || ( p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN) ) {
03597                   /* is there a following CASE/PATTERN/DEFAULT? */
03598                   if (p2->next && p2->next->type == PV_CASE) {
03599                      fall_thru = new_prio();
03600                      fall_thru->type = AEL_APPCALL;
03601                      fall_thru->app = strdup("Goto");
03602                      strncpy(buf2,p2->next->u1.str,sizeof(buf2));
03603                      buf2[sizeof(buf2)-1] = 0; /* just in case */
03604                      substitute_commas(buf2);
03605                      snprintf(buf1,sizeof(buf1),"sw-%d-%s|10",local_control_statement_count, buf2);
03606                      fall_thru->appargs = strdup(buf1);
03607                      linkprio(switch_case, fall_thru, mother_exten);
03608                   } else if (p2->next && p2->next->type == PV_PATTERN) {
03609                      fall_thru = new_prio();
03610                      fall_thru->type = AEL_APPCALL;
03611                      fall_thru->app = strdup("Goto");
03612                      gen_match_to_pattern(p2->next->u1.str, buf2);
03613                      substitute_commas(buf2);
03614                      snprintf(buf1,sizeof(buf1),"sw-%d-%s|10", local_control_statement_count, buf2);
03615                      fall_thru->appargs = strdup(buf1);
03616                      linkprio(switch_case, fall_thru, mother_exten);
03617                   } else if (p2->next && p2->next->type == PV_DEFAULT) {
03618                      fall_thru = new_prio();
03619                      fall_thru->type = AEL_APPCALL;
03620                      fall_thru->app = strdup("Goto");
03621                      snprintf(buf1,sizeof(buf1),"sw-%d-.|10",local_control_statement_count);
03622                      fall_thru->appargs = strdup(buf1);
03623                      linkprio(switch_case, fall_thru, mother_exten);
03624                   } else if (!p2->next) {
03625                      fall_thru = new_prio();
03626                      fall_thru->type = AEL_CONTROL1;
03627                      fall_thru->goto_true = switch_end;
03628                      fall_thru->app = strdup("Goto");
03629                      linkprio(switch_case, fall_thru, mother_exten);
03630                   }
03631                }
03632                if (switch_case->return_needed) {
03633                   char buf[2000];
03634                   struct ael_priority *np2 = new_prio();
03635                   np2->type = AEL_APPCALL;
03636                   np2->app = strdup("NoOp");
03637                   snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name);
03638                   np2->appargs = strdup(buf);
03639                   linkprio(switch_case, np2, mother_exten);
03640                   switch_case-> return_target = np2;
03641                }
03642             } else if (p2->type == PV_PATTERN) {
03643                /* ok, generate a extension and link it in */
03644                switch_case = new_exten();
03645                if (mother_exten && mother_exten->checked_switch) {
03646                   switch_case->has_switch = mother_exten->has_switch;
03647                   switch_case->checked_switch = mother_exten->checked_switch;
03648                }
03649                if (exten && exten->checked_switch) {
03650                   switch_case->has_switch = exten->has_switch;
03651                   switch_case->checked_switch = exten->checked_switch;
03652                }
03653                switch_case->context = this_context;
03654                switch_case->is_switch = 1;
03655                /* the break/continue locations are inherited from parent */
03656                switch_case->loop_break = exten->loop_break;
03657                switch_case->loop_continue = exten->loop_continue;
03658                
03659                linkexten(exten,switch_case);
03660                strncpy(buf2,p2->u1.str,sizeof(buf2));
03661                buf2[sizeof(buf2)-1] = 0; /* just in case */
03662                substitute_commas(buf2);
03663                snprintf(buf1,sizeof(buf1),"_sw-%d-%s", local_control_statement_count, buf2);
03664                switch_case->name = strdup(buf1);
03665                snprintf(new_label,sizeof(new_label),"sw-%s-%s-%d", label, buf2, local_control_statement_count);
03666                
03667                gen_prios(switch_case, new_label, p2->u2.statements, exten, this_context); /* this will link in all the while body statements here */
03668                /* here is where we write code to "fall thru" to the next case... if there is one... */
03669                for (p3=p2->u2.statements; p3; p3=p3->next) {
03670                   if (!p3->next)
03671                      break;
03672                }
03673                /* p3 now points the last statement... */
03674                if (!p3 || ( p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN)) {
03675                   /* is there a following CASE/PATTERN/DEFAULT? */
03676                   if (p2->next && p2->next->type == PV_CASE) {
03677                      fall_thru = new_prio();
03678                      fall_thru->type = AEL_APPCALL;
03679                      fall_thru->app = strdup("Goto");
03680                      strncpy(buf2,p2->next->u1.str,sizeof(buf2));
03681                      buf2[sizeof(buf2)-1] = 0; /* just in case */
03682                      substitute_commas(buf2);
03683                      snprintf(buf1,sizeof(buf1),"sw-%d-%s|10",local_control_statement_count, buf2);
03684                      fall_thru->appargs = strdup(buf1);
03685                      linkprio(switch_case, fall_thru, mother_exten);
03686                   } else if (p2->next && p2->next->type == PV_PATTERN) {
03687                      fall_thru = new_prio();
03688                      fall_thru->type = AEL_APPCALL;
03689                      fall_thru->app = strdup("Goto");
03690                      gen_match_to_pattern(p2->next->u1.str, buf2);
03691                      substitute_commas(buf2);
03692                      snprintf(buf1,sizeof(buf1),"sw-%d-%s|10",local_control_statement_count, buf2);
03693                      fall_thru->appargs = strdup(buf1);
03694                      linkprio(switch_case, fall_thru, mother_exten);
03695                   } else if (p2->next && p2->next->type == PV_DEFAULT) {
03696                      fall_thru = new_prio();
03697                      fall_thru->type = AEL_APPCALL;
03698                      fall_thru->app = strdup("Goto");
03699                      snprintf(buf1,sizeof(buf1),"sw-%d-.|10",local_control_statement_count);
03700                      fall_thru->appargs = strdup(buf1);
03701                      linkprio(switch_case, fall_thru, mother_exten);
03702                   } else if (!p2->next) {
03703                      fall_thru = new_prio();
03704                      fall_thru->type = AEL_CONTROL1;
03705                      fall_thru->goto_true = switch_end;
03706                      fall_thru->app = strdup("Goto");
03707                      linkprio(switch_case, fall_thru, mother_exten);
03708                   }
03709                }
03710                if (switch_case->return_needed) {
03711                   char buf[2000];
03712                   struct ael_priority *np2 = new_prio();
03713                   np2->type = AEL_APPCALL;
03714                   np2->app = strdup("NoOp");
03715                   snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name);
03716                   np2->appargs = strdup(buf);
03717                   linkprio(switch_case, np2, mother_exten);
03718                   switch_case-> return_target = np2;
03719                }
03720             } else if (p2->type == PV_DEFAULT) {
03721                /* ok, generate a extension and link it in */
03722                switch_case = new_exten();
03723                if (mother_exten && mother_exten->checked_switch) {
03724                   switch_case->has_switch = mother_exten->has_switch;
03725                   switch_case->checked_switch = mother_exten->checked_switch;
03726                }
03727                if (exten && exten->checked_switch) {
03728                   switch_case->has_switch = exten->has_switch;
03729                   switch_case->checked_switch = exten->checked_switch;
03730                }
03731                switch_case->context = this_context;
03732                switch_case->is_switch = 1;
03733                
03734                /* new: the default case intros a pattern with ., which covers ALMOST everything.
03735                   but it doesn't cover a NULL pattern. So, we'll define a null extension to match
03736                   that goto's the default extension. */
03737 
03738                default_exists++;
03739                switch_null = new_exten();
03740                if (mother_exten && mother_exten->checked_switch) {
03741                   switch_null->has_switch = mother_exten->has_switch;
03742                   switch_null->checked_switch = mother_exten->checked_switch;
03743                }
03744                if (exten && exten->checked_switch) {
03745                   switch_null->has_switch = exten->has_switch;
03746                   switch_null->checked_switch = exten->checked_switch;
03747                }
03748                switch_null->context = this_context;
03749                switch_null->is_switch = 1;
03750                switch_empty = new_prio();
03751                snprintf(buf1,sizeof(buf1),"sw-%d-.|10",local_control_statement_count);
03752                switch_empty->app = strdup("Goto");
03753                switch_empty->appargs = strdup(buf1);
03754                linkprio(switch_null, switch_empty, mother_exten);
03755                snprintf(buf1,sizeof(buf1),"sw-%d-", local_control_statement_count);
03756                switch_null->name = strdup(buf1);
03757                switch_null->loop_break = exten->loop_break;
03758                switch_null->loop_continue = exten->loop_continue;
03759                linkexten(exten,switch_null);
03760 
03761                /* the break/continue locations are inherited from parent */
03762                switch_case->loop_break = exten->loop_break;
03763                switch_case->loop_continue = exten->loop_continue;
03764                linkexten(exten,switch_case);
03765                snprintf(buf1,sizeof(buf1),"_sw-%d-.", local_control_statement_count);
03766                switch_case->name = strdup(buf1);
03767                
03768                snprintf(new_label,sizeof(new_label),"sw-%s-default-%d", label, local_control_statement_count);
03769                
03770                gen_prios(switch_case, new_label, p2->u2.statements, exten, this_context); /* this will link in all the default:  body statements here */
03771                
03772                /* here is where we write code to "fall thru" to the next case... if there is one... */
03773                for (p3=p2->u2.statements; p3; p3=p3->next) {
03774                   if (!p3->next)
03775                      break;
03776                }
03777                /* p3 now points the last statement... */
03778                if (!p3 || (p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN)) {
03779                   /* is there a following CASE/PATTERN/DEFAULT? */
03780                   if (p2->next && p2->next->type == PV_CASE) {
03781                      fall_thru = new_prio();
03782                      fall_thru->type = AEL_APPCALL;
03783                      fall_thru->app = strdup("Goto");
03784                      strncpy(buf2,p2->next->u1.str,sizeof(buf2));
03785                      buf2[sizeof(buf2)-1] = 0; /* just in case */
03786                      substitute_commas(buf2);
03787                      snprintf(buf1,sizeof(buf1),"sw-%d-%s|10",local_control_statement_count, buf2);
03788                      fall_thru->appargs = strdup(buf1);
03789                      linkprio(switch_case, fall_thru, mother_exten);
03790                   } else if (p2->next && p2->next->type == PV_PATTERN) {
03791                      fall_thru = new_prio();
03792                      fall_thru->type = AEL_APPCALL;
03793                      fall_thru->app = strdup("Goto");
03794                      gen_match_to_pattern(p2->next->u1.str, buf2);
03795                      substitute_commas(buf2);
03796                      snprintf(buf1,sizeof(buf1),"sw-%d-%s|10",local_control_statement_count, buf2);
03797                      fall_thru->appargs = strdup(buf1);
03798                      linkprio(switch_case, fall_thru, mother_exten);
03799                   } else if (p2->next && p2->next->type == PV_DEFAULT) {
03800                      fall_thru = new_prio();
03801                      fall_thru->type = AEL_APPCALL;
03802                      fall_thru->app = strdup("Goto");
03803                      snprintf(buf1,sizeof(buf1),"sw-%d-.|10",local_control_statement_count);
03804                      fall_thru->appargs = strdup(buf1);
03805                      linkprio(switch_case, fall_thru, mother_exten);
03806                   } else if (!p2->next) {
03807                      fall_thru = new_prio();
03808                      fall_thru->type = AEL_CONTROL1;
03809                      fall_thru->goto_true = switch_end;
03810                      fall_thru->app = strdup("Goto");
03811                      linkprio(switch_case, fall_thru, mother_exten);
03812                   }
03813                }
03814                if (switch_case->return_needed) {
03815                   char buf[2000];
03816                   struct ael_priority *np2 = new_prio();
03817                   np2->type = AEL_APPCALL;
03818                   np2->app = strdup("NoOp");
03819                   snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name);
03820                   np2->appargs = strdup(buf);
03821                   linkprio(switch_case, np2, mother_exten);
03822                   switch_case-> return_target = np2;
03823                }
03824             } else {
03825                /* what could it be??? */
03826             }
03827          }
03828          
03829          exten->loop_break = loop_break_save;
03830          exten->loop_continue = loop_continue_save;
03831          switch_test->origin = p;
03832          switch_end->origin = p;
03833          break;
03834 
03835       case PV_MACRO_CALL:
03836          pr = new_prio();
03837          pr->type = AEL_APPCALL;
03838          snprintf(buf1,sizeof(buf1),"%s", p->u1.str);
03839          for (p2 = p->u2.arglist; p2; p2 = p2->next) {
03840             strcat(buf1,"|");
03841             strcat(buf1,p2->u1.str);
03842          }
03843          pr->app = strdup("Macro");
03844          pr->appargs = strdup(buf1);
03845          pr->origin = p;
03846          linkprio(exten, pr, mother_exten);
03847          break;
03848 
03849       case PV_APPLICATION_CALL:
03850          pr = new_prio();
03851          pr->type = AEL_APPCALL;
03852          buf1[0] = 0;
03853          for (p2 = p->u2.arglist; p2; p2 = p2->next) {
03854             if (p2 != p->u2.arglist )
03855                strcat(buf1,"|");
03856             substitute_commas(p2->u1.str);
03857             strcat(buf1,p2->u1.str);
03858          }
03859          pr->app = strdup(p->u1.str);
03860          pr->appargs = strdup(buf1);
03861          pr->origin = p;
03862          linkprio(exten, pr, mother_exten);
03863          break;
03864 
03865       case PV_BREAK:
03866          pr = new_prio();
03867          pr->type = AEL_CONTROL1; /* simple goto */
03868          pr->goto_true = exten->loop_break;
03869          pr->origin = p;
03870          linkprio(exten, pr, mother_exten);
03871          break;
03872 
03873       case PV_RETURN: /* hmmmm */
03874          pr = new_prio();
03875          pr->type = AEL_RETURN; /* simple goto */
03876          exten->return_needed++;
03877          pr->app = strdup("Goto");
03878          pr->appargs = strdup("");
03879          pr->origin = p;
03880          linkprio(exten, pr, mother_exten);
03881          break;
03882 
03883       case PV_CONTINUE:
03884          pr = new_prio();
03885          pr->type = AEL_CONTROL1; /* simple goto */
03886          pr->goto_true = exten->loop_continue;
03887          pr->origin = p;
03888          linkprio(exten, pr, mother_exten);
03889          break;
03890 
03891 #ifdef OLD_RAND_ACTION
03892       case PV_RANDOM:
03893          control_statement_count++;
03894          snprintf(new_label,sizeof(new_label),"rand-%s-%d", label, control_statement_count);
03895          rand_test = new_prio();
03896          rand_test->type = AEL_RAND_CONTROL;
03897          snprintf(buf1,sizeof(buf1),"$[%s]",
03898                 p->u1.str );
03899          rand_test->app = 0;
03900          rand_test->appargs = strdup(buf1);
03901          rand_test->origin = p;
03902          
03903          rand_end = new_prio();
03904          rand_end->type = AEL_APPCALL;
03905          snprintf(buf1,sizeof(buf1),"Finish rand-%s-%d", label, control_statement_count);
03906          rand_end->app = strdup("NoOp");
03907          rand_end->appargs = strdup(buf1);
03908          
03909          rand_skip = new_prio();
03910          rand_skip->type = AEL_CONTROL1; /* simple goto */
03911          rand_skip->goto_true = rand_end;
03912          rand_skip->origin  = p;
03913 
03914          rand_test->goto_true = rand_skip; /* +1, really */
03915 
03916          linkprio(exten, rand_test, mother_exten);
03917          
03918          if (p->u3.else_statements) {
03919             gen_prios(exten, new_label, p->u3.else_statements, mother_exten, this_context); /* this will link in all the else statements here */
03920          }
03921          
03922          linkprio(exten, rand_skip, mother_exten);
03923          
03924          gen_prios(exten, new_label, p->u2.statements, mother_exten, this_context); /* this will link in all the "true" statements here */
03925 
03926          linkprio(exten, rand_end, mother_exten);
03927          
03928          break;
03929 #endif         
03930 
03931       case PV_IFTIME:
03932          control_statement_count++;
03933          snprintf(new_label,sizeof(new_label),"iftime-%s-%d", label, control_statement_count);
03934          
03935          if_test = new_prio();
03936          if_test->type = AEL_IFTIME_CONTROL;
03937          snprintf(buf1,sizeof(buf1),"%s|%s|%s|%s",
03938                 p->u1.list->u1.str, 
03939                 p->u1.list->next->u1.str, 
03940                 p->u1.list->next->next->u1.str, 
03941                 p->u1.list->next->next->next->u1.str);
03942          if_test->app = 0;
03943          if_test->appargs = strdup(buf1);
03944          if_test->origin = p;
03945 
03946          if_end = new_prio();
03947          if_end->type = AEL_APPCALL;
03948          snprintf(buf1,sizeof(buf1),"Finish iftime-%s-%d", label, control_statement_count);
03949          if_end->app = strdup("NoOp");
03950          if_end->appargs = strdup(buf1);
03951 
03952          if (p->u3.else_statements) {
03953             if_skip = new_prio();
03954             if_skip->type = AEL_CONTROL1; /* simple goto */
03955             if_skip->goto_true = if_end;
03956             if_skip->origin  = p;
03957 
03958          } else {
03959             if_skip = 0;
03960 
03961             if_test->goto_false = if_end;
03962          }
03963 
03964          if_false = new_prio();
03965          if_false->type = AEL_CONTROL1;
03966          if (p->u3.else_statements) {
03967             if_false->goto_true = if_skip; /* +1 */
03968          } else {
03969             if_false->goto_true = if_end;
03970          }
03971          
03972          /* link & load! */
03973          linkprio(exten, if_test, mother_exten);
03974          linkprio(exten, if_false, mother_exten);
03975          
03976          /* now, put the body of the if here */
03977          
03978          gen_prios(exten, new_label, p->u2.statements, mother_exten, this_context); /* this will link in all the statements here */
03979          
03980          if (p->u3.else_statements) {
03981             linkprio(exten, if_skip, mother_exten);
03982             gen_prios(exten, new_label, p->u3.else_statements, mother_exten, this_context); /* this will link in all the statements here */
03983 
03984          }
03985          
03986          linkprio(exten, if_end, mother_exten);
03987          
03988          break;
03989 
03990       case PV_RANDOM:
03991       case PV_IF:
03992          control_statement_count++;
03993          snprintf(new_label,sizeof(new_label),"if-%s-%d", label, control_statement_count);
03994          
03995          if_test = new_prio();
03996          if_end = new_prio();
03997          if_test->type = AEL_IF_CONTROL;
03998          if_end->type = AEL_APPCALL;
03999          if ( p->type == PV_RANDOM )
04000             snprintf(buf1,sizeof(buf1),"$[${RAND(0|99)} < (%s)]",p->u1.str);
04001          else {
04002             char buf[8000];
04003             strcpy(buf,p->u1.str);
04004             substitute_commas(buf);
04005             snprintf(buf1,sizeof(buf1),"$[%s]",buf);
04006          }
04007          
04008          if_test->app = 0;
04009          if_test->appargs = strdup(buf1);
04010          snprintf(buf1,sizeof(buf1),"Finish if-%s-%d", label, control_statement_count);
04011          if_end->app = strdup("NoOp");
04012          if_end->appargs = strdup(buf1);
04013          if_test->origin = p;
04014          
04015          if (p->u3.else_statements) {
04016             if_skip = new_prio();
04017             if_skip->type = AEL_CONTROL1; /* simple goto */
04018             if_skip->goto_true = if_end;
04019             if_test->goto_false = if_skip;;
04020          } else {
04021             if_skip = 0;
04022             if_test->goto_false = if_end;;
04023          }
04024          
04025          /* link & load! */
04026          linkprio(exten, if_test, mother_exten);
04027          
04028          /* now, put the body of the if here */
04029          
04030          gen_prios(exten, new_label, p->u2.statements, mother_exten, this_context); /* this will link in all the statements here */
04031          
04032          if (p->u3.else_statements) {
04033             linkprio(exten, if_skip, mother_exten);
04034             gen_prios(exten, new_label, p->u3.else_statements, mother_exten, this_context); /* this will link in all the statements here */
04035 
04036          }
04037          
04038          linkprio(exten, if_end, mother_exten);
04039          
04040          break;
04041 
04042       case PV_STATEMENTBLOCK:
04043          gen_prios(exten, label, p->u1.list, mother_exten, this_context ); /* recurse into the block */
04044          break;
04045 
04046       case PV_CATCH:
04047          control_statement_count++;
04048          /* generate an extension with name of catch, put all catch stats
04049             into this exten! */
04050          switch_case = new_exten();
04051          if (mother_exten && mother_exten->checked_switch) {
04052             switch_case->has_switch = mother_exten->has_switch;
04053             switch_case->checked_switch = mother_exten->checked_switch;
04054          }
04055          if (exten && exten->checked_switch) {
04056             switch_case->has_switch = exten->has_switch;
04057             switch_case->checked_switch = exten->checked_switch;
04058          }
04059          
04060          switch_case->context = this_context;
04061          linkexten(exten,switch_case);
04062          switch_case->name = strdup(p->u1.str);
04063          snprintf(new_label,sizeof(new_label),"catch-%s-%d",p->u1.str, control_statement_count);
04064          
04065          gen_prios(switch_case, new_label, p->u2.statements,mother_exten,this_context); /* this will link in all the catch body statements here */
04066          if (switch_case->return_needed) {
04067             char buf[2000];
04068             struct ael_priority *np2 = new_prio();
04069             np2->type = AEL_APPCALL;
04070             np2->app = strdup("NoOp");
04071             snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name);
04072             np2->appargs = strdup(buf);
04073             linkprio(switch_case, np2, mother_exten);
04074             switch_case-> return_target = np2;
04075          }
04076 
04077          break;
04078       default:
04079          break;
04080       }
04081    }
04082 }
04083 
04084 void set_priorities(struct ael_extension *exten)
04085 {
04086    int i;
04087    struct ael_priority *pr;
04088    do {
04089       if (exten->is_switch)
04090          i = 10;
04091       else if (exten->regexten)
04092          i=2;
04093       else
04094          i=1;
04095       
04096       for (pr=exten->plist; pr; pr=pr->next) {
04097          pr->priority_num = i;
04098          
04099          if (!pr->origin || (pr->origin && pr->origin->type != PV_LABEL) ) /* Labels don't show up in the dialplan,
04100                                       but we want them to point to the right
04101                                       priority, which would be the next line
04102                                       after the label; */
04103             i++;
04104       }
04105       
04106       exten = exten->next_exten;
04107    } while ( exten );
04108 }
04109 
04110 void add_extensions(struct ael_extension *exten)
04111 {
04112    struct ael_priority *pr;
04113    char *label=0;
04114    char realext[AST_MAX_EXTENSION];
04115    if (!exten) {
04116       ast_log(LOG_WARNING, "This file is Empty!\n" );
04117       return;
04118    }
04119    do {
04120       struct ael_priority *last = 0;
04121 
04122       memset(realext, '\0', sizeof(realext));
04123       pbx_substitute_variables_helper(NULL, exten->name, realext, sizeof(realext) - 1);
04124       if (exten->hints) {
04125          if (ast_add_extension2(exten->context, 0 /*no replace*/, realext, PRIORITY_HINT, NULL, exten->cidmatch, 
04126                           exten->hints, NULL, ast_free, registrar)) {
04127             ast_log(LOG_WARNING, "Unable to add step at priority 'hint' of extension '%s'\n",
04128                   exten->name);
04129          }
04130       }
04131       
04132       for (pr=exten->plist; pr; pr=pr->next) {
04133          char app[2000];
04134          char appargs[2000];
04135 
04136          /* before we can add the extension, we need to prep the app/appargs;
04137             the CONTROL types need to be done after the priority numbers are calculated.
04138          */
04139          if (pr->type == AEL_LABEL) /* don't try to put labels in the dialplan! */ {
04140             last = pr;
04141             continue;
04142          }
04143          
04144          if (pr->app)
04145             strcpy(app, pr->app);
04146          else
04147             app[0] = 0;
04148          if (pr->appargs )
04149             strcpy(appargs, pr->appargs);
04150          else
04151             appargs[0] = 0;
04152          switch( pr->type ) {
04153          case AEL_APPCALL:
04154             /* easy case. Everything is all set up */
04155             break;
04156             
04157          case AEL_CONTROL1: /* FOR loop, WHILE loop, BREAK, CONTINUE, IF, IFTIME */
04158             /* simple, unconditional goto. */
04159             strcpy(app,"Goto");
04160             if (pr->goto_true->origin && pr->goto_true->origin->type == PV_SWITCH ) {
04161                snprintf(appargs,sizeof(appargs),"%s|%d", pr->goto_true->exten->name, pr->goto_true->priority_num);
04162             } else if (pr->goto_true->origin && pr->goto_true->origin->type == PV_IFTIME && pr->goto_true->origin->u3.else_statements ) {
04163                snprintf(appargs,sizeof(appargs),"%d", pr->goto_true->priority_num+1);
04164             } else
04165                snprintf(appargs,sizeof(appargs),"%d", pr->goto_true->priority_num);
04166             break;
04167             
04168          case AEL_FOR_CONTROL:  /* WHILE loop test, FOR loop test */
04169             strcpy(app,"GotoIf");
04170             snprintf(appargs,sizeof(appargs),"%s?%d:%d", pr->appargs, pr->priority_num+1, pr->goto_false->priority_num);
04171             break;
04172             
04173          case AEL_IF_CONTROL:
04174             strcpy(app,"GotoIf");
04175             if (pr->origin->u3.else_statements )
04176                snprintf(appargs,sizeof(appargs),"%s?%d:%d", pr->appargs, pr->priority_num+1, pr->goto_false->priority_num+1);
04177             else
04178                snprintf(appargs,sizeof(appargs),"%s?%d:%d", pr->appargs, pr->priority_num+1, pr->goto_false->priority_num);
04179             break;
04180 
04181          case AEL_RAND_CONTROL:
04182             strcpy(app,"Random");
04183             snprintf(appargs,sizeof(appargs),"%s:%d", pr->appargs, pr->goto_true->priority_num+1);
04184             break;
04185 
04186          case AEL_IFTIME_CONTROL:
04187             strcpy(app,"GotoIfTime");
04188             snprintf(appargs,sizeof(appargs),"%s?%d", pr->appargs, pr->priority_num+2);
04189             break;
04190 
04191          case AEL_RETURN:
04192             strcpy(app,"Goto");
04193             snprintf(appargs,sizeof(appargs), "%d", exten->return_target->priority_num);
04194             break;
04195             
04196          default:
04197             break;
04198          }
04199          if (last && last->type == AEL_LABEL ) {
04200             label = last->origin->u1.str;
04201          }
04202          else
04203             label = 0;
04204          
04205          if (ast_add_extension2(exten->context, 0 /*no replace*/, realext, pr->priority_num, (label?label:NULL), exten->cidmatch, 
04206                           app, strdup(appargs), ast_free, registrar)) {
04207             ast_log(LOG_WARNING, "Unable to add step at priority '%d' of extension '%s'\n", pr->priority_num, 
04208                   exten->name);
04209          }
04210          last = pr;
04211       }
04212       exten = exten->next_exten;
04213    } while ( exten );
04214 }
04215 
04216 static void attach_exten(struct ael_extension **list, struct ael_extension *newmem)
04217 {
04218    /* travel to the end of the list... */
04219    struct ael_extension *lptr;
04220    if( !*list ) {
04221       *list = newmem;
04222       return;
04223    }
04224    lptr = *list;
04225    
04226    while( lptr->next_exten ) {
04227       lptr = lptr->next_exten;
04228    }
04229    /* lptr should now pointing to the last element in the list; it has a null next_exten pointer */
04230    lptr->next_exten = newmem;
04231 }
04232 
04233 static pval *get_extension_or_contxt(pval *p)
04234 {
04235    while( p && p->type != PV_EXTENSION && p->type != PV_CONTEXT && p->type != PV_MACRO ) {
04236       
04237       p = p->dad;
04238    }
04239    
04240    return p;
04241 }
04242 
04243 static pval *get_contxt(pval *p)
04244 {
04245    while( p && p->type != PV_CONTEXT && p->type != PV_MACRO ) {
04246       
04247       p = p->dad;
04248    }
04249    
04250    return p;
04251 }
04252 
04253 static void fix_gotos_in_extensions(struct ael_extension *exten)
04254 {
04255    struct ael_extension *e;
04256    for(e=exten;e;e=e->next_exten) {
04257 
04258       struct ael_priority *p;
04259       for(p=e->plist;p;p=p->next) {
04260          
04261          if( p->origin && p->origin->type == PV_GOTO && p->origin->u3.goto_target_in_case ) {
04262             
04263             /* fix the extension of the goto target to the actual extension in the post-compiled dialplan */
04264 
04265             pval *target = p->origin->u2.goto_target;
04266             struct ael_extension *z = target->u3.compiled_label;
04267             pval *pv2 = p->origin;
04268             char buf1[500];
04269             char *apparg_save = p->appargs;
04270             
04271             p->appargs = 0;
04272             if (!pv2->u1.list->next) /* just one  -- it won't hurt to repeat the extension */ {
04273                snprintf(buf1,sizeof(buf1),"%s|%s", z->name, pv2->u1.list->u1.str);
04274                p->appargs = strdup(buf1);
04275                
04276             } else if (pv2->u1.list->next && !pv2->u1.list->next->next) /* two */ {
04277                snprintf(buf1,sizeof(buf1),"%s|%s", z->name, pv2->u1.list->next->u1.str);
04278                p->appargs = strdup(buf1);
04279             } else if (pv2->u1.list->next && pv2->u1.list->next->next) {
04280                snprintf(buf1,sizeof(buf1),"%s|%s|%s", pv2->u1.list->u1.str, 
04281                       z->name,
04282                       pv2->u1.list->next->next->u1.str);
04283                p->appargs = strdup(buf1);
04284             }
04285             else
04286                printf("WHAT? The goto doesn't fall into one of three cases for GOTO????\n");
04287             
04288             if( apparg_save ) {
04289                free(apparg_save);
04290             }
04291          }
04292       }
04293    }
04294 }
04295 
04296 
04297 void ast_compile_ael2(struct ast_context **local_contexts, struct pval *root)
04298 {
04299    pval *p,*p2;
04300    struct ast_context *context;
04301    char buf[2000];
04302    struct ael_extension *exten;
04303    struct ael_extension *exten_list = 0;
04304 
04305    for (p=root; p; p=p->next ) { /* do the globals first, so they'll be there
04306                             when we try to eval them */
04307       switch (p->type) {
04308       case PV_GLOBALS:
04309          /* just VARDEC elements */
04310          for (p2=p->u1.list; p2; p2=p2->next) {
04311             char buf2[2000];
04312             snprintf(buf2,sizeof(buf2),"%s=%s", p2->u1.str, p2->u2.val);
04313             pbx_builtin_setvar(NULL, buf2);
04314          }
04315          break;
04316       default:
04317          break;
04318       }
04319    }
04320    
04321    for (p=root; p; p=p->next ) {
04322       pval *lp;
04323       int argc;
04324       
04325       switch (p->type) {
04326       case PV_MACRO:
04327          strcpy(buf,"macro-");
04328          strcat(buf,p->u1.str);
04329          context = ast_context_create(local_contexts, buf, registrar);
04330          
04331          exten = new_exten();
04332          exten->context = context;
04333          exten->name = strdup("s");
04334          argc = 1;
04335          for (lp=p->u2.arglist; lp; lp=lp->next) {
04336             /* for each arg, set up a "Set" command */
04337             struct ael_priority *np2 = new_prio();
04338             np2->type = AEL_APPCALL;
04339             np2->app = strdup("Set");
04340             snprintf(buf,sizeof(buf),"%s=${ARG%d}", lp->u1.str, argc++);
04341             remove_spaces_before_equals(buf);
04342             np2->appargs = strdup(buf);
04343             linkprio(exten, np2, NULL);
04344          }
04345          /* add any includes */
04346          for (p2=p->u3.macro_statements; p2; p2=p2->next) {
04347             pval *p3;
04348             
04349             switch (p2->type) {
04350             case PV_INCLUDES:
04351                for (p3 = p2->u1.list; p3 ;p3=p3->next) {
04352                   if ( p3->u2.arglist ) {
04353                      snprintf(buf,sizeof(buf), "%s|%s|%s|%s|%s", 
04354                             p3->u1.str,
04355                             p3->u2.arglist->u1.str,
04356                             p3->u2.arglist->next->u1.str,
04357                             p3->u2.arglist->next->next->u1.str,
04358                             p3->u2.arglist->next->next->next->u1.str);
04359                      ast_context_add_include2(context, buf, registrar);
04360                   } else
04361                      ast_context_add_include2(context, p3->u1.str, registrar);
04362                }
04363                break;
04364             default:
04365                break;
04366             }
04367          }
04368          /* CONTAINS APPCALLS, CATCH, just like extensions... */
04369          gen_prios(exten, p->u1.str, p->u3.macro_statements, 0, context );
04370          if (exten->return_needed) {
04371             struct ael_priority *np2 = new_prio();
04372             np2->type = AEL_APPCALL;
04373             np2->app = strdup("NoOp");
04374             snprintf(buf,sizeof(buf),"End of Macro %s-%s",p->u1.str, exten->name);
04375             np2->appargs = strdup(buf);
04376             linkprio(exten, np2, NULL);
04377             exten-> return_target = np2;
04378          }
04379          
04380          set_priorities(exten);
04381          attach_exten(&exten_list, exten);
04382          break;
04383          
04384       case PV_GLOBALS:
04385          /* already done */
04386          break;
04387          
04388       case PV_CONTEXT:
04389          context = ast_context_find_or_create(local_contexts, p->u1.str, registrar);
04390          
04391          /* contexts contain: ignorepat, includes, switches, eswitches, extensions,  */
04392          for (p2=p->u2.statements; p2; p2=p2->next) {
04393             pval *p3;
04394             char *s3;
04395             
04396             switch (p2->type) {
04397             case PV_EXTENSION:
04398                exten = new_exten();
04399                exten->name = strdup(p2->u1.str);
04400                exten->context = context;
04401                
04402                if( (s3=strchr(exten->name, '/') ) != 0 )
04403                {
04404                   *s3 = 0;
04405                   exten->cidmatch = s3+1;
04406                }
04407                
04408                if ( p2->u3.hints )
04409                   exten->hints = strdup(p2->u3.hints);
04410                exten->regexten = p2->u4.regexten;
04411                gen_prios(exten, p->u1.str, p2->u2.statements, 0, context );
04412                if (exten->return_needed) {
04413                   struct ael_priority *np2 = new_prio();
04414                   np2->type = AEL_APPCALL;
04415                   np2->app = strdup("NoOp");
04416                   snprintf(buf,sizeof(buf),"End of Extension %s", exten->name);
04417                   np2->appargs = strdup(buf);
04418                   linkprio(exten, np2, NULL);
04419                   exten-> return_target = np2;
04420                }
04421                /* is the last priority in the extension a label? Then add a trailing no-op */
04422                if ( exten->plist_last && exten->plist_last->type == AEL_LABEL ) {
04423                   struct ael_priority *np2 = new_prio();
04424                   np2->type = AEL_APPCALL;
04425                   np2->app = strdup("NoOp");
04426                   snprintf(buf,sizeof(buf),"A NoOp to follow a trailing label %s", exten->plist_last->origin->u1.str);
04427                   np2->appargs = strdup(buf);
04428                   linkprio(exten, np2, NULL);
04429                }
04430 
04431                set_priorities(exten);
04432                attach_exten(&exten_list, exten);
04433                break;
04434                
04435             case PV_IGNOREPAT:
04436                ast_context_add_ignorepat2(context, p2->u1.str, registrar);
04437                break;
04438                
04439             case PV_INCLUDES:
04440                for (p3 = p2->u1.list; p3 ;p3=p3->next) {
04441                   if ( p3->u2.arglist ) {
04442                      snprintf(buf,sizeof(buf), "%s|%s|%s|%s|%s", 
04443                             p3->u1.str,
04444                             p3->u2.arglist->u1.str,
04445                             p3->u2.arglist->next->u1.str,
04446                             p3->u2.arglist->next->next->u1.str,
04447                             p3->u2.arglist->next->next->next->u1.str);
04448                      ast_context_add_include2(context, buf, registrar);
04449                   } else
04450                      ast_context_add_include2(context, p3->u1.str, registrar);
04451                }
04452                break;
04453                
04454             case PV_SWITCHES:
04455                for (p3 = p2->u1.list; p3 ;p3=p3->next) {
04456                   char *c = strchr(p3->u1.str, '/');
04457                   if (c) {
04458                      *c = '\0';
04459                      c++;
04460                   } else
04461                      c = "";
04462 
04463                   ast_context_add_switch2(context, p3->u1.str, c, 0, registrar);
04464                }
04465                break;
04466 
04467             case PV_ESWITCHES:
04468                for (p3 = p2->u1.list; p3 ;p3=p3->next) {
04469                   char *c = strchr(p3->u1.str, '/');
04470                   if (c) {
04471                      *c = '\0';
04472                      c++;
04473                   } else
04474                      c = "";
04475 
04476                   ast_context_add_switch2(context, p3->u1.str, c, 1, registrar);
04477                }
04478                break;
04479             default:
04480                break;
04481             }
04482          }
04483          
04484          break;
04485          
04486       default:
04487          /* huh? what? */
04488          break;
04489          
04490       }
04491    }
04492    /* moved these from being done after a macro or extension were processed,
04493       to after all processing is done, for the sake of fixing gotos to labels inside cases... */
04494    /* I guess this would be considered 2nd pass of compiler now... */
04495    fix_gotos_in_extensions(exten_list); /* find and fix extension ref in gotos to labels that are in case statements */
04496    add_extensions(exten_list);   /* actually makes calls to create priorities in ast_contexts -- feeds dialplan to asterisk */
04497    destroy_extensions(exten_list);  /* all that remains is an empty husk, discard of it as is proper */
04498    
04499 }
04500 
04501 
04502 static int aeldebug = 0;
04503 
04504 /* interface stuff */
04505 
04506 /* if all the below are static, who cares if they are present? */
04507 
04508 static int pbx_load_module(void)
04509 {
04510    int errs=0, sem_err=0, sem_warn=0, sem_note=0;
04511    char *rfilename;
04512    struct ast_context *local_contexts=NULL, *con;
04513    struct pval *parse_tree;
04514 
04515    ast_log(LOG_NOTICE, "Starting AEL load process.\n");
04516    if (config[0] == '/')
04517       rfilename = (char *)config;
04518    else {
04519       rfilename = alloca(strlen(config) + strlen(ast_config_AST_CONFIG_DIR) + 2);
04520       sprintf(rfilename, "%s/%s", ast_config_AST_CONFIG_DIR, config);
04521    }
04522    ast_log(LOG_NOTICE, "AEL load process: calculated config file name '%s'.\n", rfilename);
04523 
04524    if (access(rfilename,R_OK) != 0) {
04525       ast_log(LOG_NOTICE, "File %s not found; AEL declining load\n", rfilename);
04526       return AST_MODULE_LOAD_DECLINE;
04527    }
04528    
04529    parse_tree = ael2_parse(rfilename, &errs);
04530    ast_log(LOG_NOTICE, "AEL load process: parsed config file name '%s'.\n", rfilename);
04531    ael2_semantic_check(parse_tree, &sem_err, &sem_warn, &sem_note);
04532    if (errs == 0 && sem_err == 0) {
04533       ast_log(LOG_NOTICE, "AEL load process: checked config file name '%s'.\n", rfilename);
04534       ast_compile_ael2(&local_contexts, parse_tree);
04535       ast_log(LOG_NOTICE, "AEL load process: compiled config file name '%s'.\n", rfilename);
04536       
04537       ast_merge_contexts_and_delete(&local_contexts, registrar);
04538       ast_log(LOG_NOTICE, "AEL load process: merged config file name '%s'.\n", rfilename);
04539       for (con = ast_walk_contexts(NULL); con; con = ast_walk_contexts(con))
04540          ast_context_verify_includes(con);
04541       ast_log(LOG_NOTICE, "AEL load process: verified config file name '%s'.\n", rfilename);
04542    } else {
04543       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);
04544       destroy_pval(parse_tree); /* free up the memory */
04545       return AST_MODULE_LOAD_DECLINE;
04546    }
04547    destroy_pval(parse_tree); /* free up the memory */
04548    
04549    return AST_MODULE_LOAD_SUCCESS;
04550 }
04551 
04552 /* CLI interface */
04553 static int ael2_debug_read(int fd, int argc, char *argv[])
04554 {
04555    aeldebug |= DEBUG_READ;
04556    return 0;
04557 }
04558 
04559 static int ael2_debug_tokens(int fd, int argc, char *argv[])
04560 {
04561    aeldebug |= DEBUG_TOKENS;
04562    return 0;
04563 }
04564 
04565 static int ael2_debug_macros(int fd, int argc, char *argv[])
04566 {
04567    aeldebug |= DEBUG_MACROS;
04568    return 0;
04569 }
04570 
04571 static int ael2_debug_contexts(int fd, int argc, char *argv[])
04572 {
04573    aeldebug |= DEBUG_CONTEXTS;
04574    return 0;
04575 }
04576 
04577 static int ael2_no_debug(int fd, int argc, char *argv[])
04578 {
04579    aeldebug = 0;
04580    return 0;
04581 }
04582 
04583 static int ael2_reload(int fd, int argc, char *argv[])
04584 {
04585    return (pbx_load_module());
04586 }
04587 
04588 static struct ast_cli_entry cli_ael_no_debug = {
04589    { "ael", "no", "debug", NULL },
04590    ael2_no_debug, NULL,
04591    NULL };
04592 
04593 static struct ast_cli_entry cli_ael[] = {
04594    { { "ael", "reload", NULL },
04595    ael2_reload, "Reload AEL configuration" },
04596 
04597    { { "ael", "debug", "read", NULL },
04598    ael2_debug_read, "Enable AEL read debug (does nothing)" },
04599 
04600    { { "ael", "debug", "tokens", NULL },
04601    ael2_debug_tokens, "Enable AEL tokens debug (does nothing)" },
04602 
04603    { { "ael", "debug", "macros", NULL },
04604    ael2_debug_macros, "Enable AEL macros debug (does nothing)" },
04605 
04606    { { "ael", "debug", "contexts", NULL },
04607    ael2_debug_contexts, "Enable AEL contexts debug (does nothing)" },
04608 
04609    { { "ael", "nodebug", NULL },
04610    ael2_no_debug, "Disable AEL debug messages",
04611    NULL, NULL, &cli_ael_no_debug },
04612 };
04613 
04614 static int unload_module(void)
04615 {
04616    ast_context_destroy(NULL, registrar);
04617    ast_cli_unregister_multiple(cli_ael, sizeof(cli_ael) / sizeof(struct ast_cli_entry));
04618    return 0;
04619 }
04620 
04621 static int load_module(void)
04622 {
04623    ast_cli_register_multiple(cli_ael, sizeof(cli_ael) / sizeof(struct ast_cli_entry));
04624    return (pbx_load_module());
04625 }
04626 
04627 static int reload(void)
04628 {
04629    return pbx_load_module();
04630 }
04631 
04632 #ifdef STANDALONE_AEL
04633 #define AST_MODULE "ael"
04634 int ael_external_load_module(void);
04635 int ael_external_load_module(void)
04636 {
04637    pbx_load_module();
04638    return 1;
04639 }
04640 #endif
04641 
04642 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Asterisk Extension Language Compiler",
04643       .load = load_module,
04644       .unload = unload_module,
04645       .reload = reload,
04646           );
04647 
04648 
04649 /* DESTROY the PVAL tree ============================================================================ */
04650 
04651 
04652 
04653 void destroy_pval_item(pval *item)
04654 {
04655    if (item == NULL) {
04656       ast_log(LOG_WARNING, "null item\n");
04657       return;
04658    }
04659 
04660    if (item->filename)
04661       free(item->filename);
04662    
04663    switch (item->type) {
04664    case PV_WORD:
04665       /* fields: item->u1.str == string associated with this (word). */
04666       if (item->u1.str )
04667          free(item->u1.str);
04668       if ( item->u2.arglist )
04669          destroy_pval(item->u2.arglist);
04670       break;
04671       
04672    case PV_MACRO:
04673       /* fields: item->u1.str     == name of macro
04674                  item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
04675                item->u2.arglist->u1.str  == argument
04676                item->u2.arglist->next   == next arg
04677 
04678                item->u3.macro_statements == pval list of statements in macro body.
04679       */
04680       destroy_pval(item->u2.arglist);
04681       if (item->u1.str )
04682          free(item->u1.str);
04683       destroy_pval(item->u3.macro_statements);
04684       break;
04685          
04686    case PV_CONTEXT:
04687       /* fields: item->u1.str     == name of context
04688                  item->u2.statements == pval list of statements in context body
04689                item->u3.abstract == int 1 if an abstract keyword were present
04690       */
04691       if (item->u1.str)
04692          free(item->u1.str);
04693       destroy_pval(item->u2.statements);
04694       break;
04695          
04696    case PV_MACRO_CALL:
04697       /* fields: item->u1.str     == name of macro to call
04698                  item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
04699                item->u2.arglist->u1.str  == argument
04700                item->u2.arglist->next   == next arg
04701       */
04702       if (item->u1.str)
04703          free(item->u1.str);
04704       destroy_pval(item->u2.arglist);
04705       break;
04706          
04707    case PV_APPLICATION_CALL:
04708       /* fields: item->u1.str     == name of application to call
04709                  item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
04710                item->u2.arglist->u1.str  == argument
04711                item->u2.arglist->next   == next arg
04712       */
04713       if (item->u1.str)
04714          free(item->u1.str);
04715       destroy_pval(item->u2.arglist);
04716       break;
04717          
04718    case PV_CASE:
04719       /* fields: item->u1.str     == value of case
04720                  item->u2.statements == pval list of statements under the case
04721       */
04722       if (item->u1.str)
04723          free(item->u1.str);
04724       destroy_pval(item->u2.statements);
04725       break;
04726          
04727    case PV_PATTERN:
04728       /* fields: item->u1.str     == value of case
04729                  item->u2.statements == pval list of statements under the case
04730       */
04731       if (item->u1.str)
04732          free(item->u1.str);
04733       destroy_pval(item->u2.statements);
04734       break;
04735          
04736    case PV_DEFAULT:
04737       /* fields: 
04738                  item->u2.statements == pval list of statements under the case
04739       */
04740       destroy_pval(item->u2.statements);
04741       break;
04742          
04743    case PV_CATCH:
04744       /* fields: item->u1.str     == name of extension to catch
04745                  item->u2.statements == pval list of statements in context body
04746       */
04747       if (item->u1.str)
04748          free(item->u1.str);
04749       destroy_pval(item->u2.statements);
04750       break;
04751          
04752    case PV_SWITCHES:
04753       /* fields: item->u1.list     == pval list of PV_WORD elements, one per entry in the list
04754       */
04755       destroy_pval(item->u1.list);
04756       break;
04757          
04758    case PV_ESWITCHES:
04759       /* fields: item->u1.list     == pval list of PV_WORD elements, one per entry in the list
04760       */
04761       destroy_pval(item->u1.list);
04762       break;
04763          
04764    case PV_INCLUDES:
04765       /* fields: item->u1.list     == pval list of PV_WORD elements, one per entry in the list
04766                  item->u2.arglist  == pval list of 4 PV_WORD elements for time values
04767       */
04768       destroy_pval(item->u1.list);
04769       break;
04770          
04771    case PV_STATEMENTBLOCK:
04772       /* fields: item->u1.list     == pval list of statements in block, one per entry in the list
04773       */
04774       destroy_pval(item->u1.list);
04775       break;
04776          
04777    case PV_VARDEC:
04778       /* fields: item->u1.str     == variable name
04779                  item->u2.val     == variable value to assign
04780       */
04781       if (item->u1.str)
04782          free(item->u1.str);
04783       if (item->u2.val)
04784          free(item->u2.val);
04785       break;
04786          
04787    case PV_GOTO:
04788       /* fields: item->u1.list     == pval list of PV_WORD target names, up to 3, in order as given by user.
04789                  item->u1.list->u1.str  == where the data on a PV_WORD will always be.
04790       */
04791       
04792       destroy_pval(item->u1.list);
04793       break;
04794          
04795    case PV_LABEL:
04796       /* fields: item->u1.str     == label name
04797       */
04798       if (item->u1.str)
04799          free(item->u1.str);
04800       break;
04801          
04802    case PV_FOR:
04803       /* fields: item->u1.for_init     == a string containing the initalizer
04804                  item->u2.for_test     == a string containing the loop test
04805                  item->u3.for_inc      == a string containing the loop increment
04806 
04807                item->u4.for_statements == a pval list of statements in the for ()
04808       */
04809       if (item->u1.for_init)
04810          free(item->u1.for_init);
04811       if (item->u2.for_test)
04812          free(item->u2.for_test);
04813       if (item->u3.for_inc)
04814          free(item->u3.for_inc);
04815       destroy_pval(item->u4.for_statements);
04816       break;
04817          
04818    case PV_WHILE:
04819       /* fields: item->u1.str        == the while conditional, as supplied by user
04820 
04821                item->u2.statements == a pval list of statements in the while ()
04822       */
04823       if (item->u1.str)
04824          free(item->u1.str);
04825       destroy_pval(item->u2.statements);
04826       break;
04827          
04828    case PV_BREAK:
04829       /* fields: none
04830       */
04831       break;
04832          
04833    case PV_RETURN:
04834       /* fields: none
04835       */
04836       break;
04837          
04838    case PV_CONTINUE:
04839       /* fields: none
04840       */
04841       break;
04842          
04843    case PV_IFTIME:
04844       /* fields: item->u1.list        == the 4 time values, in PV_WORD structs, linked list
04845 
04846                item->u2.statements == a pval list of statements in the if ()
04847                item->u3.else_statements == a pval list of statements in the else
04848                                     (could be zero)
04849       */
04850       destroy_pval(item->u1.list);
04851       destroy_pval(item->u2.statements);
04852       if (item->u3.else_statements) {
04853          destroy_pval(item->u3.else_statements);
04854       }
04855       break;
04856          
04857    case PV_RANDOM:
04858       /* fields: item->u1.str        == the random percentage, as supplied by user
04859 
04860                item->u2.statements == a pval list of statements in the true part ()
04861                item->u3.else_statements == a pval list of statements in the else
04862                                     (could be zero)
04863       fall thru to If */
04864    case PV_IF:
04865       /* fields: item->u1.str        == the if conditional, as supplied by user
04866 
04867                item->u2.statements == a pval list of statements in the if ()
04868                item->u3.else_statements == a pval list of statements in the else
04869                                     (could be zero)
04870       */
04871       if (item->u1.str)
04872          free(item->u1.str);
04873       destroy_pval(item->u2.statements);
04874       if (item->u3.else_statements) {
04875          destroy_pval(item->u3.else_statements);
04876       }
04877       break;
04878          
04879    case PV_SWITCH:
04880       /* fields: item->u1.str        == the switch expression
04881 
04882                item->u2.statements == a pval list of statements in the switch, 
04883                                     (will be case statements, most likely!)
04884       */
04885       if (item->u1.str)
04886          free(item->u1.str);
04887       destroy_pval(item->u2.statements);
04888       break;
04889          
04890    case PV_EXTENSION:
04891       /* fields: item->u1.str        == the extension name, label, whatever it's called
04892 
04893                item->u2.statements == a pval list of statements in the extension
04894                item->u3.hints      == a char * hint argument
04895                item->u4.regexten   == an int boolean. non-zero says that regexten was specified
04896       */
04897       if (item->u1.str)
04898          free(item->u1.str);
04899       if (item->u3.hints)
04900          free(item->u3.hints);
04901       destroy_pval(item->u2.statements);
04902       break;
04903          
04904    case PV_IGNOREPAT:
04905       /* fields: item->u1.str        == the ignorepat data
04906       */
04907       if (item->u1.str)
04908          free(item->u1.str);
04909       break;
04910          
04911    case PV_GLOBALS:
04912       /* fields: item->u1.statements     == pval list of statements, usually vardecs
04913       */
04914       destroy_pval(item->u1.statements);
04915       break;
04916    }
04917    free(item);
04918 }
04919 
04920 void destroy_pval(pval *item) 
04921 {
04922    pval *i,*nxt;
04923    
04924    for (i=item; i; i=nxt) {
04925       nxt = i->next;
04926       
04927       destroy_pval_item(i);
04928    }
04929 }
04930 
04931 #ifdef AAL_ARGCHECK
04932 static char *ael_funclist[] =
04933 {
04934    "AGENT",
04935    "ARRAY",
04936    "BASE64_DECODE",
04937    "BASE64_ENCODE",
04938    "CALLERID",
04939    "CDR",
04940    "CHANNEL",
04941    "CHECKSIPDOMAIN",
04942    "CHECK_MD5",
04943    "CURL",
04944    "CUT",
04945    "DB",
04946    "DB_EXISTS",
04947    "DUNDILOOKUP",
04948    "ENUMLOOKUP",
04949    "ENV",
04950    "EVAL",
04951    "EXISTS",
04952    "FIELDQTY",
04953    "FILTER",
04954    "GROUP",
04955    "GROUP_COUNT",
04956    "GROUP_LIST",
04957    "GROUP_MATCH_COUNT",
04958    "IAXPEER",
04959    "IF",
04960    "IFTIME",
04961    "ISNULL",
04962    "KEYPADHASH",
04963    "LANGUAGE",
04964    "LEN",
04965    "MATH",
04966    "MD5",
04967    "MUSICCLASS",
04968    "QUEUEAGENTCOUNT",
04969    "QUEUE_MEMBER_COUNT",
04970    "QUEUE_MEMBER_LIST",
04971    "QUOTE",
04972    "RAND",
04973    "REGEX",
04974    "SET",
04975    "SHA1",
04976    "SIPCHANINFO",
04977    "SIPPEER",
04978    "SIP_HEADER",
04979    "SORT",
04980    "STAT",
04981    "STRFTIME",
04982    "STRPTIME",
04983    "TIMEOUT",
04984    "TXTCIDNAME",
04985    "URIDECODE",
04986    "URIENCODE",
04987    "VMCOUNT"
04988 };
04989 
04990 
04991 int ael_is_funcname(char *name)
04992 {
04993    int s,t;
04994    t = sizeof(ael_funclist)/sizeof(char*);
04995    s = 0;
04996    while ((s < t) && strcasecmp(name, ael_funclist[s])) 
04997       s++;
04998    if ( s < t )
04999       return 1;
05000    else
05001       return 0;
05002 }
05003 #endif    

Generated on Mon Nov 24 15:34:19 2008 for Asterisk - the Open Source PBX by  doxygen 1.4.7