Sat Nov 1 06:28:35 2008

Asterisk developer's documentation


pbx_ael.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2006, Digium, Inc.
00005  *
00006  * Steve Murphy <murf@parsetree.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Compile symbolic Asterisk Extension Logic into Asterisk extensions, version 2.
00022  * 
00023  */
00024 
00025 #include "asterisk.h"
00026 
00027 #if !defined(STANDALONE_AEL)
00028 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 144924 $")
00029 #endif
00030 
00031 #include <sys/types.h>
00032 #include <stdlib.h>
00033 #include <unistd.h>
00034 #include <stdio.h>
00035 #include <string.h>
00036 #include <ctype.h>
00037 #include <errno.h>
00038 #include <regex.h>
00039 #include <sys/stat.h>
00040 
00041 #include "asterisk/pbx.h"
00042 #include "asterisk/config.h"
00043 #include "asterisk/module.h"
00044 #include "asterisk/logger.h"
00045 #include "asterisk/cli.h"
00046 #include "asterisk/app.h"
00047 #include "asterisk/callerid.h"
00048 #include "asterisk/ael_structs.h"
00049 #ifdef AAL_ARGCHECK
00050 #include "asterisk/argdesc.h"
00051 #endif
00052 
00053 static char expr_output[2096];
00054 
00055 /* these functions are in ../ast_expr2.fl */
00056 
00057 #define DEBUG_READ   (1 << 0)
00058 #define DEBUG_TOKENS (1 << 1)
00059 #define DEBUG_MACROS (1 << 2)
00060 #define DEBUG_CONTEXTS (1 << 3)
00061 
00062 static char *config = "extensions.ael";
00063 static char *registrar = "pbx_ael";
00064 static int pbx_load_module(void);
00065 
00066 static int errs, warns;
00067 static int notes;
00068 
00069 #ifndef AAL_ARGCHECK
00070 /* for the time being, short circuit all the AAL related structures
00071    without permanently removing the code; after/during the AAL 
00072    development, this code can be properly re-instated 
00073 */
00074 
00075 /* null definitions for structs passed down the infrastructure */
00076 struct argapp
00077 {
00078    struct argapp *next;
00079 };
00080 
00081 #endif
00082 
00083 #ifdef AAL_ARGCHECK
00084 int option_matches_j( struct argdesc *should, pval *is, struct argapp *app);
00085 int option_matches( struct argdesc *should, pval *is, struct argapp *app);
00086 int ael_is_funcname(char *name);
00087 #endif
00088 
00089 int check_app_args(pval *appcall, pval *arglist, struct argapp *app);
00090 void check_pval(pval *item, struct argapp *apps, int in_globals);
00091 void check_pval_item(pval *item, struct argapp *apps, int in_globals);
00092 void check_switch_expr(pval *item, struct argapp *apps);
00093 void ast_expr_register_extra_error_info(char *errmsg);
00094 void ast_expr_clear_extra_error_info(void);
00095 int  ast_expr(char *expr, char *buf, int length);
00096 struct pval *find_macro(char *name);
00097 struct pval *find_context(char *name);
00098 struct pval *find_context(char *name);
00099 struct pval *find_macro(char *name);
00100 struct ael_priority *new_prio(void);
00101 struct ael_extension *new_exten(void);
00102 void linkprio(struct ael_extension *exten, struct ael_priority *prio, struct ael_extension *mother_exten);
00103 void destroy_extensions(struct ael_extension *exten);
00104 static void linkexten(struct ael_extension *exten, struct ael_extension *add);
00105 static void gen_prios(struct ael_extension *exten, char *label, pval *statement, struct ael_extension *mother_exten, struct ast_context *context );
00106 void set_priorities(struct ael_extension *exten);
00107 void add_extensions(struct ael_extension *exten);
00108 void ast_compile_ael2(struct ast_context **local_contexts, struct pval *root);
00109 void destroy_pval(pval *item);
00110 void destroy_pval_item(pval *item);
00111 int is_float(char *arg );
00112 int is_int(char *arg );
00113 int is_empty(char *arg);
00114 static pval *current_db=0;
00115 static pval *current_context=0;
00116 static pval *current_extension=0;
00117 
00118 static const char *match_context;
00119 static const char *match_exten;
00120 static const char *match_label;
00121 static int in_abstract_context;
00122 static int count_labels; /* true, put matcher in label counting mode */
00123 static int label_count;  /* labels are only meant to be counted in a context or exten */
00124 static int return_on_context_match;
00125 static pval *last_matched_label;
00126 struct pval *match_pval(pval *item);
00127 static void check_timerange(pval *p);
00128 static void check_dow(pval *DOW);
00129 static void check_day(pval *DAY);
00130 static void check_month(pval *MON);
00131 static void check_expr2_input(pval *expr, char *str);
00132 static int extension_matches(pval *here, const char *exten, const char *pattern);
00133 static void check_goto(pval *item);
00134 static void find_pval_goto_item(pval *item, int lev);
00135 static void find_pval_gotos(pval *item, int lev);
00136 
00137 static struct pval *find_label_in_current_context(char *exten, char *label, pval *curr_cont);
00138 static struct pval *find_first_label_in_current_context(char *label, pval *curr_cont);
00139 static void print_pval_list(FILE *fin, pval *item, int depth);
00140 
00141 static struct pval *find_label_in_current_extension(const char *label, pval *curr_ext);
00142 static struct pval *find_label_in_current_db(const char *context, const char *exten, const char *label);
00143 static pval *get_goto_target(pval *item);
00144 static int label_inside_case(pval *label);
00145 static void attach_exten(struct ael_extension **list, struct ael_extension *newmem);
00146 static void fix_gotos_in_extensions(struct ael_extension *exten);
00147 static pval *get_extension_or_contxt(pval *p);
00148 static pval *get_contxt(pval *p);
00149 static void remove_spaces_before_equals(char *str);
00150 static void substitute_commas(char *str);
00151 
00152 /* I am adding this code to substitute commas with vertbars in the args to apps */
00153 static void substitute_commas(char *str)
00154 {
00155    char *p = str;
00156    
00157    while (p && *p)
00158    {
00159       if (*p == ',' && ((p != str && *(p-1) != '\\')
00160             || p == str))
00161          *p = '|';
00162       if (*p == '\\' && *(p+1) == ',') { /* learning experience: the '\,' is turned into just ',' by pbx_config; So we need to do the same */
00163          char *q = p;
00164          while (*q) {  /* move the ',' and everything after it up 1 char */
00165             *q = *(q+1);
00166             q++;
00167          }
00168       }
00169       p++;
00170    }
00171 }
00172 
00173 
00174 /* PRETTY PRINTER FOR AEL:  ============================================================================= */
00175 
00176 static void print_pval(FILE *fin, pval *item, int depth)
00177 {
00178    int i;
00179    pval *lp;
00180    
00181    for (i=0; i<depth; i++) {
00182       fprintf(fin, "\t"); /* depth == indentation */
00183    }
00184    
00185    switch ( item->type ) {
00186    case PV_WORD:
00187       fprintf(fin,"%s;\n", item->u1.str); /* usually, words are encapsulated in something else */
00188       break;
00189       
00190    case PV_MACRO:
00191       fprintf(fin,"macro %s(", item->u1.str);
00192       for (lp=item->u2.arglist; lp; lp=lp->next) {
00193          if (lp != item->u2.arglist )
00194             fprintf(fin,", ");
00195          fprintf(fin,"%s", lp->u1.str);
00196       }
00197       fprintf(fin,") {\n");
00198       print_pval_list(fin,item->u3.macro_statements,depth+1);
00199       for (i=0; i<depth; i++) {
00200          fprintf(fin,"\t"); /* depth == indentation */
00201       }
00202       fprintf(fin,"};\n\n");
00203       break;
00204          
00205    case PV_CONTEXT:
00206       if ( item->u3.abstract )
00207          fprintf(fin,"abstract context %s {\n", item->u1.str);
00208       else
00209          fprintf(fin,"context %s {\n", item->u1.str);
00210       print_pval_list(fin,item->u2.statements,depth+1);
00211       for (i=0; i<depth; i++) {
00212          fprintf(fin,"\t"); /* depth == indentation */
00213       }
00214       fprintf(fin,"};\n\n");
00215       break;
00216          
00217    case PV_MACRO_CALL:
00218       fprintf(fin,"&%s(", item->u1.str);
00219       for (lp=item->u2.arglist; lp; lp=lp->next) {
00220          if ( lp != item->u2.arglist )
00221             fprintf(fin,", ");
00222          fprintf(fin,"%s", lp->u1.str);
00223       }
00224       fprintf(fin,");\n");
00225       break;
00226          
00227    case PV_APPLICATION_CALL:
00228       fprintf(fin,"%s(", item->u1.str);
00229       for (lp=item->u2.arglist; lp; lp=lp->next) {
00230          if ( lp != item->u2.arglist )
00231             fprintf(fin,",");
00232          fprintf(fin,"%s", lp->u1.str);
00233       }
00234       fprintf(fin,");\n");
00235       break;
00236          
00237    case PV_CASE:
00238       fprintf(fin,"case %s:\n", item->u1.str);
00239       print_pval_list(fin,item->u2.statements, depth+1);
00240       break;
00241          
00242    case PV_PATTERN:
00243       fprintf(fin,"pattern %s:\n", item->u1.str);
00244       print_pval_list(fin,item->u2.statements, depth+1);
00245       break;
00246          
00247    case PV_DEFAULT:
00248       fprintf(fin,"default:\n");
00249       print_pval_list(fin,item->u2.statements, depth+1);
00250       break;
00251          
00252    case PV_CATCH:
00253       fprintf(fin,"catch %s {\n", item->u1.str);
00254       print_pval_list(fin,item->u2.statements, depth+1);
00255       for (i=0; i<depth; i++) {
00256          fprintf(fin,"\t"); /* depth == indentation */
00257       }
00258       fprintf(fin,"};\n");
00259       break;
00260          
00261    case PV_SWITCHES:
00262       fprintf(fin,"switches {\n");
00263       print_pval_list(fin,item->u1.list,depth+1);
00264       for (i=0; i<depth; i++) {
00265          fprintf(fin,"\t"); /* depth == indentation */
00266       }
00267       fprintf(fin,"};\n");
00268       break;
00269          
00270    case PV_ESWITCHES:
00271       fprintf(fin,"eswitches {\n");
00272       print_pval_list(fin,item->u1.list,depth+1);
00273       for (i=0; i<depth; i++) {
00274          fprintf(fin,"\t"); /* depth == indentation */
00275       }
00276       fprintf(fin,"};\n");
00277       break;
00278          
00279    case PV_INCLUDES:
00280       fprintf(fin,"includes {\n");
00281       for (lp=item->u1.list; lp; lp=lp->next) {
00282          for (i=0; i<depth+1; i++) {
00283             fprintf(fin,"\t"); /* depth == indentation */
00284          }
00285          fprintf(fin,"%s", lp->u1.str); /* usually, words are encapsulated in something else */
00286          if ( lp->u2.arglist )
00287             fprintf(fin,"|%s|%s|%s|%s", 
00288                   lp->u2.arglist->u1.str,
00289                   lp->u2.arglist->next->u1.str,
00290                   lp->u2.arglist->next->next->u1.str,
00291                   lp->u2.arglist->next->next->next->u1.str
00292                );
00293          fprintf(fin,";\n"); /* usually, words are encapsulated in something else */
00294       }
00295       
00296       print_pval_list(fin,item->u1.list,depth+1);
00297       for (i=0; i<depth; i++) {
00298          fprintf(fin,"\t"); /* depth == indentation */
00299       }
00300       fprintf(fin,"};\n");
00301       break;
00302          
00303    case PV_STATEMENTBLOCK:
00304       fprintf(fin,"{\n");
00305       print_pval_list(fin,item->u1.list, depth+1);
00306       for (i=0; i<depth; i++) {
00307          fprintf(fin,"\t"); /* depth == indentation */
00308       }
00309       fprintf(fin,"};\n");
00310       break;
00311          
00312    case PV_VARDEC:
00313       fprintf(fin,"%s=%s;\n", item->u1.str, item->u2.val);
00314       break;
00315          
00316    case PV_GOTO:
00317       fprintf(fin,"goto %s", item->u1.list->u1.str);
00318       if ( item->u1.list->next )
00319          fprintf(fin,"|%s", item->u1.list->next->u1.str);
00320       if ( item->u1.list->next && item->u1.list->next->next )
00321          fprintf(fin,"|%s", item->u1.list->next->next->u1.str);
00322       fprintf(fin,"\n");
00323       break;
00324          
00325    case PV_LABEL:
00326       fprintf(fin,"%s:\n", item->u1.str);
00327       break;
00328          
00329    case PV_FOR:
00330       fprintf(fin,"for (%s; %s; %s)\n", item->u1.for_init, item->u2.for_test, item->u3.for_inc);
00331       print_pval_list(fin,item->u4.for_statements,depth+1);
00332       break;
00333          
00334    case PV_WHILE:
00335       fprintf(fin,"while (%s)\n", item->u1.str);
00336       print_pval_list(fin,item->u2.statements,depth+1);
00337       break;
00338          
00339    case PV_BREAK:
00340       fprintf(fin,"break;\n");
00341       break;
00342          
00343    case PV_RETURN:
00344       fprintf(fin,"return;\n");
00345       break;
00346          
00347    case PV_CONTINUE:
00348       fprintf(fin,"continue;\n");
00349       break;
00350          
00351    case PV_RANDOM:
00352    case PV_IFTIME:
00353    case PV_IF:
00354       if ( item->type == PV_IFTIME ) {
00355          
00356          fprintf(fin,"ifTime ( %s|%s|%s|%s )\n", 
00357                item->u1.list->u1.str, 
00358                item->u1.list->next->u1.str, 
00359                item->u1.list->next->next->u1.str, 
00360                item->u1.list->next->next->next->u1.str
00361                );
00362       } else if ( item->type == PV_RANDOM ) {
00363          fprintf(fin,"random ( %s )\n", item->u1.str );
00364       } else
00365          fprintf(fin,"if ( %s )\n", item->u1.str);
00366       if ( item->u2.statements && item->u2.statements->next ) {
00367          for (i=0; i<depth; i++) {
00368             fprintf(fin,"\t"); /* depth == indentation */
00369          }
00370          fprintf(fin,"{\n");
00371          print_pval_list(fin,item->u2.statements,depth+1);
00372          for (i=0; i<depth; i++) {
00373             fprintf(fin,"\t"); /* depth == indentation */
00374          }
00375          if ( item->u3.else_statements )
00376             fprintf(fin,"}\n");
00377          else
00378             fprintf(fin,"};\n");
00379       } else if (item->u2.statements ) {
00380          print_pval_list(fin,item->u2.statements,depth+1);
00381       } else {
00382          if (item->u3.else_statements )
00383             fprintf(fin, " {} ");
00384          else
00385             fprintf(fin, " {}; ");
00386       }
00387       if ( item->u3.else_statements ) {
00388          for (i=0; i<depth; i++) {
00389             fprintf(fin,"\t"); /* depth == indentation */
00390          }
00391          fprintf(fin,"else\n");
00392          print_pval_list(fin,item->u3.else_statements, depth);
00393       }
00394       break;
00395          
00396    case PV_SWITCH:
00397       fprintf(fin,"switch( %s ) {\n", item->u1.str);
00398       print_pval_list(fin,item->u2.statements,depth+1);
00399       for (i=0; i<depth; i++) {
00400          fprintf(fin,"\t"); /* depth == indentation */
00401       }
00402       fprintf(fin,"}\n");
00403       break;
00404          
00405    case PV_EXTENSION:
00406       if ( item->u4.regexten )
00407          fprintf(fin, "regexten ");
00408       if ( item->u3.hints )
00409          fprintf(fin,"hints(%s) ", item->u3.hints);
00410       
00411       fprintf(fin,"%s => \n", item->u1.str);
00412       print_pval_list(fin,item->u2.statements,depth+1);
00413       break;
00414          
00415    case PV_IGNOREPAT:
00416       fprintf(fin,"ignorepat => %s\n", item->u1.str);
00417       break;
00418          
00419    case PV_GLOBALS:
00420       fprintf(fin,"globals {\n");
00421       print_pval_list(fin,item->u1.statements,depth+1);
00422       for (i=0; i<depth; i++) {
00423          fprintf(fin,"\t"); /* depth == indentation */
00424       }
00425       fprintf(fin,"}\n");
00426       break;
00427    }
00428 }
00429 
00430 static void print_pval_list(FILE *fin, pval *item, int depth)
00431 {
00432    pval *i;
00433    
00434    for (i=item; i; i=i->next) {
00435       print_pval(fin, i, depth);
00436    }
00437 }
00438 
00439 #if 0
00440 static void ael2_print(char *fname, pval *tree)
00441 {
00442    FILE *fin = fopen(fname,"w");
00443    if ( !fin ) {
00444       ast_log(LOG_ERROR, "Couldn't open %s for writing.\n", fname);
00445       return;
00446    }
00447    print_pval_list(fin, tree, 0);
00448    fclose(fin);
00449 }
00450 #endif
00451 
00452 
00453 /* EMPTY TEMPLATE FUNCS FOR AEL TRAVERSAL:  ============================================================================= */
00454 
00455 void traverse_pval_template(pval *item, int depth);
00456 void traverse_pval_item_template(pval *item, int depth);
00457 
00458 
00459 void traverse_pval_item_template(pval *item, int depth)/* depth comes in handy for a pretty print (indentation),
00460                                             but you may not need it */
00461 {
00462    pval *lp;
00463    
00464    switch ( item->type ) {
00465    case PV_WORD:
00466       /* fields: item->u1.str == string associated with this (word). */
00467       break;
00468       
00469    case PV_MACRO:
00470       /* fields: item->u1.str     == name of macro
00471                  item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
00472                item->u2.arglist->u1.str  == argument
00473                item->u2.arglist->next   == next arg
00474 
00475                item->u3.macro_statements == pval list of statements in macro body.
00476       */
00477       for (lp=item->u2.arglist; lp; lp=lp->next) {
00478       
00479       }
00480       traverse_pval_item_template(item->u3.macro_statements,depth+1);
00481       break;
00482          
00483    case PV_CONTEXT:
00484       /* fields: item->u1.str     == name of context
00485                  item->u2.statements == pval list of statements in context body
00486                item->u3.abstract == int 1 if an abstract keyword were present
00487       */
00488       traverse_pval_item_template(item->u2.statements,depth+1);
00489       break;
00490          
00491    case PV_MACRO_CALL:
00492       /* fields: item->u1.str     == name of macro to call
00493                  item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
00494                item->u2.arglist->u1.str  == argument
00495                item->u2.arglist->next   == next arg
00496       */
00497       for (lp=item->u2.arglist; lp; lp=lp->next) {
00498       }
00499       break;
00500          
00501    case PV_APPLICATION_CALL:
00502       /* fields: item->u1.str     == name of application to call
00503                  item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
00504                item->u2.arglist->u1.str  == argument
00505                item->u2.arglist->next   == next arg
00506       */
00507       for (lp=item->u2.arglist; lp; lp=lp->next) {
00508       }
00509       break;
00510          
00511    case PV_CASE:
00512       /* fields: item->u1.str     == value of case
00513                  item->u2.statements == pval list of statements under the case
00514       */
00515       traverse_pval_item_template(item->u2.statements,depth+1);
00516       break;
00517          
00518    case PV_PATTERN:
00519       /* fields: item->u1.str     == value of case
00520                  item->u2.statements == pval list of statements under the case
00521       */
00522       traverse_pval_item_template(item->u2.statements,depth+1);
00523       break;
00524          
00525    case PV_DEFAULT:
00526       /* fields: 
00527                  item->u2.statements == pval list of statements under the case
00528       */
00529       traverse_pval_item_template(item->u2.statements,depth+1);
00530       break;
00531          
00532    case PV_CATCH:
00533       /* fields: item->u1.str     == name of extension to catch
00534                  item->u2.statements == pval list of statements in context body
00535       */
00536       traverse_pval_item_template(item->u2.statements,depth+1);
00537       break;
00538          
00539    case PV_SWITCHES:
00540       /* fields: item->u1.list     == pval list of PV_WORD elements, one per entry in the list
00541       */
00542       traverse_pval_item_template(item->u1.list,depth+1);
00543       break;
00544          
00545    case PV_ESWITCHES:
00546       /* fields: item->u1.list     == pval list of PV_WORD elements, one per entry in the list
00547       */
00548       traverse_pval_item_template(item->u1.list,depth+1);
00549       break;
00550          
00551    case PV_INCLUDES:
00552       /* fields: item->u1.list     == pval list of PV_WORD elements, one per entry in the list
00553                  item->u2.arglist  == pval list of 4 PV_WORD elements for time values
00554       */
00555       traverse_pval_item_template(item->u1.list,depth+1);
00556       traverse_pval_item_template(item->u2.arglist,depth+1);
00557       break;
00558          
00559    case PV_STATEMENTBLOCK:
00560       /* fields: item->u1.list     == pval list of statements in block, one per entry in the list
00561       */
00562       traverse_pval_item_template(item->u1.list,depth+1);
00563       break;
00564          
00565    case PV_VARDEC:
00566       /* fields: item->u1.str     == variable name
00567                  item->u2.val     == variable value to assign
00568       */
00569       break;
00570          
00571    case PV_GOTO:
00572       /* fields: item->u1.list     == pval list of PV_WORD target names, up to 3, in order as given by user.
00573                  item->u1.list->u1.str  == where the data on a PV_WORD will always be.
00574       */
00575       
00576       if ( item->u1.list->next )
00577          ;
00578       if ( item->u1.list->next && item->u1.list->next->next )
00579          ;
00580       
00581       break;
00582          
00583    case PV_LABEL:
00584       /* fields: item->u1.str     == label name
00585       */
00586       break;
00587          
00588    case PV_FOR:
00589       /* fields: item->u1.for_init     == a string containing the initalizer
00590                  item->u2.for_test     == a string containing the loop test
00591                  item->u3.for_inc      == a string containing the loop increment
00592 
00593                item->u4.for_statements == a pval list of statements in the for ()
00594       */
00595       traverse_pval_item_template(item->u4.for_statements,depth+1);
00596       break;
00597          
00598    case PV_WHILE:
00599       /* fields: item->u1.str        == the while conditional, as supplied by user
00600 
00601                item->u2.statements == a pval list of statements in the while ()
00602       */
00603       traverse_pval_item_template(item->u2.statements,depth+1);
00604       break;
00605          
00606    case PV_BREAK:
00607       /* fields: none
00608       */
00609       break;
00610          
00611    case PV_RETURN:
00612       /* fields: none
00613       */
00614       break;
00615          
00616    case PV_CONTINUE:
00617       /* fields: none
00618       */
00619       break;
00620          
00621    case PV_IFTIME:
00622       /* fields: item->u1.list        == there are 4 linked PV_WORDs here.
00623 
00624                item->u2.statements == a pval list of statements in the if ()
00625                item->u3.else_statements == a pval list of statements in the else
00626                                     (could be zero)
00627       */
00628       traverse_pval_item_template(item->u2.statements,depth+1);
00629       if ( item->u3.else_statements ) {
00630          traverse_pval_item_template(item->u3.else_statements,depth+1);
00631       }
00632       break;
00633          
00634    case PV_RANDOM:
00635       /* fields: item->u1.str        == the random number expression, as supplied by user
00636 
00637                item->u2.statements == a pval list of statements in the if ()
00638                item->u3.else_statements == a pval list of statements in the else
00639                                     (could be zero)
00640       */
00641       traverse_pval_item_template(item->u2.statements,depth+1);
00642       if ( item->u3.else_statements ) {
00643          traverse_pval_item_template(item->u3.else_statements,depth+1);
00644       }
00645       break;
00646          
00647    case PV_IF:
00648       /* fields: item->u1.str        == the if conditional, as supplied by user
00649 
00650                item->u2.statements == a pval list of statements in the if ()
00651                item->u3.else_statements == a pval list of statements in the else
00652                                     (could be zero)
00653       */
00654       traverse_pval_item_template(item->u2.statements,depth+1);
00655       if ( item->u3.else_statements ) {
00656          traverse_pval_item_template(item->u3.else_statements,depth+1);
00657       }
00658       break;
00659          
00660    case PV_SWITCH:
00661       /* fields: item->u1.str        == the switch expression
00662 
00663                item->u2.statements == a pval list of statements in the switch, 
00664                                     (will be case statements, most likely!)
00665       */
00666       traverse_pval_item_template(item->u2.statements,depth+1);
00667       break;
00668          
00669    case PV_EXTENSION:
00670       /* fields: item->u1.str        == the extension name, label, whatever it's called
00671 
00672                item->u2.statements == a pval list of statements in the extension
00673                item->u3.hints      == a char * hint argument
00674                item->u4.regexten   == an int boolean. non-zero says that regexten was specified
00675       */
00676       traverse_pval_item_template(item->u2.statements,depth+1);
00677       break;
00678          
00679    case PV_IGNOREPAT:
00680       /* fields: item->u1.str        == the ignorepat data
00681       */
00682       break;
00683          
00684    case PV_GLOBALS:
00685       /* fields: item->u1.statements     == pval list of statements, usually vardecs
00686       */
00687       traverse_pval_item_template(item->u1.statements,depth+1);
00688       break;
00689    }
00690 }
00691 
00692 void traverse_pval_template(pval *item, int depth) /* depth comes in handy for a pretty print (indentation),
00693                                          but you may not need it */
00694 {
00695    pval *i;
00696    
00697    for (i=item; i; i=i->next) {
00698       traverse_pval_item_template(i, depth);
00699    }
00700 }
00701 
00702 
00703 /* SEMANTIC CHECKING FOR AEL:  ============================================================================= */
00704 
00705 /*   (not all that is syntactically legal is good! */
00706 
00707 
00708 
00709 static int extension_matches(pval *here, const char *exten, const char *pattern)
00710 {
00711    int err1;
00712    regex_t preg;
00713    
00714    /* simple case, they match exactly, the pattern and exten name */
00715    if( !strcmp(pattern,exten) == 0 )
00716       return 1;
00717    
00718    if ( pattern[0] == '_' ) {
00719       char reg1[2000];
00720       const char *p;
00721       char *r = reg1;
00722       
00723       if ( strlen(pattern)*5 >= 2000 ) /* safety valve */ {
00724          ast_log(LOG_ERROR,"Error: The pattern %s is way too big. Pattern matching cancelled.\n",
00725                pattern);
00726          return 0;
00727       }
00728       /* form a regular expression from the pattern, and then match it against exten */
00729       *r++ = '^'; /* what if the extension is a pattern ?? */
00730       *r++ = '_'; /* what if the extension is a pattern ?? */
00731       *r++ = '?';
00732       for (p=pattern+1; *p; p++) {
00733          switch ( *p ) {
00734          case 'X':
00735             *r++ = '[';
00736             *r++ = '0';
00737             *r++ = '-';
00738             *r++ = '9';
00739             *r++ = 'X';
00740             *r++ = ']';
00741             break;
00742             
00743          case 'Z':
00744             *r++ = '[';
00745             *r++ = '1';
00746             *r++ = '-';
00747             *r++ = '9';
00748             *r++ = 'Z';
00749             *r++ = ']';
00750             break;
00751             
00752          case 'N':
00753             *r++ = '[';
00754             *r++ = '2';
00755             *r++ = '-';
00756             *r++ = '9';
00757             *r++ = 'N';
00758             *r++ = ']';
00759             break;
00760             
00761          case '[':
00762             while ( *p && *p != ']' ) {
00763                *r++ = *p++;
00764             }
00765             if ( *p != ']') {
00766                ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The extension pattern '%s' is missing a closing bracket \n",
00767                      here->filename, here->startline, here->endline, pattern);
00768             }
00769             break;
00770             
00771          case '.':
00772          case '!':
00773             *r++ = '.';
00774             *r++ = '*';
00775             break;
00776          case '*':
00777             *r++ = '\\';
00778             *r++ = '*';
00779             break;
00780          default:
00781             *r++ = *p;
00782             break;
00783             
00784          }
00785       }
00786       *r++ = '$'; /* what if the extension is a pattern ?? */
00787       *r++ = *p++; /* put in the closing null */
00788       err1 = regcomp(&preg, reg1, REG_NOSUB|REG_EXTENDED);
00789       if ( err1 ) {
00790          char errmess[500];
00791          regerror(err1,&preg,errmess,sizeof(errmess));
00792          regfree(&preg);
00793          ast_log(LOG_WARNING, "Regcomp of %s failed, error code %d\n",
00794                reg1, err1);
00795          return 0;
00796       }
00797       err1 = regexec(&preg, exten, 0, 0, 0);
00798       regfree(&preg);
00799       
00800       if ( err1 ) {
00801          /* ast_log(LOG_NOTICE,"*****************************[%d]Extension %s did not match %s(%s)\n",
00802             err1,exten, pattern, reg1); */
00803          return 0; /* no match */
00804       } else {
00805          /* ast_log(LOG_NOTICE,"*****************************Extension %s matched %s\n",
00806             exten, pattern); */
00807          return 1;
00808       }
00809       
00810       
00811    } else {
00812       if ( strcmp(exten,pattern) == 0 ) {
00813          return 1;
00814       } else
00815          return 0;
00816    }
00817 }
00818 
00819 
00820 static void check_expr2_input(pval *expr, char *str)
00821 {
00822    int spaces = strspn(str,"\t \n");
00823    if ( !strncmp(str+spaces,"$[",2) ) {
00824       ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The expression '%s' is redundantly wrapped in '$[ ]'. \n",
00825             expr->filename, expr->startline, expr->endline, str);
00826       warns++;
00827    }
00828 }
00829 
00830 static void check_includes(pval *includes)
00831 {
00832    struct pval *p4;
00833    for (p4=includes->u1.list; p4; p4=p4->next) {
00834       /* for each context pointed to, find it, then find a context/label that matches the
00835          target here! */
00836       char *incl_context = p4->u1.str;
00837       /* find a matching context name */
00838       struct pval *that_other_context = find_context(incl_context);
00839       if (!that_other_context && strcmp(incl_context, "parkedcalls") != 0) {
00840          ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The included context '%s' cannot be found.\n\
00841  (You may ignore this warning if '%s' exists in extensions.conf, or is created by another module. I cannot check for those.)\n",
00842                includes->filename, includes->startline, includes->endline, incl_context, incl_context);
00843          warns++;
00844       }
00845    }
00846 }
00847 
00848 
00849 static void check_timerange(pval *p)
00850 {
00851    char *times;
00852    char *e;
00853    int s1, s2;
00854    int e1, e2;
00855 
00856    times = ast_strdupa(p->u1.str);
00857 
00858    /* Star is all times */
00859    if (ast_strlen_zero(times) || !strcmp(times, "*")) {
00860       return;
00861    }
00862    /* Otherwise expect a range */
00863    e = strchr(times, '-');
00864    if (!e) {
00865       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",
00866             p->filename, p->startline, p->endline, times);
00867       warns++;
00868       return;
00869    }
00870    *e = '\0';
00871    e++;
00872    while (*e && !isdigit(*e)) 
00873       e++;
00874    if (!*e) {
00875       ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The time range format (%s) is missing the end time!\n",
00876             p->filename, p->startline, p->endline, p->u1.str);
00877       warns++;
00878    }
00879    if (sscanf(times, "%d:%d", &s1, &s2) != 2) {
00880       ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start time (%s) isn't quite right!\n",
00881             p->filename, p->startline, p->endline, times);
00882       warns++;
00883    }
00884    if (sscanf(e, "%d:%d", &e1, &e2) != 2) {
00885       ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end time (%s) isn't quite right!\n",
00886             p->filename, p->startline, p->endline, times);
00887       warns++;
00888    }
00889 
00890    s1 = s1 * 30 + s2/2;
00891    if ((s1 < 0) || (s1 >= 24*30)) {
00892       ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start time (%s) is out of range!\n",
00893             p->filename, p->startline, p->endline, times);
00894       warns++;
00895    }
00896    e1 = e1 * 30 + e2/2;
00897    if ((e1 < 0) || (e1 >= 24*30)) {
00898       ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end time (%s) is out of range!\n",
00899             p->filename, p->startline, p->endline, e);
00900       warns++;
00901    }
00902    return;
00903 }
00904 
00905 static char *days[] =
00906 {
00907    "sun",
00908    "mon",
00909    "tue",
00910    "wed",
00911    "thu",
00912    "fri",
00913    "sat",
00914 };
00915 
00916 /*! \brief  get_dow: Get day of week */
00917 static void check_dow(pval *DOW)
00918 {
00919    char *dow;
00920    char *c;
00921    /* The following line is coincidence, really! */
00922    int s, e;
00923    
00924    dow = ast_strdupa(DOW->u1.str);
00925 
00926    /* Check for all days */
00927    if (ast_strlen_zero(dow) || !strcmp(dow, "*"))
00928       return;
00929    /* Get start and ending days */
00930    c = strchr(dow, '-');
00931    if (c) {
00932       *c = '\0';
00933       c++;
00934    } else
00935       c = NULL;
00936    /* Find the start */
00937    s = 0;
00938    while ((s < 7) && strcasecmp(dow, days[s])) s++;
00939    if (s >= 7) {
00940       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",
00941             DOW->filename, DOW->startline, DOW->endline, dow);
00942       warns++;
00943    }
00944    if (c) {
00945       e = 0;
00946       while ((e < 7) && strcasecmp(c, days[e])) e++;
00947       if (e >= 7) {
00948          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",
00949                DOW->filename, DOW->startline, DOW->endline, c);
00950          warns++;
00951       }
00952    } else
00953       e = s;
00954 }
00955 
00956 static void check_day(pval *DAY)
00957 {
00958    char *day;
00959    char *c;
00960    /* The following line is coincidence, really! */
00961    int s, e;
00962 
00963    day = ast_strdupa(DAY->u1.str);
00964 
00965    /* Check for all days */
00966    if (ast_strlen_zero(day) || !strcmp(day, "*")) {
00967       return;
00968    }
00969    /* Get start and ending days */
00970    c = strchr(day, '-');
00971    if (c) {
00972       *c = '\0';
00973       c++;
00974    }
00975    /* Find the start */
00976    if (sscanf(day, "%d", &s) != 1) {
00977       ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start day of month (%s) must be a number!\n",
00978             DAY->filename, DAY->startline, DAY->endline, day);
00979       warns++;
00980    }
00981    else if ((s < 1) || (s > 31)) {
00982       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",
00983             DAY->filename, DAY->startline, DAY->endline, day);
00984       warns++;
00985    }
00986    s--;
00987    if (c) {
00988       if (sscanf(c, "%d", &e) != 1) {
00989          ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end day of month (%s) must be a number!\n",
00990                DAY->filename, DAY->startline, DAY->endline, c);
00991          warns++;
00992       }
00993       else if ((e < 1) || (e > 31)) {
00994          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",
00995                DAY->filename, DAY->startline, DAY->endline, day);
00996          warns++;
00997       }
00998       e--;
00999    } else
01000       e = s;
01001 }
01002 
01003 static char *months[] =
01004 {
01005    "jan",
01006    "feb",
01007    "mar",
01008    "apr",
01009    "may",
01010    "jun",
01011    "jul",
01012    "aug",
01013    "sep",
01014    "oct",
01015    "nov",
01016    "dec",
01017 };
01018 
01019 static void check_month(pval *MON)
01020 {
01021    char *mon;
01022    char *c;
01023    /* The following line is coincidence, really! */
01024    int s, e;
01025 
01026    mon = ast_strdupa(MON->u1.str);
01027 
01028    /* Check for all days */
01029    if (ast_strlen_zero(mon) || !strcmp(mon, "*")) 
01030       return ;
01031    /* Get star