Newer
Older
/*
* 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
*
Kevin P. Fleming
committed
* \brief Dialplan Expression Lexical Scanner
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>
#include <ctype.h>
#define quad_t int64_t
#include <errno.h>
#include <regex.h>
#include <limits.h>
Kevin P. Fleming
committed
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/ast_expr.h"
#include "asterisk/logger.h"
#include "asterisk/strings.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 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;
};
void ast_yyset_column(int column_no, yyscan_t yyscanner);
int ast_yyget_column(yyscan_t yyscanner);
Kevin P. Fleming
committed
static char *expr2_token_subst(char *mess);
%}
%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;}
Kevin P. Fleming
committed
\|\| { 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_MINUS;}
\* { SET_COLUMNS; SET_STRING; return TOK_MULT;}
\/ { SET_COLUMNS; SET_STRING; return TOK_DIV;}
\% { SET_COLUMNS; SET_STRING; return TOK_MOD;}
Kevin P. Fleming
committed
\? { SET_COLUMNS; SET_STRING; return TOK_COND;}
Kevin P. Fleming
committed
\! { SET_COLUMNS; SET_STRING; return TOK_COMPL;}
\: { SET_COLUMNS; SET_STRING; return TOK_COLON;}
Kevin P. Fleming
committed
\:\: { SET_COLUMNS; SET_STRING; return TOK_COLONCOLON;}
\( { SET_COLUMNS; SET_STRING; return TOK_LP;}
\) { SET_COLUMNS; SET_STRING; return TOK_RP;}
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
\$\{ {
/* 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]+ {
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;
}
<var>[^{}]*\} {
curlycount--;
if (curlycount < 0) {
BEGIN(trail);
yymore();
} else {
yymore();
}
}
<var>[^{}]*\{ {
curlycount++;
yymore();
}
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
<trail>[^-\t\r \n$():?%/+=*<>!|&]* {
BEGIN(0);
SET_COLUMNS;
SET_STRING;
return TOKEN;
}
<trail>[-\t\r \n$():?%/+=*<>!|&] {
char c = yytext[yyleng-1];
BEGIN(0);
unput(c);
SET_COLUMNS;
SET_STRING;
return TOKEN;
}
<trail>\$\{ {
curlycount = 0;
BEGIN(var);
yymore();
}
<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 */
int ast_expr(char *expr, char *buf, int length)
struct parse_io io;
int return_value = 0;
memset(&io, 0, sizeof(io));
io.string = expr; /* to pass to the error routine */
ast_yy_scan_string(expr, io.scanner);
ast_yylex_destroy(io.scanner);
if (length > 1) {
strcpy(buf, "0");
}
if (io.val->type == AST_EXPR_integer) {
int res_length;
res_length = snprintf(buf, length, "%ld", (long int) io.val->u.i);
return_value = (res_length <= length) ? res_length : length;
} else {
#ifdef STANDALONE
strncpy(buf, io.val->u.s, length - 1);
#else /* !STANDALONE */
ast_copy_string(buf, io.val->u.s, length);
#endif /* STANDALONE */
Kevin P. Fleming
committed
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
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_RP",
"TOK_LP"
};
static char *expr2_token_equivs2[] =
{
"<token>",
"?",
"::",
"|",
"&",
"=",
">",
"<",
">=",
"<=",
"!=",
"+",
"-",
"*",
"/",
"%",
"!",
":",
"=~",
")",
"("
};
static char *expr2_token_subst(char *mess)
{
/* calc a length, malloc, fill, and return; yyerror had better free it! */
int len=0,i;
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; 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 */
char spacebuf2[8000]; /* best safe than sorry */
int i=0;
Kevin P. Fleming
committed
char *s2 = expr2_token_subst((char *)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,
Kevin P. Fleming
committed
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;
Kevin P. Fleming
committed
#ifdef STANDALONE3
/* easier to read in the standalone version */
Kevin P. Fleming
committed
printf("ast_yyerror(): %s syntax error: %s; Input:\n%s\n%s\n",
(extra_error_message_supplied?extra_error_message:""), s2, parseio->string,spacebuf2);
Kevin P. Fleming
committed
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);
Kevin P. Fleming
committed
#endif
#ifndef STANDALONE
ast_log(LOG_WARNING,"If you have questions, please refer to doc/channelvariables.txt in the asterisk source.\n");
Kevin P. Fleming
committed
free(s2);