Codename Pineapple

Home page | Mailing list | Docs

Last updated: Sat Feb 3 05:00:51 2007

Asterisk developer's documentation :: Codename Pineapple


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: 51436 $")
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 static int check_break(pval *item);
00135 static int check_continue(pval *item);
00136 static void check_label(pval *item);
00137 static void check_macro_returns(pval *macro);
00138 
00139 static struct pval *find_label_in_current_context(char *exten, char *label, pval *curr_cont);
00140 static struct pval *find_first_label_in_current_context(char *label, pval *curr_cont);
00141 static void print_pval_list(FILE *fin, pval *item, int depth);
00142 
00143 static struct pval *find_label_in_current_extension(const char *label, pval *curr_ext);
00144 static struct pval *find_label_in_current_db(const char *context, const char *exten, const char *label);
00145 static pval *get_goto_target(pval *item);
00146 static int label_inside_case(pval *label);
00147 static void attach_exten(struct ael_extension **list, struct ael_extension *newmem);
00148 static void fix_gotos_in_extensions(struct ael_extension *exten);
00149 static pval *get_extension_or_contxt(pval *p);
00150 static pval *get_contxt(pval *p);
00151 static void remove_spaces_before_equals(char *str);
00152 static void substitute_commas(char *str);
00153 
00154 /* I am adding this code to substitute commas with vertbars in the args to apps */
00155 static void substitute_commas(char *str)
00156 {
00157    char *p = str;
00158    while (p && *p)
00159    {
00160       if (*p == ',' && ((p != str && *(p-1) != '\\')
00161             || p == str))
00162          *p = '|';
00163       p++;
00164    }
00165 }
00166 
00167 
00168 /* PRETTY PRINTER FOR AEL:  ============================================================================= */
00169 
00170 static void print_pval(FILE *fin, pval *item, int depth)
00171 {
00172    int i;
00173    pval *lp;
00174    
00175    for (i=0; i<depth; i++) {
00176       fprintf(fin, "\t"); /* depth == indentation */
00177    }
00178    
00179    switch ( item->type ) {
00180    case PV_WORD:
00181       fprintf(fin,"%s;\n", item->u1.str); /* usually, words are encapsulated in something else */
00182       break;
00183       
00184    case PV_MACRO:
00185       fprintf(fin,"macro %s(", item->u1.str);
00186       for (lp=item->u2.arglist; lp; lp=lp->next) {
00187          if (lp != item->u2.arglist )
00188             fprintf(fin,", ");
00189          fprintf(fin,"%s", lp->u1.str);
00190       }
00191       fprintf(fin,") {\n");
00192       print_pval_list(fin,item->u3.macro_statements,depth+1);
00193       for (i=0; i<depth; i++) {
00194          fprintf(fin,"\t"); /* depth == indentation */
00195       }
00196       fprintf(fin,"};\n\n");
00197       break;
00198          
00199    case PV_CONTEXT:
00200       if ( item->u3.abstract )
00201          fprintf(fin,"abstract context %s {\n", item->u1.str);
00202       else
00203          fprintf(fin,"context %s {\n", item->u1.str);
00204       print_pval_list(fin,item->u2.statements,depth+1);
00205       for (i=0; i<depth; i++) {
00206          fprintf(fin,"\t"); /* depth == indentation */
00207       }
00208       fprintf(fin,"};\n\n");
00209       break;
00210          
00211    case PV_MACRO_CALL:
00212       fprintf(fin,"&%s(", item->u1.str);
00213       for (lp=item->u2.arglist; lp; lp=lp->next) {
00214          if ( lp != item->u2.arglist )
00215             fprintf(fin,", ");
00216          fprintf(fin,"%s", lp->u1.str);
00217       }
00218       fprintf(fin,");\n");
00219       break;
00220          
00221    case PV_APPLICATION_CALL:
00222       fprintf(fin,"%s(", item->u1.str);
00223       for (lp=item->u2.arglist; lp; lp=lp->next) {
00224          if ( lp != item->u2.arglist )
00225             fprintf(fin,",");
00226          fprintf(fin,"%s", lp->u1.str);
00227       }
00228       fprintf(fin,");\n");
00229       break;
00230          
00231    case PV_CASE:
00232       fprintf(fin,"case %s:\n", item->u1.str);
00233       print_pval_list(fin,item->u2.statements, depth+1);
00234       break;
00235          
00236    case PV_PATTERN:
00237       fprintf(fin,"pattern %s:\n", item->u1.str);
00238       print_pval_list(fin,item->u2.statements, depth+1);
00239       break;
00240          
00241    case PV_DEFAULT:
00242       fprintf(fin,"default:\n");
00243       print_pval_list(fin,item->u2.statements, depth+1);
00244       break;
00245          
00246    case PV_CATCH:
00247       fprintf(fin,"catch %s {\n", item->u1.str);
00248       print_pval_list(fin,item->u2.statements, depth+1);
00249       for (i=0; i<depth; i++) {
00250          fprintf(fin,"\t"); /* depth == indentation */
00251       }
00252       fprintf(fin,"};\n");
00253       break;
00254          
00255    case PV_SWITCHES:
00256       fprintf(fin,"switches {\n");
00257       print_pval_list(fin,item->u1.list,depth+1);
00258       for (i=0; i<depth; i++) {
00259          fprintf(fin,"\t"); /* depth == indentation */
00260       }
00261       fprintf(fin,"};\n");
00262       break;
00263          
00264    case PV_ESWITCHES:
00265       fprintf(fin,"eswitches {\n");
00266       print_pval_list(fin,item->u1.list,depth+1);
00267       for (i=0; i<depth; i++) {
00268          fprintf(fin,"\t"); /* depth == indentation */
00269       }
00270       fprintf(fin,"};\n");
00271       break;
00272          
00273    case PV_INCLUDES:
00274       fprintf(fin,"includes {\n");
00275       for (lp=item->u1.list; lp; lp=lp->next) {
00276          for (i=0; i<depth+1; i++) {
00277             fprintf(fin,"\t"); /* depth == indentation */
00278          }
00279          fprintf(fin,"%s", lp->u1.str); /* usually, words are encapsulated in something else */
00280          if ( lp->u2.arglist )
00281             fprintf(fin,"|%s|%s|%s|%s", 
00282                   lp->u2.arglist->u1.str,
00283                   lp->u2.arglist->next->u1.str,
00284                   lp->u2.arglist->next->next->u1.str,
00285                   lp->u2.arglist->next->next->next->u1.str
00286                );
00287          fprintf(fin,";\n"); /* usually, words are encapsulated in something else */
00288       }
00289       
00290       print_pval_list(fin,item->u1.list,depth+1);
00291       for (i=0; i<depth; i++) {
00292          fprintf(fin,"\t"); /* depth == indentation */
00293       }
00294       fprintf(fin,"};\n");
00295       break;
00296          
00297    case PV_STATEMENTBLOCK:
00298       fprintf(fin,"{\n");
00299       print_pval_list(fin,item->u1.list, depth+1);
00300       for (i=0; i<depth; i++) {
00301          fprintf(fin,"\t"); /* depth == indentation */
00302       }
00303       fprintf(fin,"};\n");
00304       break;
00305          
00306    case PV_VARDEC:
00307       fprintf(fin,"%s=%s;\n", item->u1.str, item->u2.val);
00308       break;
00309          
00310    case PV_GOTO:
00311       fprintf(fin,"goto %s", item->u1.list->u1.str);
00312       if ( item->u1.list->next )
00313          fprintf(fin,"|%s", item->u1.list->next->u1.str);
00314       if ( item->u1.list->next && item->u1.list->next->next )
00315          fprintf(fin,"|%s", item->u1.list->next->next->u1.str);
00316       fprintf(fin,"\n");
00317       break;
00318          
00319    case PV_LABEL:
00320       fprintf(fin,"%s:\n", item->u1.str);
00321       break;
00322          
00323    case PV_FOR:
00324       fprintf(fin,"for (%s; %s; %s)\n", item->u1.for_init, item->u2.for_test, item->u3.for_inc);
00325       print_pval_list(fin,item->u4.for_statements,depth+1);
00326       break;
00327          
00328    case PV_WHILE:
00329       fprintf(fin,"while (%s)\n", item->u1.str);
00330       print_pval_list(fin,item->u2.statements,depth+1);
00331       break;
00332          
00333    case PV_BREAK:
00334       fprintf(fin,"break;\n");
00335       break;
00336          
00337    case PV_RETURN:
00338       fprintf(fin,"return;\n");
00339       break;
00340          
00341    case PV_CONTINUE:
00342       fprintf(fin,"continue;\n");
00343       break;
00344          
00345    case PV_RANDOM:
00346    case PV_IFTIME:
00347    case PV_IF:
00348       if ( item->type == PV_IFTIME ) {
00349          
00350          fprintf(fin,"ifTime ( %s|%s|%s|%s )\n", 
00351                item->u1.list->u1.str, 
00352                item->u1.list->next->u1.str, 
00353                item->u1.list->next->next->u1.str, 
00354                item->u1.list->next->next->next->u1.str
00355                );
00356       } else if ( item->type == PV_RANDOM ) {
00357          fprintf(fin,"random ( %s )\n", item->u1.str );
00358       } else
00359          fprintf(fin,"if ( %s )\n", item->u1.str);
00360       if ( item->u2.statements && item->u2.statements->next ) {
00361          for (i=0; i<depth; i++) {
00362             fprintf(fin,"\t"); /* depth == indentation */
00363          }
00364          fprintf(fin,"{\n");
00365          print_pval_list(fin,item->u2.statements,depth+1);
00366          for (i=0; i<depth; i++) {
00367             fprintf(fin,"\t"); /* depth == indentation */
00368          }
00369          if ( item->u3.else_statements )
00370             fprintf(fin,"}\n");
00371          else
00372             fprintf(fin,"};\n");
00373       } else if (item->u2.statements ) {
00374          print_pval_list(fin,item->u2.statements,depth+1);
00375       } else {
00376          if (item->u3.else_statements )
00377             fprintf(fin, " {} ");
00378          else
00379             fprintf(fin, " {}; ");
00380       }
00381       if ( item->u3.else_statements ) {
00382          for (i=0; i<depth; i++) {
00383             fprintf(fin,"\t"); /* depth == indentation */
00384          }
00385          fprintf(fin,"else\n");
00386          print_pval_list(fin,item->u3.else_statements, depth);
00387       }
00388       break;
00389          
00390    case PV_SWITCH:
00391       fprintf(fin,"switch( %s ) {\n", item->u1.str);
00392       print_pval_list(fin,item->u2.statements,depth+1);
00393       for (i=0; i<depth; i++) {
00394          fprintf(fin,"\t"); /* depth == indentation */
00395       }
00396       fprintf(fin,"}\n");
00397       break;
00398          
00399    case PV_EXTENSION:
00400       if ( item->u4.regexten )
00401          fprintf(fin, "regexten ");
00402       if ( item->u3.hints )
00403          fprintf(fin,"hints(%s) ", item->u3.hints);
00404       
00405       fprintf(fin,"%s => \n", item->u1.str);
00406       print_pval_list(fin,item->u2.statements,depth+1);
00407       break;
00408          
00409    case PV_IGNOREPAT:
00410       fprintf(fin,"ignorepat => %s\n", item->u1.str);
00411       break;
00412          
00413    case PV_GLOBALS:
00414       fprintf(fin,"globals {\n");
00415       print_pval_list(fin,item->u1.statements,depth+1);
00416       for (i=0; i<depth; i++) {
00417          fprintf(fin,"\t"); /* depth == indentation */
00418       }
00419       fprintf(fin,"}\n");
00420       break;
00421    }
00422 }
00423 
00424 static void print_pval_list(FILE *fin, pval *item, int depth)
00425 {
00426    pval *i;
00427    
00428    for (i=item; i; i=i->next) {
00429       print_pval(fin, i, depth);
00430    }
00431 }
00432 
00433 #if 0
00434 static void ael2_print(char *fname, pval *tree)
00435 {
00436    FILE *fin = fopen(fname,"w");
00437    if ( !fin ) {
00438       ast_log(LOG_ERROR, "Couldn't open %s for writing.\n", fname);
00439       return;
00440    }
00441    print_pval_list(fin, tree, 0);
00442    fclose(fin);
00443 }
00444 #endif
00445 
00446 
00447 /* EMPTY TEMPLATE FUNCS FOR AEL TRAVERSAL:  ============================================================================= */
00448 
00449 void traverse_pval_template(pval *item, int depth);
00450 void traverse_pval_item_template(pval *item, int depth);
00451 
00452 
00453 void traverse_pval_item_template(pval *item, int depth)/* depth comes in handy for a pretty print (indentation),
00454                                             but you may not need it */
00455 {
00456    pval *lp;
00457    
00458    switch ( item->type ) {
00459    case PV_WORD:
00460       /* fields: item->u1.str == string associated with this (word). */
00461       break;
00462       
00463    case PV_MACRO:
00464       /* fields: item->u1.str     == name of macro
00465                  item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
00466                item->u2.arglist->u1.str  == argument
00467                item->u2.arglist->next   == next arg
00468 
00469                item->u3.macro_statements == pval list of statements in macro body.
00470       */
00471       for (lp=item->u2.arglist; lp; lp=lp->next) {
00472       
00473       }
00474       traverse_pval_item_template(item->u3.macro_statements,depth+1);
00475       break;
00476          
00477    case PV_CONTEXT:
00478       /* fields: item->u1.str     == name of context
00479                  item->u2.statements == pval list of statements in context body
00480                item->u3.abstract == int 1 if an abstract keyword were present
00481       */
00482       traverse_pval_item_template(item->u2.statements,depth+1);
00483       break;
00484          
00485    case PV_MACRO_CALL:
00486       /* fields: item->u1.str     == name of macro to call
00487                  item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
00488                item->u2.arglist->u1.str  == argument
00489                item->u2.arglist->next   == next arg
00490       */
00491       for (lp=item->u2.arglist; lp; lp=lp->next) {
00492       }
00493       break;
00494          
00495    case PV_APPLICATION_CALL:
00496       /* fields: item->u1.str     == name of application to call
00497                  item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
00498                item->u2.arglist->u1.str  == argument
00499                item->u2.arglist->next   == next arg
00500       */
00501       for (lp=item->u2.arglist; lp; lp=lp->next) {
00502       }
00503       break;
00504          
00505    case PV_CASE:
00506       /* fields: item->u1.str     == value of case
00507                  item->u2.statements == pval list of statements under the case
00508       */
00509       traverse_pval_item_template(item->u2.statements,depth+1);
00510       break;
00511          
00512    case PV_PATTERN:
00513       /* fields: item->u1.str     == value of case
00514                  item->u2.statements == pval list of statements under the case
00515       */
00516       traverse_pval_item_template(item->u2.statements,depth+1);
00517       break;
00518          
00519    case PV_DEFAULT:
00520       /* fields: 
00521                  item->u2.statements == pval list of statements under the case
00522       */
00523       traverse_pval_item_template(item->u2.statements,depth+1);
00524       break;
00525          
00526    case PV_CATCH:
00527       /* fields: item->u1.str     == name of extension to catch
00528                  item->u2.statements == pval list of statements in context body
00529       */
00530       traverse_pval_item_template(item->u2.statements,depth+1);
00531       break;
00532          
00533    case PV_SWITCHES:
00534       /* fields: item->u1.list     == pval list of PV_WORD elements, one per entry in the list
00535       */
00536       traverse_pval_item_template(item->u1.list,depth+1);
00537       break;
00538          
00539    case PV_ESWITCHES:
00540       /* fields: item->u1.list     == pval list of PV_WORD elements, one per entry in the list
00541       */
00542       traverse_pval_item_template(item->u1.list,depth+1);
00543       break;
00544          
00545    case PV_INCLUDES:
00546       /* fields: item->u1.list     == pval list of PV_WORD elements, one per entry in the list
00547                  item->u2.arglist  == pval list of 4 PV_WORD elements for time values
00548       */
00549       traverse_pval_item_template(item->u1.list,depth+1);
00550       traverse_pval_item_template(item->u2.arglist,depth+1);
00551       break;
00552          
00553    case PV_STATEMENTBLOCK:
00554       /* fields: item->u1.list     == pval list of statements in block, one per entry in the list
00555       */
00556       traverse_pval_item_template(item->u1.list,depth+1);
00557       break;
00558          
00559    case PV_VARDEC:
00560       /* fields: item->u1.str     == variable name
00561                  item->u2.val     == variable value to assign
00562       */
00563       break;
00564          
00565    case PV_GOTO:
00566       /* fields: item->u1.list     == pval list of PV_WORD target names, up to 3, in order as given by user.
00567                  item->u1.list->u1.str  == where the data on a PV_WORD will always be.
00568       */
00569       
00570       if ( item->u1.list->next )
00571          ;
00572       if ( item->u1.list->next && item->u1.list->next->next )
00573          ;
00574       
00575       break;
00576          
00577    case PV_LABEL:
00578       /* fields: item->u1.str     == label name
00579       */
00580       break;
00581          
00582    case PV_FOR:
00583       /* fields: item->u1.for_init     == a string containing the initalizer
00584                  item->u2.for_test     == a string containing the loop test
00585                  item->u3.for_inc      == a string containing the loop increment
00586 
00587                item->u4.for_statements == a pval list of statements in the for ()
00588       */
00589       traverse_pval_item_template(item->u4.for_statements,depth+1);
00590       break;
00591          
00592    case PV_WHILE:
00593       /* fields: item->u1.str        == the while conditional, as supplied by user
00594 
00595                item->u2.statements == a pval list of statements in the while ()
00596       */
00597       traverse_pval_item_template(item->u2.statements,depth+1);
00598       break;
00599          
00600    case PV_BREAK:
00601       /* fields: none
00602       */
00603       break;
00604          
00605    case PV_RETURN:
00606       /* fields: none
00607       */
00608       break;
00609          
00610    case PV_CONTINUE:
00611       /* fields: none
00612       */
00613       break;
00614          
00615    case PV_IFTIME:
00616       /* fields: item->u1.list        == there are 4 linked PV_WORDs here.
00617 
00618                item->u2.statements == a pval list of statements in the if ()
00619                item->u3.else_statements == a pval list of statements in the else
00620                                     (could be zero)
00621       */
00622       traverse_pval_item_template(item->u2.statements,depth+1);
00623       if ( item->u3.else_statements ) {
00624          traverse_pval_item_template(item->u3.else_statements,depth+1);
00625       }
00626       break;
00627          
00628    case PV_RANDOM:
00629       /* fields: item->u1.str        == the random number expression, as supplied by user
00630 
00631                item->u2.statements == a pval list of statements in the if ()
00632                item->u3.else_statements == a pval list of statements in the else
00633                                     (could be zero)
00634       */
00635       traverse_pval_item_template(item->u2.statements,depth+1);
00636       if ( item->u3.else_statements ) {
00637          traverse_pval_item_template(item->u3.else_statements,depth+1);
00638       }
00639       break;
00640          
00641    case PV_IF:
00642       /* fields: item->u1.str        == the if conditional, as supplied by user
00643 
00644                item->u2.statements == a pval list of statements in the if ()
00645                item->u3.else_statements == a pval list of statements in the else
00646                                     (could be zero)
00647       */
00648       traverse_pval_item_template(item->u2.statements,depth+1);
00649       if ( item->u3.else_statements ) {
00650          traverse_pval_item_template(item->u3.else_statements,depth+1);
00651       }
00652       break;
00653          
00654    case PV_SWITCH:
00655       /* fields: item->u1.str        == the switch expression
00656 
00657                item->u2.statements == a pval list of statements in the switch, 
00658                                     (will be case statements, most likely!)
00659       */
00660       traverse_pval_item_template(item->u2.statements,depth+1);
00661       break;
00662          
00663    case PV_EXTENSION:
00664       /* fields: item->u1.str        == the extension name, label, whatever it's called
00665 
00666                item->u2.statements == a pval list of statements in the extension
00667                item->u3.hints      == a char * hint argument
00668                item->u4.regexten   == an int boolean. non-zero says that regexten was specified
00669       */
00670       traverse_pval_item_template(item->u2.statements,depth+1);
00671       break;
00672          
00673    case PV_IGNOREPAT:
00674       /* fields: item->u1.str        == the ignorepat data
00675       */
00676       break;
00677          
00678    case PV_GLOBALS:
00679       /* fields: item->u1.statements     == pval list of statements, usually vardecs
00680       */
00681       traverse_pval_item_template(item->u1.statements,depth+1);
00682       break;
00683    }
00684 }
00685 
00686 void traverse_pval_template(pval *item, int depth) /* depth comes in handy for a pretty print (indentation),
00687                                          but you may not need it */
00688 {
00689    pval *i;
00690    
00691    for (i=item; i; i=i->next) {
00692       traverse_pval_item_template(i, depth);
00693    }
00694 }
00695 
00696 
00697 /* SEMANTIC CHECKING FOR AEL:  ============================================================================= */
00698 
00699 /*   (not all that is syntactically legal is good! */
00700 
00701 
00702 static void check_macro_returns(pval *macro)
00703 {
00704    pval *i;
00705    if (!macro->u3.macro_statements)
00706    {
00707       pval *z = calloc(1, sizeof(struct pval));
00708       ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The macro %s is empty! I will insert a return.\n",
00709             macro->filename, macro->startline, macro->endline, macro->u1.str);
00710 
00711       z->type = PV_RETURN;
00712       z->startline = macro->startline;
00713       z->endline = macro->endline;
00714       z->startcol = macro->startcol;
00715       z->endcol = macro->endcol;
00716       z->filename = strdup(macro->filename);
00717 
00718       macro->u3.macro_statements = z;
00719       return;
00720    }
00721    for (i=macro->u3.macro_statements; i; i=i->next) {
00722       /* if the last statement in the list is not return, then insert a return there */
00723       if (i->next == NULL) {
00724          if (i->type != PV_RETURN) {
00725             pval *z = calloc(1, sizeof(struct pval));
00726             ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The macro %s does not end with a return; I will insert one.\n",
00727                   macro->filename, macro->startline, macro->endline, macro->u1.str);
00728 
00729             z->type = PV_RETURN;
00730             z->startline = macro->startline;
00731             z->endline = macro->endline;
00732             z->startcol = macro->startcol;
00733             z->endcol = macro->endcol;
00734             z->filename = strdup(macro->filename);
00735 
00736             i->next = z;
00737             return;
00738          }
00739       }
00740    }
00741    return;
00742 }
00743 
00744 
00745 
00746 static int extension_matches(pval *here, const char *exten, const char *pattern)
00747 {
00748    int err1;
00749    regex_t preg;
00750    
00751    /* simple case, they match exactly, the pattern and exten name */
00752    if (!strcmp(pattern,exten) == 0)
00753       return 1;
00754    
00755    if (pattern[0] == '_') {
00756       char reg1[2000];
00757       const char *p;
00758       char *r = reg1;
00759       
00760       if ( strlen(pattern)*5 >= 2000 ) /* safety valve */ {
00761          ast_log(LOG_ERROR,"Error: The pattern %s is way too big. Pattern matching cancelled.\n",
00762                pattern);
00763          return 0;
00764       }
00765       /* form a regular expression from the pattern, and then match it against exten */
00766       *r++ = '^'; /* what if the extension is a pattern ?? */
00767       *r++ = '_'; /* what if the extension is a pattern ?? */
00768       *r++ = '?';
00769       for (p=pattern+1; *p; p++) {
00770          switch ( *p ) {
00771          case 'X':
00772             *r++ = '[';
00773             *r++ = '0';
00774             *r++ = '-';
00775             *r++ = '9';
00776             *r++ = 'X';
00777             *r++ = ']';
00778             break;
00779             
00780          case 'Z':
00781             *r++ = '[';
00782             *r++ = '1';
00783             *r++ = '-';
00784             *r++ = '9';
00785             *r++ = 'Z';
00786             *r++ = ']';
00787             break;
00788             
00789          case 'N':
00790             *r++ = '[';
00791             *r++ = '2';
00792             *r++ = '-';
00793             *r++ = '9';
00794             *r++ = 'N';
00795             *r++ = ']';
00796             break;
00797             
00798          case '[':
00799             while ( *p && *p != ']' ) {
00800                *r++ = *p++;
00801             }
00802             if ( *p != ']') {
00803                ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The extension pattern '%s' is missing a closing bracket \n",
00804                      here->filename, here->startline, here->endline, pattern);
00805             }
00806             break;
00807             
00808          case '.':
00809          case '!':
00810             *r++ = '.';
00811             *r++ = '*';
00812             break;
00813          case '*':
00814             *r++ = '\\';
00815             *r++ = '*';
00816             break;
00817          default:
00818             *r++ = *p;
00819             break;
00820             
00821          }
00822       }
00823       *r++ = '$'; /* what if the extension is a pattern ?? */
00824       *r++ = *p++; /* put in the closing null */
00825       err1 = regcomp(&preg, reg1, REG_NOSUB|REG_EXTENDED);
00826       if ( err1 ) {
00827          char errmess[500];
00828          regerror(err1,&preg,errmess,sizeof(errmess));
00829          regfree(&preg);
00830          ast_log(LOG_WARNING, "Regcomp of %s failed, error code %d\n",
00831                reg1, err1);
00832          return 0;
00833       }
00834       err1 = regexec(&preg, exten, 0, 0, 0);
00835       regfree(&preg);
00836       
00837       if ( err1 ) {
00838          /* ast_log(LOG_NOTICE,"*****************************[%d]Extension %s did not match %s(%s)\n",
00839             err1,exten, pattern, reg1); */
00840          return 0; /* no match */
00841       } else {
00842          /* ast_log(LOG_NOTICE,"*****************************Extension %s matched %s\n",
00843             exten, pattern); */
00844          return 1;
00845       }
00846       
00847       
00848    } else {
00849       if ( strcmp(exten,pattern) == 0 ) {
00850          return 1;
00851       } else
00852          return 0;
00853    }
00854 }
00855 
00856 
00857 static void check_expr2_input(pval *expr, char *str)
00858 {
00859    int spaces = strspn(str,"\t \n");
00860    if ( !strncmp(str+spaces,"$[",2) ) {
00861       ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The expression '%s' is redundantly wrapped in '$[ ]'. \n",
00862             expr->filename, expr->startline, expr->endline, str);
00863       warns++;
00864    }
00865 }
00866 
00867 static void check_includes(pval *includes)
00868 {
00869    struct pval *p4;
00870    for (p4=includes->u1.list; p4; p4=p4->next) {
00871       /* for each context pointed to, find it, then find a context/label that matches the
00872          target here! */
00873       char *incl_context = p4->u1.str;
00874       /* find a matching context name */
00875       struct pval *that_other_context = find_context(incl_context);
00876       if (!that_other_context && strcmp(incl_context, "parkedcalls") != 0) {
00877          ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The included context '%s' cannot be found.\n",
00878                includes->filename, includes->startline, includes->endline, incl_context);
00879          warns++;
00880       }
00881    }
00882 }
00883 
00884 
00885 static void check_timerange(pval *p)
00886 {
00887    char *times;
00888    char *e;
00889    int s1, s2;
00890    int e1, e2;
00891 
00892    times = ast_strdupa(p->u1.str);
00893 
00894    /* Star is all times */
00895    if (ast_strlen_zero(times) || !strcmp(times, "*")) {
00896       return;
00897    }
00898    /* Otherwise expect a range */
00899    e = strchr(times, '-');
00900    if (!e) {
00901       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",
00902             p->filename, p->startline, p->endline, times);
00903       warns++;
00904       return;
00905    }
00906    *e = '\0';
00907    e++;
00908    while (*e && !isdigit(*e)) 
00909       e++;
00910    if (!*e) {
00911       ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The time range format (%s) is missing the end time!\n",
00912             p->filename, p->startline, p->endline, p->u1.str);
00913       warns++;
00914    }
00915    if (sscanf(times, "%d:%d", &s1, &s2) != 2) {
00916       ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start time (%s) isn't quite right!\n",
00917             p->filename, p->startline, p->endline, times);
00918       warns++;
00919    }
00920    if (sscanf(e, "%d:%d", &e1, &e2) != 2) {
00921       ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end time (%s) isn't quite right!\n",
00922             p->filename, p->startline, p->endline, times);
00923       warns++;
00924    }
00925 
00926    s1 = s1 * 30 + s2/2;
00927    if ((s1 < 0) || (s1 >= 24*30)) {
00928       ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start time (%s) is out of range!\n",
00929             p->filename, p->startline, p->endline, times);
00930       warns++;
00931    }
00932    e1 = e1 * 30 + e2/2;
00933    if ((e1 < 0) || (e1 >= 24*30)) {
00934       ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end time (%s) is out of range!\n",
00935             p->filename, p->startline, p->endline, e);
00936       warns++;
00937    }
00938    return;
00939 }
00940 
00941 static char *days[] =
00942 {
00943    "sun",
00944    "mon",
00945    "tue",
00946    "wed",
00947    "thu",
00948    "fri",
00949    "sat",
00950 };
00951 
00952 /*! \brief  get_dow: Get day of week */
00953 static void check_dow(pval *DOW)
00954 {
00955    char *dow;
00956    char *c;
00957    /* The following line is coincidence, really! */
00958    int s, e;
00959    
00960    dow = ast_strdupa(DOW->u1.str);
00961 
00962    /* Check for all days */
00963    if (ast_strlen_zero(dow) || !strcmp(dow, "*"))
00964       return;
00965    /* Get start and ending days */
00966    c = strchr(dow, '-');
00967    if (c) {
00968       *c = '\0';
00969       c++;
00970    } else
00971       c = NULL;
00972    /* Find the start */
00973    s = 0;
00974    while ((s < 7) && strcasecmp(dow, days[s])) s++;
00975    if (s >= 7) {
00976       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",
00977             DOW->filename, DOW->startline, DOW->endline, dow);
00978       warns++;
00979    }
00980    if (c) {
00981       e = 0;
00982       while ((e < 7) && strcasecmp(c, days[e])) e++;
00983       if (e >= 7) {
00984          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",
00985                DOW->filename, DOW->startline, DOW->endline, c);
00986          warns++;
00987       }
00988    } else
00989       e = s;
00990 }
00991 
00992 static void check_day(pval *DAY)
00993 {
00994    char *day;
00995    char *c;
00996    /* The following line is coincidence, really! */
00997    int s, e;
00998 
00999    day = ast_strdupa(DAY->u1.str);
01000 
01001    /* Check for all days */
01002    if (ast_strlen_zero(day) || !strcmp(day, "*")) {
01003       return;
01004    }
01005    /* Get start and ending days */
01006    c = strchr(day, '-');
01007    if (c) {
01008       *c = '\0';
01009       c++;
01010    }
01011    /* Find the start */
01012    if (sscanf(day, "%d", &s) != 1) {
01013       ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start day of month (%s) must be a number!\n",
01014             DAY->filename, DAY->startline, DAY->endline, day);
01015       warns++;
01016    }
01017    else if ((s < 1) || (s > 31)) {
01018       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",
01019             DAY->filename, DAY->startline, DAY->endline, day);
01020       warns++;
01021    }
01022    s--;
01023    if (c) {
01024       if (sscanf(c, "%d", &e) != 1) {
01025          ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end day of month (%s) must be a number!\n",
01026                DAY->filename, DAY->startline, DAY->endline, c);
01027          warns++;
01028       }
01029       else if ((e < 1) || (e > 31)) {
01030          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",
01031                DAY->filename, DAY->startline, DAY->endline, day);
01032          warns++;
01033       }
01034       e--;
01035    } else
01036       e = s;
01037 }
01038 
01039 static char *months[] =
01040 {
01041    "jan",
01042    "feb",
01043    "mar",
01044    "apr",
01045    "may",
01046    "jun",
01047    "jul",
01048    "aug",
01049    "sep",
01050    "oct",
01051    "nov",
01052    "dec",
01053 };
01054 
01055 static void check_month(pval *MON)
01056 {
01057    char *mon;
01058    char *c;
01059    /* The following line is coincidence, really! */
01060    int s, e;
01061 
01062    mon = ast_strdupa(MON->u1.str);
01063 
01064    /* Check for all days */
01065    if (ast_strlen_zero(mon) || !strcmp(mon, "*")) 
01066       return ;
01067    /* Get start and ending days */
01068    c = strchr(mon, '-');
01069    if (c) {
01070       *c = '\0';
01071       c++;
01072    }
01073    /* Find the start */
01074    s = 0;
01075    while ((s < 12) && strcasecmp(mon, months[s])) s++;
01076    if (s >= 12) {
01077       ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start month (%s) must be a one of: 'jan', 'feb', ..., 'dec'!\n",
01078             MON->filename, MON->startline, MON->endline, mon);
01079       warns++;
01080    }
01081    if (c) {
01082       e = 0;
01083       while ((e < 12) && strcasecmp(mon, months[e])) e++;
01084       if (e >= 12) {
01085          ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end month (%s) must be a one of: 'jan', 'feb', ..., 'dec'!\n",
01086                MON->filename, MON->startline, MON->endline, c);
01087          warns++;
01088       }
01089    } else
01090       e = s;
01091 }
01092 
01093 static int check_break(pval *item)
01094 {
01095    pval *p = item;
01096    
01097    while( p && p->type != PV_MACRO && p->type != PV_CONTEXT ) /* early cutout, sort of */ {
01098       /* a break is allowed in WHILE, FOR, CASE, DEFAULT, PATTERN; otherwise, it don't make
01099          no sense */
01100       if( p->type == PV_CASE || p->type == PV_DEFAULT || p->type == PV_PATTERN 
01101          || p->type == PV_WHILE || p->type == PV_FOR   ) {
01102          return 1;
01103       }
01104       p = p->dad;
01105    }
01106    ast_log(LOG_ERROR,"Error: file %s, line %d-%d: 'break' not in switch, for, or while statement!\n",
01107          item->filename, item->startline, item->endline);
01108    errs++;
01109    
01110    return 0;
01111 }
01112 
01113 static int check_continue(pval *item)
01114 {
01115    pval *p = item;
01116    
01117    while( p && p->type != PV_MACRO && p->type != PV_CONTEXT ) /* early cutout, sort of */ {
01118       /* a break is allowed in WHILE, FOR, CASE, DEFAULT, PATTERN; otherwise, it don't make
01119          no sense */
01120       if( p->type == PV_WHILE || p->type == PV_FOR   ) {
01121          return 1;
01122       }
01123       p = p->dad;
01124    }
01125    ast_log(LOG_ERROR,"Error: file %s, line %d-%d: 'continue' not in 'for' or 'while' statement!\n",
01126          item->filename, item->startline, item->endline);
01127    errs++;
01128    
01129    return 0;
01130 }
01131 
01132 static struct pval *in_macro(pval *item)
01133 {
01134    struct pval *curr;
01135    curr = item;   
01136    while( curr ) {
01137       if( curr->type == PV_MACRO  ) {
01138          return curr;
01139       }
01140       curr = curr->dad;
01141    }
01142    return 0;
01143 }
01144 
01145 static struct pval *in_context(pval *item)
01146 {
01147    struct pval *curr;
01148    curr = item;   
01149    while( curr ) {
01150       if( curr->type == PV_MACRO || curr->type == PV_CONTEXT ) {
01151          return curr;
01152       }
01153       curr = curr->dad;
01154    }
01155    return 0;
01156 }
01157 
01158 
01159 /* general purpose goto finder */
01160 
01161 static void check_label(pval *item)
01162 {
01163    struct pval *curr;
01164    struct pval *x;
01165    int alright = 0;
01166    
01167    /* A label outside an extension just plain does not make sense! */
01168    
01169    curr = item;
01170    
01171    while( curr ) {
01172       if( curr->type == PV_MACRO || curr->type == PV_EXTENSION   ) {
01173          alright = 1;
01174          break;
01175       }
01176       curr = curr->dad;
01177    }
01178    if( !alright )
01179    {
01180       ast_log(LOG_ERROR,"Error: file %s, line %d-%d: Label %s is not within an extension or macro!\n",
01181             item->filename, item->startline, item->endline, item->u1.str);
01182       errs++;  
01183    }
01184    
01185    
01186    /* basically, ensure that a label is not repeated in a context. Period.
01187       The method:  well, for each label, find the first label in the context
01188       with the same name. If it's not the current label, then throw an error. */
01189 
01190    
01191    /* printf("==== check_label:   ====\n"); */
01192    if( !current_extension )
01193       curr = current_context;
01194    else
01195       curr = current_extension;
01196    
01197    x = find_first_label_in_current_context((char *)item->u1.str, curr);
01198    /* 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); */
01199    if( x && x != item )
01200    {
01201       ast_log(LOG_ERROR,"Error: file %s, line %d-%d: Duplicate label %s! Previously defined at file %s, line %d.\n",
01202             item->filename, item->startline, item->endline, item->u1.str, x->filename, x->startline);
01203       errs++;
01204    }
01205    /* printf("<<<<< check_label:   ====\n"); */
01206 }
01207 
01208 static pval *get_goto_target(pval *item)
01209 {
01210    /* just one item-- the label should be in the current extension */
01211    pval *curr_ext = get_extension_or_contxt(item); /* containing exten, or macro */
01212    pval *curr_cont;
01213    
01214    if (item->u1.list && !item->u1.list->next && !strstr((item->u1.list)->u1.str,"${")) {
01215       struct pval *x = find_label_in_current_extension((char*)((item->u1.list)->u1.str), curr_ext);
01216          return x;
01217    }
01218 
01219    curr_cont = get_contxt(item);
01220 
01221    /* TWO items */
01222    if (item->u1.list->next && !item->u1.list->next->next) {
01223       if (!strstr((item->u1.list)->u1.str,"${") 
01224          && !strstr(item->u1.list->next->u1.str,"${") ) /* Don't try to match variables */ {
01225          struct pval *x = find_label_in_current_context((char *)item->u1.list->u1.str, (char *)item->u1.list->next->u1.str, curr_cont);
01226             return x;
01227       }
01228    }
01229    
01230    /* All 3 items! */
01231    if (item->u1.list->next && item->u1.list->next->next) {
01232       /* all three */
01233       pval *first = item->u1.list;
01234       pval *second = item->u1.list->next;
01235       pval *third = item->u1.list->next->next;
01236       
01237       if (!strstr((item->u1.list)->u1.str,"${") 
01238          && !strstr(item->u1.list->next->u1.str,"${")
01239          && !strstr(item->u1.list->next->next->u1.str,"${")) /* Don't try to match variables */ {
01240          struct pval *x = find_label_in_current_db((char*)first->u1.str, (char*)second->u1.str, (char*)third->u1.str);
01241          if (!x) {
01242 
01243             struct pval *p3;
01244             struct pval *that_context = find_context(item->u1.list->u1.str);
01245             
01246             /* the target of the goto could be in an included context!! Fancy that!! */
01247             /* look for includes in the current context */
01248             if (that_context) {
01249                for (p3=that_context->u2.statements; p3; p3=p3->next) {
01250                   if (p3->type == PV_INCLUDES) {
01251                      struct pval *p4;
01252                      for (p4=p3->u1.list; p4; p4=p4->next) {
01253                         /* for each context pointed to, find it, then find a context/label that matches the
01254                            target here! */
01255                         char *incl_context = p4->u1.str;
01256                         /* find a matching context name */
01257                         struct pval *that_other_context = find_context(incl_context);
01258                         if (that_other_context) {
01259                            struct pval *x3;
01260                            x3 = find_label_in_current_context((char *)item->u1.list->next->u1.str, (char *)item->u1.list->next->next->u1.str, that_other_context);
01261                            if (x3) {
01262                               return x3;
01263                            }
01264                         }
01265                      }
01266                   }
01267                }
01268             }
01269          }
01270          return x;
01271       }
01272    }
01273    return 0;
01274 }
01275 
01276 static void check_goto(pval *item)
01277 {
01278    /* check for the target of the goto-- does it exist? */
01279    if ( !(item->u1.list)->next && !(item->u1.list)->u1.str ) {
01280       ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto:  empty label reference found!\n",
01281             item->filename, item->startline, item->endline);
01282       errs++;
01283    }
01284    
01285    /* just one item-- the label should be in the current extension */
01286    
01287    if (item->u1.list && !item->u1.list->next && !strstr((item->u1.list)->u1.str,"${")) {
01288       struct pval *z = get_extension_or_contxt(item);
01289       struct pval *x = 0;
01290       if (z)
01291          x = find_label_in_current_extension((char*)((item->u1.list)->u1.str), z); /* if in macro, use current context instead */
01292       /* printf("Called find_label_in_current_extension with arg %s; current_extension is %x: %d\n",
01293          (char*)((item->u1.list)->u1.str), current_extension?current_extension:current_context, current_extension?current_extension->type:current_context->type); */
01294       if (!x) {
01295          ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto:  no label %s exists in the current extension!\n",
01296                item->filename, item->startline, item->endline, item->u1.list->u1.str);
01297          errs++;
01298       }
01299       else
01300          return;
01301    }
01302    
01303    /* TWO items */
01304    if (item->u1.list->next && !item->u1.list->next->next) {
01305       /* two items */
01306       /* printf("Calling find_label_in_current_context with args %s, %s\n",
01307          (char*)((item->u1.list)->u1.str), (char *)item->u1.list->next->u1.str); */
01308       if (!strstr((item->u1.list)->u1.str,"${") 
01309          && !strstr(item->u1.list->next->u1.str,"${") ) /* Don't try to match variables */ {
01310          struct pval *z = get_contxt(item);
01311          struct pval *x = 0;
01312          
01313          if (z)
01314             x = find_label_in_current_context((char *)item->u1.list->u1.str, (char *)item->u1.list->next->u1.str, z);
01315 
01316          if (!x) {
01317             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",
01318                   item->filename, item->startline, item->endline, item->u1.list->u1.str, item->u1.list->next->u1.str );
01319             errs++;
01320          }
01321          else
01322             return;
01323       }
01324    }
01325    
01326    /* All 3 items! */
01327    if (item->u1.list->next && item->u1.list->next->next) {
01328       /* all three */
01329       pval *first = item->u1.list;
01330       pval *second = item->u1.list->next;
01331       pval *third = item->u1.list->next->next;
01332       
01333       /* printf("Calling find_label_in_current_db with args %s, %s, %s\n",
01334          (char*)first->u1.str, (char*)second->u1.str, (char*)third->u1.str); */
01335       if (!strstr((item->u1.list)->u1.str,"${") 
01336          && !strstr(item->u1.list->next->u1.str,"${")
01337          && !strstr(item->u1.list->next->next->u1.str,"${")) /* Don't try to match variables */ {
01338          struct pval *x = find_label_in_current_db((char*)first->u1.str, (char*)second->u1.str, (char*)third->u1.str);
01339          if (!x) {
01340             struct pval *p3;
01341             struct pval *found = 0;
01342             struct pval *that_context = find_context(item->u1.list->u1.str);
01343             
01344             /* the target of the goto could be in an included context!! Fancy that!! */
01345             /* look for includes in the current context */
01346             if (that_context) {
01347                for (p3=that_context->u2.statements; p3; p3=p3->next) {
01348                   if (p3->type == PV_INCLUDES) {
01349                      struct pval *p4;
01350                      for (p4=p3->u1.list; p4; p4=p4->next) {
01351                         /* for each context pointed to, find it, then find a context/label that matches the
01352                            target here! */
01353                         char *incl_context = p4->u1.str;
01354                         /* find a matching context name */
01355                         struct pval *that_other_context = find_context(incl_context);
01356                         if (that_other_context) {
01357                            struct pval *x3;
01358                            x3 = find_label_in_current_context((char *)item->u1.list->next->u1.str, (char *)item->u1.list->next->next->u1.str, that_other_context);
01359                            if (x3) {
01360                               found = x3;
01361                               break;
01362                            }
01363                         }
01364                      }
01365                   }
01366                }
01367                if (!found) {
01368                   ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto:  no label %s|%s exists in the context %s or its inclusions!\n",
01369                         item->filename, item->startline, item->endline, item->u1.list->next->u1.str, item->u1.list->next->next->u1.str, item->u1.list->u1.str );
01370                   errs++;
01371                } else {
01372                   struct pval *mac = in_macro(item); /* is this goto inside a macro? */
01373                   if( mac ) {    /* yes! */
01374                      struct pval *targ = in_context(found);
01375                      if( mac != targ )
01376                      {
01377                         ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: It's bad form to have a goto in a macro to a target outside the macro!\n",
01378                               item->filename, item->startline, item->endline);
01379                         warns++;                      
01380                      }
01381                   }
01382                }
01383             } else {
01384                ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto:  no context %s could be found that matches the goto target!\n",
01385                      item->filename, item->startline, item->endline, item->u1.list->u1.str);
01386                errs++;
01387             }
01388          } else {
01389             struct pval *mac = in_macro(item); /* is this goto inside a macro? */
01390             if( mac ) {    /* yes! */
01391                struct pval *targ = in_context(x);
01392                if( mac != targ )
01393                {
01394                   ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: It's bad form to have a goto in a macro to a target outside the macro!\n",
01395                         item->filename, item->startline, item->endline);
01396                   warns++;                      
01397                }
01398             }
01399          }
01400       }
01401    }
01402 }
01403    
01404 
01405 static void find_pval_goto_item(pval *item, int lev)
01406 {
01407    struct pval *p4;
01408    if (lev>100) {
01409       ast_log(LOG_ERROR,"find_pval_goto in infinite loop!\n\n");
01410       return;
01411    }
01412    
01413    switch ( item->type ) {
01414    case PV_MACRO:
01415       /* fields: item->u1.str     == name of macro
01416                  item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
01417                item->u2.arglist->u1.str  == argument
01418                item->u2.arglist->next   == next arg
01419 
01420                item->u3.macro_statements == pval list of statements in macro body.
01421       */
01422          
01423       /* printf("Descending into matching macro %s\n", match_context); */
01424       find_pval_gotos(item->u3.macro_statements,lev+1); /* if we're just searching for a context, don't bother descending into them */
01425       
01426       break;
01427          
01428    case PV_CONTEXT:
01429       /* fields: item->u1.str     == name of context
01430                  item->u2.statements == pval list of statements in context body
01431                item->u3.abstract == int 1 if an abstract keyword were present
01432       */
01433       break;
01434 
01435    case PV_CASE:
01436       /* fields: item->u1.str     == value of case
01437                  item->u2.statements == pval list of statements under the case
01438       */
01439       find_pval_gotos(item->u2.statements,lev+1);
01440       break;
01441          
01442    case PV_PATTERN:
01443       /* fields: item->u1.str     == value of case
01444                  item->u2.statements == pval list of statements under the case
01445       */
01446       find_pval_gotos(item->u2.statements,lev+1);
01447       break;
01448          
01449    case PV_DEFAULT:
01450       /* fields: 
01451                  item->u2.statements == pval list of statements under the case
01452       */
01453       find_pval_gotos(item->u2.statements,lev+1);
01454       break;
01455          
01456    case PV_CATCH:
01457       /* fields: item->u1.str     == name of extension to catch
01458                  item->u2.statements == pval list of statements in context body
01459       */
01460       find_pval_gotos(item->u2.statements,lev+1);
01461       break;
01462          
01463    case PV_STATEMENTBLOCK:
01464       /* fields: item->u1.list     == pval list of statements in block, one per entry in the list
01465       */
01466       find_pval_gotos(item->u1.list,lev+1);
01467       break;
01468          
01469    case PV_GOTO:
01470       /* fields: item->u1.list     == pval list of PV_WORD target names, up to 3, in order as given by user.
01471                  item->u1.list->u1.str  == where the data on a PV_WORD will always be.
01472       */
01473       check_goto(item);  /* THE WHOLE FUNCTION OF THIS ENTIRE ROUTINE!!!! */
01474       break;
01475          
01476    case PV_INCLUDES:
01477       /* fields: item->u1.list     == pval list of PV_WORD elements, one per entry in the list
01478       */
01479       for (p4=item->u1.list; p4; p4=p4->next) {
01480          /* for each context pointed to, find it, then find a context/label that matches the
01481             target here! */
01482          char *incl_context = p4->u1.str;
01483          /* find a matching context name */
01484          struct pval *that_context = find_context(incl_context);
01485          if (that_context) {
01486             find_pval_gotos(that_context,lev+1); /* keep working up the includes */
01487          }
01488       }
01489       break;
01490       
01491    case PV_FOR:
01492       /* fields: item->u1.for_init     == a string containing the initalizer
01493                  item->u2.for_test     == a string containing the loop test
01494                  item->u3.for_inc      == a string containing the loop increment
01495 
01496                item->u4.for_statements == a pval list of statements in the for ()
01497       */
01498       find_pval_gotos(item->u4.for_statements,lev+1);
01499       break;
01500          
01501    case PV_WHILE:
01502       /* fields: item->u1.str        == the while conditional, as supplied by user
01503 
01504                item->u2.statements == a pval list of statements in the while ()
01505       */
01506       find_pval_gotos(item->u2.statements,lev+1);
01507       break;
01508          
01509    case PV_RANDOM:
01510       /* fields: item->u1.str        == the random number expression, as supplied by user
01511 
01512                item->u2.statements == a pval list of statements in the if ()
01513                item->u3.else_statements == a pval list of statements in the else
01514                                     (could be zero)
01515        fall thru to PV_IF */
01516       
01517    case PV_IFTIME:
01518       /* fields: item->u1.list        == the time values, 4 of them, as PV_WORD structs in a list
01519 
01520                item->u2.statements == a pval list of statements in the if ()
01521                item->u3.else_statements == a pval list of statements in the else
01522                                     (could be zero)
01523       fall thru to PV_IF*/
01524    case PV_IF:
01525       /* fields: item->u1.str        == the if conditional, as supplied by user
01526 
01527                item->u2.statements == a pval list of statements in the if ()
01528                item->u3.else_statements == a pval list of statements in the else
01529                                     (could be zero)
01530       */
01531       find_pval_gotos(item->u2.statements,lev+1);
01532 
01533       if (item->u3.else_statements) {
01534          find_pval_gotos(item->u3.else_statements,lev+1);
01535       }
01536       break;
01537          
01538    case PV_SWITCH:
01539       /* fields: item->u1.str        == the switch expression
01540 
01541                item->u2.statements == a pval list of statements in the switch, 
01542                                     (will be case statements, most likely!)
01543       */
01544       find_pval_gotos(item->u3.else_statements,lev+1);
01545       break;
01546          
01547    case PV_EXTENSION:
01548       /* fields: item->u1.str        == the extension name, label, whatever it's called
01549 
01550                item->u2.statements == a pval list of statements in the extension
01551                item->u3.hints      == a char * hint argument
01552                item->u4.regexten   == an int boolean. non-zero says that regexten was specified
01553       */
01554 
01555       find_pval_gotos(item->u2.statements,lev+1);
01556       break;
01557 
01558    default:
01559       break;
01560    }
01561 }
01562 
01563 static void find_pval_gotos(pval *item,int lev)
01564 {
01565    pval *i;
01566 
01567    for (i=item; i; i=i->next) {
01568       
01569       find_pval_goto_item(i, lev);
01570    }
01571 }
01572 
01573 
01574 
01575 /* general purpose label finder */
01576 static struct pval *match_pval_item(pval *item)
01577 {
01578    pval *x;
01579    
01580    switch ( item->type ) {
01581    case PV_MACRO:
01582       /* fields: item->u1.str     == name of macro
01583                  item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
01584                item->u2.arglist->u1.str  == argument
01585                item->u2.arglist->next   == next arg
01586 
01587                item->u3.macro_statements == pval list of statements in macro body.
01588       */
01589       /* printf("    matching in MACRO %s, match_context=%s; retoncontmtch=%d; \n", item->u1.str, match_context, return_on_context_match); */
01590       if (!strcmp(match_context,"*") || !strcmp(item->u1.str, match_context)) {
01591          
01592          /* printf("MACRO: match context is: %s\n", match_context); */
01593          
01594          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 */ {
01595             /* printf("Returning on matching macro %s\n", match_context); */
01596             return item;
01597          }
01598          
01599          
01600          if (!return_on_context_match) {
01601             /* printf("Descending into matching macro %s/%s\n", match_context, item->u1.str); */
01602             if ((x=match_pval(item->u3.macro_statements)))  {
01603                /* printf("Responded with pval match %x\n", x); */
01604                return x;
01605             }
01606          }
01607       } else {
01608          /* printf("Skipping context/macro %s\n", item->u1.str); */
01609       }
01610       
01611       break;
01612          
01613    case PV_CONTEXT:
01614       /* fields: item->u1.str     == name of context
01615                  item->u2.statements == pval list of statements in context body
01616                item->u3.abstract == int 1 if an abstract keyword were present
01617       */
01618       /* printf("    matching in CONTEXT\n"); */
01619       if (!strcmp(match_context,"*") || !strcmp(item->u1.str, match_context)) {
01620          if (return_on_context_match && !strcmp(item->u1.str, match_context)) {
01621             /* printf("Returning on matching context %s\n", match_context); */
01622             /* printf("non-CONTEXT: Responded with pval match %x\n", x); */
01623             return item;
01624          }
01625          
01626          if (!return_on_context_match ) {
01627             /* printf("Descending into matching context %s\n", match_context); */
01628             if ((x=match_pval(item->u2.statements))) /* if we're just searching for a context, don't bother descending into them */ {
01629                /* printf("CONTEXT: Responded with pval match %x\n", x); */
01630                return x;
01631             }
01632          }
01633       } else {
01634          /* printf("Skipping context/macro %s\n", item->u1.str); */
01635       }
01636       break;
01637 
01638    case PV_CASE:
01639       /* fields: item->u1.str     == value of case
01640                  item->u2.statements == pval list of statements under the case
01641       */
01642       /* printf("    matching in CASE\n"); */
01643       if ((x=match_pval(item->u2.statements))) {
01644          /* printf("CASE: Responded with pval match %x\n", x); */
01645          return x;
01646       }
01647       break;
01648          
01649    case PV_PATTERN:
01650       /* fields: item->u1.str     == value of case
01651                  item->u2.statements == pval list of statements under the case
01652       */
01653       /* printf("    matching in PATTERN\n"); */
01654       if ((x=match_pval(item->u2.statements))) {
01655          /* printf("PATTERN: Responded with pval match %x\n", x); */
01656          return x;
01657       }
01658       break;
01659          
01660    case PV_DEFAULT:
01661       /* fields: 
01662                  item->u2.statements == pval list of statements under the case
01663       */
01664       /* printf("    matching in DEFAULT\n"); */
01665       if ((x=match_pval(item->u2.statements))) {
01666          /* printf("DEFAULT: Responded with pval match %x\n", x); */
01667          return x;
01668       }
01669       break;
01670          
01671    case PV_CATCH:
01672       /* fields: item->u1.str     == name of extension to catch
01673                  item->u2.statements == pval list of statements in context body
01674       */
01675       /* printf("    matching in CATCH\n"); */
01676       if ((x=match_pval(item->u2.statements))) {
01677          /* printf("CATCH: Responded with pval match %x\n", x); */
01678          return x;
01679       }
01680       break;
01681          
01682    case PV_STATEMENTBLOCK:
01683       /* fields: item->u1.list     == pval list of statements in block, one per entry in the list
01684       */
01685       /* printf("    matching in STATEMENTBLOCK\n"); */
01686       if ((x=match_pval(item->u1.list))) {
01687          /* printf("STATEMENTBLOCK: Responded with pval match %x\n", x); */
01688          return x;
01689       }
01690       break;
01691          
01692    case PV_LABEL:
01693       /* fields: item->u1.str     == label name
01694       */
01695       /* printf("PV_LABEL %s (cont=%s, exten=%s\n", 
01696          item->u1.str, current_context->u1.str, (current_extension?current_extension->u1.str:"<macro>"));*/
01697       
01698       if (count_labels) {
01699          if (!strcmp(match_label, item->u1.str)) {
01700             label_count++;
01701             last_matched_label = item;
01702          }
01703          
01704       } else {
01705          if (!strcmp(match_label, item->u1.str)) {
01706             /* printf("LABEL: Responded with pval match %x\n", x); */
01707             return item;
01708          }
01709       }
01710       break;
01711          
01712    case PV_FOR:
01713       /* fields: item->u1.for_init     == a string containing the initalizer
01714                  item->u2.for_test     == a string containing the loop test
01715                  item->u3.for_inc      == a string containing the loop increment
01716 
01717                item->u4.for_statements == a pval list of statements in the for ()
01718       */
01719       /* printf("    matching in FOR\n"); */
01720       if ((x=match_pval(item->u4.for_statements))) {
01721          /* printf("FOR: Responded with pval match %x\n", x);*/
01722          return x;
01723       }
01724       break;
01725          
01726    case PV_WHILE:
01727       /* fields: item->u1.str        == the while conditional, as supplied by user
01728 
01729                item->u2.statements == a pval list of statements in the while ()
01730       */
01731       /* printf("    matching in WHILE\n"); */
01732       if ((x=match_pval(item->u2.statements))) {
01733          /* printf("WHILE: Responded with pval match %x\n", x); */
01734          return x;
01735       }
01736       break;
01737          
01738    case PV_RANDOM:
01739       /* fields: item->u1.str        == the random number expression, as supplied by user
01740 
01741                item->u2.statements == a pval list of statements in the if ()
01742                item->u3.else_statements == a pval list of statements in the else
01743                                     (could be zero)
01744        fall thru to PV_IF */
01745       
01746    case PV_IFTIME:
01747       /* fields: item->u1.list        == the time values, 4 of them, as PV_WORD structs in a list
01748 
01749                item->u2.statements == a pval list of statements in the if ()
01750                item->u3.else_statements == a pval list of statements in the else
01751                                     (could be zero)
01752       fall thru to PV_IF*/
01753    case PV_IF:
01754       /* fields: item->u1.str        == the if conditional, as supplied by user
01755 
01756                item->u2.statements == a pval list of statements in the if ()
01757                item->u3.else_statements == a pval list of statements in the else
01758                                     (could be zero)
01759       */
01760       /* printf("    matching in IF/IFTIME/RANDOM\n"); */
01761       if ((x=match_pval(item->u2.statements))) {
01762          return x;
01763       }
01764       if (item->u3.else_statements) {
01765          if ((x=match_pval(item->u3.else_statements))) {
01766             /* printf("IF/IFTIME/RANDOM: Responded with pval match %x\n", x); */
01767             return x;
01768          }
01769       }
01770       break;
01771          
01772    case PV_SWITCH:
01773       /* fields: item->u1.str        == the switch expression
01774 
01775                item->u2.statements == a pval list of statements in the switch, 
01776                                     (will be case statements, most likely!)
01777       */
01778       /* printf("    matching in SWITCH\n"); */
01779       if ((x=match_pval(item->u2.statements))) {
01780          /* printf("SWITCH: Responded with pval match %x\n", x); */
01781          return x;
01782       }
01783       break;
01784          
01785    case PV_EXTENSION:
01786       /* fields: item->u1.str        == the extension name, label, whatever it's called
01787 
01788                item->u2.statements == a pval list of statements in the extension
01789                item->u3.hints      == a char * hint argument
01790                item->u4.regexten   == an int boolean. non-zero says that regexten was specified
01791       */
01792       /* printf("    matching in EXTENSION\n"); */
01793       if (!strcmp(match_exten,"*") || extension_matches(item, match_exten, item->u1.str) ) {
01794          /* printf("Descending into matching exten %s => %s\n", match_exten, item->u1.str); */
01795          if (strcmp(match_label,"1") == 0) {
01796             if (item->u2.statements) {
01797                struct pval *p5 = item->u2.statements;
01798                while (p5 && p5->type == PV_LABEL)  /* find the first non-label statement in this context. If it exists, there's a "1" */
01799                   p5 = p5->next;
01800                if (p5)
01801                   return p5;
01802                else
01803                   return 0;
01804             }
01805             else
01806                return 0;
01807          }
01808 
01809          if ((x=match_pval(item->u2.statements))) {
01810             /* printf("EXTENSION: Responded with pval match %x\n", x); */
01811             return x;
01812          }
01813       } else {
01814          /* printf("Skipping exten %s\n", item->u1.str); */
01815       }
01816       break;
01817    default:
01818       /* printf("    matching in default = %d\n", item->type); */
01819       break;
01820    }
01821    return 0;
01822 }
01823 
01824 struct pval *match_pval(pval *item)
01825 {
01826    pval *i;
01827 
01828    for (i=item; i; i=i->next) {
01829       pval *x;
01830       /* printf("   -- match pval: item %d\n", i->type); */
01831       
01832       if ((x = match_pval_item(i))) {
01833          /* printf("match_pval: returning x=%x\n", (int)x); */
01834          return x; /* cut the search short */
01835       }
01836    }
01837    return 0;
01838 }
01839 
01840 #if 0
01841 int count_labels_in_current_context(char *label)
01842 {
01843    label_count = 0;
01844    count_labels = 1;
01845    return_on_context_match = 0;
01846    match_pval(current_context->u2.statements);
01847    
01848    return label_count;
01849 }
01850 #endif
01851 
01852 struct pval *find_first_label_in_current_context(char *label, pval *curr_cont)
01853 {
01854    /* printf("  --- Got args %s, %s\n", exten, label); */
01855    struct pval *ret;
01856    struct pval *p3;
01857    
01858    count_labels = 0;
01859    return_on_context_match = 0;
01860    match_context = "*";
01861    match_exten = "*";
01862    match_label = label;
01863    
01864    ret =  match_pval(curr_cont);
01865    if (ret)
01866       return ret;
01867                
01868    /* the target of the goto could be in an included context!! Fancy that!! */
01869    /* look for includes in the current context */
01870    for (p3=curr_cont->u2.statements; p3; p3=p3->next) {
01871       if (p3->type == PV_INCLUDES) {
01872          struct pval *p4;
01873          for (p4=p3->u1.list; p4; p4=p4->next) {
01874             /* for each context pointed to, find it, then find a context/label that matches the
01875                target here! */
01876             char *incl_context = p4->u1.str;
01877             /* find a matching context name */
01878             struct pval *that_context = find_context(incl_context);
01879             if (that_context) {
01880                struct pval *x3;
01881                x3 = find_first_label_in_current_context(label, that_context);
01882                if (x3) {
01883                   return x3;
01884                }
01885             }
01886          }
01887       }
01888    }
01889    return 0;
01890 }
01891 
01892 struct pval *find_label_in_current_context(char *exten, char *label, pval *curr_cont)
01893 {
01894    /* printf("  --- Got args %s, %s\n", exten, label); */
01895    struct pval *ret;
01896    struct pval *p3;
01897    
01898    count_labels = 0;
01899    return_on_context_match = 0;
01900    match_context = "*";
01901    match_exten = exten;
01902    match_label = label;
01903    ret =  match_pval(curr_cont->u2.statements);
01904    if (ret)
01905       return ret;
01906                
01907    /* the target of the goto could be in an included context!! Fancy that!! */
01908    /* look for includes in the current context */
01909    for (p3=curr_cont->u2.statements; p3; p3=p3->next) {
01910       if (p3->type == PV_INCLUDES) {
01911          struct pval *p4;
01912          for (p4=p3->u1.list; p4; p4=p4->next) {
01913             /* for each context pointed to, find it, then find a context/label that matches the
01914                target here! */
01915             char *incl_context = p4->u1.str;
01916             /* find a matching context name */
01917             struct pval *that_context = find_context(incl_context);
01918             if (that_context) {
01919                struct pval *x3;
01920                x3 = find_label_in_current_context(exten, label, that_context);
01921                if (x3) {
01922                   return x3;
01923                }
01924             }
01925          }
01926       }
01927    }
01928    return 0;
01929 }
01930 
01931 static struct pval *find_label_in_current_extension(const char *label, pval *curr_ext)
01932 {
01933    /* printf("  --- Got args %s\n", label); */
01934    count_labels = 0;
01935    return_on_context_match = 0;
01936    match_context = "*";
01937    match_exten = "*";
01938    match_label = label;
01939    return match_pval(curr_ext);
01940 }
01941 
01942 static struct pval *find_label_in_current_db(const char *context, const char *exten, const char *label)
01943 {
01944    /* printf("  --- Got args %s, %s, %s\n", context, exten, label); */
01945    count_labels = 0;
01946    return_on_context_match = 0;
01947 
01948    match_context = context;
01949    match_exten = exten;
01950    match_label = label;
01951    
01952    return match_pval(current_db);
01953 }
01954 
01955 
01956 struct pval *find_macro(char *name)
01957 {
01958    return_on_context_match = 1;
01959    count_labels = 0;
01960    match_context = name;
01961    match_exten = "*";  /* don't really need to set these, shouldn't be reached */
01962    match_label = "*";
01963    return match_pval(current_db);
01964 }
01965 
01966 struct pval *find_context(char *name)
01967 {
01968    return_on_context_match = 1;
01969    count_labels = 0;
01970    match_context = name;
01971    match_exten = "*";  /* don't really need to set these, shouldn't be reached */
01972    match_label = "*";
01973    return match_pval(current_db);
01974 }
01975 
01976 int is_float(char *arg )
01977 {
01978    char *s;
01979    for (s=arg; *s; s++) {
01980       if (*s != '.' && (*s < '0' || *s > '9'))
01981          return 0;
01982    }
01983    return 1;
01984 }
01985 int is_int(char *arg )
01986 {
01987    char *s;
01988    for (s=arg; *s; s++) {
01989       if (*s < '0' || *s > '9')
01990          return 0;
01991    }
01992    return 1;
01993 }
01994 int is_empty(char *arg)
01995 {
01996    if (!arg)
01997       return 1;
01998    if (*arg == 0)
01999       return 1;
02000    while (*arg) {
02001       if (*arg != ' ' && *arg != '\t')
02002          return 0;
02003       arg++;
02004    }
02005    return 1;
02006 }
02007 
02008 #ifdef AAL_ARGCHECK
02009 int option_matches_j( struct argdesc *should, pval *is, struct argapp *app)
02010 {
02011    struct argchoice *ac;
02012    char *opcop,*q,*p;
02013    
02014    switch (should->dtype) {
02015    case ARGD_OPTIONSET:
02016       if ( strstr(is->u1.str,"${") )
02017          return 0;  /* no checking anything if there's a var reference in there! */
02018          
02019       opcop = ast_strdupa(is->u1.str);
02020 
02021       for (q=opcop;*q;q++) { /* erase the innards of X(innard) type arguments, so we don't get confused later */
02022          if ( *q == '(' ) {
02023             p = q+1;
02024             while (*p && *p != ')' )
02025                *p++ = '+';
02026             q = p+1;
02027          }
02028       }
02029       
02030       for (ac=app->opts; ac; ac=ac->next) {
02031          if (strlen(ac->name)>1  && strchr(ac->name,'(') == 0 && strcmp(ac->name,is->u1.str) == 0) /* multichar option, no parens, and a match? */
02032             return 0;
02033       }
02034       for (ac=app->opts; ac; ac=ac->next) {
02035          if (strlen(ac->name)==1  ||  strchr(ac->name,'(')) {
02036             char *p = strchr(opcop,ac->name[0]);  /* wipe out all matched options in the user-supplied string */
02037             
02038             if (p && *p == 'j') {
02039                ast_log(LOG_ERROR, "Error: file %s, line %d-%d: The j option in the %s application call is not appropriate for AEL!\n",
02040                      is->filename, is->startline, is->endline, app->name);
02041                errs++;
02042             }
02043             
02044             if (p) {
02045                *p = '+';
02046                if (ac->name[1] == '(') {
02047                   if (*(p+1) != '(') {
02048                      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",
02049                            is->filename, is->startline, is->endline, ac->name[0], app->name);
02050                      warns++;
02051                   }
02052                }
02053             }
02054          }
02055       }
02056       for (q=opcop; *q; q++) {
02057          if ( *q != '+' && *q != '(' && *q != ')') {
02058             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",
02059                   is->filename, is->startline, is->endline, *q, app->name);
02060             warns++;
02061          }
02062       }
02063       return 1;
02064       break;
02065    default:
02066       return 0;
02067    }
02068    
02069 }
02070 
02071 int option_matches( struct argdesc *should, pval *is, struct argapp *app)
02072 {
02073    struct argchoice *ac;
02074    char *opcop;
02075    
02076    switch (should->dtype) {
02077    case ARGD_STRING:
02078       if (is_empty(is->u1.str) && should->type == ARGD_REQUIRED)
02079          return 0;
02080       if (is->u1.str && strlen(is->u1.str) > 0) /* most will match */
02081          return 1;
02082       break;
02083       
02084    case ARGD_INT:
02085       if (is_int(is->u1.str))
02086          return 1;
02087       else
02088          return 0;
02089       break;
02090       
02091    case ARGD_FLOAT:
02092       if (is_float(is->u1.str))
02093          return 1;
02094       else
02095          return 0;
02096       break;
02097       
02098    case ARGD_ENUM:
02099       if( !is->u1.str || strlen(is->u1.str) == 0 )
02100          return 1; /* a null arg in the call will match an enum, I guess! */
02101       for (ac=should->choices; ac; ac=ac->next) {
02102          if (strcmp(ac->name,is->u1.str) == 0)
02103             return 1;
02104       }
02105       return 0;
02106       break;
02107       
02108    case ARGD_OPTIONSET:
02109       opcop = ast_strdupa(is->u1.str);
02110       
02111       for (ac=app->opts; ac; ac=ac->next) {
02112          if (strlen(ac->name)>1  && strchr(ac->name,'(') == 0 && strcmp(ac->name,is->u1.str) == 0) /* multichar option, no parens, and a match? */
02113             return 1;
02114       }
02115       for (ac=app->opts; ac; ac=ac->next) {
02116          if (strlen(ac->name)==1  ||  strchr(ac->name,'(')) {
02117             char *p = strchr(opcop,ac->name[0]);  /* wipe out all matched options in the user-supplied string */
02118             
02119             if (p) {
02120                *p = '+';
02121                if (ac->name[1] == '(') {
02122                   if (*(p+1) == '(') {
02123                      char *q = p+1;
02124                      while (*q && *q != ')') {
02125                         *q++ = '+';
02126                      }
02127                      *q = '+';
02128                   }
02129                }
02130             }
02131          }
02132       }
02133       return 1;
02134       break;
02135    case ARGD_VARARG:
02136       return 1; /* matches anything */
02137       break;
02138    }
02139    return 1; /* unless some for-sure match or non-match returns, then it must be close enough ... */
02140 }
02141 #endif
02142 
02143 int check_app_args(pval* appcall, pval *arglist, struct argapp *app)
02144 {
02145 #ifdef AAL_ARGCHECK
02146    struct argdesc *ad = app->args;
02147    pval *pa;
02148    int z;
02149    
02150    for (pa = arglist; pa; pa=pa->next) {
02151       if (!ad) {
02152          ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: Extra argument %s not in application call to %s !\n",
02153                arglist->filename, arglist->startline, arglist->endline, pa->u1.str, app->name);
02154          warns++;
02155          return 1;
02156       } else {
02157          /* find the first entry in the ad list that will match */
02158          do {
02159             if ( ad->dtype == ARGD_VARARG ) /* once we hit the VARARG, all bets are off. Discontinue the comparisons */
02160                break;
02161             
02162             z= option_matches( ad, pa, app);
02163             if (!z) {
02164                if ( !arglist )
02165                   arglist=appcall;
02166                
02167                if (ad->type == ARGD_REQUIRED) {
02168                   ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: Required argument %s not in application call to %s !\n",
02169                         arglist->filename, arglist->startline, arglist->endline, ad->dtype==ARGD_OPTIONSET?"options":ad->name, app->name);
02170                   warns++;
02171                   return 1;
02172                }
02173             } else if (z && ad->dtype == ARGD_OPTIONSET) {
02174                option_matches_j( ad, pa, app);
02175             }
02176             ad = ad->next;
02177          } while (ad && !z);
02178       }
02179    }
02180    /* any app nodes left, that are not optional? */
02181    for ( ; ad; ad=ad->next) {
02182       if (ad->type == ARGD_REQUIRED && ad->dtype != ARGD_VARARG) {
02183          if ( !arglist ) 
02184             arglist=appcall;
02185          ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: Required argument %s not in application call to %s !\n",
02186                arglist->filename, arglist->startline, arglist->endline, ad->dtype==ARGD_OPTIONSET?"options":ad->name, app->name);
02187          warns++;
02188          return 1;
02189       }
02190    }
02191    return 0;
02192 #else
02193    return 0;
02194 #endif
02195 }
02196 
02197 void check_switch_expr(pval *item, struct argapp *apps)
02198 {
02199 #ifdef AAL_ARGCHECK
02200    /* get and clean the variable name */
02201    char *buff1, *p;
02202    struct argapp *a,*a2;
02203    struct appsetvar *v,*v2;
02204    struct argchoice *c;
02205    pval *t;
02206    
02207    p = item->u1.str;
02208    while (p && *p && (*p == ' ' || *p == '\t' || *p == '$' || *p == '{' ) )
02209       p++;
02210    
02211    buff1 = ast_strdupa(p);
02212 
02213    while (strlen(buff1) > 0 && ( buff1[strlen(buff1)-1] == '}' || buff1[strlen(buff1)-1] == ' ' || buff1[strlen(buff1)-1] == '\t'))
02214       buff1[strlen(buff1)-1] = 0;
02215    /* buff1 now contains the variable name */
02216    v = 0;
02217    for (a=apps; a; a=a->next) {
02218       for (v=a->setvars;v;v=v->next) {
02219          if (strcmp(v->name,buff1) == 0) {
02220             break;
02221          }
02222       }
02223       if ( v )
02224          break;
02225    }
02226    if (v && v->vals) {
02227       /* we have a match, to a variable that has a set of determined values */
02228       int def= 0;
02229       int pat = 0;
02230       int f1 = 0;
02231       
02232       /* first of all, does this switch have a default case ? */
02233       for (t=item->u2.statements; t; t=t->next) {
02234          if (t->type == PV_DEFAULT) {
02235             def =1;
02236             break;
02237          }
02238          if (t->type == PV_PATTERN) {
02239             pat++;
02240          }
02241       }
02242       if (def || pat) /* nothing to check. All cases accounted for! */
02243          return;
02244       for (c=v->vals; c; c=c->next) {
02245          f1 = 0;
02246          for (t=item->u2.statements; t; t=t->next) {
02247             if (t->type == PV_CASE || t->type == PV_PATTERN) {
02248                if (!strcmp(t->u1.str,c->name)) {
02249                   f1 = 1;
02250                   break;
02251                }
02252             }
02253          }
02254          if (!f1) {
02255             ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: switch with expression(%s) does not handle the case of %s !\n",
02256                   item->filename, item->startline, item->endline, item->u1.str, c->name);
02257             warns++;
02258          }
02259       }
02260       /* next, is there an app call in the current exten, that would set this var? */
02261       f1 = 0;
02262       t = current_extension->u2.statements;
02263       if ( t && t->type == PV_STATEMENTBLOCK )
02264          t = t->u1.statements;
02265       for (; t && t != item; t=t->next) {
02266          if (t->type == PV_APPLICATION_CALL) {
02267             /* find the application that matches the u1.str */
02268             for (a2=apps; a2; a2=a2->next) {
02269                if (strcasecmp(a2->name, t->u1.str)==0) {
02270                   for (v2=a2->setvars; v2; v2=v2->next) {
02271                      if (strcmp(v2->name, buff1) == 0) {
02272                         /* found an app that sets the var */
02273                         f1 = 1;
02274                         break;
02275                      }
02276                   }
02277                }
02278                if (f1)
02279                   break;
02280             }
02281          }
02282          if (f1)
02283             break;
02284       }
02285             
02286       /* see if it sets the var */
02287       if (!f1) {
02288          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",
02289                item->filename, item->startline, item->endline, item->u1.str);
02290          warns++;
02291       }
02292    }
02293 #endif
02294 }
02295 
02296 static void check_context_names(void)
02297 {
02298    pval *i,*j;
02299    for (i=current_db; i; i=i->next) {
02300       if (i->type == PV_CONTEXT || i->type == PV_MACRO) {
02301          for (j=i->next; j; j=j->next) {
02302             if ( j->type == PV_CONTEXT || j->type == PV_MACRO ) {
02303                if ( !strcmp(i->u1.str, j->u1.str) )
02304                {
02305                   ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: The context name (%s) is also declared in file %s, line %d-%d!\n",
02306                         i->filename, i->startline, i->endline, i->u1.str,  j->filename, j->startline, j->endline);
02307                   warns++;
02308                }
02309             }
02310          }
02311       }
02312    }
02313 }
02314 
02315 static void check_abstract_reference(pval *abstract_context)
02316 {
02317    pval *i,*j;
02318    /* find some context includes that reference this context */
02319    
02320 
02321    /* otherwise, print out a warning */
02322    for (i=current_db; i; i=i->next) {
02323       if (i->type == PV_CONTEXT) {
02324          for (j=i->u2. statements; j; j=j->next) {
02325             if ( j->type == PV_INCLUDES ) {
02326                struct pval *p4;
02327                for (p4=j->u1.list; p4; p4=p4->next) {
02328                   /* for each context pointed to, find it, then find a context/label that matches the
02329                      target here! */
02330                   if ( !strcmp(p4->u1.str, abstract_context->u1.str) )
02331                      return; /* found a match! */
02332                }
02333             }
02334          }
02335       }
02336    }
02337    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",
02338          abstract_context->filename, abstract_context->startline, abstract_context->endline, abstract_context->u1.str);
02339    warns++;
02340 }
02341 
02342 
02343 void check_pval_item(pval *item, struct argapp *apps, int in_globals)
02344 {
02345    pval *lp;
02346 #ifdef AAL_ARGCHECK
02347    struct argapp *app, *found;
02348 #endif
02349    struct pval *macro_def;
02350    struct pval *app_def;
02351 
02352    char errmsg[4096];
02353    char *strp;
02354    
02355    switch (item->type) {
02356    case PV_WORD:
02357       /* fields: item->u1.str == string associated with this (word).
02358                  item->u2.arglist  == pval list of 4 PV_WORD elements for time values (only in PV_INCLUDES) */
02359       break;
02360       
02361    case PV_MACRO:
02362       /* fields: item->u1.str     == name of macro
02363                  item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
02364                item->u2.arglist->u1.str  == argument
02365                item->u2.arglist->next   == next arg
02366 
02367                item->u3.macro_statements == pval list of statements in macro body.
02368       */
02369       in_abstract_context = 0;
02370       current_context = item;
02371       current_extension = 0;
02372 
02373       check_macro_returns(item);
02374       
02375       for (lp=item->u2.arglist; lp; lp=lp->next) {
02376       
02377       }
02378       check_pval(item->u3.macro_statements, apps,in_globals);
02379       break;
02380          
02381    case PV_CONTEXT:
02382       /* fields: item->u1.str     == name of context
02383                  item->u2.statements == pval list of statements in context body
02384                item->u3.abstract == int 1 if an abstract keyword were present
02385       */
02386       current_context = item;
02387       current_extension = 0;
02388       if ( item->u3.abstract ) {
02389          in_abstract_context = 1;
02390          check_abstract_reference(item);
02391       } else
02392          in_abstract_context = 0;
02393       check_pval(item->u2.statements, apps,in_globals);
02394       break;
02395          
02396    case PV_MACRO_CALL:
02397       /* fields: item->u1.str     == name of macro to call
02398                  item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
02399                item->u2.arglist->u1.str  == argument
02400                item->u2.arglist->next   == next arg
02401       */
02402       macro_def = find_macro(item->u1.str);
02403       if (!macro_def) {
02404          ast_log(LOG_ERROR, "Error: file %s, line %d-%d: macro call to non-existent %s !\n",
02405                item->filename, item->startline, item->endline, item->u1.str);
02406          errs++;
02407       } else if (macro_def->type != PV_MACRO) {
02408          ast_log(LOG_ERROR,"Error: file %s, line %d-%d: macro call to %s references a context, not a macro!\n",
02409                item->filename, item->startline, item->endline, item->u1.str);
02410          errs++;
02411       } else {
02412          /* macro_def is a MACRO, so do the args match in number? */
02413          int hereargs = 0;
02414          int thereargs = 0;
02415          
02416          for (lp=item->u2.arglist; lp; lp=lp->next) {
02417             hereargs++;
02418          }
02419          for (lp=macro_def->u2.arglist; lp; lp=lp->next) {
02420             thereargs++;
02421          }
02422          if (hereargs != thereargs ) {
02423             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",
02424                   item->filename, item->startline, item->endline, item->u1.str, hereargs, thereargs);
02425             errs++;
02426          }
02427       }
02428       break;
02429          
02430    case PV_APPLICATION_CALL:
02431       /* fields: item->u1.str     == name of application to call
02432                  item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
02433                item->u2.arglist->u1.str  == argument
02434                item->u2.arglist->next   == next arg
02435       */
02436       /* Need to check to see if the application is available! */
02437       app_def = find_context(item->u1.str);
02438       if (app_def && app_def->type == PV_MACRO) {
02439          ast_log(LOG_ERROR,"Error: file %s, line %d-%d: application call to %s references an existing macro, but had no & preceding it!\n",
02440                item->filename, item->startline, item->endline, item->u1.str);
02441          errs++;
02442       }
02443       if (strcasecmp(item->u1.str,"GotoIf") == 0
02444          || strcasecmp(item->u1.str,"GotoIfTime") == 0
02445          || strcasecmp(item->u1.str,"while") == 0
02446          || strcasecmp(item->u1.str,"endwhile") == 0
02447          || strcasecmp(item->u1.str,"random") == 0
02448          || strcasecmp(item->u1.str,"gosub") == 0
02449          || strcasecmp(item->u1.str,"return") == 0
02450          || strcasecmp(item->u1.str,"gosubif") == 0
02451          || strcasecmp(item->u1.str,"continuewhile") == 0
02452          || strcasecmp(item->u1.str,"endwhile") == 0
02453          || strcasecmp(item->u1.str,"execif") == 0
02454          || strcasecmp(item->u1.str,"execiftime") == 0
02455          || strcasecmp(item->u1.str,"exitwhile") == 0
02456          || strcasecmp(item->u1.str,"goto") == 0
02457          || strcasecmp(item->u1.str,"macro") == 0
02458          || strcasecmp(item->u1.str,"macroexclusive") == 0
02459          || strcasecmp(item->u1.str,"macroif") == 0
02460          || strcasecmp(item->u1.str,"stackpop") == 0
02461          || strcasecmp(item->u1.str,"execIf") == 0 ) {
02462          ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: application call to %s affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead!\n",
02463                item->filename, item->startline, item->endline, item->u1.str);
02464          warns++;
02465       }
02466       if (strcasecmp(item->u1.str,"macroexit") == 0) {
02467             ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: I am converting the MacroExit call here to a return statement.\n",
02468                   item->filename, item->startline, item->endline);
02469             item->type = PV_RETURN;
02470             free(item->u1.str);
02471             item->u1.str = 0;
02472       }
02473       
02474 #ifdef AAL_ARGCHECK
02475       found = 0;
02476       for (app=apps; app; app=app->next) {
02477          if (strcasecmp(app->name, item->u1.str) == 0) {
02478             found =app;
02479             break;
02480          }
02481       }
02482       if (!found) {
02483          ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: application call to %s not listed in applist database!\n",
02484                item->filename, item->startline, item->endline, item->u1.str);
02485          warns++;
02486       } else
02487          check_app_args(item, item->u2.arglist, app);
02488 #endif
02489       break;
02490       
02491    case PV_CASE:
02492       /* fields: item->u1.str     == value of case
02493                  item->u2.statements == pval list of statements under the case
02494       */
02495       /* Make sure sequence of statements under case is terminated with  goto, return, or break */
02496       /* find the last statement */
02497       check_pval(item->u2.statements, apps,in_globals);
02498       break;
02499          
02500    case PV_PATTERN:
02501       /* fields: item->u1.str     == value of case
02502                  item->u2.statements == pval list of statements under the case
02503       */
02504       /* Make sure sequence of statements under case is terminated with  goto, return, or break */
02505       /* find the last statement */
02506       
02507       check_pval(item->u2.statements, apps,in_globals);
02508       break;
02509          
02510    case PV_DEFAULT:
02511       /* fields: 
02512                  item->u2.statements == pval list of statements under the case
02513       */
02514 
02515       check_pval(item->u2.statements, apps,in_globals);
02516       break;
02517          
02518    case PV_CATCH:
02519       /* fields: item->u1.str     == name of extension to catch
02520                  item->u2.statements == pval list of statements in context body
02521       */
02522       check_pval(item->u2.statements, apps,in_globals);
02523       break;
02524          
02525    case PV_SWITCHES:
02526       /* fields: item->u1.list     == pval list of PV_WORD elements, one per entry in the list
02527       */
02528       check_pval(item->u1.list, apps,in_globals);
02529       break;
02530          
02531    case PV_ESWITCHES:
02532       /* fields: item->u1.list     == pval list of PV_WORD elements, one per entry in the list
02533       */
02534       check_pval(item->u1.list, apps,in_globals);
02535       break;
02536          
02537    case PV_INCLUDES:
02538       /* fields: item->u1.list     == pval list of PV_WORD elements, one per entry in the list
02539       */
02540       check_pval(item->u1.list, apps,in_globals);
02541       check_includes(item);
02542       for (lp=item->u1.list; lp; lp=lp->next){
02543          char *incl_context = lp->u1.str;
02544          struct pval *that_context = find_context(incl_context);
02545 
02546          if ( lp->u2.arglist ) {
02547             check_timerange(lp->u2.arglist);
02548             check_dow(lp->u2.arglist->next);
02549             check_day(lp->u2.arglist->next->next);
02550             check_month(lp->u2.arglist->next->next->next);
02551          }
02552          
02553          if (that_context) {
02554             find_pval_gotos(that_context->u2.statements,0);
02555             
02556          }
02557       }
02558       break;
02559          
02560    case PV_STATEMENTBLOCK:
02561       /* fields: item->u1.list     == pval list of statements in block, one per entry in the list
02562       */
02563       check_pval(item->u1.list, apps,in_globals);
02564       break;
02565          
02566    case PV_VARDEC:
02567       /* fields: item->u1.str     == variable name
02568                  item->u2.val     == variable value to assign
02569       */
02570       /* the RHS of a vardec is encapsulated in a $[] expr. Is it legal? */
02571       if( !in_globals ) { /* don't check stuff inside the globals context; no wrapping in $[ ] there... */
02572          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);
02573          ast_expr_register_extra_error_info(errmsg);
02574          ast_expr(item->u2.val, expr_output, sizeof(expr_output));
02575          ast_expr_clear_extra_error_info();
02576          if ( strpbrk(item->u2.val,"~!-+<>=*/&^") && !strstr(item->u2.val,"${") ) {
02577             ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n",
02578                   item->filename, item->startline, item->endline, item->u2.val);
02579             warns++;
02580          }
02581          check_expr2_input(item,item->u2.val);
02582       }
02583       break;
02584          
02585    case PV_GOTO:
02586       /* fields: item->u1.list     == pval list of PV_WORD target names, up to 3, in order as given by user.
02587                  item->u1.list->u1.str  == where the data on a PV_WORD will always be.
02588       */
02589       /* don't check goto's in abstract contexts */
02590       if ( in_abstract_context )
02591          break;
02592       
02593       check_goto(item);
02594       break;
02595          
02596    case PV_LABEL:
02597       /* fields: item->u1.str     == label name
02598       */
02599       if ( strspn(item->u1.str, "0123456789") == strlen(item->u1.str) ) {
02600          ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: label '%s' is numeric, this is bad practice!\n",
02601                item->filename, item->startline, item->endline, item->u1.str);
02602          warns++;
02603       }
02604 
02605       check_label(item);
02606       break;
02607          
02608    case PV_FOR:
02609       /* fields: item->u1.for_init     == a string containing the initalizer
02610                  item->u2.for_test     == a string containing the loop test
02611                  item->u3.for_inc      == a string containing the loop increment
02612 
02613                item->u4.for_statements == a pval list of statements in the for ()
02614       */
02615       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);
02616       ast_expr_register_extra_error_info(errmsg);
02617 
02618       strp = strchr(item->u1.for_init, '=');
02619       if (strp) {
02620          ast_expr(strp+1, expr_output, sizeof(expr_output));
02621       }
02622       ast_expr(item->u2.for_test, expr_output, sizeof(expr_output));
02623       strp = strchr(item->u3.for_inc, '=');
02624       if (strp) {
02625          ast_expr(strp+1, expr_output, sizeof(expr_output));
02626       }
02627       if ( strpbrk(item->u2.for_test,"~!-+<>=*/&^") && !strstr(item->u2.for_test,"${") ) {
02628          ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n",
02629                item->filename, item->startline, item->endline, item->u2.for_test);
02630          warns++;
02631       }
02632       if ( strpbrk(item->u3.for_inc,"~!-+<>=*/&^") && !strstr(item->u3.for_inc,"${") ) {
02633          ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n",
02634                item->filename, item->startline, item->endline, item->u3.for_inc);
02635          warns++;
02636       }
02637       check_expr2_input(item,item->u2.for_test);
02638       check_expr2_input(item,item->u3.for_inc);
02639       
02640       ast_expr_clear_extra_error_info();
02641       check_pval(item->u4.for_statements, apps,in_globals);
02642       break;
02643          
02644    case PV_WHILE:
02645       /* fields: item->u1.str        == the while conditional, as supplied by user
02646 
02647                item->u2.statements == a pval list of statements in the while ()
02648       */
02649       snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, while expr '%s':", config, item->startline, item->startcol, item->endcol, item->u1.str);
02650       ast_expr_register_extra_error_info(errmsg);
02651       ast_expr(item->u1.str, expr_output, sizeof(expr_output));
02652       ast_expr_clear_extra_error_info();
02653       if ( strpbrk(item->u1.str,"~!-+<>=*/&^") && !strstr(item->u1.str,"${") ) {
02654          ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n",
02655                item->filename, item->startline, item->endline, item->u1.str);
02656          warns++;
02657       }
02658       check_expr2_input(item,item->u1.str);
02659       check_pval(item->u2.statements, apps,in_globals);
02660       break;
02661          
02662    case PV_BREAK:
02663       /* fields: none
02664       */
02665       check_break(item);
02666       break;
02667          
02668    case PV_RETURN:
02669       /* fields: none
02670       */
02671       break;
02672          
02673    case PV_CONTINUE:
02674       /* fields: none
02675       */
02676       check_continue(item);
02677       break;
02678          
02679    case PV_RANDOM:
02680       /* fields: item->u1.str        == the random number expression, as supplied by user
02681 
02682                item->u2.statements == a pval list of statements in the if ()
02683                item->u3.else_statements == a pval list of statements in the else
02684                                     (could be zero)
02685       */
02686       snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, random expr '%s':", config, item->startline, item->startcol, item->endcol, item->u1.str);
02687       ast_expr_register_extra_error_info(errmsg);
02688       ast_expr(item->u1.str, expr_output, sizeof(expr_output));
02689       ast_expr_clear_extra_error_info();
02690       if ( strpbrk(item->u1.str,"~!-+<>=*/&^") && !strstr(item->u1.str,"${") ) {
02691          ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: random expression '%s' has operators, but no variables. Interesting...\n",
02692                item->filename, item->startline, item->endline, item->u1.str);
02693          warns++;
02694       }
02695       check_expr2_input(item,item->u1.str);
02696       check_pval(item->u2.statements, apps,in_globals);
02697       if (item->u3.else_statements) {
02698          check_pval(item->u3.else_statements, apps,in_globals);
02699       }
02700       break;
02701 
02702    case PV_IFTIME:
02703       /* fields: item->u1.list        == the if time values, 4 of them, each in PV_WORD, linked list 
02704 
02705                item->u2.statements == a pval list of statements in the if ()
02706                item->u3.else_statements == a pval list of statements in the else
02707                                     (could be zero)
02708       */
02709       if ( item->u2.arglist ) {
02710          check_timerange(item->u1.list);
02711          check_dow(item->u1.list->next);
02712          check_day(item->u1.list->next->next);
02713          check_month(item->u1.list->next->next->next);
02714       }
02715 
02716       check_pval(item->u2.statements, apps,in_globals);
02717       if (item->u3.else_statements) {
02718          check_pval(item->u3.else_statements, apps,in_globals);
02719       }
02720       break;
02721          
02722    case PV_IF:
02723       /* fields: item->u1.str        == the if conditional, as supplied by user
02724 
02725                item->u2.statements == a pval list of statements in the if ()
02726                item->u3.else_statements == a pval list of statements in the else
02727                                     (could be zero)
02728       */
02729       snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, if expr '%s':", config, item->startline, item->startcol, item->endcol, item->u1.str);
02730       ast_expr_register_extra_error_info(errmsg);
02731       ast_expr(item->u1.str, expr_output, sizeof(expr_output));
02732       ast_expr_clear_extra_error_info();
02733       if ( strpbrk(item->u1.str,"~!-+<>=*/&^") && !strstr(item->u1.str,"${") ) {
02734          ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression '%s' has operators, but no variables. Interesting...\n",
02735                item->filename, item->startline, item->endline, item->u1.str);
02736          warns++;
02737       }
02738       check_expr2_input(item,item->u1.str);
02739       check_pval(item->u2.statements, apps,in_globals);
02740       if (item->u3.else_statements) {
02741          check_pval(item->u3.else_statements, apps,in_globals);
02742       }
02743       break;
02744          
02745    case PV_SWITCH:
02746       /* fields: item->u1.str        == the switch expression
02747 
02748                item->u2.statements == a pval list of statements in the switch, 
02749                                     (will be case statements, most likely!)
02750       */
02751       /* we can check the switch expression, see if it matches any of the app variables...
02752            if it does, then, are all the possible cases accounted for? */
02753       check_switch_expr(item, apps);
02754       check_pval(item->u2.statements, apps,in_globals);
02755       break;
02756          
02757    case PV_EXTENSION:
02758       /* fields: item->u1.str        == the extension name, label, whatever it's called
02759 
02760                item->u2.statements == a pval list of statements in the extension
02761                item->u3.hints      == a char * hint argument
02762                item->u4.regexten   == an int boolean. non-zero says that regexten was specified
02763       */
02764       current_extension = item ;
02765       
02766       check_pval(item->u2.statements, apps,in_globals);
02767       break;
02768          
02769    case PV_IGNOREPAT:
02770       /* fields: item->u1.str        == the ignorepat data
02771       */
02772       break;
02773          
02774    case PV_GLOBALS:
02775       /* fields: item->u1.statements     == pval list of statements, usually vardecs
02776       */
02777       in_abstract_context = 0;
02778       check_pval(item->u1.statements, apps, 1);
02779       break;
02780    default:
02781       break;
02782    }
02783 }
02784 
02785 void check_pval(pval *item, struct argapp *apps, int in_globals)
02786 {
02787    pval *i;
02788 
02789    /* checks to do:
02790       1. Do goto's point to actual labels? 
02791       2. Do macro calls reference a macro?
02792       3. Does the number of macro args match the definition?
02793       4. Is a macro call missing its & at the front?
02794       5. Application calls-- we could check syntax for existing applications,
02795          but I need some some sort of universal description bnf for a general
02796         sort of method for checking arguments, in number, maybe even type, at least. 
02797         Don't want to hand code checks for hundreds of applications.
02798    */
02799    
02800    for (i=item; i; i=i->next) {
02801       check_pval_item(i,apps,in_globals);
02802    }
02803 }
02804 
02805 static void ael2_semantic_check(pval *item, int *arg_errs, int *arg_warns, int *arg_notes)
02806 {
02807    
02808 #ifdef AAL_ARGCHECK
02809    int argapp_errs =0;
02810    char *rfilename;
02811 #endif
02812    struct argapp *apps=0;
02813 
02814 #ifdef AAL_ARGCHECK
02815    rfilename = alloca(10 + strlen(ast_config_AST_VAR_DIR));
02816    sprintf(rfilename, "%s/applist", ast_config_AST_VAR_DIR);
02817    
02818    apps = argdesc_parse(rfilename, &argapp_errs); /* giveth */
02819 #endif
02820    current_db = item;
02821    errs = warns = notes = 0;
02822 
02823    check_context_names();
02824    check_pval(item, apps, 0);
02825 
02826 #ifdef AAL_ARGCHECK
02827    argdesc_destroy(apps);  /* taketh away */
02828 #endif
02829    current_db = 0;
02830 
02831    *arg_errs = errs;
02832    *arg_warns = warns;
02833    *arg_notes = notes;
02834 }
02835 
02836 /* =============================================================================================== */
02837 /* "CODE" GENERATOR -- Convert the AEL representation to asterisk extension language */
02838 /* =============================================================================================== */
02839 
02840 static int control_statement_count = 0;
02841 
02842 struct ael_priority *new_prio(void)
02843 {
02844    struct ael_priority *x = (struct ael_priority *)calloc(sizeof(struct ael_priority),1);
02845    return x;
02846 }
02847 
02848 struct ael_extension *new_exten(void)
02849 {
02850    struct ael_extension *x = (struct ael_extension *)calloc(sizeof(struct ael_extension),1);
02851    return x;
02852 }
02853 
02854 void linkprio(struct ael_extension *exten, struct ael_priority *prio)
02855 {
02856    if (!exten->plist) {
02857       exten->plist = prio;
02858       exten->plist_last = prio;
02859    } else {
02860       exten->plist_last->next = prio;
02861       exten->plist_last = prio;
02862    }
02863    if( !prio->exten )
02864       prio->exten = exten; /* don't override the switch value */
02865 }
02866 
02867 void destroy_extensions(struct ael_extension *exten)
02868 {
02869    struct ael_extension *ne, *nen;
02870    for (ne=exten; ne; ne=nen) {
02871       struct ael_priority *pe, *pen;
02872       
02873       if (ne->name)
02874          free(ne->name);
02875       
02876       /* cidmatch fields are allocated with name, and freed when
02877          the name field is freed. Don't do a free for this field,
02878          unless you LIKE to see a crash! */
02879 
02880       if (ne->hints)
02881          free(ne->hints);
02882       
02883       for (pe=ne->plist; pe; pe=pen) {
02884          pen = pe->next;
02885          if (pe->app)
02886             free(pe->app);
02887          pe->app = 0;
02888          if (pe->appargs)
02889             free(pe->appargs);
02890          pe->appargs = 0;
02891          pe->origin = 0;
02892          pe->goto_true = 0;
02893          pe->goto_false = 0;
02894          free(pe);
02895       }
02896       nen = ne->next_exten;
02897       ne->next_exten = 0;
02898       ne->plist =0;
02899       ne->plist_last = 0;
02900       ne->next_exten = 0;
02901       ne->loop_break = 0;
02902       ne->loop_continue = 0;
02903       free(ne);
02904    }
02905 }
02906 
02907 static int label_inside_case(pval *label)
02908 {
02909    pval *p = label;
02910    
02911    while( p && p->type != PV_MACRO && p->type != PV_CONTEXT ) /* early cutout, sort of */ {
02912       if( p->type == PV_CASE || p->type == PV_DEFAULT || p->type == PV_PATTERN ) {
02913          return 1;
02914       }
02915 
02916       p = p->dad;
02917    }
02918    return 0;
02919 }
02920 
02921 static void linkexten(struct ael_extension *exten, struct ael_extension *add)
02922 {
02923    add->next_exten = exten->next_exten; /* this will reverse the order. Big deal. */
02924    exten->next_exten = add;
02925 }
02926 
02927 static void remove_spaces_before_equals(char *str)
02928 {
02929    char *p;
02930    while( str && *str && *str != '=' )
02931    {
02932       if( *str == ' ' || *str == '\n' || *str == '\r' || *str == '\t' )
02933       {
02934          p = str;
02935          while( *p )
02936          {
02937             *p = *(p+1);
02938             p++;
02939          }
02940       }
02941       else
02942          str++;
02943    }
02944 }
02945 
02946 static void gen_match_to_pattern(char *pattern, char *result)
02947 {
02948    /* the result will be a string that will be matched by pattern */
02949    char *p=pattern, *t=result;
02950    while (*p) {
02951       if (*p == 'x' || *p == 'n' || *p == 'z' || *p == 'X' || *p == 'N' || *p == 'Z')
02952          *t++ = '9';
02953       else if (*p == '[') {
02954          char *z = p+1;
02955          while (*z != ']')
02956             z++;
02957          if (*(z+1)== ']')
02958             z++;
02959          *t++=*(p+1); /* use the first char in the set */
02960          p = z;
02961       } else {
02962          *t++ = *p;
02963       }
02964       p++;
02965    }
02966    *t++ = 0; /* cap it off */
02967 }
02968 
02969 static void gen_prios(struct ael_extension *exten, char *label, pval *statement, struct ael_extension *mother_exten, struct ast_context *this_context )
02970 {
02971    pval *p,*p2,*p3;
02972    struct ael_priority *pr;
02973    struct ael_priority *for_init, *for_test, *for_inc, *for_loop, *for_end;
02974    struct ael_priority *while_test, *while_loop, *while_end;
02975    struct ael_priority *switch_test, *switch_end, *fall_thru;
02976    struct ael_priority *if_test, *if_end, *if_skip, *if_false;
02977 #ifdef OLD_RAND_ACTION
02978    struct ael_priority *rand_test, *rand_end, *rand_skip;
02979 #endif
02980    char buf1[2000];
02981    char buf2[2000];
02982    char *strp, *strp2;
02983    char new_label[2000];
02984    int default_exists;
02985    int local_control_statement_count;
02986    int first;
02987    struct ael_priority *loop_break_save;
02988    struct ael_priority *loop_continue_save;
02989    struct ael_extension *switch_case;
02990    
02991    for (p=statement; p; p=p->next) {
02992       switch (p->type) {
02993       case PV_VARDEC:
02994          pr = new_prio();
02995          pr->type = AEL_APPCALL;
02996          snprintf(buf1,sizeof(buf1),"%s=$[%s]", p->u1.str, p->u2.val);
02997          pr->app = strdup("Set");
02998          remove_spaces_before_equals(buf1);
02999          pr->appargs = strdup(buf1);
03000          pr->origin = p;
03001          linkprio(exten, pr);
03002          break;
03003 
03004       case PV_GOTO:
03005          pr = new_prio();
03006          pr->type = AEL_APPCALL;
03007          p->u2.goto_target = get_goto_target(p);
03008          if( p->u2.goto_target ) {
03009             p->u3.goto_target_in_case = p->u2.goto_target->u2.label_in_case = label_inside_case(p->u2.goto_target);
03010          }
03011          
03012          if (!p->u1.list->next) /* just one */ {
03013             pr->app = strdup("Goto");
03014             if (!mother_exten)
03015                pr->appargs = strdup(p->u1.list->u1.str);
03016             else {  /* for the case of simple within-extension gotos in case/pattern/default statement blocks: */ 
03017                snprintf(buf1,sizeof(buf1),"%s|%s", mother_exten->name, p->u1.list->u1.str);
03018                pr->appargs = strdup(buf1);
03019             }
03020             
03021          } else if (p->u1.list->next && !p->u1.list->next->next) /* two */ {
03022             snprintf(buf1,sizeof(buf1),"%s|%s", p->u1.list->u1.str, p->u1.list->next->u1.str);
03023             pr->app = strdup("Goto");
03024             pr->appargs = strdup(buf1);
03025          } else if (p->u1.list->next && p->u1.list->next->next) {
03026             snprintf(buf1,sizeof(buf1),"%s|%s|%s", p->u1.list->u1.str, 
03027                   p->u1.list->next->u1.str,
03028                   p->u1.list->next->next->u1.str);
03029             pr->app = strdup("Goto");
03030             pr->appargs = strdup(buf1);
03031          }
03032          pr->origin = p;
03033          linkprio(exten, pr);
03034          break;
03035 
03036       case PV_LABEL:
03037          pr = new_prio();
03038          pr->type = AEL_LABEL;
03039          pr->origin = p;
03040          p->u3.compiled_label = exten;
03041          linkprio(exten, pr);
03042          break;
03043 
03044       case PV_FOR:
03045          control_statement_count++;
03046          loop_break_save = exten->loop_break; /* save them, then restore before leaving */
03047          loop_continue_save = exten->loop_continue;
03048          snprintf(new_label,sizeof(new_label),"for-%s-%d", label, control_statement_count);
03049          for_init = new_prio();
03050          for_inc = new_prio();
03051          for_test = new_prio();
03052          for_loop = new_prio();
03053          for_end = new_prio();
03054          for_init->type = AEL_APPCALL;
03055          for_inc->type = AEL_APPCALL;
03056          for_test->type = AEL_FOR_CONTROL;
03057          for_test->goto_false = for_end;
03058          for_loop->type = AEL_CONTROL1; /* simple goto */
03059          for_end->type = AEL_APPCALL;
03060          for_init->app = strdup("Set");
03061          
03062          strcpy(buf2,p->u1.for_init);
03063          remove_spaces_before_equals(buf2);
03064          strp = strchr(buf2, '=');
03065          strp2 = strchr(p->u1.for_init, '=');
03066          if (strp) {
03067             *(strp+1) = 0;
03068             strcat(buf2,"$[");
03069             strncat(buf2,strp2+1, sizeof(buf2)-strlen(strp2+1)-2);
03070             strcat(buf2,"]");
03071             for_init->appargs = strdup(buf2);
03072          } else
03073             for_init->appargs = strdup(p->u1.for_init);
03074 
03075          for_inc->app = strdup("Set");
03076 
03077          strcpy(buf2,p->u3.for_inc);
03078          remove_spaces_before_equals(buf2);
03079          strp = strchr(buf2, '=');
03080          strp2 = strchr(p->u3.for_inc, '=');
03081          if (strp) {
03082             *(strp+1) = 0;
03083             strcat(buf2,"$[");
03084             strncat(buf2,strp2+1, sizeof(buf2)-strlen(strp2+1)-2);
03085             strcat(buf2,"]");
03086             for_inc->appargs = strdup(buf2);
03087          } else
03088             for_inc->appargs = strdup(p->u3.for_inc);
03089          snprintf(buf1,sizeof(buf1),"$[%s]",p->u2.for_test);
03090          for_test->app = 0;
03091          for_test->appargs = strdup(buf1);
03092          for_loop->goto_true = for_test;
03093          snprintf(buf1,sizeof(buf1),"Finish for-%s-%d", label, control_statement_count);
03094          for_end->app = strdup("NoOp");
03095          for_end->appargs = strdup(buf1);
03096          /* link & load! */
03097          linkprio(exten, for_init);
03098          linkprio(exten, for_test);
03099          
03100          /* now, put the body of the for loop here */
03101          exten->loop_break = for_end;
03102          exten->loop_continue = for_test;
03103          
03104          gen_prios(exten, new_label, p->u4.for_statements, mother_exten, this_context); /* this will link in all the statements here */
03105          
03106          linkprio(exten, for_inc);
03107          linkprio(exten, for_loop);
03108          linkprio(exten, for_end);
03109          
03110          
03111          exten->loop_break = loop_break_save;
03112          exten->loop_continue = loop_continue_save;
03113          for_loop->origin = p;
03114          break;
03115 
03116       case PV_WHILE:
03117          control_statement_count++;
03118          loop_break_save = exten->loop_break; /* save them, then restore before leaving */
03119          loop_continue_save = exten->loop_continue;
03120          snprintf(new_label,sizeof(new_label),"while-%s-%d", label, control_statement_count);
03121          while_test = new_prio();
03122          while_loop = new_prio();
03123          while_end = new_prio();
03124          while_test->type = AEL_FOR_CONTROL;
03125          while_test->goto_false = while_end;
03126          while_loop->type = AEL_CONTROL1; /* simple goto */
03127          while_end->type = AEL_APPCALL;
03128          snprintf(buf1,sizeof(buf1),"$[%s]",p->u1.str);
03129          while_test->app = 0;
03130          while_test->appargs = strdup(buf1);
03131          while_loop->goto_true = while_test;
03132          snprintf(buf1,sizeof(buf1),"Finish while-%s-%d", label, control_statement_count);
03133          while_end->app = strdup("NoOp");
03134          while_end->appargs = strdup(buf1);
03135 
03136          linkprio(exten, while_test);
03137          
03138          /* now, put the body of the for loop here */
03139          exten->loop_break = while_end;
03140          exten->loop_continue = while_test;
03141          
03142          gen_prios(exten, new_label, p->u2.statements, mother_exten, this_context); /* this will link in all the while body statements here */
03143 
03144          linkprio(exten, while_loop);
03145          linkprio(exten, while_end);
03146          
03147          
03148          exten->loop_break = loop_break_save;
03149          exten->loop_continue = loop_continue_save;
03150          while_loop->origin = p;
03151          break;
03152 
03153       case PV_SWITCH:
03154          control_statement_count++;
03155          local_control_statement_count = control_statement_count;
03156          loop_break_save = exten->loop_break; /* save them, then restore before leaving */
03157          loop_continue_save = exten->loop_continue;
03158          snprintf(new_label,sizeof(new_label),"sw-%s-%d", label, control_statement_count);
03159 
03160          switch_test = new_prio();
03161          switch_end = new_prio();
03162          switch_test->type = AEL_APPCALL;
03163          switch_end->type = AEL_APPCALL;
03164          snprintf(buf1,sizeof(buf1),"sw-%d-%s|1",control_statement_count, p->u1.str);
03165          switch_test->app = strdup("Goto");
03166          switch_test->appargs = strdup(buf1);
03167          snprintf(buf1,sizeof(buf1),"Finish switch-%s-%d", label, control_statement_count);
03168          switch_end->app = strdup("NoOp");
03169          switch_end->appargs = strdup(buf1);
03170          switch_end->origin = p;
03171          switch_end->exten = exten;
03172 
03173          linkprio(exten, switch_test);
03174          linkprio(exten, switch_end);
03175          
03176          exten->loop_break = switch_end;
03177          exten->loop_continue = 0;
03178          default_exists = 0;
03179          
03180          for (p2=p->u2.statements; p2; p2=p2->next) {
03181             /* now, for each case/default put the body of the for loop here */
03182             if (p2->type == PV_CASE) {
03183                /* ok, generate a extension and link it in */
03184                switch_case = new_exten();
03185                switch_case->context = this_context;
03186                /* the break/continue locations are inherited from parent */
03187                switch_case->loop_break = exten->loop_break;
03188                switch_case->loop_continue = exten->loop_continue;
03189                
03190                linkexten(exten,switch_case);
03191                snprintf(buf1,sizeof(buf1),"sw-%d-%s", local_control_statement_count, p2->u1.str);
03192                switch_case->name = strdup(buf1);
03193                snprintf(new_label,sizeof(new_label),"sw-%s-%s-%d", label, p2->u1.str, local_control_statement_count);
03194                
03195                gen_prios(switch_case, new_label, p2->u2.statements, exten, this_context); /* this will link in all the case body statements here */
03196 
03197                /* here is where we write code to "fall thru" to the next case... if there is one... */
03198                for (p3=p2->u2.statements; p3; p3=p3->next) {
03199                   if (!p3->next)
03200                      break;
03201                }
03202                /* p3 now points the last statement... */
03203                if (!p3 || ( p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN) ) {
03204                   /* is there a following CASE/PATTERN/DEFAULT? */
03205                   if (p2->next && p2->next->type == PV_CASE) {
03206                      fall_thru = new_prio();
03207                      fall_thru->type = AEL_APPCALL;
03208                      fall_thru->app = strdup("Goto");
03209                      snprintf(buf1,sizeof(buf1),"sw-%d-%s|1",local_control_statement_count, p2->next->u1.str);
03210                      fall_thru->appargs = strdup(buf1);
03211                      linkprio(switch_case, fall_thru);
03212                   } else if (p2->next && p2->next->type == PV_PATTERN) {
03213                      fall_thru = new_prio();
03214                      fall_thru->type = AEL_APPCALL;
03215                      fall_thru->app = strdup("Goto");
03216                      gen_match_to_pattern(p2->next->u1.str, buf2);
03217                      snprintf(buf1,sizeof(buf1),"sw-%d-%s|1", local_control_statement_count, buf2);
03218                      fall_thru->appargs = strdup(buf1);
03219                      linkprio(switch_case, fall_thru);
03220                   } else if (p2->next && p2->next->type == PV_DEFAULT) {
03221                      fall_thru = new_prio();
03222                      fall_thru->type = AEL_APPCALL;
03223                      fall_thru->app = strdup("Goto");
03224                      snprintf(buf1,sizeof(buf1),"sw-%d-.|1",local_control_statement_count);
03225                      fall_thru->appargs = strdup(buf1);
03226                      linkprio(switch_case, fall_thru);
03227                   } else if (!p2->next) {
03228                      fall_thru = new_prio();
03229                      fall_thru->type = AEL_CONTROL1;
03230                      fall_thru->goto_true = switch_end;
03231                      fall_thru->app = strdup("Goto");
03232                      linkprio(switch_case, fall_thru);
03233                   }
03234                }
03235                if (switch_case->return_needed) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */
03236                   char buf[2000];
03237                   struct ael_priority *np2 = new_prio();
03238                   np2->type = AEL_APPCALL;
03239                   np2->app = strdup("NoOp");
03240                   snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name);
03241                   np2->appargs = strdup(buf);
03242                   linkprio(switch_case, np2);
03243                   switch_case-> return_target = np2;
03244                }
03245             } else if (p2->type == PV_PATTERN) {
03246                /* ok, generate a extension and link it in */
03247                switch_case = new_exten();
03248                switch_case->context = this_context;
03249                /* the break/continue locations are inherited from parent */
03250                switch_case->loop_break = exten->loop_break;
03251                switch_case->loop_continue = exten->loop_continue;
03252                
03253                linkexten(exten,switch_case);
03254                snprintf(buf1,sizeof(buf1),"_sw-%d-%s", local_control_statement_count, p2->u1.str);
03255                switch_case->name = strdup(buf1);
03256                snprintf(new_label,sizeof(new_label),"sw-%s-%s-%d", label, p2->u1.str, local_control_statement_count);
03257                
03258                gen_prios(switch_case, new_label, p2->u2.statements, exten, this_context); /* this will link in all the while body statements here */
03259                /* here is where we write code to "fall thru" to the next case... if there is one... */
03260                for (p3=p2->u2.statements; p3; p3=p3->next) {
03261                   if (!p3->next)
03262                      break;
03263                }
03264                /* p3 now points the last statement... */
03265                if (!p3 || ( p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN)) {
03266                   /* is there a following CASE/PATTERN/DEFAULT? */
03267                   if (p2->next && p2->next->type == PV_CASE) {
03268                      fall_thru = new_prio();
03269                      fall_thru->type = AEL_APPCALL;
03270                      fall_thru->app = strdup("Goto");
03271                      snprintf(buf1,sizeof(buf1),"sw-%d-%s|1",local_control_statement_count, p2->next->u1.str);
03272                      fall_thru->appargs = strdup(buf1);
03273                      linkprio(switch_case, fall_thru);
03274                   } else if (p2->next && p2->next->type == PV_PATTERN) {
03275                      fall_thru = new_prio();
03276                      fall_thru->type = AEL_APPCALL;
03277                      fall_thru->app = strdup("Goto");
03278                      gen_match_to_pattern(p2->next->u1.str, buf2);
03279                      snprintf(buf1,sizeof(buf1),"sw-%d-%s|1",local_control_statement_count, buf2);
03280                      fall_thru->appargs = strdup(buf1);
03281                      linkprio(switch_case, fall_thru);
03282                   } else if (p2->next && p2->next->type == PV_DEFAULT) {
03283                      fall_thru = new_prio();
03284                      fall_thru->type = AEL_APPCALL;
03285                      fall_thru->app = strdup("Goto");
03286                      snprintf(buf1,sizeof(buf1),"sw-%d-.|1",local_control_statement_count);
03287                      fall_thru->appargs = strdup(buf1);
03288                      linkprio(switch_case, fall_thru);
03289                   } else if (!p2->next) {
03290                      fall_thru = new_prio();
03291                      fall_thru->type = AEL_CONTROL1;
03292                      fall_thru->goto_true = switch_end;
03293                      fall_thru->app = strdup("Goto");
03294                      linkprio(switch_case, fall_thru);
03295                   }
03296                }
03297                if (switch_case->return_needed) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */
03298                   char buf[2000];
03299                   struct ael_priority *np2 = new_prio();
03300                   np2->type = AEL_APPCALL;
03301                   np2->app = strdup("NoOp");
03302                   snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name);
03303                   np2->appargs = strdup(buf);
03304                   linkprio(switch_case, np2);
03305                   switch_case-> return_target = np2;
03306                }
03307             } else if (p2->type == PV_DEFAULT) {
03308                default_exists++;
03309                /* ok, generate a extension and link it in */
03310                switch_case = new_exten();
03311                switch_case->context = this_context;
03312                /* the break/continue locations are inherited from parent */
03313                switch_case->loop_break = exten->loop_break;
03314                switch_case->loop_continue = exten->loop_continue;
03315                linkexten(exten,switch_case);
03316                snprintf(buf1,sizeof(buf1),"_sw-%d-.", local_control_statement_count);
03317                switch_case->name = strdup(buf1);
03318                
03319                snprintf(new_label,sizeof(new_label),"sw-%s-default-%d", label, local_control_statement_count);
03320                
03321                gen_prios(switch_case, new_label, p2->u2.statements, exten, this_context); /* this will link in all the while body statements here */
03322                
03323                /* here is where we write code to "fall thru" to the next case... if there is one... */
03324                for (p3=p2->u2.statements; p3; p3=p3->next) {
03325                   if (!p3->next)
03326                      break;
03327                }
03328                /* p3 now points the last statement... */
03329                if (!p3 || (p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN)) {
03330                   /* is there a following CASE/PATTERN/DEFAULT? */
03331                   if (p2->next && p2->next->type == PV_CASE) {
03332                      fall_thru = new_prio();
03333                      fall_thru->type = AEL_APPCALL;
03334                      fall_thru->app = strdup("Goto");
03335                      snprintf(buf1,sizeof(buf1),"sw-%d-%s|1",local_control_statement_count, p2->next->u1.str);
03336                      fall_thru->appargs = strdup(buf1);
03337                      linkprio(switch_case, fall_thru);
03338                   } else if (p2->next && p2->next->type == PV_PATTERN) {
03339                      fall_thru = new_prio();
03340                      fall_thru->type = AEL_APPCALL;
03341                      fall_thru->app = strdup("Goto");
03342                      gen_match_to_pattern(p2->next->u1.str, buf2);
03343                      snprintf(buf1,sizeof(buf1),"sw-%d-%s|1",local_control_statement_count, buf2);
03344                      fall_thru->appargs = strdup(buf1);
03345                      linkprio(switch_case, fall_thru);
03346                   } else if (p2->next && p2->next->type == PV_DEFAULT) {
03347                      fall_thru = new_prio();
03348                      fall_thru->type = AEL_APPCALL;
03349                      fall_thru->app = strdup("Goto");
03350                      snprintf(buf1,sizeof(buf1),"sw-%d-.|1",local_control_statement_count);
03351                      fall_thru->appargs = strdup(buf1);
03352                      linkprio(switch_case, fall_thru);
03353                   } else if (!p2->next) {
03354                      fall_thru = new_prio();
03355                      fall_thru->type = AEL_CONTROL1;
03356                      fall_thru->goto_true = switch_end;
03357                      fall_thru->app = strdup("Goto");
03358                      linkprio(switch_case, fall_thru);
03359                   }
03360                }
03361                if (switch_case->return_needed) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */
03362                   char buf[2000];
03363                   struct ael_priority *np2 = new_prio();
03364                   np2->type = AEL_APPCALL;
03365                   np2->app = strdup("NoOp");
03366                   snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name);
03367                   np2->appargs = strdup(buf);
03368                   linkprio(switch_case, np2);
03369                   switch_case-> return_target = np2;
03370                }
03371             } else {
03372                /* what could it be??? */
03373             }
03374          }
03375          
03376          exten->loop_break = loop_break_save;
03377          exten->loop_continue = loop_continue_save;
03378          switch_test->origin = p;
03379          switch_end->origin = p;
03380          break;
03381 
03382       case PV_MACRO_CALL:
03383          pr = new_prio();
03384          pr->type = AEL_APPCALL;
03385          snprintf(buf1,sizeof(buf1),"%s|s|1", p->u1.str);
03386          first = 1;
03387          for (p2 = p->u2.arglist; p2; p2 = p2->next) {
03388             if (first)
03389             {
03390                strcat(buf1,"(");
03391                first = 0;
03392             }
03393             else
03394                strcat(buf1,"|");
03395             strcat(buf1,p2->u1.str);
03396          }
03397          if (!first)
03398             strcat(buf1,")");
03399 
03400          pr->app = strdup("Gosub");
03401          pr->appargs = strdup(buf1);
03402          pr->origin = p;
03403          linkprio(exten, pr);
03404          break;
03405 
03406       case PV_APPLICATION_CALL:
03407          pr = new_prio();
03408          pr->type = AEL_APPCALL;
03409          buf1[0] = 0;
03410          for (p2 = p->u2.arglist; p2; p2 = p2->next) {
03411             if (p2 != p->u2.arglist )
03412                strcat(buf1,"|");
03413             substitute_commas(p2->u1.str);
03414             strcat(buf1,p2->u1.str);
03415          }
03416          pr->app = strdup(p->u1.str);
03417          pr->appargs = strdup(buf1);
03418          pr->origin = p;
03419          linkprio(exten, pr);
03420          break;
03421 
03422       case PV_BREAK:
03423          pr = new_prio();
03424          pr->type = AEL_CONTROL1; /* simple goto */
03425          pr->goto_true = exten->loop_break;
03426          pr->origin = p;
03427          linkprio(exten, pr);
03428          break;
03429 
03430       case PV_RETURN: /* hmmmm */
03431          pr = new_prio();
03432          pr->type = AEL_RETURN; /* simple Return */
03433          /* exten->return_needed++; */
03434          pr->app = strdup("Return");
03435          pr->appargs = strdup("");
03436          pr->origin = p;
03437          linkprio(exten, pr);
03438          break;
03439 
03440       case PV_CONTINUE:
03441          pr = new_prio();
03442          pr->type = AEL_CONTROL1; /* simple goto */
03443          pr->goto_true = exten->loop_continue;
03444          pr->origin = p;
03445          linkprio(exten, pr);
03446          break;
03447 
03448       case PV_IFTIME:
03449          control_statement_count++;
03450          snprintf(new_label,sizeof(new_label),"iftime-%s-%d", label, control_statement_count);
03451          
03452          if_test = new_prio();
03453          if_test->type = AEL_IFTIME_CONTROL;
03454          snprintf(buf1,sizeof(buf1),"%s|%s|%s|%s",
03455                 p->u1.list->u1.str, 
03456                 p->u1.list->next->u1.str, 
03457                 p->u1.list->next->next->u1.str, 
03458                 p->u1.list->next->next->next->u1.str);
03459          if_test->app = 0;
03460          if_test->appargs = strdup(buf1);
03461          if_test->origin = p;
03462 
03463          if_end = new_prio();
03464          if_end->type = AEL_APPCALL;
03465          snprintf(buf1,sizeof(buf1),"Finish iftime-%s-%d", label, control_statement_count);
03466          if_end->app = strdup("NoOp");
03467          if_end->appargs = strdup(buf1);
03468 
03469          if (p->u3.else_statements) {
03470             if_skip = new_prio();
03471             if_skip->type = AEL_CONTROL1; /* simple goto */
03472             if_skip->goto_true = if_end;
03473             if_skip->origin  = p;
03474 
03475          } else {
03476             if_skip = 0;
03477 
03478             if_test->goto_false = if_end;
03479          }
03480 
03481          if_false = new_prio();
03482          if_false->type = AEL_CONTROL1;
03483          if (p->u3.else_statements) {
03484             if_false->goto_true = if_skip; /* +1 */
03485          } else {
03486             if_false->goto_true = if_end;
03487          }
03488          
03489          /* link & load! */
03490          linkprio(exten, if_test);
03491          linkprio(exten, if_false);
03492          
03493          /* now, put the body of the if here */
03494          
03495          gen_prios(exten, new_label, p->u2.statements, mother_exten, this_context); /* this will link in all the statements here */
03496          
03497          if (p->u3.else_statements) {
03498             linkprio(exten, if_skip);
03499             gen_prios(exten, new_label, p->u3.else_statements, mother_exten, this_context); /* this will link in all the statements here */
03500 
03501          }
03502          
03503          linkprio(exten, if_end);
03504          
03505          break;
03506 
03507       case PV_RANDOM:
03508       case PV_IF:
03509          control_statement_count++;
03510          snprintf(new_label,sizeof(new_label),"if-%s-%d", label, control_statement_count);
03511          
03512          if_test = new_prio();
03513          if_end = new_prio();
03514          if_test->type = AEL_IF_CONTROL;
03515          if_end->type = AEL_APPCALL;
03516          if ( p->type == PV_RANDOM )
03517             snprintf(buf1,sizeof(buf1),"$[${RAND(0,99)} < (%s)]",p->u1.str);
03518          else
03519             snprintf(buf1,sizeof(buf1),"$[%s]",p->u1.str);
03520          if_test->app = 0;
03521          if_test->appargs = strdup(buf1);
03522          snprintf(buf1,sizeof(buf1),"Finish if-%s-%d", label, control_statement_count);
03523          if_end->app = strdup("NoOp");
03524          if_end->appargs = strdup(buf1);
03525          if_test->origin = p;
03526          
03527          if (p->u3.else_statements) {
03528             if_skip = new_prio();
03529             if_skip->type = AEL_CONTROL1; /* simple goto */
03530             if_skip->goto_true = if_end;
03531             if_test->goto_false = if_skip;;
03532          } else {
03533             if_skip = 0;
03534             if_test->goto_false = if_end;;
03535          }
03536          
03537          /* link & load! */
03538          linkprio(exten, if_test);
03539          
03540          /* now, put the body of the if here */
03541          
03542          gen_prios(exten, new_label, p->u2.statements, mother_exten, this_context); /* this will link in all the statements here */
03543          
03544          if (p->u3.else_statements) {
03545             linkprio(exten, if_skip);
03546             gen_prios(exten, new_label, p->u3.else_statements, mother_exten, this_context); /* this will link in all the statements here */
03547 
03548          }
03549          
03550          linkprio(exten, if_end);
03551          
03552          break;
03553 
03554       case PV_STATEMENTBLOCK:
03555          gen_prios(exten, label, p->u1.list, mother_exten, this_context ); /* recurse into the block */
03556          break;
03557 
03558       case PV_CATCH:
03559          control_statement_count++;
03560          /* generate an extension with name of catch, put all catch stats
03561             into this exten! */
03562          switch_case = new_exten();
03563          switch_case->context = this_context;
03564          linkexten(exten,switch_case);
03565          switch_case->name = strdup(p->u1.str);
03566          snprintf(new_label,sizeof(new_label),"catch-%s-%d",p->u1.str, control_statement_count);
03567          
03568          gen_prios(switch_case, new_label, p->u2.statements,mother_exten,this_context); /* this will link in all the catch body statements here */
03569          if (switch_case->return_needed) { /* returns now generate a Return() app call, no longer a goto to the end of the exten */
03570             char buf[2000];
03571             struct ael_priority *np2 = new_prio();
03572             np2->type = AEL_APPCALL;
03573             np2->app = strdup("NoOp");
03574             snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name);
03575             np2->appargs = strdup(buf);
03576             linkprio(switch_case, np2);
03577             switch_case-> return_target = np2;
03578          }
03579 
03580          break;
03581       default:
03582          break;
03583       }
03584    }
03585 }
03586 
03587 void set_priorities(struct ael_extension *exten)
03588 {
03589    int i;
03590    struct ael_priority *pr;
03591    do {
03592       if (exten->regexten)
03593          i=2;
03594       else
03595          i=1;
03596       
03597       for (pr=exten->plist; pr; pr=pr->next) {
03598          pr->priority_num = i;
03599          
03600          if (!pr->origin || (pr->origin && pr->origin->type != PV_LABEL) ) /* Labels don't show up in the dialplan,
03601                                       but we want them to point to the right
03602                                       priority, which would be the next line
03603                                       after the label; */
03604             i++;
03605       }
03606       
03607       exten = exten->next_exten;
03608    } while ( exten );
03609 }
03610 
03611 void add_extensions(struct ael_extension *exten)
03612 {
03613    struct ael_priority *pr;
03614    char *label=0;
03615    if (!exten) {
03616       ast_log(LOG_WARNING, "This file is Empty!\n" );
03617       return;
03618    }
03619    do {
03620       struct ael_priority *last = 0;
03621       
03622       if (exten->hints) {
03623          if (ast_add_extension2(exten->context, 0 /*no replace*/, exten->name, PRIORITY_HINT, NULL, exten->cidmatch, 
03624                           exten->hints, NULL, ast_free, registrar)) {
03625             ast_log(LOG_WARNING, "Unable to add step at priority 'hint' of extension '%s'\n",
03626                   exten->name);
03627          }
03628       }
03629       
03630       for (pr=exten->plist; pr; pr=pr->next) {
03631          char app[2000];
03632          char appargs[2000];
03633 
03634          /* before we can add the extension, we need to prep the app/appargs;
03635             the CONTROL types need to be done after the priority numbers are calculated.
03636          */
03637          if (pr->type == AEL_LABEL) /* don't try to put labels in the dialplan! */ {
03638             last = pr;
03639             continue;
03640          }
03641          
03642          if (pr->app)
03643             strcpy(app, pr->app);
03644          else
03645             app[0] = 0;
03646          if (pr->appargs )
03647             strcpy(appargs, pr->appargs);
03648          else
03649             appargs[0] = 0;
03650          switch( pr->type ) {
03651          case AEL_APPCALL:
03652             /* easy case. Everything is all set up */
03653             break;
03654             
03655          case AEL_CONTROL1: /* FOR loop, WHILE loop, BREAK, CONTINUE, IF, IFTIME */
03656             /* simple, unconditional goto. */
03657             strcpy(app,"Goto");
03658             if (pr->goto_true->origin && pr->goto_true->origin->type == PV_SWITCH ) {
03659                snprintf(appargs,sizeof(appargs),"%s|%d", pr->goto_true->exten->name, pr->goto_true->priority_num);
03660             } else if (pr->goto_true->origin && pr->goto_true->origin->type == PV_IFTIME && pr->goto_true->origin->u3.else_statements ) {
03661                snprintf(appargs,sizeof(appargs),"%d", pr->goto_true->priority_num+1);
03662             } else
03663                snprintf(appargs,sizeof(appargs),"%d", pr->goto_true->priority_num);
03664             break;
03665             
03666          case AEL_FOR_CONTROL:  /* WHILE loop test, FOR loop test */
03667             strcpy(app,"GotoIf");
03668             snprintf(appargs,sizeof(appargs),"%s?%d:%d", pr->appargs, pr->priority_num+1, pr->goto_false->priority_num);
03669             break;
03670             
03671          case AEL_IF_CONTROL:
03672             strcpy(app,"GotoIf");
03673             if (pr->origin->u3.else_statements )
03674                snprintf(appargs,sizeof(appargs),"%s?%d:%d", pr->appargs, pr->priority_num+1, pr->goto_false->priority_num+1);
03675             else<