476 lines
11 KiB
Plaintext
476 lines
11 KiB
Plaintext
%{
|
|
/*
|
|
* Asterisk -- An open source telephony toolkit.
|
|
*
|
|
* Copyright (C) 1999 - 2006, Digium, Inc.
|
|
*
|
|
* Mark Spencer <markster@digium.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 Dialplan Expression Lexical Scanner
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <stdio.h>
|
|
|
|
#if defined(STANDALONE)
|
|
#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 <stdlib.h>
|
|
#include <string.h>
|
|
#include <locale.h>
|
|
#include <ctype.h>
|
|
#if !defined(SOLARIS) && !defined(__CYGWIN__)
|
|
/* #include <err.h> */
|
|
#else
|
|
#define quad_t int64_t
|
|
#endif
|
|
#include <errno.h>
|
|
#include <regex.h>
|
|
#include <limits.h>
|
|
|
|
#include "asterisk/ast_expr.h"
|
|
#include "asterisk/logger.h"
|
|
#ifndef STANDALONE
|
|
#include "asterisk/strings.h"
|
|
#include "asterisk/channel.h"
|
|
#endif
|
|
|
|
/* Conditionally redefine the macro from flex 2.5.35, in case someone uses flex <2.5.35 to regenerate this file. */
|
|
#ifndef ECHO
|
|
#define ECHO do { if (fwrite( yytext, yyleng, 1, yyout )) {} } while (0)
|
|
#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 8bit
|
|
%option outfile="ast_expr2f.c"
|
|
%option reentrant
|
|
%option bison-bridge
|
|
%option bison-locations
|
|
%option noyywrap
|
|
%option noyyfree
|
|
%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_TILDETILDE;}
|
|
\> { 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\.';\\_^#@]|[\x80-\xff]|($[^{]))+ {
|
|
SET_COLUMNS;
|
|
SET_STRING;
|
|
return TOKEN;
|
|
}
|
|
|
|
([a-zA-Z0-9\.';\\_^#@]|[\x80-\xff]|($[^{]))+\$\{ {
|
|
curlycount = 0;
|
|
BEGIN(var);
|
|
yymore();
|
|
}
|
|
|
|
<var>[^{}]*\} {
|
|
curlycount--;
|
|
if (curlycount < 0) {
|
|
BEGIN(trail);
|
|
yymore();
|
|
} else {
|
|
yymore();
|
|
}
|
|
}
|
|
|
|
<var>[^{}]*\{ {
|
|
curlycount++;
|
|
yymore();
|
|
}
|
|
|
|
|
|
<trail>[^-\t\r \n$():?%/+=*<>!|&]* {
|
|
BEGIN(0);
|
|
SET_COLUMNS;
|
|
SET_STRING;
|
|
return TOKEN;
|
|
}
|
|
|
|
<trail>[^-\t\r \n$():?%/+=*<>!|&]*\$\{ {
|
|
curlycount = 0;
|
|
BEGIN(var);
|
|
yymore();
|
|
}
|
|
|
|
<trail>[-\t\r \n$():?%/+=*<>!|&] {
|
|
char c = yytext[yyleng-1];
|
|
BEGIN(0);
|
|
unput(c);
|
|
SET_COLUMNS;
|
|
SET_STRING;
|
|
return TOKEN;
|
|
}
|
|
|
|
<trail><<EOF>> {
|
|
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 */
|
|
|
|
void ast_yyfree(void *ptr, yyscan_t yyscanner)
|
|
{
|
|
/* the normal generated yyfree func just frees its first arg;
|
|
this get complaints on some systems, as sometimes this
|
|
arg is a nil ptr! It's usually not fatal, but is irritating! */
|
|
free( (char *) ptr );
|
|
}
|
|
|
|
int ast_expr(char *expr, char *buf, int length, struct ast_channel *chan)
|
|
{
|
|
struct parse_io io = { .string = expr, .chan = chan };
|
|
int return_value = 0;
|
|
|
|
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)
|
|
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);
|
|
free(io.val->u.s);
|
|
}
|
|
free(io.val);
|
|
}
|
|
return return_value;
|
|
}
|
|
|
|
#ifndef STANDALONE
|
|
int ast_str_expr(struct ast_str **str, ssize_t maxlen, struct ast_channel *chan, char *expr)
|
|
{
|
|
struct parse_io io = { .string = expr, .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) {
|
|
ast_str_set(str, maxlen, "0");
|
|
} else {
|
|
if (io.val->type == AST_EXPR_number) {
|
|
ast_str_set(str, maxlen, FP___PRINTF, io.val->u.i);
|
|
} else if (io.val->u.s) {
|
|
ast_str_set(str, maxlen, "%s", io.val->u.s);
|
|
free(io.val->u.s);
|
|
}
|
|
free(io.val);
|
|
}
|
|
return ast_str_strlen(*str);
|
|
}
|
|
#endif
|
|
|
|
|
|
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 const char * const 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 const char * const expr2_token_equivs2[] =
|
|
{
|
|
"<token>",
|
|
"?",
|
|
"::",
|
|
"|",
|
|
"&",
|
|
"=",
|
|
">",
|
|
"<",
|
|
">=",
|
|
"<=",
|
|
"!=",
|
|
"+",
|
|
"-",
|
|
"*",
|
|
"/",
|
|
"%",
|
|
"!",
|
|
":",
|
|
"=~",
|
|
",",
|
|
")",
|
|
"("
|
|
};
|
|
|
|
|
|
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;
|
|
const char *t;
|
|
int expr2_token_equivs_entries = sizeof(expr2_token_equivs1)/sizeof(char*);
|
|
|
|
for (p=mess; *p; p++) {
|
|
for (i=0; i<expr2_token_equivs_entries; i++) {
|
|
if ( strncmp(p,expr2_token_equivs1[i],strlen(expr2_token_equivs1[i])) == 0 )
|
|
{
|
|
len+=strlen(expr2_token_equivs2[i])+2;
|
|
p += strlen(expr2_token_equivs1[i])-1;
|
|
break;
|
|
}
|
|
}
|
|
len++;
|
|
}
|
|
res = (char*)malloc(len+1);
|
|
res[0] = 0;
|
|
s = res;
|
|
for (p=mess; *p;) {
|
|
int found = 0;
|
|
for (i=0; i<expr2_token_equivs_entries; i++) {
|
|
if ( strncmp(p,expr2_token_equivs1[i],strlen(expr2_token_equivs1[i])) == 0 ) {
|
|
*s++ = '\'';
|
|
for (t=expr2_token_equivs2[i]; *t;) {
|
|
*s++ = *t++;
|
|
}
|
|
*s++ = '\'';
|
|
p += strlen(expr2_token_equivs1[i]);
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
if( !found )
|
|
*s++ = *p++;
|
|
}
|
|
*s++ = 0;
|
|
return res;
|
|
}
|
|
|
|
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 */
|
|
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++) {
|
|
spacebuf[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. */
|
|
|
|
spacebuf[i++] = '^';
|
|
spacebuf[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, spacebuf);
|
|
#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, spacebuf);
|
|
#endif
|
|
#ifndef STANDALONE
|
|
ast_log(LOG_WARNING,"If you have questions, please refer to https://wiki.asterisk.org/wiki/display/AST/Channel+Variables\n");
|
|
#endif
|
|
free(s2);
|
|
return(0);
|
|
}
|