Mon May 14 04:42:59 2007

Asterisk developer's documentation


pbx_ael.c

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

Generated on Mon May 14 04:43:00 2007 for Asterisk - the Open Source PBX by  doxygen 1.5.1