![]() |
Home page |
Mailing list |
Docs
Asterisk developer's documentation :: Codename Pineapple
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 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 51436 $") 00028 00029 #include <sys/types.h> 00030 #include <stdlib.h> 00031 #include <unistd.h> 00032 #include <stdio.h> 00033 #include <string.h> 00034 #include <ctype.h> 00035 #include <errno.h> 00036 #include <regex.h> 00037 #include <sys/stat.h> 00038 00039 #include "asterisk/pbx.h" 00040 #include "asterisk/config.h" 00041 #include "asterisk/module.h" 00042 #include "asterisk/logger.h" 00043 #include "asterisk/cli.h" 00044 #include "asterisk/app.h" 00045 #include "asterisk/callerid.h" 00046 #include "asterisk/ael_structs.h" 00047 #ifdef AAL_ARGCHECK 00048 #include "asterisk/argdesc.h" 00049 #endif 00050 00051 static char expr_output[2096]; 00052 00053 /* these functions are in ../ast_expr2.fl */ 00054 00055 #define DEBUG_READ (1 << 0) 00056 #define DEBUG_TOKENS (1 << 1) 00057 #define DEBUG_MACROS (1 << 2) 00058 #define DEBUG_CONTEXTS (1 << 3) 00059 00060 static char *config = "extensions.ael"; 00061 static char *registrar = "pbx_ael"; 00062 static int pbx_load_module(void); 00063 00064 static int errs, warns; 00065 static int notes; 00066 00067 #ifndef AAL_ARGCHECK 00068 /* for the time being, short circuit all the AAL related structures 00069 without permanently removing the code; after/during the AAL 00070 development, this code can be properly re-instated 00071 */ 00072 00073 /* null definitions for structs passed down the infrastructure */ 00074 struct argapp 00075 { 00076 struct argapp *next; 00077 }; 00078 00079 #endif 00080 00081 #ifdef AAL_ARGCHECK 00082 int option_matches_j( struct argdesc *should, pval *is, struct argapp *app); 00083 int option_matches( struct argdesc *should, pval *is, struct argapp *app); 00084 int ael_is_funcname(char *name); 00085 #endif 00086 00087 int check_app_args(pval *appcall, pval *arglist, struct argapp *app); 00088 void check_pval(pval *item, struct argapp *apps, int in_globals); 00089 void check_pval_item(pval *item, struct argapp *apps, int in_globals); 00090 void check_switch_expr(pval *item, struct argapp *apps); 00091 void ast_expr_register_extra_error_info(char *errmsg); 00092 void ast_expr_clear_extra_error_info(void); 00093 int ast_expr(char *expr, char *buf, int length); 00094 struct pval *find_macro(char *name); 00095 struct pval *find_context(char *name); 00096 struct pval *find_context(char *name); 00097 struct pval *find_macro(char *name); 00098 struct ael_priority *new_prio(void); 00099 struct ael_extension *new_exten(void); 00100 void linkprio(struct ael_extension *exten, struct ael_priority *prio); 00101 void destroy_extensions(struct ael_extension *exten); 00102 static void linkexten(struct ael_extension *exten, struct ael_extension *add); 00103 static void gen_prios(struct ael_extension *exten, char *label, pval *statement, struct ael_extension *mother_exten, struct ast_context *context ); 00104 void set_priorities(struct ael_extension *exten); 00105 void add_extensions(struct ael_extension *exten); 00106 void ast_compile_ael2(struct ast_context **local_contexts, struct pval *root); 00107 void destroy_pval(pval *item); 00108 void destroy_pval_item(pval *item); 00109 int is_float(char *arg ); 00110 int is_int(char *arg ); 00111 int is_empty(char *arg); 00112 static pval *current_db; 00113 static pval *current_context; 00114 static pval *current_extension; 00115 00116 static const char *match_context; 00117 static const char *match_exten; 00118 static const char *match_label; 00119 static int in_abstract_context; 00120 static int count_labels; /* true, put matcher in label counting mode */ 00121 static int label_count; /* labels are only meant to be counted in a context or exten */ 00122 static int return_on_context_match; 00123 static pval *last_matched_label; 00124 struct pval *match_pval(pval *item); 00125 static void check_timerange(pval *p); 00126 static void check_dow(pval *DOW); 00127 static void check_day(pval *DAY); 00128 static void check_month(pval *MON); 00129 static void check_expr2_input(pval *expr, char *str); 00130 static int extension_matches(pval *here, const char *exten, const char *pattern); 00131 static void check_goto(pval *item); 00132 static void find_pval_goto_item(pval *item, int lev); 00133 static void find_pval_gotos(pval *item, int lev); 00134 static int check_break(pval *item); 00135 static int check_continue(pval *item); 00136 static void check_label(pval *item); 00137 static void check_macro_returns(pval *macro); 00138 00139 static struct pval *find_label_in_current_context(char *exten, char *label, pval *curr_cont); 00140 static struct pval *find_first_label_in_current_context(char *label, pval *curr_cont); 00141 static void print_pval_list(FILE *fin, pval *item, int depth); 00142 00143 static struct pval *find_label_in_current_extension(const char *label, pval *curr_ext); 00144 static struct pval *find_label_in_current_db(const char *context, const char *exten, const char *label); 00145 static pval *get_goto_target(pval *item); 00146 static int label_inside_case(pval *label); 00147 static void attach_exten(struct ael_extension **list, struct ael_extension *newmem); 00148 static void fix_gotos_in_extensions(struct ael_extension *exten); 00149 static pval *get_extension_or_contxt(pval *p); 00150 static pval *get_contxt(pval *p); 00151 static void remove_spaces_before_equals(char *str); 00152 static void substitute_commas(char *str); 00153 00154 /* I am adding this code to substitute commas with vertbars in the args to apps */ 00155 static void substitute_commas(char *str) 00156 { 00157 char *p = str; 00158 while (p && *p) 00159 { 00160 if (*p == ',' && ((p != str && *(p-1) != '\\') 00161 || p == str)) 00162 *p = '|'; 00163 p++; 00164 } 00165 } 00166 00167 00168 /* PRETTY PRINTER FOR AEL: ============================================================================= */ 00169 00170 static void print_pval(FILE *fin, pval *item, int depth) 00171 { 00172 int i; 00173 pval *lp; 00174 00175 for (i=0; i<depth; i++) { 00176 fprintf(fin, "\t"); /* depth == indentation */ 00177 } 00178 00179 switch ( item->type ) { 00180 case PV_WORD: 00181 fprintf(fin,"%s;\n", item->u1.str); /* usually, words are encapsulated in something else */ 00182 break; 00183 00184 case PV_MACRO: 00185 fprintf(fin,"macro %s(", item->u1.str); 00186 for (lp=item->u2.arglist; lp; lp=lp->next) { 00187 if (lp != item->u2.arglist ) 00188 fprintf(fin,", "); 00189 fprintf(fin,"%s", lp->u1.str); 00190 } 00191 fprintf(fin,") {\n"); 00192 print_pval_list(fin,item->u3.macro_statements,depth+1); 00193 for (i=0; i<depth; i++) { 00194 fprintf(fin,"\t"); /* depth == indentation */ 00195 } 00196 fprintf(fin,"};\n\n"); 00197 break; 00198 00199 case PV_CONTEXT: 00200 if ( item->u3.abstract ) 00201 fprintf(fin,"abstract context %s {\n", item->u1.str); 00202 else 00203 fprintf(fin,"context %s {\n", item->u1.str); 00204 print_pval_list(fin,item->u2.statements,depth+1); 00205 for (i=0; i<depth; i++) { 00206 fprintf(fin,"\t"); /* depth == indentation */ 00207 } 00208 fprintf(fin,"};\n\n"); 00209 break; 00210 00211 case PV_MACRO_CALL: 00212 fprintf(fin,"&%s(", item->u1.str); 00213 for (lp=item->u2.arglist; lp; lp=lp->next) { 00214 if ( lp != item->u2.arglist ) 00215 fprintf(fin,", "); 00216 fprintf(fin,"%s", lp->u1.str); 00217 } 00218 fprintf(fin,");\n"); 00219 break; 00220 00221 case PV_APPLICATION_CALL: 00222 fprintf(fin,"%s(", item->u1.str); 00223 for (lp=item->u2.arglist; lp; lp=lp->next) { 00224 if ( lp != item->u2.arglist ) 00225 fprintf(fin,","); 00226 fprintf(fin,"%s", lp->u1.str); 00227 } 00228 fprintf(fin,");\n"); 00229 break; 00230 00231 case PV_CASE: 00232 fprintf(fin,"case %s:\n", item->u1.str); 00233 print_pval_list(fin,item->u2.statements, depth+1); 00234 break; 00235 00236 case PV_PATTERN: 00237 fprintf(fin,"pattern %s:\n", item->u1.str); 00238 print_pval_list(fin,item->u2.statements, depth+1); 00239 break; 00240 00241 case PV_DEFAULT: 00242 fprintf(fin,"default:\n"); 00243 print_pval_list(fin,item->u2.statements, depth+1); 00244 break; 00245 00246 case PV_CATCH: 00247 fprintf(fin,"catch %s {\n", item->u1.str); 00248 print_pval_list(fin,item->u2.statements, depth+1); 00249 for (i=0; i<depth; i++) { 00250 fprintf(fin,"\t"); /* depth == indentation */ 00251 } 00252 fprintf(fin,"};\n"); 00253 break; 00254 00255 case PV_SWITCHES: 00256 fprintf(fin,"switches {\n"); 00257 print_pval_list(fin,item->u1.list,depth+1); 00258 for (i=0; i<depth; i++) { 00259 fprintf(fin,"\t"); /* depth == indentation */ 00260 } 00261 fprintf(fin,"};\n"); 00262 break; 00263 00264 case PV_ESWITCHES: 00265 fprintf(fin,"eswitches {\n"); 00266 print_pval_list(fin,item->u1.list,depth+1); 00267 for (i=0; i<depth; i++) { 00268 fprintf(fin,"\t"); /* depth == indentation */ 00269 } 00270 fprintf(fin,"};\n"); 00271 break; 00272 00273 case PV_INCLUDES: 00274 fprintf(fin,"includes {\n"); 00275 for (lp=item->u1.list; lp; lp=lp->next) { 00276 for (i=0; i<depth+1; i++) { 00277 fprintf(fin,"\t"); /* depth == indentation */ 00278 } 00279 fprintf(fin,"%s", lp->u1.str); /* usually, words are encapsulated in something else */ 00280 if ( lp->u2.arglist ) 00281 fprintf(fin,"|%s|%s|%s|%s", 00282 lp->u2.arglist->u1.str, 00283 lp->u2.arglist->next->u1.str, 00284 lp->u2.arglist->next->next->u1.str, 00285 lp->u2.arglist->next->next->next->u1.str 00286 ); 00287 fprintf(fin,";\n"); /* usually, words are encapsulated in something else */ 00288 } 00289 00290 print_pval_list(fin,item->u1.list,depth+1); 00291 for (i=0; i<depth; i++) { 00292 fprintf(fin,"\t"); /* depth == indentation */ 00293 } 00294 fprintf(fin,"};\n"); 00295 break; 00296 00297 case PV_STATEMENTBLOCK: 00298 fprintf(fin,"{\n"); 00299 print_pval_list(fin,item->u1.list, depth+1); 00300 for (i=0; i<depth; i++) { 00301 fprintf(fin,"\t"); /* depth == indentation */ 00302 } 00303 fprintf(fin,"};\n"); 00304 break; 00305 00306 case PV_VARDEC: 00307 fprintf(fin,"%s=%s;\n", item->u1.str, item->u2.val); 00308 break; 00309 00310 case PV_GOTO: 00311 fprintf(fin,"goto %s", item->u1.list->u1.str); 00312 if ( item->u1.list->next ) 00313 fprintf(fin,"|%s", item->u1.list->next->u1.str); 00314 if ( item->u1.list->next && item->u1.list->next->next ) 00315 fprintf(fin,"|%s", item->u1.list->next->next->u1.str); 00316 fprintf(fin,"\n"); 00317 break; 00318 00319 case PV_LABEL: 00320 fprintf(fin,"%s:\n", item->u1.str); 00321 break; 00322 00323 case PV_FOR: 00324 fprintf(fin,"for (%s; %s; %s)\n", item->u1.for_init, item->u2.for_test, item->u3.for_inc); 00325 print_pval_list(fin,item->u4.for_statements,depth+1); 00326 break; 00327 00328 case PV_WHILE: 00329 fprintf(fin,"while (%s)\n", item->u1.str); 00330 print_pval_list(fin,item->u2.statements,depth+1); 00331 break; 00332 00333 case PV_BREAK: 00334 fprintf(fin,"break;\n"); 00335 break; 00336 00337 case PV_RETURN: 00338 fprintf(fin,"return;\n"); 00339 break; 00340 00341 case PV_CONTINUE: 00342 fprintf(fin,"continue;\n"); 00343 break; 00344 00345 case PV_RANDOM: 00346 case PV_IFTIME: 00347 case PV_IF: 00348 if ( item->type == PV_IFTIME ) { 00349 00350 fprintf(fin,"ifTime ( %s|%s|%s|%s )\n", 00351 item->u1.list->u1.str, 00352 item->u1.list->next->u1.str, 00353 item->u1.list->next->next->u1.str, 00354 item->u1.list->next->next->next->u1.str 00355 ); 00356 } else if ( item->type == PV_RANDOM ) { 00357 fprintf(fin,"random ( %s )\n", item->u1.str ); 00358 } else 00359 fprintf(fin,"if ( %s )\n", item->u1.str); 00360 if ( item->u2.statements && item->u2.statements->next ) { 00361 for (i=0; i<depth; i++) { 00362 fprintf(fin,"\t"); /* depth == indentation */ 00363 } 00364 fprintf(fin,"{\n"); 00365 print_pval_list(fin,item->u2.statements,depth+1); 00366 for (i=0; i<depth; i++) { 00367 fprintf(fin,"\t"); /* depth == indentation */ 00368 } 00369 if ( item->u3.else_statements ) 00370 fprintf(fin,"}\n"); 00371 else 00372 fprintf(fin,"};\n"); 00373 } else if (item->u2.statements ) { 00374 print_pval_list(fin,item->u2.statements,depth+1); 00375 } else { 00376 if (item->u3.else_statements ) 00377 fprintf(fin, " {} "); 00378 else 00379 fprintf(fin, " {}; "); 00380 } 00381 if ( item->u3.else_statements ) { 00382 for (i=0; i<depth; i++) { 00383 fprintf(fin,"\t"); /* depth == indentation */ 00384 } 00385 fprintf(fin,"else\n"); 00386 print_pval_list(fin,item->u3.else_statements, depth); 00387 } 00388 break; 00389 00390 case PV_SWITCH: 00391 fprintf(fin,"switch( %s ) {\n", item->u1.str); 00392 print_pval_list(fin,item->u2.statements,depth+1); 00393 for (i=0; i<depth; i++) { 00394 fprintf(fin,"\t"); /* depth == indentation */ 00395 } 00396 fprintf(fin,"}\n"); 00397 break; 00398 00399 case PV_EXTENSION: 00400 if ( item->u4.regexten ) 00401 fprintf(fin, "regexten "); 00402 if ( item->u3.hints ) 00403 fprintf(fin,"hints(%s) ", item->u3.hints); 00404 00405 fprintf(fin,"%s => \n", item->u1.str); 00406 print_pval_list(fin,item->u2.statements,depth+1); 00407 break; 00408 00409 case PV_IGNOREPAT: 00410 fprintf(fin,"ignorepat => %s\n", item->u1.str); 00411 break; 00412 00413 case PV_GLOBALS: 00414 fprintf(fin,"globals {\n"); 00415 print_pval_list(fin,item->u1.statements,depth+1); 00416 for (i=0; i<depth; i++) { 00417 fprintf(fin,"\t"); /* depth == indentation */ 00418 } 00419 fprintf(fin,"}\n"); 00420 break; 00421 } 00422 } 00423 00424 static void print_pval_list(FILE *fin, pval *item, int depth) 00425 { 00426 pval *i; 00427 00428 for (i=item; i; i=i->next) { 00429 print_pval(fin, i, depth); 00430 } 00431 } 00432 00433 #if 0 00434 static void ael2_print(char *fname, pval *tree) 00435 { 00436 FILE *fin = fopen(fname,"w"); 00437 if ( !fin ) { 00438 ast_log(LOG_ERROR, "Couldn't open %s for writing.\n", fname); 00439 return; 00440 } 00441 print_pval_list(fin, tree, 0); 00442 fclose(fin); 00443 } 00444 #endif 00445 00446 00447 /* EMPTY TEMPLATE FUNCS FOR AEL TRAVERSAL: ============================================================================= */ 00448 00449 void traverse_pval_template(pval *item, int depth); 00450 void traverse_pval_item_template(pval *item, int depth); 00451 00452 00453 void traverse_pval_item_template(pval *item, int depth)/* depth comes in handy for a pretty print (indentation), 00454 but you may not need it */ 00455 { 00456 pval *lp; 00457 00458 switch ( item->type ) { 00459 case PV_WORD: 00460 /* fields: item->u1.str == string associated with this (word). */ 00461 break; 00462 00463 case PV_MACRO: 00464 /* fields: item->u1.str == name of macro 00465 item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user 00466 item->u2.arglist->u1.str == argument 00467 item->u2.arglist->next == next arg 00468 00469 item->u3.macro_statements == pval list of statements in macro body. 00470 */ 00471 for (lp=item->u2.arglist; lp; lp=lp->next) { 00472 00473 } 00474 traverse_pval_item_template(item->u3.macro_statements,depth+1); 00475 break; 00476 00477 case PV_CONTEXT: 00478 /* fields: item->u1.str == name of context 00479 item->u2.statements == pval list of statements in context body 00480 item->u3.abstract == int 1 if an abstract keyword were present 00481 */ 00482 traverse_pval_item_template(item->u2.statements,depth+1); 00483 break; 00484 00485 case PV_MACRO_CALL: 00486 /* fields: item->u1.str == name of macro to call 00487 item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user 00488 item->u2.arglist->u1.str == argument 00489 item->u2.arglist->next == next arg 00490 */ 00491 for (lp=item->u2.arglist; lp; lp=lp->next) { 00492 } 00493 break; 00494 00495 case PV_APPLICATION_CALL: 00496 /* fields: item->u1.str == name of application to call 00497 item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user 00498 item->u2.arglist->u1.str == argument 00499 item->u2.arglist->next == next arg 00500 */ 00501 for (lp=item->u2.arglist; lp; lp=lp->next) { 00502 } 00503 break; 00504 00505 case PV_CASE: 00506 /* fields: item->u1.str == value of case 00507 item->u2.statements == pval list of statements under the case 00508 */ 00509 traverse_pval_item_template(item->u2.statements,depth+1); 00510 break; 00511 00512 case PV_PATTERN: 00513 /* fields: item->u1.str == value of case 00514 item->u2.statements == pval list of statements under the case 00515 */ 00516 traverse_pval_item_template(item->u2.statements,depth+1); 00517 break; 00518 00519 case PV_DEFAULT: 00520 /* fields: 00521 item->u2.statements == pval list of statements under the case 00522 */ 00523 traverse_pval_item_template(item->u2.statements,depth+1); 00524 break; 00525 00526 case PV_CATCH: 00527 /* fields: item->u1.str == name of extension to catch 00528 item->u2.statements == pval list of statements in context body 00529 */ 00530 traverse_pval_item_template(item->u2.statements,depth+1); 00531 break; 00532 00533 case PV_SWITCHES: 00534 /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list 00535 */ 00536 traverse_pval_item_template(item->u1.list,depth+1); 00537 break; 00538 00539 case PV_ESWITCHES: 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_INCLUDES: 00546 /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list 00547 item->u2.arglist == pval list of 4 PV_WORD elements for time values 00548 */ 00549 traverse_pval_item_template(item->u1.list,depth+1); 00550 traverse_pval_item_template(item->u2.arglist,depth+1); 00551 break; 00552 00553 case PV_STATEMENTBLOCK: 00554 /* fields: item->u1.list == pval list of statements in block, one per entry in the list 00555 */ 00556 traverse_pval_item_template(item->u1.list,depth+1); 00557 break; 00558 00559 case PV_VARDEC: 00560 /* fields: item->u1.str == variable name 00561 item->u2.val == variable value to assign 00562 */ 00563 break; 00564 00565 case PV_GOTO: 00566 /* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user. 00567 item->u1.list->u1.str == where the data on a PV_WORD will always be. 00568 */ 00569 00570 if ( item->u1.list->next ) 00571 ; 00572 if ( item->u1.list->next && item->u1.list->next->next ) 00573 ; 00574 00575 break; 00576 00577 case PV_LABEL: 00578 /* fields: item->u1.str == label name 00579 */ 00580 break; 00581 00582 case PV_FOR: 00583 /* fields: item->u1.for_init == a string containing the initalizer 00584 item->u2.for_test == a string containing the loop test 00585 item->u3.for_inc == a string containing the loop increment 00586 00587 item->u4.for_statements == a pval list of statements in the for () 00588 */ 00589 traverse_pval_item_template(item->u4.for_statements,depth+1); 00590 break; 00591 00592 case PV_WHILE: 00593 /* fields: item->u1.str == the while conditional, as supplied by user 00594 00595 item->u2.statements == a pval list of statements in the while () 00596 */ 00597 traverse_pval_item_template(item->u2.statements,depth+1); 00598 break; 00599 00600 case PV_BREAK: 00601 /* fields: none 00602 */ 00603 break; 00604 00605 case PV_RETURN: 00606 /* fields: none 00607 */ 00608 break; 00609 00610 case PV_CONTINUE: 00611 /* fields: none 00612 */ 00613 break; 00614 00615 case PV_IFTIME: 00616 /* fields: item->u1.list == there are 4 linked PV_WORDs here. 00617 00618 item->u2.statements == a pval list of statements in the if () 00619 item->u3.else_statements == a pval list of statements in the else 00620 (could be zero) 00621 */ 00622 traverse_pval_item_template(item->u2.statements,depth+1); 00623 if ( item->u3.else_statements ) { 00624 traverse_pval_item_template(item->u3.else_statements,depth+1); 00625 } 00626 break; 00627 00628 case PV_RANDOM: 00629 /* fields: item->u1.str == the random number expression, as supplied by user 00630 00631 item->u2.statements == a pval list of statements in the if () 00632 item->u3.else_statements == a pval list of statements in the else 00633 (could be zero) 00634 */ 00635 traverse_pval_item_template(item->u2.statements,depth+1); 00636 if ( item->u3.else_statements ) { 00637 traverse_pval_item_template(item->u3.else_statements,depth+1); 00638 } 00639 break; 00640 00641 case PV_IF: 00642 /* fields: item->u1.str == the if conditional, as supplied by user 00643 00644 item->u2.statements == a pval list of statements in the if () 00645 item->u3.else_statements == a pval list of statements in the else 00646 (could be zero) 00647 */ 00648 traverse_pval_item_template(item->u2.statements,depth+1); 00649 if ( item->u3.else_statements ) { 00650 traverse_pval_item_template(item->u3.else_statements,depth+1); 00651 } 00652 break; 00653 00654 case PV_SWITCH: 00655 /* fields: item->u1.str == the switch expression 00656 00657 item->u2.statements == a pval list of statements in the switch, 00658 (will be case statements, most likely!) 00659 */ 00660 traverse_pval_item_template(item->u2.statements,depth+1); 00661 break; 00662 00663 case PV_EXTENSION: 00664 /* fields: item->u1.str == the extension name, label, whatever it's called 00665 00666 item->u2.statements == a pval list of statements in the extension 00667 item->u3.hints == a char * hint argument 00668 item->u4.regexten == an int boolean. non-zero says that regexten was specified 00669 */ 00670 traverse_pval_item_template(item->u2.statements,depth+1); 00671 break; 00672 00673 case PV_IGNOREPAT: 00674 /* fields: item->u1.str == the ignorepat data 00675 */ 00676 break; 00677 00678 case PV_GLOBALS: 00679 /* fields: item->u1.statements == pval list of statements, usually vardecs 00680 */ 00681 traverse_pval_item_template(item->u1.statements,depth+1); 00682 break; 00683 } 00684 } 00685 00686 void traverse_pval_template(pval *item, int depth) /* depth comes in handy for a pretty print (indentation), 00687 but you may not need it */ 00688 { 00689 pval *i; 00690 00691 for (i=item; i; i=i->next) { 00692 traverse_pval_item_template(i, depth); 00693 } 00694 } 00695 00696 00697 /* SEMANTIC CHECKING FOR AEL: ============================================================================= */ 00698 00699 /* (not all that is syntactically legal is good! */ 00700 00701 00702 static void check_macro_returns(pval *macro) 00703 { 00704 pval *i; 00705 if (!macro->u3.macro_statements) 00706 { 00707 pval *z = calloc(1, sizeof(struct pval)); 00708 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The macro %s is empty! I will insert a return.\n", 00709 macro->filename, macro->startline, macro->endline, macro->u1.str); 00710 00711 z->type = PV_RETURN; 00712 z->startline = macro->startline; 00713 z->endline = macro->endline; 00714 z->startcol = macro->startcol; 00715 z->endcol = macro->endcol; 00716 z->filename = strdup(macro->filename); 00717 00718 macro->u3.macro_statements = z; 00719 return; 00720 } 00721 for (i=macro->u3.macro_statements; i; i=i->next) { 00722 /* if the last statement in the list is not return, then insert a return there */ 00723 if (i->next == NULL) { 00724 if (i->type != PV_RETURN) { 00725 pval *z = calloc(1, sizeof(struct pval)); 00726 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The macro %s does not end with a return; I will insert one.\n", 00727 macro->filename, macro->startline, macro->endline, macro->u1.str); 00728 00729 z->type = PV_RETURN; 00730 z->startline = macro->startline; 00731 z->endline = macro->endline; 00732 z->startcol = macro->startcol; 00733 z->endcol = macro->endcol; 00734 z->filename = strdup(macro->filename); 00735 00736 i->next = z; 00737 return; 00738 } 00739 } 00740 } 00741 return; 00742 } 00743 00744 00745 00746 static int extension_matches(pval *here, const char *exten, const char *pattern) 00747 { 00748 int err1; 00749 regex_t preg; 00750 00751 /* simple case, they match exactly, the pattern and exten name */ 00752 if (!strcmp(pattern,exten) == 0) 00753 return 1; 00754 00755 if (pattern[0] == '_') { 00756 char reg1[2000]; 00757 const char *p; 00758 char *r = reg1; 00759 00760 if ( strlen(pattern)*5 >= 2000 ) /* safety valve */ { 00761 ast_log(LOG_ERROR,"Error: The pattern %s is way too big. Pattern matching cancelled.\n", 00762 pattern); 00763 return 0; 00764 } 00765 /* form a regular expression from the pattern, and then match it against exten */ 00766 *r++ = '^'; /* what if the extension is a pattern ?? */ 00767 *r++ = '_'; /* what if the extension is a pattern ?? */ 00768 *r++ = '?'; 00769 for (p=pattern+1; *p; p++) { 00770 switch ( *p ) { 00771 case 'X': 00772 *r++ = '['; 00773 *r++ = '0'; 00774 *r++ = '-'; 00775 *r++ = '9'; 00776 *r++ = 'X'; 00777 *r++ = ']'; 00778 break; 00779 00780 case 'Z': 00781 *r++ = '['; 00782 *r++ = '1'; 00783 *r++ = '-'; 00784 *r++ = '9'; 00785 *r++ = 'Z'; 00786 *r++ = ']'; 00787 break; 00788 00789 case 'N': 00790 *r++ = '['; 00791 *r++ = '2'; 00792 *r++ = '-'; 00793 *r++ = '9'; 00794 *r++ = 'N'; 00795 *r++ = ']'; 00796 break; 00797 00798 case '[': 00799 while ( *p && *p != ']' ) { 00800 *r++ = *p++; 00801 } 00802 if ( *p != ']') { 00803 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The extension pattern '%s' is missing a closing bracket \n", 00804 here->filename, here->startline, here->endline, pattern); 00805 } 00806 break; 00807 00808 case '.': 00809 case '!': 00810 *r++ = '.'; 00811 *r++ = '*'; 00812 break; 00813 case '*': 00814 *r++ = '\\'; 00815 *r++ = '*'; 00816 break; 00817 default: 00818 *r++ = *p; 00819 break; 00820 00821 } 00822 } 00823 *r++ = '$'; /* what if the extension is a pattern ?? */ 00824 *r++ = *p++; /* put in the closing null */ 00825 err1 = regcomp(&preg, reg1, REG_NOSUB|REG_EXTENDED); 00826 if ( err1 ) { 00827 char errmess[500]; 00828 regerror(err1,&preg,errmess,sizeof(errmess)); 00829 regfree(&preg); 00830 ast_log(LOG_WARNING, "Regcomp of %s failed, error code %d\n", 00831 reg1, err1); 00832 return 0; 00833 } 00834 err1 = regexec(&preg, exten, 0, 0, 0); 00835 regfree(&preg); 00836 00837 if ( err1 ) { 00838 /* ast_log(LOG_NOTICE,"*****************************[%d]Extension %s did not match %s(%s)\n", 00839 err1,exten, pattern, reg1); */ 00840 return 0; /* no match */ 00841 } else { 00842 /* ast_log(LOG_NOTICE,"*****************************Extension %s matched %s\n", 00843 exten, pattern); */ 00844 return 1; 00845 } 00846 00847 00848 } else { 00849 if ( strcmp(exten,pattern) == 0 ) { 00850 return 1; 00851 } else 00852 return 0; 00853 } 00854 } 00855 00856 00857 static void check_expr2_input(pval *expr, char *str) 00858 { 00859 int spaces = strspn(str,"\t \n"); 00860 if ( !strncmp(str+spaces,"$[",2) ) { 00861 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The expression '%s' is redundantly wrapped in '$[ ]'. \n", 00862 expr->filename, expr->startline, expr->endline, str); 00863 warns++; 00864 } 00865 } 00866 00867 static void check_includes(pval *includes) 00868 { 00869 struct pval *p4; 00870 for (p4=includes->u1.list; p4; p4=p4->next) { 00871 /* for each context pointed to, find it, then find a context/label that matches the 00872 target here! */ 00873 char *incl_context = p4->u1.str; 00874 /* find a matching context name */ 00875 struct pval *that_other_context = find_context(incl_context); 00876 if (!that_other_context && strcmp(incl_context, "parkedcalls") != 0) { 00877 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The included context '%s' cannot be found.\n", 00878 includes->filename, includes->startline, includes->endline, incl_context); 00879 warns++; 00880 } 00881 } 00882 } 00883 00884 00885 static void check_timerange(pval *p) 00886 { 00887 char *times; 00888 char *e; 00889 int s1, s2; 00890 int e1, e2; 00891 00892 times = ast_strdupa(p->u1.str); 00893 00894 /* Star is all times */ 00895 if (ast_strlen_zero(times) || !strcmp(times, "*")) { 00896 return; 00897 } 00898 /* Otherwise expect a range */ 00899 e = strchr(times, '-'); 00900 if (!e) { 00901 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", 00902 p->filename, p->startline, p->endline, times); 00903 warns++; 00904 return; 00905 } 00906 *e = '\0'; 00907 e++; 00908 while (*e && !isdigit(*e)) 00909 e++; 00910 if (!*e) { 00911 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The time range format (%s) is missing the end time!\n", 00912 p->filename, p->startline, p->endline, p->u1.str); 00913 warns++; 00914 } 00915 if (sscanf(times, "%d:%d", &s1, &s2) != 2) { 00916 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start time (%s) isn't quite right!\n", 00917 p->filename, p->startline, p->endline, times); 00918 warns++; 00919 } 00920 if (sscanf(e, "%d:%d", &e1, &e2) != 2) { 00921 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end time (%s) isn't quite right!\n", 00922 p->filename, p->startline, p->endline, times); 00923 warns++; 00924 } 00925 00926 s1 = s1 * 30 + s2/2; 00927 if ((s1 < 0) || (s1 >= 24*30)) { 00928 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start time (%s) is out of range!\n", 00929 p->filename, p->startline, p->endline, times); 00930 warns++; 00931 } 00932 e1 = e1 * 30 + e2/2; 00933 if ((e1 < 0) || (e1 >= 24*30)) { 00934 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end time (%s) is out of range!\n", 00935 p->filename, p->startline, p->endline, e); 00936 warns++; 00937 } 00938 return; 00939 } 00940 00941 static char *days[] = 00942 { 00943 "sun", 00944 "mon", 00945 "tue", 00946 "wed", 00947 "thu", 00948 "fri", 00949 "sat", 00950 }; 00951 00952 /*! \brief get_dow: Get day of week */ 00953 static void check_dow(pval *DOW) 00954 { 00955 char *dow; 00956 char *c; 00957 /* The following line is coincidence, really! */ 00958 int s, e; 00959 00960 dow = ast_strdupa(DOW->u1.str); 00961 00962 /* Check for all days */ 00963 if (ast_strlen_zero(dow) || !strcmp(dow, "*")) 00964 return; 00965 /* Get start and ending days */ 00966 c = strchr(dow, '-'); 00967 if (c) { 00968 *c = '\0'; 00969 c++; 00970 } else 00971 c = NULL; 00972 /* Find the start */ 00973 s = 0; 00974 while ((s < 7) && strcasecmp(dow, days[s])) s++; 00975 if (s >= 7) { 00976 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", 00977 DOW->filename, DOW->startline, DOW->endline, dow); 00978 warns++; 00979 } 00980 if (c) { 00981 e = 0; 00982 while ((e < 7) && strcasecmp(c, days[e])) e++; 00983 if (e >= 7) { 00984 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", 00985 DOW->filename, DOW->startline, DOW->endline, c); 00986 warns++; 00987 } 00988 } else 00989 e = s; 00990 } 00991 00992 static void check_day(pval *DAY) 00993 { 00994 char *day; 00995 char *c; 00996 /* The following line is coincidence, really! */ 00997 int s, e; 00998 00999 day = ast_strdupa(DAY->u1.str); 01000 01001 /* Check for all days */ 01002 if (ast_strlen_zero(day) || !strcmp(day, "*")) { 01003 return; 01004 } 01005 /* Get start and ending days */ 01006 c = strchr(day, '-'); 01007 if (c) { 01008 *c = '\0'; 01009 c++; 01010 } 01011 /* Find the start */ 01012 if (sscanf(day, "%d", &s) != 1) { 01013 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start day of month (%s) must be a number!\n", 01014 DAY->filename, DAY->startline, DAY->endline, day); 01015 warns++; 01016 } 01017 else if ((s < 1) || (s > 31)) { 01018 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", 01019 DAY->filename, DAY->startline, DAY->endline, day); 01020 warns++; 01021 } 01022 s--; 01023 if (c) { 01024 if (sscanf(c, "%d", &e) != 1) { 01025 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end day of month (%s) must be a number!\n", 01026 DAY->filename, DAY->startline, DAY->endline, c); 01027 warns++; 01028 } 01029 else if ((e < 1) || (e > 31)) { 01030 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", 01031 DAY->filename, DAY->startline, DAY->endline, day); 01032 warns++; 01033 } 01034 e--; 01035 } else 01036 e = s; 01037 } 01038 01039 static char *months[] = 01040 { 01041 "jan", 01042 "feb", 01043 "mar", 01044 "apr", 01045 "may", 01046 "jun", 01047 "jul", 01048 "aug", 01049 "sep", 01050 "oct", 01051 "nov", 01052 "dec", 01053 }; 01054 01055 static void check_month(pval *MON) 01056 { 01057 char *mon; 01058 char *c; 01059 /* The following line is coincidence, really! */ 01060 int s, e; 01061 01062 mon = ast_strdupa(MON->u1.str); 01063 01064 /* Check for all days */ 01065 if (ast_strlen_zero(mon) || !strcmp(mon, "*")) 01066 return ; 01067 /* Get start and ending days */ 01068 c = strchr(mon, '-'); 01069 if (c) { 01070 *c = '\0'; 01071 c++; 01072 } 01073 /* Find the start */ 01074 s = 0; 01075 while ((s < 12) && strcasecmp(mon, months[s])) s++; 01076 if (s >= 12) { 01077 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start month (%s) must be a one of: 'jan', 'feb', ..., 'dec'!\n", 01078 MON->filename, MON->startline, MON->endline, mon); 01079 warns++; 01080 } 01081 if (c) { 01082 e = 0; 01083 while ((e < 12) && strcasecmp(mon, months[e])) e++; 01084 if (e >= 12) { 01085 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end month (%s) must be a one of: 'jan', 'feb', ..., 'dec'!\n", 01086 MON->filename, MON->startline, MON->endline, c); 01087 warns++; 01088 } 01089 } else 01090 e = s; 01091 } 01092 01093 static int check_break(pval *item) 01094 { 01095 pval *p = item; 01096 01097 while( p && p->type != PV_MACRO && p->type != PV_CONTEXT ) /* early cutout, sort of */ { 01098 /* a break is allowed in WHILE, FOR, CASE, DEFAULT, PATTERN; otherwise, it don't make 01099 no sense */ 01100 if( p->type == PV_CASE || p->type == PV_DEFAULT || p->type == PV_PATTERN 01101 || p->type == PV_WHILE || p->type == PV_FOR ) { 01102 return 1; 01103 } 01104 p = p->dad; 01105 } 01106 ast_log(LOG_ERROR,"Error: file %s, line %d-%d: 'break' not in switch, for, or while statement!\n", 01107 item->filename, item->startline, item->endline); 01108 errs++; 01109 01110 return 0; 01111 } 01112 01113 static int check_continue(pval *item) 01114 { 01115 pval *p = item; 01116 01117 while( p && p->type != PV_MACRO && p->type != PV_CONTEXT ) /* early cutout, sort of */ { 01118 /* a break is allowed in WHILE, FOR, CASE, DEFAULT, PATTERN; otherwise, it don't make 01119 no sense */ 01120 if( p->type == PV_WHILE || p->type == PV_FOR ) { 01121 return 1; 01122 } 01123 p = p->dad; 01124 } 01125 ast_log(LOG_ERROR,"Error: file %s, line %d-%d: 'continue' not in 'for' or 'while' statement!\n", 01126 item->filename, item->startline, item->endline); 01127 errs++; 01128 01129 return 0; 01130 } 01131 01132 static struct pval *in_macro(pval *item) 01133 { 01134 struct pval *curr; 01135 curr = item; 01136 while( curr ) { 01137 if( curr->type == PV_MACRO ) { 01138 return curr; 01139 } 01140 curr = curr->dad; 01141 } 01142 return 0; 01143 } 01144 01145 static struct pval *in_context(pval *item) 01146 { 01147 struct pval *curr; 01148 curr = item; 01149 while( curr ) { 01150 if( curr->type == PV_MACRO || curr->type == PV_CONTEXT ) { 01151 return curr; 01152 } 01153 curr = curr->dad; 01154 } 01155 return 0; 01156 } 01157 01158 01159 /* general purpose goto finder */ 01160 01161 static void check_label(pval *item) 01162 { 01163 struct pval *curr; 01164 struct pval *x; 01165 int alright = 0; 01166 01167 /* A label outside an extension just plain does not make sense! */ 01168 01169 curr = item; 01170 01171 while( curr ) { 01172 if( curr->type == PV_MACRO || curr->type == PV_EXTENSION ) { 01173 alright = 1; 01174 break; 01175 } 01176 curr = curr->dad; 01177 } 01178 if( !alright ) 01179 { 01180 ast_log(LOG_ERROR,"Error: file %s, line %d-%d: Label %s is not within an extension or macro!\n", 01181 item->filename, item->startline, item->endline, item->u1.str); 01182 errs++; 01183 } 01184 01185 01186 /* basically, ensure that a label is not repeated in a context. Period. 01187 The method: well, for each label, find the first label in the context 01188 with the same name. If it's not the current label, then throw an error. */ 01189 01190 01191 /* printf("==== check_label: ====\n"); */ 01192 if( !current_extension ) 01193 curr = current_context; 01194 else 01195 curr = current_extension; 01196 01197 x = find_first_label_in_current_context((char *)item->u1.str, curr); 01198 /* 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); */ 01199 if( x && x != item ) 01200 { 01201 ast_log(LOG_ERROR,"Error: file %s, line %d-%d: Duplicate label %s! Previously defined at file %s, line %d.\n", 01202 item->filename, item->startline, item->endline, item->u1.str, x->filename, x->startline); 01203 errs++; 01204 } 01205 /* printf("<<<<< check_label: ====\n"); */ 01206 } 01207 01208 static pval *get_goto_target(pval *item) 01209 { 01210 /* just one item-- the label should be in the current extension */ 01211 pval *curr_ext = get_extension_or_contxt(item); /* containing exten, or macro */ 01212 pval *curr_cont; 01213 01214 if (item->u1.list && !item->u1.list->next && !strstr((item->u1.list)->u1.str,"${")) { 01215 struct pval *x = find_label_in_current_extension((char*)((item->u1.list)->u1.str), curr_ext); 01216 return x; 01217 } 01218 01219 curr_cont = get_contxt(item); 01220 01221 /* TWO items */ 01222 if (item->u1.list->next && !item->u1.list->next->next) { 01223 if (!strstr((item->u1.list)->u1.str,"${") 01224 && !strstr(item->u1.list->next->u1.str,"${") ) /* Don't try to match variables */ { 01225 struct pval *x = find_label_in_current_context((char *)item->u1.list->u1.str, (char *)item->u1.list->next->u1.str, curr_cont); 01226 return x; 01227 } 01228 } 01229 01230 /* All 3 items! */ 01231 if (item->u1.list->next && item->u1.list->next->next) { 01232 /* all three */ 01233 pval *first = item->u1.list; 01234 pval *second = item->u1.list->next; 01235 pval *third = item->u1.list->next->next; 01236 01237 if (!strstr((item->u1.list)->u1.str,"${") 01238 && !strstr(item->u1.list->next->u1.str,"${") 01239 && !strstr(item->u1.list->next->next->u1.str,"${")) /* Don't try to match variables */ { 01240 struct pval *x = find_label_in_current_db((char*)first->u1.str, (char*)second->u1.str, (char*)third->u1.str); 01241 if (!x) { 01242 01243 struct pval *p3; 01244 struct pval *that_context = find_context(item->u1.list->u1.str); 01245 01246 /* the target of the goto could be in an included context!! Fancy that!! */ 01247 /* look for includes in the current context */ 01248 if (that_context) { 01249 for (p3=that_context->u2.statements; p3; p3=p3->next) { 01250 if (p3->type == PV_INCLUDES) { 01251 struct pval *p4; 01252 for (p4=p3->u1.list; p4; p4=p4->next) { 01253 /* for each context pointed to, find it, then find a context/label that matches the 01254 target here! */ 01255 char *incl_context = p4->u1.str; 01256 /* find a matching context name */ 01257 struct pval *that_other_context = find_context(incl_context); 01258 if (that_other_context) { 01259 struct pval *x3; 01260 x3 = find_label_in_current_context((char *)item->u1.list->next->u1.str, (char *)item->u1.list->next->next->u1.str, that_other_context); 01261 if (x3) { 01262 return x3; 01263 } 01264 } 01265 } 01266 } 01267 } 01268 } 01269 } 01270 return x; 01271 } 01272 } 01273 return 0; 01274 } 01275 01276 static void check_goto(pval *item) 01277 { 01278 /* check for the target of the goto-- does it exist? */ 01279 if ( !(item->u1.list)->next && !(item->u1.list)->u1.str ) { 01280 ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto: empty label reference found!\n", 01281 item->filename, item->startline, item->endline); 01282 errs++; 01283 } 01284 01285 /* just one item-- the label should be in the current extension */ 01286 01287 if (item->u1.list && !item->u1.list->next && !strstr((item->u1.list)->u1.str,"${")) { 01288 struct pval *z = get_extension_or_contxt(item); 01289 struct pval *x = 0; 01290 if (z) 01291 x = find_label_in_current_extension((char*)((item->u1.list)->u1.str), z); /* if in macro, use current context instead */ 01292 /* printf("Called find_label_in_current_extension with arg %s; current_extension is %x: %d\n", 01293 (char*)((item->u1.list)->u1.str), current_extension?current_extension:current_context, current_extension?current_extension->type:current_context->type); */ 01294 if (!x) { 01295 ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto: no label %s exists in the current extension!\n", 01296 item->filename, item->startline, item->endline, item->u1.list->u1.str); 01297 errs++; 01298 } 01299 else 01300 return; 01301 } 01302 01303 /* TWO items */ 01304 if (item->u1.list->next && !item->u1.list->next->next) { 01305 /* two items */ 01306 /* printf("Calling find_label_in_current_context with args %s, %s\n", 01307 (char*)((item->u1.list)->u1.str), (char *)item->u1.list->next->u1.str); */ 01308 if (!strstr((item->u1.list)->u1.str,"${") 01309 && !strstr(item->u1.list->next->u1.str,"${") ) /* Don't try to match variables */ { 01310 struct pval *z = get_contxt(item); 01311 struct pval *x = 0; 01312 01313 if (z) 01314 x = find_label_in_current_context((char *)item->u1.list->u1.str, (char *)item->u1.list->next->u1.str, z); 01315 01316 if (!x) { 01317 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", 01318 item->filename, item->startline, item->endline, item->u1.list->u1.str, item->u1.list->next->u1.str ); 01319 errs++; 01320 } 01321 else 01322 return; 01323 } 01324 } 01325 01326 /* All 3 items! */ 01327 if (item->u1.list->next && item->u1.list->next->next) { 01328 /* all three */ 01329 pval *first = item->u1.list; 01330 pval *second = item->u1.list->next; 01331 pval *third = item->u1.list->next->next; 01332 01333 /* printf("Calling find_label_in_current_db with args %s, %s, %s\n", 01334 (char*)first->u1.str, (char*)second->u1.str, (char*)third->u1.str); */ 01335 if (!strstr((item->u1.list)->u1.str,"${") 01336 && !strstr(item->u1.list->next->u1.str,"${") 01337 && !strstr(item->u1.list->next->next->u1.str,"${")) /* Don't try to match variables */ { 01338 struct pval *x = find_label_in_current_db((char*)first->u1.str, (char*)second->u1.str, (char*)third->u1.str); 01339 if (!x) { 01340 struct pval *p3; 01341 struct pval *found = 0; 01342 struct pval *that_context = find_context(item->u1.list->u1.str); 01343 01344 /* the target of the goto could be in an included context!! Fancy that!! */ 01345 /* look for includes in the current context */ 01346 if (that_context) { 01347 for (p3=that_context->u2.statements; p3; p3=p3->next) { 01348 if (p3->type == PV_INCLUDES) { 01349 struct pval *p4; 01350 for (p4=p3->u1.list; p4; p4=p4->next) { 01351 /* for each context pointed to, find it, then find a context/label that matches the 01352 target here! */ 01353 char *incl_context = p4->u1.str; 01354 /* find a matching context name */ 01355 struct pval *that_other_context = find_context(incl_context); 01356 if (that_other_context) { 01357 struct pval *x3; 01358 x3 = find_label_in_current_context((char *)item->u1.list->next->u1.str, (char *)item->u1.list->next->next->u1.str, that_other_context); 01359 if (x3) { 01360 found = x3; 01361 break; 01362 } 01363 } 01364 } 01365 } 01366 } 01367 if (!found) { 01368 ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto: no label %s|%s exists in the context %s or its inclusions!\n", 01369 item->filename, item->startline, item->endline, item->u1.list->next->u1.str, item->u1.list->next->next->u1.str, item->u1.list->u1.str ); 01370 errs++; 01371 } else { 01372 struct pval *mac = in_macro(item); /* is this goto inside a macro? */ 01373 if( mac ) { /* yes! */ 01374 struct pval *targ = in_context(found); 01375 if( mac != targ ) 01376 { 01377 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", 01378 item->filename, item->startline, item->endline); 01379 warns++; 01380 } 01381 } 01382 } 01383 } else { 01384 ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto: no context %s could be found that matches the goto target!\n", 01385 item->filename, item->startline, item->endline, item->u1.list->u1.str); 01386 errs++; 01387 } 01388 } else { 01389 struct pval *mac = in_macro(item); /* is this goto inside a macro? */ 01390 if( mac ) { /* yes! */ 01391 struct pval *targ = in_context(x); 01392 if( mac != targ ) 01393 { 01394 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", 01395 item->filename, item->startline, item->endline); 01396 warns++; 01397 } 01398 } 01399 } 01400 } 01401 } 01402 } 01403 01404 01405 static void find_pval_goto_item(pval *item, int lev) 01406 { 01407 struct pval *p4; 01408 if (lev>100) { 01409 ast_log(LOG_ERROR,"find_pval_goto in infinite loop!\n\n"); 01410 return; 01411 } 01412 01413 switch ( item->type ) { 01414 case PV_MACRO: 01415 /* fields: item->u1.str == name of macro 01416 item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user 01417 item->u2.arglist->u1.str == argument 01418 item->u2.arglist->next == next arg 01419 01420 item->u3.macro_statements == pval list of statements in macro body. 01421 */ 01422 01423 /* printf("Descending into matching macro %s\n", match_context); */ 01424 find_pval_gotos(item->u3.macro_statements,lev+1); /* if we're just searching for a context, don't bother descending into them */ 01425 01426 break; 01427 01428 case PV_CONTEXT: 01429 /* fields: item->u1.str == name of context 01430 item->u2.statements == pval list of statements in context body 01431 item->u3.abstract == int 1 if an abstract keyword were present 01432 */ 01433 break; 01434 01435 case PV_CASE: 01436 /* fields: item->u1.str == value of case 01437 item->u2.statements == pval list of statements under the case 01438 */ 01439 find_pval_gotos(item->u2.statements,lev+1); 01440 break; 01441 01442 case PV_PATTERN: 01443 /* fields: item->u1.str == value of case 01444 item->u2.statements == pval list of statements under the case 01445 */ 01446 find_pval_gotos(item->u2.statements,lev+1); 01447 break; 01448 01449 case PV_DEFAULT: 01450 /* fields: 01451 item->u2.statements == pval list of statements under the case 01452 */ 01453 find_pval_gotos(item->u2.statements,lev+1); 01454 break; 01455 01456 case PV_CATCH: 01457 /* fields: item->u1.str == name of extension to catch 01458 item->u2.statements == pval list of statements in context body 01459 */ 01460 find_pval_gotos(item->u2.statements,lev+1); 01461 break; 01462 01463 case PV_STATEMENTBLOCK: 01464 /* fields: item->u1.list == pval list of statements in block, one per entry in the list 01465 */ 01466 find_pval_gotos(item->u1.list,lev+1); 01467 break; 01468 01469 case PV_GOTO: 01470 /* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user. 01471 item->u1.list->u1.str == where the data on a PV_WORD will always be. 01472 */ 01473 check_goto(item); /* THE WHOLE FUNCTION OF THIS ENTIRE ROUTINE!!!! */ 01474 break; 01475 01476 case PV_INCLUDES: 01477 /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list 01478 */ 01479 for (p4=item->u1.list; p4; p4=p4->next) { 01480 /* for each context pointed to, find it, then find a context/label that matches the 01481 target here! */ 01482 char *incl_context = p4->u1.str; 01483 /* find a matching context name */ 01484 struct pval *that_context = find_context(incl_context); 01485 if (that_context) { 01486 find_pval_gotos(that_context,lev+1); /* keep working up the includes */ 01487 } 01488 } 01489 break; 01490 01491 case PV_FOR: 01492 /* fields: item->u1.for_init == a string containing the initalizer 01493 item->u2.for_test == a string containing the loop test 01494 item->u3.for_inc == a string containing the loop increment 01495 01496 item->u4.for_statements == a pval list of statements in the for () 01497 */ 01498 find_pval_gotos(item->u4.for_statements,lev+1); 01499 break; 01500 01501 case PV_WHILE: 01502 /* fields: item->u1.str == the while conditional, as supplied by user 01503 01504 item->u2.statements == a pval list of statements in the while () 01505 */ 01506 find_pval_gotos(item->u2.statements,lev+1); 01507 break; 01508 01509 case PV_RANDOM: 01510 /* fields: item->u1.str == the random number expression, as supplied by user 01511 01512 item->u2.statements == a pval list of statements in the if () 01513 item->u3.else_statements == a pval list of statements in the else 01514 (could be zero) 01515 fall thru to PV_IF */ 01516 01517 case PV_IFTIME: 01518 /* fields: item->u1.list == the time values, 4 of them, as PV_WORD structs in a list 01519 01520 item->u2.statements == a pval list of statements in the if () 01521 item->u3.else_statements == a pval list of statements in the else 01522 (could be zero) 01523 fall thru to PV_IF*/ 01524 case PV_IF: 01525 /* fields: item->u1.str == the if conditional, as supplied by user 01526 01527 item->u2.statements == a pval list of statements in the if () 01528 item->u3.else_statements == a pval list of statements in the else 01529 (could be zero) 01530 */ 01531 find_pval_gotos(item->u2.statements,lev+1); 01532 01533 if (item->u3.else_statements) { 01534 find_pval_gotos(item->u3.else_statements,lev+1); 01535 } 01536 break; 01537 01538 case PV_SWITCH: 01539 /* fields: item->u1.str == the switch expression 01540 01541 item->u2.statements == a pval list of statements in the switch, 01542 (will be case statements, most likely!) 01543 */ 01544 find_pval_gotos(item->u3.else_statements,lev+1); 01545 break; 01546 01547 case PV_EXTENSION: 01548 /* fields: item->u1.str == the extension name, label, whatever it's called 01549 01550 item->u2.statements == a pval list of statements in the extension 01551 item->u3.hints == a char * hint argument 01552 item->u4.regexten == an int boolean. non-zero says that regexten was specified 01553 */ 01554 01555 find_pval_gotos(item->u2.statements,lev+1); 01556 break; 01557 01558 default: 01559 break; 01560 } 01561 } 01562 01563 static void find_pval_gotos(pval *item,int lev) 01564 { 01565 pval *i; 01566 01567 for (i=item; i; i=i->next) { 01568 01569 find_pval_goto_item(i, lev); 01570 } 01571 } 01572 01573 01574 01575 /* general purpose label finder */ 01576 static struct pval *match_pval_item(pval *item) 01577 { 01578 pval *x; 01579 01580 switch ( item->type ) { 01581 case PV_MACRO: 01582 /* fields: item->u1.str == name of macro 01583 item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user 01584 item->u2.arglist->u1.str == argument 01585 item->u2.arglist->next == next arg 01586 01587 item->u3.macro_statements == pval list of statements in macro body. 01588 */ 01589 /* printf(" matching in MACRO %s, match_context=%s; retoncontmtch=%d; \n", item->u1.str, match_context, return_on_context_match); */ 01590 if (!strcmp(match_context,"*") || !strcmp(item->u1.str, match_context)) { 01591 01592 /* printf("MACRO: match context is: %s\n", match_context); */ 01593 01594 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 */ { 01595 /* printf("Returning on matching macro %s\n", match_context); */ 01596 return item; 01597 } 01598 01599 01600 if (!return_on_context_match) { 01601 /* printf("Descending into matching macro %s/%s\n", match_context, item->u1.str); */ 01602 if ((x=match_pval(item->u3.macro_statements))) { 01603 /* printf("Responded with pval match %x\n", x); */ 01604 return x; 01605 } 01606 } 01607 } else { 01608 /* printf("Skipping context/macro %s\n", item->u1.str); */ 01609 } 01610 01611 break; 01612 01613 case PV_CONTEXT: 01614 /* fields: item->u1.str == name of context 01615 item->u2.statements == pval list of statements in context body 01616 item->u3.abstract == int 1 if an abstract keyword were present 01617 */ 01618 /* printf(" matching in CONTEXT\n"); */ 01619 if (!strcmp(match_context,"*") || !strcmp(item->u1.str, match_context)) { 01620 if (return_on_context_match && !strcmp(item->u1.str, match_context)) { 01621 /* printf("Returning on matching context %s\n", match_context); */ 01622 /* printf("non-CONTEXT: Responded with pval match %x\n", x); */ 01623 return item; 01624 } 01625 01626 if (!return_on_context_match ) { 01627 /* printf("Descending into matching context %s\n", match_context); */ 01628 if ((x=match_pval(item->u2.statements))) /* if we're just searching for a context, don't bother descending into them */ { 01629 /* printf("CONTEXT: Responded with pval match %x\n", x); */ 01630 return x; 01631 } 01632 } 01633 } else { 01634 /* printf("Skipping context/macro %s\n", item->u1.str); */ 01635 } 01636 break; 01637 01638 case PV_CASE: 01639 /* fields: item->u1.str == value of case 01640 item->u2.statements == pval list of statements under the case 01641 */ 01642 /* printf(" matching in CASE\n"); */ 01643 if ((x=match_pval(item->u2.statements))) { 01644 /* printf("CASE: Responded with pval match %x\n", x); */ 01645 return x; 01646 } 01647 break; 01648 01649 case PV_PATTERN: 01650 /* fields: item->u1.str == value of case 01651 item->u2.statements == pval list of statements under the case 01652 */ 01653 /* printf(" matching in PATTERN\n"); */ 01654 if ((x=match_pval(item->u2.statements))) { 01655 /* printf("PATTERN: Responded with pval match %x\n", x); */ 01656 return x; 01657 } 01658 break; 01659 01660 case PV_DEFAULT: 01661 /* fields: 01662 item->u2.statements == pval list of statements under the case 01663 */ 01664 /* printf(" matching in DEFAULT\n"); */ 01665 if ((x=match_pval(item->u2.statements))) { 01666 /* printf("DEFAULT: Responded with pval match %x\n", x); */ 01667 return x; 01668 } 01669 break; 01670 01671 case PV_CATCH: 01672 /* fields: item->u1.str == name of extension to catch 01673 item->u2.statements == pval list of statements in context body 01674 */ 01675 /* printf(" matching in CATCH\n"); */ 01676 if ((x=match_pval(item->u2.statements))) { 01677 /* printf("CATCH: Responded with pval match %x\n", x); */ 01678 return x; 01679 } 01680 break; 01681 01682 case PV_STATEMENTBLOCK: 01683 /* fields: item->u1.list == pval list of statements in block, one per entry in the list 01684 */ 01685 /* printf(" matching in STATEMENTBLOCK\n"); */ 01686 if ((x=match_pval(item->u1.list))) { 01687 /* printf("STATEMENTBLOCK: Responded with pval match %x\n", x); */ 01688 return x; 01689 } 01690 break; 01691 01692 case PV_LABEL: 01693 /* fields: item->u1.str == label name 01694 */ 01695 /* printf("PV_LABEL %s (cont=%s, exten=%s\n", 01696 item->u1.str, current_context->u1.str, (current_extension?current_extension->u1.str:"<macro>"));*/ 01697 01698 if (count_labels) { 01699 if (!strcmp(match_label, item->u1.str)) { 01700 label_count++; 01701 last_matched_label = item; 01702 } 01703 01704 } else { 01705 if (!strcmp(match_label, item->u1.str)) { 01706 /* printf("LABEL: Responded with pval match %x\n", x); */ 01707 return item; 01708 } 01709 } 01710 break; 01711 01712 case PV_FOR: 01713 /* fields: item->u1.for_init == a string containing the initalizer 01714 item->u2.for_test == a string containing the loop test 01715 item->u3.for_inc == a string containing the loop increment 01716 01717 item->u4.for_statements == a pval list of statements in the for () 01718 */ 01719 /* printf(" matching in FOR\n"); */ 01720 if ((x=match_pval(item->u4.for_statements))) { 01721 /* printf("FOR: Responded with pval match %x\n", x);*/ 01722 return x; 01723 } 01724 break; 01725 01726 case PV_WHILE: 01727 /* fields: item->u1.str == the while conditional, as supplied by user 01728 01729 item->u2.statements == a pval list of statements in the while () 01730 */ 01731 /* printf(" matching in WHILE\n"); */ 01732 if ((x=match_pval(item->u2.statements))) { 01733 /* printf("WHILE: Responded with pval match %x\n", x); */ 01734 return x; 01735 } 01736 break; 01737 01738 case PV_RANDOM: 01739 /* fields: item->u1.str == the random number expression, as supplied by user 01740 01741 item->u2.statements == a pval list of statements in the if () 01742 item->u3.else_statements == a pval list of statements in the else 01743 (could be zero) 01744 fall thru to PV_IF */ 01745 01746 case PV_IFTIME: 01747 /* fields: item->u1.list == the time values, 4 of them, as PV_WORD structs in a list 01748 01749 item->u2.statements == a pval list of statements in the if () 01750 item->u3.else_statements == a pval list of statements in the else 01751 (could be zero) 01752 fall thru to PV_IF*/ 01753 case PV_IF: 01754 /* fields: item->u1.str == the if conditional, as supplied by user 01755 01756 item->u2.statements == a pval list of statements in the if () 01757 item->u3.else_statements == a pval list of statements in the else 01758 (could be zero) 01759 */ 01760 /* printf(" matching in IF/IFTIME/RANDOM\n"); */ 01761 if ((x=match_pval(item->u2.statements))) { 01762 return x; 01763 } 01764 if (item->u3.else_statements) { 01765 if ((x=match_pval(item->u3.else_statements))) { 01766 /* printf("IF/IFTIME/RANDOM: Responded with pval match %x\n", x); */ 01767 return x; 01768 } 01769 } 01770 break; 01771 01772 case PV_SWITCH: 01773 /* fields: item->u1.str == the switch expression 01774 01775 item->u2.statements == a pval list of statements in the switch, 01776 (will be case statements, most likely!) 01777 */ 01778 /* printf(" matching in SWITCH\n"); */ 01779 if ((x=match_pval(item->u2.statements))) { 01780 /* printf("SWITCH: Responded with pval match %x\n", x); */ 01781 return x; 01782 } 01783 break; 01784 01785 case PV_EXTENSION: 01786 /* fields: item->u1.str == the extension name, label, whatever it's called 01787 01788 item->u2.statements == a pval list of statements in the extension 01789 item->u3.hints == a char * hint argument 01790 item->u4.regexten == an int boolean. non-zero says that regexten was specified 01791 */ 01792 /* printf(" matching in EXTENSION\n"); */ 01793 if (!strcmp(match_exten,"*") || extension_matches(item, match_exten, item->u1.str) ) { 01794 /* printf("Descending into matching exten %s => %s\n", match_exten, item->u1.str); */ 01795 if (strcmp(match_label,"1") == 0) { 01796 if (item->u2.statements) { 01797 struct pval *p5 = item->u2.statements; 01798 while (p5 && p5->type == PV_LABEL) /* find the first non-label statement in this context. If it exists, there's a "1" */ 01799 p5 = p5->next; 01800 if (p5) 01801 return p5; 01802 else 01803 return 0; 01804 } 01805 else 01806 return 0; 01807 } 01808 01809 if ((x=match_pval(item->u2.statements))) { 01810 /* printf("EXTENSION: Responded with pval match %x\n", x); */ 01811 return x; 01812 } 01813 } else { 01814 /* printf("Skipping exten %s\n", item->u1.str); */ 01815 } 01816 break; 01817 default: 01818 /* printf(" matching in default = %d\n", item->type); */ 01819 break; 01820 } 01821 return 0; 01822 } 01823 01824 struct pval *match_pval(pval *item) 01825 { 01826 pval *i; 01827 01828 for (i=item; i; i=i->next) { 01829 pval *x; 01830 /* printf(" -- match pval: item %d\n", i->type); */ 01831 01832 if ((x = match_pval_item(i))) { 01833 /* printf("match_pval: returning x=%x\n", (int)x); */ 01834 return x; /* cut the search short */ 01835 } 01836 } 01837 return 0; 01838 } 01839 01840 #if 0 01841 int count_labels_in_current_context(char *label) 01842 { 01843 label_count = 0; 01844 count_labels = 1; 01845 return_on_context_match = 0; 01846 match_pval(current_context->u2.statements); 01847 01848 return label_count; 01849 } 01850 #endif 01851 01852 struct pval *find_first_label_in_current_context(char *label, pval *curr_cont) 01853 { 01854 /* printf(" --- Got args %s, %s\n", exten, label); */ 01855 struct pval *ret; 01856 struct pval *p3; 01857 01858 count_labels = 0; 01859 return_on_context_match = 0; 01860 match_context = "*"; 01861 match_exten = "*"; 01862 match_label = label; 01863 01864 ret = match_pval(curr_cont); 01865 if (ret) 01866 return ret; 01867 01868 /* the target of the goto could be in an included context!! Fancy that!! */ 01869 /* look for includes in the current context */ 01870 for (p3=curr_cont->u2.statements; p3; p3=p3->next) { 01871 if (p3->type == PV_INCLUDES) { 01872 struct pval *p4; 01873 for (p4=p3->u1.list; p4; p4=p4->next) { 01874 /* for each context pointed to, find it, then find a context/label that matches the 01875 target here! */ 01876 char *incl_context = p4->u1.str; 01877 /* find a matching context name */ 01878 struct pval *that_context = find_context(incl_context); 01879 if (that_context) { 01880 struct pval *x3; 01881 x3 = find_first_label_in_current_context(label, that_context); 01882 if (x3) { 01883 return x3; 01884 } 01885 } 01886 } 01887 } 01888 } 01889 return 0; 01890 } 01891 01892 struct pval *find_label_in_current_context(char *exten, char *label, pval *curr_cont) 01893 { 01894 /* printf(" --- Got args %s, %s\n", exten, label); */ 01895 struct pval *ret; 01896 struct pval *p3; 01897 01898 count_labels = 0; 01899 return_on_context_match = 0; 01900 match_context = "*"; 01901 match_exten = exten; 01902 match_label = label; 01903 ret = match_pval(curr_cont->u2.statements); 01904 if (ret) 01905 return ret; 01906 01907 /* the target of the goto could be in an included context!! Fancy that!! */ 01908 /* look for includes in the current context */ 01909 for (p3=curr_cont->u2.statements; p3; p3=p3->next) { 01910 if (p3->type == PV_INCLUDES) { 01911 struct pval *p4; 01912 for (p4=p3->u1.list; p4; p4=p4->next) { 01913 /* for each context pointed to, find it, then find a context/label that matches the 01914 target here! */ 01915 char *incl_context = p4->u1.str; 01916 /* find a matching context name */ 01917 struct pval *that_context = find_context(incl_context); 01918 if (that_context) { 01919 struct pval *x3; 01920 x3 = find_label_in_current_context(exten, label, that_context); 01921 if (x3) { 01922 return x3; 01923 } 01924 } 01925 } 01926 } 01927 } 01928 return 0; 01929 } 01930 01931 static struct pval *find_label_in_current_extension(const char *label, pval *curr_ext) 01932 { 01933 /* printf(" --- Got args %s\n", label); */ 01934 count_labels = 0; 01935 return_on_context_match = 0; 01936 match_context = "*"; 01937 match_exten = "*"; 01938 match_label = label; 01939 return match_pval(curr_ext); 01940 } 01941 01942 static struct pval *find_label_in_current_db(const char *context, const char *exten, const char *label) 01943 { 01944 /* printf(" --- Got args %s, %s, %s\n", context, exten, label); */ 01945 count_labels = 0; 01946 return_on_context_match = 0; 01947 01948 match_context = context; 01949 match_exten = exten; 01950 match_label = label; 01951 01952 return match_pval(current_db); 01953 } 01954 01955 01956 struct pval *find_macro(char *name) 01957 { 01958 return_on_context_match = 1; 01959 count_labels = 0; 01960 match_context = name; 01961 match_exten = "*"; /* don't really need to set these, shouldn't be reached */ 01962 match_label = "*"; 01963 return match_pval(current_db); 01964 } 01965 01966 struct pval *find_context(char *name) 01967 { 01968 return_on_context_match = 1; 01969 count_labels = 0; 01970 match_context = name; 01971 match_exten = "*"; /* don't really need to set these, shouldn't be reached */ 01972 match_label = "*"; 01973 return match_pval(current_db); 01974 } 01975 01976 int is_float(char *arg ) 01977 { 01978 char *s; 01979 for (s=arg; *s; s++) { 01980 if (*s != '.' && (*s < '0' || *s > '9')) 01981 return 0; 01982 } 01983 return 1; 01984 } 01985 int is_int(char *arg ) 01986 { 01987 char *s; 01988 for (s=arg; *s; s++) { 01989 if (*s < '0' || *s > '9') 01990 return 0; 01991 } 01992 return 1; 01993 } 01994 int is_empty(char *arg) 01995 { 01996 if (!arg) 01997 return 1; 01998 if (*arg == 0) 01999 return 1; 02000 while (*arg) { 02001 if (*arg != ' ' && *arg != '\t') 02002 return 0; 02003 arg++; 02004 } 02005 return 1; 02006 } 02007 02008 #ifdef AAL_ARGCHECK 02009 int option_matches_j( struct argdesc *should, pval *is, struct argapp *app) 02010 { 02011 struct argchoice *ac; 02012 char *opcop,*q,*p; 02013 02014 switch (should->dtype) { 02015 case ARGD_OPTIONSET: 02016 if ( strstr(is->u1.str,"${") ) 02017 return 0; /* no checking anything if there's a var reference in there! */ 02018 02019 opcop = ast_strdupa(is->u1.str); 02020 02021 for (q=opcop;*q;q++) { /* erase the innards of X(innard) type arguments, so we don't get confused later */ 02022 if ( *q == '(' ) { 02023 p = q+1; 02024 while (*p && *p != ')' ) 02025 *p++ = '+'; 02026 q = p+1; 02027 } 02028 } 02029 02030 for (ac=app->opts; ac; ac=ac->next) { 02031 if (strlen(ac->name)>1 && strchr(ac->name,'(') == 0 && strcmp(ac->name,is->u1.str) == 0) /* multichar option, no parens, and a match? */ 02032 return 0; 02033 } 02034 for (ac=app->opts; ac; ac=ac->next) { 02035 if (strlen(ac->name)==1 || strchr(ac->name,'(')) { 02036 char *p = strchr(opcop,ac->name[0]); /* wipe out all matched options in the user-supplied string */ 02037 02038 if (p && *p == 'j') { 02039 ast_log(LOG_ERROR, "Error: file %s, line %d-%d: The j option in the %s application call is not appropriate for AEL!\n", 02040 is->filename, is->startline, is->endline, app->name); 02041 errs++; 02042 } 02043 02044 if (p) { 02045 *p = '+'; 02046 if (ac->name[1] == '(') { 02047 if (*(p+1) != '(') { 02048 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", 02049 is->filename, is->startline, is->endline, ac->name[0], app->name); 02050 warns++; 02051 } 02052 } 02053 } 02054 } 02055 } 02056 for (q=opcop; *q; q++) { 02057 if ( *q != '+' && *q != '(' && *q != ')') { 02058 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", 02059 is->filename, is->startline, is->endline, *q, app->name); 02060 warns++; 02061 } 02062 } 02063 return 1; 02064 break; 02065 default: 02066 return 0; 02067 } 02068 02069 } 02070 02071 int option_matches( struct argdesc *should, pval *is, struct argapp *app) 02072 { 02073 struct argchoice *ac; 02074 char *opcop; 02075 02076 switch (should->dtype) { 02077 case ARGD_STRING: 02078 if (is_empty(is->u1.str) && should->type == ARGD_REQUIRED) 02079 return 0; 02080 if (is->u1.str && strlen(is->u1.str) > 0) /* most will match */ 02081 return 1; 02082 break; 02083 02084 case ARGD_INT: 02085 if (is_int(is->u1.str)) 02086 return 1; 02087 else 02088 return 0; 02089 break; 02090 02091 case ARGD_FLOAT: 02092 if (is_float(is->u1.str)) 02093 return 1; 02094 else 02095 return 0; 02096 break; 02097 02098 case ARGD_ENUM: 02099 if( !is->u1.str || strlen(is->u1.str) == 0 ) 02100 return 1; /* a null arg in the call will match an enum, I guess! */ 02101 for (ac=should->choices; ac; ac=ac->next) { 02102 if (strcmp(ac->name,is->u1.str) == 0) 02103 return 1; 02104 } 02105 return 0; 02106 break; 02107 02108 case ARGD_OPTIONSET: 02109 opcop = ast_strdupa(is->u1.str); 02110 02111 for (ac=app->opts; ac; ac=ac->next) { 02112 if (strlen(ac->name)>1 && strchr(ac->name,'(') == 0 && strcmp(ac->name,is->u1.str) == 0) /* multichar option, no parens, and a match? */ 02113 return 1; 02114 } 02115 for (ac=app->opts; ac; ac=ac->next) { 02116 if (strlen(ac->name)==1 || strchr(ac->name,'(')) { 02117 char *p = strchr(opcop,ac->name[0]); /* wipe out all matched options in the user-supplied string */ 02118 02119 if (p) { 02120 *p = '+'; 02121 if (ac->name[1] == '(') { 02122 if (*(p+1) == '(') { 02123 char *q = p+1; 02124 while (*q && *q != ')') { 02125 *q++ = '+'; 02126 } 02127 *q = '+'; 02128 } 02129 } 02130 } 02131 } 02132 } 02133 return 1; 02134 break; 02135 case ARGD_VARARG: 02136 return 1; /* matches anything */ 02137 break; 02138 } 02139 return 1; /* unless some for-sure match or non-match returns, then it must be close enough ... */ 02140 } 02141 #endif 02142 02143 int check_app_args(pval* appcall, pval *arglist, struct argapp *app) 02144 { 02145 #ifdef AAL_ARGCHECK 02146 struct argdesc *ad = app->args; 02147 pval *pa; 02148 int z; 02149 02150 for (pa = arglist; pa; pa=pa->next) { 02151 if (!ad) { 02152 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: Extra argument %s not in application call to %s !\n", 02153 arglist->filename, arglist->startline, arglist->endline, pa->u1.str, app->name); 02154 warns++; 02155 return 1; 02156 } else { 02157 /* find the first entry in the ad list that will match */ 02158 do { 02159 if ( ad->dtype == ARGD_VARARG ) /* once we hit the VARARG, all bets are off. Discontinue the comparisons */ 02160 break; 02161 02162 z= option_matches( ad, pa, app); 02163 if (!z) { 02164 if ( !arglist ) 02165 arglist=appcall; 02166 02167 if (ad->type == ARGD_REQUIRED) { 02168 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: Required argument %s not in application call to %s !\n", 02169 arglist->filename, arglist->startline, arglist->endline, ad->dtype==ARGD_OPTIONSET?"options":ad->name, app->name); 02170 warns++; 02171 return 1; 02172 } 02173 } else if (z && ad->dtype == ARGD_OPTIONSET) { 02174 option_matches_j( ad, pa, app); 02175 } 02176 ad = ad->next; 02177 } while (ad && !z); 02178 } 02179 } 02180 /* any app nodes left, that are not optional? */ 02181 for ( ; ad; ad=ad->next) { 02182 if (ad->type == ARGD_REQUIRED && ad->dtype != ARGD_VARARG) { 02183 if ( !arglist ) 02184 arglist=appcall; 02185 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: Required argument %s not in application call to %s !\n", 02186 arglist->filename, arglist->startline, arglist->endline, ad->dtype==ARGD_OPTIONSET?"options":ad->name, app->name); 02187 warns++; 02188 return 1; 02189 } 02190 } 02191 return 0; 02192 #else 02193 return 0; 02194 #endif 02195 } 02196 02197 void check_switch_expr(pval *item, struct argapp *apps) 02198 { 02199 #ifdef AAL_ARGCHECK 02200 /* get and clean the variable name */ 02201 char *buff1, *p; 02202 struct argapp *a,*a2; 02203 struct appsetvar *v,*v2; 02204 struct argchoice *c; 02205 pval *t; 02206 02207 p = item->u1.str; 02208 while (p && *p && (*p == ' ' || *p == '\t' || *p == '$' || *p == '{' ) ) 02209 p++; 02210 02211 buff1 = ast_strdupa(p); 02212 02213 while (strlen(buff1) > 0 && ( buff1[strlen(buff1)-1] == '}' || buff1[strlen(buff1)-1] == ' ' || buff1[strlen(buff1)-1] == '\t')) 02214 buff1[strlen(buff1)-1] = 0; 02215 /* buff1 now contains the variable name */ 02216 v = 0; 02217 for (a=apps; a; a=a->next) { 02218 for (v=a->setvars;v;v=v->next) { 02219 if (strcmp(v->name,buff1) == 0) { 02220 break; 02221 } 02222 } 02223 if ( v ) 02224 break; 02225 } 02226 if (v && v->vals) { 02227 /* we have a match, to a variable that has a set of determined values */ 02228 int def= 0; 02229 int pat = 0; 02230 int f1 = 0; 02231 02232 /* first of all, does this switch have a default case ? */ 02233 for (t=item->u2.statements; t; t=t->next) { 02234 if (t->type == PV_DEFAULT) { 02235 def =1; 02236 break; 02237 } 02238 if (t->type == PV_PATTERN) { 02239 pat++; 02240 } 02241 } 02242 if (def || pat) /* nothing to check. All cases accounted for! */ 02243 return; 02244 for (c=v->vals; c; c=c->next) { 02245 f1 = 0; 02246 for (t=item->u2.statements; t; t=t->next) { 02247 if (t->type == PV_CASE || t->type == PV_PATTERN) { 02248 if (!strcmp(t->u1.str,c->name)) { 02249 f1 = 1; 02250 break; 02251 } 02252 } 02253 } 02254 if (!f1) { 02255 ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: switch with expression(%s) does not handle the case of %s !\n", 02256 item->filename, item->startline, item->endline, item->u1.str, c->name); 02257 warns++; 02258 } 02259 } 02260 /* next, is there an app call in the current exten, that would set this var? */ 02261 f1 = 0; 02262 t = current_extension->u2.statements; 02263 if ( t && t->type == PV_STATEMENTBLOCK ) 02264 t = t->u1.statements; 02265 for (; t && t != item; t=t->next) { 02266 if (t->type == PV_APPLICATION_CALL) { 02267 /* find the application that matches the u1.str */ 02268 for (a2=apps; a2; a2=a2->next) { 02269 if (strcasecmp(a2->name, t->u1.str)==0) { 02270 for (v2=a2->setvars; v2; v2=v2->next) { 02271 if (strcmp(v2->name, buff1) == 0) { 02272 /* found an app that sets the var */ 02273 f1 = 1; 02274 break; 02275 } 02276 } 02277 } 02278 if (f1) 02279 break; 02280 } 02281 } 02282 if (f1) 02283 break; 02284 } 02285 02286 /* see if it sets the var */ 02287 if (!f1) { 02288 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", 02289 item->filename, item->startline, item->endline, item->u1.str); 02290 warns++; 02291 } 02292 } 02293 #endif 02294 } 02295 02296 static void check_context_names(void) 02297 { 02298 pval *i,*j; 02299 for (i=current_db; i; i=i->next) { 02300 if (i->type == PV_CONTEXT || i->type == PV_MACRO) { 02301 for (j=i->next; j; j=j->next) { 02302 if ( j->type == PV_CONTEXT || j->type == PV_MACRO ) { 02303 if ( !strcmp(i->u1.str, j->u1.str) ) 02304 { 02305 ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: The context name (%s) is also declared in file %s, line %d-%d!\n", 02306 i->filename, i->startline, i->endline, i->u1.str, j->filename, j->startline, j->endline); 02307 warns++; 02308 } 02309 } 02310 } 02311 } 02312 } 02313 } 02314 02315 static void check_abstract_reference(pval *abstract_context) 02316 { 02317 pval *i,*j; 02318 /* find some context includes that reference this context */ 02319 02320 02321 /* otherwise, print out a warning */ 02322 for (i=current_db; i; i=i->next) { 02323 if (i->type == PV_CONTEXT) { 02324 for (j=i->u2. statements; j; j=j->next) { 02325 if ( j->type == PV_INCLUDES ) { 02326 struct pval *p4; 02327 for (p4=j->u1.list; p4; p4=p4->next) { 02328 /* for each context pointed to, find it, then find a context/label that matches the 02329 target here! */ 02330 if ( !strcmp(p4->u1.str, abstract_context->u1.str) ) 02331 return; /* found a match! */ 02332 } 02333 } 02334 } 02335 } 02336 } 02337 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", 02338 abstract_context->filename, abstract_context->startline, abstract_context->endline, abstract_context->u1.str); 02339 warns++; 02340 } 02341 02342 02343 void check_pval_item(pval *item, struct argapp *apps, int in_globals) 02344 { 02345 pval *lp; 02346 #ifdef AAL_ARGCHECK 02347 struct argapp *app, *found; 02348 #endif 02349 struct pval *macro_def; 02350 struct pval *app_def; 02351 02352 char errmsg[4096]; 02353 char *strp; 02354 02355 switch (item->type) { 02356 case PV_WORD: 02357 /* fields: item->u1.str == string associated with this (word). 02358 item->u2.arglist == pval list of 4 PV_WORD elements for time values (only in PV_INCLUDES) */ 02359 break; 02360 02361 case PV_MACRO: 02362 /* fields: item->u1.str == name of macro 02363 item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user 02364 item->u2.arglist->u1.str == argument 02365 item->u2.arglist->next == next arg 02366 02367 item->u3.macro_statements == pval list of statements in macro body. 02368 */ 02369 in_abstract_context = 0; 02370 current_context = item; 02371 current_extension = 0; 02372 02373 check_macro_returns(item); 02374 02375 for (lp=item->u2.arglist; lp; lp=lp->next) { 02376 02377 } 02378 check_pval(item->u3.macro_statements, apps,in_globals); 02379 break; 02380 02381 case PV_CONTEXT: 02382 /* fields: item->u1.str == name of context 02383 item->u2.statements == pval list of statements in context body 02384 item->u3.abstract == int 1 if an abstract keyword were present 02385 */ 02386 current_context = item; 02387 current_extension = 0; 02388 if ( item->u3.abstract ) { 02389 in_abstract_context = 1; 02390 check_abstract_reference(item); 02391 } else 02392 in_abstract_context = 0; 02393 check_pval(item->u2.statements, apps,in_globals); 02394 break; 02395 02396 case PV_MACRO_CALL: 02397 /* fields: item->u1.str == name of macro to call 02398 item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user 02399 item->u2.arglist->u1.str == argument 02400 item->u2.arglist->next == next arg 02401 */ 02402 macro_def = find_macro(item->u1.str); 02403 if (!macro_def) { 02404 ast_log(LOG_ERROR, "Error: file %s, line %d-%d: macro call to non-existent %s !\n", 02405 item->filename, item->startline, item->endline, item->u1.str); 02406 errs++; 02407 } else if (macro_def->type != PV_MACRO) { 02408 ast_log(LOG_ERROR,"Error: file %s, line %d-%d: macro call to %s references a context, not a macro!\n", 02409 item->filename, item->startline, item->endline, item->u1.str); 02410 errs++; 02411 } else { 02412 /* macro_def is a MACRO, so do the args match in number? */ 02413 int hereargs = 0; 02414 int thereargs = 0; 02415 02416 for (lp=item->u2.arglist; lp; lp=lp->next) { 02417 hereargs++; 02418 } 02419 for (lp=macro_def->u2.arglist; lp; lp=lp->next) { 02420 thereargs++; 02421 } 02422 if (hereargs != thereargs ) { 02423 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", 02424 item->filename, item->startline, item->endline, item->u1.str, hereargs, thereargs); 02425 errs++; 02426 } 02427 } 02428 break; 02429 02430 case PV_APPLICATION_CALL: 02431 /* fields: item->u1.str == name of application to call 02432 item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user 02433 item->u2.arglist->u1.str == argument 02434 item->u2.arglist->next == next arg 02435 */ 02436 /* Need to check to see if the application is available! */ 02437 app_def = find_context(item->u1.str); 02438 if (app_def && app_def->type == PV_MACRO) { 02439 ast_log(LOG_ERROR,"Error: file %s, line %d-%d: application call to %s references an existing macro, but had no & preceding it!\n", 02440 item->filename, item->startline, item->endline, item->u1.str); 02441 errs++; 02442 } 02443 if (strcasecmp(item->u1.str,"GotoIf") == 0 02444 || strcasecmp(item->u1.str,"GotoIfTime") == 0 02445 || strcasecmp(item->u1.str,"while") == 0 02446 || strcasecmp(item->u1.str,"endwhile") == 0 02447 || strcasecmp(item->u1.str,"random") == 0 02448 || strcasecmp(item->u1.str,"gosub") == 0 02449 || strcasecmp(item->u1.str,"return") == 0 02450 || strcasecmp(item->u1.str,"gosubif") == 0 02451 || strcasecmp(item->u1.str,"continuewhile") == 0 02452 || strcasecmp(item->u1.str,"endwhile") == 0 02453 || strcasecmp(item->u1.str,"execif") == 0 02454 || strcasecmp(item->u1.str,"execiftime") == 0 02455 || strcasecmp(item->u1.str,"exitwhile") == 0 02456 || strcasecmp(item->u1.str,"goto") == 0 02457 || strcasecmp(item->u1.str,"macro") == 0 02458 || strcasecmp(item->u1.str,"macroexclusive") == 0 02459 || strcasecmp(item->u1.str,"macroif") == 0 02460 || strcasecmp(item->u1.str,"stackpop") == 0 02461 || strcasecmp(item->u1.str,"execIf") == 0 ) { 02462 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", 02463 item->filename, item->startline, item->endline, item->u1.str); 02464 warns++; 02465 } 02466 if (strcasecmp(item->u1.str,"macroexit") == 0) { 02467 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: I am converting the MacroExit call here to a return statement.\n", 02468 item->filename, item->startline, item->endline); 02469 item->type = PV_RETURN; 02470 free(item->u1.str); 02471 item->u1.str = 0; 02472 } 02473 02474 #ifdef AAL_ARGCHECK 02475 found = 0; 02476 for (app=apps; app; app=app->next) { 02477 if (strcasecmp(app->name, item->u1.str) == 0) { 02478 found =app; 02479 break; 02480 } 02481 } 02482 if (!found) { 02483 ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: application call to %s not listed in applist database!\n", 02484 item->filename, item->startline, item->endline, item->u1.str); 02485 warns++; 02486 } else 02487 check_app_args(item, item->u2.arglist, app); 02488 #endif 02489 break; 02490 02491 case PV_CASE: 02492 /* fields: item->u1.str == value of case 02493 item->u2.statements == pval list of statements under the case 02494 */ 02495 /* Make sure sequence of statements under case is terminated with goto, return, or break */ 02496 /* find the last statement */ 02497 check_pval(item->u2.statements, apps,in_globals); 02498 break; 02499 02500 case PV_PATTERN: 02501 /* fields: item->u1.str == value of case 02502 item->u2.statements == pval list of statements under the case 02503 */ 02504 /* Make sure sequence of statements under case is terminated with goto, return, or break */ 02505 /* find the last statement */ 02506 02507 check_pval(item->u2.statements, apps,in_globals); 02508 break; 02509 02510 case PV_DEFAULT: 02511 /* fields: 02512 item->u2.statements == pval list of statements under the case 02513 */ 02514 02515 check_pval(item->u2.statements, apps,in_globals); 02516 break; 02517 02518 case PV_CATCH: 02519 /* fields: item->u1.str == name of extension to catch 02520 item->u2.statements == pval list of statements in context body 02521 */ 02522 check_pval(item->u2.statements, apps,in_globals); 02523 break; 02524 02525 case PV_SWITCHES: 02526 /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list 02527 */ 02528 check_pval(item->u1.list, apps,in_globals); 02529 break; 02530 02531 case PV_ESWITCHES: 02532 /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list 02533 */ 02534 check_pval(item->u1.list, apps,in_globals); 02535 break; 02536 02537 case PV_INCLUDES: 02538 /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list 02539 */ 02540 check_pval(item->u1.list, apps,in_globals); 02541 check_includes(item); 02542 for (lp=item->u1.list; lp; lp=lp->next){ 02543 char *incl_context = lp->u1.str; 02544 struct pval *that_context = find_context(incl_context); 02545 02546 if ( lp->u2.arglist ) { 02547 check_timerange(lp->u2.arglist); 02548 check_dow(lp->u2.arglist->next); 02549 check_day(lp->u2.arglist->next->next); 02550 check_month(lp->u2.arglist->next->next->next); 02551 } 02552 02553 if (that_context) { 02554 find_pval_gotos(that_context->u2.statements,0); 02555 02556 } 02557 } 02558 break; 02559 02560 case PV_STATEMENTBLOCK: 02561 /* fields: item->u1.list == pval list of statements in block, one per entry in the list 02562 */ 02563 check_pval(item->u1.list, apps,in_globals); 02564 break; 02565 02566 case PV_VARDEC: 02567 /* fields: item->u1.str == variable name 02568 item->u2.val == variable value to assign 02569 */ 02570 /* the RHS of a vardec is encapsulated in a $[] expr. Is it legal? */ 02571 if( !in_globals ) { /* don't check stuff inside the globals context; no wrapping in $[ ] there... */ 02572 snprintf(errmsg,sizeof(errmsg), "file %s, line %d, columns %d-%d, variable declaration expr '%s':", config, item->startline, item->startcol, item->endcol, item->u2.val); 02573 ast_expr_register_extra_error_info(errmsg); 02574 ast_expr(item->u2.val, expr_output, sizeof(expr_output)); 02575 ast_expr_clear_extra_error_info(); 02576 if ( strpbrk(item->u2.val,"~!-+<>=*/&^") && !strstr(item->u2.val,"${") ) { 02577 ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n", 02578 item->filename, item->startline, item->endline, item->u2.val); 02579 warns++; 02580 } 02581 check_expr2_input(item,item->u2.val); 02582 } 02583 break; 02584 02585 case PV_GOTO: 02586 /* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user. 02587 item->u1.list->u1.str == where the data on a PV_WORD will always be. 02588 */ 02589 /* don't check goto's in abstract contexts */ 02590 if ( in_abstract_context ) 02591 break; 02592 02593 check_goto(item); 02594 break; 02595 02596 case PV_LABEL: 02597 /* fields: item->u1.str == label name 02598 */ 02599 if ( strspn(item->u1.str, "0123456789") == strlen(item->u1.str) ) { 02600 ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: label '%s' is numeric, this is bad practice!\n", 02601 item->filename, item->startline, item->endline, item->u1.str); 02602 warns++; 02603 } 02604 02605 check_label(item); 02606 break; 02607 02608 case PV_FOR: 02609 /* fields: item->u1.for_init == a string containing the initalizer 02610 item->u2.for_test == a string containing the loop test 02611 item->u3.for_inc == a string containing the loop increment 02612 02613 item->u4.for_statements == a pval list of statements in the for () 02614 */ 02615 snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, for test expr '%s':", config, item->startline, item->startcol, item->endcol, item->u2.for_test); 02616 ast_expr_register_extra_error_info(errmsg); 02617 02618 strp = strchr(item->u1.for_init, '='); 02619 if (strp) { 02620 ast_expr(strp+1, expr_output, sizeof(expr_output)); 02621 } 02622 ast_expr(item->u2.for_test, expr_output, sizeof(expr_output)); 02623 strp = strchr(item->u3.for_inc, '='); 02624 if (strp) { 02625 ast_expr(strp+1, expr_output, sizeof(expr_output)); 02626 } 02627 if ( strpbrk(item->u2.for_test,"~!-+<>=*/&^") && !strstr(item->u2.for_test,"${") ) { 02628 ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n", 02629 item->filename, item->startline, item->endline, item->u2.for_test); 02630 warns++; 02631 } 02632 if ( strpbrk(item->u3.for_inc,"~!-+<>=*/&^") && !strstr(item->u3.for_inc,"${") ) { 02633 ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n", 02634 item->filename, item->startline, item->endline, item->u3.for_inc); 02635 warns++; 02636 } 02637 check_expr2_input(item,item->u2.for_test); 02638 check_expr2_input(item,item->u3.for_inc); 02639 02640 ast_expr_clear_extra_error_info(); 02641 check_pval(item->u4.for_statements, apps,in_globals); 02642 break; 02643 02644 case PV_WHILE: 02645 /* fields: item->u1.str == the while conditional, as supplied by user 02646 02647 item->u2.statements == a pval list of statements in the while () 02648 */ 02649 snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, while expr '%s':", config, item->startline, item->startcol, item->endcol, item->u1.str); 02650 ast_expr_register_extra_error_info(errmsg); 02651 ast_expr(item->u1.str, expr_output, sizeof(expr_output)); 02652 ast_expr_clear_extra_error_info(); 02653 if ( strpbrk(item->u1.str,"~!-+<>=*/&^") && !strstr(item->u1.str,"${") ) { 02654 ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n", 02655 item->filename, item->startline, item->endline, item->u1.str); 02656 warns++; 02657 } 02658 check_expr2_input(item,item->u1.str); 02659 check_pval(item->u2.statements, apps,in_globals); 02660 break; 02661 02662 case PV_BREAK: 02663 /* fields: none 02664 */ 02665 check_break(item); 02666 break; 02667 02668 case PV_RETURN: 02669 /* fields: none 02670 */ 02671 break; 02672 02673 case PV_CONTINUE: 02674 /* fields: none 02675 */ 02676 check_continue(item); 02677 break; 02678 02679 case PV_RANDOM: 02680 /* fields: item->u1.str == the random number expression, as supplied by user 02681 02682 item->u2.statements == a pval list of statements in the if () 02683 item->u3.else_statements == a pval list of statements in the else 02684 (could be zero) 02685 */ 02686 snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, random expr '%s':", config, item->startline, item->startcol, item->endcol, item->u1.str); 02687 ast_expr_register_extra_error_info(errmsg); 02688 ast_expr(item->u1.str, expr_output, sizeof(expr_output)); 02689 ast_expr_clear_extra_error_info(); 02690 if ( strpbrk(item->u1.str,"~!-+<>=*/&^") && !strstr(item->u1.str,"${") ) { 02691 ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: random expression '%s' has operators, but no variables. Interesting...\n", 02692 item->filename, item->startline, item->endline, item->u1.str); 02693 warns++; 02694 } 02695 check_expr2_input(item,item->u1.str); 02696 check_pval(item->u2.statements, apps,in_globals); 02697 if (item->u3.else_statements) { 02698 check_pval(item->u3.else_statements, apps,in_globals); 02699 } 02700 break; 02701 02702 case PV_IFTIME: 02703 /* fields: item->u1.list == the if time values, 4 of them, each in PV_WORD, linked list 02704 02705 item->u2.statements == a pval list of statements in the if () 02706 item->u3.else_statements == a pval list of statements in the else 02707 (could be zero) 02708 */ 02709 if ( item->u2.arglist ) { 02710 check_timerange(item->u1.list); 02711 check_dow(item->u1.list->next); 02712 check_day(item->u1.list->next->next); 02713 check_month(item->u1.list->next->next->next); 02714 } 02715 02716 check_pval(item->u2.statements, apps,in_globals); 02717 if (item->u3.else_statements) { 02718 check_pval(item->u3.else_statements, apps,in_globals); 02719 } 02720 break; 02721 02722 case PV_IF: 02723 /* fields: item->u1.str == the if conditional, as supplied by user 02724 02725 item->u2.statements == a pval list of statements in the if () 02726 item->u3.else_statements == a pval list of statements in the else 02727 (could be zero) 02728 */ 02729 snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, if expr '%s':", config, item->startline, item->startcol, item->endcol, item->u1.str); 02730 ast_expr_register_extra_error_info(errmsg); 02731 ast_expr(item->u1.str, expr_output, sizeof(expr_output)); 02732 ast_expr_clear_extra_error_info(); 02733 if ( strpbrk(item->u1.str,"~!-+<>=*/&^") && !strstr(item->u1.str,"${") ) { 02734 ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression '%s' has operators, but no variables. Interesting...\n", 02735 item->filename, item->startline, item->endline, item->u1.str); 02736 warns++; 02737 } 02738 check_expr2_input(item,item->u1.str); 02739 check_pval(item->u2.statements, apps,in_globals); 02740 if (item->u3.else_statements) { 02741 check_pval(item->u3.else_statements, apps,in_globals); 02742 } 02743 break; 02744 02745 case PV_SWITCH: 02746 /* fields: item->u1.str == the switch expression 02747 02748 item->u2.statements == a pval list of statements in the switch, 02749 (will be case statements, most likely!) 02750 */ 02751 /* we can check the switch expression, see if it matches any of the app variables... 02752 if it does, then, are all the possible cases accounted for? */ 02753 check_switch_expr(item, apps); 02754 check_pval(item->u2.statements, apps,in_globals); 02755 break; 02756 02757 case PV_EXTENSION: 02758 /* fields: item->u1.str == the extension name, label, whatever it's called 02759 02760 item->u2.statements == a pval list of statements in the extension 02761 item->u3.hints == a char * hint argument 02762 item->u4.regexten == an int boolean. non-zero says that regexten was specified 02763 */ 02764 current_extension = item ; 02765 02766 check_pval(item->u2.statements, apps,in_globals); 02767 break; 02768 02769 case PV_IGNOREPAT: 02770 /* fields: item->u1.str == the ignorepat data 02771 */ 02772 break; 02773 02774 case PV_GLOBALS: 02775 /* fields: item->u1.statements == pval list of statements, usually vardecs 02776 */ 02777 in_abstract_context = 0; 02778 check_pval(item->u1.statements, apps, 1); 02779 break; 02780 default: 02781 break; 02782 } 02783 } 02784 02785 void check_pval(pval *item, struct argapp *apps, int in_globals) 02786 { 02787 pval *i; 02788 02789 /* checks to do: 02790 1. Do goto's point to actual labels? 02791 2. Do macro calls reference a macro? 02792 3. Does the number of macro args match the definition? 02793 4. Is a macro call missing its & at the front? 02794 5. Application calls-- we could check syntax for existing applications, 02795 but I need some some sort of universal description bnf for a general 02796 sort of method for checking arguments, in number, maybe even type, at least. 02797 Don't want to hand code checks for hundreds of applications. 02798 */ 02799 02800 for (i=item; i; i=i->next) { 02801 check_pval_item(i,apps,in_globals); 02802 } 02803 } 02804 02805 static void ael2_semantic_check(pval *item, int *arg_errs, int *arg_warns, int *arg_notes) 02806 { 02807 02808 #ifdef AAL_ARGCHECK 02809 int argapp_errs =0; 02810 char *rfilename; 02811 #endif 02812 struct argapp *apps=0; 02813 02814 #ifdef AAL_ARGCHECK 02815 rfilename = alloca(10 + strlen(ast_config_AST_VAR_DIR)); 02816 sprintf(rfilename, "%s/applist", ast_config_AST_VAR_DIR); 02817 02818 apps = argdesc_parse(rfilename, &argapp_errs); /* giveth */ 02819 #endif 02820 current_db = item; 02821 errs = warns = notes = 0; 02822 02823 check_context_names(); 02824 check_pval(item, apps, 0); 02825 02826 #ifdef AAL_ARGCHECK 02827 argdesc_destroy(apps); /* taketh away */ 02828 #endif 02829 current_db = 0; 02830 02831 *arg_errs = errs; 02832 *arg_warns = warns; 02833 *arg_notes = notes; 02834 } 02835 02836 /* =============================================================================================== */ 02837 /* "CODE" GENERATOR -- Convert the AEL representation to asterisk extension language */ 02838 /* =============================================================================================== */ 02839 02840 static int control_statement_count = 0; 02841 02842 struct ael_priority *new_prio(void) 02843 { 02844 struct ael_priority *x = (struct ael_priority *)calloc(sizeof(struct ael_priority),1); 02845 return x; 02846 } 02847 02848 struct ael_extension *new_exten(void) 02849 { 02850 struct ael_extension *x = (struct ael_extension *)calloc(sizeof(struct ael_extension),1); 02851 return x; 02852 } 02853 02854 void linkprio(struct ael_extension *exten, struct ael_priority *prio) 02855 { 02856 if (!exten->plist) { 02857 exten->plist = prio; 02858 exten->plist_last = prio; 02859 } else { 02860 exten->plist_last->next = prio; 02861 exten->plist_last = prio; 02862 } 02863 if( !prio->exten ) 02864 prio->exten = exten; /* don't override the switch value */ 02865 } 02866 02867 void destroy_extensions(struct ael_extension *exten) 02868 { 02869 struct ael_extension *ne, *nen; 02870 for (ne=exten; ne; ne=nen) { 02871 struct ael_priority *pe, *pen; 02872 02873 if (ne->name) 02874 free(ne->name); 02875 02876 /* cidmatch fields are allocated with name, and freed when 02877 the name field is freed. Don't do a free for this field, 02878 unless you LIKE to see a crash! */ 02879 02880 if (ne->hints) 02881 free(ne->hints); 02882 02883 for (pe=ne->plist; pe; pe=pen) { 02884 pen = pe->next; 02885 if (pe->app) 02886 free(pe->app); 02887 pe->app = 0; 02888 if (pe->appargs) 02889 free(pe->appargs); 02890 pe->appargs = 0; 02891 pe->origin = 0; 02892 pe->goto_true = 0; 02893 pe->goto_false = 0; 02894 free(pe); 02895 } 02896 nen = ne->next_exten; 02897 ne->next_exten = 0; 02898 ne->plist =0; 02899 ne->plist_last = 0; 02900 ne->next_exten = 0; 02901 ne->loop_break = 0; 02902 ne->loop_continue = 0; 02903 free(ne); 02904 } 02905 } 02906 02907 static int label_inside_case(pval *label) 02908 { 02909 pval *p = label; 02910 02911 while( p && p->type != PV_MACRO && p->type != PV_CONTEXT ) /* early cutout, sort of */ { 02912 if( p->type == PV_CASE || p->type == PV_DEFAULT || p->type == PV_PATTERN ) { 02913 return 1; 02914 } 02915 02916 p = p->dad; 02917 } 02918 return 0; 02919 } 02920 02921 static void linkexten(struct ael_extension *exten, struct ael_extension *add) 02922 { 02923 add->next_exten = exten->next_exten; /* this will reverse the order. Big deal. */ 02924 exten->next_exten = add; 02925 } 02926 02927 static void remove_spaces_before_equals(char *str) 02928 { 02929 char *p; 02930 while( str && *str && *str != '=' ) 02931 { 02932 if( *str == ' ' || *str == '\n' || *str == '\r' || *str == '\t' ) 02933 { 02934 p = str; 02935 while( *p ) 02936 { 02937 *p = *(p+1); 02938 p++; 02939 } 02940 } 02941 else 02942 str++; 02943 } 02944 } 02945 02946 static void gen_match_to_pattern(char *pattern, char *result) 02947 { 02948 /* the result will be a string that will be matched by pattern */ 02949 char *p=pattern, *t=result; 02950 while (*p) { 02951 if (*p == 'x' || *p == 'n' || *p == 'z' || *p == 'X' || *p == 'N' || *p == 'Z') 02952 *t++ = '9'; 02953 else if (*p == '[') { 02954 char *z = p+1; 02955 while (*z != ']') 02956 z++; 02957 if (*(z+1)== ']') 02958 z++; 02959 *t++=*(p+1); /* use the first char in the set */ 02960 p = z; 02961 } else { 02962 *t++ = *p; 02963 } 02964 p++; 02965 } 02966 *t++ = 0; /* cap it off */ 02967 } 02968 02969 static void gen_prios(struct ael_extension *exten, char *label, pval *statement, struct ael_extension *mother_exten, struct ast_context *this_context ) 02970 { 02971 pval *p,*p2,*p3; 02972 struct ael_priority *pr; 02973 struct ael_priority *for_init, *for_test, *for_inc, *for_loop, *for_end; 02974 struct ael_priority *while_test, *while_loop, *while_end; 02975 struct ael_priority *switch_test, *switch_end, *fall_thru; 02976 struct ael_priority *if_test, *if_end, *if_skip, *if_false; 02977 #ifdef OLD_RAND_ACTION 02978 struct ael_priority *rand_test, *rand_end, *rand_skip; 02979 #endif 02980 char buf1[2000]; 02981 char buf2[2000]; 02982 char *strp, *strp2; 02983 char new_label[2000]; 02984 int default_exists; 02985 int local_control_statement_count; 02986 int first; 02987 struct ael_priority *loop_break_save; 02988 struct ael_priority *loop_continue_save; 02989 struct ael_extension *switch_case; 02990 02991 for (p=statement; p; p=p->next) { 02992 switch (p->type) { 02993 case PV_VARDEC: 02994 pr = new_prio(); 02995 pr->type = AEL_APPCALL; 02996 snprintf(buf1,sizeof(buf1),"%s=$[%s]", p->u1.str, p->u2.val); 02997 pr->app = strdup("Set"); 02998 remove_spaces_before_equals(buf1); 02999 pr->appargs = strdup(buf1); 03000 pr->origin = p; 03001 linkprio(exten, pr); 03002 break; 03003 03004 case PV_GOTO: 03005 pr = new_prio(); 03006 pr->type = AEL_APPCALL; 03007 p->u2.goto_target = get_goto_target(p); 03008 if( p->u2.goto_target ) { 03009 p->u3.goto_target_in_case = p->u2.goto_target->u2.label_in_case = label_inside_case(p->u2.goto_target); 03010 } 03011 03012 if (!p->u1.list->next) /* just one */ { 03013 pr->app = strdup("Goto"); 03014 if (!mother_exten) 03015 pr->appargs = strdup(p->u1.list->u1.str); 03016 else { /* for the case of simple within-extension gotos in case/pattern/default statement blocks: */ 03017 snprintf(buf1,sizeof(buf1),"%s|%s", mother_exten->name, p->u1.list->u1.str); 03018 pr->appargs = strdup(buf1); 03019 } 03020 03021 } else if (p->u1.list->next && !p->u1.list->next->next) /* two */ { 03022 snprintf(buf1,sizeof(buf1),"%s|%s", p->u1.list->u1.str, p->u1.list->next->u1.str); 03023 pr->app = strdup("Goto"); 03024 pr->appargs = strdup(buf1); 03025 } else if (p->u1.list->next && p->u1.list->next->next) { 03026 snprintf(buf1,sizeof(buf1),"%s|%s|%s", p->u1.list->u1.str, 03027 p->u1.list->next->u1.str, 03028 p->u1.list->next->next->u1.str); 03029 pr->app = strdup("Goto"); 03030 pr->appargs = strdup(buf1); 03031 } 03032 pr->origin = p; 03033 linkprio(exten, pr); 03034 break; 03035 03036 case PV_LABEL: 03037 pr = new_prio(); 03038 pr->type = AEL_LABEL; 03039 pr->origin = p; 03040 p->u3.compiled_label = exten; 03041 linkprio(exten, pr); 03042 break; 03043 03044 case PV_FOR: 03045 control_statement_count++; 03046 loop_break_save = exten->loop_break; /* save them, then restore before leaving */ 03047 loop_continue_save = exten->loop_continue; 03048 snprintf(new_label,sizeof(new_label),"for-%s-%d", label, control_statement_count); 03049 for_init = new_prio(); 03050 for_inc = new_prio(); 03051 for_test = new_prio(); 03052 for_loop = new_prio(); 03053 for_end = new_prio(); 03054 for_init->type = AEL_APPCALL; 03055 for_inc->type = AEL_APPCALL; 03056 for_test->type = AEL_FOR_CONTROL; 03057 for_test->goto_false = for_end; 03058 for_loop->type = AEL_CONTROL1; /* simple goto */ 03059 for_end->type = AEL_APPCALL; 03060 for_init->app = strdup("Set"); 03061 03062 strcpy(buf2,p->u1.for_init); 03063 remove_spaces_before_equals(buf2); 03064 strp = strchr(buf2, '='); 03065 strp2 = strchr(p->u1.for_init, '='); 03066 if (strp) { 03067 *(strp+1) = 0; 03068 strcat(buf2,"$["); 03069 strncat(buf2,strp2+1, sizeof(buf2)-strlen(strp2+1)-2); 03070 strcat(buf2,"]"); 03071 for_init->appargs = strdup(buf2); 03072 } else 03073 for_init->appargs = strdup(p->u1.for_init); 03074 03075 for_inc->app = strdup("Set"); 03076 03077 strcpy(buf2,p->u3.for_inc); 03078 remove_spaces_before_equals(buf2); 03079 strp = strchr(buf2, '='); 03080 strp2 = strchr(p->u3.for_inc, '='); 03081 if (strp) { 03082 *(strp+1) = 0; 03083 strcat(buf2,"$["); 03084 strncat(buf2,strp2+1, sizeof(buf2)-strlen(strp2+1)-2); 03085 strcat(buf2,"]"); 03086 for_inc->appargs = strdup(buf2); 03087 } else 03088 for_inc->appargs = strdup(p->u3.for_inc); 03089 snprintf(buf1,sizeof(buf1),"$[%s]",p->u2.for_test); 03090 for_test->app = 0; 03091 for_test->appargs = strdup(buf1); 03092 for_loop->goto_true = for_test; 03093 snprintf(buf1,sizeof(buf1),"Finish for-%s-%d", label, control_statement_count); 03094 for_end->app = strdup("NoOp"); 03095 for_end->appargs = strdup(buf1); 03096 /* link & load! */ 03097 linkprio(exten, for_init); 03098 linkprio(exten, for_test); 03099 03100 /* now, put the body of the for loop here */ 03101 exten->loop_break = for_end; 03102 exten->loop_continue = for_test; 03103 03104 gen_prios(exten, new_label, p->u4.for_statements, mother_exten, this_context); /* this will link in all the statements here */ 03105 03106 linkprio(exten, for_inc); 03107 linkprio(exten, for_loop); 03108 linkprio(exten, for_end); 03109 03110 03111 exten->loop_break = loop_break_save; 03112 exten->loop_continue = loop_continue_save; 03113 for_loop->origin = p; 03114 break; 03115 03116 case PV_WHILE: 03117 control_statement_count++; 03118 loop_break_save = exten->loop_break; /* save them, then restore before leaving */ 03119 loop_continue_save = exten->loop_continue; 03120 snprintf(new_label,sizeof(new_label),"while-%s-%d", label, control_statement_count); 03121 while_test = new_prio(); 03122 while_loop = new_prio(); 03123 while_end = new_prio(); 03124 while_test->type = AEL_FOR_CONTROL; 03125 while_test->goto_false = while_end; 03126 while_loop->type = AEL_CONTROL1; /* simple goto */ 03127 while_end->type = AEL_APPCALL; 03128 snprintf(buf1,sizeof(buf1),"$[%s]",p->u1.str); 03129 while_test->app = 0; 03130 while_test->appargs = strdup(buf1); 03131 while_loop->goto_true = while_test; 03132 snprintf(buf1,sizeof(buf1),"Finish while-%s-%d", label, control_statement_count); 03133 while_end->app = strdup("NoOp"); 03134 while_end->appargs = strdup(buf1); 03135 03136 linkprio(exten, while_test); 03137 03138 /* now, put the body of the for loop here */ 03139 exten->loop_break = while_end; 03140 exten->loop_continue = while_test; 03141 03142 gen_prios(exten, new_label, p->u2.statements, mother_exten, this_context); /* this will link in all the while body statements here */ 03143 03144 linkprio(exten, while_loop); 03145 linkprio(exten, while_end); 03146 03147 03148 exten->loop_break = loop_break_save; 03149 exten->loop_continue = loop_continue_save; 03150 while_loop->origin = p; 03151 break; 03152 03153 case PV_SWITCH: 03154 control_statement_count++; 03155 local_control_statement_count = control_statement_count; 03156 loop_break_save = exten->loop_break; /* save them, then restore before leaving */ 03157 loop_continue_save = exten->loop_continue; 03158 snprintf(new_label,sizeof(new_label),"sw-%s-%d", label, control_statement_count); 03159 03160 switch_test = new_prio(); 03161 switch_end = new_prio(); 03162 switch_test->type = AEL_APPCALL; 03163 switch_end->type = AEL_APPCALL; 03164 snprintf(buf1,sizeof(buf1),"sw-%d-%s|1",control_statement_count, p->u1.str); 03165 switch_test->app = strdup("Goto"); 03166 switch_test->appargs = strdup(buf1); 03167 snprintf(buf1,sizeof(buf1),"Finish switch-%s-%d", label, control_statement_count); 03168 switch_end->app = strdup("NoOp"); 03169 switch_end->appargs = strdup(buf1); 03170 switch_end->origin = p; 03171 switch_end->exten = exten; 03172 03173 linkprio(exten, switch_test); 03174 linkprio(exten, switch_end); 03175 03176 exten->loop_break = switch_end; 03177 exten->loop_continue = 0; 03178 default_exists = 0; 03179 03180 for (p2=p->u2.statements; p2; p2=p2->next) { 03181 /* now, for each case/default put the body of the for loop here */ 03182 if (p2->type == PV_CASE) { 03183 /* ok, generate a extension and link it in */ 03184 switch_case = new_exten(); 03185 switch_case->context = this_context; 03186 /* the break/continue locations are inherited from parent */ 03187 switch_case->loop_break = exten->loop_break; 03188 switch_case->loop_continue = exten->loop_continue; 03189 03190 linkexten(exten,switch_case); 03191 snprintf(buf1,sizeof(buf1),"sw-%d-%s", local_control_statement_count, p2->u1.str); 03192 switch_case->name = strdup(buf1); 03193 snprintf(new_label,sizeof(new_label),"sw-%s-%s-%d", label, p2->u1.str, local_control_statement_count); 03194 03195 gen_prios(switch_case, new_label, p2->u2.statements, exten, this_context); /* this will link in all the case body statements here */ 03196 03197 /* here is where we write code to "fall thru" to the next case... if there is one... */ 03198 for (p3=p2->u2.statements; p3; p3=p3->next) { 03199 if (!p3->next) 03200 break; 03201 } 03202 /* p3 now points the last statement... */ 03203 if (!p3 || ( p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN) ) { 03204 /* is there a following CASE/PATTERN/DEFAULT? */ 03205 if (p2->next && p2->next->type == PV_CASE) { 03206 fall_thru = new_prio(); 03207 fall_thru->type = AEL_APPCALL; 03208 fall_thru->app = strdup("Goto"); 03209 snprintf(buf1,sizeof(buf1),"sw-%d-%s|1",local_control_statement_count, p2->next->u1.str); 03210 fall_thru->appargs = strdup(buf1); 03211 linkprio(switch_case, fall_thru); 03212 } else if (p2->next && p2->next->type == PV_PATTERN) { 03213 fall_thru = new_prio(); 03214 fall_thru->type = AEL_APPCALL; 03215 fall_thru->app = strdup("Goto"); 03216 gen_match_to_pattern(p2->next->u1.str, buf2); 03217 snprintf(buf1,sizeof(buf1),"sw-%d-%s|1", local_control_statement_count, buf2); 03218 fall_thru->appargs = strdup(buf1); 03219 linkprio(switch_case, fall_thru); 03220 } else if (p2->next && p2->next->type == PV_DEFAULT) { 03221 fall_thru = new_prio(); 03222 fall_thru->type = AEL_APPCALL; 03223 fall_thru->app = strdup("Goto"); 03224 snprintf(buf1,sizeof(buf1),"sw-%d-.|1",local_control_statement_count); 03225 fall_thru->appargs = strdup(buf1); 03226 linkprio(switch_case, fall_thru); 03227 } else if (!p2->next) { 03228 fall_thru = new_prio(); 03229 fall_thru->type = AEL_CONTROL1; 03230 fall_thru->goto_true = switch_end; 03231 fall_thru->app = strdup("Goto"); 03232 linkprio(switch_case, fall_thru); 03233 } 03234 } 03235 if (switch_case->return_needed) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */ 03236 char buf[2000]; 03237 struct ael_priority *np2 = new_prio(); 03238 np2->type = AEL_APPCALL; 03239 np2->app = strdup("NoOp"); 03240 snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name); 03241 np2->appargs = strdup(buf); 03242 linkprio(switch_case, np2); 03243 switch_case-> return_target = np2; 03244 } 03245 } else if (p2->type == PV_PATTERN) { 03246 /* ok, generate a extension and link it in */ 03247 switch_case = new_exten(); 03248 switch_case->context = this_context; 03249 /* the break/continue locations are inherited from parent */ 03250 switch_case->loop_break = exten->loop_break; 03251 switch_case->loop_continue = exten->loop_continue; 03252 03253 linkexten(exten,switch_case); 03254 snprintf(buf1,sizeof(buf1),"_sw-%d-%s", local_control_statement_count, p2->u1.str); 03255 switch_case->name = strdup(buf1); 03256 snprintf(new_label,sizeof(new_label),"sw-%s-%s-%d", label, p2->u1.str, local_control_statement_count); 03257 03258 gen_prios(switch_case, new_label, p2->u2.statements, exten, this_context); /* this will link in all the while body statements here */ 03259 /* here is where we write code to "fall thru" to the next case... if there is one... */ 03260 for (p3=p2->u2.statements; p3; p3=p3->next) { 03261 if (!p3->next) 03262 break; 03263 } 03264 /* p3 now points the last statement... */ 03265 if (!p3 || ( p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN)) { 03266 /* is there a following CASE/PATTERN/DEFAULT? */ 03267 if (p2->next && p2->next->type == PV_CASE) { 03268 fall_thru = new_prio(); 03269 fall_thru->type = AEL_APPCALL; 03270 fall_thru->app = strdup("Goto"); 03271 snprintf(buf1,sizeof(buf1),"sw-%d-%s|1",local_control_statement_count, p2->next->u1.str); 03272 fall_thru->appargs = strdup(buf1); 03273 linkprio(switch_case, fall_thru); 03274 } else if (p2->next && p2->next->type == PV_PATTERN) { 03275 fall_thru = new_prio(); 03276 fall_thru->type = AEL_APPCALL; 03277 fall_thru->app = strdup("Goto"); 03278 gen_match_to_pattern(p2->next->u1.str, buf2); 03279 snprintf(buf1,sizeof(buf1),"sw-%d-%s|1",local_control_statement_count, buf2); 03280 fall_thru->appargs = strdup(buf1); 03281 linkprio(switch_case, fall_thru); 03282 } else if (p2->next && p2->next->type == PV_DEFAULT) { 03283 fall_thru = new_prio(); 03284 fall_thru->type = AEL_APPCALL; 03285 fall_thru->app = strdup("Goto"); 03286 snprintf(buf1,sizeof(buf1),"sw-%d-.|1",local_control_statement_count); 03287 fall_thru->appargs = strdup(buf1); 03288 linkprio(switch_case, fall_thru); 03289 } else if (!p2->next) { 03290 fall_thru = new_prio(); 03291 fall_thru->type = AEL_CONTROL1; 03292 fall_thru->goto_true = switch_end; 03293 fall_thru->app = strdup("Goto"); 03294 linkprio(switch_case, fall_thru); 03295 } 03296 } 03297 if (switch_case->return_needed) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */ 03298 char buf[2000]; 03299 struct ael_priority *np2 = new_prio(); 03300 np2->type = AEL_APPCALL; 03301 np2->app = strdup("NoOp"); 03302 snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name); 03303 np2->appargs = strdup(buf); 03304 linkprio(switch_case, np2); 03305 switch_case-> return_target = np2; 03306 } 03307 } else if (p2->type == PV_DEFAULT) { 03308 default_exists++; 03309 /* ok, generate a extension and link it in */ 03310 switch_case = new_exten(); 03311 switch_case->context = this_context; 03312 /* the break/continue locations are inherited from parent */ 03313 switch_case->loop_break = exten->loop_break; 03314 switch_case->loop_continue = exten->loop_continue; 03315 linkexten(exten,switch_case); 03316 snprintf(buf1,sizeof(buf1),"_sw-%d-.", local_control_statement_count); 03317 switch_case->name = strdup(buf1); 03318 03319 snprintf(new_label,sizeof(new_label),"sw-%s-default-%d", label, local_control_statement_count); 03320 03321 gen_prios(switch_case, new_label, p2->u2.statements, exten, this_context); /* this will link in all the while body statements here */ 03322 03323 /* here is where we write code to "fall thru" to the next case... if there is one... */ 03324 for (p3=p2->u2.statements; p3; p3=p3->next) { 03325 if (!p3->next) 03326 break; 03327 } 03328 /* p3 now points the last statement... */ 03329 if (!p3 || (p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN)) { 03330 /* is there a following CASE/PATTERN/DEFAULT? */ 03331 if (p2->next && p2->next->type == PV_CASE) { 03332 fall_thru = new_prio(); 03333 fall_thru->type = AEL_APPCALL; 03334 fall_thru->app = strdup("Goto"); 03335 snprintf(buf1,sizeof(buf1),"sw-%d-%s|1",local_control_statement_count, p2->next->u1.str); 03336 fall_thru->appargs = strdup(buf1); 03337 linkprio(switch_case, fall_thru); 03338 } else if (p2->next && p2->next->type == PV_PATTERN) { 03339 fall_thru = new_prio(); 03340 fall_thru->type = AEL_APPCALL; 03341 fall_thru->app = strdup("Goto"); 03342 gen_match_to_pattern(p2->next->u1.str, buf2); 03343 snprintf(buf1,sizeof(buf1),"sw-%d-%s|1",local_control_statement_count, buf2); 03344 fall_thru->appargs = strdup(buf1); 03345 linkprio(switch_case, fall_thru); 03346 } else if (p2->next && p2->next->type == PV_DEFAULT) { 03347 fall_thru = new_prio(); 03348 fall_thru->type = AEL_APPCALL; 03349 fall_thru->app = strdup("Goto"); 03350 snprintf(buf1,sizeof(buf1),"sw-%d-.|1",local_control_statement_count); 03351 fall_thru->appargs = strdup(buf1); 03352 linkprio(switch_case, fall_thru); 03353 } else if (!p2->next) { 03354 fall_thru = new_prio(); 03355 fall_thru->type = AEL_CONTROL1; 03356 fall_thru->goto_true = switch_end; 03357 fall_thru->app = strdup("Goto"); 03358 linkprio(switch_case, fall_thru); 03359 } 03360 } 03361 if (switch_case->return_needed) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */ 03362 char buf[2000]; 03363 struct ael_priority *np2 = new_prio(); 03364 np2->type = AEL_APPCALL; 03365 np2->app = strdup("NoOp"); 03366 snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name); 03367 np2->appargs = strdup(buf); 03368 linkprio(switch_case, np2); 03369 switch_case-> return_target = np2; 03370 } 03371 } else { 03372 /* what could it be??? */ 03373 } 03374 } 03375 03376 exten->loop_break = loop_break_save; 03377 exten->loop_continue = loop_continue_save; 03378 switch_test->origin = p; 03379 switch_end->origin = p; 03380 break; 03381 03382 case PV_MACRO_CALL: 03383 pr = new_prio(); 03384 pr->type = AEL_APPCALL; 03385 snprintf(buf1,sizeof(buf1),"%s|s|1", p->u1.str); 03386 first = 1; 03387 for (p2 = p->u2.arglist; p2; p2 = p2->next) { 03388 if (first) 03389 { 03390 strcat(buf1,"("); 03391 first = 0; 03392 } 03393 else 03394 strcat(buf1,"|"); 03395 strcat(buf1,p2->u1.str); 03396 } 03397 if (!first) 03398 strcat(buf1,")"); 03399 03400 pr->app = strdup("Gosub"); 03401 pr->appargs = strdup(buf1); 03402 pr->origin = p; 03403 linkprio(exten, pr); 03404 break; 03405 03406 case PV_APPLICATION_CALL: 03407 pr = new_prio(); 03408 pr->type = AEL_APPCALL; 03409 buf1[0] = 0; 03410 for (p2 = p->u2.arglist; p2; p2 = p2->next) { 03411 if (p2 != p->u2.arglist ) 03412 strcat(buf1,"|"); 03413 substitute_commas(p2->u1.str); 03414 strcat(buf1,p2->u1.str); 03415 } 03416 pr->app = strdup(p->u1.str); 03417 pr->appargs = strdup(buf1); 03418 pr->origin = p; 03419 linkprio(exten, pr); 03420 break; 03421 03422 case PV_BREAK: 03423 pr = new_prio(); 03424 pr->type = AEL_CONTROL1; /* simple goto */ 03425 pr->goto_true = exten->loop_break; 03426 pr->origin = p; 03427 linkprio(exten, pr); 03428 break; 03429 03430 case PV_RETURN: /* hmmmm */ 03431 pr = new_prio(); 03432 pr->type = AEL_RETURN; /* simple Return */ 03433 /* exten->return_needed++; */ 03434 pr->app = strdup("Return"); 03435 pr->appargs = strdup(""); 03436 pr->origin = p; 03437 linkprio(exten, pr); 03438 break; 03439 03440 case PV_CONTINUE: 03441 pr = new_prio(); 03442 pr->type = AEL_CONTROL1; /* simple goto */ 03443 pr->goto_true = exten->loop_continue; 03444 pr->origin = p; 03445 linkprio(exten, pr); 03446 break; 03447 03448 case PV_IFTIME: 03449 control_statement_count++; 03450 snprintf(new_label,sizeof(new_label),"iftime-%s-%d", label, control_statement_count); 03451 03452 if_test = new_prio(); 03453 if_test->type = AEL_IFTIME_CONTROL; 03454 snprintf(buf1,sizeof(buf1),"%s|%s|%s|%s", 03455 p->u1.list->u1.str, 03456 p->u1.list->next->u1.str, 03457 p->u1.list->next->next->u1.str, 03458 p->u1.list->next->next->next->u1.str); 03459 if_test->app = 0; 03460 if_test->appargs = strdup(buf1); 03461 if_test->origin = p; 03462 03463 if_end = new_prio(); 03464 if_end->type = AEL_APPCALL; 03465 snprintf(buf1,sizeof(buf1),"Finish iftime-%s-%d", label, control_statement_count); 03466 if_end->app = strdup("NoOp"); 03467 if_end->appargs = strdup(buf1); 03468 03469 if (p->u3.else_statements) { 03470 if_skip = new_prio(); 03471 if_skip->type = AEL_CONTROL1; /* simple goto */ 03472 if_skip->goto_true = if_end; 03473 if_skip->origin = p; 03474 03475 } else { 03476 if_skip = 0; 03477 03478 if_test->goto_false = if_end; 03479 } 03480 03481 if_false = new_prio(); 03482 if_false->type = AEL_CONTROL1; 03483 if (p->u3.else_statements) { 03484 if_false->goto_true = if_skip; /* +1 */ 03485 } else { 03486 if_false->goto_true = if_end; 03487 } 03488 03489 /* link & load! */ 03490 linkprio(exten, if_test); 03491 linkprio(exten, if_false); 03492 03493 /* now, put the body of the if here */ 03494 03495 gen_prios(exten, new_label, p->u2.statements, mother_exten, this_context); /* this will link in all the statements here */ 03496 03497 if (p->u3.else_statements) { 03498 linkprio(exten, if_skip); 03499 gen_prios(exten, new_label, p->u3.else_statements, mother_exten, this_context); /* this will link in all the statements here */ 03500 03501 } 03502 03503 linkprio(exten, if_end); 03504 03505 break; 03506 03507 case PV_RANDOM: 03508 case PV_IF: 03509 control_statement_count++; 03510 snprintf(new_label,sizeof(new_label),"if-%s-%d", label, control_statement_count); 03511 03512 if_test = new_prio(); 03513 if_end = new_prio(); 03514 if_test->type = AEL_IF_CONTROL; 03515 if_end->type = AEL_APPCALL; 03516 if ( p->type == PV_RANDOM ) 03517 snprintf(buf1,sizeof(buf1),"$[${RAND(0,99)} < (%s)]",p->u1.str); 03518 else 03519 snprintf(buf1,sizeof(buf1),"$[%s]",p->u1.str); 03520 if_test->app = 0; 03521 if_test->appargs = strdup(buf1); 03522 snprintf(buf1,sizeof(buf1),"Finish if-%s-%d", label, control_statement_count); 03523 if_end->app = strdup("NoOp"); 03524 if_end->appargs = strdup(buf1); 03525 if_test->origin = p; 03526 03527 if (p->u3.else_statements) { 03528 if_skip = new_prio(); 03529 if_skip->type = AEL_CONTROL1; /* simple goto */ 03530 if_skip->goto_true = if_end; 03531 if_test->goto_false = if_skip;; 03532 } else { 03533 if_skip = 0; 03534 if_test->goto_false = if_end;; 03535 } 03536 03537 /* link & load! */ 03538 linkprio(exten, if_test); 03539 03540 /* now, put the body of the if here */ 03541 03542 gen_prios(exten, new_label, p->u2.statements, mother_exten, this_context); /* this will link in all the statements here */ 03543 03544 if (p->u3.else_statements) { 03545 linkprio(exten, if_skip); 03546 gen_prios(exten, new_label, p->u3.else_statements, mother_exten, this_context); /* this will link in all the statements here */ 03547 03548 } 03549 03550 linkprio(exten, if_end); 03551 03552 break; 03553 03554 case PV_STATEMENTBLOCK: 03555 gen_prios(exten, label, p->u1.list, mother_exten, this_context ); /* recurse into the block */ 03556 break; 03557 03558 case PV_CATCH: 03559 control_statement_count++; 03560 /* generate an extension with name of catch, put all catch stats 03561 into this exten! */ 03562 switch_case = new_exten(); 03563 switch_case->context = this_context; 03564 linkexten(exten,switch_case); 03565 switch_case->name = strdup(p->u1.str); 03566 snprintf(new_label,sizeof(new_label),"catch-%s-%d",p->u1.str, control_statement_count); 03567 03568 gen_prios(switch_case, new_label, p->u2.statements,mother_exten,this_context); /* this will link in all the catch body statements here */ 03569 if (switch_case->return_needed) { /* returns now generate a Return() app call, no longer a goto to the end of the exten */ 03570 char buf[2000]; 03571 struct ael_priority *np2 = new_prio(); 03572 np2->type = AEL_APPCALL; 03573 np2->app = strdup("NoOp"); 03574 snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name); 03575 np2->appargs = strdup(buf); 03576 linkprio(switch_case, np2); 03577 switch_case-> return_target = np2; 03578 } 03579 03580 break; 03581 default: 03582 break; 03583 } 03584 } 03585 } 03586 03587 void set_priorities(struct ael_extension *exten) 03588 { 03589 int i; 03590 struct ael_priority *pr; 03591 do { 03592 if (exten->regexten) 03593 i=2; 03594 else 03595 i=1; 03596 03597 for (pr=exten->plist; pr; pr=pr->next) { 03598 pr->priority_num = i; 03599 03600 if (!pr->origin || (pr->origin && pr->origin->type != PV_LABEL) ) /* Labels don't show up in the dialplan, 03601 but we want them to point to the right 03602 priority, which would be the next line 03603 after the label; */ 03604 i++; 03605 } 03606 03607 exten = exten->next_exten; 03608 } while ( exten ); 03609 } 03610 03611 void add_extensions(struct ael_extension *exten) 03612 { 03613 struct ael_priority *pr; 03614 char *label=0; 03615 if (!exten) { 03616 ast_log(LOG_WARNING, "This file is Empty!\n" ); 03617 return; 03618 } 03619 do { 03620 struct ael_priority *last = 0; 03621 03622 if (exten->hints) { 03623 if (ast_add_extension2(exten->context, 0 /*no replace*/, exten->name, PRIORITY_HINT, NULL, exten->cidmatch, 03624 exten->hints, NULL, ast_free, registrar)) { 03625 ast_log(LOG_WARNING, "Unable to add step at priority 'hint' of extension '%s'\n", 03626 exten->name); 03627 } 03628 } 03629 03630 for (pr=exten->plist; pr; pr=pr->next) { 03631 char app[2000]; 03632 char appargs[2000]; 03633 03634 /* before we can add the extension, we need to prep the app/appargs; 03635 the CONTROL types need to be done after the priority numbers are calculated. 03636 */ 03637 if (pr->type == AEL_LABEL) /* don't try to put labels in the dialplan! */ { 03638 last = pr; 03639 continue; 03640 } 03641 03642 if (pr->app) 03643 strcpy(app, pr->app); 03644 else 03645 app[0] = 0; 03646 if (pr->appargs ) 03647 strcpy(appargs, pr->appargs); 03648 else 03649 appargs[0] = 0; 03650 switch( pr->type ) { 03651 case AEL_APPCALL: 03652 /* easy case. Everything is all set up */ 03653 break; 03654 03655 case AEL_CONTROL1: /* FOR loop, WHILE loop, BREAK, CONTINUE, IF, IFTIME */ 03656 /* simple, unconditional goto. */ 03657 strcpy(app,"Goto"); 03658 if (pr->goto_true->origin && pr->goto_true->origin->type == PV_SWITCH ) { 03659 snprintf(appargs,sizeof(appargs),"%s|%d", pr->goto_true->exten->name, pr->goto_true->priority_num); 03660 } else if (pr->goto_true->origin && pr->goto_true->origin->type == PV_IFTIME && pr->goto_true->origin->u3.else_statements ) { 03661 snprintf(appargs,sizeof(appargs),"%d", pr->goto_true->priority_num+1); 03662 } else 03663 snprintf(appargs,sizeof(appargs),"%d", pr->goto_true->priority_num); 03664 break; 03665 03666 case AEL_FOR_CONTROL: /* WHILE loop test, FOR loop test */ 03667 strcpy(app,"GotoIf"); 03668 snprintf(appargs,sizeof(appargs),"%s?%d:%d", pr->appargs, pr->priority_num+1, pr->goto_false->priority_num); 03669 break; 03670 03671 case AEL_IF_CONTROL: 03672 strcpy(app,"GotoIf"); 03673 if (pr->origin->u3.else_statements ) 03674 snprintf(appargs,sizeof(appargs),"%s?%d:%d", pr->appargs, pr->priority_num+1, pr->goto_false->priority_num+1); 03675 else<