Thu Mar 25 12:09:36 2010

Asterisk developer's documentation


pbx_ael.c

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

Generated on Thu Mar 25 12:09:37 2010 for Asterisk - the Open Source PBX by  doxygen 1.4.7