%{ /* * Asterisk -- An open source telephony toolkit. * * Copyright (C) 1999 - 2006, Digium, Inc. * * Mark Spencer * * 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 Dialplan Expression Lexical Scanner */ #include "asterisk.h" #include #include #ifndef STANDALONE ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #else #ifndef __USE_ISOC99 #define __USE_ISOC99 1 #endif #endif #ifdef __USE_ISOC99 #define FP___PRINTF "%.18Lg" #define FP___FMOD fmodl #define FP___STRTOD strtold #define FP___TYPE long double #else #define FP___PRINTF "%.16g" #define FP___FMOD fmod #define FP___STRTOD strtod #define FP___TYPE double #endif #include #include #include #include #if !defined(SOLARIS) && !defined(__CYGWIN__) /* #include */ #else #define quad_t int64_t #endif #include #include #include #include "asterisk/ast_expr.h" #include "asterisk/logger.h" #ifndef STANDALONE #include "asterisk/strings.h" #include "asterisk/channel.h" #endif enum valtype { AST_EXPR_number, AST_EXPR_numeric_string, AST_EXPR_string } ; struct val { enum valtype type; union { char *s; FP___TYPE i; /* long double or just double if it's a bad day */ } u; } ; #include "ast_expr2.h" /* the o/p of the bison on ast_expr2.y */ #define SET_COLUMNS do { \ yylloc_param->first_column = (int)(yyg->yytext_r - YY_CURRENT_BUFFER_LVALUE->yy_ch_buf); \ yylloc_param->last_column += yyleng - 1; \ yylloc_param->first_line = yylloc_param->last_line = 1; \ } while (0) #define SET_STRING do { \ yylval_param->val = calloc(1, sizeof(struct val)); \ yylval_param->val->type = AST_EXPR_string; \ yylval_param->val->u.s = strdup(yytext); \ } while (0) #define SET_NUMERIC_STRING do { \ yylval_param->val = calloc(1, sizeof(struct val)); \ yylval_param->val->type = AST_EXPR_numeric_string; \ yylval_param->val->u.s = strdup(yytext); \ } while (0) struct parse_io { char *string; struct val *val; yyscan_t scanner; struct ast_channel *chan; }; void ast_yyset_column(int column_no, yyscan_t yyscanner); int ast_yyget_column(yyscan_t yyscanner); static int curlycount = 0; static char *expr2_token_subst(const char *mess); %} %option prefix="ast_yy" %option batch %option outfile="ast_expr2f.c" %option reentrant %option bison-bridge %option bison-locations %option noyywrap %x var trail %% \| { 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_OR;} \&\& { SET_COLUMNS; SET_STRING; return TOK_AND;} \=\= { SET_COLUMNS; SET_STRING; return TOK_EQ;} \=~ { SET_COLUMNS; SET_STRING; return TOK_EQTILDE;} \> { 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_COMMA;} \- { 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_COND;} \! { SET_COLUMNS; SET_STRING; return TOK_COMPL;} \: { SET_COLUMNS; SET_STRING; return TOK_COLON;} \:\: { SET_COLUMNS; SET_STRING; return TOK_COLONCOLON;} \( { SET_COLUMNS; SET_STRING; return TOK_LP;} \) { SET_COLUMNS; SET_STRING; return TOK_RP;} \$\{ { /* gather the contents of ${} expressions, with trailing stuff, * into a single TOKEN. * They are much more complex now than they used to be */ curlycount = 0; BEGIN(var); yymore(); } [ \t\r] {} \"[^"]*\" {SET_COLUMNS; SET_STRING; return TOKEN;} [\n] {/* what to do with eol */} [0-9]+(\.[0-9]+)? { SET_COLUMNS; /* the original behavior of the expression parser was * to bring in numbers as a numeric string */ SET_NUMERIC_STRING; return TOKEN; } [a-zA-Z0-9\.';\\_^$#@]+ { SET_COLUMNS; SET_STRING; return TOKEN; } [^{}]*\} { curlycount--; if (curlycount < 0) { BEGIN(trail); yymore(); } else { yymore(); } } [^{}]*\{ { curlycount++; yymore(); } [^-\t\r \n$():?%/+=*<>!|&]* { BEGIN(0); SET_COLUMNS; SET_STRING; return TOKEN; } [-\t\r \n$():?%/+=*<>!|&] { char c = yytext[yyleng-1]; BEGIN(0); unput(c); SET_COLUMNS; SET_STRING; return TOKEN; } \$\{ { curlycount = 0; BEGIN(var); yymore(); } <> { BEGIN(0); SET_COLUMNS; SET_STRING; return TOKEN; /*actually, if an expr is only a variable ref, this could happen a LOT */ } %% /* 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 */ int ast_yyerror(const char *, YYLTYPE *, struct parse_io *); /* likewise */ int ast_expr(char *expr, char *buf, int length, struct ast_channel *chan) { struct parse_io io; int return_value = 0; memset(&io, 0, sizeof(io)); io.string = expr; /* to pass to the error routine */ io.chan = chan; ast_yylex_init(&io.scanner); ast_yy_scan_string(expr, io.scanner); ast_yyparse ((void *) &io); ast_yylex_destroy(io.scanner); if (!io.val) { if (length > 1) { strcpy(buf, "0"); return_value = 1; } } else { if (io.val->type == AST_EXPR_number) { int res_length; res_length = snprintf(buf, length, FP___PRINTF, io.val->u.i); return_value = (res_length <= length) ? res_length : length; } else { if (io.val->u.s) #if defined(STANDALONE) || defined(LOW_MEMORY) || defined(STANDALONE_AEL) strncpy(buf, io.val->u.s, length - 1); #else /* !STANDALONE && !LOW_MEMORY */ ast_copy_string(buf, io.val->u.s, length); #endif /* STANDALONE || LOW_MEMORY */ else buf[0] = 0; return_value = strlen(buf); if (io.val->u.s) free(io.val->u.s); } free(io.val); } return return_value; } char extra_error_message[4095]; int extra_error_message_supplied = 0; void ast_expr_register_extra_error_info(char *message); void ast_expr_clear_extra_error_info(void); void ast_expr_register_extra_error_info(char *message) { extra_error_message_supplied=1; strcpy(extra_error_message, message); } void ast_expr_clear_extra_error_info(void) { extra_error_message_supplied=0; extra_error_message[0] = 0; } static char *expr2_token_equivs1[] = { "TOKEN", "TOK_COND", "TOK_COLONCOLON", "TOK_OR", "TOK_AND", "TOK_EQ", "TOK_GT", "TOK_LT", "TOK_GE", "TOK_LE", "TOK_NE", "TOK_PLUS", "TOK_MINUS", "TOK_MULT", "TOK_DIV", "TOK_MOD", "TOK_COMPL", "TOK_COLON", "TOK_EQTILDE", "TOK_COMMA", "TOK_RP", "TOK_LP" }; static char *expr2_token_equivs2[] = { "", "?", "::", "|", "&", "=", ">", "<", ">=", "<=", "!=", "+", "-", "*", "/", "%", "!", ":", "=~", ",", ")", "(" }; static char *expr2_token_subst(const char *mess) { /* calc a length, malloc, fill, and return; yyerror had better free it! */ int len=0,i; const char *p; char *res, *s,*t; int expr2_token_equivs_entries = sizeof(expr2_token_equivs1)/sizeof(char*); for (p=mess; *p; p++) { for (i=0; iscanner); char spacebuf[8000]; /* best safe than sorry */ char spacebuf2[8000]; /* best safe than sorry */ int i=0; char *s2 = expr2_token_subst(s); spacebuf[0] = 0; 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 STANDALONE3 /* easier to read in the standalone version */ printf("ast_yyerror(): %s syntax error: %s; Input:\n%s\n%s\n", (extra_error_message_supplied?extra_error_message:""), s2, parseio->string,spacebuf2); #else ast_log(LOG_WARNING,"ast_yyerror(): %s syntax error: %s; Input:\n%s\n%s\n", (extra_error_message_supplied?extra_error_message:""), s2, parseio->string,spacebuf2); #endif #ifndef STANDALONE ast_log(LOG_WARNING,"If you have questions, please refer to doc/channelvariables.txt in the asterisk source.\n"); #endif free(s2); return(0); }