Index: utils/check_expr.c =================================================================== RCS file: /usr/cvsroot/asterisk/utils/check_expr.c,v retrieving revision 1.1 diff -u -r1.1 check_expr.c --- utils/check_expr.c 9 Jun 2005 20:50:48 -0000 1.1 +++ utils/check_expr.c 21 Jun 2005 04:49:07 -0000 @@ -1,14 +1,60 @@ #include #include +#include #include #include -int lineno = 1; -int expr_count = 0; -int expr_max_size = 0; -int expr_tot_size = 0; -int warn_count = 0; -int OK_count = 0; +int global_lineno = 1; +int global_expr_count = 0; +int global_expr_max_size = 0; +int global_expr_tot_size = 0; +int global_warn_count = 0; +int global_OK_count = 0; + +struct varz +{ + char varname[100]; /* a really ultra-simple, space-wasting linked list of var=val data */ + char varval[1000]; /* if any varname is bigger than 100 chars, or val greater than 1000, then **CRASH** */ + struct varz *next; +}; + +struct varz *global_varlist; + +/* Our own version of ast_log, since the expr parser uses it. */ + +void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...) __attribute__ ((format (printf,5,6))); + +void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...) +{ + va_list vars; + va_start(vars,fmt); + + printf("LOG: lev:%d file:%s line:%d func: %s ", + level, file, line, function); + vprintf(fmt, vars); + fflush(stdout); + va_end(vars); +} + +char *find_var(const char *varname) /* the list should be pretty short, if there's any list at all */ +{ + struct varz *t; + for( t= global_varlist; t; t = t->next ) { + if( !strcmp(t->varname, varname) ) { + return t->varval; + } + } + return 0; +} + +void set_var(const char *varname, const char *varval) +{ + struct varz *t = calloc(1,sizeof(struct varz)); + strcpy(t->varname, varname); + strcpy(t->varval, varval); + t->next = global_varlist; + global_varlist = t; +} int check_expr(char *buffer, char *error_report) { @@ -18,8 +64,7 @@ error_report[0] = 0; - for(cp=buffer;*cp;cp++) - { + for(cp=buffer;*cp;cp++) { if( *cp == '|' || *cp == '&' @@ -35,43 +80,36 @@ || *cp == ':' /* || *cp == '(' || *cp == ')' These are pretty hard to track, as they are in funcalls, etc. */ - || *cp == '"' ) - { - if( *cp == '"' ) - { + || *cp == '"' ) { + if( *cp == '"' ) { /* skip to the other end */ cp++; while( *cp && *cp != '"' ) cp++; - if( *cp == 0 ) - { + if( *cp == 0 ) { fprintf(stderr,"Trouble? Unterminated double quote found at line %d\n", - lineno); + global_lineno); } } - else - { - if( ((*cp == '>'||*cp == '<' ||*cp=='!') && *(cp+1) == '=' ) ) - { + else { + if( ((*cp == '>'||*cp == '<' ||*cp=='!') && *(cp+1) == '=' ) ) { oplen = 2; } - else - { + else { oplen = 1; } - if( (cp > buffer && *(cp-1) != ' ') || *(cp+oplen) != ' ' ) - { + if( (cp > buffer && *(cp-1) != ' ') || *(cp+oplen) != ' ' ) { char tbuf[1000]; if( oplen == 1 ) sprintf(tbuf,"WARNING: line %d, '%c' operator not separated by spaces. This may lead to confusion. You may wish to use double quotes to quote the grouping it is in. Please check!\n", - lineno, *cp); + global_lineno, *cp); else sprintf(tbuf,"WARNING: line %d, '%c%c' operator not separated by spaces. This may lead to confusion. You may wish to use double quotes to quote the grouping it is in. Please check!\n", - lineno, *cp, *(cp+1)); + global_lineno, *cp, *(cp+1)); strcat(error_report,tbuf); - warn_count++; + global_warn_count++; warn_found++; } } @@ -80,8 +118,80 @@ return warn_found; } +int check_eval(char *buffer, char *error_report) +{ + char *cp, *ep, *xp, *s; + char evalbuf[80000]; + extern char *ast_expr(char *); + int oplen = 0; + int warn_found = 0; + + error_report[0] = 0; + ep = evalbuf; + + for(cp=buffer;*cp;cp++) { + if( *cp == '$' && *(cp+1) == '{' ) { + int brack_lev = 1; + char *xp= cp+2; + + while( *xp ) { + if( *xp == '{' ) + brack_lev++; + else if( *xp == '}' ) + brack_lev--; + + if( brack_lev == 0 ) + break; + xp++; + } + if( *xp == '}' ) { + char varname[200]; + char *val; + + strncpy(varname,cp+2, xp-cp-2); + varname[xp-cp-2] = 0; + cp = xp; + val = find_var(varname); + if( val ) { + char *z = val; + while( *z ) + *ep++ = *z++; + } + else { + *ep++ = '5'; /* why not */ + *ep++ = '5'; + *ep++ = '5'; + } + } + else { + printf("Unterminated variable reference at line %d\n", global_lineno); + *ep++ = *cp; + } + } + else if( *cp == '\\' ) { + /* braindead simple elim of backslash */ + cp++; + *ep++ = *cp; + } + else + *ep++ = *cp; + } + *ep++ = 0; + + /* now, run the test */ + s = ast_expr(evalbuf); + if( s ) { + sprintf(error_report,"line %d, evaluation of $[ %s ] result: %s\n", global_lineno, evalbuf, s); + return 1; + } + else { + sprintf(error_report,"line %d, evaluation of $[ %s ] result: ****SYNTAX ERROR****\n", global_lineno, evalbuf); + return 1; + } +} + -void parse_file(char *fname) +void parse_file(const char *fname) { FILE *f = fopen(fname,"r"); FILE *l = fopen("expr2_log","w"); @@ -89,90 +199,83 @@ char last_char= 0; char buffer[30000]; /* I sure hope no expr gets this big! */ - if( !f ) - { + if( !f ) { fprintf(stderr,"Couldn't open %s for reading... need an extensions.conf file to parse!\n"); exit(20); } - if( !l ) - { + if( !l ) { fprintf(stderr,"Couldn't open 'expr2_log' file for writing... please fix and re-run!\n"); exit(21); } - lineno = 1; + global_lineno = 1; - while( (c1 = fgetc(f)) != EOF ) - { + while( (c1 = fgetc(f)) != EOF ) { if( c1 == '\n' ) - lineno++; - else if( c1 == '[' ) - { - if( last_char == '$' ) - { + global_lineno++; + else if( c1 == '[' ) { + if( last_char == '$' ) { /* bingo, an expr */ int bracklev = 1; int bufcount = 0; int retval; char error_report[30000]; - while( (c1 = fgetc(f)) != EOF ) - { + while( (c1 = fgetc(f)) != EOF ) { if( c1 == '[' ) bracklev++; else if( c1 == ']' ) bracklev--; - if( c1 == '\n' ) - { - fprintf(l, "ERROR-- A newline in an expression? Weird! ...at line %d\n", lineno); + if( c1 == '\n' ) { + fprintf(l, "ERROR-- A newline in an expression? Weird! ...at line %d\n", global_lineno); fclose(f); fclose(l); - printf("--- ERROR --- A newline in the middle of an expression at line %d!\n", lineno); + printf("--- ERROR --- A newline in the middle of an expression at line %d!\n", global_lineno); } if( bracklev == 0 ) break; buffer[bufcount++] = c1; } - if( c1 == EOF ) - { - fprintf(l, "ERROR-- End of File Reached in the middle of an Expr at line %d\n", lineno); + if( c1 == EOF ) { + fprintf(l, "ERROR-- End of File Reached in the middle of an Expr at line %d\n", global_lineno); fclose(f); fclose(l); - printf("--- ERROR --- EOF reached in middle of an expression at line %d!\n", lineno); + printf("--- ERROR --- EOF reached in middle of an expression at line %d!\n", global_lineno); exit(22); } buffer[bufcount] = 0; /* update stats */ - expr_tot_size += bufcount; - expr_count++; - if( bufcount > expr_max_size ) - expr_max_size = bufcount; + global_expr_tot_size += bufcount; + global_expr_count++; + if( bufcount > global_expr_max_size ) + global_expr_max_size = bufcount; retval = check_expr(buffer, error_report); /* check_expr should bump the warning counter */ - if( retval != 0 ) - { + if( retval != 0 ) { /* print error report */ printf("Warning(s) at line %d, expression: $[%s]; see expr2_log file for details\n", - lineno, buffer); + global_lineno, buffer); fprintf(l, "%s", error_report); } - else - { - printf("OK -- $[%s] at line %d\n", buffer, lineno); - OK_count++; + else { + printf("OK -- $[%s] at line %d\n", buffer, global_lineno); + global_OK_count++; } + error_report[0] = 0; + retval = check_eval(buffer, error_report); + fprintf(l, "%s", error_report); } } last_char = c1; } printf("Summary:\n Expressions detected: %d\n Expressions OK: %d\n Total # Warnings: %d\n Longest Expr: %d chars\n Ave expr len: %d chars\n", - expr_count, - OK_count, - warn_count, - expr_max_size, - (expr_count) ? expr_tot_size/expr_count : 0); + global_expr_count, + global_OK_count, + global_warn_count, + global_expr_max_size, + (global_expr_count) ? global_expr_tot_size/global_expr_count : 0); fclose(f); fclose(l); @@ -181,11 +284,22 @@ main(int argc,char **argv) { - if( argc < 2 ) - { + int argc1; + char *eq; + + if( argc < 2 ) { printf("Hey-- give me a path to an extensions.conf file!\n"); exit(19); } + global_varlist = 0; + for(argc1=2;argc1 < argc; argc1++) { + if( (eq= strchr(argv[argc1],'=')) ) { + *eq = 0; + set_var(argv[argc1],eq+1); + } + } + + /* parse command args for x=y and set varz */ parse_file(argv[1]); } Index: doc/README.variables =================================================================== RCS file: /usr/cvsroot/asterisk/doc/README.variables,v retrieving revision 1.46 diff -u -r1.46 README.variables --- doc/README.variables 16 May 2005 00:43:16 -0000 1.46 +++ doc/README.variables 21 Jun 2005 04:49:08 -0000 @@ -310,12 +324,20 @@ numeric or string constants were replaced with a variable reference ${CALLERIDNUM}, for instance. - +__________________________ +Numbers vs Strings +-------------------------- + +Tokens consisting only of numbers are converted to 64-bit numbers for most of the +operators. This means that overflows can occur when the numbers get above 18 digits. +Warnings will appear in the logs in this case. + + ___________________________ CONDITIONALS --------------------------- -There is one conditional operator - the conditional goto : +There is one conditional application - the conditional goto : exten => 1,2,gotoif(condition?label1:label2) @@ -498,6 +520,73 @@ ** match anywhere in the string. The only diff with the ':' is that ** match doesn't have to be anchored to the beginning of the string. + +-------------------------------------------------------- +Debugging Hints for $[ ] expressions +-------------------------------------------------------- + +** THE FOLLOWING UTILITIES ARE PROVIDED ONLY FOR SYSTEMS THAT +** HAVE FLEX-2.5.31 OR GREATER, AND USE THE UPGRADED LEXER!!! +** +** There are two utilities you can build to help debug the $[ ] in +** your extensions.conf file. +** +** The first, and most simplistic, is to issue the command: +** +** make testexpr2 +** +** in the top level asterisk source directory. This will build +** a small executable, that is able to take the first command line +** argument, and run it thru the expression parser. No variable +** substitutions will be performed. It might be safest to wrap +** the expression in single quotes... +** +** testexpr2 '2*2+2/2' +** +** is an example. +** +** And, in the utils directory, you can say: +** +** make check_expr +** +** and a small program will be built, that will check the file +** mentioned in the first command line argument, for any expressions +** that might be have problems when you move to flex-2.5.31. +** It was originally designed to help spot possible incompatibilities +** when moving from the pre-2.5.31 world to the upgraded version of +** the lexer. +** +** But one more capability has been added to check_expr, that might +** make it more generally useful. It now does a simple minded evaluation of +** all variables, and then passes the $[] exprs to the parser. If there are +** any parse errors, they will be reported in the log file. You can use +** check_expr to do a quick sanity check of the expressions in your +** extensions.conf file, to see if they pass a crude syntax check. +** +** The "simple-minded" variable substitution replaces ${varname} variable +** references with '555'. You can override the 555 for variable values, +** by entering in var=val arguments after the filename on the command line. +** So... +** +** check_expr /etc/asterisk/extensions.conf CALLERIDNUM=3075551212 DIALSTATUS=TORTURE EXTEN=121 +** +** will substitute any ${CALLERIDNUM} variable references with 3075551212, any ${DIALSTATUS} +** variable references with 'TORTURE', and any ${EXTEN} references with '121'. +** If there is any fancy stuff going on in the reference, like ${EXTEN:2}, then the +** override will not work. Everything in the ${...} has to match. So, to substitute +** #{EXTEN:2} references, you'd best say: +** +** check_expr /etc/asterisk/extensions.conf CALLERIDNUM=3075551212 DIALSTATUS=TORTURE EXTEN:2=121 +** +** on stdout, you will see something like: +** +** OK -- $[ "${DIALSTATUS}" = "TORTURE" | "${DIALSTATUS}" = "DONTCALL" ] at line 416 +** +** In the expr2_log file that is generated, you will see: +** +** line 416, evaluation of $[ "TORTURE" = "TORTURE" | "TORTURE" = "DONTCALL" ] result: 1 +** +** check_expr is a very simplistic algorithm, and it is far from being guaranteed +** to work in all cases, but it is hoped that it will be useful. --------------------------------------------------------- Asterisk standard channel variables Index: utils/Makefile =================================================================== RCS file: /usr/cvsroot/asterisk/utils/Makefile,v retrieving revision 1.11 diff -u -r1.11 Makefile --- utils/Makefile 9 Jun 2005 21:12:03 -0000 1.11 +++ utils/Makefile 21 Jun 2005 17:13:24 -0000 @@ -34,6 +34,9 @@ stereorize: stereorize.o frame.o $(CC) $(CFLAGS) -o stereorize stereorize.o frame.o -lm +check_expr : check_expr.c ../ast_expr.a + $(CC) $(CFLAGS) -o check_expr check_expr.c ../ast_expr.a + smsq: smsq.o $(CC) $(CFLAGS) -o smsq ${SOL} smsq.o -lpopt