Tue Aug 20 16:34:36 2013

Asterisk developer's documentation


pval.c

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

Generated on 20 Aug 2013 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1