Mon Sep 20 2010 00:23:29

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