dect
/
asterisk
Archived
13
0
Fork 0

add upgraded expression parser (bug #2058)

git-svn-id: http://svn.digium.com/svn/asterisk/trunk@5691 f38db490-d61c-443f-a65b-d21fe96a405b
This commit is contained in:
kpfleming 2005-05-16 00:35:38 +00:00
parent cd6784bf2b
commit 5e9ff3009e
5 changed files with 1476 additions and 9 deletions

View File

@ -258,10 +258,21 @@ ifeq (${OSARCH},SunOS)
LIBS+=-lpthread -ldl -lnsl -lsocket -lresolv -L$(CROSS_COMPILE_TARGET)/usr/local/ssl/lib
endif
LIBS+=-lssl
FLEXVER_GT_2_5_31=$(shell ./vercomp.sh flex \>= 2.5.31)
BISONVER=$(shell bison --version | grep \^bison | egrep -o '[0-9]+\.[-0-9.]+[a-z]?' )
BISONVERGE_85=$(shell ./vercomp.sh bison \>= 1.85 )
ifeq (${FLEXVER_GT_2_5_31},true)
FLEXOBJS=ast_expr2.o ast_expr2f.o
else
FLEXOBJS=ast_expr.o
endif
OBJS=io.o sched.o logger.o frame.o loader.o config.o channel.o \
translate.o file.o say.o pbx.o cli.o md5.o term.o \
ulaw.o alaw.o callerid.o fskmodem.o image.o app.o \
cdr.o tdd.o acl.o rtp.o manager.o asterisk.o ast_expr.o \
cdr.o tdd.o acl.o rtp.o manager.o asterisk.o ${FLEXOBJS} \
dsp.o chanvars.o indications.o autoservice.o db.o privacy.o \
astmm.o enum.o srv.o dns.o aescrypt.o aestab.o aeskey.o \
utils.o config_old.o plc.o jitterbuf.o dnsmgr.o
@ -333,14 +344,46 @@ _version:
.version: _version
.y.c:
bison $< --name-prefix=ast_yy -o $@
@if (($(BISONVERGE_85) = false)); then \
echo ================================================================================= ;\
echo NOTE: you may have trouble if you do not have bison-1.85 or higher installed! ;\
echo NOTE: you can pick up a copy at: http://ftp.gnu.org/ or its mirrors ;\
echo NOTE: You Have: $(BISONVER) ;\
echo ================================================================================; \
else \
echo EXCELLENT-- You have Bison version $(BISONVER), this should work just fine...;\
fi
bison -v -d --name-prefix=ast_yy $< -o $@
ast_expr.o: ast_expr.c
@echo NOTE:
@echo NOTE:
@echo NOTE: Using older version of ast_expr. To use the newer version,
@echo NOTE: Upgrade to flex 2.5.31 or higher, which can be found at http://
@echo NOTE: http://sourceforge.net/project/showfiles.php?group_id=72099
@echo NOTE:
@echo NOTE:
$(CC) -c $(CPPFLAGS) $(CFLAGS) ast_expr.c
ast_expr2.o: ast_expr2.c
ast_expr2f.o: ast_expr2f.c
ast_expr2f.c: ast_expr2.fl
flex ast_expr2.fl
cli.o: cli.c build.h
asterisk.o: asterisk.c build.h
testexpr2 :
flex ast_expr2.fl
bison -v -d --name-prefix=ast_yy -o ast_expr2.c ast_expr2.y
gcc -g -c -DSTANDALONE ast_expr2f.c
gcc -g -c -DSTANDALONE ast_expr2.c
gcc -g -o testexpr2 ast_expr2f.o ast_expr2.o
rm ast_expr2.c ast_expr2.o ast_expr2f.o ast_expr2f.c
manpage: asterisk.8.gz
asterisk.8.gz: asterisk.sgml

167
ast_expr2.fl Executable file
View File

@ -0,0 +1,167 @@
%{
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <regex.h>
#include <limits.h>
#include <asterisk/ast_expr.h>
#include <asterisk/logger.h>
enum valtype {
AST_EXPR_integer, AST_EXPR_numeric_string, AST_EXPR_string
} ;
struct val {
enum valtype type;
union {
char *s;
quad_t i;
} u;
} ;
#include "ast_expr2.h" /* the o/p of the bison on ast_expr2.y */
#define SET_COLUMNS yylloc_param->first_column = (int)(yyg->yytext_r - YY_CURRENT_BUFFER_LVALUE->yy_ch_buf);yylloc_param->last_column = yylloc_param->last_column + yyleng - 1; yylloc_param->first_line = yylloc_param->last_line = 1
#define SET_STRING yylval_param->val = (struct val *)calloc(sizeof(struct val),1); yylval_param->val->type = AST_EXPR_string; yylval_param->val->u.s = strdup(yytext);
struct parse_io
{
char *string;
struct val *val;
yyscan_t scanner;
};
%}
%option prefix="ast_yy"
%option batch
%option outfile="ast_expr2f.c"
%option reentrant
%option bison-bridge
%option bison-locations
%option noyywrap
%%
\| { SET_COLUMNS; SET_STRING; return TOK_OR;}
\& { SET_COLUMNS; SET_STRING; return TOK_AND;}
\= { SET_COLUMNS; SET_STRING; return TOK_EQ;}
\> { SET_COLUMNS; SET_STRING; return TOK_GT;}
\< { SET_COLUMNS; SET_STRING; return TOK_LT;}
\>\= { SET_COLUMNS; SET_STRING; return TOK_GE;}
\<\= { SET_COLUMNS; SET_STRING; return TOK_LE;}
\!\= { SET_COLUMNS; SET_STRING; return TOK_NE;}
\+ { SET_COLUMNS; SET_STRING; return TOK_PLUS;}
\- { SET_COLUMNS; SET_STRING; return TOK_MINUS;}
\* { SET_COLUMNS; SET_STRING; return TOK_MULT;}
\/ { SET_COLUMNS; SET_STRING; return TOK_DIV;}
\% { SET_COLUMNS; SET_STRING; return TOK_MOD;}
\: { SET_COLUMNS; SET_STRING; return TOK_COLON;}
\( { SET_COLUMNS; SET_STRING; return TOK_LP;}
\) { SET_COLUMNS; SET_STRING; return TOK_RP;}
[ \r] {}
\"[^"]*\" {SET_COLUMNS; SET_STRING; return TOKEN;}
[\n] {/* what to do with eol */}
[0-9]+ { SET_COLUMNS;
yylval_param->val = (struct val *)calloc(sizeof(struct val),1);
yylval_param->val->type = AST_EXPR_integer;
yylval_param->val->u.i = atoi(yytext);
return TOKEN;}
[a-zA-Z0-9,.?';{}\\_^%$#@!]+ {SET_COLUMNS; SET_STRING; return TOKEN;}
%%
/* I'm putting the interface routine to the whole parse here in the flexer input file
mainly because of all the flexer initialization that has to be done. Shouldn't matter
where it is, as long as it's somewhere. I didn't want to define a prototype for the
ast_yy_scan_string in the .y file, because then, I'd have to define YY_BUFFER_STATE there...
UGH! that would be inappropriate. */
int ast_yyparse( void *); /* need to/should define this prototype for the call to yyparse */
char *ast_expr(char *arg); /* and this prototype for the following func */
int ast_yyerror(const char *,YYLTYPE *, struct parse_io *); /* likewise */
char *ast_expr (char *arg)
{
struct parse_io *io;
char *pirouni;
io = (struct parse_io *)calloc(sizeof(struct parse_io),1);
io->string = arg; /* to pass to the error routine */
ast_yylex_init(&io->scanner);
ast_yy_scan_string(arg,io->scanner);
ast_yyparse ((void *)io);
ast_yylex_destroy(io->scanner);
if (io->val==NULL) {
pirouni=strdup("0");
return(pirouni);
} else {
if (io->val->type == AST_EXPR_integer) {
pirouni=malloc(256);
sprintf (pirouni,"%lld", (long long)io->val->u.i);
}
else {
pirouni=strdup(io->val->u.s);
}
free(io->val);
}
free(io);
return(pirouni);
}
int ast_yyerror (const char *s, yyltype *loc, struct parse_io *parseio )
{
struct yyguts_t * yyg = (struct yyguts_t*)(parseio->scanner);
char spacebuf[8000]; /* best safe than sorry */
char spacebuf2[8000]; /* best safe than sorry */
int i=0;
spacebuf[0] = 0;
#ifdef WHEN_LOC_MEANS_SOMETHING
if( loc->first_column > 7990 ) /* if things get out of whack, why crash? */
loc->first_column = 7990;
if( loc->last_column > 7990 )
loc->last_column = 7990;
for(i=0;i<loc->first_column;i++) spacebuf[i] = ' ';
for( ;i<loc->last_column;i++) spacebuf[i] = '^';
spacebuf[i] = 0;
#endif
for(i=0;i< (int)(yytext - YY_CURRENT_BUFFER_LVALUE->yy_ch_buf);i++) spacebuf2[i] = ' '; /* uh... assuming yyg is defined, then I can use the yycolumn macro,
which is the same thing as... get this:
yyg->yy_buffer_stack[yyg->yy_buffer_stack_top]->yy_bs_column
I was tempted to just use yy_buf_pos in the STATE, but..., well:
a. the yy_buf_pos is the current position in the buffer, which
may not relate to the entire string/buffer because of the
buffering.
b. but, analysis of the situation is that when you use the
yy_scan_string func, it creates a single buffer the size of
string, so the two would be the same...
so, in the end, the yycolumn macro is available, shorter, therefore easier. */
spacebuf2[i++]='^';
spacebuf2[i]= 0;
#ifdef STANDALONE
/* easier to read in the standalone version */
printf("ast_yyerror(): syntax error: %s; Input:\n%s\n%s\n",
s, parseio->string,spacebuf2);
#else
ast_log(LOG_WARNING,"ast_yyerror(): syntax error: %s; Input:\n%s\n%s\n",
s, parseio->string,spacebuf2);
ast_log(LOG_WARNING,"If you have questions, please refer to doc/README.variables2 in the asterisk source.\n");
#endif
return(0);
}

901
ast_expr2.y Executable file
View File

@ -0,0 +1,901 @@
%{
/* Written by Pace Willisson (pace@blitz.com)
* and placed in the public domain.
*
* Largely rewritten by J.T. Conklin (jtc@wimsey.com)
*
* And then overhauled twice by Steve Murphy (murf@e-tools.com)
* to add double-quoted strings, allow mult. spaces, improve
* error messages, and then to fold in a flex scanner for the
* yylex operation.
*
* $FreeBSD: src/bin/expr/expr.y,v 1.16 2000/07/22 10:59:36 se Exp $
*/
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <regex.h>
#include <limits.h>
#include <asterisk/ast_expr.h>
#include <asterisk/logger.h>
#ifdef LONG_LONG_MIN
#define QUAD_MIN LONG_LONG_MIN
#endif
#ifdef LONG_LONG_MAX
#define QUAD_MAX LONG_LONG_MAX
#endif
# if ! defined(QUAD_MIN)
# define QUAD_MIN (-0x7fffffffffffffffL-1)
# endif
# if ! defined(QUAD_MAX)
# define QUAD_MAX (0x7fffffffffffffffL)
# endif
#define YYPARSE_PARAM parseio
#define YYLEX_PARAM ((struct parse_io *)parseio)->scanner
#define YYERROR_VERBOSE 1
/* #define ast_log fprintf
#define LOG_WARNING stderr */
enum valtype {
AST_EXPR_integer, AST_EXPR_numeric_string, AST_EXPR_string
} ;
struct val {
enum valtype type;
union {
char *s;
quad_t i;
} u;
} ;
typedef void *yyscan_t;
struct parse_io
{
char *string;
struct val *val;
yyscan_t scanner;
};
static int chk_div __P((quad_t, quad_t));
static int chk_minus __P((quad_t, quad_t, quad_t));
static int chk_plus __P((quad_t, quad_t, quad_t));
static int chk_times __P((quad_t, quad_t, quad_t));
static void free_value __P((struct val *));
static int is_zero_or_null __P((struct val *));
static int isstring __P((struct val *));
static struct val *make_integer __P((quad_t));
static struct val *make_str __P((const char *));
static struct val *op_and __P((struct val *, struct val *));
static struct val *op_colon __P((struct val *, struct val *));
static struct val *op_eqtilde __P((struct val *, struct val *));
static struct val *op_div __P((struct val *, struct val *));
static struct val *op_eq __P((struct val *, struct val *));
static struct val *op_ge __P((struct val *, struct val *));
static struct val *op_gt __P((struct val *, struct val *));
static struct val *op_le __P((struct val *, struct val *));
static struct val *op_lt __P((struct val *, struct val *));
static struct val *op_minus __P((struct val *, struct val *));
static struct val *op_negate __P((struct val *));
static struct val *op_compl __P((struct val *));
static struct val *op_ne __P((struct val *, struct val *));
static struct val *op_or __P((struct val *, struct val *));
static struct val *op_plus __P((struct val *, struct val *));
static struct val *op_rem __P((struct val *, struct val *));
static struct val *op_times __P((struct val *, struct val *));
static quad_t to_integer __P((struct val *));
static void to_string __P((struct val *));
/* uh, if I want to predeclare yylex with a YYLTYPE, I have to predeclare the yyltype... sigh */
typedef struct yyltype
{
int first_line;
int first_column;
int last_line;
int last_column;
} yyltype;
# define YYLTYPE yyltype
# define YYLTYPE_IS_TRIVIAL 1
/* we will get warning about no prototype for yylex! But we can't
define it here, we have no definition yet for YYSTYPE. */
int ast_yyerror(const char *,YYLTYPE *, struct parse_io *);
/* I wanted to add args to the yyerror routine, so I could print out
some useful info about the error. Not as easy as it looks, but it
is possible. */
#define ast_yyerror(x) ast_yyerror(x,&yyloc,parseio)
%}
%pure-parser
%locations
/* %debug for when you are having big problems */
/* %name-prefix="ast_yy" */
%union
{
struct val *val;
}
/* IN_ANOTHER_LIFE
%{
static int ast_yylex __P((YYSTYPE *, YYLTYPE *, yyscan_t));
%}
*/
%left <val> TOK_OR
%left <val> TOK_AND
%left <val> TOK_EQ TOK_GT TOK_LT TOK_GE TOK_LE TOK_NE
%left <val> TOK_PLUS TOK_MINUS
%left <val> TOK_MULT TOK_DIV TOK_MOD
%left <val> TOK_COMPL TOK_EQTILDE
%left UMINUS
%left <val> TOK_COLON
%left <val> TOK_RP TOK_LP
%token <val> TOKEN
%type <val> start expr
%%
start: expr { ((struct parse_io *)parseio)->val = (struct val *)calloc(sizeof(struct val),1);
((struct parse_io *)parseio)->val->type = $$->type;
((struct parse_io *)parseio)->val->u.s = $$->u.s; }
;
expr: TOKEN { $$= $1;}
| TOK_LP expr TOK_RP { $$ = $2;
@$.first_column = @1.first_column; @$.last_column = @3.last_column;
@$.first_line=0; @$.last_line=0;}
| expr TOK_OR expr { $$ = op_or ($1, $3);
@$.first_column = @1.first_column; @$.last_column = @3.last_column;
@$.first_line=0; @$.last_line=0;}
| expr TOK_AND expr { $$ = op_and ($1, $3);
@$.first_column = @1.first_column; @$.last_column = @3.last_column;
@$.first_line=0; @$.last_line=0;}
| expr TOK_EQ expr { $$ = op_eq ($1, $3);
@$.first_column = @1.first_column; @$.last_column = @3.last_column;
@$.first_line=0; @$.last_line=0;}
| expr TOK_GT expr { $$ = op_gt ($1, $3);
@$.first_column = @1.first_column; @$.last_column = @3.last_column;
@$.first_line=0; @$.last_line=0;}
| expr TOK_LT expr { $$ = op_lt ($1, $3);
@$.first_column = @1.first_column; @$.last_column = @3.last_column;
@$.first_line=0; @$.last_line=0;}
| expr TOK_GE expr { $$ = op_ge ($1, $3);
@$.first_column = @1.first_column; @$.last_column = @3.last_column;
@$.first_line=0; @$.last_line=0;}
| expr TOK_LE expr { $$ = op_le ($1, $3);
@$.first_column = @1.first_column; @$.last_column = @3.last_column;
@$.first_line=0; @$.last_line=0;}
| expr TOK_NE expr { $$ = op_ne ($1, $3);
@$.first_column = @1.first_column; @$.last_column = @3.last_column;
@$.first_line=0; @$.last_line=0;}
| expr TOK_PLUS expr { $$ = op_plus ($1, $3);
@$.first_column = @1.first_column; @$.last_column = @3.last_column;
@$.first_line=0; @$.last_line=0;}
| expr TOK_MINUS expr { $$ = op_minus ($1, $3);
@$.first_column = @1.first_column; @$.last_column = @3.last_column;
@$.first_line=0; @$.last_line=0;}
| TOK_MINUS expr %prec UMINUS { $$ = op_negate ($2);
@$.first_column = @1.first_column; @$.last_column = @2.last_column;
@$.first_line=0; @$.last_line=0;}
| TOK_COMPL expr %prec UMINUS { $$ = op_compl ($2);
@$.first_column = @1.first_column; @$.last_column = @2.last_column;
@$.first_line=0; @$.last_line=0;}
| expr TOK_MULT expr { $$ = op_times ($1, $3);
@$.first_column = @1.first_column; @$.last_column = @3.last_column;
@$.first_line=0; @$.last_line=0;}
| expr TOK_DIV expr { $$ = op_div ($1, $3);
@$.first_column = @1.first_column; @$.last_column = @3.last_column;
@$.first_line=0; @$.last_line=0;}
| expr TOK_MOD expr { $$ = op_rem ($1, $3);
@$.first_column = @1.first_column; @$.last_column = @3.last_column;
@$.first_line=0; @$.last_line=0;}
| expr TOK_COLON expr { $$ = op_colon ($1, $3);
@$.first_column = @1.first_column; @$.last_column = @3.last_column;
@$.first_line=0; @$.last_line=0;}
| expr TOK_EQTILDE expr { $$ = op_eqtilde ($1, $3);
@$.first_column = @1.first_column; @$.last_column = @3.last_column;
@$.first_line=0; @$.last_line=0;}
;
%%
static struct val *
make_integer (quad_t i)
{
struct val *vp;
vp = (struct val *) malloc (sizeof (*vp));
if (vp == NULL) {
ast_log(LOG_WARNING, "malloc() failed\n");
return(NULL);
}
vp->type = AST_EXPR_integer;
vp->u.i = i;
return vp;
}
static struct val *
make_str (const char *s)
{
struct val *vp;
size_t i;
int isint;
vp = (struct val *) malloc (sizeof (*vp));
if (vp == NULL || ((vp->u.s = strdup (s)) == NULL)) {
ast_log(LOG_WARNING,"malloc() failed\n");
return(NULL);
}
for(i = 1, isint = isdigit(s[0]) || s[0] == '-';
isint && i < strlen(s);
i++)
{
if(!isdigit(s[i]))
isint = 0;
}
if (isint)
vp->type = AST_EXPR_numeric_string;
else
vp->type = AST_EXPR_string;
return vp;
}
static void
free_value (struct val *vp)
{
if (vp==NULL) {
return;
}
if (vp->type == AST_EXPR_string || vp->type == AST_EXPR_numeric_string)
free (vp->u.s);
}
static quad_t
to_integer (struct val *vp)
{
quad_t i;
if (vp == NULL) {
ast_log(LOG_WARNING,"vp==NULL in to_integer()\n");
return(0);
}
if (vp->type == AST_EXPR_integer)
return 1;
if (vp->type == AST_EXPR_string)
return 0;
/* vp->type == AST_EXPR_numeric_string, make it numeric */
errno = 0;
i = strtoq(vp->u.s, (char**)NULL, 10);
if (errno != 0) {
free(vp->u.s);
ast_log(LOG_WARNING,"overflow\n");
return(0);
}
free (vp->u.s);
vp->u.i = i;
vp->type = AST_EXPR_integer;
return 1;
}
static void
strip_quotes(struct val *vp)
{
if (vp->type != AST_EXPR_string && vp->type != AST_EXPR_numeric_string)
return;
if( vp->u.s[0] == '"' && vp->u.s[strlen(vp->u.s)-1] == '"' )
{
char *f, *t;
f = vp->u.s;
t = vp->u.s;
while( *f )
{
if( *f && *f != '"' )
*t++ = *f++;
else
f++;
}
*t = *f;
}
}
static void
to_string (struct val *vp)
{
char *tmp;
if (vp->type == AST_EXPR_string || vp->type == AST_EXPR_numeric_string)
return;
tmp = malloc ((size_t)25);
if (tmp == NULL) {
ast_log(LOG_WARNING,"malloc() failed\n");
return;
}
sprintf (tmp, "%lld", (long long)vp->u.i);
vp->type = AST_EXPR_string;
vp->u.s = tmp;
}
static int
isstring (struct val *vp)
{
/* only TRUE if this string is not a valid integer */
return (vp->type == AST_EXPR_string);
}
static int
is_zero_or_null (struct val *vp)
{
if (vp->type == AST_EXPR_integer) {
return (vp->u.i == 0);
} else {
return (*vp->u.s == 0 || (to_integer (vp) && vp->u.i == 0));
}
/* NOTREACHED */
}
#ifdef STANDALONE
void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
{
printf("LOG: lev:%d file:%s line:%d func: %s fmt:%s\n",
level, file, line, function, fmt);
fflush(stdout);
}
int main(int argc,char **argv) {
char *s;
s=ast_expr(argv[1]);
printf("=====%s======\n",s);
}
#endif
#undef ast_yyerror
#define ast_yyerror(x) ast_yyerror(x, YYLTYPE *yylloc, struct parse_io *parseio)
/* I put the ast_yyerror func in the flex input file,
because it refers to the buffer state. Best to
let it access the BUFFER stuff there and not trying
define all the structs, macros etc. in this file! */
static struct val *
op_or (struct val *a, struct val *b)
{
if (is_zero_or_null (a)) {
free_value (a);
return (b);
} else {
free_value (b);
return (a);
}
}
static struct val *
op_and (struct val *a, struct val *b)
{
if (is_zero_or_null (a) || is_zero_or_null (b)) {
free_value (a);
free_value (b);
return (make_integer ((quad_t)0));
} else {
free_value (b);
return (a);
}
}
static struct val *
op_eq (struct val *a, struct val *b)
{
struct val *r;
if (isstring (a) || isstring (b)) {
to_string (a);
to_string (b);
r = make_integer ((quad_t)(strcoll (a->u.s, b->u.s) == 0));
} else {
(void)to_integer(a);
(void)to_integer(b);
r = make_integer ((quad_t)(a->u.i == b->u.i));
}
free_value (a);
free_value (b);
return r;
}
static struct val *
op_gt (struct val *a, struct val *b)
{
struct val *r;
if (isstring (a) || isstring (b)) {
to_string (a);
to_string (b);
r = make_integer ((quad_t)(strcoll (a->u.s, b->u.s) > 0));
} else {
(void)to_integer(a);
(void)to_integer(b);
r = make_integer ((quad_t)(a->u.i > b->u.i));
}
free_value (a);
free_value (b);
return r;
}
static struct val *
op_lt (struct val *a, struct val *b)
{
struct val *r;
if (isstring (a) || isstring (b)) {
to_string (a);
to_string (b);
r = make_integer ((quad_t)(strcoll (a->u.s, b->u.s) < 0));
} else {
(void)to_integer(a);
(void)to_integer(b);
r = make_integer ((quad_t)(a->u.i < b->u.i));
}
free_value (a);
free_value (b);
return r;
}
static struct val *
op_ge (struct val *a, struct val *b)
{
struct val *r;
if (isstring (a) || isstring (b)) {
to_string (a);
to_string (b);
r = make_integer ((quad_t)(strcoll (a->u.s, b->u.s) >= 0));
} else {
(void)to_integer(a);
(void)to_integer(b);
r = make_integer ((quad_t)(a->u.i >= b->u.i));
}
free_value (a);
free_value (b);
return r;
}
static struct val *
op_le (struct val *a, struct val *b)
{
struct val *r;
if (isstring (a) || isstring (b)) {
to_string (a);
to_string (b);
r = make_integer ((quad_t)(strcoll (a->u.s, b->u.s) <= 0));
} else {
(void)to_integer(a);
(void)to_integer(b);
r = make_integer ((quad_t)(a->u.i <= b->u.i));
}
free_value (a);
free_value (b);
return r;
}
static struct val *
op_ne (struct val *a, struct val *b)
{
struct val *r;
if (isstring (a) || isstring (b)) {
to_string (a);
to_string (b);
r = make_integer ((quad_t)(strcoll (a->u.s, b->u.s) != 0));
} else {
(void)to_integer(a);
(void)to_integer(b);
r = make_integer ((quad_t)(a->u.i != b->u.i));
}
free_value (a);
free_value (b);
return r;
}
static int
chk_plus (quad_t a, quad_t b, quad_t r)
{
/* sum of two positive numbers must be positive */
if (a > 0 && b > 0 && r <= 0)
return 1;
/* sum of two negative numbers must be negative */
if (a < 0 && b < 0 && r >= 0)
return 1;
/* all other cases are OK */
return 0;
}
static struct val *
op_plus (struct val *a, struct val *b)
{
struct val *r;
if (!to_integer (a)) {
ast_log(LOG_WARNING,"non-numeric argument\n");
if (!to_integer (b)) {
free_value(a);
free_value(b);
return make_integer(0);
} else {
free_value(a);
return (b);
}
} else if (!to_integer(b)) {
free_value(b);
return (a);
}
r = make_integer (/*(quad_t)*/(a->u.i + b->u.i));
if (chk_plus (a->u.i, b->u.i, r->u.i)) {
ast_log(LOG_WARNING,"overflow\n");
}
free_value (a);
free_value (b);
return r;
}
static int
chk_minus (quad_t a, quad_t b, quad_t r)
{
/* special case subtraction of QUAD_MIN */
if (b == QUAD_MIN) {
if (a >= 0)
return 1;
else
return 0;
}
/* this is allowed for b != QUAD_MIN */
return chk_plus (a, -b, r);
}
static struct val *
op_minus (struct val *a, struct val *b)
{
struct val *r;
if (!to_integer (a)) {
ast_log(LOG_WARNING, "non-numeric argument\n");
if (!to_integer (b)) {
free_value(a);
free_value(b);
return make_integer(0);
} else {
r = make_integer(0 - b->u.i);
free_value(a);
free_value(b);
return (r);
}
} else if (!to_integer(b)) {
ast_log(LOG_WARNING, "non-numeric argument\n");
free_value(b);
return (a);
}
r = make_integer (/*(quad_t)*/(a->u.i - b->u.i));
if (chk_minus (a->u.i, b->u.i, r->u.i)) {
ast_log(LOG_WARNING, "overflow\n");
}
free_value (a);
free_value (b);
return r;
}
static struct val *
op_negate (struct val *a)
{
struct val *r;
if (!to_integer (a) ) {
free_value(a);
ast_log(LOG_WARNING, "non-numeric argument\n");
return make_integer(0);
}
r = make_integer (/*(quad_t)*/(- a->u.i));
if (chk_minus (0, a->u.i, r->u.i)) {
ast_log(LOG_WARNING, "overflow\n");
}
free_value (a);
return r;
}
static struct val *
op_compl (struct val *a)
{
int v1 = 1;
struct val *r;
if( !a )
{
v1 = 0;
}
else
{
switch( a->type )
{
case AST_EXPR_integer:
if( a->u.i == 0 )
v1 = 0;
break;
case AST_EXPR_string:
if( a->u.s == 0 )
v1 = 0;
else
{
if( a->u.s[0] == 0 )
v1 = 0;
else if (strlen(a->u.s) == 1 && a->u.s[0] == '0' )
v1 = 0;
}
break;
case AST_EXPR_numeric_string:
if( a->u.s == 0 )
v1 = 0;
else
{
if( a->u.s[0] == 0 )
v1 = 0;
else if (strlen(a->u.s) == 1 && a->u.s[0] == '0' )
v1 = 0;
}
break;
}
}
r = make_integer (!v1);
free_value (a);
return r;
}
static int
chk_times (quad_t a, quad_t b, quad_t r)
{
/* special case: first operand is 0, no overflow possible */
if (a == 0)
return 0;
/* cerify that result of division matches second operand */
if (r / a != b)
return 1;
return 0;
}
static struct val *
op_times (struct val *a, struct val *b)
{
struct val *r;
if (!to_integer (a) || !to_integer (b)) {
free_value(a);
free_value(b);
ast_log(LOG_WARNING, "non-numeric argument\n");
return(make_integer(0));
}
r = make_integer (/*(quad_t)*/(a->u.i * b->u.i));
if (chk_times (a->u.i, b->u.i, r->u.i)) {
ast_log(LOG_WARNING, "overflow\n");
}
free_value (a);
free_value (b);
return (r);
}
static int
chk_div (quad_t a, quad_t b)
{
/* div by zero has been taken care of before */
/* only QUAD_MIN / -1 causes overflow */
if (a == QUAD_MIN && b == -1)
return 1;
/* everything else is OK */
return 0;
}
static struct val *
op_div (struct val *a, struct val *b)
{
struct val *r;
if (!to_integer (a)) {
free_value(a);
free_value(b);
ast_log(LOG_WARNING, "non-numeric argument\n");
return make_integer(0);
} else if (!to_integer (b)) {
free_value(a);
free_value(b);
ast_log(LOG_WARNING, "non-numeric argument\n");
return make_integer(INT_MAX);
}
if (b->u.i == 0) {
ast_log(LOG_WARNING, "division by zero\n");
free_value(a);
free_value(b);
return make_integer(INT_MAX);
}
r = make_integer (/*(quad_t)*/(a->u.i / b->u.i));
if (chk_div (a->u.i, b->u.i)) {
ast_log(LOG_WARNING, "overflow\n");
}
free_value (a);
free_value (b);
return r;
}
static struct val *
op_rem (struct val *a, struct val *b)
{
struct val *r;
if (!to_integer (a) || !to_integer (b)) {
ast_log(LOG_WARNING, "non-numeric argument\n");
free_value(a);
free_value(b);
return make_integer(0);
}
if (b->u.i == 0) {
ast_log(LOG_WARNING, "div by zero\n");
free_value(a);
return(b);
}
r = make_integer (/*(quad_t)*/(a->u.i % b->u.i));
/* chk_rem necessary ??? */
free_value (a);
free_value (b);
return r;
}
static struct val *
op_colon (struct val *a, struct val *b)
{
regex_t rp;
regmatch_t rm[2];
char errbuf[256];
int eval;
struct val *v;
/* coerce to both arguments to strings */
to_string(a);
to_string(b);
/* strip double quotes from both -- they'll screw up the pattern, and the search string starting at ^ */
strip_quotes(a);
strip_quotes(b);
/* compile regular expression */
if ((eval = regcomp (&rp, b->u.s, REG_EXTENDED)) != 0) {
regerror (eval, &rp, errbuf, sizeof(errbuf));
ast_log(LOG_WARNING,"regcomp() error : %s",errbuf);
free_value(a);
free_value(b);
return make_str("");
}
/* compare string against pattern */
/* remember that patterns are anchored to the beginning of the line */
if (regexec(&rp, a->u.s, (size_t)2, rm, 0) == 0 && rm[0].rm_so == 0) {
if (rm[1].rm_so >= 0) {
*(a->u.s + rm[1].rm_eo) = '\0';
v = make_str (a->u.s + rm[1].rm_so);
} else {
v = make_integer ((quad_t)(rm[0].rm_eo - rm[0].rm_so));
}
} else {
if (rp.re_nsub == 0) {
v = make_integer ((quad_t)0);
} else {
v = make_str ("");
}
}
/* free arguments and pattern buffer */
free_value (a);
free_value (b);
regfree (&rp);
return v;
}
static struct val *
op_eqtilde (struct val *a, struct val *b)
{
regex_t rp;
regmatch_t rm[2];
char errbuf[256];
int eval;
struct val *v;
/* coerce to both arguments to strings */
to_string(a);
to_string(b);
/* strip double quotes from both -- they'll screw up the pattern, and the search string starting at ^ */
strip_quotes(a);
strip_quotes(b);
/* compile regular expression */
if ((eval = regcomp (&rp, b->u.s, REG_EXTENDED)) != 0) {
regerror (eval, &rp, errbuf, sizeof(errbuf));
ast_log(LOG_WARNING,"regcomp() error : %s",errbuf);
free_value(a);
free_value(b);
return make_str("");
}
/* compare string against pattern */
/* remember that patterns are anchored to the beginning of the line */
if (regexec(&rp, a->u.s, (size_t)2, rm, 0) == 0 ) {
if (rm[1].rm_so >= 0) {
*(a->u.s + rm[1].rm_eo) = '\0';
v = make_str (a->u.s + rm[1].rm_so);
} else {
v = make_integer ((quad_t)(rm[0].rm_eo - rm[0].rm_so));
}
} else {
if (rp.re_nsub == 0) {
v = make_integer ((quad_t)0);
} else {
v = make_str ("");
}
}
/* free arguments and pattern buffer */
free_value (a);
free_value (b);
regfree (&rp);
return v;
}

View File

@ -1,5 +1,6 @@
----------------------------
Asterisk dial plan variables
---------------------------
----------------------------
There are two levels of parameter evaluation done in the Asterisk
dial plan in extensions.conf.
@ -12,6 +13,15 @@ Asterisk has user-defined variables and standard variables set
by various modules in Asterisk. These standard variables are
listed at the end of this document.
NOTE: During the Asterisk build process, the versions of bison and
flex available on your system are probed. If you have versions of
flex greater than or equal to 2.5.31, it will use flex to build a
"pure" (re-entrant) tokenizer for expressions. If you use bison version
greater than 1.85, it will use a bison grammar to generate a pure (re-entrant)
parser for $[] expressions.
Notes specific to the flex parser are marked with "**" at the beginning
of the line.
___________________________
PARAMETER QUOTING:
---------------------------
@ -123,6 +133,10 @@ considered as an expression and it is evaluated. Evaluation works similar to
evaluation.
Note: The arguments and operands of the expression MUST BE separated
by at least one space.
** Using the Flex generated tokenizer, this is no longer the case. Spaces
** are only required where they would seperate tokens that would normally
** be merged into a single token. Using the new tokenizer, spaces can be
** used freely.
For example, after the sequence:
@ -132,6 +146,11 @@ exten => 1,2,Set(koko=$[2 * ${lala}])
the value of variable koko is "6".
** Using the new Flex generated tokenizer, the expressions above are still
** legal, but so are the following:
** exten => 1,1,Set(lala=$[1+2])
** exten => 1,2,Set(koko=$[2* ${lala}])
And, further:
exten => 1,1,Set(lala=$[1+2]);
@ -141,15 +160,19 @@ token "1+2" are not numbers, it will be evaluated as the string "1+2". Again,
please do not forget, that this is a very simple parsing engine, and it
uses a space (at least one), to separate "tokens".
** Please note that spaces are not required to separate tokens if you have
** Flex version 2.5.31 or higher on your system.
and, further:
exten => 1,1,Set,"lala=$[ 1 + 2 ]";
will parse as intended. Extra spaces are ignored.
___________________________
SPACES INSIDE VARIABLE
---------------------------
______________________________
SPACES INSIDE VARIABLE VALUES
------------------------------
If the variable being evaluated contains spaces, there can be problems.
For these cases, double quotes around text that may contain spaces
@ -173,7 +196,7 @@ DELOREAN MOTORS : Privacy Manager
and will result in syntax errors, because token DELOREAN is immediately
followed by token MOTORS and the expression parser will not know how to
evaluate this expression.
evaluate this expression, because it does not match its grammar.
_____________________
OPERATORS
@ -204,6 +227,14 @@ with equal precedence are grouped within { } symbols.
Return the results of multiplication, integer division, or
remainder of integer-valued arguments.
** - expr1
** Return the result of subtracting expr1 from 0.
**
** ! expr1
** Return the result of a logical complement of expr1.
** In other words, if expr1 is null, 0, an empty string,
** or the string "0", return a 1. Otherwise, return a "0". (only with flex >= 2.5.31)
expr1 : expr2
The `:' operator matches expr1 against expr2, which must be a
regular expression. The regular expression is anchored to the
@ -216,11 +247,70 @@ with equal precedence are grouped within { } symbols.
the pattern contains a regular expression subexpression the null
string is returned; otherwise 0.
Normally, the double quotes wrapping a string are left as part
of the string. This is disastrous to the : operator. Therefore,
before the regex match is made, beginning and ending double quote
characters are stripped from both the pattern and the string.
** expr1 =~ expr2
** Exactly the same as the ':' operator, except that the match is
** not anchored to the beginning of the string. Pardon any similarity
** to seemingly similar operators in other programming languages!
** (only if flex >= 2.5.31)
Parentheses are used for grouping in the usual manner.
The parser must be parsed with bison (bison is REQUIRED - yacc cannot
produce pure parsers, which are reentrant)
Operator precedence is applied as one would expect in any of the C
or C derived languages.
The parser must be generated with bison (bison is REQUIRED - yacc cannot
produce pure parsers, which are reentrant) The same with flex, if flex
is at 2.5.31 or greater; Re-entrant scanners were not available before that
version.
Examples
** "One Thousand Five Hundred" =~ "(T[^ ]+)"
** returns: Thousand
** "One Thousand Five Hundred" =~ "T[^ ]+"
** returns: 8
"One Thousand Five Hundred" : "T[^ ]+"
returns: 0
"8015551212" : "(...)"
returns: 801
"3075551212":"...(...)"
returns: 555
** ! "One Thousand Five Hundred" =~ "T[^ ]+"
** returns: 0 (because it applies to the string, which is non-null, which it turns to "0",
and then looks for the pattern in the "0", and doesn't find it)
** !( "One Thousand Five Hundred" : "T[^ ]+" )
** returns: 1 (because the string doesn't start with a word starting with T, so the
match evals to 0, and the ! operator inverts it to 1 ).
2 + 8 / 2
returns 6. (because of operator precedence; the division is done first, then the addition).
** 2+8/2
** returns 6. Spaces aren't necessary.
**(2+8)/2
** returns 5, of course.
Of course, all of the above examples use constants, but would work the same if any of the
numeric or string constants were replaced with a variable reference ${CALLERIDNUM}, for
instance.
___________________________
CONDITIONALS
---------------------------
@ -277,6 +367,26 @@ going to be somewhere between the last '^' on the second line, and the
'^' on the third line. That's right, in the example above, there are two
'&' chars, separated by a space, and this is a definite no-no!
** WITH FLEX >= 2.5.31, this has changed slightly. The line showing the
** part of the expression that was successfully parsed has been dropped,
** and the parse error is explained in a somewhat cryptic format in the log.
**
** The same line in extensions.conf as above, will now generate an error
** message in /var/log/asterisk/messages that looks like this:
**
** Jul 15 21:27:49 WARNING[1251240752]: ast_yyerror(): syntax error: parse error, unexpected TOK_AND, expecting TOK_MINUS or TOK_LP or TOKEN; Input:
** "3072312154" = "3071234567" & & "Steves Extension" : "Privacy Manager"
** ^
**
** The log line tells you that a syntax error was encountered. It now
** also tells you (in grand standard bison format) that it hit an "AND" (&)
** token unexpectedly, and that was hoping for for a MINUS (-), LP (left parenthesis),
** or a plain token (a string or number).
**
** As before, the next line shows the evaluated expression, and the line after
** that, the position of the parser in the expression when it became confused,
** marked with the "^" character.
___________________________
NULL STRINGS
@ -306,6 +416,89 @@ whatever language you desire, be it Perl, C, C++, Cobol, RPG, Java,
Snobol, PL/I, Scheme, Common Lisp, Shell scripts, Tcl, Forth, Modula,
Pascal, APL, assembler, etc.
----------------------------
INCOMPATIBILITIES
----------------------------
The asterisk expression parser has undergone some evolution. It is hoped
that the changes will be viewed as positive.
The "original" expression parser had a simple, hand-written scanner, and
a simple bison grammar. This was upgraded to a more involved bison grammar,
and a hand-written scanner upgraded to allow extra spaces, and to generate
better error diagnostics. This upgrade required bison 1.85, and a [art of the user
community felt the pain of having to upgrade their bison version.
The next upgrade included new bison and flex input files, and the makefile
was upgraded to detect current version of both flex and bison, conditionally
compiling and linking the new files if the versions of flex and bison would
allow it.
If you have not touched your extensions.conf files in a year or so, the
above upgrades may cause you some heartburn in certain circumstances, as
several changes have been made, and these will affect asterisk's behavior on
legacy extension.conf constructs. The changes have been engineered
to minimize these conflicts, but there are bound to be problems.
The following list gives some (and most likely, not all) of areas
of possible concern with "legacy" extension.conf files:
1. Tokens separated by space(s).
Previously, tokens were separated by spaces. Thus, ' 1 + 1 ' would evaluate
to the value '2', but '1+1' would evaluate to the string '1+1'. If this
behavior was depended on, then the expression evaluation will break. '1+1'
will now evaluate to '2', and something is not going to work right.
To keep such strings from being evaluated, simply wrap them in double
quotes: ' "1+1" '
2. The colon operator. In versions previous to double quoting, the
colon operator takes the right hand string, and using it as a
regex pattern, looks for it in the left hand string. It is given
an implicit ^ operator at the beginning, meaning the pattern
will match only at the beginning of the left hand string.
If the pattern or the matching string had double quotes around
them, these could get in the way of the pattern match. Now,
the wrapping double quotes are stripped from both the pattern
and the left hand string before applying the pattern. This
was done because it recognized that the new way of
scanning the expression doesn't use spaces to separate tokens,
and the average regex expression is full of operators that
the scanner will recognize as expression operators. Thus, unless
the pattern is wrapped in double quotes, there will be trouble.
For instance, ${VAR1} : (Who|What*)+
may have have worked before, but unless you wrap the pattern
in double quotes now, look out for trouble! This is better:
"${VAR1}" : "(Who|What*)+"
and should work as previous.
3. Variables and Double Quotes
Before these changes, if a variable's value contained one or more double
quotes, it was no reason for concern. It is now!
4. LE, GE, NE operators removed. The code supported these operators,
but they were not documented. The symbolic operators, <=, >=, and !=
should be used instead.
**5. flex 2.5.31 or greater should be used. Bison-1.875 or greater. In
** the case of flex, earlier versions do not generate 'pure', or
** reentrant C scanners. In the case of bison-1.875, earlier versions
** didn't support the location tracking mechanism.
** http://ftp.gnu.org/gnu/bison/bison-1.875.tar.bz2
** http://prdownloads.sourceforge.net/lex/flex-2.5.31.tar.bz2?download
** or http://lex.sourceforge.net/
**6. Added the unary '-' operator. So you can 3+ -4 and get -1.
**7. Added the unary '!' operator, which is a logical complement.
** Basically, if the string or number is null, empty, or '0',
** a '1' is returned. Otherwise a '0' is returned.
**8. Added the '=~' operator, just in case someone is just looking for
** 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.
---------------------------------------------------------
Asterisk standard channel variables
---------------------------------------------------------

163
vercomp.sh Executable file
View File

@ -0,0 +1,163 @@
#! /bin/bash
### flex just outputs a single line:
## flex version 2.5.4
### but bison is a bit more wordy
## bison (GNU Bison) 1.875c
## Written by Robert Corbett and Richard Stallman.
##
## Copyright (C) 2003 Free Software Foundation, Inc.
## This is free software; see the source for copying conditions. There is NO
## warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
### based on this, the version number of the program:
### a. in the first line of output
### b. is the last "word" of that line
program=$1
comparefunc=$2
argver=$3
progver1=`$program --version | head -1`
[[ $progver1 =~ '([^ ]+$)' ]]
progver=$BASH_REMATCH
progver2=$progver
numprogverlist=0
while [[ $progver2 =~ '^([^.]+)\.(.*)' ]]; do
progver2=${BASH_REMATCH[2]}
progverlist[$numprogverlist]=${BASH_REMATCH[1]}
progverlist[$(( ${numprogverlist}+1 ))]=${BASH_REMATCH[2]}
## echo ${BASH_REMATCH[0]}
## echo ${BASH_REMATCH[1]}
## echo ${BASH_REMATCH[2]}
(( numprogverlist=$(( $numprogverlist+1 )) ))
done
(( numprogverlist=$(( $numprogverlist+1 )) ))
## echo number of elements = $numprogverlist
## echo element 0 = ${progverlist[0]}
## echo element 1 = ${progverlist[1]}
## echo element 2 = ${progverlist[2]}
argver2=$argver
numargverlist=0
while [[ $argver2 =~ '^([^.]+)\.(.*)' ]]; do
argver2=${BASH_REMATCH[2]}
argverlist[$numargverlist]=${BASH_REMATCH[1]}
argverlist[$(( ${numargverlist}+1 ))]=${BASH_REMATCH[2]}
## echo ${BASH_REMATCH[0]}
## echo ${BASH_REMATCH[1]}
## echo ${BASH_REMATCH[2]}
(( numargverlist=$(( $numargverlist+1 )) ))
done
(( numargverlist=$(( $numargverlist+1 )) ))
## echo number of argver elements = $numargverlist
## echo element 0 = ${argverlist[0]}
## echo element 1 = ${argverlist[1]}
## echo element 2 = ${argverlist[2]}
if (( $numprogverlist < $numargverlist )); then
for (( i=$numprogverlist ; $i < $numargverlist ; i=$i + 1 )) ; do
## echo setting progverlist "[" $i "]" to 0
(( progverlist[$i]='0' ))
(( numprogverlist=${numprogverlist}+1 ))
done
elif (( $numargverlist < $numprogverlist )); then
for (( i=$numargverlist ; $i < $numprogverlist ; i=$i + 1 )) ; do
## echo setting argverlist "[" $i "]" to 0
(( argverlist[$i]='0' ))
(( numargverlist=${numargverlist}+1 ))
done
fi
## echo numarg=$numargverlist numprog=$numprogverlist
## echo arg0: ${argverlist[0]}
## echo arg1: ${argverlist[1]}
## echo arg2: ${argverlist[2]}
## echo prog0: ${progverlist[0]}
## echo prog1: ${progverlist[1]}
## echo prog2: ${progverlist[2]}
## the main comparison loop
for (( i=0 ; $i < $numargverlist ; i=$i + 1 )) ; do
## echo i= $i
if [[ ${progverlist[$i]} =~ '^[0-9]+$' && ${argverlist[$i]} =~ '^[0-9]+$' ]] ; then ## nothing but numbers
if (( ${progverlist[$i]} != ${argverlist[$i]} )); then
if [[ ${progverlist[$i]} -lt ${argverlist[$i]} ]]; then
if [[ $comparefunc == "=" ]]; then
echo "false"
exit 0;
elif [[ $comparefunc == "<" || $comparefunc == "<=" ]]; then
echo "true"
exit 0;
elif [[ $comparefunc == ">" || $comparefunc == ">=" ]]; then
echo "false"
exit 0;
fi
elif [[ ${progverlist[$i]} -gt ${argverlist[$i]} ]]; then
if [[ $comparefunc == "=" ]]; then
echo "false"
exit 0;
elif [[ $comparefunc == "<" || $comparefunc == "<=" ]]; then
echo "false"
exit 0;
elif [[ $comparefunc == ">" || $comparefunc == ">=" ]]; then
echo "true"
exit 0;
fi
fi
fi
else ## something besides just numbers
if [[ ${progverlist[$i]} != ${argverlist[$i]} ]]; then
if [[ ${progverlist[$i]} < ${argverlist[$i]} ]]; then
if [[ $comparefunc == "=" ]]; then
echo "false"
exit 0;
elif [[ $comparefunc == "<" || $comparefunc == "<=" ]]; then
echo "true"
exit 0;
elif [[ $comparefunc == ">" || $comparefunc == ">=" ]]; then
echo "false"
exit 0;
fi
elif [[ ${progverlist[$i]} > ${argverlist[$i]} ]]; then
if [[ $comparefunc == "=" ]]; then
echo "false"
exit 0;
elif [[ $comparefunc == "<" || $comparefunc == "<=" ]]; then
echo "false"
exit 0;
elif [[ $comparefunc == ">" || $comparefunc == ">=" ]]; then
echo "true"
exit 0;
fi
fi
fi
fi
done
if [[ $comparefunc == "=" ]]; then
echo "true"
elif [[ $comparefunc == "<=" || $comparefunc == ">=" ]]; then
echo "true"
else
echo "false"
fi
exit 0;