Fri Aug 17 00:17:18 2018

Asterisk developer's documentation


pval.c

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

Generated on 17 Aug 2018 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1