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 #define WRAP_LIBC_MALLOC
00031 #include "asterisk.h"
00032 
00033 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 423978 $")
00034 
00035 #include <sys/types.h>
00036 #include <stdlib.h>
00037 #include <unistd.h>
00038 #include <stdio.h>
00039 #include <string.h>
00040 #include <ctype.h>
00041 #include <errno.h>
00042 #include <regex.h>
00043 #include <sys/stat.h>
00044 
00045 #include "asterisk/pbx.h"
00046 #include "asterisk/config.h"
00047 #include "asterisk/module.h"
00048 #include "asterisk/logger.h"
00049 #include "asterisk/cli.h"
00050 #include "asterisk/app.h"
00051 #include "asterisk/channel.h"
00052 #include "asterisk/callerid.h"
00053 #include "asterisk/pval.h"
00054 #include "asterisk/ael_structs.h"
00055 #ifdef AAL_ARGCHECK
00056 #include "asterisk/argdesc.h"
00057 #endif
00058 #include "asterisk/utils.h"
00059 
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                   switch_set->app = strdup("MSet");
03388                   /* Are we likely inside a gosub subroutine? */
03389                   if (!strcmp(mother_exten->name, "~~s~~") && first) {
03390                      /* If we're not actually within a gosub, this will fail, but the
03391                       * second time through, it will get set.  If we are within gosub,
03392                       * the second time through is redundant, but acceptable. */
03393                      switch_set->appargs = strdup("LOCAL(~~EXTEN~~)=${EXTEN}");
03394                   } else {
03395                      switch_set->appargs = strdup("~~EXTEN~~=${EXTEN}");
03396                      first = 0;
03397                   }
03398                   linkprio(exten, switch_set, mother_exten);
03399                   mother_exten->has_switch = 1;
03400                   mother_exten->checked_switch = 1;
03401                   if (exten) {
03402                      exten->has_switch = 1;
03403                      exten->checked_switch = 1;
03404                   }
03405                }
03406             }
03407          } else if (exten) {
03408             if (!exten->has_switch) {
03409                for (first = 1; first >= 0; first--) {
03410                   switch_set = new_prio();
03411                   switch_set->type = AEL_APPCALL;
03412                   switch_set->app = strdup("MSet");
03413                   /* Are we likely inside a gosub subroutine? */
03414                   if (!strcmp(exten->name, "~~s~~")) {
03415                      /* If we're not actually within a gosub, this will fail, but the
03416                       * second time through, it will get set.  If we are within gosub,
03417                       * the second time through is redundant, but acceptable. */
03418                      switch_set->appargs = strdup("LOCAL(~~EXTEN~~)=${EXTEN}");
03419                   } else {
03420                      switch_set->appargs = strdup("~~EXTEN~~=${EXTEN}");
03421                      first = 0;
03422                   }
03423                   linkprio(exten, switch_set, mother_exten);
03424                   exten->has_switch = 1;
03425                   exten->checked_switch = 1;
03426                   if (mother_exten) {
03427                      mother_exten->has_switch = 1;
03428                      mother_exten->checked_switch = 1;
03429                   }
03430                }
03431             }
03432          }
03433       } else {
03434          if (mother_exten) {
03435             mother_exten->checked_switch = 1;
03436          }
03437          if (exten) {
03438             exten->checked_switch = 1;
03439          }
03440       }
03441    }
03442    for (p=statement; p; p=p->next) {
03443       switch (p->type) {
03444       case PV_VARDEC:
03445          pr = new_prio();
03446          pr->type = AEL_APPCALL;
03447          snprintf(buf1, BUF_SIZE, "%s=$[%s]", p->u1.str, p->u2.val);
03448          pr->app = strdup("MSet");
03449          remove_spaces_before_equals(buf1);
03450          pr->appargs = strdup(buf1);
03451          pr->origin = p;
03452          linkprio(exten, pr, mother_exten);
03453          break;
03454 
03455       case PV_LOCALVARDEC:
03456          pr = new_prio();
03457          pr->type = AEL_APPCALL;
03458          snprintf(buf1, BUF_SIZE, "LOCAL(%s)=$[%s]", p->u1.str, p->u2.val);
03459          pr->app = strdup("MSet");
03460          remove_spaces_before_equals(buf1);
03461          pr->appargs = strdup(buf1);
03462          pr->origin = p;
03463          linkprio(exten, pr, mother_exten);
03464          break;
03465          
03466       case PV_GOTO:
03467          pr = new_prio();
03468          pr->type = AEL_APPCALL;
03469          p->u2.goto_target = get_goto_target(p);
03470          if( p->u2.goto_target ) {
03471             p->u3.goto_target_in_case = label_inside_case(p->u2.goto_target);
03472          }
03473          
03474          if (!p->u1.list->next) /* just one */ {
03475             pr->app = strdup("Goto");
03476             if (!mother_exten)
03477                pr->appargs = strdup(p->u1.list->u1.str);
03478             else {  /* for the case of simple within-extension gotos in case/pattern/default statement blocks: */ 
03479                snprintf(buf1, BUF_SIZE, "%s,%s", mother_exten->name, p->u1.list->u1.str);
03480                pr->appargs = strdup(buf1);
03481             }
03482             
03483          } else if (p->u1.list->next && !p->u1.list->next->next) /* two */ {
03484             snprintf(buf1, BUF_SIZE, "%s,%s", p->u1.list->u1.str, p->u1.list->next->u1.str);
03485             pr->app = strdup("Goto");
03486             pr->appargs = strdup(buf1);
03487          } else if (p->u1.list->next && p->u1.list->next->next) {
03488             snprintf(buf1, BUF_SIZE, "%s,%s,%s", p->u1.list->u1.str, 
03489                   p->u1.list->next->u1.str,
03490                   p->u1.list->next->next->u1.str);
03491             pr->app = strdup("Goto");
03492             pr->appargs = strdup(buf1);
03493          }
03494          pr->origin = p;
03495          linkprio(exten, pr, mother_exten);
03496          break;
03497 
03498       case PV_LABEL:
03499          pr = new_prio();
03500          pr->type = AEL_LABEL;
03501          pr->origin = p;
03502          p->u3.compiled_label = exten;
03503          linkprio(exten, pr, mother_exten);
03504          break;
03505 
03506       case PV_FOR:
03507          control_statement_count++;
03508          loop_break_save = exten->loop_break; /* save them, then restore before leaving */
03509          loop_continue_save = exten->loop_continue;
03510          snprintf(new_label, BUF_SIZE, "for_%s_%d", label, control_statement_count);
03511          for_init = new_prio();
03512          for_inc = new_prio();
03513          for_test = new_prio();
03514          for_loop = new_prio();
03515          for_end = new_prio();
03516          for_init->type = AEL_APPCALL;
03517          for_inc->type = AEL_APPCALL;
03518          for_test->type = AEL_FOR_CONTROL;
03519          for_test->goto_false = for_end;
03520          for_loop->type = AEL_CONTROL1; /* simple goto */
03521          for_end->type = AEL_APPCALL;
03522          for_init->app = strdup("MSet");
03523          
03524          strcpy(buf2,p->u1.for_init);
03525          remove_spaces_before_equals(buf2);
03526          strp = strchr(buf2, '=');
03527          if (strp) {
03528             strp2 = strchr(p->u1.for_init, '=');
03529             *(strp+1) = 0;
03530             strcat(buf2,"$[");
03531             strncat(buf2,strp2+1, BUF_SIZE-strlen(strp2+1)-2);
03532             strcat(buf2,"]");
03533             for_init->appargs = strdup(buf2);
03534          } else {
03535             strp2 = p->u1.for_init;
03536             while (*strp2 && isspace(*strp2))
03537                strp2++;
03538             if (*strp2 == '&') { /* itsa macro call */
03539                char *strp3 = strp2+1;
03540                while (*strp3 && isspace(*strp3))
03541                   strp3++;
03542                strcpy(buf2, strp3);
03543                strp3 = strchr(buf2,'(');
03544                if (strp3) {
03545                   *strp3 = '|';
03546                }
03547                while ((strp3=strchr(buf2,','))) {
03548                   *strp3 = '|';
03549                }
03550                strp3 = strrchr(buf2, ')');
03551                if (strp3)
03552                   *strp3 = 0; /* remove the closing paren */
03553 
03554                for_init->appargs = strdup(buf2);
03555                free(for_init->app);
03556                for_init->app = strdup("Macro");
03557             } else {  /* must be a regular app call */
03558                char *strp3;
03559                strcpy(buf2, strp2);
03560                strp3 = strchr(buf2,'(');
03561                if (strp3) {
03562                   *strp3 = 0;
03563                   free(for_init->app);
03564                   for_init->app = strdup(buf2);
03565                   for_init->appargs = strdup(strp3+1);
03566                   strp3 = strrchr(for_init->appargs, ')');
03567                   if (strp3)
03568                      *strp3 = 0; /* remove the closing paren */
03569                }
03570             }
03571          }
03572 
03573          strcpy(buf2,p->u3.for_inc);
03574          remove_spaces_before_equals(buf2);
03575          strp = strchr(buf2, '=');
03576          if (strp) {  /* there's an = in this part; that means an assignment. set it up */
03577             strp2 = strchr(p->u3.for_inc, '=');
03578             *(strp+1) = 0;
03579             strcat(buf2,"$[");
03580             strncat(buf2,strp2+1, BUF_SIZE-strlen(strp2+1)-2);
03581             strcat(buf2,"]");
03582             for_inc->appargs = strdup(buf2);
03583             for_inc->app = strdup("MSet");
03584          } else {
03585             strp2 = p->u3.for_inc;
03586             while (*strp2 && isspace(*strp2))
03587                strp2++;
03588             if (*strp2 == '&') { /* itsa macro call  */
03589                char *strp3 = strp2+1;
03590                while (*strp3 && isspace(*strp3))
03591                   strp3++;
03592                strcpy(buf2, strp3);
03593                strp3 = strchr(buf2,'(');
03594                if (strp3) {
03595                   *strp3 = ',';
03596                }
03597                strp3 = strrchr(buf2, ')');
03598                if (strp3)
03599                   *strp3 = 0; /* remove the closing paren */
03600 
03601                for_inc->appargs = strdup(buf2);
03602 
03603                for_inc->app = strdup("Macro");
03604             } else {  /* must be a regular app call */
03605                char *strp3;
03606                strcpy(buf2, strp2);
03607                strp3 = strchr(buf2,'(');
03608                if (strp3) {
03609                   *strp3 = 0;
03610                   for_inc->app = strdup(buf2);
03611                   for_inc->appargs = strdup(strp3+1);
03612                   strp3 = strrchr(for_inc->appargs, ')');
03613                   if (strp3)
03614                      *strp3 = 0; /* remove the closing paren */
03615                }
03616             }
03617          }
03618          snprintf(buf1, BUF_SIZE, "$[%s]",p->u2.for_test);
03619          for_test->app = 0;
03620          for_test->appargs = strdup(buf1);
03621          for_loop->goto_true = for_test;
03622          snprintf(buf1, BUF_SIZE, "Finish for_%s_%d", label, control_statement_count);
03623          for_end->app = strdup("NoOp");
03624          for_end->appargs = strdup(buf1);
03625          /* link & load! */
03626          linkprio(exten, for_init, mother_exten);
03627          linkprio(exten, for_test, mother_exten);
03628          
03629          /* now, put the body of the for loop here */
03630          exten->loop_break = for_end;
03631          exten->loop_continue = for_inc;
03632          
03633          if (gen_prios(exten, new_label, p->u4.for_statements, mother_exten, this_context)) { /* this will link in all the statements here */
03634             return -1;
03635          }
03636          
03637          linkprio(exten, for_inc, mother_exten);
03638          linkprio(exten, for_loop, mother_exten);
03639          linkprio(exten, for_end, mother_exten);
03640          
03641          
03642          exten->loop_break = loop_break_save;
03643          exten->loop_continue = loop_continue_save;
03644          for_loop->origin = p;
03645          break;
03646 
03647       case PV_WHILE:
03648          control_statement_count++;
03649          loop_break_save = exten->loop_break; /* save them, then restore before leaving */
03650          loop_continue_save = exten->loop_continue;
03651          snprintf(new_label, BUF_SIZE, "while_%s_%d", label, control_statement_count);
03652          while_test = new_prio();
03653          while_loop = new_prio();
03654          while_end = new_prio();
03655          while_test->type = AEL_FOR_CONTROL;
03656          while_test->goto_false = while_end;
03657          while_loop->type = AEL_CONTROL1; /* simple goto */
03658          while_end->type = AEL_APPCALL;
03659          snprintf(buf1, BUF_SIZE, "$[%s]",p->u1.str);
03660          while_test->app = 0;
03661          while_test->appargs = strdup(buf1);
03662          while_loop->goto_true = while_test;
03663          snprintf(buf1, BUF_SIZE, "Finish while_%s_%d", label, control_statement_count);
03664          while_end->app = strdup("NoOp");
03665          while_end->appargs = strdup(buf1);
03666 
03667          linkprio(exten, while_test, mother_exten);
03668          
03669          /* now, put the body of the for loop here */
03670          exten->loop_break = while_end;
03671          exten->loop_continue = while_test;
03672          
03673          if (gen_prios(exten, new_label, p->u2.statements, mother_exten, this_context)) { /* this will link in all the while body statements here */
03674             return -1;
03675          }
03676 
03677          linkprio(exten, while_loop, mother_exten);
03678          linkprio(exten, while_end, mother_exten);
03679          
03680          
03681          exten->loop_break = loop_break_save;
03682          exten->loop_continue = loop_continue_save;
03683          while_loop->origin = p;
03684          break;
03685 
03686       case PV_SWITCH:
03687          control_statement_count++;
03688          local_control_statement_count = control_statement_count;
03689          loop_break_save = exten->loop_break; /* save them, then restore before leaving */
03690          loop_continue_save = exten->loop_continue;
03691          snprintf(new_label, BUF_SIZE, "sw_%s_%d", label, control_statement_count);
03692          switch_test = new_prio();
03693          switch_end = new_prio();
03694          switch_test->type = AEL_APPCALL;
03695          switch_end->type = AEL_APPCALL;
03696          snprintf(buf1, BUF_SIZE, "sw_%d_%s,10", control_statement_count, p->u1.str);
03697          switch_test->app = strdup("Goto");
03698          switch_test->appargs = strdup(buf1);
03699          snprintf(buf1, BUF_SIZE, "Finish switch_%s_%d", label, control_statement_count);
03700          switch_end->app = strdup("NoOp");
03701          switch_end->appargs = strdup(buf1);
03702          switch_end->origin = p;
03703          switch_end->exten = exten;
03704 
03705          linkprio(exten, switch_test, mother_exten);
03706          linkprio(exten, switch_end, mother_exten);
03707          
03708          exten->loop_break = switch_end;
03709          exten->loop_continue = 0;
03710          default_exists = 0;
03711          
03712          for (p2=p->u2.statements; p2; p2=p2->next) {
03713             /* now, for each case/default put the body of the for loop here */
03714             if (p2->type == PV_CASE) {
03715                /* ok, generate a extension and link it in */
03716                switch_case = new_exten();
03717                if (mother_exten && mother_exten->checked_switch) {
03718                   switch_case->has_switch = mother_exten->has_switch;
03719                   switch_case->checked_switch = mother_exten->checked_switch;
03720                }
03721                if (exten && exten->checked_switch) {
03722                   switch_case->has_switch = exten->has_switch;
03723                   switch_case->checked_switch = exten->checked_switch;
03724                }
03725                switch_case->context = this_context;
03726                switch_case->is_switch = 1;
03727                /* the break/continue locations are inherited from parent */
03728                switch_case->loop_break = exten->loop_break;
03729                switch_case->loop_continue = exten->loop_continue;
03730                
03731                linkexten(exten,switch_case);
03732                snprintf(buf1, BUF_SIZE, "sw_%d_%s", local_control_statement_count, p2->u1.str);
03733                switch_case->name = strdup(buf1);
03734                snprintf(new_label, BUF_SIZE, "sw_%s_%s_%d", label, p2->u1.str, local_control_statement_count);
03735                
03736                if (gen_prios(switch_case, new_label, p2->u2.statements, exten, this_context)) { /* this will link in all the case body statements here */
03737                   return -1;
03738                }
03739 
03740                /* here is where we write code to "fall thru" to the next case... if there is one... */
03741                for (p3=p2->u2.statements; p3; p3=p3->next) {
03742                   if (!p3->next)
03743                      break;
03744                }
03745                /* p3 now points the last statement... */
03746                if (!p3 || ( p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN) ) {
03747                   /* is there a following CASE/PATTERN/DEFAULT? */
03748                   if (p2->next && p2->next->type == PV_CASE) {
03749                      fall_thru = new_prio();
03750                      fall_thru->type = AEL_APPCALL;
03751                      fall_thru->app = strdup("Goto");
03752                      snprintf(buf1, BUF_SIZE, "sw_%d_%s,10", local_control_statement_count, p2->next->u1.str);
03753                      fall_thru->appargs = strdup(buf1);
03754                      linkprio(switch_case, fall_thru, mother_exten);
03755                   } else if (p2->next && p2->next->type == PV_PATTERN) {
03756                      fall_thru = new_prio();
03757                      fall_thru->type = AEL_APPCALL;
03758                      fall_thru->app = strdup("Goto");
03759                      gen_match_to_pattern(p2->next->u1.str, buf2);
03760                      snprintf(buf1, BUF_SIZE, "sw_%d_%s,10", local_control_statement_count, buf2);
03761                      fall_thru->appargs = strdup(buf1);
03762                      linkprio(switch_case, fall_thru, mother_exten);
03763                   } else if (p2->next && p2->next->type == PV_DEFAULT) {
03764                      fall_thru = new_prio();
03765                      fall_thru->type = AEL_APPCALL;
03766                      fall_thru->app = strdup("Goto");
03767                      snprintf(buf1, BUF_SIZE, "sw_%d_.,10", local_control_statement_count);
03768                      fall_thru->appargs = strdup(buf1);
03769                      linkprio(switch_case, fall_thru, mother_exten);
03770                   } else if (!p2->next) {
03771                      fall_thru = new_prio();
03772                      fall_thru->type = AEL_CONTROL1;
03773                      fall_thru->goto_true = switch_end;
03774                      fall_thru->app = strdup("Goto");
03775                      linkprio(switch_case, fall_thru, mother_exten);
03776                   }
03777                }
03778                if (switch_case->return_needed) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */
03779                   char buf[2000];
03780                   struct ael_priority *np2 = new_prio();
03781                   np2->type = AEL_APPCALL;
03782                   np2->app = strdup("NoOp");
03783                   snprintf(buf, BUF_SIZE, "End of Extension %s", switch_case->name);
03784                   np2->appargs = strdup(buf);
03785                   linkprio(switch_case, np2, mother_exten);
03786                   switch_case-> return_target = np2;
03787                }
03788             } else if (p2->type == PV_PATTERN) {
03789                /* ok, generate a extension and link it in */
03790                switch_case = new_exten();
03791                if (mother_exten && mother_exten->checked_switch) {
03792                   switch_case->has_switch = mother_exten->has_switch;
03793                   switch_case->checked_switch = mother_exten->checked_switch;
03794                }
03795                if (exten && exten->checked_switch) {
03796                   switch_case->has_switch = exten->has_switch;
03797                   switch_case->checked_switch = exten->checked_switch;
03798                }
03799                switch_case->context = this_context;
03800                switch_case->is_switch = 1;
03801                /* the break/continue locations are inherited from parent */
03802                switch_case->loop_break = exten->loop_break;
03803                switch_case->loop_continue = exten->loop_continue;
03804 
03805                linkexten(exten,switch_case);
03806                snprintf(buf1, BUF_SIZE, "_sw_%d_%s", local_control_statement_count, p2->u1.str);
03807                switch_case->name = strdup(buf1);
03808                snprintf(new_label, BUF_SIZE, "sw_%s_%s_%d", label, p2->u1.str, local_control_statement_count);
03809 
03810                if (gen_prios(switch_case, new_label, p2->u2.statements, exten, this_context)) { /* this will link in all the while body statements here */
03811                   return -1;
03812                }
03813                /* here is where we write code to "fall thru" to the next case... if there is one... */
03814                for (p3=p2->u2.statements; p3; p3=p3->next) {
03815                   if (!p3->next)
03816                      break;
03817                }
03818                /* p3 now points the last statement... */
03819                if (!p3 || ( p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN)) {
03820                   /* is there a following CASE/PATTERN/DEFAULT? */
03821                   if (p2->next && p2->next->type == PV_CASE) {
03822                      fall_thru = new_prio();
03823                      fall_thru->type = AEL_APPCALL;
03824                      fall_thru->app = strdup("Goto");
03825                      snprintf(buf1, BUF_SIZE, "sw_%d_%s,10", local_control_statement_count, p2->next->u1.str);
03826                      fall_thru->appargs = strdup(buf1);
03827                      linkprio(switch_case, fall_thru, mother_exten);
03828                   } else if (p2->next && p2->next->type == PV_PATTERN) {
03829                      fall_thru = new_prio();
03830                      fall_thru->type = AEL_APPCALL;
03831                      fall_thru->app = strdup("Goto");
03832                      gen_match_to_pattern(p2->next->u1.str, buf2);
03833                      snprintf(buf1, BUF_SIZE, "sw_%d_%s,10", local_control_statement_count, buf2);
03834                      fall_thru->appargs = strdup(buf1);
03835                      linkprio(switch_case, fall_thru, mother_exten);
03836                   } else if (p2->next && p2->next->type == PV_DEFAULT) {
03837                      fall_thru = new_prio();
03838                      fall_thru->type = AEL_APPCALL;
03839                      fall_thru->app = strdup("Goto");
03840                      snprintf(buf1, BUF_SIZE, "sw_%d_.,10", local_control_statement_count);
03841                      fall_thru->appargs = strdup(buf1);
03842                      linkprio(switch_case, fall_thru, mother_exten);
03843                   } else if (!p2->next) {
03844                      fall_thru = new_prio();
03845                      fall_thru->type = AEL_CONTROL1;
03846                      fall_thru->goto_true = switch_end;
03847                      fall_thru->app = strdup("Goto");
03848                      linkprio(switch_case, fall_thru, mother_exten);
03849                   }
03850                }
03851                if (switch_case->return_needed) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */
03852                   char buf[2000];
03853                   struct ael_priority *np2 = new_prio();
03854                   np2->type = AEL_APPCALL;
03855                   np2->app = strdup("NoOp");
03856                   snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name);
03857                   np2->appargs = strdup(buf);
03858                   linkprio(switch_case, np2, mother_exten);
03859                   switch_case-> return_target = np2;
03860                }
03861             } else if (p2->type == PV_DEFAULT) {
03862                /* ok, generate a extension and link it in */
03863                switch_case = new_exten();
03864                if (mother_exten && mother_exten->checked_switch) {
03865                   switch_case->has_switch = mother_exten->has_switch;
03866                   switch_case->checked_switch = mother_exten->checked_switch;
03867                }
03868                if (exten && exten->checked_switch) {
03869                   switch_case->has_switch = exten->has_switch;
03870                   switch_case->checked_switch = exten->checked_switch;
03871                }
03872                switch_case->context = this_context;
03873                switch_case->is_switch = 1;
03874                
03875                /* new: the default case intros a pattern with ., which covers ALMOST everything.
03876                   but it doesn't cover a NULL pattern. So, we'll define a null extension to match
03877                   that goto's the default extension. */
03878 
03879                default_exists++;
03880                switch_null = new_exten();
03881                if (mother_exten && mother_exten->checked_switch) {
03882                   switch_null->has_switch = mother_exten->has_switch;
03883                   switch_null->checked_switch = mother_exten->checked_switch;
03884                }
03885                if (exten && exten->checked_switch) {
03886                   switch_null->has_switch = exten->has_switch;
03887                   switch_null->checked_switch = exten->checked_switch;
03888                }
03889                switch_null->context = this_context;
03890                switch_null->is_switch = 1;
03891                switch_empty = new_prio();
03892                snprintf(buf1, BUF_SIZE, "sw_%d_.,10", local_control_statement_count);
03893                switch_empty->app = strdup("Goto");
03894                switch_empty->appargs = strdup(buf1);
03895                linkprio(switch_null, switch_empty, mother_exten);
03896                snprintf(buf1, BUF_SIZE, "sw_%d_", local_control_statement_count);
03897                switch_null->name = strdup(buf1);
03898                switch_null->loop_break = exten->loop_break;
03899                switch_null->loop_continue = exten->loop_continue;
03900                linkexten(exten,switch_null);
03901 
03902                /* the break/continue locations are inherited from parent */
03903                switch_case->loop_break = exten->loop_break;
03904                switch_case->loop_continue = exten->loop_continue;
03905                linkexten(exten,switch_case);
03906                snprintf(buf1, BUF_SIZE, "_sw_%d_.", local_control_statement_count);
03907                switch_case->name = strdup(buf1);
03908                
03909                snprintf(new_label, BUF_SIZE, "sw_%s_default_%d", label, local_control_statement_count);
03910                
03911                if (gen_prios(switch_case, new_label, p2->u2.statements, exten, this_context)) { /* this will link in all the default:  body statements here */
03912                   return -1;
03913                }
03914                
03915                /* here is where we write code to "fall thru" to the next case... if there is one... */
03916                for (p3=p2->u2.statements; p3; p3=p3->next) {
03917                   if (!p3->next)
03918                      break;
03919                }
03920                /* p3 now points the last statement... */
03921                if (!p3 || (p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN)) {
03922                   /* is there a following CASE/PATTERN/DEFAULT? */
03923                   if (p2->next && p2->next->type == PV_CASE) {
03924                      fall_thru = new_prio();
03925                      fall_thru->type = AEL_APPCALL;
03926                      fall_thru->app = strdup("Goto");
03927                      snprintf(buf1, BUF_SIZE, "sw_%d_%s,10", local_control_statement_count, p2->next->u1.str);
03928                      fall_thru->appargs = strdup(buf1);
03929                      linkprio(switch_case, fall_thru, mother_exten);
03930                   } else if (p2->next && p2->next->type == PV_PATTERN) {
03931                      fall_thru = new_prio();
03932                      fall_thru->type = AEL_APPCALL;
03933                      fall_thru->app = strdup("Goto");
03934                      gen_match_to_pattern(p2->next->u1.str, buf2);
03935                      snprintf(buf1, BUF_SIZE, "sw_%d_%s,10", local_control_statement_count, buf2);
03936                      fall_thru->appargs = strdup(buf1);
03937                      linkprio(switch_case, fall_thru, mother_exten);
03938                   } else if (p2->next && p2->next->type == PV_DEFAULT) {
03939                      fall_thru = new_prio();
03940                      fall_thru->type = AEL_APPCALL;
03941                      fall_thru->app = strdup("Goto");
03942                      snprintf(buf1, BUF_SIZE, "sw_%d_.,10", local_control_statement_count);
03943                      fall_thru->appargs = strdup(buf1);
03944                      linkprio(switch_case, fall_thru, mother_exten);
03945                   } else if (!p2->next) {
03946                      fall_thru = new_prio();
03947                      fall_thru->type = AEL_CONTROL1;
03948                      fall_thru->goto_true = switch_end;
03949                      fall_thru->app = strdup("Goto");
03950                      linkprio(switch_case, fall_thru, mother_exten);
03951                   }
03952                }
03953                if (switch_case->return_needed) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */
03954                   char buf[2000];
03955                   struct ael_priority *np2 = new_prio();
03956                   np2->type = AEL_APPCALL;
03957                   np2->app = strdup("NoOp");
03958                   snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name);
03959                   np2->appargs = strdup(buf);
03960                   linkprio(switch_case, np2, mother_exten);
03961                   switch_case-> return_target = np2;
03962                }
03963             } else {
03964                /* what could it be??? */
03965             }
03966          }
03967          
03968          exten->loop_break = loop_break_save;
03969          exten->loop_continue = loop_continue_save;
03970          switch_test->origin = p;
03971          switch_end->origin = p;
03972          break;
03973 
03974       case PV_MACRO_CALL:
03975          pr = new_prio();
03976          pr->type = AEL_APPCALL;
03977          snprintf(buf1, BUF_SIZE, "%s,~~s~~,1", p->u1.str);
03978          first = 1;
03979          for (p2 = p->u2.arglist; p2; p2 = p2->next) {
03980             if (first)
03981             {
03982                strcat(buf1,"(");
03983                first = 0;
03984             }
03985             else
03986                strcat(buf1,",");
03987             strcat(buf1,p2->u1.str);
03988          }
03989          if (!first)
03990             strcat(buf1,")");
03991 
03992          pr->app = strdup("Gosub");
03993          pr->appargs = strdup(buf1);
03994          pr->origin = p;
03995          linkprio(exten, pr, mother_exten);
03996          break;
03997 
03998       case PV_APPLICATION_CALL:
03999          pr = new_prio();
04000          pr->type = AEL_APPCALL;
04001          buf1[0] = 0;
04002          for (p2 = p->u2.arglist; p2; p2 = p2->next) {
04003             if (p2 != p->u2.arglist )
04004                strcat(buf1,",");
04005             strcat(buf1,p2->u1.str);
04006          }
04007          pr->app = strdup(p->u1.str);
04008          pr->appargs = strdup(buf1);
04009          pr->origin = p;
04010          linkprio(exten, pr, mother_exten);
04011          break;
04012 
04013       case PV_BREAK:
04014          pr = new_prio();
04015          pr->type = AEL_CONTROL1; /* simple goto */
04016          pr->goto_true = exten->loop_break;
04017          pr->origin = p;
04018          linkprio(exten, pr, mother_exten);
04019          break;
04020 
04021       case PV_RETURN: /* hmmmm */
04022          pr = new_prio();
04023          pr->type = AEL_RETURN; /* simple Return */
04024          /* exten->return_needed++; */
04025          pr->app = strdup("Return");
04026          pr->appargs = strdup("");
04027          pr->origin = p;
04028          linkprio(exten, pr, mother_exten);
04029          break;
04030 
04031       case PV_CONTINUE:
04032          pr = new_prio();
04033          pr->type = AEL_CONTROL1; /* simple goto */
04034          pr->goto_true = exten->loop_continue;
04035          pr->origin = p;
04036          linkprio(exten, pr, mother_exten);
04037          break;
04038 
04039       case PV_IFTIME:
04040          control_statement_count++;
04041          snprintf(new_label, BUF_SIZE, "iftime_%s_%d", label, control_statement_count);
04042          
04043          if_test = new_prio();
04044          if_test->type = AEL_IFTIME_CONTROL;
04045          snprintf(buf1, BUF_SIZE, "%s,%s,%s,%s",
04046                 p->u1.list->u1.str, 
04047                 p->u1.list->next->u1.str, 
04048                 p->u1.list->next->next->u1.str, 
04049                 p->u1.list->next->next->next->u1.str);
04050          if_test->app = 0;
04051          if_test->appargs = strdup(buf1);
04052          if_test->origin = p;
04053 
04054          if_end = new_prio();
04055          if_end->type = AEL_APPCALL;
04056          snprintf(buf1, BUF_SIZE, "Finish iftime_%s_%d", label, control_statement_count);
04057          if_end->app = strdup("NoOp");
04058          if_end->appargs = strdup(buf1);
04059 
04060          if (p->u3.else_statements) {
04061             if_skip = new_prio();
04062             if_skip->type = AEL_CONTROL1; /* simple goto */
04063             if_skip->goto_true = if_end;
04064             if_skip->origin  = p;
04065 
04066          } else {
04067             if_skip = 0;
04068 
04069             if_test->goto_false = if_end;
04070          }
04071 
04072          if_false = new_prio();
04073          if_false->type = AEL_CONTROL1;
04074          if (p->u3.else_statements) {
04075             if_false->goto_true = if_skip; /* +1 */
04076          } else {
04077             if_false->goto_true = if_end;
04078          }
04079          
04080          /* link & load! */
04081          linkprio(exten, if_test, mother_exten);
04082          linkprio(exten, if_false, mother_exten);
04083          
04084          /* now, put the body of the if here */
04085          
04086          if (gen_prios(exten, new_label, p->u2.statements, mother_exten, this_context)) { /* this will link in all the statements here */
04087             return -1;
04088          }
04089          
04090          if (p->u3.else_statements) {
04091             linkprio(exten, if_skip, mother_exten);
04092             if (gen_prios(exten, new_label, p->u3.else_statements, mother_exten, this_context)) { /* this will link in all the statements here */
04093                return -1;
04094             }
04095          }
04096          
04097          linkprio(exten, if_end, mother_exten);
04098          
04099          break;
04100 
04101       case PV_RANDOM:
04102       case PV_IF:
04103          control_statement_count++;
04104          snprintf(new_label, BUF_SIZE, "if_%s_%d", label, control_statement_count);
04105          
04106          if_test = new_prio();
04107          if_end = new_prio();
04108          if_test->type = AEL_IF_CONTROL;
04109          if_end->type = AEL_APPCALL;
04110          if ( p->type == PV_RANDOM )
04111             snprintf(buf1, BUF_SIZE, "$[${RAND(0,99)} < (%s)]", p->u1.str);
04112          else
04113             snprintf(buf1, BUF_SIZE, "$[%s]", p->u1.str);
04114          if_test->app = 0;
04115          if_test->appargs = strdup(buf1);
04116          snprintf(buf1, BUF_SIZE, "Finish if_%s_%d", label, control_statement_count);
04117          if_end->app = strdup("NoOp");
04118          if_end->appargs = strdup(buf1);
04119          if_test->origin = p;
04120          
04121          if (p->u3.else_statements) {
04122             if_skip = new_prio();
04123             if_skip->type = AEL_CONTROL1; /* simple goto */
04124             if_skip->goto_true = if_end;
04125             if_test->goto_false = if_skip;;
04126          } else {
04127             if_skip = 0;
04128             if_test->goto_false = if_end;;
04129          }
04130          
04131          /* link & load! */
04132          linkprio(exten, if_test, mother_exten);
04133          
04134          /* now, put the body of the if here */
04135          
04136          if (gen_prios(exten, new_label, p->u2.statements, mother_exten, this_context)) { /* this will link in all the statements here */
04137             return -1;
04138          }
04139          
04140          if (p->u3.else_statements) {
04141             linkprio(exten, if_skip, mother_exten);
04142             if (gen_prios(exten, new_label, p->u3.else_statements, mother_exten, this_context)) { /* this will link in all the statements here */
04143                return -1;
04144             }
04145          }
04146          
04147          linkprio(exten, if_end, mother_exten);
04148          
04149          break;
04150 
04151       case PV_STATEMENTBLOCK:
04152          if (gen_prios(exten, label, p->u1.list, mother_exten, this_context)) { /* recurse into the block */
04153             return -1;
04154          }
04155          break;
04156 
04157       case PV_CATCH:
04158          control_statement_count++;
04159          /* generate an extension with name of catch, put all catch stats
04160             into this exten! */
04161          switch_case = new_exten();
04162          if (mother_exten && mother_exten->checked_switch) {
04163             switch_case->has_switch = mother_exten->has_switch;
04164             switch_case->checked_switch = mother_exten->checked_switch;
04165          }
04166          if (exten && exten->checked_switch) {
04167             switch_case->has_switch = exten->has_switch;
04168             switch_case->checked_switch = exten->checked_switch;
04169          }
04170          
04171          switch_case->context = this_context;
04172          linkexten(exten,switch_case);
04173          switch_case->name = strdup(p->u1.str);
04174          snprintf(new_label, BUF_SIZE, "catch_%s_%d",p->u1.str, control_statement_count);
04175          
04176          if (gen_prios(switch_case, new_label, p->u2.statements, mother_exten,this_context)) { /* this will link in all the catch body statements here */
04177             return -1;
04178          }
04179          if (switch_case->return_needed) { /* returns now generate a Return() app call, no longer a goto to the end of the exten */
04180             char buf[2000];
04181             struct ael_priority *np2 = new_prio();
04182             np2->type = AEL_APPCALL;
04183             np2->app = strdup("NoOp");
04184             snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name);
04185             np2->appargs = strdup(buf);
04186             linkprio(switch_case, np2, mother_exten);
04187             switch_case-> return_target = np2;
04188          }
04189 
04190          break;
04191       default:
04192          break;
04193       }
04194    }
04195    free(buf1);
04196    free(buf2);
04197    free(new_label);
04198    return 0;
04199 }
04200 
04201 void set_priorities(struct ael_extension *exten)
04202 {
04203    int i;
04204    struct ael_priority *pr;
04205    do {
04206       if (exten->is_switch)
04207          i = 10;
04208       else if (exten->regexten)
04209          i=2;
04210       else
04211          i=1;
04212       
04213       for (pr=exten->plist; pr; pr=pr->next) {
04214          pr->priority_num = i;
04215          
04216          if (!pr->origin || (pr->origin && pr->origin->type != PV_LABEL) ) /* Labels don't show up in the dialplan,
04217                                       but we want them to point to the right
04218                                       priority, which would be the next line
04219                                       after the label; */
04220             i++;
04221       }
04222       
04223       exten = exten->next_exten;
04224    } while ( exten );
04225 }
04226 
04227 void add_extensions(struct ael_extension *exten)
04228 {
04229    struct ael_priority *pr;
04230    char *label=0;
04231    char realext[AST_MAX_EXTENSION];
04232    if (!exten) {
04233       ast_log(LOG_WARNING, "This file is Empty!\n" );
04234       return;
04235    }
04236    do {
04237       struct ael_priority *last = 0;
04238       
04239       pbx_substitute_variables_helper(NULL, exten->name, realext, sizeof(realext) - 1);
04240       if (exten->hints) {
04241          if (ast_add_extension2(exten->context, 0 /*no replace*/, realext, PRIORITY_HINT, NULL, exten->cidmatch, 
04242                           exten->hints, NULL, ast_free_ptr, registrar)) {
04243             ast_log(LOG_WARNING, "Unable to add step at priority 'hint' of extension '%s'\n",
04244                   exten->name);
04245          }
04246       }
04247       
04248       for (pr=exten->plist; pr; pr=pr->next) {
04249          char app[2000];
04250          char appargs[2000];
04251 
04252          /* before we can add the extension, we need to prep the app/appargs;
04253             the CONTROL types need to be done after the priority numbers are calculated.
04254          */
04255          if (pr->type == AEL_LABEL) /* don't try to put labels in the dialplan! */ {
04256             last = pr;
04257             continue;
04258          }
04259          
04260          if (pr->app)
04261             strcpy(app, pr->app);
04262          else
04263             app[0] = 0;
04264          if (pr->appargs )
04265             strcpy(appargs, pr->appargs);
04266          else
04267             appargs[0] = 0;
04268          switch( pr->type ) {
04269          case AEL_APPCALL:
04270             /* easy case. Everything is all set up */
04271             break;
04272             
04273          case AEL_CONTROL1: /* FOR loop, WHILE loop, BREAK, CONTINUE, IF, IFTIME */
04274             /* simple, unconditional goto. */
04275             strcpy(app,"Goto");
04276             if (pr->goto_true->origin && pr->goto_true->origin->type == PV_SWITCH ) {
04277                snprintf(appargs,sizeof(appargs),"%s,%d", pr->goto_true->exten->name, pr->goto_true->priority_num);
04278             } else if (pr->goto_true->origin && pr->goto_true->origin->type == PV_IFTIME && pr->goto_true->origin->u3.else_statements ) {
04279                snprintf(appargs,sizeof(appargs),"%d", pr->goto_true->priority_num+1);
04280             } else
04281                snprintf(appargs,sizeof(appargs),"%d", pr->goto_true->priority_num);
04282             break;
04283             
04284          case AEL_FOR_CONTROL:  /* WHILE loop test, FOR loop test */
04285             strcpy(app,"GotoIf");
04286             snprintf(appargs,sizeof(appargs),"%s?%d:%d", pr->appargs, pr->priority_num+1, pr->goto_false->priority_num);
04287             break;
04288             
04289          case AEL_IF_CONTROL:
04290             strcpy(app,"GotoIf");
04291             if (pr->origin->u3.else_statements )
04292                snprintf(appargs,sizeof(appargs),"%s?%d:%d", pr->appargs, pr->priority_num+1, pr->goto_false->priority_num+1);
04293             else
04294                snprintf(appargs,sizeof(appargs),"%s?%d:%d", pr->appargs, pr->priority_num+1, pr->goto_false->priority_num);
04295             break;
04296 
04297          case AEL_RAND_CONTROL:
04298             strcpy(app,"Random");
04299             snprintf(appargs,sizeof(appargs),"%s:%d", pr->appargs, pr->goto_true->priority_num+1);
04300             break;
04301 
04302          case AEL_IFTIME_CONTROL:
04303             strcpy(app,"GotoIfTime");
04304             snprintf(appargs,sizeof(appargs),"%s?%d", pr->appargs, pr->priority_num+2);
04305             break;
04306 
04307          case AEL_RETURN:
04308             strcpy(app,"Return");
04309             appargs[0] = 0;
04310             break;
04311             
04312          default:
04313             break;
04314          }
04315          if (last && last->type == AEL_LABEL ) {
04316             label = last->origin->u1.str;
04317          }
04318          else
04319             label = 0;
04320          
04321          if (ast_add_extension2(exten->context, 0 /*no replace*/, realext, pr->priority_num, (label?label:NULL), exten->cidmatch, 
04322                           app, strdup(appargs), ast_free_ptr, registrar)) {
04323             ast_log(LOG_WARNING, "Unable to add step at priority '%d' of extension '%s'\n", pr->priority_num, 
04324                   exten->name);
04325          }
04326          last = pr;
04327       }
04328       exten = exten->next_exten;
04329    } while ( exten );
04330 }
04331 
04332 static void attach_exten(struct ael_extension **list, struct ael_extension *newmem)
04333 {
04334    /* travel to the end of the list... */
04335    struct ael_extension *lptr;
04336    if( !*list ) {
04337       *list = newmem;
04338       return;
04339    }
04340    lptr = *list;
04341    
04342    while( lptr->next_exten ) {
04343       lptr = lptr->next_exten;
04344    }
04345    /* lptr should now pointing to the last element in the list; it has a null next_exten pointer */
04346    lptr->next_exten = newmem;
04347 }
04348 
04349 static pval *get_extension_or_contxt(pval *p)
04350 {
04351    while( p && p->type != PV_EXTENSION && p->type != PV_CONTEXT && p->type != PV_MACRO ) {
04352       
04353       p = p->dad;
04354    }
04355    
04356    return p;
04357 }
04358 
04359 static pval *get_contxt(pval *p)
04360 {
04361    while( p && p->type != PV_CONTEXT && p->type != PV_MACRO ) {
04362       
04363       p = p->dad;
04364    }
04365    
04366    return p;
04367 }
04368 
04369 static void fix_gotos_in_extensions(struct ael_extension *exten)
04370 {
04371    struct ael_extension *e;
04372    for(e=exten;e;e=e->next_exten) {
04373 
04374       struct ael_priority *p;
04375       for(p=e->plist;p;p=p->next) {
04376          
04377          if( p->origin && p->origin->type == PV_GOTO && p->origin->u3.goto_target_in_case ) {
04378             
04379             /* fix the extension of the goto target to the actual extension in the post-compiled dialplan */
04380 
04381             pval *target = p->origin->u2.goto_target;
04382             struct ael_extension *z = target->u3.compiled_label;
04383             pval *pv2 = p->origin;
04384             char buf1[500];
04385             char *apparg_save = p->appargs;
04386             
04387             p->appargs = 0;
04388             if (!pv2->u1.list->next) /* just one  -- it won't hurt to repeat the extension */ {
04389                snprintf(buf1,sizeof(buf1),"%s,%s", z->name, pv2->u1.list->u1.str);
04390                p->appargs = strdup(buf1);
04391                
04392             } else if (pv2->u1.list->next && !pv2->u1.list->next->next) /* two */ {
04393                snprintf(buf1,sizeof(buf1),"%s,%s", z->name, pv2->u1.list->next->u1.str);
04394                p->appargs = strdup(buf1);
04395             } else if (pv2->u1.list->next && pv2->u1.list->next->next) {
04396                snprintf(buf1,sizeof(buf1),"%s,%s,%s", pv2->u1.list->u1.str, 
04397                       z->name,
04398                       pv2->u1.list->next->next->u1.str);
04399                p->appargs = strdup(buf1);
04400             }
04401             else
04402                printf("WHAT? The goto doesn't fall into one of three cases for GOTO????\n");
04403             
04404             if( apparg_save ) {
04405                free(apparg_save);
04406             }
04407          }
04408       }
04409    }
04410 }
04411 
04412 static int context_used(struct ael_extension *exten_list, struct ast_context *context)
04413 {
04414    struct ael_extension *exten;
04415    /* Check the simple elements first */
04416    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)) {
04417       return 1;
04418    }
04419    for (exten = exten_list; exten; exten = exten->next_exten) {
04420       if (exten->context == context) {
04421          return 1;
04422       }
04423    }
04424    return 0;
04425 }
04426 
04427 int ast_compile_ael2(struct ast_context **local_contexts, struct ast_hashtab *local_table, struct pval *root)
04428 {
04429    pval *p,*p2;
04430    struct ast_context *context;
04431    char buf[2000];
04432    struct ael_extension *exten;
04433    struct ael_extension *exten_list = 0;
04434 
04435    for (p=root; p; p=p->next ) { /* do the globals first, so they'll be there
04436                             when we try to eval them */
04437       switch (p->type) {
04438       case PV_GLOBALS:
04439          /* just VARDEC elements */
04440          for (p2=p->u1.list; p2; p2=p2->next) {
04441             char buf2[2000];
04442             snprintf(buf2,sizeof(buf2),"%s=%s", p2->u1.str, p2->u2.val);
04443             pbx_builtin_setvar(NULL, buf2);
04444          }
04445          break;
04446       default:
04447          break;
04448       }
04449    }
04450 
04451    for (p=root; p; p=p->next ) {
04452       pval *lp;
04453       int argc;
04454       
04455       switch (p->type) {
04456       case PV_MACRO:
04457          
04458          context = ast_context_find_or_create(local_contexts, local_table, p->u1.str, registrar);
04459          
04460          exten = new_exten();
04461          exten->context = context;
04462          exten->name = strdup("~~s~~");
04463          argc = 1;
04464          for (lp=p->u2.arglist; lp; lp=lp->next) {
04465             /* for each arg, set up a "Set" command */
04466             struct ael_priority *np2 = new_prio();
04467             np2->type = AEL_APPCALL;
04468             np2->app = strdup("MSet");
04469             snprintf(buf,sizeof(buf),"LOCAL(%s)=${ARG%d}", lp->u1.str, argc++);
04470             remove_spaces_before_equals(buf);
04471             np2->appargs = strdup(buf);
04472             linkprio(exten, np2, NULL);
04473          }
04474          
04475          /* CONTAINS APPCALLS, CATCH, just like extensions... */
04476          if (gen_prios(exten, p->u1.str, p->u3.macro_statements, 0, context)) {
04477             return -1;
04478          }
04479          if (exten->return_needed) {  /* most likely, this will go away */
04480             struct ael_priority *np2 = new_prio();
04481             np2->type = AEL_APPCALL;
04482             np2->app = strdup("NoOp");
04483             snprintf(buf,sizeof(buf),"End of Macro %s-%s",p->u1.str, exten->name);
04484             np2->appargs = strdup(buf);
04485             linkprio(exten, np2, NULL);
04486             exten-> return_target = np2;
04487          }
04488          
04489          set_priorities(exten);
04490          attach_exten(&exten_list, exten);
04491          break;
04492          
04493       case PV_GLOBALS:
04494          /* already done */
04495          break;
04496          
04497       case PV_CONTEXT:
04498          context = ast_context_find_or_create(local_contexts, local_table, p->u1.str, registrar);
04499          
04500          /* contexts contain: ignorepat, includes, switches, eswitches, extensions,  */
04501          for (p2=p->u2.statements; p2; p2=p2->next) {
04502             pval *p3;
04503             char *s3;
04504             
04505             switch (p2->type) {
04506             case PV_EXTENSION:
04507                exten = new_exten();
04508                exten->name = strdup(p2->u1.str);
04509                exten->context = context;
04510                
04511                if( (s3=strchr(exten->name, '/') ) != 0 )
04512                {
04513                   *s3 = 0;
04514                   exten->cidmatch = s3+1;
04515                }
04516                
04517                if ( p2->u3.hints )
04518                   exten->hints = strdup(p2->u3.hints);
04519                exten->regexten = p2->u4.regexten;
04520                if (gen_prios(exten, p->u1.str, p2->u2.statements, 0, context)) {
04521                   return -1;
04522                }
04523                if (exten->return_needed) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */
04524                   struct ael_priority *np2 = new_prio();
04525                   np2->type = AEL_APPCALL;
04526                   np2->app = strdup("NoOp");
04527                   snprintf(buf,sizeof(buf),"End of Extension %s", exten->name);
04528                   np2->appargs = strdup(buf);
04529                   linkprio(exten, np2, NULL);
04530                   exten-> return_target = np2;
04531                }
04532                /* is the last priority in the extension a label? Then add a trailing no-op */
04533                if ( exten->plist_last && exten->plist_last->type == AEL_LABEL ) {
04534                   struct ael_priority *np2 = new_prio();
04535                   np2->type = AEL_APPCALL;
04536                   np2->app = strdup("NoOp");
04537                   snprintf(buf,sizeof(buf),"A NoOp to follow a trailing label %s", exten->plist_last->origin->u1.str);
04538                   np2->appargs = strdup(buf);
04539                   linkprio(exten, np2, NULL);
04540                }
04541 
04542                set_priorities(exten);
04543                attach_exten(&exten_list, exten);
04544                break;
04545                
04546             case PV_IGNOREPAT:
04547                ast_context_add_ignorepat2(context, p2->u1.str, registrar);
04548                break;
04549                
04550             case PV_INCLUDES:
04551                for (p3 = p2->u1.list; p3 ;p3=p3->next) {
04552                   if ( p3->u2.arglist ) {
04553                      snprintf(buf,sizeof(buf), "%s,%s,%s,%s,%s", 
04554                             p3->u1.str,
04555                             p3->u2.arglist->u1.str,
04556                             p3->u2.arglist->next->u1.str,
04557                             p3->u2.arglist->next->next->u1.str,
04558                             p3->u2.arglist->next->next->next->u1.str);
04559                      ast_context_add_include2(context, buf, registrar);
04560                   } else
04561                      ast_context_add_include2(context, p3->u1.str, registrar);
04562                }
04563                break;
04564                
04565             case PV_SWITCHES:
04566                for (p3 = p2->u1.list; p3 ;p3=p3->next) {
04567                   char *c = strchr(p3->u1.str, '/');
04568                   if (c) {
04569                      *c = '\0';
04570                      c++;
04571                   } else
04572                      c = "";
04573 
04574                   ast_context_add_switch2(context, p3->u1.str, c, 0, registrar);
04575                }
04576                break;
04577 
04578             case PV_ESWITCHES:
04579                for (p3 = p2->u1.list; p3 ;p3=p3->next) {
04580                   char *c = strchr(p3->u1.str, '/');
04581                   if (c) {
04582                      *c = '\0';
04583                      c++;
04584                   } else
04585                      c = "";
04586 
04587                   ast_context_add_switch2(context, p3->u1.str, c, 1, registrar);
04588                }
04589                break;
04590             default:
04591                break;
04592             }
04593          }
04594          
04595          break;
04596          
04597       default:
04598          /* huh? what? */
04599          break;
04600          
04601       }
04602    }
04603 
04604    /* Create default "h" bubble context */
04605    if (ast_custom_function_find("DIALPLAN_EXISTS") && ast_custom_function_find("STACK_PEEK")) {
04606       int i;
04607       const char *h_context = "ael-builtin-h-bubble";
04608       struct ael_priority *np;
04609       struct {
04610          int priority;
04611          const char *app;
04612          const char *arg;
04613       } steps[] = {
04614          /* Start high, to avoid conflict with existing h extensions */
04615          { 1, "Goto", "9991" },
04616          /* Save the context, because after the StackPop, it disappears */
04617          { 9991, "Set", "~~parentcxt~~=${STACK_PEEK(1,c,1)}" },
04618          /* If we're not in a Gosub frame, exit */
04619          { 9992, "GotoIf", "$[\"${~~parentcxt~~}\"=\"\"]?9996" },
04620          /* Check for an "h" extension in that context */
04621          { 9993, "GotoIf", "${DIALPLAN_EXISTS(${~~parentcxt~~},h,1)}?9994:9996" },
04622          /* Pop off the stack frame to prevent an infinite loop */
04623          { 9994, "StackPop", "" },
04624          /* Finally, go there. */
04625          { 9995, "Goto", "${~~parentcxt~~},h,1" },
04626          /* Just an empty priority for jumping out early */
04627          { 9996, "NoOp", "" }
04628       };
04629       context = ast_context_find_or_create(local_contexts, local_table, h_context, registrar);
04630       if (context_used(exten_list, context)) {
04631          int found = 0;
04632          while (!found) {
04633             /* Pick a new context name that is not used. */
04634             char h_context_template[] = "/tmp/ael-builtin-h-bubble-XXXXXX";
04635             int fd = mkstemp(h_context_template);
04636             unlink(h_context_template);
04637             close(fd);
04638             context = ast_context_find_or_create(local_contexts, local_table, h_context_template + 5, registrar);
04639             found = !context_used(exten_list, context);
04640          }
04641          h_context = ast_get_context_name(context);
04642       }
04643       exten = new_exten();
04644       exten->context = context;
04645       exten->name = strdup("h");
04646 
04647       for (i = 0; i < ARRAY_LEN(steps); i++) {
04648          np = new_prio();
04649          np->type = AEL_APPCALL;
04650          np->priority_num = steps[i].priority;
04651          np->app = strdup(steps[i].app);
04652          np->appargs = strdup(steps[i].arg);
04653          linkprio(exten, np, NULL);
04654       }
04655       attach_exten(&exten_list, exten);
04656 
04657       /* Include the default "h" bubble context in each macro context */
04658       for (exten = exten_list; exten; exten = exten->next_exten) {
04659          /* All macros contain a "~~s~~" extension, and it's the first created.  If
04660           * we perchance get a non-macro context, it's no big deal; the logic is
04661           * designed to exit out smoothly if not called from within a Gosub. */
04662          if (!strcmp(exten->name, "~~s~~")) {
04663             ast_context_add_include2(exten->context, h_context, registrar);
04664          }
04665       }
04666    }
04667 
04668    /* moved these from being done after a macro or extension were processed,
04669       to after all processing is done, for the sake of fixing gotos to labels inside cases... */
04670    /* I guess this would be considered 2nd pass of compiler now... */
04671    fix_gotos_in_extensions(exten_list); /* find and fix extension ref in gotos to labels that are in case statements */
04672    add_extensions(exten_list);   /* actually makes calls to create priorities in ast_contexts -- feeds dialplan to asterisk */
04673    destroy_extensions(exten_list);  /* all that remains is an empty husk, discard of it as is proper */
04674    
04675    return 0;
04676 }
04677 
04678 
04679 /* DESTROY the PVAL tree ============================================================================ */
04680 
04681 
04682 
04683 void destroy_pval_item(pval *item)
04684 {
04685    if (item == NULL) {
04686       ast_log(LOG_WARNING, "null item\n");
04687       return;
04688    }
04689 
04690    if (item->filename)
04691       free(item->filename);
04692    
04693    switch (item->type) {
04694    case PV_WORD:
04695       /* fields: item->u1.str == string associated with this (word). */
04696       if (item->u1.str )
04697          free(item->u1.str);
04698       if ( item->u2.arglist )
04699          destroy_pval(item->u2.arglist);
04700       break;
04701       
04702    case PV_MACRO:
04703       /* fields: item->u1.str     == name of macro
04704                  item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
04705                item->u2.arglist->u1.str  == argument
04706                item->u2.arglist->next   == next arg
04707 
04708                item->u3.macro_statements == pval list of statements in macro body.
04709       */
04710       destroy_pval(item->u2.arglist);
04711       if (item->u1.str )
04712          free(item->u1.str);
04713       destroy_pval(item->u3.macro_statements);
04714       break;
04715          
04716    case PV_CONTEXT:
04717       /* fields: item->u1.str     == name of context
04718                  item->u2.statements == pval list of statements in context body
04719                item->u3.abstract == int 1 if an abstract keyword were present
04720       */
04721       if (item->u1.str)
04722          free(item->u1.str);
04723       destroy_pval(item->u2.statements);
04724       break;
04725          
04726    case PV_MACRO_CALL:
04727       /* fields: item->u1.str     == name of macro to call
04728                  item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
04729                item->u2.arglist->u1.str  == argument
04730                item->u2.arglist->next   == next arg
04731       */
04732       if (item->u1.str)
04733          free(item->u1.str);
04734       destroy_pval(item->u2.arglist);
04735       break;
04736          
04737    case PV_APPLICATION_CALL:
04738       /* fields: item->u1.str     == name of application to call
04739                  item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
04740                item->u2.arglist->u1.str  == argument
04741                item->u2.arglist->next   == next arg
04742       */
04743       if (item->u1.str)
04744          free(item->u1.str);
04745       destroy_pval(item->u2.arglist);
04746       break;
04747          
04748    case PV_CASE:
04749       /* fields: item->u1.str     == value of case
04750                  item->u2.statements == pval list of statements under the case
04751       */
04752       if (item->u1.str)
04753          free(item->u1.str);
04754       destroy_pval(item->u2.statements);
04755       break;
04756          
04757    case PV_PATTERN:
04758       /* fields: item->u1.str     == value of case
04759                  item->u2.statements == pval list of statements under the case
04760       */
04761       if (item->u1.str)
04762          free(item->u1.str);
04763       destroy_pval(item->u2.statements);
04764       break;
04765          
04766    case PV_DEFAULT:
04767       /* fields: 
04768                  item->u2.statements == pval list of statements under the case
04769       */
04770       destroy_pval(item->u2.statements);
04771       break;
04772          
04773    case PV_CATCH:
04774       /* fields: item->u1.str     == name of extension to catch
04775                  item->u2.statements == pval list of statements in context body
04776       */
04777       if (item->u1.str)
04778          free(item->u1.str);
04779       destroy_pval(item->u2.statements);
04780       break;
04781          
04782    case PV_SWITCHES:
04783       /* fields: item->u1.list     == pval list of PV_WORD elements, one per entry in the list
04784       */
04785       destroy_pval(item->u1.list);
04786       break;
04787          
04788    case PV_ESWITCHES:
04789       /* fields: item->u1.list     == pval list of PV_WORD elements, one per entry in the list
04790       */
04791       destroy_pval(item->u1.list);
04792       break;
04793          
04794    case PV_INCLUDES:
04795       /* fields: item->u1.list     == pval list of PV_WORD elements, one per entry in the list
04796                  item->u2.arglist  == pval list of 4 PV_WORD elements for time values
04797       */
04798       destroy_pval(item->u1.list);
04799       break;
04800          
04801    case PV_STATEMENTBLOCK:
04802       /* fields: item->u1.list     == pval list of statements in block, one per entry in the list
04803       */
04804       destroy_pval(item->u1.list);
04805       break;
04806          
04807    case PV_LOCALVARDEC:
04808    case PV_VARDEC:
04809       /* fields: item->u1.str     == variable name
04810                  item->u2.val     == variable value to assign
04811       */
04812       if (item->u1.str)
04813          free(item->u1.str);
04814       if (item->u2.val)
04815          free(item->u2.val);
04816       break;
04817          
04818    case PV_GOTO:
04819       /* fields: item->u1.list     == pval list of PV_WORD target names, up to 3, in order as given by user.
04820                  item->u1.list->u1.str  == where the data on a PV_WORD will always be.
04821       */
04822       
04823       destroy_pval(item->u1.list);
04824       break;
04825          
04826    case PV_LABEL:
04827       /* fields: item->u1.str     == label name
04828       */
04829       if (item->u1.str)
04830          free(item->u1.str);
04831       break;
04832          
04833    case PV_FOR:
04834       /* fields: item->u1.for_init     == a string containing the initalizer
04835                  item->u2.for_test     == a string containing the loop test
04836                  item->u3.for_inc      == a string containing the loop increment
04837 
04838                item->u4.for_statements == a pval list of statements in the for ()
04839       */
04840       if (item->u1.for_init)
04841          free(item->u1.for_init);
04842       if (item->u2.for_test)
04843          free(item->u2.for_test);
04844       if (item->u3.for_inc)
04845          free(item->u3.for_inc);
04846       destroy_pval(item->u4.for_statements);
04847       break;
04848          
04849    case PV_WHILE:
04850       /* fields: item->u1.str        == the while conditional, as supplied by user
04851 
04852                item->u2.statements == a pval list of statements in the while ()
04853       */
04854       if (item->u1.str)
04855          free(item->u1.str);
04856       destroy_pval(item->u2.statements);
04857       break;
04858          
04859    case PV_BREAK:
04860       /* fields: none
04861       */
04862       break;
04863          
04864    case PV_RETURN:
04865       /* fields: none
04866       */
04867       break;
04868          
04869    case PV_CONTINUE:
04870       /* fields: none
04871       */
04872       break;
04873          
04874    case PV_IFTIME:
04875       /* fields: item->u1.list        == the 4 time values, in PV_WORD structs, linked list
04876 
04877                item->u2.statements == a pval list of statements in the if ()
04878                item->u3.else_statements == a pval list of statements in the else
04879                                     (could be zero)
04880       */
04881       destroy_pval(item->u1.list);
04882       destroy_pval(item->u2.statements);
04883       if (item->u3.else_statements) {
04884          destroy_pval(item->u3.else_statements);
04885       }
04886       break;
04887          
04888    case PV_RANDOM:
04889       /* fields: item->u1.str        == the random percentage, as supplied by user
04890 
04891                item->u2.statements == a pval list of statements in the true part ()
04892                item->u3.else_statements == a pval list of statements in the else
04893                                     (could be zero)
04894       fall thru to If */
04895    case PV_IF:
04896       /* fields: item->u1.str        == the if conditional, as supplied by user
04897 
04898                item->u2.statements == a pval list of statements in the if ()
04899                item->u3.else_statements == a pval list of statements in the else
04900                                     (could be zero)
04901       */
04902       if (item->u1.str)
04903          free(item->u1.str);
04904       destroy_pval(item->u2.statements);
04905       if (item->u3.else_statements) {
04906          destroy_pval(item->u3.else_statements);
04907       }
04908       break;
04909          
04910    case PV_SWITCH:
04911       /* fields: item->u1.str        == the switch expression
04912 
04913                item->u2.statements == a pval list of statements in the switch, 
04914                                     (will be case statements, most likely!)
04915       */
04916       if (item->u1.str)
04917          free(item->u1.str);
04918       destroy_pval(item->u2.statements);
04919       break;
04920          
04921    case PV_EXTENSION:
04922       /* fields: item->u1.str        == the extension name, label, whatever it's called
04923 
04924                item->u2.statements == a pval list of statements in the extension
04925                item->u3.hints      == a char * hint argument
04926                item->u4.regexten   == an int boolean. non-zero says that regexten was specified
04927       */
04928       if (item->u1.str)
04929          free(item->u1.str);
04930       if (item->u3.hints)
04931          free(item->u3.hints);
04932       destroy_pval(item->u2.statements);
04933       break;
04934          
04935    case PV_IGNOREPAT:
04936       /* fields: item->u1.str        == the ignorepat data
04937       */
04938       if (item->u1.str)
04939          free(item->u1.str);
04940       break;
04941          
04942    case PV_GLOBALS:
04943       /* fields: item->u1.statements     == pval list of statements, usually vardecs
04944       */
04945       destroy_pval(item->u1.statements);
04946       break;
04947    }
04948    free(item);
04949 }
04950 
04951 void destroy_pval(pval *item) 
04952 {
04953    pval *i,*nxt;
04954    
04955    for (i=item; i; i=nxt) {
04956       nxt = i->next;
04957       
04958       destroy_pval_item(i);
04959    }
04960 }
04961 
04962 #ifdef AAL_ARGCHECK
04963 static char *ael_funclist[] =
04964 {
04965    "AGENT",
04966    "ARRAY",
04967    "BASE64_DECODE",
04968    "BASE64_ENCODE",
04969    "CALLERID",
04970    "CDR",
04971    "CHANNEL",
04972    "CHECKSIPDOMAIN",
04973    "CHECK_MD5",
04974    "CURL",
04975    "CUT",
04976    "DB",
04977    "DB_EXISTS",
04978    "DUNDILOOKUP",
04979    "ENUMLOOKUP",
04980    "ENV",
04981    "EVAL",
04982    "EXISTS",
04983    "FIELDQTY",
04984    "FILTER",
04985    "GROUP",
04986    "GROUP_COUNT",
04987    "GROUP_LIST",
04988    "GROUP_MATCH_COUNT",
04989    "IAXPEER",
04990    "IF",
04991    "IFTIME",
04992    "ISNULL",
04993    "KEYPADHASH",
04994    "LANGUAGE",
04995    "LEN",
04996    "MATH",
04997    "MD5",
04998    "MUSICCLASS",
04999    "QUEUEAGENTCOUNT",
05000    "QUEUE_MEMBER_COUNT",
05001    "QUEUE_MEMBER_LIST",
05002    "QUOTE",
05003    "RAND",
05004    "REGEX",
05005    "SET",
05006    "SHA1",
05007    "SIPCHANINFO",
05008    "SIPPEER",
05009    "SIP_HEADER",
05010    "SORT",
05011    "STAT",
05012    "STRFTIME",
05013    "STRPTIME",
05014    "TIMEOUT",
05015    "TXTCIDNAME",
05016    "URIDECODE",
05017    "URIENCODE",
05018    "VMCOUNT"
05019 };
05020 
05021 
05022 int ael_is_funcname(char *name)
05023 {
05024    int s,t;
05025    t = sizeof(ael_funclist)/sizeof(char*);
05026    s = 0;
05027    while ((s < t) && strcasecmp(name, ael_funclist[s])) 
05028       s++;
05029    if ( s < t )
05030       return 1;
05031    else
05032       return 0;
05033 }
05034 #endif    
05035 
05036 
05037 /* PVAL PI */
05038 
05039 /* ----------------- implementation ------------------- */
05040 
05041 
05042 int  pvalCheckType( pval *p, char *funcname, pvaltype type )
05043 {
05044    if (p->type != type)
05045    {
05046       ast_log(LOG_ERROR, "Func: %s the pval passed is not appropriate for this function!\n", funcname);
05047       return 0;
05048    }
05049    return 1;
05050 }
05051 
05052 
05053 pval *pvalCreateNode( pvaltype type )
05054 {
05055    pval *p = calloc(1,sizeof(pval)); /* why, oh why, don't I use ast_calloc? Way, way, way too messy if I do! */
05056    p->type = type;                   /* remember, this can be used externally or internally to asterisk */
05057    return p;
05058 }
05059 
05060 pvaltype pvalObjectGetType( pval *p )
05061 {
05062    return p->type;
05063 }
05064 
05065 
05066 void pvalWordSetString( pval *p, char *str)
05067 {
05068    if (!pvalCheckType(p, "pvalWordSetString", PV_WORD))
05069       return;
05070    p->u1.str = str;
05071 }
05072 
05073 char *pvalWordGetString( pval *p )
05074 {
05075    if (!pvalCheckType(p, "pvalWordGetString", PV_WORD))
05076       return 0;
05077    return p->u1.str;
05078 }
05079 
05080 
05081 void pvalMacroSetName( pval *p, char *name)
05082 {
05083    if (!pvalCheckType(p, "pvalMacroSetName", PV_MACRO))
05084       return;
05085    p->u1.str = name;
05086 }
05087 
05088 char *pvalMacroGetName( pval *p )
05089 {
05090    if (!pvalCheckType(p, "pvalMacroGetName", PV_MACRO))
05091       return 0;
05092    return p->u1.str;
05093 }
05094 
05095 void pvalMacroSetArglist( pval *p, pval *arglist )
05096 {
05097    if (!pvalCheckType(p, "pvalMacroSetArglist", PV_MACRO))
05098       return;
05099    p->u2.arglist = arglist;
05100 }
05101 
05102 void pvalMacroAddArg( pval *p, pval *arg ) /* single arg only! */
05103 {
05104    if (!pvalCheckType(p, "pvalMacroAddArg", PV_MACRO))
05105       return;
05106    if (!p->u2.arglist)
05107       p->u2.arglist = arg;
05108    else
05109       linku1(p->u2.arglist, arg);
05110 
05111 }
05112 
05113 pval *pvalMacroWalkArgs( pval *p, pval **arg )
05114 {
05115    if (!pvalCheckType(p, "pvalMacroWalkArgs", PV_MACRO))
05116       return 0;
05117    if (!(*arg))
05118       *arg = p->u2.arglist;
05119    else {
05120       *arg = (*arg)->next;
05121    }
05122    return *arg;
05123 }
05124 
05125 void pvalMacroAddStatement( pval *p, pval *statement )
05126 {
05127    if (!pvalCheckType(p, "pvalMacroAddStatement", PV_MACRO))
05128       return;
05129    if (!p->u3.macro_statements)
05130       p->u3.macro_statements = statement;
05131    else
05132       linku1(p->u3.macro_statements, statement);
05133 
05134    
05135 }
05136 
05137 pval *pvalMacroWalkStatements( pval *p, pval **next_statement )
05138 {
05139    if (!pvalCheckType(p, "pvalMacroWalkStatements", PV_MACRO))
05140       return 0;
05141    if (!(*next_statement))
05142       *next_statement = p->u3.macro_statements;
05143    else {
05144       *next_statement = (*next_statement)->next;
05145    }
05146    return *next_statement;
05147 }
05148 
05149 
05150 
05151 void pvalContextSetName( pval *p, char *name)
05152 {
05153    if (!pvalCheckType(p, "pvalContextSetName", PV_CONTEXT))
05154       return;
05155    p->u1.str = name;
05156 }
05157 
05158 char *pvalContextGetName( pval *p )
05159 {
05160    if (!pvalCheckType(p, "pvalContextGetName", PV_CONTEXT))
05161       return 0;
05162    return p->u1.str;
05163 }
05164 
05165 void pvalContextSetAbstract( pval *p )
05166 {
05167    if (!pvalCheckType(p, "pvalContextSetAbstract", PV_CONTEXT))
05168       return;
05169    p->u3.abstract = 1;
05170 }
05171 
05172 void pvalContextUnsetAbstract( pval *p )
05173 {
05174    if (!pvalCheckType(p, "pvalContextUnsetAbstract", PV_CONTEXT))
05175       return;
05176    p->u3.abstract = 0;
05177 }
05178 
05179 int  pvalContextGetAbstract( pval *p )
05180 {
05181    if (!pvalCheckType(p, "pvalContextGetAbstract", PV_CONTEXT))
05182       return 0;
05183    return p->u3.abstract;
05184 }
05185 
05186 
05187 
05188 void pvalContextAddStatement( pval *p, pval *statement) /* this includes SWITCHES, INCLUDES, IGNOREPAT, etc */
05189 {
05190    if (!pvalCheckType(p, "pvalContextAddStatement", PV_CONTEXT))
05191       return;
05192    if (!p->u2.statements)
05193       p->u2.statements = statement;
05194    else
05195       linku1(p->u2.statements, statement);
05196 }
05197 
05198 pval *pvalContextWalkStatements( pval *p, pval **statements )
05199 {
05200    if (!pvalCheckType(p, "pvalContextWalkStatements", PV_CONTEXT))
05201       return 0;
05202    if (!(*statements))
05203       *statements = p->u2.statements;
05204    else {
05205       *statements = (*statements)->next;
05206    }
05207    return *statements;
05208 }
05209 
05210 
05211 void pvalMacroCallSetMacroName( pval *p, char *name )
05212 {
05213    if (!pvalCheckType(p, "pvalMacroCallSetMacroName", PV_MACRO_CALL))
05214       return;
05215    p->u1.str = name;
05216 }
05217 
05218 char* pvalMacroCallGetMacroName( pval *p )
05219 {
05220    if (!pvalCheckType(p, "pvalMacroCallGetMacroName", PV_MACRO_CALL))
05221       return 0;
05222    return p->u1.str;
05223 }
05224 
05225 void pvalMacroCallSetArglist( pval *p, pval *arglist )
05226 {
05227    if (!pvalCheckType(p, "pvalMacroCallSetArglist", PV_MACRO_CALL))
05228       return;
05229    p->u2.arglist = arglist;
05230 }
05231 
05232 void pvalMacroCallAddArg( pval *p, pval *arg )
05233 {
05234    if (!pvalCheckType(p, "pvalMacroCallGetAddArg", PV_MACRO_CALL))
05235       return;
05236    if (!p->u2.arglist)
05237       p->u2.arglist = arg;
05238    else
05239       linku1(p->u2.arglist, arg);
05240 }
05241 
05242 pval *pvalMacroCallWalkArgs( pval *p, pval **args )
05243 {
05244    if (!pvalCheckType(p, "pvalMacroCallWalkArgs", PV_MACRO_CALL))
05245       return 0;
05246    if (!(*args))
05247       *args = p->u2.arglist;
05248    else {
05249       *args = (*args)->next;
05250    }
05251    return *args;
05252 }
05253 
05254 
05255 void pvalAppCallSetAppName( pval *p, char *name )
05256 {
05257    if (!pvalCheckType(p, "pvalAppCallSetAppName", PV_APPLICATION_CALL))
05258       return;
05259    p->u1.str = name;
05260 }
05261 
05262 char* pvalAppCallGetAppName( pval *p )
05263 {
05264    if (!pvalCheckType(p, "pvalAppCallGetAppName", PV_APPLICATION_CALL))
05265       return 0;
05266    return p->u1.str;
05267 }
05268 
05269 void pvalAppCallSetArglist( pval *p, pval *arglist )
05270 {
05271    if (!pvalCheckType(p, "pvalAppCallSetArglist", PV_APPLICATION_CALL))
05272       return;
05273    p->u2.arglist = arglist;
05274 }
05275 
05276 void pvalAppCallAddArg( pval *p, pval *arg )
05277 {
05278    if (!pvalCheckType(p, "pvalAppCallAddArg", PV_APPLICATION_CALL))
05279       return;
05280    if (!p->u2.arglist)
05281       p->u2.arglist = arg;
05282    else
05283       linku1(p->u2.arglist, arg);
05284 }
05285 
05286 pval *pvalAppCallWalkArgs( pval *p, pval **args )
05287 {
05288    if (!pvalCheckType(p, "pvalAppCallWalkArgs", PV_APPLICATION_CALL))
05289       return 0;
05290    if (!(*args))
05291       *args = p->u2.arglist;
05292    else {
05293       *args = (*args)->next;
05294    }
05295    return *args;
05296 }
05297 
05298 
05299 void pvalCasePatSetVal( pval *p, char *val )
05300 {
05301    if (!pvalCheckType(p, "pvalAppCallWalkArgs", PV_APPLICATION_CALL))
05302       return;
05303    p->u1.str = val;
05304 }
05305 
05306 char* pvalCasePatGetVal( pval *p )
05307 {
05308    return p->u1.str;
05309 }
05310 
05311 void pvalCasePatDefAddStatement( pval *p, pval *statement )
05312 {
05313    if (!p->u2.arglist)
05314       p->u2.statements = statement;
05315    else
05316       linku1(p->u2.statements, statement);
05317 }
05318 
05319 pval *pvalCasePatDefWalkStatements( pval *p, pval **statement )
05320 {
05321    if (!(*statement))
05322       *statement = p->u2.statements;
05323    else {
05324       *statement = (*statement)->next;
05325    }
05326    return *statement;
05327 }
05328 
05329 
05330 void pvalCatchSetExtName( pval *p, char *name )
05331 {
05332    if (!pvalCheckType(p, "pvalCatchSetExtName", PV_CATCH))
05333       return;
05334    p->u1.str = name;
05335 }
05336 
05337 char* pvalCatchGetExtName( pval *p )
05338 {
05339    if (!pvalCheckType(p, "pvalCatchGetExtName", PV_CATCH))
05340       return 0;
05341    return p->u1.str;
05342 }
05343 
05344 void pvalCatchSetStatement( pval *p, pval *statement )
05345 {
05346    if (!pvalCheckType(p, "pvalCatchSetStatement", PV_CATCH))
05347       return;
05348    p->u2.statements = statement;
05349 }
05350 
05351 pval *pvalCatchGetStatement( pval *p )
05352 {
05353    if (!pvalCheckType(p, "pvalCatchGetStatement", PV_CATCH))
05354       return 0;
05355    return p->u2.statements;
05356 }
05357 
05358 
05359 void pvalSwitchesAddSwitch( pval *p, char *name )
05360 {
05361    pval *s;
05362    if (!pvalCheckType(p, "pvalSwitchesAddSwitch", PV_SWITCHES))
05363       return;
05364    s = pvalCreateNode(PV_WORD);
05365    s->u1.str = name;
05366    p->u1.list = linku1(p->u1.list, s);
05367 }
05368 
05369 char* pvalSwitchesWalkNames( pval *p, pval **next_item )
05370 {
05371    if (!pvalCheckType(p, "pvalSwitchesWalkNames", PV_SWITCHES))
05372       return 0;
05373    if (!(*next_item))
05374       *next_item = p->u1.list;
05375    else {
05376       *next_item = (*next_item)->next;
05377    }
05378    return (*next_item)->u1.str;
05379 }
05380 
05381 void pvalESwitchesAddSwitch( pval *p, char *name )
05382 {
05383    pval *s;
05384    if (!pvalCheckType(p, "pvalESwitchesAddSwitch", PV_ESWITCHES))
05385       return;
05386    s = pvalCreateNode(PV_WORD);
05387    s->u1.str = name;
05388    p->u1.list = linku1(p->u1.list, s);
05389 }
05390 
05391 char* pvalESwitchesWalkNames( pval *p, pval **next_item )
05392 {
05393    if (!pvalCheckType(p, "pvalESwitchesWalkNames", PV_ESWITCHES))
05394       return 0;
05395    if (!(*next_item))
05396       *next_item = p->u1.list;
05397    else {
05398       *next_item = (*next_item)->next;
05399    }
05400    return (*next_item)->u1.str;
05401 }
05402 
05403 
05404 void pvalIncludesAddInclude( pval *p, const char *include )
05405 {
05406    pval *s;
05407    if (!pvalCheckType(p, "pvalIncludesAddSwitch", PV_INCLUDES))
05408       return;
05409    s = pvalCreateNode(PV_WORD);
05410    s->u1.str = (char *)include;
05411    p->u1.list = linku1(p->u1.list, s);
05412 }
05413  /* an include is a WORD with string set to path */
05414 
05415 void pvalIncludesAddIncludeWithTimeConstraints( pval *p, const char *include, char *hour_range, char *dom_range, char *dow_range, char *month_range )
05416 {
05417    pval *hr = pvalCreateNode(PV_WORD);
05418    pval *dom = pvalCreateNode(PV_WORD);
05419    pval *dow = pvalCreateNode(PV_WORD);
05420    pval *mon = pvalCreateNode(PV_WORD);
05421    pval *s = pvalCreateNode(PV_WORD);
05422    
05423    if (!pvalCheckType(p, "pvalIncludeAddIncludeWithTimeConstraints", PV_INCLUDES))
05424       return;
05425 
05426    s->u1.str = (char *)include;
05427    p->u1.list = linku1(p->u1.list, s);
05428 
05429    hr->u1.str = hour_range;
05430    dom->u1.str = dom_range;
05431    dow->u1.str = dow_range;
05432    mon->u1.str = month_range;
05433 
05434    s->u2.arglist = hr;
05435 
05436    hr->next = dom;
05437    dom->next = dow;
05438    dow->next = mon;
05439    mon->next = 0;
05440 }
05441  /* is this right??? come back and correct it */ /*the ptr is to the WORD */
05442 void pvalIncludeGetTimeConstraints( pval *p, char **hour_range, char **dom_range, char **dow_range, char **month_range )
05443 {
05444    if (!pvalCheckType(p, "pvalIncludeGetTimeConstraints", PV_WORD))
05445       return;
05446    if (p->u2.arglist) {
05447       *hour_range = p->u2.arglist->u1.str;
05448       *dom_range = p->u2.arglist->next->u1.str;
05449       *dow_range = p->u2.arglist->next->next->u1.str;
05450       *month_range = p->u2.arglist->next->next->next->u1.str;
05451    } else {
05452       *hour_range = 0;
05453       *dom_range = 0;
05454       *dow_range = 0;
05455       *month_range = 0;
05456    }
05457 }
05458  /* is this right??? come back and correct it */ /*the ptr is to the WORD */
05459 char* pvalIncludesWalk( pval *p, pval **next_item )
05460 {
05461    if (!pvalCheckType(p, "pvalIncludesWalk", PV_INCLUDES))
05462       return 0;
05463    if (!(*next_item))
05464       *next_item = p->u1.list;
05465    else {
05466       *next_item = (*next_item)->next;
05467    }
05468    return (*next_item)->u1.str;
05469 }
05470 
05471 
05472 void pvalStatementBlockAddStatement( pval *p, pval *statement)
05473 {
05474    if (!pvalCheckType(p, "pvalStatementBlockAddStatement", PV_STATEMENTBLOCK))
05475       return;
05476    p->u1.list = linku1(p->u1.list, statement);
05477 }
05478 
05479 pval *pvalStatementBlockWalkStatements( pval *p, pval **next_statement)
05480 {
05481    if (!pvalCheckType(p, "pvalStatementBlockWalkStatements", PV_STATEMENTBLOCK))
05482       return 0;
05483    if (!(*next_statement))
05484       *next_statement = p->u1.list;
05485    else {
05486       *next_statement = (*next_statement)->next;
05487    }
05488    return *next_statement;
05489 }
05490 
05491 void pvalVarDecSetVarname( pval *p, char *name )
05492 {
05493    if (!pvalCheckType(p, "pvalVarDecSetVarname", PV_VARDEC))
05494       return;
05495    p->u1.str = name;
05496 }
05497 
05498 void pvalVarDecSetValue( pval *p, char *value )
05499 {
05500    if (!pvalCheckType(p, "pvalVarDecSetValue", PV_VARDEC))
05501       return;
05502    p->u2.val = value;
05503 }
05504 
05505 char* pvalVarDecGetVarname( pval *p )
05506 {
05507    if (!pvalCheckType(p, "pvalVarDecGetVarname", PV_VARDEC))
05508       return 0;
05509    return p->u1.str;
05510 }
05511 
05512 char* pvalVarDecGetValue( pval *p )
05513 {
05514    if (!pvalCheckType(p, "pvalVarDecGetValue", PV_VARDEC))
05515       return 0;
05516    return p->u2.val;
05517 }
05518 
05519 void pvalGotoSetTarget( pval *p, char *context, char *exten, char *label )
05520 {
05521    pval *con, *ext, *pri;
05522    
05523    if (!pvalCheckType(p, "pvalGotoSetTarget", PV_GOTO))
05524       return;
05525    if (context && strlen(context)) {
05526       con = pvalCreateNode(PV_WORD);
05527       ext = pvalCreateNode(PV_WORD);
05528       pri = pvalCreateNode(PV_WORD);
05529       
05530       con->u1.str = context;
05531       ext->u1.str = exten;
05532       pri->u1.str = label;
05533       
05534       con->next = ext;
05535       ext->next = pri;
05536       p->u1.list = con;
05537    } else if (exten && strlen(exten)) {
05538       ext = pvalCreateNode(PV_WORD);
05539       pri = pvalCreateNode(PV_WORD);
05540       
05541       ext->u1.str = exten;
05542       pri->u1.str = label;
05543       
05544       ext->next = pri;
05545       p->u1.list = ext;
05546    } else {
05547       pri = pvalCreateNode(PV_WORD);
05548       
05549       pri->u1.str = label;
05550       
05551       p->u1.list = pri;
05552    }
05553 }
05554 
05555 void pvalGotoGetTarget( pval *p, char **context, char **exten, char **label )
05556 {
05557    if (!pvalCheckType(p, "pvalGotoGetTarget", PV_GOTO))
05558       return;
05559    if (p->u1.list && p->u1.list->next && p->u1.list->next->next) {
05560       *context = p->u1.list->u1.str;
05561       *exten = p->u1.list->next->u1.str;
05562       *label = p->u1.list->next->next->u1.str;
05563       
05564    } else if (p->u1.list && p->u1.list->next ) {
05565       *exten = p->u1.list->u1.str;
05566       *label = p->u1.list->next->u1.str;
05567       *context = 0;
05568 
05569    } else if (p->u1.list) {
05570       *label = p->u1.list->u1.str;
05571       *context = 0;
05572       *exten = 0;
05573       
05574    } else {
05575       *context = 0;
05576       *exten = 0;
05577       *label = 0;
05578    }
05579 }
05580 
05581 
05582 void pvalLabelSetName( pval *p, char *name )
05583 {
05584    if (!pvalCheckType(p, "pvalLabelSetName", PV_LABEL))
05585       return;
05586    p->u1.str = name;
05587 }
05588 
05589 char* pvalLabelGetName( pval *p )
05590 {
05591    if (!pvalCheckType(p, "pvalLabelGetName", PV_LABEL))
05592       return 0;
05593    return p->u1.str;
05594 }
05595 
05596 
05597 void pvalForSetInit( pval *p, char *init )
05598 {
05599    if (!pvalCheckType(p, "pvalForSetInit", PV_FOR))
05600       return;
05601    p->u1.for_init = init;
05602 }
05603 
05604 void pvalForSetTest( pval *p, char *test )
05605 {
05606    if (!pvalCheckType(p, "pvalForSetTest", PV_FOR))
05607       return;
05608    p->u2.for_test = test;
05609 }
05610 
05611 void pvalForSetInc( pval *p, char *inc )
05612 {
05613    if (!pvalCheckType(p, "pvalForSetInc", PV_FOR))
05614       return;
05615    p->u3.for_inc = inc;
05616 }
05617 
05618 void pvalForSetStatement( pval *p, pval *statement )
05619 {
05620    if (!pvalCheckType(p, "pvalForSetStatement", PV_FOR))
05621       return;
05622    p->u4.for_statements = statement;
05623 }
05624 
05625 char* pvalForGetInit( pval *p )
05626 {
05627    if (!pvalCheckType(p, "pvalForGetInit", PV_FOR))
05628       return 0;
05629    return p->u1.for_init;
05630 }
05631 
05632 char* pvalForGetTest( pval *p )
05633 {
05634    if (!pvalCheckType(p, "pvalForGetTest", PV_FOR))
05635       return 0;
05636    return p->u2.for_test;
05637 }
05638 
05639 char* pvalForGetInc( pval *p )
05640 {
05641    if (!pvalCheckType(p, "pvalForGetInc", PV_FOR))
05642       return 0;
05643    return p->u3.for_inc;
05644 }
05645 
05646 pval* pvalForGetStatement( pval *p )
05647 {
05648    if (!pvalCheckType(p, "pvalForGetStatement", PV_FOR))
05649       return 0;
05650    return p->u4.for_statements;
05651 }
05652 
05653 
05654 
05655 void pvalIfSetCondition( pval *p, char *expr )
05656 {
05657    if (!pvalCheckType(p, "pvalIfSetCondition", PV_IF))
05658       return;
05659    p->u1.str = expr;
05660 }
05661 
05662 char* pvalIfGetCondition( pval *p )
05663 {
05664    if (!pvalCheckType(p, "pvalIfGetCondition", PV_IFTIME))
05665       return 0;
05666    return p->u1.str;
05667 }
05668 
05669 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 */
05670 {
05671    pval *hr = pvalCreateNode(PV_WORD);
05672    pval *dow = pvalCreateNode(PV_WORD);
05673    pval *dom = pvalCreateNode(PV_WORD);
05674    pval *mon = pvalCreateNode(PV_WORD);
05675    if (!pvalCheckType(p, "pvalIfTimeSetCondition", PV_IFTIME))
05676       return;
05677    pvalWordSetString(hr, hour_range);
05678    pvalWordSetString(dow, dow_range);
05679    pvalWordSetString(dom, dom_range);
05680    pvalWordSetString(mon, mon_range);
05681    dom->next = mon;
05682    dow->next = dom;
05683    hr->next = dow;
05684    p->u1.list = hr;
05685 }
05686 
05687  /* is this right??? come back and correct it */
05688 void pvalIfTimeGetCondition( pval *p, char **hour_range, char **dow_range, char **dom_range, char **month_range )
05689 {
05690    if (!pvalCheckType(p, "pvalIfTimeGetCondition", PV_IFTIME))
05691       return;
05692    *hour_range = p->u1.list->u1.str;
05693    *dow_range = p->u1.list->next->u1.str;
05694    *dom_range = p->u1.list->next->next->u1.str;
05695    *month_range = p->u1.list->next->next->next->u1.str;
05696 }
05697 
05698 void pvalRandomSetCondition( pval *p, char *percent )
05699 {
05700    if (!pvalCheckType(p, "pvalRandomSetCondition", PV_RANDOM))
05701       return;
05702    p->u1.str = percent;
05703 }
05704 
05705 char* pvalRandomGetCondition( pval *p )
05706 {
05707    if (!pvalCheckType(p, "pvalRandomGetCondition", PV_RANDOM))
05708       return 0;
05709    return p->u1.str;
05710 }
05711 
05712 void pvalConditionalSetThenStatement( pval *p, pval *statement )
05713 {
05714    p->u2.statements = statement;
05715 }
05716 
05717 void pvalConditionalSetElseStatement( pval *p, pval *statement )
05718 {
05719    p->u3.else_statements = statement;
05720 }
05721 
05722 pval* pvalConditionalGetThenStatement( pval *p )
05723 {
05724    return p->u2.statements;
05725 }
05726 
05727 pval* pvalConditionalGetElseStatement( pval *p )
05728 {
05729    return p->u3.else_statements;
05730 }
05731 
05732 void pvalSwitchSetTestexpr( pval *p, char *expr )
05733 {
05734    if (!pvalCheckType(p, "pvalSwitchSetTestexpr", PV_SWITCH))
05735       return;
05736    p->u1.str = expr;
05737 }
05738 
05739 char* pvalSwitchGetTestexpr( pval *p )
05740 {
05741    if (!pvalCheckType(p, "pvalSwitchGetTestexpr", PV_SWITCH))
05742       return 0;
05743    return p->u1.str;
05744 }
05745 
05746 void pvalSwitchAddCase( pval *p, pval *Case )
05747 {
05748    if (!pvalCheckType(p, "pvalSwitchAddCase", PV_SWITCH))
05749       return;
05750    if (!pvalCheckType(Case, "pvalSwitchAddCase", PV_CASE))
05751       return;
05752    if (!p->u2.statements)
05753       p->u2.statements = Case;
05754    else
05755       linku1(p->u2.statements, Case);
05756 }
05757 
05758 pval* pvalSwitchWalkCases( pval *p, pval **next_case )
05759 {
05760    if (!pvalCheckType(p, "pvalSwitchWalkCases", PV_SWITCH))
05761       return 0;
05762    if (!(*next_case))
05763       *next_case = p->u2.statements;
05764    else {
05765       *next_case = (*next_case)->next;
05766    }
05767    return *next_case;
05768 }
05769 
05770 
05771 void pvalExtenSetName( pval *p, char *name )
05772 {
05773    if (!pvalCheckType(p, "pvalExtenSetName", PV_EXTENSION))
05774       return;
05775    p->u1.str = name;
05776 }
05777 
05778 char* pvalExtenGetName( pval *p )
05779 {
05780    if (!pvalCheckType(p, "pvalExtenGetName", PV_EXTENSION))
05781       return 0;
05782    return p->u1.str;
05783 }
05784 
05785 void pvalExtenSetRegexten( pval *p )
05786 {
05787    if (!pvalCheckType(p, "pvalExtenSetRegexten", PV_EXTENSION))
05788       return;
05789    p->u4.regexten = 1;
05790 }
05791 
05792 void pvalExtenUnSetRegexten( pval *p )
05793 {
05794    if (!pvalCheckType(p, "pvalExtenUnSetRegexten", PV_EXTENSION))
05795       return;
05796    p->u4.regexten = 0;
05797 }
05798 
05799 int pvalExtenGetRegexten( pval *p )
05800 {
05801    if (!pvalCheckType(p, "pvalExtenGetRegexten", PV_EXTENSION))
05802       return 0;
05803    return p->u4.regexten;
05804 }
05805 
05806 void pvalExtenSetHints( pval *p, char *hints )
05807 {
05808    if (!pvalCheckType(p, "pvalExtenSetHints", PV_EXTENSION))
05809       return;
05810    p->u3.hints = hints;
05811 }
05812 
05813 char* pvalExtenGetHints( pval *p )
05814 {
05815    if (!pvalCheckType(p, "pvalExtenGetHints", PV_EXTENSION))
05816       return 0;
05817    return p->u3.hints;
05818 }
05819 
05820 void pvalExtenSetStatement( pval *p, pval *statement )
05821 {
05822    if (!pvalCheckType(p, "pvalExtenSetStatement", PV_EXTENSION))
05823       return;
05824    p->u2.statements = statement;
05825 }
05826 
05827 pval* pvalExtenGetStatement( pval *p )
05828 {
05829    if (!pvalCheckType(p, "pvalExtenGetStatement", PV_EXTENSION))
05830       return 0;
05831    return p->u2.statements;
05832 }
05833 
05834 
05835 void pvalIgnorePatSetPattern( pval *p, char *pat )
05836 {
05837    if (!pvalCheckType(p, "pvalIgnorePatSetPattern", PV_IGNOREPAT))
05838       return;
05839    p->u1.str = pat;
05840 }
05841 
05842 char* pvalIgnorePatGetPattern( pval *p )
05843 {
05844    if (!pvalCheckType(p, "pvalIgnorePatGetPattern", PV_IGNOREPAT))
05845       return 0;
05846    return p->u1.str;
05847 }
05848 
05849 
05850 void pvalGlobalsAddStatement( pval *p, pval *statement )
05851 {
05852    if (p->type != PV_GLOBALS) {
05853       ast_log(LOG_ERROR, "pvalGlobalsAddStatement called where first arg is not a Globals!\n");
05854    } else {
05855       if (!p->u1.statements) {
05856          p->u1.statements = statement;
05857       } else {
05858          p->u1.statements = linku1(p->u1.statements,statement);
05859       }
05860    }
05861 }
05862 
05863 pval* pvalGlobalsWalkStatements( pval *p, pval **next_statement )
05864 {
05865    if (!pvalCheckType(p, "pvalGlobalsWalkStatements", PV_GLOBALS))
05866       return 0;
05867    if (!*next_statement) {
05868       *next_statement = p;
05869       return p;
05870    } else {
05871       *next_statement = (*next_statement)->next;
05872       return (*next_statement)->next;
05873    }
05874 }
05875 
05876 
05877 void pvalTopLevAddObject( pval *p, pval *contextOrObj )
05878 {
05879    if (p) {
05880       linku1(p,contextOrObj);
05881    } else {
05882       ast_log(LOG_ERROR, "First arg to pvalTopLevel is NULL!\n");
05883    }
05884 }
05885 
05886 pval *pvalTopLevWalkObjects(pval *p, pval **next_obj )
05887 {
05888    if (!*next_obj) {
05889       *next_obj = p;
05890       return p;
05891    } else {
05892       *next_obj = (*next_obj)->next;
05893       return (*next_obj)->next;
05894    }
05895 }
05896 
05897 /* append second element to the list in the first one via next pointers */
05898 pval * linku1(pval *head, pval *tail)
05899 {
05900    if (!head)
05901       return tail;
05902    if (tail) {
05903       if (!head->next) {
05904          head->next = tail;
05905       } else {
05906          head->u1_last->next = tail;
05907       }
05908       head->u1_last = tail;
05909       tail->prev = head; /* the dad link only points to containers */
05910    }
05911    return head;
05912 }
05913 

Generated on Thu Apr 16 06:27:43 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6