Sat Aug 6 00:39:30 2011

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

Generated on Sat Aug 6 00:39:30 2011 for Asterisk - the Open Source PBX by  doxygen 1.4.7