2007-08-15 19:21:27 +00:00
/*
* Asterisk - - An open source telephony toolkit .
*
* Copyright ( C ) 2006 , Digium , Inc .
*
* Steve Murphy < murf @ parsetree . com >
*
* See http : //www.asterisk.org for more information about
* the Asterisk project . Please do not directly contact
* any of the maintainers of this project for assistance ;
* the project provides a web site , mailing lists and IRC
* channels for your use .
*
* This program is free software , distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree .
*/
/*! \file
*
* \ brief Compile symbolic Asterisk Extension Logic into Asterisk extensions , version 2.
*
*/
# include "asterisk.h"
ASTERISK_FILE_VERSION ( __FILE__ , " $Revision$ " )
# include <sys/types.h>
# include <stdlib.h>
# include <unistd.h>
# include <stdio.h>
# include <string.h>
# include <ctype.h>
# include <errno.h>
# include <regex.h>
# include <sys/stat.h>
# include "asterisk/pbx.h"
# include "asterisk/config.h"
# include "asterisk/module.h"
# include "asterisk/logger.h"
# include "asterisk/cli.h"
# include "asterisk/app.h"
2007-11-22 03:50:04 +00:00
# include "asterisk/channel.h"
2007-08-15 19:21:27 +00:00
# include "asterisk/callerid.h"
# include "asterisk/pval.h"
# include "asterisk/ael_structs.h"
# ifdef AAL_ARGCHECK
# include "asterisk/argdesc.h"
# endif
2007-09-17 18:57:56 +00:00
# include "asterisk/utils.h"
2007-08-15 19:21:27 +00:00
In these changes, I have added some explanation
of changes to the Set and MSet apps, so people aren't
so shocked and surprised when they upgrade from
1.4 to 1.6.
Also, for the sake of those upgrading from 1.4 to
1.6 with AEL, I provide automatic support for the
"old" way of using Set(), that still does the
exact same old thing with quotes and backslashes
and so on as 1.4 did, by having AEL compile in the
use of MSet() instead of Set(), everywhere it inserts
this code.
But, if the app_set var is set to 1.6 or higher,
it uses the "new", non-evaluative Set().
This only usually happens if the user manually
inserts this into the asterisk.conf file, or runs
the "make samples" command.
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@140824 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2008-09-03 14:01:27 +00:00
extern struct ast_flags ast_compat ;
2007-08-15 19:21:27 +00:00
extern int localized_pbx_load_module ( void ) ;
static char expr_output [ 2096 ] ;
# define AST_PBX_MAX_STACK 128
/* these functions are in ../ast_expr2.fl */
static int errs , warns ;
static int notes ;
# ifdef STANDALONE
static int extensions_dot_conf_loaded = 0 ;
# endif
static char * registrar = " pbx_ael " ;
static pval * current_db ;
static pval * current_context ;
static pval * current_extension ;
static const char * match_context ;
static const char * match_exten ;
static const char * match_label ;
static int in_abstract_context ;
static int count_labels ; /* true, put matcher in label counting mode */
static int label_count ; /* labels are only meant to be counted in a context or exten */
static int return_on_context_match ;
static pval * last_matched_label ;
struct pval * match_pval ( pval * item ) ;
static void check_timerange ( pval * p ) ;
static void check_dow ( pval * DOW ) ;
static void check_day ( pval * DAY ) ;
static void check_month ( pval * MON ) ;
static void check_expr2_input ( pval * expr , char * str ) ;
static int extension_matches ( pval * here , const char * exten , const char * pattern ) ;
static void check_goto ( pval * item ) ;
static void find_pval_goto_item ( pval * item , int lev ) ;
static void find_pval_gotos ( pval * item , int lev ) ;
static int check_break ( pval * item ) ;
static int check_continue ( pval * item ) ;
static void check_label ( pval * item ) ;
static void check_macro_returns ( pval * macro ) ;
static struct pval * find_label_in_current_context ( char * exten , char * label , pval * curr_cont ) ;
static struct pval * find_first_label_in_current_context ( char * label , pval * curr_cont ) ;
static void print_pval_list ( FILE * fin , pval * item , int depth ) ;
static struct pval * find_label_in_current_extension ( const char * label , pval * curr_ext ) ;
static struct pval * find_label_in_current_db ( const char * context , const char * exten , const char * label ) ;
static pval * get_goto_target ( pval * item ) ;
static int label_inside_case ( pval * label ) ;
static void attach_exten ( struct ael_extension * * list , struct ael_extension * newmem ) ;
static void fix_gotos_in_extensions ( struct ael_extension * exten ) ;
static pval * get_extension_or_contxt ( pval * p ) ;
static pval * get_contxt ( pval * p ) ;
static void remove_spaces_before_equals ( char * str ) ;
/* PRETTY PRINTER FOR AEL: ============================================================================= */
static void print_pval ( FILE * fin , pval * item , int depth )
{
int i ;
pval * lp ;
for ( i = 0 ; i < depth ; i + + ) {
fprintf ( fin , " \t " ) ; /* depth == indentation */
}
switch ( item - > type ) {
case PV_WORD :
fprintf ( fin , " %s; \n " , item - > u1 . str ) ; /* usually, words are encapsulated in something else */
break ;
case PV_MACRO :
fprintf ( fin , " macro %s( " , item - > u1 . str ) ;
for ( lp = item - > u2 . arglist ; lp ; lp = lp - > next ) {
if ( lp ! = item - > u2 . arglist )
fprintf ( fin , " , " ) ;
fprintf ( fin , " %s " , lp - > u1 . str ) ;
}
fprintf ( fin , " ) { \n " ) ;
print_pval_list ( fin , item - > u3 . macro_statements , depth + 1 ) ;
for ( i = 0 ; i < depth ; i + + ) {
fprintf ( fin , " \t " ) ; /* depth == indentation */
}
fprintf ( fin , " }; \n \n " ) ;
break ;
case PV_CONTEXT :
if ( item - > u3 . abstract )
fprintf ( fin , " abstract context %s { \n " , item - > u1 . str ) ;
else
fprintf ( fin , " context %s { \n " , item - > u1 . str ) ;
print_pval_list ( fin , item - > u2 . statements , depth + 1 ) ;
for ( i = 0 ; i < depth ; i + + ) {
fprintf ( fin , " \t " ) ; /* depth == indentation */
}
fprintf ( fin , " }; \n \n " ) ;
break ;
case PV_MACRO_CALL :
fprintf ( fin , " &%s( " , item - > u1 . str ) ;
for ( lp = item - > u2 . arglist ; lp ; lp = lp - > next ) {
if ( lp ! = item - > u2 . arglist )
fprintf ( fin , " , " ) ;
fprintf ( fin , " %s " , lp - > u1 . str ) ;
}
fprintf ( fin , " ); \n " ) ;
break ;
case PV_APPLICATION_CALL :
fprintf ( fin , " %s( " , item - > u1 . str ) ;
for ( lp = item - > u2 . arglist ; lp ; lp = lp - > next ) {
if ( lp ! = item - > u2 . arglist )
fprintf ( fin , " , " ) ;
fprintf ( fin , " %s " , lp - > u1 . str ) ;
}
fprintf ( fin , " ); \n " ) ;
break ;
case PV_CASE :
fprintf ( fin , " case %s: \n " , item - > u1 . str ) ;
print_pval_list ( fin , item - > u2 . statements , depth + 1 ) ;
break ;
case PV_PATTERN :
fprintf ( fin , " pattern %s: \n " , item - > u1 . str ) ;
print_pval_list ( fin , item - > u2 . statements , depth + 1 ) ;
break ;
case PV_DEFAULT :
fprintf ( fin , " default: \n " ) ;
print_pval_list ( fin , item - > u2 . statements , depth + 1 ) ;
break ;
case PV_CATCH :
fprintf ( fin , " catch %s { \n " , item - > u1 . str ) ;
print_pval_list ( fin , item - > u2 . statements , depth + 1 ) ;
for ( i = 0 ; i < depth ; i + + ) {
fprintf ( fin , " \t " ) ; /* depth == indentation */
}
fprintf ( fin , " }; \n " ) ;
break ;
case PV_SWITCHES :
fprintf ( fin , " switches { \n " ) ;
print_pval_list ( fin , item - > u1 . list , depth + 1 ) ;
for ( i = 0 ; i < depth ; i + + ) {
fprintf ( fin , " \t " ) ; /* depth == indentation */
}
fprintf ( fin , " }; \n " ) ;
break ;
case PV_ESWITCHES :
fprintf ( fin , " eswitches { \n " ) ;
print_pval_list ( fin , item - > u1 . list , depth + 1 ) ;
for ( i = 0 ; i < depth ; i + + ) {
fprintf ( fin , " \t " ) ; /* depth == indentation */
}
fprintf ( fin , " }; \n " ) ;
break ;
case PV_INCLUDES :
fprintf ( fin , " includes { \n " ) ;
for ( lp = item - > u1 . list ; lp ; lp = lp - > next ) {
for ( i = 0 ; i < depth + 1 ; i + + ) {
fprintf ( fin , " \t " ) ; /* depth == indentation */
}
fprintf ( fin , " %s " , lp - > u1 . str ) ; /* usually, words are encapsulated in something else */
if ( lp - > u2 . arglist )
fprintf ( fin , " |%s|%s|%s|%s " ,
lp - > u2 . arglist - > u1 . str ,
lp - > u2 . arglist - > next - > u1 . str ,
lp - > u2 . arglist - > next - > next - > u1 . str ,
lp - > u2 . arglist - > next - > next - > next - > u1 . str
) ;
fprintf ( fin , " ; \n " ) ; /* usually, words are encapsulated in something else */
}
for ( i = 0 ; i < depth ; i + + ) {
fprintf ( fin , " \t " ) ; /* depth == indentation */
}
fprintf ( fin , " }; \n " ) ;
break ;
case PV_STATEMENTBLOCK :
fprintf ( fin , " { \n " ) ;
print_pval_list ( fin , item - > u1 . list , depth + 1 ) ;
for ( i = 0 ; i < depth ; i + + ) {
fprintf ( fin , " \t " ) ; /* depth == indentation */
}
fprintf ( fin , " } \n " ) ;
break ;
case PV_VARDEC :
fprintf ( fin , " %s=%s; \n " , item - > u1 . str , item - > u2 . val ) ;
break ;
case PV_LOCALVARDEC :
fprintf ( fin , " local %s=%s; \n " , item - > u1 . str , item - > u2 . val ) ;
break ;
case PV_GOTO :
fprintf ( fin , " goto %s " , item - > u1 . list - > u1 . str ) ;
if ( item - > u1 . list - > next )
fprintf ( fin , " ,%s " , item - > u1 . list - > next - > u1 . str ) ;
if ( item - > u1 . list - > next & & item - > u1 . list - > next - > next )
fprintf ( fin , " ,%s " , item - > u1 . list - > next - > next - > u1 . str ) ;
fprintf ( fin , " \n " ) ;
break ;
case PV_LABEL :
fprintf ( fin , " %s: \n " , item - > u1 . str ) ;
break ;
case PV_FOR :
fprintf ( fin , " for (%s; %s; %s) \n " , item - > u1 . for_init , item - > u2 . for_test , item - > u3 . for_inc ) ;
print_pval_list ( fin , item - > u4 . for_statements , depth + 1 ) ;
break ;
case PV_WHILE :
fprintf ( fin , " while (%s) \n " , item - > u1 . str ) ;
print_pval_list ( fin , item - > u2 . statements , depth + 1 ) ;
break ;
case PV_BREAK :
fprintf ( fin , " break; \n " ) ;
break ;
case PV_RETURN :
fprintf ( fin , " return; \n " ) ;
break ;
case PV_CONTINUE :
fprintf ( fin , " continue; \n " ) ;
break ;
case PV_RANDOM :
case PV_IFTIME :
case PV_IF :
if ( item - > type = = PV_IFTIME ) {
fprintf ( fin , " ifTime ( %s|%s|%s|%s ) \n " ,
item - > u1 . list - > u1 . str ,
item - > u1 . list - > next - > u1 . str ,
item - > u1 . list - > next - > next - > u1 . str ,
item - > u1 . list - > next - > next - > next - > u1 . str
) ;
} else if ( item - > type = = PV_RANDOM ) {
fprintf ( fin , " random ( %s ) \n " , item - > u1 . str ) ;
} else
fprintf ( fin , " if ( %s ) \n " , item - > u1 . str ) ;
if ( item - > u2 . statements & & item - > u2 . statements - > next ) {
for ( i = 0 ; i < depth ; i + + ) {
fprintf ( fin , " \t " ) ; /* depth == indentation */
}
fprintf ( fin , " { \n " ) ;
print_pval_list ( fin , item - > u2 . statements , depth + 1 ) ;
for ( i = 0 ; i < depth ; i + + ) {
fprintf ( fin , " \t " ) ; /* depth == indentation */
}
if ( item - > u3 . else_statements )
fprintf ( fin , " } \n " ) ;
else
fprintf ( fin , " }; \n " ) ;
} else if ( item - > u2 . statements ) {
print_pval_list ( fin , item - > u2 . statements , depth + 1 ) ;
} else {
if ( item - > u3 . else_statements )
fprintf ( fin , " {} " ) ;
else
fprintf ( fin , " {}; " ) ;
}
if ( item - > u3 . else_statements ) {
for ( i = 0 ; i < depth ; i + + ) {
fprintf ( fin , " \t " ) ; /* depth == indentation */
}
fprintf ( fin , " else \n " ) ;
print_pval_list ( fin , item - > u3 . else_statements , depth ) ;
}
break ;
case PV_SWITCH :
fprintf ( fin , " switch( %s ) { \n " , item - > u1 . str ) ;
print_pval_list ( fin , item - > u2 . statements , depth + 1 ) ;
for ( i = 0 ; i < depth ; i + + ) {
fprintf ( fin , " \t " ) ; /* depth == indentation */
}
fprintf ( fin , " } \n " ) ;
break ;
case PV_EXTENSION :
if ( item - > u4 . regexten )
fprintf ( fin , " regexten " ) ;
if ( item - > u3 . hints )
fprintf ( fin , " hints(%s) " , item - > u3 . hints ) ;
2007-08-21 20:55:44 +00:00
fprintf ( fin , " %s => " , item - > u1 . str ) ;
2007-08-15 19:21:27 +00:00
print_pval_list ( fin , item - > u2 . statements , depth + 1 ) ;
2007-08-21 20:55:44 +00:00
fprintf ( fin , " \n " ) ;
2007-08-15 19:21:27 +00:00
break ;
case PV_IGNOREPAT :
fprintf ( fin , " ignorepat => %s; \n " , item - > u1 . str ) ;
break ;
case PV_GLOBALS :
fprintf ( fin , " globals { \n " ) ;
print_pval_list ( fin , item - > u1 . statements , depth + 1 ) ;
for ( i = 0 ; i < depth ; i + + ) {
fprintf ( fin , " \t " ) ; /* depth == indentation */
}
fprintf ( fin , " } \n " ) ;
break ;
}
}
static void print_pval_list ( FILE * fin , pval * item , int depth )
{
pval * i ;
for ( i = item ; i ; i = i - > next ) {
print_pval ( fin , i , depth ) ;
}
}
void ael2_print ( char * fname , pval * tree )
{
FILE * fin = fopen ( fname , " w " ) ;
if ( ! fin ) {
ast_log ( LOG_ERROR , " Couldn't open %s for writing. \n " , fname ) ;
return ;
}
print_pval_list ( fin , tree , 0 ) ;
fclose ( fin ) ;
}
/* EMPTY TEMPLATE FUNCS FOR AEL TRAVERSAL: ============================================================================= */
void traverse_pval_template ( pval * item , int depth ) ;
void traverse_pval_item_template ( pval * item , int depth ) ;
void traverse_pval_item_template ( pval * item , int depth ) /* depth comes in handy for a pretty print (indentation),
but you may not need it */
{
pval * lp ;
switch ( item - > type ) {
case PV_WORD :
/* fields: item->u1.str == string associated with this (word). */
break ;
case PV_MACRO :
/* fields: item->u1.str == name of macro
item - > u2 . arglist = = pval list of PV_WORD arguments of macro , as given by user
item - > u2 . arglist - > u1 . str = = argument
item - > u2 . arglist - > next = = next arg
item - > u3 . macro_statements = = pval list of statements in macro body .
*/
for ( lp = item - > u2 . arglist ; lp ; lp = lp - > next ) {
}
traverse_pval_item_template ( item - > u3 . macro_statements , depth + 1 ) ;
break ;
case PV_CONTEXT :
/* fields: item->u1.str == name of context
item - > u2 . statements = = pval list of statements in context body
item - > u3 . abstract = = int 1 if an abstract keyword were present
*/
traverse_pval_item_template ( item - > u2 . statements , depth + 1 ) ;
break ;
case PV_MACRO_CALL :
/* fields: item->u1.str == name of macro to call
item - > u2 . arglist = = pval list of PV_WORD arguments of macro call , as given by user
item - > u2 . arglist - > u1 . str = = argument
item - > u2 . arglist - > next = = next arg
*/
for ( lp = item - > u2 . arglist ; lp ; lp = lp - > next ) {
}
break ;
case PV_APPLICATION_CALL :
/* fields: item->u1.str == name of application to call
item - > u2 . arglist = = pval list of PV_WORD arguments of macro call , as given by user
item - > u2 . arglist - > u1 . str = = argument
item - > u2 . arglist - > next = = next arg
*/
for ( lp = item - > u2 . arglist ; lp ; lp = lp - > next ) {
}
break ;
case PV_CASE :
/* fields: item->u1.str == value of case
item - > u2 . statements = = pval list of statements under the case
*/
traverse_pval_item_template ( item - > u2 . statements , depth + 1 ) ;
break ;
case PV_PATTERN :
/* fields: item->u1.str == value of case
item - > u2 . statements = = pval list of statements under the case
*/
traverse_pval_item_template ( item - > u2 . statements , depth + 1 ) ;
break ;
case PV_DEFAULT :
/* fields:
item - > u2 . statements = = pval list of statements under the case
*/
traverse_pval_item_template ( item - > u2 . statements , depth + 1 ) ;
break ;
case PV_CATCH :
/* fields: item->u1.str == name of extension to catch
item - > u2 . statements = = pval list of statements in context body
*/
traverse_pval_item_template ( item - > u2 . statements , depth + 1 ) ;
break ;
case PV_SWITCHES :
/* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
*/
traverse_pval_item_template ( item - > u1 . list , depth + 1 ) ;
break ;
case PV_ESWITCHES :
/* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
*/
traverse_pval_item_template ( item - > u1 . list , depth + 1 ) ;
break ;
case PV_INCLUDES :
/* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
item - > u2 . arglist = = pval list of 4 PV_WORD elements for time values
*/
traverse_pval_item_template ( item - > u1 . list , depth + 1 ) ;
traverse_pval_item_template ( item - > u2 . arglist , depth + 1 ) ;
break ;
case PV_STATEMENTBLOCK :
/* fields: item->u1.list == pval list of statements in block, one per entry in the list
*/
traverse_pval_item_template ( item - > u1 . list , depth + 1 ) ;
break ;
case PV_LOCALVARDEC :
case PV_VARDEC :
/* fields: item->u1.str == variable name
item - > u2 . val = = variable value to assign
*/
break ;
case PV_GOTO :
/* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user.
item - > u1 . list - > u1 . str = = where the data on a PV_WORD will always be .
*/
if ( item - > u1 . list - > next )
;
if ( item - > u1 . list - > next & & item - > u1 . list - > next - > next )
;
break ;
case PV_LABEL :
/* fields: item->u1.str == label name
*/
break ;
case PV_FOR :
/* fields: item->u1.for_init == a string containing the initalizer
item - > u2 . for_test = = a string containing the loop test
item - > u3 . for_inc = = a string containing the loop increment
item - > u4 . for_statements = = a pval list of statements in the for ( )
*/
traverse_pval_item_template ( item - > u4 . for_statements , depth + 1 ) ;
break ;
case PV_WHILE :
/* fields: item->u1.str == the while conditional, as supplied by user
item - > u2 . statements = = a pval list of statements in the while ( )
*/
traverse_pval_item_template ( item - > u2 . statements , depth + 1 ) ;
break ;
case PV_BREAK :
/* fields: none
*/
break ;
case PV_RETURN :
/* fields: none
*/
break ;
case PV_CONTINUE :
/* fields: none
*/
break ;
case PV_IFTIME :
/* fields: item->u1.list == there are 4 linked PV_WORDs here.
item - > u2 . statements = = a pval list of statements in the if ( )
item - > u3 . else_statements = = a pval list of statements in the else
( could be zero )
*/
traverse_pval_item_template ( item - > u2 . statements , depth + 1 ) ;
if ( item - > u3 . else_statements ) {
traverse_pval_item_template ( item - > u3 . else_statements , depth + 1 ) ;
}
break ;
case PV_RANDOM :
/* fields: item->u1.str == the random number expression, as supplied by user
item - > u2 . statements = = a pval list of statements in the if ( )
item - > u3 . else_statements = = a pval list of statements in the else
( could be zero )
*/
traverse_pval_item_template ( item - > u2 . statements , depth + 1 ) ;
if ( item - > u3 . else_statements ) {
traverse_pval_item_template ( item - > u3 . else_statements , depth + 1 ) ;
}
break ;
case PV_IF :
/* fields: item->u1.str == the if conditional, as supplied by user
item - > u2 . statements = = a pval list of statements in the if ( )
item - > u3 . else_statements = = a pval list of statements in the else
( could be zero )
*/
traverse_pval_item_template ( item - > u2 . statements , depth + 1 ) ;
if ( item - > u3 . else_statements ) {
traverse_pval_item_template ( item - > u3 . else_statements , depth + 1 ) ;
}
break ;
case PV_SWITCH :
/* fields: item->u1.str == the switch expression
item - > u2 . statements = = a pval list of statements in the switch ,
( will be case statements , most likely ! )
*/
traverse_pval_item_template ( item - > u2 . statements , depth + 1 ) ;
break ;
case PV_EXTENSION :
/* fields: item->u1.str == the extension name, label, whatever it's called
item - > u2 . statements = = a pval list of statements in the extension
item - > u3 . hints = = a char * hint argument
item - > u4 . regexten = = an int boolean . non - zero says that regexten was specified
*/
traverse_pval_item_template ( item - > u2 . statements , depth + 1 ) ;
break ;
case PV_IGNOREPAT :
/* fields: item->u1.str == the ignorepat data
*/
break ;
case PV_GLOBALS :
/* fields: item->u1.statements == pval list of statements, usually vardecs
*/
traverse_pval_item_template ( item - > u1 . statements , depth + 1 ) ;
break ;
}
}
void traverse_pval_template ( pval * item , int depth ) /* depth comes in handy for a pretty print (indentation),
but you may not need it */
{
pval * i ;
for ( i = item ; i ; i = i - > next ) {
traverse_pval_item_template ( i , depth ) ;
}
}
/* SEMANTIC CHECKING FOR AEL: ============================================================================= */
/* (not all that is syntactically legal is good! */
static void check_macro_returns ( pval * macro )
{
pval * i ;
if ( ! macro - > u3 . macro_statements )
{
pval * z = calloc ( 1 , sizeof ( struct pval ) ) ;
ast_log ( LOG_WARNING , " Warning: file %s, line %d-%d: The macro %s is empty! I will insert a return. \n " ,
macro - > filename , macro - > startline , macro - > endline , macro - > u1 . str ) ;
z - > type = PV_RETURN ;
z - > startline = macro - > startline ;
z - > endline = macro - > endline ;
z - > startcol = macro - > startcol ;
z - > endcol = macro - > endcol ;
z - > filename = strdup ( macro - > filename ) ;
macro - > u3 . macro_statements = z ;
return ;
}
for ( i = macro - > u3 . macro_statements ; i ; i = i - > next ) {
/* if the last statement in the list is not return, then insert a return there */
if ( i - > next = = NULL ) {
if ( i - > type ! = PV_RETURN ) {
pval * z = calloc ( 1 , sizeof ( struct pval ) ) ;
ast_log ( LOG_WARNING , " Warning: file %s, line %d-%d: The macro %s does not end with a return; I will insert one. \n " ,
macro - > filename , macro - > startline , macro - > endline , macro - > u1 . str ) ;
z - > type = PV_RETURN ;
z - > startline = macro - > startline ;
z - > endline = macro - > endline ;
z - > startcol = macro - > startcol ;
z - > endcol = macro - > endcol ;
z - > filename = strdup ( macro - > filename ) ;
i - > next = z ;
return ;
}
}
}
return ;
}
static int extension_matches ( pval * here , const char * exten , const char * pattern )
{
int err1 ;
regex_t preg ;
/* simple case, they match exactly, the pattern and exten name */
Merged revisions 162013 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r162013 | murf | 2008-12-09 09:31:55 -0700 (Tue, 09 Dec 2008) | 45 lines
(closes issue #14019)
Reported by: ckjohnsonme
Patches:
14019.diff uploaded by murf (license 17)
Tested by: ckjohnsonme, murf
This crash was the result of a few small errors that
would combine in 64-bit land to result in a crash.
32-bit land might have seen these combine to mysteriously
drop the args to an application call, in certain
circumstances.
Also, in trying to find this bug, I spotted
a situation in the flex input, where, in passing
back a 'word' to the parser, it would allocate
a buffer larger than necessary. I changed the
usage in such situations, so that strdup was
not used, but rather, an ast_malloc, followed
by ast_copy_string.
I removed a field from the pval struct, in
u2, that was never getting used, and set in
one spot in the code. I believe it was an
artifact of a previous fix to make switch
cases work invisibly with extens.
And, for goto's I removed a '!' from
before a strcmp, that has been there
since the initial merging of AEL2, that
might prevent the proper target of a
goto from being found. This was pretty
harmless on its own, as it would just
louse up a consistency check for users.
Many thanks to ckjohnsonme for providing
a simplified and complete set of information
about the bug, that helped considerably in
finding and fixing the problem.
Now, to get aelparse up and running again
in trunk, and out of its "horribly broken" state,
so I can run the regression suite!
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@162079 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2008-12-09 17:18:03 +00:00
if ( strcmp ( pattern , exten ) = = 0 )
2007-08-15 19:21:27 +00:00
return 1 ;
if ( pattern [ 0 ] = = ' _ ' ) {
char reg1 [ 2000 ] ;
const char * p ;
char * r = reg1 ;
if ( strlen ( pattern ) * 5 > = 2000 ) /* safety valve */ {
ast_log ( LOG_ERROR , " Error: The pattern %s is way too big. Pattern matching cancelled. \n " ,
pattern ) ;
return 0 ;
}
/* form a regular expression from the pattern, and then match it against exten */
* r + + = ' ^ ' ; /* what if the extension is a pattern ?? */
* r + + = ' _ ' ; /* what if the extension is a pattern ?? */
* r + + = ' ? ' ;
for ( p = pattern + 1 ; * p ; p + + ) {
switch ( * p ) {
case ' X ' :
* r + + = ' [ ' ;
* r + + = ' 0 ' ;
* r + + = ' - ' ;
* r + + = ' 9 ' ;
* r + + = ' X ' ;
* r + + = ' ] ' ;
break ;
case ' Z ' :
* r + + = ' [ ' ;
* r + + = ' 1 ' ;
* r + + = ' - ' ;
* r + + = ' 9 ' ;
* r + + = ' Z ' ;
* r + + = ' ] ' ;
break ;
case ' N ' :
* r + + = ' [ ' ;
* r + + = ' 2 ' ;
* r + + = ' - ' ;
* r + + = ' 9 ' ;
* r + + = ' N ' ;
* r + + = ' ] ' ;
break ;
case ' [ ' :
while ( * p & & * p ! = ' ] ' ) {
* r + + = * p + + ;
}
2009-01-16 00:34:31 +00:00
* r + + = ' ] ' ;
2007-08-15 19:21:27 +00:00
if ( * p ! = ' ] ' ) {
ast_log ( LOG_WARNING , " Warning: file %s, line %d-%d: The extension pattern '%s' is missing a closing bracket \n " ,
here - > filename , here - > startline , here - > endline , pattern ) ;
}
break ;
case ' . ' :
case ' ! ' :
* r + + = ' . ' ;
* r + + = ' * ' ;
break ;
case ' * ' :
* r + + = ' \\ ' ;
* r + + = ' * ' ;
break ;
default :
* r + + = * p ;
break ;
}
}
* r + + = ' $ ' ; /* what if the extension is a pattern ?? */
* r + + = * p + + ; /* put in the closing null */
err1 = regcomp ( & preg , reg1 , REG_NOSUB | REG_EXTENDED ) ;
if ( err1 ) {
char errmess [ 500 ] ;
regerror ( err1 , & preg , errmess , sizeof ( errmess ) ) ;
regfree ( & preg ) ;
ast_log ( LOG_WARNING , " Regcomp of %s failed, error code %d \n " ,
reg1 , err1 ) ;
return 0 ;
}
err1 = regexec ( & preg , exten , 0 , 0 , 0 ) ;
regfree ( & preg ) ;
if ( err1 ) {
/* ast_log(LOG_NOTICE,"*****************************[%d]Extension %s did not match %s(%s)\n",
err1 , exten , pattern , reg1 ) ; */
return 0 ; /* no match */
} else {
/* ast_log(LOG_NOTICE,"*****************************Extension %s matched %s\n",
exten , pattern ) ; */
return 1 ;
}
} else {
if ( strcmp ( exten , pattern ) = = 0 ) {
return 1 ;
} else
return 0 ;
}
}
static void check_expr2_input ( pval * expr , char * str )
{
int spaces = strspn ( str , " \t \n " ) ;
if ( ! strncmp ( str + spaces , " $[ " , 2 ) ) {
ast_log ( LOG_WARNING , " Warning: file %s, line %d-%d: The expression '%s' is redundantly wrapped in '$[ ]'. \n " ,
expr - > filename , expr - > startline , expr - > endline , str ) ;
warns + + ;
}
}
static void check_includes ( pval * includes )
{
struct pval * p4 ;
for ( p4 = includes - > u1 . list ; p4 ; p4 = p4 - > next ) {
/* for each context pointed to, find it, then find a context/label that matches the
target here ! */
char * incl_context = p4 - > u1 . str ;
/* find a matching context name */
struct pval * that_other_context = find_context ( incl_context ) ;
if ( ! that_other_context & & strcmp ( incl_context , " parkedcalls " ) ! = 0 ) {
2007-10-31 04:20:40 +00:00
ast_log ( LOG_WARNING , " Warning: file %s, line %d-%d: The included context '%s' cannot be found. \n \
( You may ignore this warning if ' % s ' exists in extensions . conf , or is created by another module . I cannot check for those . ) \ n " ,
includes - > filename , includes - > startline , includes - > endline , incl_context , incl_context ) ;
2007-08-15 19:21:27 +00:00
warns + + ;
}
}
}
static void check_timerange ( pval * p )
{
char * times ;
char * e ;
int s1 , s2 ;
int e1 , e2 ;
times = ast_strdupa ( p - > u1 . str ) ;
/* Star is all times */
if ( ast_strlen_zero ( times ) | | ! strcmp ( times , " * " ) ) {
return ;
}
/* Otherwise expect a range */
e = strchr ( times , ' - ' ) ;
if ( ! e ) {
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 " ,
p - > filename , p - > startline , p - > endline , times ) ;
warns + + ;
return ;
}
* e = ' \0 ' ;
e + + ;
while ( * e & & ! isdigit ( * e ) )
e + + ;
if ( ! * e ) {
ast_log ( LOG_WARNING , " Warning: file %s, line %d-%d: The time range format (%s) is missing the end time! \n " ,
p - > filename , p - > startline , p - > endline , p - > u1 . str ) ;
warns + + ;
}
if ( sscanf ( times , " %d:%d " , & s1 , & s2 ) ! = 2 ) {
ast_log ( LOG_WARNING , " Warning: file %s, line %d-%d: The start time (%s) isn't quite right! \n " ,
p - > filename , p - > startline , p - > endline , times ) ;
warns + + ;
}
if ( sscanf ( e , " %d:%d " , & e1 , & e2 ) ! = 2 ) {
ast_log ( LOG_WARNING , " Warning: file %s, line %d-%d: The end time (%s) isn't quite right! \n " ,
p - > filename , p - > startline , p - > endline , times ) ;
warns + + ;
}
s1 = s1 * 30 + s2 / 2 ;
if ( ( s1 < 0 ) | | ( s1 > = 24 * 30 ) ) {
ast_log ( LOG_WARNING , " Warning: file %s, line %d-%d: The start time (%s) is out of range! \n " ,
p - > filename , p - > startline , p - > endline , times ) ;
warns + + ;
}
e1 = e1 * 30 + e2 / 2 ;
if ( ( e1 < 0 ) | | ( e1 > = 24 * 30 ) ) {
ast_log ( LOG_WARNING , " Warning: file %s, line %d-%d: The end time (%s) is out of range! \n " ,
p - > filename , p - > startline , p - > endline , e ) ;
warns + + ;
}
return ;
}
static char * days [ ] =
{
" sun " ,
" mon " ,
" tue " ,
" wed " ,
" thu " ,
" fri " ,
" sat " ,
} ;
/*! \brief get_dow: Get day of week */
static void check_dow ( pval * DOW )
{
char * dow ;
char * c ;
/* The following line is coincidence, really! */
int s , e ;
dow = ast_strdupa ( DOW - > u1 . str ) ;
/* Check for all days */
if ( ast_strlen_zero ( dow ) | | ! strcmp ( dow , " * " ) )
return ;
/* Get start and ending days */
c = strchr ( dow , ' - ' ) ;
if ( c ) {
* c = ' \0 ' ;
c + + ;
} else
c = NULL ;
/* Find the start */
s = 0 ;
while ( ( s < 7 ) & & strcasecmp ( dow , days [ s ] ) ) s + + ;
if ( s > = 7 ) {
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 " ,
DOW - > filename , DOW - > startline , DOW - > endline , dow ) ;
warns + + ;
}
if ( c ) {
e = 0 ;
while ( ( e < 7 ) & & strcasecmp ( c , days [ e ] ) ) e + + ;
if ( e > = 7 ) {
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 " ,
DOW - > filename , DOW - > startline , DOW - > endline , c ) ;
warns + + ;
}
} else
e = s ;
}
static void check_day ( pval * DAY )
{
char * day ;
char * c ;
/* The following line is coincidence, really! */
int s , e ;
day = ast_strdupa ( DAY - > u1 . str ) ;
/* Check for all days */
if ( ast_strlen_zero ( day ) | | ! strcmp ( day , " * " ) ) {
return ;
}
/* Get start and ending days */
c = strchr ( day , ' - ' ) ;
if ( c ) {
* c = ' \0 ' ;
c + + ;
}
/* Find the start */
if ( sscanf ( day , " %d " , & s ) ! = 1 ) {
ast_log ( LOG_WARNING , " Warning: file %s, line %d-%d: The start day of month (%s) must be a number! \n " ,
DAY - > filename , DAY - > startline , DAY - > endline , day ) ;
warns + + ;
}
else if ( ( s < 1 ) | | ( s > 31 ) ) {
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 " ,
DAY - > filename , DAY - > startline , DAY - > endline , day ) ;
warns + + ;
}
s - - ;
if ( c ) {
if ( sscanf ( c , " %d " , & e ) ! = 1 ) {
ast_log ( LOG_WARNING , " Warning: file %s, line %d-%d: The end day of month (%s) must be a number! \n " ,
DAY - > filename , DAY - > startline , DAY - > endline , c ) ;
warns + + ;
}
else if ( ( e < 1 ) | | ( e > 31 ) ) {
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 " ,
DAY - > filename , DAY - > startline , DAY - > endline , day ) ;
warns + + ;
}
e - - ;
} else
e = s ;
}
static char * months [ ] =
{
" jan " ,
" feb " ,
" mar " ,
" apr " ,
" may " ,
" jun " ,
" jul " ,
" aug " ,
" sep " ,
" oct " ,
" nov " ,
" dec " ,
} ;
static void check_month ( pval * MON )
{
char * mon ;
char * c ;
/* The following line is coincidence, really! */
int s , e ;
mon = ast_strdupa ( MON - > u1 . str ) ;
/* Check for all days */
if ( ast_strlen_zero ( mon ) | | ! strcmp ( mon , " * " ) )
return ;
/* Get start and ending days */
c = strchr ( mon , ' - ' ) ;
if ( c ) {
* c = ' \0 ' ;
c + + ;
}
/* Find the start */
s = 0 ;
while ( ( s < 12 ) & & strcasecmp ( mon , months [ s ] ) ) s + + ;
if ( s > = 12 ) {
ast_log ( LOG_WARNING , " Warning: file %s, line %d-%d: The start month (%s) must be a one of: 'jan', 'feb', ..., 'dec'! \n " ,
MON - > filename , MON - > startline , MON - > endline , mon ) ;
warns + + ;
}
if ( c ) {
e = 0 ;
while ( ( e < 12 ) & & strcasecmp ( mon , months [ e ] ) ) e + + ;
if ( e > = 12 ) {
ast_log ( LOG_WARNING , " Warning: file %s, line %d-%d: The end month (%s) must be a one of: 'jan', 'feb', ..., 'dec'! \n " ,
MON - > filename , MON - > startline , MON - > endline , c ) ;
warns + + ;
}
} else
e = s ;
}
static int check_break ( pval * item )
{
pval * p = item ;
while ( p & & p - > type ! = PV_MACRO & & p - > type ! = PV_CONTEXT ) /* early cutout, sort of */ {
/* a break is allowed in WHILE, FOR, CASE, DEFAULT, PATTERN; otherwise, it don't make
no sense */
if ( p - > type = = PV_CASE | | p - > type = = PV_DEFAULT | | p - > type = = PV_PATTERN
| | p - > type = = PV_WHILE | | p - > type = = PV_FOR ) {
return 1 ;
}
p = p - > dad ;
}
ast_log ( LOG_ERROR , " Error: file %s, line %d-%d: 'break' not in switch, for, or while statement! \n " ,
item - > filename , item - > startline , item - > endline ) ;
errs + + ;
return 0 ;
}
static int check_continue ( pval * item )
{
pval * p = item ;
while ( p & & p - > type ! = PV_MACRO & & p - > type ! = PV_CONTEXT ) /* early cutout, sort of */ {
/* a break is allowed in WHILE, FOR, CASE, DEFAULT, PATTERN; otherwise, it don't make
no sense */
if ( p - > type = = PV_WHILE | | p - > type = = PV_FOR ) {
return 1 ;
}
p = p - > dad ;
}
ast_log ( LOG_ERROR , " Error: file %s, line %d-%d: 'continue' not in 'for' or 'while' statement! \n " ,
item - > filename , item - > startline , item - > endline ) ;
errs + + ;
return 0 ;
}
static struct pval * in_macro ( pval * item )
{
struct pval * curr ;
curr = item ;
while ( curr ) {
if ( curr - > type = = PV_MACRO ) {
return curr ;
}
curr = curr - > dad ;
}
return 0 ;
}
static struct pval * in_context ( pval * item )
{
struct pval * curr ;
curr = item ;
while ( curr ) {
if ( curr - > type = = PV_MACRO | | curr - > type = = PV_CONTEXT ) {
return curr ;
}
curr = curr - > dad ;
}
return 0 ;
}
/* general purpose goto finder */
static void check_label ( pval * item )
{
struct pval * curr ;
struct pval * x ;
int alright = 0 ;
/* A label outside an extension just plain does not make sense! */
curr = item ;
while ( curr ) {
if ( curr - > type = = PV_MACRO | | curr - > type = = PV_EXTENSION ) {
alright = 1 ;
break ;
}
curr = curr - > dad ;
}
if ( ! alright )
{
ast_log ( LOG_ERROR , " Error: file %s, line %d-%d: Label %s is not within an extension or macro! \n " ,
item - > filename , item - > startline , item - > endline , item - > u1 . str ) ;
errs + + ;
}
/* basically, ensure that a label is not repeated in a context. Period.
The method : well , for each label , find the first label in the context
with the same name . If it ' s not the current label , then throw an error . */
/* printf("==== check_label: ====\n"); */
if ( ! current_extension )
curr = current_context ;
else
curr = current_extension ;
x = find_first_label_in_current_context ( ( char * ) item - > u1 . str , curr ) ;
/* 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); */
if ( x & & x ! = item )
{
ast_log ( LOG_ERROR , " Error: file %s, line %d-%d: Duplicate label %s! Previously defined at file %s, line %d. \n " ,
item - > filename , item - > startline , item - > endline , item - > u1 . str , x - > filename , x - > startline ) ;
errs + + ;
}
/* printf("<<<<< check_label: ====\n"); */
}
static pval * get_goto_target ( pval * item )
{
/* just one item-- the label should be in the current extension */
pval * curr_ext = get_extension_or_contxt ( item ) ; /* containing exten, or macro */
pval * curr_cont ;
if ( item - > u1 . list & & ! item - > u1 . list - > next & & ! strstr ( ( item - > u1 . list ) - > u1 . str , " ${ " ) ) {
struct pval * x = find_label_in_current_extension ( ( char * ) ( ( item - > u1 . list ) - > u1 . str ) , curr_ext ) ;
return x ;
}
curr_cont = get_contxt ( item ) ;
/* TWO items */
if ( item - > u1 . list - > next & & ! item - > u1 . list - > next - > next ) {
if ( ! strstr ( ( item - > u1 . list ) - > u1 . str , " ${ " )
& & ! strstr ( item - > u1 . list - > next - > u1 . str , " ${ " ) ) /* Don't try to match variables */ {
struct pval * x = find_label_in_current_context ( ( char * ) item - > u1 . list - > u1 . str , ( char * ) item - > u1 . list - > next - > u1 . str , curr_cont ) ;
return x ;
}
}
/* All 3 items! */
if ( item - > u1 . list - > next & & item - > u1 . list - > next - > next ) {
/* all three */
pval * first = item - > u1 . list ;
pval * second = item - > u1 . list - > next ;
pval * third = item - > u1 . list - > next - > next ;
if ( ! strstr ( ( item - > u1 . list ) - > u1 . str , " ${ " )
& & ! strstr ( item - > u1 . list - > next - > u1 . str , " ${ " )
& & ! strstr ( item - > u1 . list - > next - > next - > u1 . str , " ${ " ) ) /* Don't try to match variables */ {
struct pval * x = find_label_in_current_db ( ( char * ) first - > u1 . str , ( char * ) second - > u1 . str , ( char * ) third - > u1 . str ) ;
if ( ! x ) {
struct pval * p3 ;
struct pval * that_context = find_context ( item - > u1 . list - > u1 . str ) ;
/* the target of the goto could be in an included context!! Fancy that!! */
/* look for includes in the current context */
if ( that_context ) {
for ( p3 = that_context - > u2 . statements ; p3 ; p3 = p3 - > next ) {
if ( p3 - > type = = PV_INCLUDES ) {
struct pval * p4 ;
for ( p4 = p3 - > u1 . list ; p4 ; p4 = p4 - > next ) {
/* for each context pointed to, find it, then find a context/label that matches the
target here ! */
char * incl_context = p4 - > u1 . str ;
/* find a matching context name */
struct pval * that_other_context = find_context ( incl_context ) ;
if ( that_other_context ) {
struct pval * x3 ;
x3 = find_label_in_current_context ( ( char * ) item - > u1 . list - > next - > u1 . str , ( char * ) item - > u1 . list - > next - > next - > u1 . str , that_other_context ) ;
if ( x3 ) {
return x3 ;
}
}
}
}
}
}
}
return x ;
}
}
return 0 ;
}
static void check_goto ( pval * item )
{
/* check for the target of the goto-- does it exist? */
if ( ! ( item - > u1 . list ) - > next & & ! ( item - > u1 . list ) - > u1 . str ) {
ast_log ( LOG_ERROR , " Error: file %s, line %d-%d: goto: empty label reference found! \n " ,
item - > filename , item - > startline , item - > endline ) ;
errs + + ;
}
/* just one item-- the label should be in the current extension */
if ( item - > u1 . list & & ! item - > u1 . list - > next & & ! strstr ( ( item - > u1 . list ) - > u1 . str , " ${ " ) ) {
struct pval * z = get_extension_or_contxt ( item ) ;
struct pval * x = 0 ;
if ( z )
x = find_label_in_current_extension ( ( char * ) ( ( item - > u1 . list ) - > u1 . str ) , z ) ; /* if in macro, use current context instead */
/* printf("Called find_label_in_current_extension with arg %s; current_extension is %x: %d\n",
( char * ) ( ( item - > u1 . list ) - > u1 . str ) , current_extension ? current_extension : current_context , current_extension ? current_extension - > type : current_context - > type ) ; */
if ( ! x ) {
ast_log ( LOG_ERROR , " Error: file %s, line %d-%d: goto: no label %s exists in the current extension! \n " ,
item - > filename , item - > startline , item - > endline , item - > u1 . list - > u1 . str ) ;
errs + + ;
}
else
return ;
}
/* TWO items */
if ( item - > u1 . list - > next & & ! item - > u1 . list - > next - > next ) {
/* two items */
/* printf("Calling find_label_in_current_context with args %s, %s\n",
( char * ) ( ( item - > u1 . list ) - > u1 . str ) , ( char * ) item - > u1 . list - > next - > u1 . str ) ; */
if ( ! strstr ( ( item - > u1 . list ) - > u1 . str , " ${ " )
& & ! strstr ( item - > u1 . list - > next - > u1 . str , " ${ " ) ) /* Don't try to match variables */ {
struct pval * z = get_contxt ( item ) ;
struct pval * x = 0 ;
if ( z )
x = find_label_in_current_context ( ( char * ) item - > u1 . list - > u1 . str , ( char * ) item - > u1 . list - > next - > u1 . str , z ) ;
if ( ! x ) {
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 " ,
item - > filename , item - > startline , item - > endline , item - > u1 . list - > u1 . str , item - > u1 . list - > next - > u1 . str ) ;
errs + + ;
}
else
return ;
}
}
/* All 3 items! */
if ( item - > u1 . list - > next & & item - > u1 . list - > next - > next ) {
/* all three */
pval * first = item - > u1 . list ;
pval * second = item - > u1 . list - > next ;
pval * third = item - > u1 . list - > next - > next ;
/* printf("Calling find_label_in_current_db with args %s, %s, %s\n",
( char * ) first - > u1 . str , ( char * ) second - > u1 . str , ( char * ) third - > u1 . str ) ; */
if ( ! strstr ( ( item - > u1 . list ) - > u1 . str , " ${ " )
& & ! strstr ( item - > u1 . list - > next - > u1 . str , " ${ " )
& & ! strstr ( item - > u1 . list - > next - > next - > u1 . str , " ${ " ) ) /* Don't try to match variables */ {
struct pval * x = find_label_in_current_db ( ( char * ) first - > u1 . str , ( char * ) second - > u1 . str , ( char * ) third - > u1 . str ) ;
if ( ! x ) {
struct pval * p3 ;
struct pval * found = 0 ;
struct pval * that_context = find_context ( item - > u1 . list - > u1 . str ) ;
/* the target of the goto could be in an included context!! Fancy that!! */
/* look for includes in the current context */
if ( that_context ) {
for ( p3 = that_context - > u2 . statements ; p3 ; p3 = p3 - > next ) {
if ( p3 - > type = = PV_INCLUDES ) {
struct pval * p4 ;
for ( p4 = p3 - > u1 . list ; p4 ; p4 = p4 - > next ) {
/* for each context pointed to, find it, then find a context/label that matches the
target here ! */
char * incl_context = p4 - > u1 . str ;
/* find a matching context name */
struct pval * that_other_context = find_context ( incl_context ) ;
if ( that_other_context ) {
struct pval * x3 ;
x3 = find_label_in_current_context ( ( char * ) item - > u1 . list - > next - > u1 . str , ( char * ) item - > u1 . list - > next - > next - > u1 . str , that_other_context ) ;
if ( x3 ) {
found = x3 ;
break ;
}
}
}
}
}
if ( ! found ) {
ast_log ( LOG_ERROR , " Error: file %s, line %d-%d: goto: no label %s|%s exists in the context %s or its inclusions! \n " ,
item - > filename , item - > startline , item - > endline , item - > u1 . list - > next - > u1 . str , item - > u1 . list - > next - > next - > u1 . str , item - > u1 . list - > u1 . str ) ;
errs + + ;
} else {
struct pval * mac = in_macro ( item ) ; /* is this goto inside a macro? */
if ( mac ) { /* yes! */
struct pval * targ = in_context ( found ) ;
if ( mac ! = targ )
{
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 " ,
item - > filename , item - > startline , item - > endline ) ;
warns + + ;
}
}
}
} else {
/* here is where code would go to check for target existence in extensions.conf files */
# ifdef STANDALONE
struct pbx_find_info pfiq = { . stacklen = 0 } ;
extern int localized_pbx_load_module ( void ) ;
/* if this is a standalone, we will need to make sure the
localized load of extensions . conf is done */
if ( ! extensions_dot_conf_loaded ) {
localized_pbx_load_module ( ) ;
extensions_dot_conf_loaded + + ;
}
pbx_find_extension ( NULL , NULL , & pfiq , first - > u1 . str , second - > u1 . str , atoi ( third - > u1 . str ) ,
atoi ( third - > u1 . str ) ? NULL : third - > u1 . str , NULL ,
atoi ( third - > u1 . str ) ? E_MATCH : E_FINDLABEL ) ;
if ( pfiq . status ! = STATUS_SUCCESS ) {
ast_log ( LOG_WARNING , " Warning: file %s, line %d-%d: goto: Couldn't find goto target %s|%s|%s, not even in extensions.conf! \n " ,
item - > filename , item - > startline , item - > endline , first - > u1 . str , second - > u1 . str , third - > u1 . str ) ;
warns + + ;
}
# else
ast_log ( LOG_WARNING , " Warning: file %s, line %d-%d: goto: Couldn't find goto target %s|%s|%s in the AEL code! \n " ,
item - > filename , item - > startline , item - > endline , first - > u1 . str , second - > u1 . str , third - > u1 . str ) ;
warns + + ;
# endif
}
} else {
struct pval * mac = in_macro ( item ) ; /* is this goto inside a macro? */
if ( mac ) { /* yes! */
struct pval * targ = in_context ( x ) ;
if ( mac ! = targ )
{
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 " ,
item - > filename , item - > startline , item - > endline ) ;
warns + + ;
}
}
}
}
}
}
static void find_pval_goto_item ( pval * item , int lev )
{
struct pval * p4 ;
2008-07-16 17:59:33 +00:00
2007-08-15 19:21:27 +00:00
if ( lev > 100 ) {
2008-07-16 17:59:33 +00:00
ast_log ( LOG_ERROR , " find_pval_goto in infinite loop! item_type: %d \n \n " , item - > type ) ;
2007-08-15 19:21:27 +00:00
return ;
}
switch ( item - > type ) {
case PV_MACRO :
/* fields: item->u1.str == name of macro
item - > u2 . arglist = = pval list of PV_WORD arguments of macro , as given by user
item - > u2 . arglist - > u1 . str = = argument
item - > u2 . arglist - > next = = next arg
item - > u3 . macro_statements = = pval list of statements in macro body .
*/
2008-07-16 17:59:33 +00:00
/* printf("Descending into macro %s at line %d\n", item->u1.str, item->startline); */
2007-08-15 19:21:27 +00:00
find_pval_gotos ( item - > u3 . macro_statements , lev + 1 ) ; /* if we're just searching for a context, don't bother descending into them */
break ;
case PV_CONTEXT :
/* fields: item->u1.str == name of context
item - > u2 . statements = = pval list of statements in context body
item - > u3 . abstract = = int 1 if an abstract keyword were present
*/
break ;
case PV_CASE :
/* fields: item->u1.str == value of case
item - > u2 . statements = = pval list of statements under the case
*/
2008-07-16 17:59:33 +00:00
/* printf("Descending into Case of %s\n", item->u1.str); */
2007-08-15 19:21:27 +00:00
find_pval_gotos ( item - > u2 . statements , lev + 1 ) ;
break ;
case PV_PATTERN :
/* fields: item->u1.str == value of case
item - > u2 . statements = = pval list of statements under the case
*/
2008-07-16 17:59:33 +00:00
/* printf("Descending into Pattern of %s\n", item->u1.str); */
2007-08-15 19:21:27 +00:00
find_pval_gotos ( item - > u2 . statements , lev + 1 ) ;
break ;
case PV_DEFAULT :
/* fields:
item - > u2 . statements = = pval list of statements under the case
*/
2008-07-16 17:59:33 +00:00
/* printf("Descending into default\n"); */
2007-08-15 19:21:27 +00:00
find_pval_gotos ( item - > u2 . statements , lev + 1 ) ;
break ;
case PV_CATCH :
/* fields: item->u1.str == name of extension to catch
item - > u2 . statements = = pval list of statements in context body
*/
2008-07-16 17:59:33 +00:00
/* printf("Descending into catch of %s\n", item->u1.str); */
2007-08-15 19:21:27 +00:00
find_pval_gotos ( item - > u2 . statements , lev + 1 ) ;
break ;
case PV_STATEMENTBLOCK :
/* fields: item->u1.list == pval list of statements in block, one per entry in the list
*/
2008-07-16 17:59:33 +00:00
/* printf("Descending into statement block\n"); */
2007-08-15 19:21:27 +00:00
find_pval_gotos ( item - > u1 . list , lev + 1 ) ;
break ;
case PV_GOTO :
/* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user.
item - > u1 . list - > u1 . str = = where the data on a PV_WORD will always be .
*/
check_goto ( item ) ; /* THE WHOLE FUNCTION OF THIS ENTIRE ROUTINE!!!! */
break ;
case PV_INCLUDES :
/* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
*/
for ( p4 = item - > u1 . list ; p4 ; p4 = p4 - > next ) {
/* for each context pointed to, find it, then find a context/label that matches the
target here ! */
char * incl_context = p4 - > u1 . str ;
/* find a matching context name */
struct pval * that_context = find_context ( incl_context ) ;
2008-07-16 17:59:33 +00:00
if ( that_context & & that_context - > u2 . statements ) {
/* printf("Descending into include of '%s' at line %d; that_context=%s, that_context type=%d\n", incl_context, item->startline, that_context->u1.str, that_context->type); */
find_pval_gotos ( that_context - > u2 . statements , lev + 1 ) ; /* keep working up the includes */
2007-08-15 19:21:27 +00:00
}
}
break ;
case PV_FOR :
/* fields: item->u1.for_init == a string containing the initalizer
item - > u2 . for_test = = a string containing the loop test
item - > u3 . for_inc = = a string containing the loop increment
item - > u4 . for_statements = = a pval list of statements in the for ( )
*/
2008-07-16 17:59:33 +00:00
/* printf("Descending into for at line %d\n", item->startline); */
2007-08-15 19:21:27 +00:00
find_pval_gotos ( item - > u4 . for_statements , lev + 1 ) ;
break ;
case PV_WHILE :
/* fields: item->u1.str == the while conditional, as supplied by user
item - > u2 . statements = = a pval list of statements in the while ( )
*/
2008-07-16 17:59:33 +00:00
/* printf("Descending into while at line %d\n", item->startline); */
2007-08-15 19:21:27 +00:00
find_pval_gotos ( item - > u2 . statements , lev + 1 ) ;
break ;
case PV_RANDOM :
/* fields: item->u1.str == the random number expression, as supplied by user
item - > u2 . statements = = a pval list of statements in the if ( )
item - > u3 . else_statements = = a pval list of statements in the else
( could be zero )
fall thru to PV_IF */
case PV_IFTIME :
/* fields: item->u1.list == the time values, 4 of them, as PV_WORD structs in a list
item - > u2 . statements = = a pval list of statements in the if ( )
item - > u3 . else_statements = = a pval list of statements in the else
( could be zero )
fall thru to PV_IF */
case PV_IF :
/* fields: item->u1.str == the if conditional, as supplied by user
item - > u2 . statements = = a pval list of statements in the if ( )
item - > u3 . else_statements = = a pval list of statements in the else
( could be zero )
*/
2008-07-16 17:59:33 +00:00
/* printf("Descending into random/iftime/if at line %d\n", item->startline); */
2007-08-15 19:21:27 +00:00
find_pval_gotos ( item - > u2 . statements , lev + 1 ) ;
if ( item - > u3 . else_statements ) {
2008-07-16 17:59:33 +00:00
/* printf("Descending into random/iftime/if's ELSE at line %d\n", item->startline); */
2007-08-15 19:21:27 +00:00
find_pval_gotos ( item - > u3 . else_statements , lev + 1 ) ;
}
break ;
case PV_SWITCH :
/* fields: item->u1.str == the switch expression
item - > u2 . statements = = a pval list of statements in the switch ,
( will be case statements , most likely ! )
*/
2008-07-16 17:59:33 +00:00
/* printf("Descending into switch at line %d\n", item->startline); */
2007-08-15 19:21:27 +00:00
find_pval_gotos ( item - > u3 . else_statements , lev + 1 ) ;
break ;
case PV_EXTENSION :
/* fields: item->u1.str == the extension name, label, whatever it's called
item - > u2 . statements = = a pval list of statements in the extension
item - > u3 . hints = = a char * hint argument
item - > u4 . regexten = = an int boolean . non - zero says that regexten was specified
*/
2008-07-16 17:59:33 +00:00
/* printf("Descending into extension %s at line %d\n", item->u1.str, item->startline); */
2007-08-15 19:21:27 +00:00
find_pval_gotos ( item - > u2 . statements , lev + 1 ) ;
break ;
default :
break ;
}
}
static void find_pval_gotos ( pval * item , int lev )
{
pval * i ;
2008-07-16 17:59:33 +00:00
2007-08-15 19:21:27 +00:00
for ( i = item ; i ; i = i - > next ) {
2008-07-16 17:59:33 +00:00
/* printf("About to call pval_goto_item, itemcount=%d, itemtype=%d\n", item_count, i->type); */
2007-08-15 19:21:27 +00:00
find_pval_goto_item ( i , lev ) ;
}
}
/* general purpose label finder */
static struct pval * match_pval_item ( pval * item )
{
pval * x ;
switch ( item - > type ) {
case PV_MACRO :
/* fields: item->u1.str == name of macro
item - > u2 . arglist = = pval list of PV_WORD arguments of macro , as given by user
item - > u2 . arglist - > u1 . str = = argument
item - > u2 . arglist - > next = = next arg
item - > u3 . macro_statements = = pval list of statements in macro body .
*/
/* printf(" matching in MACRO %s, match_context=%s; retoncontmtch=%d; \n", item->u1.str, match_context, return_on_context_match); */
if ( ! strcmp ( match_context , " * " ) | | ! strcmp ( item - > u1 . str , match_context ) ) {
/* printf("MACRO: match context is: %s\n", match_context); */
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 */ {
/* printf("Returning on matching macro %s\n", match_context); */
return item ;
}
if ( ! return_on_context_match ) {
/* printf("Descending into matching macro %s/%s\n", match_context, item->u1.str); */
if ( ( x = match_pval ( item - > u3 . macro_statements ) ) ) {
/* printf("Responded with pval match %x\n", x); */
return x ;
}
}
} else {
/* printf("Skipping context/macro %s\n", item->u1.str); */
}
break ;
case PV_CONTEXT :
/* fields: item->u1.str == name of context
item - > u2 . statements = = pval list of statements in context body
item - > u3 . abstract = = int 1 if an abstract keyword were present
*/
/* printf(" matching in CONTEXT\n"); */
if ( ! strcmp ( match_context , " * " ) | | ! strcmp ( item - > u1 . str , match_context ) ) {
if ( return_on_context_match & & ! strcmp ( item - > u1 . str , match_context ) ) {
/* printf("Returning on matching context %s\n", match_context); */
/* printf("non-CONTEXT: Responded with pval match %x\n", x); */
return item ;
}
if ( ! return_on_context_match ) {
/* printf("Descending into matching context %s\n", match_context); */
if ( ( x = match_pval ( item - > u2 . statements ) ) ) /* if we're just searching for a context, don't bother descending into them */ {
/* printf("CONTEXT: Responded with pval match %x\n", x); */
return x ;
}
}
} else {
/* printf("Skipping context/macro %s\n", item->u1.str); */
}
break ;
case PV_CASE :
/* fields: item->u1.str == value of case
item - > u2 . statements = = pval list of statements under the case
*/
/* printf(" matching in CASE\n"); */
if ( ( x = match_pval ( item - > u2 . statements ) ) ) {
/* printf("CASE: Responded with pval match %x\n", x); */
return x ;
}
break ;
case PV_PATTERN :
/* fields: item->u1.str == value of case
item - > u2 . statements = = pval list of statements under the case
*/
/* printf(" matching in PATTERN\n"); */
if ( ( x = match_pval ( item - > u2 . statements ) ) ) {
/* printf("PATTERN: Responded with pval match %x\n", x); */
return x ;
}
break ;
case PV_DEFAULT :
/* fields:
item - > u2 . statements = = pval list of statements under the case
*/
/* printf(" matching in DEFAULT\n"); */
if ( ( x = match_pval ( item - > u2 . statements ) ) ) {
/* printf("DEFAULT: Responded with pval match %x\n", x); */
return x ;
}
break ;
case PV_CATCH :
/* fields: item->u1.str == name of extension to catch
item - > u2 . statements = = pval list of statements in context body
*/
/* printf(" matching in CATCH\n"); */
if ( ( x = match_pval ( item - > u2 . statements ) ) ) {
/* printf("CATCH: Responded with pval match %x\n", x); */
return x ;
}
break ;
case PV_STATEMENTBLOCK :
/* fields: item->u1.list == pval list of statements in block, one per entry in the list
*/
/* printf(" matching in STATEMENTBLOCK\n"); */
if ( ( x = match_pval ( item - > u1 . list ) ) ) {
/* printf("STATEMENTBLOCK: Responded with pval match %x\n", x); */
return x ;
}
break ;
case PV_LABEL :
/* fields: item->u1.str == label name
*/
/* printf("PV_LABEL %s (cont=%s, exten=%s\n",
item - > u1 . str , current_context - > u1 . str , ( current_extension ? current_extension - > u1 . str : " <macro> " ) ) ; */
if ( count_labels ) {
if ( ! strcmp ( match_label , item - > u1 . str ) ) {
label_count + + ;
last_matched_label = item ;
}
} else {
if ( ! strcmp ( match_label , item - > u1 . str ) ) {
/* printf("LABEL: Responded with pval match %x\n", x); */
return item ;
}
}
break ;
case PV_FOR :
/* fields: item->u1.for_init == a string containing the initalizer
item - > u2 . for_test = = a string containing the loop test
item - > u3 . for_inc = = a string containing the loop increment
item - > u4 . for_statements = = a pval list of statements in the for ( )
*/
/* printf(" matching in FOR\n"); */
if ( ( x = match_pval ( item - > u4 . for_statements ) ) ) {
/* printf("FOR: Responded with pval match %x\n", x);*/
return x ;
}
break ;
case PV_WHILE :
/* fields: item->u1.str == the while conditional, as supplied by user
item - > u2 . statements = = a pval list of statements in the while ( )
*/
/* printf(" matching in WHILE\n"); */
if ( ( x = match_pval ( item - > u2 . statements ) ) ) {
/* printf("WHILE: Responded with pval match %x\n", x); */
return x ;
}
break ;
case PV_RANDOM :
/* fields: item->u1.str == the random number expression, as supplied by user
item - > u2 . statements = = a pval list of statements in the if ( )
item - > u3 . else_statements = = a pval list of statements in the else
( could be zero )
fall thru to PV_IF */
case PV_IFTIME :
/* fields: item->u1.list == the time values, 4 of them, as PV_WORD structs in a list
item - > u2 . statements = = a pval list of statements in the if ( )
item - > u3 . else_statements = = a pval list of statements in the else
( could be zero )
fall thru to PV_IF */
case PV_IF :
/* fields: item->u1.str == the if conditional, as supplied by user
item - > u2 . statements = = a pval list of statements in the if ( )
item - > u3 . else_statements = = a pval list of statements in the else
( could be zero )
*/
/* printf(" matching in IF/IFTIME/RANDOM\n"); */
if ( ( x = match_pval ( item - > u2 . statements ) ) ) {
return x ;
}
if ( item - > u3 . else_statements ) {
if ( ( x = match_pval ( item - > u3 . else_statements ) ) ) {
/* printf("IF/IFTIME/RANDOM: Responded with pval match %x\n", x); */
return x ;
}
}
break ;
case PV_SWITCH :
/* fields: item->u1.str == the switch expression
item - > u2 . statements = = a pval list of statements in the switch ,
( will be case statements , most likely ! )
*/
/* printf(" matching in SWITCH\n"); */
if ( ( x = match_pval ( item - > u2 . statements ) ) ) {
/* printf("SWITCH: Responded with pval match %x\n", x); */
return x ;
}
break ;
case PV_EXTENSION :
/* fields: item->u1.str == the extension name, label, whatever it's called
item - > u2 . statements = = a pval list of statements in the extension
item - > u3 . hints = = a char * hint argument
item - > u4 . regexten = = an int boolean . non - zero says that regexten was specified
*/
/* printf(" matching in EXTENSION\n"); */
if ( ! strcmp ( match_exten , " * " ) | | extension_matches ( item , match_exten , item - > u1 . str ) ) {
/* printf("Descending into matching exten %s => %s\n", match_exten, item->u1.str); */
if ( strcmp ( match_label , " 1 " ) = = 0 ) {
if ( item - > u2 . statements ) {
struct pval * p5 = item - > u2 . statements ;
while ( p5 & & p5 - > type = = PV_LABEL ) /* find the first non-label statement in this context. If it exists, there's a "1" */
p5 = p5 - > next ;
if ( p5 )
return p5 ;
else
return 0 ;
}
else
return 0 ;
}
if ( ( x = match_pval ( item - > u2 . statements ) ) ) {
/* printf("EXTENSION: Responded with pval match %x\n", x); */
return x ;
}
} else {
/* printf("Skipping exten %s\n", item->u1.str); */
}
break ;
default :
/* printf(" matching in default = %d\n", item->type); */
break ;
}
return 0 ;
}
struct pval * match_pval ( pval * item )
{
pval * i ;
for ( i = item ; i ; i = i - > next ) {
pval * x ;
/* printf(" -- match pval: item %d\n", i->type); */
if ( ( x = match_pval_item ( i ) ) ) {
/* printf("match_pval: returning x=%x\n", (int)x); */
return x ; /* cut the search short */
}
}
return 0 ;
}
#if 0
int count_labels_in_current_context ( char * label )
{
label_count = 0 ;
count_labels = 1 ;
return_on_context_match = 0 ;
match_pval ( current_context - > u2 . statements ) ;
return label_count ;
}
# endif
struct pval * find_first_label_in_current_context ( char * label , pval * curr_cont )
{
/* printf(" --- Got args %s, %s\n", exten, label); */
struct pval * ret ;
struct pval * p3 ;
count_labels = 0 ;
return_on_context_match = 0 ;
match_context = " * " ;
match_exten = " * " ;
match_label = label ;
ret = match_pval ( curr_cont ) ;
if ( ret )
return ret ;
/* the target of the goto could be in an included context!! Fancy that!! */
/* look for includes in the current context */
for ( p3 = curr_cont - > u2 . statements ; p3 ; p3 = p3 - > next ) {
if ( p3 - > type = = PV_INCLUDES ) {
struct pval * p4 ;
for ( p4 = p3 - > u1 . list ; p4 ; p4 = p4 - > next ) {
/* for each context pointed to, find it, then find a context/label that matches the
target here ! */
char * incl_context = p4 - > u1 . str ;
/* find a matching context name */
struct pval * that_context = find_context ( incl_context ) ;
if ( that_context ) {
struct pval * x3 ;
x3 = find_first_label_in_current_context ( label , that_context ) ;
if ( x3 ) {
return x3 ;
}
}
}
}
}
return 0 ;
}
struct pval * find_label_in_current_context ( char * exten , char * label , pval * curr_cont )
{
/* printf(" --- Got args %s, %s\n", exten, label); */
struct pval * ret ;
struct pval * p3 ;
count_labels = 0 ;
return_on_context_match = 0 ;
match_context = " * " ;
match_exten = exten ;
match_label = label ;
ret = match_pval ( curr_cont - > u2 . statements ) ;
if ( ret )
return ret ;
/* the target of the goto could be in an included context!! Fancy that!! */
/* look for includes in the current context */
for ( p3 = curr_cont - > u2 . statements ; p3 ; p3 = p3 - > next ) {
if ( p3 - > type = = PV_INCLUDES ) {
struct pval * p4 ;
for ( p4 = p3 - > u1 . list ; p4 ; p4 = p4 - > next ) {
/* for each context pointed to, find it, then find a context/label that matches the
target here ! */
char * incl_context = p4 - > u1 . str ;
/* find a matching context name */
struct pval * that_context = find_context ( incl_context ) ;
if ( that_context ) {
struct pval * x3 ;
x3 = find_label_in_current_context ( exten , label , that_context ) ;
if ( x3 ) {
return x3 ;
}
}
}
}
}
return 0 ;
}
static struct pval * find_label_in_current_extension ( const char * label , pval * curr_ext )
{
/* printf(" --- Got args %s\n", label); */
count_labels = 0 ;
return_on_context_match = 0 ;
match_context = " * " ;
match_exten = " * " ;
match_label = label ;
return match_pval ( curr_ext ) ;
}
static struct pval * find_label_in_current_db ( const char * context , const char * exten , const char * label )
{
/* printf(" --- Got args %s, %s, %s\n", context, exten, label); */
count_labels = 0 ;
return_on_context_match = 0 ;
match_context = context ;
match_exten = exten ;
match_label = label ;
return match_pval ( current_db ) ;
}
struct pval * find_macro ( char * name )
{
return_on_context_match = 1 ;
count_labels = 0 ;
match_context = name ;
match_exten = " * " ; /* don't really need to set these, shouldn't be reached */
match_label = " * " ;
return match_pval ( current_db ) ;
}
struct pval * find_context ( char * name )
{
return_on_context_match = 1 ;
count_labels = 0 ;
match_context = name ;
match_exten = " * " ; /* don't really need to set these, shouldn't be reached */
match_label = " * " ;
return match_pval ( current_db ) ;
}
int is_float ( char * arg )
{
char * s ;
for ( s = arg ; * s ; s + + ) {
if ( * s ! = ' . ' & & ( * s < ' 0 ' | | * s > ' 9 ' ) )
return 0 ;
}
return 1 ;
}
int is_int ( char * arg )
{
char * s ;
for ( s = arg ; * s ; s + + ) {
if ( * s < ' 0 ' | | * s > ' 9 ' )
return 0 ;
}
return 1 ;
}
int is_empty ( char * arg )
{
if ( ! arg )
return 1 ;
if ( * arg = = 0 )
return 1 ;
while ( * arg ) {
if ( * arg ! = ' ' & & * arg ! = ' \t ' )
return 0 ;
arg + + ;
}
return 1 ;
}
# ifdef AAL_ARGCHECK
int option_matches_j ( struct argdesc * should , pval * is , struct argapp * app )
{
struct argchoice * ac ;
char * opcop , * q , * p ;
switch ( should - > dtype ) {
case ARGD_OPTIONSET :
if ( strstr ( is - > u1 . str , " ${ " ) )
return 0 ; /* no checking anything if there's a var reference in there! */
opcop = ast_strdupa ( is - > u1 . str ) ;
for ( q = opcop ; * q ; q + + ) { /* erase the innards of X(innard) type arguments, so we don't get confused later */
if ( * q = = ' ( ' ) {
p = q + 1 ;
while ( * p & & * p ! = ' ) ' )
* p + + = ' + ' ;
q = p + 1 ;
}
}
for ( ac = app - > opts ; ac ; ac = ac - > next ) {
if ( strlen ( ac - > name ) > 1 & & strchr ( ac - > name , ' ( ' ) = = 0 & & strcmp ( ac - > name , is - > u1 . str ) = = 0 ) /* multichar option, no parens, and a match? */
return 0 ;
}
for ( ac = app - > opts ; ac ; ac = ac - > next ) {
if ( strlen ( ac - > name ) = = 1 | | strchr ( ac - > name , ' ( ' ) ) {
char * p = strchr ( opcop , ac - > name [ 0 ] ) ; /* wipe out all matched options in the user-supplied string */
if ( p & & * p = = ' j ' ) {
ast_log ( LOG_ERROR , " Error: file %s, line %d-%d: The j option in the %s application call is not appropriate for AEL! \n " ,
is - > filename , is - > startline , is - > endline , app - > name ) ;
errs + + ;
}
if ( p ) {
* p = ' + ' ;
if ( ac - > name [ 1 ] = = ' ( ' ) {
if ( * ( p + 1 ) ! = ' ( ' ) {
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 " ,
is - > filename , is - > startline , is - > endline , ac - > name [ 0 ] , app - > name ) ;
warns + + ;
}
}
}
}
}
for ( q = opcop ; * q ; q + + ) {
if ( * q ! = ' + ' & & * q ! = ' ( ' & & * q ! = ' ) ' ) {
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 " ,
is - > filename , is - > startline , is - > endline , * q , app - > name ) ;
warns + + ;
}
}
return 1 ;
break ;
default :
return 0 ;
}
}
int option_matches ( struct argdesc * should , pval * is , struct argapp * app )
{
struct argchoice * ac ;
char * opcop ;
switch ( should - > dtype ) {
case ARGD_STRING :
if ( is_empty ( is - > u1 . str ) & & should - > type = = ARGD_REQUIRED )
return 0 ;
if ( is - > u1 . str & & strlen ( is - > u1 . str ) > 0 ) /* most will match */
return 1 ;
break ;
case ARGD_INT :
if ( is_int ( is - > u1 . str ) )
return 1 ;
else
return 0 ;
break ;
case ARGD_FLOAT :
if ( is_float ( is - > u1 . str ) )
return 1 ;
else
return 0 ;
break ;
case ARGD_ENUM :
if ( ! is - > u1 . str | | strlen ( is - > u1 . str ) = = 0 )
return 1 ; /* a null arg in the call will match an enum, I guess! */
for ( ac = should - > choices ; ac ; ac = ac - > next ) {
if ( strcmp ( ac - > name , is - > u1 . str ) = = 0 )
return 1 ;
}
return 0 ;
break ;
case ARGD_OPTIONSET :
opcop = ast_strdupa ( is - > u1 . str ) ;
for ( ac = app - > opts ; ac ; ac = ac - > next ) {
if ( strlen ( ac - > name ) > 1 & & strchr ( ac - > name , ' ( ' ) = = 0 & & strcmp ( ac - > name , is - > u1 . str ) = = 0 ) /* multichar option, no parens, and a match? */
return 1 ;
}
for ( ac = app - > opts ; ac ; ac = ac - > next ) {
if ( strlen ( ac - > name ) = = 1 | | strchr ( ac - > name , ' ( ' ) ) {
char * p = strchr ( opcop , ac - > name [ 0 ] ) ; /* wipe out all matched options in the user-supplied string */
if ( p ) {
* p = ' + ' ;
if ( ac - > name [ 1 ] = = ' ( ' ) {
if ( * ( p + 1 ) = = ' ( ' ) {
char * q = p + 1 ;
while ( * q & & * q ! = ' ) ' ) {
* q + + = ' + ' ;
}
* q = ' + ' ;
}
}
}
}
}
return 1 ;
break ;
case ARGD_VARARG :
return 1 ; /* matches anything */
break ;
}
return 1 ; /* unless some for-sure match or non-match returns, then it must be close enough ... */
}
# endif
int check_app_args ( pval * appcall , pval * arglist , struct argapp * app )
{
# ifdef AAL_ARGCHECK
struct argdesc * ad = app - > args ;
pval * pa ;
int z ;
for ( pa = arglist ; pa ; pa = pa - > next ) {
if ( ! ad ) {
ast_log ( LOG_WARNING , " Warning: file %s, line %d-%d: Extra argument %s not in application call to %s ! \n " ,
arglist - > filename , arglist - > startline , arglist - > endline , pa - > u1 . str , app - > name ) ;
warns + + ;
return 1 ;
} else {
/* find the first entry in the ad list that will match */
do {
if ( ad - > dtype = = ARGD_VARARG ) /* once we hit the VARARG, all bets are off. Discontinue the comparisons */
break ;
z = option_matches ( ad , pa , app ) ;
if ( ! z ) {
if ( ! arglist )
arglist = appcall ;
if ( ad - > type = = ARGD_REQUIRED ) {
ast_log ( LOG_WARNING , " Warning: file %s, line %d-%d: Required argument %s not in application call to %s ! \n " ,
arglist - > filename , arglist - > startline , arglist - > endline , ad - > dtype = = ARGD_OPTIONSET ? " options " : ad - > name , app - > name ) ;
warns + + ;
return 1 ;
}
} else if ( z & & ad - > dtype = = ARGD_OPTIONSET ) {
option_matches_j ( ad , pa , app ) ;
}
ad = ad - > next ;
} while ( ad & & ! z ) ;
}
}
/* any app nodes left, that are not optional? */
for ( ; ad ; ad = ad - > next ) {
if ( ad - > type = = ARGD_REQUIRED & & ad - > dtype ! = ARGD_VARARG ) {
if ( ! arglist )
arglist = appcall ;
ast_log ( LOG_WARNING , " Warning: file %s, line %d-%d: Required argument %s not in application call to %s ! \n " ,
arglist - > filename , arglist - > startline , arglist - > endline , ad - > dtype = = ARGD_OPTIONSET ? " options " : ad - > name , app - > name ) ;
warns + + ;
return 1 ;
}
}
return 0 ;
# else
return 0 ;
# endif
}
void check_switch_expr ( pval * item , struct argapp * apps )
{
# ifdef AAL_ARGCHECK
/* get and clean the variable name */
char * buff1 , * p ;
struct argapp * a , * a2 ;
struct appsetvar * v , * v2 ;
struct argchoice * c ;
pval * t ;
p = item - > u1 . str ;
while ( p & & * p & & ( * p = = ' ' | | * p = = ' \t ' | | * p = = ' $ ' | | * p = = ' { ' ) )
p + + ;
buff1 = ast_strdupa ( p ) ;
while ( strlen ( buff1 ) > 0 & & ( buff1 [ strlen ( buff1 ) - 1 ] = = ' } ' | | buff1 [ strlen ( buff1 ) - 1 ] = = ' ' | | buff1 [ strlen ( buff1 ) - 1 ] = = ' \t ' ) )
buff1 [ strlen ( buff1 ) - 1 ] = 0 ;
/* buff1 now contains the variable name */
v = 0 ;
for ( a = apps ; a ; a = a - > next ) {
for ( v = a - > setvars ; v ; v = v - > next ) {
if ( strcmp ( v - > name , buff1 ) = = 0 ) {
break ;
}
}
if ( v )
break ;
}
if ( v & & v - > vals ) {
/* we have a match, to a variable that has a set of determined values */
int def = 0 ;
int pat = 0 ;
int f1 = 0 ;
/* first of all, does this switch have a default case ? */
for ( t = item - > u2 . statements ; t ; t = t - > next ) {
if ( t - > type = = PV_DEFAULT ) {
def = 1 ;
break ;
}
if ( t - > type = = PV_PATTERN ) {
pat + + ;
}
}
if ( def | | pat ) /* nothing to check. All cases accounted for! */
return ;
for ( c = v - > vals ; c ; c = c - > next ) {
f1 = 0 ;
for ( t = item - > u2 . statements ; t ; t = t - > next ) {
if ( t - > type = = PV_CASE | | t - > type = = PV_PATTERN ) {
if ( ! strcmp ( t - > u1 . str , c - > name ) ) {
f1 = 1 ;
break ;
}
}
}
if ( ! f1 ) {
ast_log ( LOG_WARNING , " Warning: file %s, line %d-%d: switch with expression(%s) does not handle the case of %s ! \n " ,
item - > filename , item - > startline , item - > endline , item - > u1 . str , c - > name ) ;
warns + + ;
}
}
/* next, is there an app call in the current exten, that would set this var? */
f1 = 0 ;
t = current_extension - > u2 . statements ;
if ( t & & t - > type = = PV_STATEMENTBLOCK )
t = t - > u1 . statements ;
for ( ; t & & t ! = item ; t = t - > next ) {
if ( t - > type = = PV_APPLICATION_CALL ) {
/* find the application that matches the u1.str */
for ( a2 = apps ; a2 ; a2 = a2 - > next ) {
if ( strcasecmp ( a2 - > name , t - > u1 . str ) = = 0 ) {
for ( v2 = a2 - > setvars ; v2 ; v2 = v2 - > next ) {
if ( strcmp ( v2 - > name , buff1 ) = = 0 ) {
/* found an app that sets the var */
f1 = 1 ;
break ;
}
}
}
if ( f1 )
break ;
}
}
if ( f1 )
break ;
}
/* see if it sets the var */
if ( ! f1 ) {
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 " ,
item - > filename , item - > startline , item - > endline , item - > u1 . str ) ;
warns + + ;
}
}
# else
pval * t , * tl = 0 , * p2 ;
int def = 0 ;
/* first of all, does this switch have a default case ? */
for ( t = item - > u2 . statements ; t ; t = t - > next ) {
if ( t - > type = = PV_DEFAULT ) {
def = 1 ;
break ;
}
tl = t ;
}
if ( def ) /* nothing to check. All cases accounted for! */
return ;
/* if no default, warn and insert a default case at the end */
p2 = tl - > next = calloc ( 1 , sizeof ( struct pval ) ) ;
p2 - > type = PV_DEFAULT ;
p2 - > startline = tl - > startline ;
p2 - > endline = tl - > endline ;
p2 - > startcol = tl - > startcol ;
p2 - > endcol = tl - > endcol ;
p2 - > filename = strdup ( tl - > filename ) ;
ast_log ( LOG_WARNING , " Warning: file %s, line %d-%d: A default case was automatically added to the switch. \n " ,
p2 - > filename , p2 - > startline , p2 - > endline ) ;
warns + + ;
# endif
}
static void check_context_names ( void )
{
pval * i , * j ;
for ( i = current_db ; i ; i = i - > next ) {
if ( i - > type = = PV_CONTEXT | | i - > type = = PV_MACRO ) {
for ( j = i - > next ; j ; j = j - > next ) {
if ( j - > type = = PV_CONTEXT | | j - > type = = PV_MACRO ) {
Merged revisions 87168 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r87168 | murf | 2007-10-26 10:34:02 -0600 (Fri, 26 Oct 2007) | 1 line
closes issue #11086 where a user complains that references to following contexts report a problem; The problem was REALLy that he was referring to empty contexts, which were being ignored. Reporter stated that empty contexts should be OK. I checked it out against extensions.conf, and sure enough, empty contexts ARE ok. So, I removed the restriction from AEL. This, though, highlighted a problem with multiple contexts of the same name. This should be OK, also. So, I added the extend keyword to AEL, and it can preceed the 'context' keyword (mixed with 'abstract', if nec.). This will turn off the warnings in AEL if the same context name is used 2 or more times. Also, I now call ast_context_find_or_create for contexts now, instead of just ast_context_create; I did this because pbx_config does this. The 'extend' keyword thus becomes a statement of intent. AEL can now duplicate the behavior of pbx_config,
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@87187 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2007-10-26 17:39:39 +00:00
if ( ! strcmp ( i - > u1 . str , j - > u1 . str ) & & ! ( i - > u3 . abstract & 2 ) & & ! ( j - > u3 . abstract & 2 ) )
2007-08-15 19:21:27 +00:00
{
Merged revisions 87168 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r87168 | murf | 2007-10-26 10:34:02 -0600 (Fri, 26 Oct 2007) | 1 line
closes issue #11086 where a user complains that references to following contexts report a problem; The problem was REALLy that he was referring to empty contexts, which were being ignored. Reporter stated that empty contexts should be OK. I checked it out against extensions.conf, and sure enough, empty contexts ARE ok. So, I removed the restriction from AEL. This, though, highlighted a problem with multiple contexts of the same name. This should be OK, also. So, I added the extend keyword to AEL, and it can preceed the 'context' keyword (mixed with 'abstract', if nec.). This will turn off the warnings in AEL if the same context name is used 2 or more times. Also, I now call ast_context_find_or_create for contexts now, instead of just ast_context_create; I did this because pbx_config does this. The 'extend' keyword thus becomes a statement of intent. AEL can now duplicate the behavior of pbx_config,
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@87187 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2007-10-26 17:39:39 +00:00
ast_log ( LOG_WARNING , " Warning: file %s, line %d-%d: The context name (%s) is also declared in file %s, line %d-%d! (and neither is marked 'extend') \n " ,
2007-08-15 19:21:27 +00:00
i - > filename , i - > startline , i - > endline , i - > u1 . str , j - > filename , j - > startline , j - > endline ) ;
warns + + ;
}
}
}
}
}
}
static void check_abstract_reference ( pval * abstract_context )
{
pval * i , * j ;
/* find some context includes that reference this context */
/* otherwise, print out a warning */
for ( i = current_db ; i ; i = i - > next ) {
if ( i - > type = = PV_CONTEXT ) {
for ( j = i - > u2 . statements ; j ; j = j - > next ) {
if ( j - > type = = PV_INCLUDES ) {
struct pval * p4 ;
for ( p4 = j - > u1 . list ; p4 ; p4 = p4 - > next ) {
/* for each context pointed to, find it, then find a context/label that matches the
target here ! */
if ( ! strcmp ( p4 - > u1 . str , abstract_context - > u1 . str ) )
return ; /* found a match! */
}
}
}
}
}
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 " ,
abstract_context - > filename , abstract_context - > startline , abstract_context - > endline , abstract_context - > u1 . str ) ;
warns + + ;
}
void check_pval_item ( pval * item , struct argapp * apps , int in_globals )
{
pval * lp ;
# ifdef AAL_ARGCHECK
struct argapp * app , * found ;
# endif
struct pval * macro_def ;
struct pval * app_def ;
char errmsg [ 4096 ] ;
char * strp ;
switch ( item - > type ) {
case PV_WORD :
/* fields: item->u1.str == string associated with this (word).
item - > u2 . arglist = = pval list of 4 PV_WORD elements for time values ( only in PV_INCLUDES ) */
break ;
case PV_MACRO :
/* fields: item->u1.str == name of macro
item - > u2 . arglist = = pval list of PV_WORD arguments of macro , as given by user
item - > u2 . arglist - > u1 . str = = argument
item - > u2 . arglist - > next = = next arg
item - > u3 . macro_statements = = pval list of statements in macro body .
*/
in_abstract_context = 0 ;
current_context = item ;
current_extension = 0 ;
check_macro_returns ( item ) ;
for ( lp = item - > u2 . arglist ; lp ; lp = lp - > next ) {
}
check_pval ( item - > u3 . macro_statements , apps , in_globals ) ;
break ;
case PV_CONTEXT :
/* fields: item->u1.str == name of context
item - > u2 . statements = = pval list of statements in context body
item - > u3 . abstract = = int 1 if an abstract keyword were present
*/
current_context = item ;
current_extension = 0 ;
if ( item - > u3 . abstract ) {
in_abstract_context = 1 ;
check_abstract_reference ( item ) ;
} else
in_abstract_context = 0 ;
check_pval ( item - > u2 . statements , apps , in_globals ) ;
break ;
case PV_MACRO_CALL :
/* fields: item->u1.str == name of macro to call
item - > u2 . arglist = = pval list of PV_WORD arguments of macro call , as given by user
item - > u2 . arglist - > u1 . str = = argument
item - > u2 . arglist - > next = = next arg
*/
# ifdef STANDALONE
/* if this is a standalone, we will need to make sure the
localized load of extensions . conf is done */
if ( ! extensions_dot_conf_loaded ) {
localized_pbx_load_module ( ) ;
extensions_dot_conf_loaded + + ;
}
# endif
macro_def = find_macro ( item - > u1 . str ) ;
if ( ! macro_def ) {
# ifdef STANDALONE
struct pbx_find_info pfiq = { . stacklen = 0 } ;
struct pbx_find_info pfiq2 = { . stacklen = 0 } ;
/* look for the macro in the extensions.conf world */
pbx_find_extension ( NULL , NULL , & pfiq , item - > u1 . str , " s " , 1 , NULL , NULL , E_MATCH ) ;
if ( pfiq . status ! = STATUS_SUCCESS ) {
char namebuf2 [ 256 ] ;
snprintf ( namebuf2 , 256 , " macro-%s " , item - > u1 . str ) ;
/* look for the macro in the extensions.conf world */
pbx_find_extension ( NULL , NULL , & pfiq2 , namebuf2 , " s " , 1 , NULL , NULL , E_MATCH ) ;
if ( pfiq2 . status = = STATUS_SUCCESS ) {
ast_log ( LOG_WARNING , " Warning: file %s, line %d-%d: macro call to non-existent %s! (macro-%s was found in the extensions.conf stuff, but we are using gosubs!) \n " ,
item - > filename , item - > startline , item - > endline , item - > u1 . str , item - > u1 . str ) ;
warns + + ;
} else {
ast_log ( LOG_WARNING , " Warning: file %s, line %d-%d: macro call to non-existent %s! (Not even in the extensions.conf stuff!) \n " ,
item - > filename , item - > startline , item - > endline , item - > u1 . str ) ;
warns + + ;
}
}
# else
ast_log ( LOG_WARNING , " Warning: file %s, line %d-%d: macro call to %s cannot be found in the AEL code! \n " ,
item - > filename , item - > startline , item - > endline , item - > u1 . str ) ;
warns + + ;
# endif
# ifdef THIS_IS_1DOT4
char namebuf2 [ 256 ] ;
snprintf ( namebuf2 , 256 , " macro-%s " , item - > u1 . str ) ;
/* look for the macro in the extensions.conf world */
pbx_find_extension ( NULL , NULL , & pfiq , namebuf2 , " s " , 1 , NULL , NULL , E_MATCH ) ;
if ( pfiq . status ! = STATUS_SUCCESS ) {
ast_log ( LOG_WARNING , " Warning: file %s, line %d-%d: macro call to %s was not found in the AEL, nor the extensions.conf ! \n " ,
item - > filename , item - > startline , item - > endline , item - > u1 . str ) ;
warns + + ;
}
# endif
} else if ( macro_def - > type ! = PV_MACRO ) {
ast_log ( LOG_ERROR , " Error: file %s, line %d-%d: macro call to %s references a context, not a macro! \n " ,
item - > filename , item - > startline , item - > endline , item - > u1 . str ) ;
errs + + ;
} else {
/* macro_def is a MACRO, so do the args match in number? */
int hereargs = 0 ;
int thereargs = 0 ;
for ( lp = item - > u2 . arglist ; lp ; lp = lp - > next ) {
hereargs + + ;
}
for ( lp = macro_def - > u2 . arglist ; lp ; lp = lp - > next ) {
thereargs + + ;
}
if ( hereargs ! = thereargs ) {
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 " ,
item - > filename , item - > startline , item - > endline , item - > u1 . str , hereargs , thereargs ) ;
errs + + ;
}
}
break ;
case PV_APPLICATION_CALL :
/* fields: item->u1.str == name of application to call
item - > u2 . arglist = = pval list of PV_WORD arguments of macro call , as given by user
item - > u2 . arglist - > u1 . str = = argument
item - > u2 . arglist - > next = = next arg
*/
/* Need to check to see if the application is available! */
app_def = find_context ( item - > u1 . str ) ;
if ( app_def & & app_def - > type = = PV_MACRO ) {
ast_log ( LOG_ERROR , " Error: file %s, line %d-%d: application call to %s references an existing macro, but had no & preceding it! \n " ,
item - > filename , item - > startline , item - > endline , item - > u1 . str ) ;
errs + + ;
}
if ( strcasecmp ( item - > u1 . str , " GotoIf " ) = = 0
| | strcasecmp ( item - > u1 . str , " GotoIfTime " ) = = 0
| | strcasecmp ( item - > u1 . str , " while " ) = = 0
| | strcasecmp ( item - > u1 . str , " endwhile " ) = = 0
| | strcasecmp ( item - > u1 . str , " random " ) = = 0
| | strcasecmp ( item - > u1 . str , " gosub " ) = = 0
| | strcasecmp ( item - > u1 . str , " return " ) = = 0
| | strcasecmp ( item - > u1 . str , " gosubif " ) = = 0
| | strcasecmp ( item - > u1 . str , " continuewhile " ) = = 0
| | strcasecmp ( item - > u1 . str , " endwhile " ) = = 0
| | strcasecmp ( item - > u1 . str , " execif " ) = = 0
| | strcasecmp ( item - > u1 . str , " execiftime " ) = = 0
| | strcasecmp ( item - > u1 . str , " exitwhile " ) = = 0
| | strcasecmp ( item - > u1 . str , " goto " ) = = 0
| | strcasecmp ( item - > u1 . str , " macro " ) = = 0
| | strcasecmp ( item - > u1 . str , " macroexclusive " ) = = 0
| | strcasecmp ( item - > u1 . str , " macroif " ) = = 0
| | strcasecmp ( item - > u1 . str , " stackpop " ) = = 0
| | strcasecmp ( item - > u1 . str , " execIf " ) = = 0 ) {
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 " ,
item - > filename , item - > startline , item - > endline , item - > u1 . str ) ;
warns + + ;
}
if ( strcasecmp ( item - > u1 . str , " macroexit " ) = = 0 ) {
ast_log ( LOG_WARNING , " Warning: file %s, line %d-%d: I am converting the MacroExit call here to a return statement. \n " ,
item - > filename , item - > startline , item - > endline ) ;
item - > type = PV_RETURN ;
free ( item - > u1 . str ) ;
item - > u1 . str = 0 ;
}
# ifdef AAL_ARGCHECK
found = 0 ;
for ( app = apps ; app ; app = app - > next ) {
if ( strcasecmp ( app - > name , item - > u1 . str ) = = 0 ) {
found = app ;
break ;
}
}
if ( ! found ) {
ast_log ( LOG_WARNING , " Warning: file %s, line %d-%d: application call to %s not listed in applist database! \n " ,
item - > filename , item - > startline , item - > endline , item - > u1 . str ) ;
warns + + ;
} else
check_app_args ( item , item - > u2 . arglist , app ) ;
# endif
break ;
case PV_CASE :
/* fields: item->u1.str == value of case
item - > u2 . statements = = pval list of statements under the case
*/
/* Make sure sequence of statements under case is terminated with goto, return, or break */
/* find the last statement */
check_pval ( item - > u2 . statements , apps , in_globals ) ;
break ;
case PV_PATTERN :
/* fields: item->u1.str == value of case
item - > u2 . statements = = pval list of statements under the case
*/
/* Make sure sequence of statements under case is terminated with goto, return, or break */
/* find the last statement */
check_pval ( item - > u2 . statements , apps , in_globals ) ;
break ;
case PV_DEFAULT :
/* fields:
item - > u2 . statements = = pval list of statements under the case
*/
check_pval ( item - > u2 . statements , apps , in_globals ) ;
break ;
case PV_CATCH :
/* fields: item->u1.str == name of extension to catch
item - > u2 . statements = = pval list of statements in context body
*/
check_pval ( item - > u2 . statements , apps , in_globals ) ;
break ;
case PV_SWITCHES :
/* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
*/
check_pval ( item - > u1 . list , apps , in_globals ) ;
break ;
case PV_ESWITCHES :
/* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
*/
check_pval ( item - > u1 . list , apps , in_globals ) ;
break ;
case PV_INCLUDES :
/* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
*/
check_pval ( item - > u1 . list , apps , in_globals ) ;
check_includes ( item ) ;
for ( lp = item - > u1 . list ; lp ; lp = lp - > next ) {
char * incl_context = lp - > u1 . str ;
struct pval * that_context = find_context ( incl_context ) ;
if ( lp - > u2 . arglist ) {
check_timerange ( lp - > u2 . arglist ) ;
check_dow ( lp - > u2 . arglist - > next ) ;
check_day ( lp - > u2 . arglist - > next - > next ) ;
check_month ( lp - > u2 . arglist - > next - > next - > next ) ;
}
if ( that_context ) {
find_pval_gotos ( that_context - > u2 . statements , 0 ) ;
}
}
break ;
case PV_STATEMENTBLOCK :
/* fields: item->u1.list == pval list of statements in block, one per entry in the list
*/
check_pval ( item - > u1 . list , apps , in_globals ) ;
break ;
case PV_VARDEC :
/* fields: item->u1.str == variable name
item - > u2 . val = = variable value to assign
*/
/* the RHS of a vardec is encapsulated in a $[] expr. Is it legal? */
if ( ! in_globals ) { /* don't check stuff inside the globals context; no wrapping in $[ ] there... */
snprintf ( errmsg , sizeof ( errmsg ) , " file %s, line %d, columns %d-%d, variable declaration expr '%s': " , item - > filename , item - > startline , item - > startcol , item - > endcol , item - > u2 . val ) ;
ast_expr_register_extra_error_info ( errmsg ) ;
ast_expr ( item - > u2 . val , expr_output , sizeof ( expr_output ) , NULL ) ;
ast_expr_clear_extra_error_info ( ) ;
if ( strpbrk ( item - > u2 . val , " ~!-+<>=*/&^ " ) & & ! strstr ( item - > u2 . val , " ${ " ) ) {
ast_log ( LOG_WARNING , " Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting... \n " ,
item - > filename , item - > startline , item - > endline , item - > u2 . val ) ;
warns + + ;
}
check_expr2_input ( item , item - > u2 . val ) ;
}
break ;
case PV_LOCALVARDEC :
/* fields: item->u1.str == variable name
item - > u2 . val = = variable value to assign
*/
/* the RHS of a vardec is encapsulated in a $[] expr. Is it legal? */
snprintf ( errmsg , sizeof ( errmsg ) , " file %s, line %d, columns %d-%d, variable declaration expr '%s': " , item - > filename , item - > startline , item - > startcol , item - > endcol , item - > u2 . val ) ;
ast_expr_register_extra_error_info ( errmsg ) ;
ast_expr ( item - > u2 . val , expr_output , sizeof ( expr_output ) , NULL ) ;
ast_expr_clear_extra_error_info ( ) ;
if ( strpbrk ( item - > u2 . val , " ~!-+<>=*/&^ " ) & & ! strstr ( item - > u2 . val , " ${ " ) ) {
ast_log ( LOG_WARNING , " Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting... \n " ,
item - > filename , item - > startline , item - > endline , item - > u2 . val ) ;
warns + + ;
}
check_expr2_input ( item , item - > u2 . val ) ;
break ;
case PV_GOTO :
/* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user.
item - > u1 . list - > u1 . str = = where the data on a PV_WORD will always be .
*/
/* don't check goto's in abstract contexts */
if ( in_abstract_context )
break ;
check_goto ( item ) ;
break ;
case PV_LABEL :
/* fields: item->u1.str == label name
*/
if ( strspn ( item - > u1 . str , " 0123456789 " ) = = strlen ( item - > u1 . str ) ) {
ast_log ( LOG_WARNING , " Warning: file %s, line %d-%d: label '%s' is numeric, this is bad practice! \n " ,
item - > filename , item - > startline , item - > endline , item - > u1 . str ) ;
warns + + ;
}
check_label ( item ) ;
break ;
case PV_FOR :
/* fields: item->u1.for_init == a string containing the initalizer
item - > u2 . for_test = = a string containing the loop test
item - > u3 . for_inc = = a string containing the loop increment
item - > u4 . for_statements = = a pval list of statements in the for ( )
*/
snprintf ( errmsg , sizeof ( errmsg ) , " file %s, line %d, columns %d-%d, for test expr '%s': " , item - > filename , item - > startline , item - > startcol , item - > endcol , item - > u2 . for_test ) ;
ast_expr_register_extra_error_info ( errmsg ) ;
strp = strchr ( item - > u1 . for_init , ' = ' ) ;
if ( strp ) {
ast_expr ( strp + 1 , expr_output , sizeof ( expr_output ) , NULL ) ;
}
ast_expr ( item - > u2 . for_test , expr_output , sizeof ( expr_output ) , NULL ) ;
strp = strchr ( item - > u3 . for_inc , ' = ' ) ;
if ( strp ) {
ast_expr ( strp + 1 , expr_output , sizeof ( expr_output ) , NULL ) ;
}
if ( strpbrk ( item - > u2 . for_test , " ~!-+<>=*/&^ " ) & & ! strstr ( item - > u2 . for_test , " ${ " ) ) {
ast_log ( LOG_WARNING , " Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting... \n " ,
item - > filename , item - > startline , item - > endline , item - > u2 . for_test ) ;
warns + + ;
}
if ( strpbrk ( item - > u3 . for_inc , " ~!-+<>=*/&^ " ) & & ! strstr ( item - > u3 . for_inc , " ${ " ) ) {
ast_log ( LOG_WARNING , " Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting... \n " ,
item - > filename , item - > startline , item - > endline , item - > u3 . for_inc ) ;
warns + + ;
}
check_expr2_input ( item , item - > u2 . for_test ) ;
check_expr2_input ( item , item - > u3 . for_inc ) ;
ast_expr_clear_extra_error_info ( ) ;
check_pval ( item - > u4 . for_statements , apps , in_globals ) ;
break ;
case PV_WHILE :
/* fields: item->u1.str == the while conditional, as supplied by user
item - > u2 . statements = = a pval list of statements in the while ( )
*/
snprintf ( errmsg , sizeof ( errmsg ) , " file %s, line %d, columns %d-%d, while expr '%s': " , item - > filename , item - > startline , item - > startcol , item - > endcol , item - > u1 . str ) ;
ast_expr_register_extra_error_info ( errmsg ) ;
ast_expr ( item - > u1 . str , expr_output , sizeof ( expr_output ) , NULL ) ;
ast_expr_clear_extra_error_info ( ) ;
if ( strpbrk ( item - > u1 . str , " ~!-+<>=*/&^ " ) & & ! strstr ( item - > u1 . str , " ${ " ) ) {
ast_log ( LOG_WARNING , " Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting... \n " ,
item - > filename , item - > startline , item - > endline , item - > u1 . str ) ;
warns + + ;
}
check_expr2_input ( item , item - > u1 . str ) ;
check_pval ( item - > u2 . statements , apps , in_globals ) ;
break ;
case PV_BREAK :
/* fields: none
*/
check_break ( item ) ;
break ;
case PV_RETURN :
/* fields: none
*/
break ;
case PV_CONTINUE :
/* fields: none
*/
check_continue ( item ) ;
break ;
case PV_RANDOM :
/* fields: item->u1.str == the random number expression, as supplied by user
item - > u2 . statements = = a pval list of statements in the if ( )
item - > u3 . else_statements = = a pval list of statements in the else
( could be zero )
*/
snprintf ( errmsg , sizeof ( errmsg ) , " file %s, line %d, columns %d-%d, random expr '%s': " , item - > filename , item - > startline , item - > startcol , item - > endcol , item - > u1 . str ) ;
ast_expr_register_extra_error_info ( errmsg ) ;
ast_expr ( item - > u1 . str , expr_output , sizeof ( expr_output ) , NULL ) ;
ast_expr_clear_extra_error_info ( ) ;
if ( strpbrk ( item - > u1 . str , " ~!-+<>=*/&^ " ) & & ! strstr ( item - > u1 . str , " ${ " ) ) {
ast_log ( LOG_WARNING , " Warning: file %s, line %d-%d: random expression '%s' has operators, but no variables. Interesting... \n " ,
item - > filename , item - > startline , item - > endline , item - > u1 . str ) ;
warns + + ;
}
check_expr2_input ( item , item - > u1 . str ) ;
check_pval ( item - > u2 . statements , apps , in_globals ) ;
if ( item - > u3 . else_statements ) {
check_pval ( item - > u3 . else_statements , apps , in_globals ) ;
}
break ;
case PV_IFTIME :
/* fields: item->u1.list == the if time values, 4 of them, each in PV_WORD, linked list
item - > u2 . statements = = a pval list of statements in the if ( )
item - > u3 . else_statements = = a pval list of statements in the else
( could be zero )
*/
if ( item - > u2 . arglist ) {
check_timerange ( item - > u1 . list ) ;
check_dow ( item - > u1 . list - > next ) ;
check_day ( item - > u1 . list - > next - > next ) ;
check_month ( item - > u1 . list - > next - > next - > next ) ;
}
check_pval ( item - > u2 . statements , apps , in_globals ) ;
if ( item - > u3 . else_statements ) {
check_pval ( item - > u3 . else_statements , apps , in_globals ) ;
}
break ;
case PV_IF :
/* fields: item->u1.str == the if conditional, as supplied by user
item - > u2 . statements = = a pval list of statements in the if ( )
item - > u3 . else_statements = = a pval list of statements in the else
( could be zero )
*/
snprintf ( errmsg , sizeof ( errmsg ) , " file %s, line %d, columns %d-%d, if expr '%s': " , item - > filename , item - > startline , item - > startcol , item - > endcol , item - > u1 . str ) ;
ast_expr_register_extra_error_info ( errmsg ) ;
ast_expr ( item - > u1 . str , expr_output , sizeof ( expr_output ) , NULL ) ;
ast_expr_clear_extra_error_info ( ) ;
if ( strpbrk ( item - > u1 . str , " ~!-+<>=*/&^ " ) & & ! strstr ( item - > u1 . str , " ${ " ) ) {
ast_log ( LOG_WARNING , " Warning: file %s, line %d-%d: expression '%s' has operators, but no variables. Interesting... \n " ,
item - > filename , item - > startline , item - > endline , item - > u1 . str ) ;
warns + + ;
}
check_expr2_input ( item , item - > u1 . str ) ;
check_pval ( item - > u2 . statements , apps , in_globals ) ;
if ( item - > u3 . else_statements ) {
check_pval ( item - > u3 . else_statements , apps , in_globals ) ;
}
break ;
case PV_SWITCH :
/* fields: item->u1.str == the switch expression
item - > u2 . statements = = a pval list of statements in the switch ,
( will be case statements , most likely ! )
*/
/* we can check the switch expression, see if it matches any of the app variables...
if it does , then , are all the possible cases accounted for ? */
check_switch_expr ( item , apps ) ;
check_pval ( item - > u2 . statements , apps , in_globals ) ;
break ;
case PV_EXTENSION :
/* fields: item->u1.str == the extension name, label, whatever it's called
item - > u2 . statements = = a pval list of statements in the extension
item - > u3 . hints = = a char * hint argument
item - > u4 . regexten = = an int boolean . non - zero says that regexten was specified
*/
current_extension = item ;
check_pval ( item - > u2 . statements , apps , in_globals ) ;
break ;
case PV_IGNOREPAT :
/* fields: item->u1.str == the ignorepat data
*/
break ;
case PV_GLOBALS :
/* fields: item->u1.statements == pval list of statements, usually vardecs
*/
in_abstract_context = 0 ;
check_pval ( item - > u1 . statements , apps , 1 ) ;
break ;
default :
break ;
}
}
void check_pval ( pval * item , struct argapp * apps , int in_globals )
{
pval * i ;
/* checks to do:
1. Do goto ' s point to actual labels ?
2. Do macro calls reference a macro ?
3. Does the number of macro args match the definition ?
4. Is a macro call missing its & at the front ?
5. Application calls - - we could check syntax for existing applications ,
but I need some some sort of universal description bnf for a general
sort of method for checking arguments , in number , maybe even type , at least .
Don ' t want to hand code checks for hundreds of applications .
*/
for ( i = item ; i ; i = i - > next ) {
check_pval_item ( i , apps , in_globals ) ;
}
}
void ael2_semantic_check ( pval * item , int * arg_errs , int * arg_warns , int * arg_notes )
{
# ifdef AAL_ARGCHECK
int argapp_errs = 0 ;
char * rfilename ;
# endif
struct argapp * apps = 0 ;
2007-10-01 23:03:50 +00:00
if ( ! item )
return ; /* don't check an empty tree */
2007-08-15 19:21:27 +00:00
# ifdef AAL_ARGCHECK
rfilename = alloca ( 10 + strlen ( ast_config_AST_VAR_DIR ) ) ;
sprintf ( rfilename , " %s/applist " , ast_config_AST_VAR_DIR ) ;
apps = argdesc_parse ( rfilename , & argapp_errs ) ; /* giveth */
# endif
current_db = item ;
errs = warns = notes = 0 ;
check_context_names ( ) ;
check_pval ( item , apps , 0 ) ;
# ifdef AAL_ARGCHECK
argdesc_destroy ( apps ) ; /* taketh away */
# endif
current_db = 0 ;
* arg_errs = errs ;
* arg_warns = warns ;
* arg_notes = notes ;
}
/* =============================================================================================== */
/* "CODE" GENERATOR -- Convert the AEL representation to asterisk extension language */
/* =============================================================================================== */
static int control_statement_count = 0 ;
struct ael_priority * new_prio ( void )
{
struct ael_priority * x = ( struct ael_priority * ) calloc ( sizeof ( struct ael_priority ) , 1 ) ;
return x ;
}
struct ael_extension * new_exten ( void )
{
struct ael_extension * x = ( struct ael_extension * ) calloc ( sizeof ( struct ael_extension ) , 1 ) ;
return x ;
}
2008-03-27 04:47:12 +00:00
void linkprio ( struct ael_extension * exten , struct ael_priority * prio , struct ael_extension * mother_exten )
2007-08-15 19:21:27 +00:00
{
2008-03-27 04:47:12 +00:00
char * p1 , * p2 ;
2007-08-15 19:21:27 +00:00
if ( ! exten - > plist ) {
exten - > plist = prio ;
exten - > plist_last = prio ;
} else {
exten - > plist_last - > next = prio ;
exten - > plist_last = prio ;
}
if ( ! prio - > exten )
prio - > exten = exten ; /* don't override the switch value */
2008-03-27 04:47:12 +00:00
/* The following code will cause all priorities within an extension
to have $ { EXTEN } or $ { EXTEN : replaced with ~ ~ EXTEN ~ ~ , which is
set just before the first switch in an exten . The switches
will muck up the original $ { EXTEN } value , so we save it away
and the user accesses this copy instead . */
if ( prio - > appargs & & ( ( mother_exten & & mother_exten - > has_switch ) | | exten - > has_switch ) ) {
while ( ( p1 = strstr ( prio - > appargs , " ${EXTEN} " ) ) ) {
p2 = malloc ( strlen ( prio - > appargs ) + 5 ) ;
* p1 = 0 ;
strcpy ( p2 , prio - > appargs ) ;
strcat ( p2 , " ${~~EXTEN~~} " ) ;
if ( * ( p1 + 8 ) )
strcat ( p2 , p1 + 8 ) ;
free ( prio - > appargs ) ;
prio - > appargs = p2 ;
}
while ( ( p1 = strstr ( prio - > appargs , " ${EXTEN: " ) ) ) {
p2 = malloc ( strlen ( prio - > appargs ) + 5 ) ;
* p1 = 0 ;
strcpy ( p2 , prio - > appargs ) ;
strcat ( p2 , " ${~~EXTEN~~: " ) ;
if ( * ( p1 + 8 ) )
strcat ( p2 , p1 + 8 ) ;
free ( prio - > appargs ) ;
prio - > appargs = p2 ;
}
}
2007-08-15 19:21:27 +00:00
}
void destroy_extensions ( struct ael_extension * exten )
{
struct ael_extension * ne , * nen ;
for ( ne = exten ; ne ; ne = nen ) {
struct ael_priority * pe , * pen ;
if ( ne - > name )
free ( ne - > name ) ;
/* cidmatch fields are allocated with name, and freed when
the name field is freed . Don ' t do a free for this field ,
unless you LIKE to see a crash ! */
if ( ne - > hints )
free ( ne - > hints ) ;
for ( pe = ne - > plist ; pe ; pe = pen ) {
pen = pe - > next ;
if ( pe - > app )
free ( pe - > app ) ;
pe - > app = 0 ;
if ( pe - > appargs )
free ( pe - > appargs ) ;
pe - > appargs = 0 ;
pe - > origin = 0 ;
pe - > goto_true = 0 ;
pe - > goto_false = 0 ;
free ( pe ) ;
}
nen = ne - > next_exten ;
ne - > next_exten = 0 ;
ne - > plist = 0 ;
ne - > plist_last = 0 ;
ne - > next_exten = 0 ;
ne - > loop_break = 0 ;
ne - > loop_continue = 0 ;
free ( ne ) ;
}
}
static int label_inside_case ( pval * label )
{
pval * p = label ;
while ( p & & p - > type ! = PV_MACRO & & p - > type ! = PV_CONTEXT ) /* early cutout, sort of */ {
if ( p - > type = = PV_CASE | | p - > type = = PV_DEFAULT | | p - > type = = PV_PATTERN ) {
return 1 ;
}
p = p - > dad ;
}
return 0 ;
}
static void linkexten ( struct ael_extension * exten , struct ael_extension * add )
{
add - > next_exten = exten - > next_exten ; /* this will reverse the order. Big deal. */
exten - > next_exten = add ;
}
static void remove_spaces_before_equals ( char * str )
{
char * p ;
while ( str & & * str & & * str ! = ' = ' )
{
if ( * str = = ' ' | | * str = = ' \n ' | | * str = = ' \r ' | | * str = = ' \t ' )
{
p = str ;
while ( * p )
{
* p = * ( p + 1 ) ;
p + + ;
}
}
else
str + + ;
}
}
/* =============================================================================================== */
/* "CODE" GENERATOR -- Convert the AEL representation to asterisk extension language */
/* =============================================================================================== */
static void gen_match_to_pattern ( char * pattern , char * result )
{
/* the result will be a string that will be matched by pattern */
char * p = pattern , * t = result ;
while ( * p ) {
if ( * p = = ' x ' | | * p = = ' n ' | | * p = = ' z ' | | * p = = ' X ' | | * p = = ' N ' | | * p = = ' Z ' )
* t + + = ' 9 ' ;
else if ( * p = = ' [ ' ) {
char * z = p + 1 ;
while ( * z ! = ' ] ' )
z + + ;
if ( * ( z + 1 ) = = ' ] ' )
z + + ;
* t + + = * ( p + 1 ) ; /* use the first char in the set */
p = z ;
} else {
* t + + = * p ;
}
p + + ;
}
* t + + = 0 ; /* cap it off */
}
2008-08-08 00:48:35 +00:00
/* ==== a set of routines to search for a switch statement contained in the pval description */
int find_switch_item ( pval * item ) ;
int contains_switch ( pval * item ) ;
int find_switch_item ( pval * item )
{
switch ( item - > type ) {
case PV_LOCALVARDEC :
/* fields: item->u1.str == string associated with this (word). */
break ;
case PV_WORD :
/* fields: item->u1.str == string associated with this (word). */
break ;
case PV_MACRO :
/* fields: item->u1.str == name of macro
item - > u2 . arglist = = pval list of PV_WORD arguments of macro , as given by user
item - > u2 . arglist - > u1 . str = = argument
item - > u2 . arglist - > next = = next arg
item - > u3 . macro_statements = = pval list of statements in macro body .
*/
/* had better not see this */
if ( contains_switch ( item - > u3 . macro_statements ) )
return 1 ;
break ;
case PV_CONTEXT :
/* fields: item->u1.str == name of context
item - > u2 . statements = = pval list of statements in context body
item - > u3 . abstract = = int 1 if an abstract keyword were present
*/
/* had better not see this */
if ( contains_switch ( item - > u2 . statements ) )
return 1 ;
break ;
case PV_MACRO_CALL :
/* fields: item->u1.str == name of macro to call
item - > u2 . arglist = = pval list of PV_WORD arguments of macro call , as given by user
item - > u2 . arglist - > u1 . str = = argument
item - > u2 . arglist - > next = = next arg
*/
break ;
case PV_APPLICATION_CALL :
/* fields: item->u1.str == name of application to call
item - > u2 . arglist = = pval list of PV_WORD arguments of macro call , as given by user
item - > u2 . arglist - > u1 . str = = argument
item - > u2 . arglist - > next = = next arg
*/
break ;
case PV_CASE :
/* fields: item->u1.str == value of case
item - > u2 . statements = = pval list of statements under the case
*/
/* had better not see this */
if ( contains_switch ( item - > u2 . statements ) )
return 1 ;
break ;
case PV_PATTERN :
/* fields: item->u1.str == value of case
item - > u2 . statements = = pval list of statements under the case
*/
/* had better not see this */
if ( contains_switch ( item - > u2 . statements ) )
return 1 ;
break ;
case PV_DEFAULT :
/* fields:
item - > u2 . statements = = pval list of statements under the case
*/
/* had better not see this */
if ( contains_switch ( item - > u2 . statements ) )
return 1 ;
break ;
case PV_CATCH :
/* fields: item->u1.str == name of extension to catch
item - > u2 . statements = = pval list of statements in context body
*/
/* had better not see this */
if ( contains_switch ( item - > u2 . statements ) )
return 1 ;
break ;
case PV_SWITCHES :
/* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
*/
break ;
case PV_ESWITCHES :
/* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
*/
break ;
case PV_INCLUDES :
/* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
item - > u2 . arglist = = pval list of 4 PV_WORD elements for time values
*/
break ;
case PV_STATEMENTBLOCK :
/* fields: item->u1.list == pval list of statements in block, one per entry in the list
*/
if ( contains_switch ( item - > u1 . list ) )
return 1 ;
break ;
case PV_VARDEC :
/* fields: item->u1.str == variable name
item - > u2 . val = = variable value to assign
*/
break ;
case PV_GOTO :
/* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user.
item - > u1 . list - > u1 . str = = where the data on a PV_WORD will always be .
*/
break ;
case PV_LABEL :
/* fields: item->u1.str == label name
*/
break ;
case PV_FOR :
/* fields: item->u1.for_init == a string containing the initalizer
item - > u2 . for_test = = a string containing the loop test
item - > u3 . for_inc = = a string containing the loop increment
item - > u4 . for_statements = = a pval list of statements in the for ( )
*/
if ( contains_switch ( item - > u4 . for_statements ) )
return 1 ;
break ;
case PV_WHILE :
/* fields: item->u1.str == the while conditional, as supplied by user
item - > u2 . statements = = a pval list of statements in the while ( )
*/
if ( contains_switch ( item - > u2 . statements ) )
return 1 ;
break ;
case PV_BREAK :
/* fields: none
*/
break ;
case PV_RETURN :
/* fields: none
*/
break ;
case PV_CONTINUE :
/* fields: none
*/
break ;
case PV_IFTIME :
/* fields: item->u1.list == there are 4 linked PV_WORDs here.
item - > u2 . statements = = a pval list of statements in the if ( )
item - > u3 . else_statements = = a pval list of statements in the else
( could be zero )
*/
if ( contains_switch ( item - > u2 . statements ) )
return 1 ;
if ( item - > u3 . else_statements ) {
if ( contains_switch ( item - > u3 . else_statements ) )
return 1 ;
}
break ;
case PV_RANDOM :
/* fields: item->u1.str == the random number expression, as supplied by user
item - > u2 . statements = = a pval list of statements in the if ( )
item - > u3 . else_statements = = a pval list of statements in the else
( could be zero )
*/
if ( contains_switch ( item - > u2 . statements ) )
return 1 ;
if ( item - > u3 . else_statements ) {
if ( contains_switch ( item - > u3 . else_statements ) )
return 1 ;
}
break ;
case PV_IF :
/* fields: item->u1.str == the if conditional, as supplied by user
item - > u2 . statements = = a pval list of statements in the if ( )
item - > u3 . else_statements = = a pval list of statements in the else
( could be zero )
*/
if ( contains_switch ( item - > u2 . statements ) )
return 1 ;
if ( item - > u3 . else_statements ) {
if ( contains_switch ( item - > u3 . else_statements ) )
return 1 ;
}
break ;
case PV_SWITCH :
/* fields: item->u1.str == the switch expression
item - > u2 . statements = = a pval list of statements in the switch ,
( will be case statements , most likely ! )
*/
return 1 ; /* JACKPOT */
break ;
case PV_EXTENSION :
/* fields: item->u1.str == the extension name, label, whatever it's called
item - > u2 . statements = = a pval list of statements in the extension
item - > u3 . hints = = a char * hint argument
item - > u4 . regexten = = an int boolean . non - zero says that regexten was specified
*/
if ( contains_switch ( item - > u2 . statements ) )
return 1 ;
break ;
case PV_IGNOREPAT :
/* fields: item->u1.str == the ignorepat data
*/
break ;
case PV_GLOBALS :
/* fields: item->u1.statements == pval list of statements, usually vardecs
*/
break ;
}
return 0 ;
}
int contains_switch ( pval * item )
{
pval * i ;
for ( i = item ; i ; i = i - > next ) {
if ( find_switch_item ( i ) )
return 1 ;
}
return 0 ;
}
2007-08-15 19:21:27 +00:00
static void gen_prios ( struct ael_extension * exten , char * label , pval * statement , struct ael_extension * mother_exten , struct ast_context * this_context )
{
pval * p , * p2 , * p3 ;
struct ael_priority * pr ;
struct ael_priority * for_init , * for_test , * for_inc , * for_loop , * for_end ;
struct ael_priority * while_test , * while_loop , * while_end ;
2008-03-27 04:47:12 +00:00
struct ael_priority * switch_set , * switch_test , * switch_end , * fall_thru , * switch_empty ;
2007-08-15 19:21:27 +00:00
struct ael_priority * if_test , * if_end , * if_skip , * if_false ;
# ifdef OLD_RAND_ACTION
struct ael_priority * rand_test , * rand_end , * rand_skip ;
# endif
char buf1 [ 2000 ] ;
char buf2 [ 2000 ] ;
char * strp , * strp2 ;
char new_label [ 2000 ] ;
int default_exists ;
int local_control_statement_count ;
int first ;
struct ael_priority * loop_break_save ;
struct ael_priority * loop_continue_save ;
Merged revisions 84511 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r84511 | murf | 2007-10-03 08:23:00 -0600 (Wed, 03 Oct 2007) | 1 line
closes issue #10834 ; where a null input to a switch statement results in a hangup; since switch is implemented with extensions, and the default case is implemented with a '.', and the '.' matches 1 or more remaining characters, the case where 0 characters exist isn't matched, and the extension isn't matched, and the goto fails, and a hangup occurs. Now, when a default case is generated, it also generates a single fixed extension that will match a null input. That extension just does a goto to the default extension for that switch. I played with an alternate solution, where I just tack an extra char onto all the patterns and the goto, but not the default case's pattern. Then even a null input will still have at least one char in it. But it made me nervous, having that extra char in , even if that's a pretty secret and low-level issue.
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@84512 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2007-10-03 14:39:05 +00:00
struct ael_extension * switch_case , * switch_null ;
2007-08-15 19:21:27 +00:00
2008-08-08 00:48:35 +00:00
if ( ( mother_exten & & ! mother_exten - > checked_switch ) | | ( exten & & ! exten - > checked_switch ) ) {
if ( contains_switch ( statement ) ) { /* only run contains_switch if you haven't checked before */
if ( mother_exten ) {
if ( ! mother_exten - > has_switch ) {
switch_set = new_prio ( ) ;
switch_set - > type = AEL_APPCALL ;
In these changes, I have added some explanation
of changes to the Set and MSet apps, so people aren't
so shocked and surprised when they upgrade from
1.4 to 1.6.
Also, for the sake of those upgrading from 1.4 to
1.6 with AEL, I provide automatic support for the
"old" way of using Set(), that still does the
exact same old thing with quotes and backslashes
and so on as 1.4 did, by having AEL compile in the
use of MSet() instead of Set(), everywhere it inserts
this code.
But, if the app_set var is set to 1.6 or higher,
it uses the "new", non-evaluative Set().
This only usually happens if the user manually
inserts this into the asterisk.conf file, or runs
the "make samples" command.
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@140824 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2008-09-03 14:01:27 +00:00
if ( ! ast_compat_app_set ) {
switch_set - > app = strdup ( " MSet " ) ;
} else {
switch_set - > app = strdup ( " Set " ) ;
}
2008-08-08 00:48:35 +00:00
switch_set - > appargs = strdup ( " ~~EXTEN~~=${EXTEN} " ) ;
linkprio ( exten , switch_set , mother_exten ) ;
mother_exten - > has_switch = 1 ;
mother_exten - > checked_switch = 1 ;
if ( exten ) {
exten - > has_switch = 1 ;
exten - > checked_switch = 1 ;
}
}
} else if ( exten ) {
if ( ! exten - > has_switch ) {
switch_set = new_prio ( ) ;
switch_set - > type = AEL_APPCALL ;
In these changes, I have added some explanation
of changes to the Set and MSet apps, so people aren't
so shocked and surprised when they upgrade from
1.4 to 1.6.
Also, for the sake of those upgrading from 1.4 to
1.6 with AEL, I provide automatic support for the
"old" way of using Set(), that still does the
exact same old thing with quotes and backslashes
and so on as 1.4 did, by having AEL compile in the
use of MSet() instead of Set(), everywhere it inserts
this code.
But, if the app_set var is set to 1.6 or higher,
it uses the "new", non-evaluative Set().
This only usually happens if the user manually
inserts this into the asterisk.conf file, or runs
the "make samples" command.
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@140824 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2008-09-03 14:01:27 +00:00
if ( ! ast_compat_app_set ) {
switch_set - > app = strdup ( " MSet " ) ;
} else {
switch_set - > app = strdup ( " Set " ) ;
}
2008-08-08 00:48:35 +00:00
switch_set - > appargs = strdup ( " ~~EXTEN~~=${EXTEN} " ) ;
linkprio ( exten , switch_set , mother_exten ) ;
exten - > has_switch = 1 ;
exten - > checked_switch = 1 ;
if ( mother_exten ) {
mother_exten - > has_switch = 1 ;
mother_exten - > checked_switch = 1 ;
}
}
}
} else {
if ( mother_exten ) {
mother_exten - > checked_switch = 1 ;
}
if ( exten ) {
exten - > checked_switch = 1 ;
}
}
}
2007-08-15 19:21:27 +00:00
for ( p = statement ; p ; p = p - > next ) {
switch ( p - > type ) {
case PV_VARDEC :
pr = new_prio ( ) ;
pr - > type = AEL_APPCALL ;
snprintf ( buf1 , sizeof ( buf1 ) , " %s=$[%s] " , p - > u1 . str , p - > u2 . val ) ;
In these changes, I have added some explanation
of changes to the Set and MSet apps, so people aren't
so shocked and surprised when they upgrade from
1.4 to 1.6.
Also, for the sake of those upgrading from 1.4 to
1.6 with AEL, I provide automatic support for the
"old" way of using Set(), that still does the
exact same old thing with quotes and backslashes
and so on as 1.4 did, by having AEL compile in the
use of MSet() instead of Set(), everywhere it inserts
this code.
But, if the app_set var is set to 1.6 or higher,
it uses the "new", non-evaluative Set().
This only usually happens if the user manually
inserts this into the asterisk.conf file, or runs
the "make samples" command.
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@140824 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2008-09-03 14:01:27 +00:00
if ( ! ast_compat_app_set ) {
pr - > app = strdup ( " MSet " ) ;
} else {
pr - > app = strdup ( " Set " ) ;
}
2007-08-15 19:21:27 +00:00
remove_spaces_before_equals ( buf1 ) ;
pr - > appargs = strdup ( buf1 ) ;
pr - > origin = p ;
2008-03-27 04:47:12 +00:00
linkprio ( exten , pr , mother_exten ) ;
2007-08-15 19:21:27 +00:00
break ;
case PV_LOCALVARDEC :
pr = new_prio ( ) ;
pr - > type = AEL_APPCALL ;
snprintf ( buf1 , sizeof ( buf1 ) , " LOCAL(%s)=$[%s] " , p - > u1 . str , p - > u2 . val ) ;
In these changes, I have added some explanation
of changes to the Set and MSet apps, so people aren't
so shocked and surprised when they upgrade from
1.4 to 1.6.
Also, for the sake of those upgrading from 1.4 to
1.6 with AEL, I provide automatic support for the
"old" way of using Set(), that still does the
exact same old thing with quotes and backslashes
and so on as 1.4 did, by having AEL compile in the
use of MSet() instead of Set(), everywhere it inserts
this code.
But, if the app_set var is set to 1.6 or higher,
it uses the "new", non-evaluative Set().
This only usually happens if the user manually
inserts this into the asterisk.conf file, or runs
the "make samples" command.
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@140824 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2008-09-03 14:01:27 +00:00
if ( ! ast_compat_app_set ) {
pr - > app = strdup ( " MSet " ) ;
} else {
pr - > app = strdup ( " Set " ) ;
}
2007-08-15 19:21:27 +00:00
remove_spaces_before_equals ( buf1 ) ;
pr - > appargs = strdup ( buf1 ) ;
pr - > origin = p ;
2008-03-27 04:47:12 +00:00
linkprio ( exten , pr , mother_exten ) ;
2007-08-15 19:21:27 +00:00
break ;
2008-08-08 00:48:35 +00:00
2007-08-15 19:21:27 +00:00
case PV_GOTO :
pr = new_prio ( ) ;
pr - > type = AEL_APPCALL ;
p - > u2 . goto_target = get_goto_target ( p ) ;
if ( p - > u2 . goto_target ) {
Merged revisions 162013 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r162013 | murf | 2008-12-09 09:31:55 -0700 (Tue, 09 Dec 2008) | 45 lines
(closes issue #14019)
Reported by: ckjohnsonme
Patches:
14019.diff uploaded by murf (license 17)
Tested by: ckjohnsonme, murf
This crash was the result of a few small errors that
would combine in 64-bit land to result in a crash.
32-bit land might have seen these combine to mysteriously
drop the args to an application call, in certain
circumstances.
Also, in trying to find this bug, I spotted
a situation in the flex input, where, in passing
back a 'word' to the parser, it would allocate
a buffer larger than necessary. I changed the
usage in such situations, so that strdup was
not used, but rather, an ast_malloc, followed
by ast_copy_string.
I removed a field from the pval struct, in
u2, that was never getting used, and set in
one spot in the code. I believe it was an
artifact of a previous fix to make switch
cases work invisibly with extens.
And, for goto's I removed a '!' from
before a strcmp, that has been there
since the initial merging of AEL2, that
might prevent the proper target of a
goto from being found. This was pretty
harmless on its own, as it would just
louse up a consistency check for users.
Many thanks to ckjohnsonme for providing
a simplified and complete set of information
about the bug, that helped considerably in
finding and fixing the problem.
Now, to get aelparse up and running again
in trunk, and out of its "horribly broken" state,
so I can run the regression suite!
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@162079 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2008-12-09 17:18:03 +00:00
p - > u3 . goto_target_in_case = label_inside_case ( p - > u2 . goto_target ) ;
2007-08-15 19:21:27 +00:00
}
if ( ! p - > u1 . list - > next ) /* just one */ {
pr - > app = strdup ( " Goto " ) ;
if ( ! mother_exten )
pr - > appargs = strdup ( p - > u1 . list - > u1 . str ) ;
else { /* for the case of simple within-extension gotos in case/pattern/default statement blocks: */
snprintf ( buf1 , sizeof ( buf1 ) , " %s,%s " , mother_exten - > name , p - > u1 . list - > u1 . str ) ;
pr - > appargs = strdup ( buf1 ) ;
}
} else if ( p - > u1 . list - > next & & ! p - > u1 . list - > next - > next ) /* two */ {
snprintf ( buf1 , sizeof ( buf1 ) , " %s,%s " , p - > u1 . list - > u1 . str , p - > u1 . list - > next - > u1 . str ) ;
pr - > app = strdup ( " Goto " ) ;
pr - > appargs = strdup ( buf1 ) ;
} else if ( p - > u1 . list - > next & & p - > u1 . list - > next - > next ) {
snprintf ( buf1 , sizeof ( buf1 ) , " %s,%s,%s " , p - > u1 . list - > u1 . str ,
p - > u1 . list - > next - > u1 . str ,
p - > u1 . list - > next - > next - > u1 . str ) ;
pr - > app = strdup ( " Goto " ) ;
pr - > appargs = strdup ( buf1 ) ;
}
pr - > origin = p ;
2008-03-27 04:47:12 +00:00
linkprio ( exten , pr , mother_exten ) ;
2007-08-15 19:21:27 +00:00
break ;
case PV_LABEL :
pr = new_prio ( ) ;
pr - > type = AEL_LABEL ;
pr - > origin = p ;
p - > u3 . compiled_label = exten ;
2008-03-27 04:47:12 +00:00
linkprio ( exten , pr , mother_exten ) ;
2007-08-15 19:21:27 +00:00
break ;
case PV_FOR :
control_statement_count + + ;
loop_break_save = exten - > loop_break ; /* save them, then restore before leaving */
loop_continue_save = exten - > loop_continue ;
snprintf ( new_label , sizeof ( new_label ) , " for-%s-%d " , label , control_statement_count ) ;
for_init = new_prio ( ) ;
for_inc = new_prio ( ) ;
for_test = new_prio ( ) ;
for_loop = new_prio ( ) ;
for_end = new_prio ( ) ;
for_init - > type = AEL_APPCALL ;
for_inc - > type = AEL_APPCALL ;
for_test - > type = AEL_FOR_CONTROL ;
for_test - > goto_false = for_end ;
for_loop - > type = AEL_CONTROL1 ; /* simple goto */
for_end - > type = AEL_APPCALL ;
In these changes, I have added some explanation
of changes to the Set and MSet apps, so people aren't
so shocked and surprised when they upgrade from
1.4 to 1.6.
Also, for the sake of those upgrading from 1.4 to
1.6 with AEL, I provide automatic support for the
"old" way of using Set(), that still does the
exact same old thing with quotes and backslashes
and so on as 1.4 did, by having AEL compile in the
use of MSet() instead of Set(), everywhere it inserts
this code.
But, if the app_set var is set to 1.6 or higher,
it uses the "new", non-evaluative Set().
This only usually happens if the user manually
inserts this into the asterisk.conf file, or runs
the "make samples" command.
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@140824 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2008-09-03 14:01:27 +00:00
if ( ! ast_compat_app_set ) {
for_init - > app = strdup ( " MSet " ) ;
} else {
for_init - > app = strdup ( " Set " ) ;
}
2007-08-15 19:21:27 +00:00
strcpy ( buf2 , p - > u1 . for_init ) ;
remove_spaces_before_equals ( buf2 ) ;
strp = strchr ( buf2 , ' = ' ) ;
if ( strp ) {
2007-09-22 19:54:10 +00:00
strp2 = strchr ( p - > u1 . for_init , ' = ' ) ;
2007-08-15 19:21:27 +00:00
* ( strp + 1 ) = 0 ;
strcat ( buf2 , " $[ " ) ;
strncat ( buf2 , strp2 + 1 , sizeof ( buf2 ) - strlen ( strp2 + 1 ) - 2 ) ;
strcat ( buf2 , " ] " ) ;
for_init - > appargs = strdup ( buf2 ) ;
2007-09-22 19:54:10 +00:00
} else {
strp2 = p - > u1 . for_init ;
while ( * strp2 & & isspace ( * strp2 ) )
strp2 + + ;
if ( * strp2 = = ' & ' ) { /* itsa macro call */
char * strp3 = strp2 + 1 ;
while ( * strp3 & & isspace ( * strp3 ) )
strp3 + + ;
strcpy ( buf2 , strp3 ) ;
strp3 = strchr ( buf2 , ' ( ' ) ;
if ( strp3 ) {
* strp3 = ' | ' ;
}
while ( ( strp3 = strchr ( buf2 , ' , ' ) ) ) {
* strp3 = ' | ' ;
}
strp3 = strrchr ( buf2 , ' ) ' ) ;
if ( strp3 )
* strp3 = 0 ; /* remove the closing paren */
for_init - > appargs = strdup ( buf2 ) ;
2007-09-25 13:41:02 +00:00
free ( for_init - > app ) ;
2007-09-22 19:54:10 +00:00
for_init - > app = strdup ( " Macro " ) ;
} else { /* must be a regular app call */
char * strp3 ;
strcpy ( buf2 , strp2 ) ;
strp3 = strchr ( buf2 , ' ( ' ) ;
if ( strp3 ) {
* strp3 = 0 ;
2007-09-25 13:41:02 +00:00
free ( for_init - > app ) ;
2007-09-22 19:54:10 +00:00
for_init - > app = strdup ( buf2 ) ;
for_init - > appargs = strdup ( strp3 + 1 ) ;
strp3 = strrchr ( for_init - > appargs , ' ) ' ) ;
if ( strp3 )
* strp3 = 0 ; /* remove the closing paren */
}
}
}
2007-08-15 19:21:27 +00:00
strcpy ( buf2 , p - > u3 . for_inc ) ;
remove_spaces_before_equals ( buf2 ) ;
strp = strchr ( buf2 , ' = ' ) ;
2007-09-22 17:39:37 +00:00
if ( strp ) { /* there's an = in this part; that means an assignment. set it up */
strp2 = strchr ( p - > u3 . for_inc , ' = ' ) ;
2007-08-15 19:21:27 +00:00
* ( strp + 1 ) = 0 ;
strcat ( buf2 , " $[ " ) ;
strncat ( buf2 , strp2 + 1 , sizeof ( buf2 ) - strlen ( strp2 + 1 ) - 2 ) ;
strcat ( buf2 , " ] " ) ;
for_inc - > appargs = strdup ( buf2 ) ;
In these changes, I have added some explanation
of changes to the Set and MSet apps, so people aren't
so shocked and surprised when they upgrade from
1.4 to 1.6.
Also, for the sake of those upgrading from 1.4 to
1.6 with AEL, I provide automatic support for the
"old" way of using Set(), that still does the
exact same old thing with quotes and backslashes
and so on as 1.4 did, by having AEL compile in the
use of MSet() instead of Set(), everywhere it inserts
this code.
But, if the app_set var is set to 1.6 or higher,
it uses the "new", non-evaluative Set().
This only usually happens if the user manually
inserts this into the asterisk.conf file, or runs
the "make samples" command.
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@140824 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2008-09-03 14:01:27 +00:00
if ( ! ast_compat_app_set ) {
for_inc - > app = strdup ( " MSet " ) ;
} else {
for_inc - > app = strdup ( " Set " ) ;
}
2007-09-22 17:39:37 +00:00
} else {
strp2 = p - > u3 . for_inc ;
while ( * strp2 & & isspace ( * strp2 ) )
strp2 + + ;
if ( * strp2 = = ' & ' ) { /* itsa macro call */
char * strp3 = strp2 + 1 ;
while ( * strp3 & & isspace ( * strp3 ) )
strp3 + + ;
strcpy ( buf2 , strp3 ) ;
strp3 = strchr ( buf2 , ' ( ' ) ;
if ( strp3 ) {
* strp3 = ' , ' ;
}
strp3 = strrchr ( buf2 , ' ) ' ) ;
if ( strp3 )
* strp3 = 0 ; /* remove the closing paren */
for_inc - > appargs = strdup ( buf2 ) ;
for_inc - > app = strdup ( " Macro " ) ;
} else { /* must be a regular app call */
char * strp3 ;
strcpy ( buf2 , strp2 ) ;
strp3 = strchr ( buf2 , ' ( ' ) ;
if ( strp3 ) {
* strp3 = 0 ;
for_inc - > app = strdup ( buf2 ) ;
for_inc - > appargs = strdup ( strp3 + 1 ) ;
strp3 = strrchr ( for_inc - > appargs , ' ) ' ) ;
if ( strp3 )
* strp3 = 0 ; /* remove the closing paren */
}
}
}
2007-08-15 19:21:27 +00:00
snprintf ( buf1 , sizeof ( buf1 ) , " $[%s] " , p - > u2 . for_test ) ;
for_test - > app = 0 ;
for_test - > appargs = strdup ( buf1 ) ;
for_loop - > goto_true = for_test ;
snprintf ( buf1 , sizeof ( buf1 ) , " Finish for-%s-%d " , label , control_statement_count ) ;
for_end - > app = strdup ( " NoOp " ) ;
for_end - > appargs = strdup ( buf1 ) ;
/* link & load! */
2008-03-27 04:47:12 +00:00
linkprio ( exten , for_init , mother_exten ) ;
linkprio ( exten , for_test , mother_exten ) ;
2007-08-15 19:21:27 +00:00
/* now, put the body of the for loop here */
exten - > loop_break = for_end ;
exten - > loop_continue = for_inc ;
gen_prios ( exten , new_label , p - > u4 . for_statements , mother_exten , this_context ) ; /* this will link in all the statements here */
2008-03-27 04:47:12 +00:00
linkprio ( exten , for_inc , mother_exten ) ;
linkprio ( exten , for_loop , mother_exten ) ;
linkprio ( exten , for_end , mother_exten ) ;
2007-08-15 19:21:27 +00:00
exten - > loop_break = loop_break_save ;
exten - > loop_continue = loop_continue_save ;
for_loop - > origin = p ;
break ;
case PV_WHILE :
control_statement_count + + ;
loop_break_save = exten - > loop_break ; /* save them, then restore before leaving */
loop_continue_save = exten - > loop_continue ;
snprintf ( new_label , sizeof ( new_label ) , " while-%s-%d " , label , control_statement_count ) ;
while_test = new_prio ( ) ;
while_loop = new_prio ( ) ;
while_end = new_prio ( ) ;
while_test - > type = AEL_FOR_CONTROL ;
while_test - > goto_false = while_end ;
while_loop - > type = AEL_CONTROL1 ; /* simple goto */
while_end - > type = AEL_APPCALL ;
snprintf ( buf1 , sizeof ( buf1 ) , " $[%s] " , p - > u1 . str ) ;
while_test - > app = 0 ;
while_test - > appargs = strdup ( buf1 ) ;
while_loop - > goto_true = while_test ;
snprintf ( buf1 , sizeof ( buf1 ) , " Finish while-%s-%d " , label , control_statement_count ) ;
while_end - > app = strdup ( " NoOp " ) ;
while_end - > appargs = strdup ( buf1 ) ;
2008-03-27 04:47:12 +00:00
linkprio ( exten , while_test , mother_exten ) ;
2007-08-15 19:21:27 +00:00
/* now, put the body of the for loop here */
exten - > loop_break = while_end ;
exten - > loop_continue = while_test ;
gen_prios ( exten , new_label , p - > u2 . statements , mother_exten , this_context ) ; /* this will link in all the while body statements here */
2008-03-27 04:47:12 +00:00
linkprio ( exten , while_loop , mother_exten ) ;
linkprio ( exten , while_end , mother_exten ) ;
2007-08-15 19:21:27 +00:00
exten - > loop_break = loop_break_save ;
exten - > loop_continue = loop_continue_save ;
while_loop - > origin = p ;
break ;
case PV_SWITCH :
control_statement_count + + ;
local_control_statement_count = control_statement_count ;
loop_break_save = exten - > loop_break ; /* save them, then restore before leaving */
loop_continue_save = exten - > loop_continue ;
snprintf ( new_label , sizeof ( new_label ) , " sw-%s-%d " , label , control_statement_count ) ;
switch_test = new_prio ( ) ;
switch_end = new_prio ( ) ;
switch_test - > type = AEL_APPCALL ;
switch_end - > type = AEL_APPCALL ;
snprintf ( buf1 , sizeof ( buf1 ) , " sw-%d-%s,10 " , control_statement_count , p - > u1 . str ) ;
switch_test - > app = strdup ( " Goto " ) ;
switch_test - > appargs = strdup ( buf1 ) ;
snprintf ( buf1 , sizeof ( buf1 ) , " Finish switch-%s-%d " , label , control_statement_count ) ;
switch_end - > app = strdup ( " NoOp " ) ;
switch_end - > appargs = strdup ( buf1 ) ;
switch_end - > origin = p ;
switch_end - > exten = exten ;
2008-03-27 04:47:12 +00:00
linkprio ( exten , switch_test , mother_exten ) ;
linkprio ( exten , switch_end , mother_exten ) ;
2007-08-15 19:21:27 +00:00
exten - > loop_break = switch_end ;
exten - > loop_continue = 0 ;
default_exists = 0 ;
for ( p2 = p - > u2 . statements ; p2 ; p2 = p2 - > next ) {
/* now, for each case/default put the body of the for loop here */
if ( p2 - > type = = PV_CASE ) {
/* ok, generate a extension and link it in */
switch_case = new_exten ( ) ;
2008-08-08 00:48:35 +00:00
if ( mother_exten & & mother_exten - > checked_switch ) {
switch_case - > has_switch = mother_exten - > has_switch ;
switch_case - > checked_switch = mother_exten - > checked_switch ;
}
if ( exten & & exten - > checked_switch ) {
switch_case - > has_switch = exten - > has_switch ;
switch_case - > checked_switch = exten - > checked_switch ;
}
2007-08-15 19:21:27 +00:00
switch_case - > context = this_context ;
switch_case - > is_switch = 1 ;
/* the break/continue locations are inherited from parent */
switch_case - > loop_break = exten - > loop_break ;
switch_case - > loop_continue = exten - > loop_continue ;
linkexten ( exten , switch_case ) ;
snprintf ( buf1 , sizeof ( buf1 ) , " sw-%d-%s " , local_control_statement_count , p2 - > u1 . str ) ;
switch_case - > name = strdup ( buf1 ) ;
snprintf ( new_label , sizeof ( new_label ) , " sw-%s-%s-%d " , label , p2 - > u1 . str , local_control_statement_count ) ;
gen_prios ( switch_case , new_label , p2 - > u2 . statements , exten , this_context ) ; /* this will link in all the case body statements here */
/* here is where we write code to "fall thru" to the next case... if there is one... */
for ( p3 = p2 - > u2 . statements ; p3 ; p3 = p3 - > next ) {
if ( ! p3 - > next )
break ;
}
/* p3 now points the last statement... */
if ( ! p3 | | ( p3 - > type ! = PV_GOTO & & p3 - > type ! = PV_BREAK & & p3 - > type ! = PV_RETURN ) ) {
/* is there a following CASE/PATTERN/DEFAULT? */
if ( p2 - > next & & p2 - > next - > type = = PV_CASE ) {
fall_thru = new_prio ( ) ;
fall_thru - > type = AEL_APPCALL ;
fall_thru - > app = strdup ( " Goto " ) ;
snprintf ( buf1 , sizeof ( buf1 ) , " sw-%d-%s,10 " , local_control_statement_count , p2 - > next - > u1 . str ) ;
fall_thru - > appargs = strdup ( buf1 ) ;
2008-03-27 04:47:12 +00:00
linkprio ( switch_case , fall_thru , mother_exten ) ;
2007-08-15 19:21:27 +00:00
} else if ( p2 - > next & & p2 - > next - > type = = PV_PATTERN ) {
fall_thru = new_prio ( ) ;
fall_thru - > type = AEL_APPCALL ;
fall_thru - > app = strdup ( " Goto " ) ;
gen_match_to_pattern ( p2 - > next - > u1 . str , buf2 ) ;
snprintf ( buf1 , sizeof ( buf1 ) , " sw-%d-%s,10 " , local_control_statement_count , buf2 ) ;
fall_thru - > appargs = strdup ( buf1 ) ;
2008-03-27 04:47:12 +00:00
linkprio ( switch_case , fall_thru , mother_exten ) ;
2007-08-15 19:21:27 +00:00
} else if ( p2 - > next & & p2 - > next - > type = = PV_DEFAULT ) {
fall_thru = new_prio ( ) ;
fall_thru - > type = AEL_APPCALL ;
fall_thru - > app = strdup ( " Goto " ) ;
snprintf ( buf1 , sizeof ( buf1 ) , " sw-%d-.,10 " , local_control_statement_count ) ;
fall_thru - > appargs = strdup ( buf1 ) ;
2008-03-27 04:47:12 +00:00
linkprio ( switch_case , fall_thru , mother_exten ) ;
2007-08-15 19:21:27 +00:00
} else if ( ! p2 - > next ) {
fall_thru = new_prio ( ) ;
fall_thru - > type = AEL_CONTROL1 ;
fall_thru - > goto_true = switch_end ;
fall_thru - > app = strdup ( " Goto " ) ;
2008-03-27 04:47:12 +00:00
linkprio ( switch_case , fall_thru , mother_exten ) ;
2007-08-15 19:21:27 +00:00
}
}
if ( switch_case - > return_needed ) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */
char buf [ 2000 ] ;
struct ael_priority * np2 = new_prio ( ) ;
np2 - > type = AEL_APPCALL ;
np2 - > app = strdup ( " NoOp " ) ;
snprintf ( buf , sizeof ( buf ) , " End of Extension %s " , switch_case - > name ) ;
np2 - > appargs = strdup ( buf ) ;
2008-03-27 04:47:12 +00:00
linkprio ( switch_case , np2 , mother_exten ) ;
2007-08-15 19:21:27 +00:00
switch_case - > return_target = np2 ;
}
} else if ( p2 - > type = = PV_PATTERN ) {
/* ok, generate a extension and link it in */
switch_case = new_exten ( ) ;
2008-08-08 00:48:35 +00:00
if ( mother_exten & & mother_exten - > checked_switch ) {
switch_case - > has_switch = mother_exten - > has_switch ;
switch_case - > checked_switch = mother_exten - > checked_switch ;
}
if ( exten & & exten - > checked_switch ) {
switch_case - > has_switch = exten - > has_switch ;
switch_case - > checked_switch = exten - > checked_switch ;
}
2007-08-15 19:21:27 +00:00
switch_case - > context = this_context ;
switch_case - > is_switch = 1 ;
/* the break/continue locations are inherited from parent */
switch_case - > loop_break = exten - > loop_break ;
switch_case - > loop_continue = exten - > loop_continue ;
linkexten ( exten , switch_case ) ;
snprintf ( buf1 , sizeof ( buf1 ) , " _sw-%d-%s " , local_control_statement_count , p2 - > u1 . str ) ;
switch_case - > name = strdup ( buf1 ) ;
snprintf ( new_label , sizeof ( new_label ) , " sw-%s-%s-%d " , label , p2 - > u1 . str , local_control_statement_count ) ;
gen_prios ( switch_case , new_label , p2 - > u2 . statements , exten , this_context ) ; /* this will link in all the while body statements here */
/* here is where we write code to "fall thru" to the next case... if there is one... */
for ( p3 = p2 - > u2 . statements ; p3 ; p3 = p3 - > next ) {
if ( ! p3 - > next )
break ;
}
/* p3 now points the last statement... */
if ( ! p3 | | ( p3 - > type ! = PV_GOTO & & p3 - > type ! = PV_BREAK & & p3 - > type ! = PV_RETURN ) ) {
/* is there a following CASE/PATTERN/DEFAULT? */
if ( p2 - > next & & p2 - > next - > type = = PV_CASE ) {
fall_thru = new_prio ( ) ;
fall_thru - > type = AEL_APPCALL ;
fall_thru - > app = strdup ( " Goto " ) ;
snprintf ( buf1 , sizeof ( buf1 ) , " sw-%d-%s,10 " , local_control_statement_count , p2 - > next - > u1 . str ) ;
fall_thru - > appargs = strdup ( buf1 ) ;
2008-03-27 04:47:12 +00:00
linkprio ( switch_case , fall_thru , mother_exten ) ;
2007-08-15 19:21:27 +00:00
} else if ( p2 - > next & & p2 - > next - > type = = PV_PATTERN ) {
fall_thru = new_prio ( ) ;
fall_thru - > type = AEL_APPCALL ;
fall_thru - > app = strdup ( " Goto " ) ;
gen_match_to_pattern ( p2 - > next - > u1 . str , buf2 ) ;
snprintf ( buf1 , sizeof ( buf1 ) , " sw-%d-%s,10 " , local_control_statement_count , buf2 ) ;
fall_thru - > appargs = strdup ( buf1 ) ;
2008-03-27 04:47:12 +00:00
linkprio ( switch_case , fall_thru , mother_exten ) ;
2007-08-15 19:21:27 +00:00
} else if ( p2 - > next & & p2 - > next - > type = = PV_DEFAULT ) {
fall_thru = new_prio ( ) ;
fall_thru - > type = AEL_APPCALL ;
fall_thru - > app = strdup ( " Goto " ) ;
snprintf ( buf1 , sizeof ( buf1 ) , " sw-%d-.,10 " , local_control_statement_count ) ;
fall_thru - > appargs = strdup ( buf1 ) ;
2008-03-27 04:47:12 +00:00
linkprio ( switch_case , fall_thru , mother_exten ) ;
2007-08-15 19:21:27 +00:00
} else if ( ! p2 - > next ) {
fall_thru = new_prio ( ) ;
fall_thru - > type = AEL_CONTROL1 ;
fall_thru - > goto_true = switch_end ;
fall_thru - > app = strdup ( " Goto " ) ;
2008-03-27 04:47:12 +00:00
linkprio ( switch_case , fall_thru , mother_exten ) ;
2007-08-15 19:21:27 +00:00
}
}
if ( switch_case - > return_needed ) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */
char buf [ 2000 ] ;
struct ael_priority * np2 = new_prio ( ) ;
np2 - > type = AEL_APPCALL ;
np2 - > app = strdup ( " NoOp " ) ;
snprintf ( buf , sizeof ( buf ) , " End of Extension %s " , switch_case - > name ) ;
np2 - > appargs = strdup ( buf ) ;
2008-03-27 04:47:12 +00:00
linkprio ( switch_case , np2 , mother_exten ) ;
2007-08-15 19:21:27 +00:00
switch_case - > return_target = np2 ;
}
} else if ( p2 - > type = = PV_DEFAULT ) {
/* ok, generate a extension and link it in */
switch_case = new_exten ( ) ;
2008-08-08 00:48:35 +00:00
if ( mother_exten & & mother_exten - > checked_switch ) {
switch_case - > has_switch = mother_exten - > has_switch ;
switch_case - > checked_switch = mother_exten - > checked_switch ;
}
if ( exten & & exten - > checked_switch ) {
switch_case - > has_switch = exten - > has_switch ;
switch_case - > checked_switch = exten - > checked_switch ;
}
2007-08-15 19:21:27 +00:00
switch_case - > context = this_context ;
switch_case - > is_switch = 1 ;
Merged revisions 84511 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r84511 | murf | 2007-10-03 08:23:00 -0600 (Wed, 03 Oct 2007) | 1 line
closes issue #10834 ; where a null input to a switch statement results in a hangup; since switch is implemented with extensions, and the default case is implemented with a '.', and the '.' matches 1 or more remaining characters, the case where 0 characters exist isn't matched, and the extension isn't matched, and the goto fails, and a hangup occurs. Now, when a default case is generated, it also generates a single fixed extension that will match a null input. That extension just does a goto to the default extension for that switch. I played with an alternate solution, where I just tack an extra char onto all the patterns and the goto, but not the default case's pattern. Then even a null input will still have at least one char in it. But it made me nervous, having that extra char in , even if that's a pretty secret and low-level issue.
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@84512 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2007-10-03 14:39:05 +00:00
/* new: the default case intros a pattern with ., which covers ALMOST everything.
but it doesn ' t cover a NULL pattern . So , we ' ll define a null extension to match
that goto ' s the default extension . */
default_exists + + ;
switch_null = new_exten ( ) ;
2008-08-08 00:48:35 +00:00
if ( mother_exten & & mother_exten - > checked_switch ) {
switch_null - > has_switch = mother_exten - > has_switch ;
switch_null - > checked_switch = mother_exten - > checked_switch ;
}
if ( exten & & exten - > checked_switch ) {
switch_null - > has_switch = exten - > has_switch ;
switch_null - > checked_switch = exten - > checked_switch ;
}
Merged revisions 84511 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r84511 | murf | 2007-10-03 08:23:00 -0600 (Wed, 03 Oct 2007) | 1 line
closes issue #10834 ; where a null input to a switch statement results in a hangup; since switch is implemented with extensions, and the default case is implemented with a '.', and the '.' matches 1 or more remaining characters, the case where 0 characters exist isn't matched, and the extension isn't matched, and the goto fails, and a hangup occurs. Now, when a default case is generated, it also generates a single fixed extension that will match a null input. That extension just does a goto to the default extension for that switch. I played with an alternate solution, where I just tack an extra char onto all the patterns and the goto, but not the default case's pattern. Then even a null input will still have at least one char in it. But it made me nervous, having that extra char in , even if that's a pretty secret and low-level issue.
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@84512 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2007-10-03 14:39:05 +00:00
switch_null - > context = this_context ;
switch_null - > is_switch = 1 ;
switch_empty = new_prio ( ) ;
2008-11-20 19:41:23 +00:00
snprintf ( buf1 , sizeof ( buf1 ) , " sw-%d-.,10 " , local_control_statement_count ) ;
Merged revisions 84511 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r84511 | murf | 2007-10-03 08:23:00 -0600 (Wed, 03 Oct 2007) | 1 line
closes issue #10834 ; where a null input to a switch statement results in a hangup; since switch is implemented with extensions, and the default case is implemented with a '.', and the '.' matches 1 or more remaining characters, the case where 0 characters exist isn't matched, and the extension isn't matched, and the goto fails, and a hangup occurs. Now, when a default case is generated, it also generates a single fixed extension that will match a null input. That extension just does a goto to the default extension for that switch. I played with an alternate solution, where I just tack an extra char onto all the patterns and the goto, but not the default case's pattern. Then even a null input will still have at least one char in it. But it made me nervous, having that extra char in , even if that's a pretty secret and low-level issue.
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@84512 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2007-10-03 14:39:05 +00:00
switch_empty - > app = strdup ( " Goto " ) ;
switch_empty - > appargs = strdup ( buf1 ) ;
2008-03-27 04:47:12 +00:00
linkprio ( switch_null , switch_empty , mother_exten ) ;
Merged revisions 84511 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r84511 | murf | 2007-10-03 08:23:00 -0600 (Wed, 03 Oct 2007) | 1 line
closes issue #10834 ; where a null input to a switch statement results in a hangup; since switch is implemented with extensions, and the default case is implemented with a '.', and the '.' matches 1 or more remaining characters, the case where 0 characters exist isn't matched, and the extension isn't matched, and the goto fails, and a hangup occurs. Now, when a default case is generated, it also generates a single fixed extension that will match a null input. That extension just does a goto to the default extension for that switch. I played with an alternate solution, where I just tack an extra char onto all the patterns and the goto, but not the default case's pattern. Then even a null input will still have at least one char in it. But it made me nervous, having that extra char in , even if that's a pretty secret and low-level issue.
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@84512 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2007-10-03 14:39:05 +00:00
snprintf ( buf1 , sizeof ( buf1 ) , " sw-%d- " , local_control_statement_count ) ;
switch_null - > name = strdup ( buf1 ) ;
switch_null - > loop_break = exten - > loop_break ;
switch_null - > loop_continue = exten - > loop_continue ;
linkexten ( exten , switch_null ) ;
2007-08-15 19:21:27 +00:00
/* the break/continue locations are inherited from parent */
switch_case - > loop_break = exten - > loop_break ;
switch_case - > loop_continue = exten - > loop_continue ;
linkexten ( exten , switch_case ) ;
snprintf ( buf1 , sizeof ( buf1 ) , " _sw-%d-. " , local_control_statement_count ) ;
switch_case - > name = strdup ( buf1 ) ;
snprintf ( new_label , sizeof ( new_label ) , " sw-%s-default-%d " , label , local_control_statement_count ) ;
Merged revisions 84511 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r84511 | murf | 2007-10-03 08:23:00 -0600 (Wed, 03 Oct 2007) | 1 line
closes issue #10834 ; where a null input to a switch statement results in a hangup; since switch is implemented with extensions, and the default case is implemented with a '.', and the '.' matches 1 or more remaining characters, the case where 0 characters exist isn't matched, and the extension isn't matched, and the goto fails, and a hangup occurs. Now, when a default case is generated, it also generates a single fixed extension that will match a null input. That extension just does a goto to the default extension for that switch. I played with an alternate solution, where I just tack an extra char onto all the patterns and the goto, but not the default case's pattern. Then even a null input will still have at least one char in it. But it made me nervous, having that extra char in , even if that's a pretty secret and low-level issue.
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@84512 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2007-10-03 14:39:05 +00:00
gen_prios ( switch_case , new_label , p2 - > u2 . statements , exten , this_context ) ; /* this will link in all the default: body statements here */
2007-08-15 19:21:27 +00:00
/* here is where we write code to "fall thru" to the next case... if there is one... */
for ( p3 = p2 - > u2 . statements ; p3 ; p3 = p3 - > next ) {
if ( ! p3 - > next )
break ;
}
/* p3 now points the last statement... */
if ( ! p3 | | ( p3 - > type ! = PV_GOTO & & p3 - > type ! = PV_BREAK & & p3 - > type ! = PV_RETURN ) ) {
/* is there a following CASE/PATTERN/DEFAULT? */
if ( p2 - > next & & p2 - > next - > type = = PV_CASE ) {
fall_thru = new_prio ( ) ;
fall_thru - > type = AEL_APPCALL ;
fall_thru - > app = strdup ( " Goto " ) ;
snprintf ( buf1 , sizeof ( buf1 ) , " sw-%d-%s,10 " , local_control_statement_count , p2 - > next - > u1 . str ) ;
fall_thru - > appargs = strdup ( buf1 ) ;
2008-03-27 04:47:12 +00:00
linkprio ( switch_case , fall_thru , mother_exten ) ;
2007-08-15 19:21:27 +00:00
} else if ( p2 - > next & & p2 - > next - > type = = PV_PATTERN ) {
fall_thru = new_prio ( ) ;
fall_thru - > type = AEL_APPCALL ;
fall_thru - > app = strdup ( " Goto " ) ;
gen_match_to_pattern ( p2 - > next - > u1 . str , buf2 ) ;
snprintf ( buf1 , sizeof ( buf1 ) , " sw-%d-%s,10 " , local_control_statement_count , buf2 ) ;
fall_thru - > appargs = strdup ( buf1 ) ;
2008-03-27 04:47:12 +00:00
linkprio ( switch_case , fall_thru , mother_exten ) ;
2007-08-15 19:21:27 +00:00
} else if ( p2 - > next & & p2 - > next - > type = = PV_DEFAULT ) {
fall_thru = new_prio ( ) ;
fall_thru - > type = AEL_APPCALL ;
fall_thru - > app = strdup ( " Goto " ) ;
snprintf ( buf1 , sizeof ( buf1 ) , " sw-%d-.,10 " , local_control_statement_count ) ;
fall_thru - > appargs = strdup ( buf1 ) ;
2008-03-27 04:47:12 +00:00
linkprio ( switch_case , fall_thru , mother_exten ) ;
2007-08-15 19:21:27 +00:00
} else if ( ! p2 - > next ) {
fall_thru = new_prio ( ) ;
fall_thru - > type = AEL_CONTROL1 ;
fall_thru - > goto_true = switch_end ;
fall_thru - > app = strdup ( " Goto " ) ;
2008-03-27 04:47:12 +00:00
linkprio ( switch_case , fall_thru , mother_exten ) ;
2007-08-15 19:21:27 +00:00
}
}
if ( switch_case - > return_needed ) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */
char buf [ 2000 ] ;
struct ael_priority * np2 = new_prio ( ) ;
np2 - > type = AEL_APPCALL ;
np2 - > app = strdup ( " NoOp " ) ;
snprintf ( buf , sizeof ( buf ) , " End of Extension %s " , switch_case - > name ) ;
np2 - > appargs = strdup ( buf ) ;
2008-03-27 04:47:12 +00:00
linkprio ( switch_case , np2 , mother_exten ) ;
2007-08-15 19:21:27 +00:00
switch_case - > return_target = np2 ;
}
} else {
/* what could it be??? */
}
}
exten - > loop_break = loop_break_save ;
exten - > loop_continue = loop_continue_save ;
switch_test - > origin = p ;
switch_end - > origin = p ;
break ;
case PV_MACRO_CALL :
pr = new_prio ( ) ;
pr - > type = AEL_APPCALL ;
snprintf ( buf1 , sizeof ( buf1 ) , " %s,s,1 " , p - > u1 . str ) ;
first = 1 ;
for ( p2 = p - > u2 . arglist ; p2 ; p2 = p2 - > next ) {
if ( first )
{
strcat ( buf1 , " ( " ) ;
first = 0 ;
}
else
strcat ( buf1 , " , " ) ;
strcat ( buf1 , p2 - > u1 . str ) ;
}
if ( ! first )
strcat ( buf1 , " ) " ) ;
pr - > app = strdup ( " Gosub " ) ;
pr - > appargs = strdup ( buf1 ) ;
pr - > origin = p ;
2008-03-27 04:47:12 +00:00
linkprio ( exten , pr , mother_exten ) ;
2007-08-15 19:21:27 +00:00
break ;
case PV_APPLICATION_CALL :
pr = new_prio ( ) ;
pr - > type = AEL_APPCALL ;
buf1 [ 0 ] = 0 ;
for ( p2 = p - > u2 . arglist ; p2 ; p2 = p2 - > next ) {
if ( p2 ! = p - > u2 . arglist )
strcat ( buf1 , " , " ) ;
strcat ( buf1 , p2 - > u1 . str ) ;
}
pr - > app = strdup ( p - > u1 . str ) ;
pr - > appargs = strdup ( buf1 ) ;
pr - > origin = p ;
2008-03-27 04:47:12 +00:00
linkprio ( exten , pr , mother_exten ) ;
2007-08-15 19:21:27 +00:00
break ;
case PV_BREAK :
pr = new_prio ( ) ;
pr - > type = AEL_CONTROL1 ; /* simple goto */
pr - > goto_true = exten - > loop_break ;
pr - > origin = p ;
2008-03-27 04:47:12 +00:00
linkprio ( exten , pr , mother_exten ) ;
2007-08-15 19:21:27 +00:00
break ;
case PV_RETURN : /* hmmmm */
pr = new_prio ( ) ;
pr - > type = AEL_RETURN ; /* simple Return */
/* exten->return_needed++; */
pr - > app = strdup ( " Return " ) ;
pr - > appargs = strdup ( " " ) ;
pr - > origin = p ;
2008-03-27 04:47:12 +00:00
linkprio ( exten , pr , mother_exten ) ;
2007-08-15 19:21:27 +00:00
break ;
case PV_CONTINUE :
pr = new_prio ( ) ;
pr - > type = AEL_CONTROL1 ; /* simple goto */
pr - > goto_true = exten - > loop_continue ;
pr - > origin = p ;
2008-03-27 04:47:12 +00:00
linkprio ( exten , pr , mother_exten ) ;
2007-08-15 19:21:27 +00:00
break ;
case PV_IFTIME :
control_statement_count + + ;
snprintf ( new_label , sizeof ( new_label ) , " iftime-%s-%d " , label , control_statement_count ) ;
if_test = new_prio ( ) ;
if_test - > type = AEL_IFTIME_CONTROL ;
snprintf ( buf1 , sizeof ( buf1 ) , " %s,%s,%s,%s " ,
p - > u1 . list - > u1 . str ,
p - > u1 . list - > next - > u1 . str ,
p - > u1 . list - > next - > next - > u1 . str ,
p - > u1 . list - > next - > next - > next - > u1 . str ) ;
if_test - > app = 0 ;
if_test - > appargs = strdup ( buf1 ) ;
if_test - > origin = p ;
if_end = new_prio ( ) ;
if_end - > type = AEL_APPCALL ;
snprintf ( buf1 , sizeof ( buf1 ) , " Finish iftime-%s-%d " , label , control_statement_count ) ;
if_end - > app = strdup ( " NoOp " ) ;
if_end - > appargs = strdup ( buf1 ) ;
if ( p - > u3 . else_statements ) {
if_skip = new_prio ( ) ;
if_skip - > type = AEL_CONTROL1 ; /* simple goto */
if_skip - > goto_true = if_end ;
if_skip - > origin = p ;
} else {
if_skip = 0 ;
if_test - > goto_false = if_end ;
}
if_false = new_prio ( ) ;
if_false - > type = AEL_CONTROL1 ;
if ( p - > u3 . else_statements ) {
if_false - > goto_true = if_skip ; /* +1 */
} else {
if_false - > goto_true = if_end ;
}
/* link & load! */
2008-03-27 04:47:12 +00:00
linkprio ( exten , if_test , mother_exten ) ;
linkprio ( exten , if_false , mother_exten ) ;
2007-08-15 19:21:27 +00:00
/* now, put the body of the if here */
gen_prios ( exten , new_label , p - > u2 . statements , mother_exten , this_context ) ; /* this will link in all the statements here */
if ( p - > u3 . else_statements ) {
2008-03-27 04:47:12 +00:00
linkprio ( exten , if_skip , mother_exten ) ;
2007-08-15 19:21:27 +00:00
gen_prios ( exten , new_label , p - > u3 . else_statements , mother_exten , this_context ) ; /* this will link in all the statements here */
}
2008-03-27 04:47:12 +00:00
linkprio ( exten , if_end , mother_exten ) ;
2007-08-15 19:21:27 +00:00
break ;
case PV_RANDOM :
case PV_IF :
control_statement_count + + ;
snprintf ( new_label , sizeof ( new_label ) , " if-%s-%d " , label , control_statement_count ) ;
if_test = new_prio ( ) ;
if_end = new_prio ( ) ;
if_test - > type = AEL_IF_CONTROL ;
if_end - > type = AEL_APPCALL ;
if ( p - > type = = PV_RANDOM )
snprintf ( buf1 , sizeof ( buf1 ) , " $[${RAND(0,99)} < (%s)] " , p - > u1 . str ) ;
else
snprintf ( buf1 , sizeof ( buf1 ) , " $[%s] " , p - > u1 . str ) ;
if_test - > app = 0 ;
if_test - > appargs = strdup ( buf1 ) ;
snprintf ( buf1 , sizeof ( buf1 ) , " Finish if-%s-%d " , label , control_statement_count ) ;
if_end - > app = strdup ( " NoOp " ) ;
if_end - > appargs = strdup ( buf1 ) ;
if_test - > origin = p ;
if ( p - > u3 . else_statements ) {
if_skip = new_prio ( ) ;
if_skip - > type = AEL_CONTROL1 ; /* simple goto */
if_skip - > goto_true = if_end ;
if_test - > goto_false = if_skip ; ;
} else {
if_skip = 0 ;
if_test - > goto_false = if_end ; ;
}
/* link & load! */
2008-03-27 04:47:12 +00:00
linkprio ( exten , if_test , mother_exten ) ;
2007-08-15 19:21:27 +00:00
/* now, put the body of the if here */
gen_prios ( exten , new_label , p - > u2 . statements , mother_exten , this_context ) ; /* this will link in all the statements here */
if ( p - > u3 . else_statements ) {
2008-03-27 04:47:12 +00:00
linkprio ( exten , if_skip , mother_exten ) ;
2007-08-15 19:21:27 +00:00
gen_prios ( exten , new_label , p - > u3 . else_statements , mother_exten , this_context ) ; /* this will link in all the statements here */
}
2008-03-27 04:47:12 +00:00
linkprio ( exten , if_end , mother_exten ) ;
2007-08-15 19:21:27 +00:00
break ;
case PV_STATEMENTBLOCK :
gen_prios ( exten , label , p - > u1 . list , mother_exten , this_context ) ; /* recurse into the block */
break ;
case PV_CATCH :
control_statement_count + + ;
/* generate an extension with name of catch, put all catch stats
into this exten ! */
switch_case = new_exten ( ) ;
2008-08-08 00:48:35 +00:00
if ( mother_exten & & mother_exten - > checked_switch ) {
switch_case - > has_switch = mother_exten - > has_switch ;
switch_case - > checked_switch = mother_exten - > checked_switch ;
}
if ( exten & & exten - > checked_switch ) {
switch_case - > has_switch = exten - > has_switch ;
switch_case - > checked_switch = exten - > checked_switch ;
}
2007-08-15 19:21:27 +00:00
switch_case - > context = this_context ;
linkexten ( exten , switch_case ) ;
switch_case - > name = strdup ( p - > u1 . str ) ;
snprintf ( new_label , sizeof ( new_label ) , " catch-%s-%d " , p - > u1 . str , control_statement_count ) ;
gen_prios ( switch_case , new_label , p - > u2 . statements , mother_exten , this_context ) ; /* this will link in all the catch body statements here */
if ( switch_case - > return_needed ) { /* returns now generate a Return() app call, no longer a goto to the end of the exten */
char buf [ 2000 ] ;
struct ael_priority * np2 = new_prio ( ) ;
np2 - > type = AEL_APPCALL ;
np2 - > app = strdup ( " NoOp " ) ;
snprintf ( buf , sizeof ( buf ) , " End of Extension %s " , switch_case - > name ) ;
np2 - > appargs = strdup ( buf ) ;
2008-03-27 04:47:12 +00:00
linkprio ( switch_case , np2 , mother_exten ) ;
2007-08-15 19:21:27 +00:00
switch_case - > return_target = np2 ;
}
break ;
default :
break ;
}
}
}
void set_priorities ( struct ael_extension * exten )
{
int i ;
struct ael_priority * pr ;
do {
if ( exten - > is_switch )
i = 10 ;
else if ( exten - > regexten )
i = 2 ;
else
i = 1 ;
for ( pr = exten - > plist ; pr ; pr = pr - > next ) {
pr - > priority_num = i ;
if ( ! pr - > origin | | ( pr - > origin & & pr - > origin - > type ! = PV_LABEL ) ) /* Labels don't show up in the dialplan,
but we want them to point to the right
priority , which would be the next line
after the label ; */
i + + ;
}
exten = exten - > next_exten ;
} while ( exten ) ;
}
void add_extensions ( struct ael_extension * exten )
{
struct ael_priority * pr ;
char * label = 0 ;
char realext [ AST_MAX_EXTENSION ] ;
if ( ! exten ) {
ast_log ( LOG_WARNING , " This file is Empty! \n " ) ;
return ;
}
do {
struct ael_priority * last = 0 ;
pbx_substitute_variables_helper ( NULL , exten - > name , realext , sizeof ( realext ) - 1 ) ;
if ( exten - > hints ) {
if ( ast_add_extension2 ( exten - > context , 0 /*no replace*/ , realext , PRIORITY_HINT , NULL , exten - > cidmatch ,
2007-09-17 18:57:56 +00:00
exten - > hints , NULL , ast_free_ptr , registrar ) ) {
2007-08-15 19:21:27 +00:00
ast_log ( LOG_WARNING , " Unable to add step at priority 'hint' of extension '%s' \n " ,
exten - > name ) ;
}
}
for ( pr = exten - > plist ; pr ; pr = pr - > next ) {
char app [ 2000 ] ;
char appargs [ 2000 ] ;
/* before we can add the extension, we need to prep the app/appargs;
the CONTROL types need to be done after the priority numbers are calculated .
*/
if ( pr - > type = = AEL_LABEL ) /* don't try to put labels in the dialplan! */ {
last = pr ;
continue ;
}
if ( pr - > app )
strcpy ( app , pr - > app ) ;
else
app [ 0 ] = 0 ;
if ( pr - > appargs )
strcpy ( appargs , pr - > appargs ) ;
else
appargs [ 0 ] = 0 ;
switch ( pr - > type ) {
case AEL_APPCALL :
/* easy case. Everything is all set up */
break ;
case AEL_CONTROL1 : /* FOR loop, WHILE loop, BREAK, CONTINUE, IF, IFTIME */
/* simple, unconditional goto. */
strcpy ( app , " Goto " ) ;
if ( pr - > goto_true - > origin & & pr - > goto_true - > origin - > type = = PV_SWITCH ) {
snprintf ( appargs , sizeof ( appargs ) , " %s,%d " , pr - > goto_true - > exten - > name , pr - > goto_true - > priority_num ) ;
} else if ( pr - > goto_true - > origin & & pr - > goto_true - > origin - > type = = PV_IFTIME & & pr - > goto_true - > origin - > u3 . else_statements ) {
snprintf ( appargs , sizeof ( appargs ) , " %d " , pr - > goto_true - > priority_num + 1 ) ;
} else
snprintf ( appargs , sizeof ( appargs ) , " %d " , pr - > goto_true - > priority_num ) ;
break ;
case AEL_FOR_CONTROL : /* WHILE loop test, FOR loop test */
strcpy ( app , " GotoIf " ) ;
snprintf ( appargs , sizeof ( appargs ) , " %s?%d:%d " , pr - > appargs , pr - > priority_num + 1 , pr - > goto_false - > priority_num ) ;
break ;
case AEL_IF_CONTROL :
strcpy ( app , " GotoIf " ) ;
if ( pr - > origin - > u3 . else_statements )
snprintf ( appargs , sizeof ( appargs ) , " %s?%d:%d " , pr - > appargs , pr - > priority_num + 1 , pr - > goto_false - > priority_num + 1 ) ;
else
snprintf ( appargs , sizeof ( appargs ) , " %s?%d:%d " , pr - > appargs , pr - > priority_num + 1 , pr - > goto_false - > priority_num ) ;
break ;
case AEL_RAND_CONTROL :
strcpy ( app , " Random " ) ;
snprintf ( appargs , sizeof ( appargs ) , " %s:%d " , pr - > appargs , pr - > goto_true - > priority_num + 1 ) ;
break ;
case AEL_IFTIME_CONTROL :
strcpy ( app , " GotoIfTime " ) ;
snprintf ( appargs , sizeof ( appargs ) , " %s?%d " , pr - > appargs , pr - > priority_num + 2 ) ;
break ;
case AEL_RETURN :
strcpy ( app , " Return " ) ;
appargs [ 0 ] = 0 ;
break ;
default :
break ;
}
if ( last & & last - > type = = AEL_LABEL ) {
label = last - > origin - > u1 . str ;
}
else
label = 0 ;
if ( ast_add_extension2 ( exten - > context , 0 /*no replace*/ , realext , pr - > priority_num , ( label ? label : NULL ) , exten - > cidmatch ,
2007-09-17 18:57:56 +00:00
app , strdup ( appargs ) , ast_free_ptr , registrar ) ) {
2007-08-15 19:21:27 +00:00
ast_log ( LOG_WARNING , " Unable to add step at priority '%d' of extension '%s' \n " , pr - > priority_num ,
exten - > name ) ;
}
last = pr ;
}
exten = exten - > next_exten ;
} while ( exten ) ;
}
static void attach_exten ( struct ael_extension * * list , struct ael_extension * newmem )
{
/* travel to the end of the list... */
struct ael_extension * lptr ;
if ( ! * list ) {
* list = newmem ;
return ;
}
lptr = * list ;
while ( lptr - > next_exten ) {
lptr = lptr - > next_exten ;
}
/* lptr should now pointing to the last element in the list; it has a null next_exten pointer */
lptr - > next_exten = newmem ;
}
static pval * get_extension_or_contxt ( pval * p )
{
while ( p & & p - > type ! = PV_EXTENSION & & p - > type ! = PV_CONTEXT & & p - > type ! = PV_MACRO ) {
p = p - > dad ;
}
return p ;
}
static pval * get_contxt ( pval * p )
{
while ( p & & p - > type ! = PV_CONTEXT & & p - > type ! = PV_MACRO ) {
p = p - > dad ;
}
return p ;
}
static void fix_gotos_in_extensions ( struct ael_extension * exten )
{
struct ael_extension * e ;
for ( e = exten ; e ; e = e - > next_exten ) {
struct ael_priority * p ;
for ( p = e - > plist ; p ; p = p - > next ) {
if ( p - > origin & & p - > origin - > type = = PV_GOTO & & p - > origin - > u3 . goto_target_in_case ) {
/* fix the extension of the goto target to the actual extension in the post-compiled dialplan */
pval * target = p - > origin - > u2 . goto_target ;
struct ael_extension * z = target - > u3 . compiled_label ;
pval * pv2 = p - > origin ;
char buf1 [ 500 ] ;
char * apparg_save = p - > appargs ;
p - > appargs = 0 ;
if ( ! pv2 - > u1 . list - > next ) /* just one -- it won't hurt to repeat the extension */ {
snprintf ( buf1 , sizeof ( buf1 ) , " %s,%s " , z - > name , pv2 - > u1 . list - > u1 . str ) ;
p - > appargs = strdup ( buf1 ) ;
} else if ( pv2 - > u1 . list - > next & & ! pv2 - > u1 . list - > next - > next ) /* two */ {
snprintf ( buf1 , sizeof ( buf1 ) , " %s,%s " , z - > name , pv2 - > u1 . list - > next - > u1 . str ) ;
p - > appargs = strdup ( buf1 ) ;
} else if ( pv2 - > u1 . list - > next & & pv2 - > u1 . list - > next - > next ) {
snprintf ( buf1 , sizeof ( buf1 ) , " %s,%s,%s " , pv2 - > u1 . list - > u1 . str ,
z - > name ,
pv2 - > u1 . list - > next - > next - > u1 . str ) ;
p - > appargs = strdup ( buf1 ) ;
}
else
printf ( " WHAT? The goto doesn't fall into one of three cases for GOTO???? \n " ) ;
if ( apparg_save ) {
free ( apparg_save ) ;
}
}
}
}
}
(closes issue #6002)
Reported by: rizzo
Tested by: murf
Proposal of the changes to be made, and then an announcement of how they were accomplished:
http://lists.digium.com/pipermail/asterisk-dev/2008-February/032065.html
and:
http://lists.digium.com/pipermail/asterisk-dev/2008-March/032124.html
Here is a recap, file by file, of what I have done:
pbx/pbx_config.c
pbx/pbx_ael.c
All funcs that were passed a ptr to the context list, now will ALSO be passed a hashtab ptr to the same set.
Why? because (for the time being), the dialplan is stored in both, to facilitate a quick, low-cost move to
hash-tables to speed up dialplan processing. If it was deemed necessary to pass the context LIST, well, it
is just as necessary to have the TABLE available. This is because the list/table in question might not be
the global one, but temporary ones we would use to stage the dialplan on, and then swap into the global
position when things are ready.
We now have one external function for apps to use, "ast_context_find_or_create()" instead of the pre-existing
"find" and "create", as all existing usages used both in tandem anyway.
pbx_config, and pbx_ael, will stage the reloaded dialplan into local lists and tables, and
then call merge_contexts_and_delete, which will merge (now) existing contexts and
priorities from other registrars into this local set by copying them. Then, merge_contexts_and_delete will
lock down the contexts, swap the lists and tables, and unlock (real quick), and then
destroy the old dialplan.
chan_sip.c
chan_iax.c
chan_skinny.c
All the channel drivers that would add regcontexts now use the ast_context_find_or_create now.
chan_sip also includes a small fix to get rid of warnings about removing priorities that never got entered.
apps/app_meetme.c
apps/app_dial.c
apps/app_queue.c
All the apps that added a context/exten/priority were also modified to use ast_context_find_or_create instead.
include/asterisk/pbx.h
ast_context_create() is removed. Find_or_create_ is the new method.
ast_context_find_or_create() interface gets the hashtab added.
ast_merge_contexts_and_delete() gets the local hashtab arg added.
ast_wrlock_contexts_version() is added so you can detect if someone else got a writelock between your readlocking and writelocking.
ast_hashtab_compare_contexts was made public for use in pbx_config/pbx_ael
ast_hashtab_hash_contexts was in like fashion make public.
include/asterisk/pval.h
ast_compile_ael2() interface changed to include the local hashtab table ptr.
main/features.c
For the sake of the parking context, we use ast_context_find_or_create().
main/pbx.c
I changed all the "tree" names to "table" instead. That's because the original
implementation was based on binary trees. (had a free library). Then I moved
to hashtabs. Now, the names move forward too.
refcount field added to contexts, so you can keep track of how many modules
wanted this context to exist.
Some log messages that are warnings were inflated from LOG_NOTICE to LOG_WARNING.
Added some calls to ast_verb(3,...) for debug messages
Lots of little mods to ast_context_remove_extension2, which is now excersized in ways
it was not previously; one definite bug fixed.
find_or_create was upgraded to handle both local lists/tables as well as the globals.
context_merge() was added to do the per-context merging of the old/present contexts/extens/prios into the new/proposed local list/tables
ast_merge_contexts_and_delete() was heavily modified.
ast_add_extension2() was also upgraded to handle changes.
the context_destroy() code was re-engineered to handle the new way of doing things,
by exten/prio instead of by context.
res/ael/pval.c
res/ael/ael.tab.c
res/ael/ael.tab.h
res/ael/ael.y
res/ael/ael_lex.c
res/ael/ael.flex
utils/ael_main.c
utils/extconf.c
utils/conf2ael.c
utils/Makefile
Had to change the interface to ast_compile_ael2(), to include the hashtab ptr.
This ended up involving several external apps. The main gotcha was I had to
include lock.h and hashtab.h in several places.
As a side note, I tested this stuff pretty thoroughly, I replicated the problems
originally reported by Luigi, and made triply sure that reloads worked, and everything
worked thru "stop gracefully". I found a and fixed a few bugs as I was merging into
trunk, that did not appear in my tests of bug6002.
How's this for verbose commit messages?
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@106757 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2008-03-07 18:57:57 +00:00
void ast_compile_ael2 ( struct ast_context * * local_contexts , struct ast_hashtab * local_table , struct pval * root )
2007-08-15 19:21:27 +00:00
{
pval * p , * p2 ;
struct ast_context * context ;
char buf [ 2000 ] ;
struct ael_extension * exten ;
struct ael_extension * exten_list = 0 ;
for ( p = root ; p ; p = p - > next ) { /* do the globals first, so they'll be there
when we try to eval them */
switch ( p - > type ) {
case PV_GLOBALS :
/* just VARDEC elements */
for ( p2 = p - > u1 . list ; p2 ; p2 = p2 - > next ) {
char buf2 [ 2000 ] ;
snprintf ( buf2 , sizeof ( buf2 ) , " %s=%s " , p2 - > u1 . str , p2 - > u2 . val ) ;
pbx_builtin_setvar ( NULL , buf2 ) ;
}
break ;
default :
break ;
}
}
for ( p = root ; p ; p = p - > next ) {
pval * lp ;
int argc ;
switch ( p - > type ) {
case PV_MACRO :
(closes issue #6002)
Reported by: rizzo
Tested by: murf
Proposal of the changes to be made, and then an announcement of how they were accomplished:
http://lists.digium.com/pipermail/asterisk-dev/2008-February/032065.html
and:
http://lists.digium.com/pipermail/asterisk-dev/2008-March/032124.html
Here is a recap, file by file, of what I have done:
pbx/pbx_config.c
pbx/pbx_ael.c
All funcs that were passed a ptr to the context list, now will ALSO be passed a hashtab ptr to the same set.
Why? because (for the time being), the dialplan is stored in both, to facilitate a quick, low-cost move to
hash-tables to speed up dialplan processing. If it was deemed necessary to pass the context LIST, well, it
is just as necessary to have the TABLE available. This is because the list/table in question might not be
the global one, but temporary ones we would use to stage the dialplan on, and then swap into the global
position when things are ready.
We now have one external function for apps to use, "ast_context_find_or_create()" instead of the pre-existing
"find" and "create", as all existing usages used both in tandem anyway.
pbx_config, and pbx_ael, will stage the reloaded dialplan into local lists and tables, and
then call merge_contexts_and_delete, which will merge (now) existing contexts and
priorities from other registrars into this local set by copying them. Then, merge_contexts_and_delete will
lock down the contexts, swap the lists and tables, and unlock (real quick), and then
destroy the old dialplan.
chan_sip.c
chan_iax.c
chan_skinny.c
All the channel drivers that would add regcontexts now use the ast_context_find_or_create now.
chan_sip also includes a small fix to get rid of warnings about removing priorities that never got entered.
apps/app_meetme.c
apps/app_dial.c
apps/app_queue.c
All the apps that added a context/exten/priority were also modified to use ast_context_find_or_create instead.
include/asterisk/pbx.h
ast_context_create() is removed. Find_or_create_ is the new method.
ast_context_find_or_create() interface gets the hashtab added.
ast_merge_contexts_and_delete() gets the local hashtab arg added.
ast_wrlock_contexts_version() is added so you can detect if someone else got a writelock between your readlocking and writelocking.
ast_hashtab_compare_contexts was made public for use in pbx_config/pbx_ael
ast_hashtab_hash_contexts was in like fashion make public.
include/asterisk/pval.h
ast_compile_ael2() interface changed to include the local hashtab table ptr.
main/features.c
For the sake of the parking context, we use ast_context_find_or_create().
main/pbx.c
I changed all the "tree" names to "table" instead. That's because the original
implementation was based on binary trees. (had a free library). Then I moved
to hashtabs. Now, the names move forward too.
refcount field added to contexts, so you can keep track of how many modules
wanted this context to exist.
Some log messages that are warnings were inflated from LOG_NOTICE to LOG_WARNING.
Added some calls to ast_verb(3,...) for debug messages
Lots of little mods to ast_context_remove_extension2, which is now excersized in ways
it was not previously; one definite bug fixed.
find_or_create was upgraded to handle both local lists/tables as well as the globals.
context_merge() was added to do the per-context merging of the old/present contexts/extens/prios into the new/proposed local list/tables
ast_merge_contexts_and_delete() was heavily modified.
ast_add_extension2() was also upgraded to handle changes.
the context_destroy() code was re-engineered to handle the new way of doing things,
by exten/prio instead of by context.
res/ael/pval.c
res/ael/ael.tab.c
res/ael/ael.tab.h
res/ael/ael.y
res/ael/ael_lex.c
res/ael/ael.flex
utils/ael_main.c
utils/extconf.c
utils/conf2ael.c
utils/Makefile
Had to change the interface to ast_compile_ael2(), to include the hashtab ptr.
This ended up involving several external apps. The main gotcha was I had to
include lock.h and hashtab.h in several places.
As a side note, I tested this stuff pretty thoroughly, I replicated the problems
originally reported by Luigi, and made triply sure that reloads worked, and everything
worked thru "stop gracefully". I found a and fixed a few bugs as I was merging into
trunk, that did not appear in my tests of bug6002.
How's this for verbose commit messages?
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@106757 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2008-03-07 18:57:57 +00:00
context = ast_context_find_or_create ( local_contexts , local_table , p - > u1 . str , registrar ) ;
2007-08-15 19:21:27 +00:00
exten = new_exten ( ) ;
exten - > context = context ;
exten - > name = strdup ( " s " ) ;
argc = 1 ;
for ( lp = p - > u2 . arglist ; lp ; lp = lp - > next ) {
/* for each arg, set up a "Set" command */
struct ael_priority * np2 = new_prio ( ) ;
np2 - > type = AEL_APPCALL ;
In these changes, I have added some explanation
of changes to the Set and MSet apps, so people aren't
so shocked and surprised when they upgrade from
1.4 to 1.6.
Also, for the sake of those upgrading from 1.4 to
1.6 with AEL, I provide automatic support for the
"old" way of using Set(), that still does the
exact same old thing with quotes and backslashes
and so on as 1.4 did, by having AEL compile in the
use of MSet() instead of Set(), everywhere it inserts
this code.
But, if the app_set var is set to 1.6 or higher,
it uses the "new", non-evaluative Set().
This only usually happens if the user manually
inserts this into the asterisk.conf file, or runs
the "make samples" command.
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@140824 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2008-09-03 14:01:27 +00:00
if ( ! ast_compat_app_set ) {
np2 - > app = strdup ( " MSet " ) ;
} else {
np2 - > app = strdup ( " Set " ) ;
}
2007-08-15 19:21:27 +00:00
snprintf ( buf , sizeof ( buf ) , " LOCAL(%s)=${ARG%d} " , lp - > u1 . str , argc + + ) ;
remove_spaces_before_equals ( buf ) ;
np2 - > appargs = strdup ( buf ) ;
2008-03-27 04:47:12 +00:00
linkprio ( exten , np2 , NULL ) ;
2007-08-15 19:21:27 +00:00
}
/* CONTAINS APPCALLS, CATCH, just like extensions... */
gen_prios ( exten , p - > u1 . str , p - > u3 . macro_statements , 0 , context ) ;
if ( exten - > return_needed ) { /* most likely, this will go away */
struct ael_priority * np2 = new_prio ( ) ;
np2 - > type = AEL_APPCALL ;
np2 - > app = strdup ( " NoOp " ) ;
snprintf ( buf , sizeof ( buf ) , " End of Macro %s-%s " , p - > u1 . str , exten - > name ) ;
np2 - > appargs = strdup ( buf ) ;
2008-03-27 04:47:12 +00:00
linkprio ( exten , np2 , NULL ) ;
2007-08-15 19:21:27 +00:00
exten - > return_target = np2 ;
}
set_priorities ( exten ) ;
attach_exten ( & exten_list , exten ) ;
break ;
case PV_GLOBALS :
/* already done */
break ;
case PV_CONTEXT :
(closes issue #6002)
Reported by: rizzo
Tested by: murf
Proposal of the changes to be made, and then an announcement of how they were accomplished:
http://lists.digium.com/pipermail/asterisk-dev/2008-February/032065.html
and:
http://lists.digium.com/pipermail/asterisk-dev/2008-March/032124.html
Here is a recap, file by file, of what I have done:
pbx/pbx_config.c
pbx/pbx_ael.c
All funcs that were passed a ptr to the context list, now will ALSO be passed a hashtab ptr to the same set.
Why? because (for the time being), the dialplan is stored in both, to facilitate a quick, low-cost move to
hash-tables to speed up dialplan processing. If it was deemed necessary to pass the context LIST, well, it
is just as necessary to have the TABLE available. This is because the list/table in question might not be
the global one, but temporary ones we would use to stage the dialplan on, and then swap into the global
position when things are ready.
We now have one external function for apps to use, "ast_context_find_or_create()" instead of the pre-existing
"find" and "create", as all existing usages used both in tandem anyway.
pbx_config, and pbx_ael, will stage the reloaded dialplan into local lists and tables, and
then call merge_contexts_and_delete, which will merge (now) existing contexts and
priorities from other registrars into this local set by copying them. Then, merge_contexts_and_delete will
lock down the contexts, swap the lists and tables, and unlock (real quick), and then
destroy the old dialplan.
chan_sip.c
chan_iax.c
chan_skinny.c
All the channel drivers that would add regcontexts now use the ast_context_find_or_create now.
chan_sip also includes a small fix to get rid of warnings about removing priorities that never got entered.
apps/app_meetme.c
apps/app_dial.c
apps/app_queue.c
All the apps that added a context/exten/priority were also modified to use ast_context_find_or_create instead.
include/asterisk/pbx.h
ast_context_create() is removed. Find_or_create_ is the new method.
ast_context_find_or_create() interface gets the hashtab added.
ast_merge_contexts_and_delete() gets the local hashtab arg added.
ast_wrlock_contexts_version() is added so you can detect if someone else got a writelock between your readlocking and writelocking.
ast_hashtab_compare_contexts was made public for use in pbx_config/pbx_ael
ast_hashtab_hash_contexts was in like fashion make public.
include/asterisk/pval.h
ast_compile_ael2() interface changed to include the local hashtab table ptr.
main/features.c
For the sake of the parking context, we use ast_context_find_or_create().
main/pbx.c
I changed all the "tree" names to "table" instead. That's because the original
implementation was based on binary trees. (had a free library). Then I moved
to hashtabs. Now, the names move forward too.
refcount field added to contexts, so you can keep track of how many modules
wanted this context to exist.
Some log messages that are warnings were inflated from LOG_NOTICE to LOG_WARNING.
Added some calls to ast_verb(3,...) for debug messages
Lots of little mods to ast_context_remove_extension2, which is now excersized in ways
it was not previously; one definite bug fixed.
find_or_create was upgraded to handle both local lists/tables as well as the globals.
context_merge() was added to do the per-context merging of the old/present contexts/extens/prios into the new/proposed local list/tables
ast_merge_contexts_and_delete() was heavily modified.
ast_add_extension2() was also upgraded to handle changes.
the context_destroy() code was re-engineered to handle the new way of doing things,
by exten/prio instead of by context.
res/ael/pval.c
res/ael/ael.tab.c
res/ael/ael.tab.h
res/ael/ael.y
res/ael/ael_lex.c
res/ael/ael.flex
utils/ael_main.c
utils/extconf.c
utils/conf2ael.c
utils/Makefile
Had to change the interface to ast_compile_ael2(), to include the hashtab ptr.
This ended up involving several external apps. The main gotcha was I had to
include lock.h and hashtab.h in several places.
As a side note, I tested this stuff pretty thoroughly, I replicated the problems
originally reported by Luigi, and made triply sure that reloads worked, and everything
worked thru "stop gracefully". I found a and fixed a few bugs as I was merging into
trunk, that did not appear in my tests of bug6002.
How's this for verbose commit messages?
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@106757 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2008-03-07 18:57:57 +00:00
context = ast_context_find_or_create ( local_contexts , local_table , p - > u1 . str , registrar ) ;
2007-08-15 19:21:27 +00:00
/* contexts contain: ignorepat, includes, switches, eswitches, extensions, */
for ( p2 = p - > u2 . statements ; p2 ; p2 = p2 - > next ) {
pval * p3 ;
char * s3 ;
switch ( p2 - > type ) {
case PV_EXTENSION :
exten = new_exten ( ) ;
exten - > name = strdup ( p2 - > u1 . str ) ;
exten - > context = context ;
if ( ( s3 = strchr ( exten - > name , ' / ' ) ) ! = 0 )
{
* s3 = 0 ;
exten - > cidmatch = s3 + 1 ;
}
if ( p2 - > u3 . hints )
exten - > hints = strdup ( p2 - > u3 . hints ) ;
exten - > regexten = p2 - > u4 . regexten ;
gen_prios ( exten , p - > u1 . str , p2 - > u2 . statements , 0 , context ) ;
if ( exten - > return_needed ) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */
struct ael_priority * np2 = new_prio ( ) ;
np2 - > type = AEL_APPCALL ;
np2 - > app = strdup ( " NoOp " ) ;
snprintf ( buf , sizeof ( buf ) , " End of Extension %s " , exten - > name ) ;
np2 - > appargs = strdup ( buf ) ;
2008-03-27 04:47:12 +00:00
linkprio ( exten , np2 , NULL ) ;
2007-08-15 19:21:27 +00:00
exten - > return_target = np2 ;
}
/* is the last priority in the extension a label? Then add a trailing no-op */
if ( exten - > plist_last & & exten - > plist_last - > type = = AEL_LABEL ) {
struct ael_priority * np2 = new_prio ( ) ;
np2 - > type = AEL_APPCALL ;
np2 - > app = strdup ( " NoOp " ) ;
snprintf ( buf , sizeof ( buf ) , " A NoOp to follow a trailing label %s " , exten - > plist_last - > origin - > u1 . str ) ;
np2 - > appargs = strdup ( buf ) ;
2008-03-27 04:47:12 +00:00
linkprio ( exten , np2 , NULL ) ;
2007-08-15 19:21:27 +00:00
}
set_priorities ( exten ) ;
attach_exten ( & exten_list , exten ) ;
break ;
case PV_IGNOREPAT :
ast_context_add_ignorepat2 ( context , p2 - > u1 . str , registrar ) ;
break ;
case PV_INCLUDES :
for ( p3 = p2 - > u1 . list ; p3 ; p3 = p3 - > next ) {
if ( p3 - > u2 . arglist ) {
snprintf ( buf , sizeof ( buf ) , " %s,%s,%s,%s,%s " ,
p3 - > u1 . str ,
p3 - > u2 . arglist - > u1 . str ,
p3 - > u2 . arglist - > next - > u1 . str ,
p3 - > u2 . arglist - > next - > next - > u1 . str ,
p3 - > u2 . arglist - > next - > next - > next - > u1 . str ) ;
ast_context_add_include2 ( context , buf , registrar ) ;
} else
ast_context_add_include2 ( context , p3 - > u1 . str , registrar ) ;
}
break ;
case PV_SWITCHES :
for ( p3 = p2 - > u1 . list ; p3 ; p3 = p3 - > next ) {
char * c = strchr ( p3 - > u1 . str , ' / ' ) ;
if ( c ) {
* c = ' \0 ' ;
c + + ;
} else
c = " " ;
ast_context_add_switch2 ( context , p3 - > u1 . str , c , 0 , registrar ) ;
}
break ;
case PV_ESWITCHES :
for ( p3 = p2 - > u1 . list ; p3 ; p3 = p3 - > next ) {
char * c = strchr ( p3 - > u1 . str , ' / ' ) ;
if ( c ) {
* c = ' \0 ' ;
c + + ;
} else
c = " " ;
ast_context_add_switch2 ( context , p3 - > u1 . str , c , 1 , registrar ) ;
}
break ;
default :
break ;
}
}
break ;
default :
/* huh? what? */
break ;
}
}
/* moved these from being done after a macro or extension were processed,
to after all processing is done , for the sake of fixing gotos to labels inside cases . . . */
/* I guess this would be considered 2nd pass of compiler now... */
fix_gotos_in_extensions ( exten_list ) ; /* find and fix extension ref in gotos to labels that are in case statements */
add_extensions ( exten_list ) ; /* actually makes calls to create priorities in ast_contexts -- feeds dialplan to asterisk */
destroy_extensions ( exten_list ) ; /* all that remains is an empty husk, discard of it as is proper */
}
/* DESTROY the PVAL tree ============================================================================ */
void destroy_pval_item ( pval * item )
{
if ( item = = NULL ) {
ast_log ( LOG_WARNING , " null item \n " ) ;
return ;
}
if ( item - > filename )
free ( item - > filename ) ;
switch ( item - > type ) {
case PV_WORD :
/* fields: item->u1.str == string associated with this (word). */
if ( item - > u1 . str )
free ( item - > u1 . str ) ;
if ( item - > u2 . arglist )
destroy_pval ( item - > u2 . arglist ) ;
break ;
case PV_MACRO :
/* fields: item->u1.str == name of macro
item - > u2 . arglist = = pval list of PV_WORD arguments of macro , as given by user
item - > u2 . arglist - > u1 . str = = argument
item - > u2 . arglist - > next = = next arg
item - > u3 . macro_statements = = pval list of statements in macro body .
*/
destroy_pval ( item - > u2 . arglist ) ;
if ( item - > u1 . str )
free ( item - > u1 . str ) ;
destroy_pval ( item - > u3 . macro_statements ) ;
break ;
case PV_CONTEXT :
/* fields: item->u1.str == name of context
item - > u2 . statements = = pval list of statements in context body
item - > u3 . abstract = = int 1 if an abstract keyword were present
*/
if ( item - > u1 . str )
free ( item - > u1 . str ) ;
destroy_pval ( item - > u2 . statements ) ;
break ;
case PV_MACRO_CALL :
/* fields: item->u1.str == name of macro to call
item - > u2 . arglist = = pval list of PV_WORD arguments of macro call , as given by user
item - > u2 . arglist - > u1 . str = = argument
item - > u2 . arglist - > next = = next arg
*/
if ( item - > u1 . str )
free ( item - > u1 . str ) ;
destroy_pval ( item - > u2 . arglist ) ;
break ;
case PV_APPLICATION_CALL :
/* fields: item->u1.str == name of application to call
item - > u2 . arglist = = pval list of PV_WORD arguments of macro call , as given by user
item - > u2 . arglist - > u1 . str = = argument
item - > u2 . arglist - > next = = next arg
*/
if ( item - > u1 . str )
free ( item - > u1 . str ) ;
destroy_pval ( item - > u2 . arglist ) ;
break ;
case PV_CASE :
/* fields: item->u1.str == value of case
item - > u2 . statements = = pval list of statements under the case
*/
if ( item - > u1 . str )
free ( item - > u1 . str ) ;
destroy_pval ( item - > u2 . statements ) ;
break ;
case PV_PATTERN :
/* fields: item->u1.str == value of case
item - > u2 . statements = = pval list of statements under the case
*/
if ( item - > u1 . str )
free ( item - > u1 . str ) ;
destroy_pval ( item - > u2 . statements ) ;
break ;
case PV_DEFAULT :
/* fields:
item - > u2 . statements = = pval list of statements under the case
*/
destroy_pval ( item - > u2 . statements ) ;
break ;
case PV_CATCH :
/* fields: item->u1.str == name of extension to catch
item - > u2 . statements = = pval list of statements in context body
*/
if ( item - > u1 . str )
free ( item - > u1 . str ) ;
destroy_pval ( item - > u2 . statements ) ;
break ;
case PV_SWITCHES :
/* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
*/
destroy_pval ( item - > u1 . list ) ;
break ;
case PV_ESWITCHES :
/* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
*/
destroy_pval ( item - > u1 . list ) ;
break ;
case PV_INCLUDES :
/* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
item - > u2 . arglist = = pval list of 4 PV_WORD elements for time values
*/
destroy_pval ( item - > u1 . list ) ;
break ;
case PV_STATEMENTBLOCK :
/* fields: item->u1.list == pval list of statements in block, one per entry in the list
*/
destroy_pval ( item - > u1 . list ) ;
break ;
case PV_LOCALVARDEC :
case PV_VARDEC :
/* fields: item->u1.str == variable name
item - > u2 . val = = variable value to assign
*/
if ( item - > u1 . str )
free ( item - > u1 . str ) ;
if ( item - > u2 . val )
free ( item - > u2 . val ) ;
break ;
case PV_GOTO :
/* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user.
item - > u1 . list - > u1 . str = = where the data on a PV_WORD will always be .
*/
destroy_pval ( item - > u1 . list ) ;
break ;
case PV_LABEL :
/* fields: item->u1.str == label name
*/
if ( item - > u1 . str )
free ( item - > u1 . str ) ;
break ;
case PV_FOR :
/* fields: item->u1.for_init == a string containing the initalizer
item - > u2 . for_test = = a string containing the loop test
item - > u3 . for_inc = = a string containing the loop increment
item - > u4 . for_statements = = a pval list of statements in the for ( )
*/
if ( item - > u1 . for_init )
free ( item - > u1 . for_init ) ;
if ( item - > u2 . for_test )
free ( item - > u2 . for_test ) ;
if ( item - > u3 . for_inc )
free ( item - > u3 . for_inc ) ;
destroy_pval ( item - > u4 . for_statements ) ;
break ;
case PV_WHILE :
/* fields: item->u1.str == the while conditional, as supplied by user
item - > u2 . statements = = a pval list of statements in the while ( )
*/
if ( item - > u1 . str )
free ( item - > u1 . str ) ;
destroy_pval ( item - > u2 . statements ) ;
break ;
case PV_BREAK :
/* fields: none
*/
break ;
case PV_RETURN :
/* fields: none
*/
break ;
case PV_CONTINUE :
/* fields: none
*/
break ;
case PV_IFTIME :
/* fields: item->u1.list == the 4 time values, in PV_WORD structs, linked list
item - > u2 . statements = = a pval list of statements in the if ( )
item - > u3 . else_statements = = a pval list of statements in the else
( could be zero )
*/
destroy_pval ( item - > u1 . list ) ;
destroy_pval ( item - > u2 . statements ) ;
if ( item - > u3 . else_statements ) {
destroy_pval ( item - > u3 . else_statements ) ;
}
break ;
case PV_RANDOM :
/* fields: item->u1.str == the random percentage, as supplied by user
item - > u2 . statements = = a pval list of statements in the true part ( )
item - > u3 . else_statements = = a pval list of statements in the else
( could be zero )
fall thru to If */
case PV_IF :
/* fields: item->u1.str == the if conditional, as supplied by user
item - > u2 . statements = = a pval list of statements in the if ( )
item - > u3 . else_statements = = a pval list of statements in the else
( could be zero )
*/
if ( item - > u1 . str )
free ( item - > u1 . str ) ;
destroy_pval ( item - > u2 . statements ) ;
if ( item - > u3 . else_statements ) {
destroy_pval ( item - > u3 . else_statements ) ;
}
break ;
case PV_SWITCH :
/* fields: item->u1.str == the switch expression
item - > u2 . statements = = a pval list of statements in the switch ,
( will be case statements , most likely ! )
*/
if ( item - > u1 . str )
free ( item - > u1 . str ) ;
destroy_pval ( item - > u2 . statements ) ;
break ;
case PV_EXTENSION :
/* fields: item->u1.str == the extension name, label, whatever it's called
item - > u2 . statements = = a pval list of statements in the extension
item - > u3 . hints = = a char * hint argument
item - > u4 . regexten = = an int boolean . non - zero says that regexten was specified
*/
if ( item - > u1 . str )
free ( item - > u1 . str ) ;
if ( item - > u3 . hints )
free ( item - > u3 . hints ) ;
destroy_pval ( item - > u2 . statements ) ;
break ;
case PV_IGNOREPAT :
/* fields: item->u1.str == the ignorepat data
*/
if ( item - > u1 . str )
free ( item - > u1 . str ) ;
break ;
case PV_GLOBALS :
/* fields: item->u1.statements == pval list of statements, usually vardecs
*/
destroy_pval ( item - > u1 . statements ) ;
break ;
}
free ( item ) ;
}
void destroy_pval ( pval * item )
{
pval * i , * nxt ;
for ( i = item ; i ; i = nxt ) {
nxt = i - > next ;
destroy_pval_item ( i ) ;
}
}
# ifdef AAL_ARGCHECK
static char * ael_funclist [ ] =
{
" AGENT " ,
" ARRAY " ,
" BASE64_DECODE " ,
" BASE64_ENCODE " ,
" CALLERID " ,
" CDR " ,
" CHANNEL " ,
" CHECKSIPDOMAIN " ,
" CHECK_MD5 " ,
" CURL " ,
" CUT " ,
" DB " ,
" DB_EXISTS " ,
" DUNDILOOKUP " ,
" ENUMLOOKUP " ,
" ENV " ,
" EVAL " ,
" EXISTS " ,
" FIELDQTY " ,
" FILTER " ,
" GROUP " ,
" GROUP_COUNT " ,
" GROUP_LIST " ,
" GROUP_MATCH_COUNT " ,
" IAXPEER " ,
" IF " ,
" IFTIME " ,
" ISNULL " ,
" KEYPADHASH " ,
" LANGUAGE " ,
" LEN " ,
" MATH " ,
" MD5 " ,
" MUSICCLASS " ,
" QUEUEAGENTCOUNT " ,
" QUEUE_MEMBER_COUNT " ,
" QUEUE_MEMBER_LIST " ,
" QUOTE " ,
" RAND " ,
" REGEX " ,
" SET " ,
" SHA1 " ,
" SIPCHANINFO " ,
" SIPPEER " ,
" SIP_HEADER " ,
" SORT " ,
" STAT " ,
" STRFTIME " ,
" STRPTIME " ,
" TIMEOUT " ,
" TXTCIDNAME " ,
" URIDECODE " ,
" URIENCODE " ,
" VMCOUNT "
} ;
int ael_is_funcname ( char * name )
{
int s , t ;
t = sizeof ( ael_funclist ) / sizeof ( char * ) ;
s = 0 ;
while ( ( s < t ) & & strcasecmp ( name , ael_funclist [ s ] ) )
s + + ;
if ( s < t )
return 1 ;
else
return 0 ;
}
# endif
/* PVAL PI */
/* ----------------- implementation ------------------- */
int pvalCheckType ( pval * p , char * funcname , pvaltype type )
{
if ( p - > type ! = type )
{
ast_log ( LOG_ERROR , " Func: %s the pval passed is not appropriate for this function! \n " , funcname ) ;
return 0 ;
}
return 1 ;
}
pval * pvalCreateNode ( pvaltype type )
{
pval * p = calloc ( 1 , sizeof ( pval ) ) ; /* why, oh why, don't I use ast_calloc? Way, way, way too messy if I do! */
p - > type = type ; /* remember, this can be used externally or internally to asterisk */
return p ;
}
pvaltype pvalObjectGetType ( pval * p )
{
return p - > type ;
}
void pvalWordSetString ( pval * p , char * str )
{
if ( ! pvalCheckType ( p , " pvalWordSetString " , PV_WORD ) )
return ;
p - > u1 . str = str ;
}
char * pvalWordGetString ( pval * p )
{
if ( ! pvalCheckType ( p , " pvalWordGetString " , PV_WORD ) )
return 0 ;
return p - > u1 . str ;
}
void pvalMacroSetName ( pval * p , char * name )
{
if ( ! pvalCheckType ( p , " pvalMacroSetName " , PV_MACRO ) )
return ;
p - > u1 . str = name ;
}
char * pvalMacroGetName ( pval * p )
{
if ( ! pvalCheckType ( p , " pvalMacroGetName " , PV_MACRO ) )
return 0 ;
return p - > u1 . str ;
}
void pvalMacroSetArglist ( pval * p , pval * arglist )
{
if ( ! pvalCheckType ( p , " pvalMacroSetArglist " , PV_MACRO ) )
return ;
p - > u2 . arglist = arglist ;
}
void pvalMacroAddArg ( pval * p , pval * arg ) /* single arg only! */
{
if ( ! pvalCheckType ( p , " pvalMacroAddArg " , PV_MACRO ) )
return ;
if ( ! p - > u2 . arglist )
p - > u2 . arglist = arg ;
else
linku1 ( p - > u2 . arglist , arg ) ;
}
pval * pvalMacroWalkArgs ( pval * p , pval * * arg )
{
if ( ! pvalCheckType ( p , " pvalMacroWalkArgs " , PV_MACRO ) )
return 0 ;
if ( ! ( * arg ) )
* arg = p - > u2 . arglist ;
else {
* arg = ( * arg ) - > next ;
}
return * arg ;
}
void pvalMacroAddStatement ( pval * p , pval * statement )
{
if ( ! pvalCheckType ( p , " pvalMacroAddStatement " , PV_MACRO ) )
return ;
if ( ! p - > u3 . macro_statements )
p - > u3 . macro_statements = statement ;
else
linku1 ( p - > u3 . macro_statements , statement ) ;
}
pval * pvalMacroWalkStatements ( pval * p , pval * * next_statement )
{
if ( ! pvalCheckType ( p , " pvalMacroWalkStatements " , PV_MACRO ) )
return 0 ;
if ( ! ( * next_statement ) )
* next_statement = p - > u3 . macro_statements ;
else {
* next_statement = ( * next_statement ) - > next ;
}
return * next_statement ;
}
void pvalContextSetName ( pval * p , char * name )
{
if ( ! pvalCheckType ( p , " pvalContextSetName " , PV_CONTEXT ) )
return ;
p - > u1 . str = name ;
}
char * pvalContextGetName ( pval * p )
{
if ( ! pvalCheckType ( p , " pvalContextGetName " , PV_CONTEXT ) )
return 0 ;
return p - > u1 . str ;
}
void pvalContextSetAbstract ( pval * p )
{
if ( ! pvalCheckType ( p , " pvalContextSetAbstract " , PV_CONTEXT ) )
return ;
p - > u3 . abstract = 1 ;
}
void pvalContextUnsetAbstract ( pval * p )
{
if ( ! pvalCheckType ( p , " pvalContextUnsetAbstract " , PV_CONTEXT ) )
return ;
p - > u3 . abstract = 0 ;
}
int pvalContextGetAbstract ( pval * p )
{
if ( ! pvalCheckType ( p , " pvalContextGetAbstract " , PV_CONTEXT ) )
return 0 ;
return p - > u3 . abstract ;
}
void pvalContextAddStatement ( pval * p , pval * statement ) /* this includes SWITCHES, INCLUDES, IGNOREPAT, etc */
{
if ( ! pvalCheckType ( p , " pvalContextAddStatement " , PV_CONTEXT ) )
return ;
if ( ! p - > u2 . statements )
p - > u2 . statements = statement ;
else
linku1 ( p - > u2 . statements , statement ) ;
}
pval * pvalContextWalkStatements ( pval * p , pval * * statements )
{
if ( ! pvalCheckType ( p , " pvalContextWalkStatements " , PV_CONTEXT ) )
return 0 ;
if ( ! ( * statements ) )
* statements = p - > u2 . statements ;
else {
* statements = ( * statements ) - > next ;
}
return * statements ;
}
void pvalMacroCallSetMacroName ( pval * p , char * name )
{
if ( ! pvalCheckType ( p , " pvalMacroCallSetMacroName " , PV_MACRO_CALL ) )
return ;
p - > u1 . str = name ;
}
char * pvalMacroCallGetMacroName ( pval * p )
{
if ( ! pvalCheckType ( p , " pvalMacroCallGetMacroName " , PV_MACRO_CALL ) )
return 0 ;
return p - > u1 . str ;
}
void pvalMacroCallSetArglist ( pval * p , pval * arglist )
{
if ( ! pvalCheckType ( p , " pvalMacroCallSetArglist " , PV_MACRO_CALL ) )
return ;
p - > u2 . arglist = arglist ;
}
void pvalMacroCallAddArg ( pval * p , pval * arg )
{
if ( ! pvalCheckType ( p , " pvalMacroCallGetAddArg " , PV_MACRO_CALL ) )
return ;
if ( ! p - > u2 . arglist )
p - > u2 . arglist = arg ;
else
linku1 ( p - > u2 . arglist , arg ) ;
}
pval * pvalMacroCallWalkArgs ( pval * p , pval * * args )
{
if ( ! pvalCheckType ( p , " pvalMacroCallWalkArgs " , PV_MACRO_CALL ) )
return 0 ;
if ( ! ( * args ) )
* args = p - > u2 . arglist ;
else {
* args = ( * args ) - > next ;
}
return * args ;
}
void pvalAppCallSetAppName ( pval * p , char * name )
{
if ( ! pvalCheckType ( p , " pvalAppCallSetAppName " , PV_APPLICATION_CALL ) )
return ;
p - > u1 . str = name ;
}
char * pvalAppCallGetAppName ( pval * p )
{
if ( ! pvalCheckType ( p , " pvalAppCallGetAppName " , PV_APPLICATION_CALL ) )
return 0 ;
return p - > u1 . str ;
}
void pvalAppCallSetArglist ( pval * p , pval * arglist )
{
if ( ! pvalCheckType ( p , " pvalAppCallSetArglist " , PV_APPLICATION_CALL ) )
return ;
p - > u2 . arglist = arglist ;
}
void pvalAppCallAddArg ( pval * p , pval * arg )
{
if ( ! pvalCheckType ( p , " pvalAppCallAddArg " , PV_APPLICATION_CALL ) )
return ;
if ( ! p - > u2 . arglist )
p - > u2 . arglist = arg ;
else
linku1 ( p - > u2 . arglist , arg ) ;
}
pval * pvalAppCallWalkArgs ( pval * p , pval * * args )
{
if ( ! pvalCheckType ( p , " pvalAppCallWalkArgs " , PV_APPLICATION_CALL ) )
return 0 ;
if ( ! ( * args ) )
* args = p - > u2 . arglist ;
else {
* args = ( * args ) - > next ;
}
return * args ;
}
void pvalCasePatSetVal ( pval * p , char * val )
{
if ( ! pvalCheckType ( p , " pvalAppCallWalkArgs " , PV_APPLICATION_CALL ) )
return ;
p - > u1 . str = val ;
}
char * pvalCasePatGetVal ( pval * p )
{
return p - > u1 . str ;
}
void pvalCasePatDefAddStatement ( pval * p , pval * statement )
{
if ( ! p - > u2 . arglist )
p - > u2 . statements = statement ;
else
linku1 ( p - > u2 . statements , statement ) ;
}
pval * pvalCasePatDefWalkStatements ( pval * p , pval * * statement )
{
if ( ! ( * statement ) )
* statement = p - > u2 . statements ;
else {
* statement = ( * statement ) - > next ;
}
return * statement ;
}
void pvalCatchSetExtName ( pval * p , char * name )
{
if ( ! pvalCheckType ( p , " pvalCatchSetExtName " , PV_CATCH ) )
return ;
p - > u1 . str = name ;
}
char * pvalCatchGetExtName ( pval * p )
{
if ( ! pvalCheckType ( p , " pvalCatchGetExtName " , PV_CATCH ) )
return 0 ;
return p - > u1 . str ;
}
void pvalCatchSetStatement ( pval * p , pval * statement )
{
if ( ! pvalCheckType ( p , " pvalCatchSetStatement " , PV_CATCH ) )
return ;
p - > u2 . statements = statement ;
}
pval * pvalCatchGetStatement ( pval * p )
{
if ( ! pvalCheckType ( p , " pvalCatchGetStatement " , PV_CATCH ) )
return 0 ;
return p - > u2 . statements ;
}
void pvalSwitchesAddSwitch ( pval * p , char * name )
{
pval * s ;
if ( ! pvalCheckType ( p , " pvalSwitchesAddSwitch " , PV_SWITCHES ) )
return ;
s = pvalCreateNode ( PV_WORD ) ;
s - > u1 . str = name ;
p - > u1 . list = linku1 ( p - > u1 . list , s ) ;
}
char * pvalSwitchesWalkNames ( pval * p , pval * * next_item )
{
if ( ! pvalCheckType ( p , " pvalSwitchesWalkNames " , PV_SWITCHES ) )
return 0 ;
if ( ! ( * next_item ) )
* next_item = p - > u1 . list ;
else {
* next_item = ( * next_item ) - > next ;
}
return ( * next_item ) - > u1 . str ;
}
void pvalESwitchesAddSwitch ( pval * p , char * name )
{
pval * s ;
if ( ! pvalCheckType ( p , " pvalESwitchesAddSwitch " , PV_ESWITCHES ) )
return ;
s = pvalCreateNode ( PV_WORD ) ;
s - > u1 . str = name ;
p - > u1 . list = linku1 ( p - > u1 . list , s ) ;
}
char * pvalESwitchesWalkNames ( pval * p , pval * * next_item )
{
if ( ! pvalCheckType ( p , " pvalESwitchesWalkNames " , PV_ESWITCHES ) )
return 0 ;
if ( ! ( * next_item ) )
* next_item = p - > u1 . list ;
else {
* next_item = ( * next_item ) - > next ;
}
return ( * next_item ) - > u1 . str ;
}
void pvalIncludesAddInclude ( pval * p , const char * include )
{
pval * s ;
if ( ! pvalCheckType ( p , " pvalIncludesAddSwitch " , PV_INCLUDES ) )
return ;
s = pvalCreateNode ( PV_WORD ) ;
s - > u1 . str = ( char * ) include ;
p - > u1 . list = linku1 ( p - > u1 . list , s ) ;
}
/* an include is a WORD with string set to path */
void pvalIncludesAddIncludeWithTimeConstraints ( pval * p , const char * include , char * hour_range , char * dom_range , char * dow_range , char * month_range )
{
pval * hr = pvalCreateNode ( PV_WORD ) ;
pval * dom = pvalCreateNode ( PV_WORD ) ;
pval * dow = pvalCreateNode ( PV_WORD ) ;
pval * mon = pvalCreateNode ( PV_WORD ) ;
pval * s = pvalCreateNode ( PV_WORD ) ;
if ( ! pvalCheckType ( p , " pvalIncludeAddIncludeWithTimeConstraints " , PV_INCLUDES ) )
return ;
s - > u1 . str = ( char * ) include ;
p - > u1 . list = linku1 ( p - > u1 . list , s ) ;
hr - > u1 . str = hour_range ;
dom - > u1 . str = dom_range ;
dow - > u1 . str = dow_range ;
mon - > u1 . str = month_range ;
s - > u2 . arglist = hr ;
hr - > next = dom ;
dom - > next = dow ;
dow - > next = mon ;
mon - > next = 0 ;
}
/* is this right??? come back and correct it */ /*the ptr is to the WORD */
void pvalIncludeGetTimeConstraints ( pval * p , char * * hour_range , char * * dom_range , char * * dow_range , char * * month_range )
{
if ( ! pvalCheckType ( p , " pvalIncludeGetTimeConstraints " , PV_WORD ) )
return ;
if ( p - > u2 . arglist ) {
* hour_range = p - > u2 . arglist - > u1 . str ;
* dom_range = p - > u2 . arglist - > next - > u1 . str ;
* dow_range = p - > u2 . arglist - > next - > next - > u1 . str ;
* month_range = p - > u2 . arglist - > next - > next - > next - > u1 . str ;
} else {
* hour_range = 0 ;
* dom_range = 0 ;
* dow_range = 0 ;
* month_range = 0 ;
}
}
/* is this right??? come back and correct it */ /*the ptr is to the WORD */
char * pvalIncludesWalk ( pval * p , pval * * next_item )
{
if ( ! pvalCheckType ( p , " pvalIncludesWalk " , PV_INCLUDES ) )
return 0 ;
if ( ! ( * next_item ) )
* next_item = p - > u1 . list ;
else {
* next_item = ( * next_item ) - > next ;
}
return ( * next_item ) - > u1 . str ;
}
void pvalStatementBlockAddStatement ( pval * p , pval * statement )
{
if ( ! pvalCheckType ( p , " pvalStatementBlockAddStatement " , PV_STATEMENTBLOCK ) )
return ;
p - > u1 . list = linku1 ( p - > u1 . list , statement ) ;
}
pval * pvalStatementBlockWalkStatements ( pval * p , pval * * next_statement )
{
if ( ! pvalCheckType ( p , " pvalStatementBlockWalkStatements " , PV_STATEMENTBLOCK ) )
return 0 ;
if ( ! ( * next_statement ) )
* next_statement = p - > u1 . list ;
else {
* next_statement = ( * next_statement ) - > next ;
}
return * next_statement ;
}
void pvalVarDecSetVarname ( pval * p , char * name )
{
if ( ! pvalCheckType ( p , " pvalVarDecSetVarname " , PV_VARDEC ) )
return ;
p - > u1 . str = name ;
}
void pvalVarDecSetValue ( pval * p , char * value )
{
if ( ! pvalCheckType ( p , " pvalVarDecSetValue " , PV_VARDEC ) )
return ;
p - > u2 . val = value ;
}
char * pvalVarDecGetVarname ( pval * p )
{
if ( ! pvalCheckType ( p , " pvalVarDecGetVarname " , PV_VARDEC ) )
return 0 ;
return p - > u1 . str ;
}
char * pvalVarDecGetValue ( pval * p )
{
if ( ! pvalCheckType ( p , " pvalVarDecGetValue " , PV_VARDEC ) )
return 0 ;
return p - > u2 . val ;
}
void pvalGotoSetTarget ( pval * p , char * context , char * exten , char * label )
{
pval * con , * ext , * pri ;
if ( ! pvalCheckType ( p , " pvalGotoSetTarget " , PV_GOTO ) )
return ;
if ( context & & strlen ( context ) ) {
con = pvalCreateNode ( PV_WORD ) ;
ext = pvalCreateNode ( PV_WORD ) ;
pri = pvalCreateNode ( PV_WORD ) ;
con - > u1 . str = context ;
ext - > u1 . str = exten ;
pri - > u1 . str = label ;
con - > next = ext ;
ext - > next = pri ;
p - > u1 . list = con ;
} else if ( exten & & strlen ( exten ) ) {
ext = pvalCreateNode ( PV_WORD ) ;
pri = pvalCreateNode ( PV_WORD ) ;
ext - > u1 . str = exten ;
pri - > u1 . str = label ;
ext - > next = pri ;
p - > u1 . list = ext ;
} else {
pri = pvalCreateNode ( PV_WORD ) ;
pri - > u1 . str = label ;
p - > u1 . list = pri ;
}
}
void pvalGotoGetTarget ( pval * p , char * * context , char * * exten , char * * label )
{
if ( ! pvalCheckType ( p , " pvalGotoGetTarget " , PV_GOTO ) )
return ;
if ( p - > u1 . list & & p - > u1 . list - > next & & p - > u1 . list - > next - > next ) {
* context = p - > u1 . list - > u1 . str ;
* exten = p - > u1 . list - > next - > u1 . str ;
* label = p - > u1 . list - > next - > next - > u1 . str ;
} else if ( p - > u1 . list & & p - > u1 . list - > next ) {
* exten = p - > u1 . list - > u1 . str ;
* label = p - > u1 . list - > next - > u1 . str ;
* context = 0 ;
} else if ( p - > u1 . list ) {
* label = p - > u1 . list - > u1 . str ;
* context = 0 ;
* exten = 0 ;
} else {
* context = 0 ;
* exten = 0 ;
* label = 0 ;
}
}
void pvalLabelSetName ( pval * p , char * name )
{
if ( ! pvalCheckType ( p , " pvalLabelSetName " , PV_LABEL ) )
return ;
p - > u1 . str = name ;
}
char * pvalLabelGetName ( pval * p )
{
if ( ! pvalCheckType ( p , " pvalLabelGetName " , PV_LABEL ) )
return 0 ;
return p - > u1 . str ;
}
void pvalForSetInit ( pval * p , char * init )
{
if ( ! pvalCheckType ( p , " pvalForSetInit " , PV_FOR ) )
return ;
p - > u1 . for_init = init ;
}
void pvalForSetTest ( pval * p , char * test )
{
if ( ! pvalCheckType ( p , " pvalForSetTest " , PV_FOR ) )
return ;
p - > u2 . for_test = test ;
}
void pvalForSetInc ( pval * p , char * inc )
{
if ( ! pvalCheckType ( p , " pvalForSetInc " , PV_FOR ) )
return ;
p - > u3 . for_inc = inc ;
}
void pvalForSetStatement ( pval * p , pval * statement )
{
if ( ! pvalCheckType ( p , " pvalForSetStatement " , PV_FOR ) )
return ;
p - > u4 . for_statements = statement ;
}
char * pvalForGetInit ( pval * p )
{
if ( ! pvalCheckType ( p , " pvalForGetInit " , PV_FOR ) )
return 0 ;
return p - > u1 . for_init ;
}
char * pvalForGetTest ( pval * p )
{
if ( ! pvalCheckType ( p , " pvalForGetTest " , PV_FOR ) )
return 0 ;
return p - > u2 . for_test ;
}
char * pvalForGetInc ( pval * p )
{
if ( ! pvalCheckType ( p , " pvalForGetInc " , PV_FOR ) )
return 0 ;
return p - > u3 . for_inc ;
}
pval * pvalForGetStatement ( pval * p )
{
if ( ! pvalCheckType ( p , " pvalForGetStatement " , PV_FOR ) )
return 0 ;
return p - > u4 . for_statements ;
}
void pvalIfSetCondition ( pval * p , char * expr )
{
if ( ! pvalCheckType ( p , " pvalIfSetCondition " , PV_IF ) )
return ;
p - > u1 . str = expr ;
}
char * pvalIfGetCondition ( pval * p )
{
if ( ! pvalCheckType ( p , " pvalIfGetCondition " , PV_IFTIME ) )
return 0 ;
return p - > u1 . str ;
}
void pvalIfTimeSetCondition ( pval * p , char * hour_range , char * dow_range , char * dom_range , char * mon_range ) /* time range format: 24-hour format begin-end|dow range|dom range|month range */
{
pval * hr = pvalCreateNode ( PV_WORD ) ;
pval * dow = pvalCreateNode ( PV_WORD ) ;
pval * dom = pvalCreateNode ( PV_WORD ) ;
pval * mon = pvalCreateNode ( PV_WORD ) ;
if ( ! pvalCheckType ( p , " pvalIfTimeSetCondition " , PV_IFTIME ) )
return ;
pvalWordSetString ( hr , hour_range ) ;
pvalWordSetString ( dow , dow_range ) ;
pvalWordSetString ( dom , dom_range ) ;
pvalWordSetString ( mon , mon_range ) ;
dom - > next = mon ;
dow - > next = dom ;
hr - > next = dow ;
p - > u1 . list = hr ;
}
/* is this right??? come back and correct it */
void pvalIfTimeGetCondition ( pval * p , char * * hour_range , char * * dow_range , char * * dom_range , char * * month_range )
{
if ( ! pvalCheckType ( p , " pvalIfTimeGetCondition " , PV_IFTIME ) )
return ;
* hour_range = p - > u1 . list - > u1 . str ;
* dow_range = p - > u1 . list - > next - > u1 . str ;
* dom_range = p - > u1 . list - > next - > next - > u1 . str ;
* month_range = p - > u1 . list - > next - > next - > next - > u1 . str ;
}
void pvalRandomSetCondition ( pval * p , char * percent )
{
if ( ! pvalCheckType ( p , " pvalRandomSetCondition " , PV_RANDOM ) )
return ;
p - > u1 . str = percent ;
}
char * pvalRandomGetCondition ( pval * p )
{
if ( ! pvalCheckType ( p , " pvalRandomGetCondition " , PV_RANDOM ) )
return 0 ;
return p - > u1 . str ;
}
void pvalConditionalSetThenStatement ( pval * p , pval * statement )
{
p - > u2 . statements = statement ;
}
void pvalConditionalSetElseStatement ( pval * p , pval * statement )
{
p - > u3 . else_statements = statement ;
}
pval * pvalConditionalGetThenStatement ( pval * p )
{
return p - > u2 . statements ;
}
pval * pvalConditionalGetElseStatement ( pval * p )
{
return p - > u3 . else_statements ;
}
void pvalSwitchSetTestexpr ( pval * p , char * expr )
{
if ( ! pvalCheckType ( p , " pvalSwitchSetTestexpr " , PV_SWITCH ) )
return ;
p - > u1 . str = expr ;
}
char * pvalSwitchGetTestexpr ( pval * p )
{
if ( ! pvalCheckType ( p , " pvalSwitchGetTestexpr " , PV_SWITCH ) )
return 0 ;
return p - > u1 . str ;
}
void pvalSwitchAddCase ( pval * p , pval * Case )
{
if ( ! pvalCheckType ( p , " pvalSwitchAddCase " , PV_SWITCH ) )
return ;
if ( ! pvalCheckType ( Case , " pvalSwitchAddCase " , PV_CASE ) )
return ;
if ( ! p - > u2 . statements )
p - > u2 . statements = Case ;
else
linku1 ( p - > u2 . statements , Case ) ;
}
pval * pvalSwitchWalkCases ( pval * p , pval * * next_case )
{
if ( ! pvalCheckType ( p , " pvalSwitchWalkCases " , PV_SWITCH ) )
return 0 ;
if ( ! ( * next_case ) )
* next_case = p - > u2 . statements ;
else {
* next_case = ( * next_case ) - > next ;
}
return * next_case ;
}
void pvalExtenSetName ( pval * p , char * name )
{
if ( ! pvalCheckType ( p , " pvalExtenSetName " , PV_EXTENSION ) )
return ;
p - > u1 . str = name ;
}
char * pvalExtenGetName ( pval * p )
{
if ( ! pvalCheckType ( p , " pvalExtenGetName " , PV_EXTENSION ) )
return 0 ;
return p - > u1 . str ;
}
void pvalExtenSetRegexten ( pval * p )
{
if ( ! pvalCheckType ( p , " pvalExtenSetRegexten " , PV_EXTENSION ) )
return ;
p - > u4 . regexten = 1 ;
}
void pvalExtenUnSetRegexten ( pval * p )
{
if ( ! pvalCheckType ( p , " pvalExtenUnSetRegexten " , PV_EXTENSION ) )
return ;
p - > u4 . regexten = 0 ;
}
int pvalExtenGetRegexten ( pval * p )
{
if ( ! pvalCheckType ( p , " pvalExtenGetRegexten " , PV_EXTENSION ) )
return 0 ;
return p - > u4 . regexten ;
}
void pvalExtenSetHints ( pval * p , char * hints )
{
if ( ! pvalCheckType ( p , " pvalExtenSetHints " , PV_EXTENSION ) )
return ;
p - > u3 . hints = hints ;
}
char * pvalExtenGetHints ( pval * p )
{
if ( ! pvalCheckType ( p , " pvalExtenGetHints " , PV_EXTENSION ) )
return 0 ;
return p - > u3 . hints ;
}
void pvalExtenSetStatement ( pval * p , pval * statement )
{
if ( ! pvalCheckType ( p , " pvalExtenSetStatement " , PV_EXTENSION ) )
return ;
p - > u2 . statements = statement ;
}
pval * pvalExtenGetStatement ( pval * p )
{
if ( ! pvalCheckType ( p , " pvalExtenGetStatement " , PV_EXTENSION ) )
return 0 ;
return p - > u2 . statements ;
}
void pvalIgnorePatSetPattern ( pval * p , char * pat )
{
if ( ! pvalCheckType ( p , " pvalIgnorePatSetPattern " , PV_IGNOREPAT ) )
return ;
p - > u1 . str = pat ;
}
char * pvalIgnorePatGetPattern ( pval * p )
{
if ( ! pvalCheckType ( p , " pvalIgnorePatGetPattern " , PV_IGNOREPAT ) )
return 0 ;
return p - > u1 . str ;
}
void pvalGlobalsAddStatement ( pval * p , pval * statement )
{
if ( p - > type ! = PV_GLOBALS ) {
ast_log ( LOG_ERROR , " pvalGlobalsAddStatement called where first arg is not a Globals! \n " ) ;
} else {
if ( ! p - > u1 . statements ) {
p - > u1 . statements = statement ;
} else {
p - > u1 . statements = linku1 ( p - > u1 . statements , statement ) ;
}
}
}
pval * pvalGlobalsWalkStatements ( pval * p , pval * * next_statement )
{
if ( ! pvalCheckType ( p , " pvalGlobalsWalkStatements " , PV_GLOBALS ) )
return 0 ;
if ( ! next_statement ) {
* next_statement = p ;
return p ;
} else {
* next_statement = ( * next_statement ) - > next ;
return ( * next_statement ) - > next ;
}
}
void pvalTopLevAddObject ( pval * p , pval * contextOrObj )
{
if ( p ) {
linku1 ( p , contextOrObj ) ;
} else {
ast_log ( LOG_ERROR , " First arg to pvalTopLevel is NULL! \n " ) ;
}
}
pval * pvalTopLevWalkObjects ( pval * p , pval * * next_obj )
{
if ( ! next_obj ) {
* next_obj = p ;
return p ;
} else {
* next_obj = ( * next_obj ) - > next ;
return ( * next_obj ) - > next ;
}
}
/* append second element to the list in the first one via next pointers */
pval * linku1 ( pval * head , pval * tail )
{
if ( ! head )
return tail ;
if ( tail ) {
if ( ! head - > next ) {
head - > next = tail ;
} else {
head - > u1_last - > next = tail ;
}
head - > u1_last = tail ;
tail - > prev = head ; /* the dad link only points to containers */
}
return head ;
}