75b3e76329f1085020a13525b983d7664bc578b3
[asterisk/asterisk.git] / main / ast_expr2.fl
1 %{
2 /*
3  * Asterisk -- An open source telephony toolkit.
4  *
5  * Copyright (C) 1999 - 2006, Digium, Inc.
6  *
7  * Mark Spencer <markster@digium.com>
8  *
9  * See http://www.asterisk.org for more information about
10  * the Asterisk project. Please do not directly contact
11  * any of the maintainers of this project for assistance;
12  * the project provides a web site, mailing lists and IRC
13  * channels for your use.
14  *
15  * This program is free software, distributed under the terms of
16  * the GNU General Public License Version 2. See the LICENSE file
17  * at the top of the source tree.
18  */
19
20 /*! \file
21  *
22  * \brief Dialplan Expression Lexical Scanner
23  */
24
25 #include <sys/types.h>
26 #include <stdio.h>
27
28 #if !defined(STANDALONE)
29 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
30 #else
31 #ifndef __USE_ISOC99
32 #define __USE_ISOC99 1
33 #endif
34 #endif
35
36 #ifdef __USE_ISOC99
37 #define FP___PRINTF "%.18Lg"
38 #define FP___FMOD   fmodl
39 #define FP___STRTOD  strtold
40 #define FP___TYPE    long double
41 #else
42 #define FP___PRINTF "%.16g"
43 #define FP___FMOD   fmod
44 #define FP___STRTOD  strtod
45 #define FP___TYPE    double
46 #endif
47
48 #include <stdlib.h>
49 #include <string.h>
50 #include <locale.h>
51 #include <ctype.h>
52 #if !defined(SOLARIS) && !defined(__CYGWIN__)
53 /* #include <err.h> */
54 #else
55 #define quad_t int64_t
56 #endif
57 #include <errno.h>
58 #include <regex.h>
59 #include <limits.h>
60
61 #include "asterisk/ast_expr.h"
62 #include "asterisk/logger.h"
63 #ifndef STANDALONE
64 #include "asterisk/strings.h"
65 #include "asterisk/channel.h"
66 #endif
67
68 #ifdef NEED_FLEX_FWRITE_WORKAROUND
69 /*!\note Some versions of Flex use fwrite without checking its return value, which
70  * is a warning on some compilers.  Therefore, we use this workaround, to trick
71  * the compiler into suppressing this warning. */
72 #define fwrite(a,b,c,d) do { int __res = fwrite(a,b,c,d); (__res); } while (0)
73 #endif
74
75 enum valtype {
76         AST_EXPR_number, AST_EXPR_numeric_string, AST_EXPR_string
77 } ;
78
79 struct val {
80         enum valtype type;
81         union {
82                 char *s;
83                 FP___TYPE i; /* long double or just double if it's a bad day */
84         } u;
85 } ;
86
87 #include "ast_expr2.h" /* the o/p of the bison on ast_expr2.y */
88
89 #define SET_COLUMNS     do {            \
90         yylloc_param->first_column = (int)(yyg->yytext_r - YY_CURRENT_BUFFER_LVALUE->yy_ch_buf); \
91         yylloc_param->last_column += yyleng - 1; \
92         yylloc_param->first_line = yylloc_param->last_line = 1; \
93         } while (0)
94
95 #define SET_STRING      do {            \
96         yylval_param->val = calloc(1, sizeof(struct val));      \
97         yylval_param->val->type = AST_EXPR_string;              \
98         yylval_param->val->u.s = strdup(yytext);                \
99         } while (0)
100
101 #define SET_NUMERIC_STRING      do {    \
102         yylval_param->val = calloc(1, sizeof(struct val));      \
103         yylval_param->val->type = AST_EXPR_numeric_string;      \
104         yylval_param->val->u.s = strdup(yytext);        \
105         } while (0)
106
107 struct parse_io
108 {
109         char *string;
110         struct val *val;
111         yyscan_t scanner;
112         struct ast_channel *chan;
113 };
114  
115 void ast_yyset_column(int column_no, yyscan_t yyscanner);
116 int ast_yyget_column(yyscan_t yyscanner);
117 static int curlycount = 0;
118 static char *expr2_token_subst(const char *mess);
119 %}
120
121 %option prefix="ast_yy"
122 %option batch
123 %option 8bit
124 %option outfile="ast_expr2f.c"
125 %option reentrant
126 %option bison-bridge
127 %option bison-locations
128 %option noyywrap
129 %option noyyfree
130 %x var trail
131
132 %%
133
134 \|      { SET_COLUMNS; SET_STRING; return TOK_OR;}
135 \&      { SET_COLUMNS; SET_STRING; return TOK_AND;}
136 \=      { SET_COLUMNS; SET_STRING; return TOK_EQ;}
137 \|\|    { SET_COLUMNS; SET_STRING; return TOK_OR;}
138 \&\&    { SET_COLUMNS; SET_STRING; return TOK_AND;}
139 \=\=    { SET_COLUMNS; SET_STRING; return TOK_EQ;}
140 \=~     { SET_COLUMNS; SET_STRING; return TOK_EQTILDE;}
141 \~~     { SET_COLUMNS; SET_STRING; return TOK_TILDETILDE;}
142 \>      { SET_COLUMNS; SET_STRING; return TOK_GT;}
143 \<      { SET_COLUMNS; SET_STRING; return TOK_LT;}
144 \>\=    { SET_COLUMNS; SET_STRING; return TOK_GE;}
145 \<\=    { SET_COLUMNS; SET_STRING; return TOK_LE;}
146 \!\=    { SET_COLUMNS; SET_STRING; return TOK_NE;}
147 \+      { SET_COLUMNS; SET_STRING; return TOK_PLUS;}
148 \,      { SET_COLUMNS; SET_STRING; return TOK_COMMA;}
149 \-      { SET_COLUMNS; SET_STRING; return TOK_MINUS;}
150 \*      { SET_COLUMNS; SET_STRING; return TOK_MULT;}
151 \/      { SET_COLUMNS; SET_STRING; return TOK_DIV;}
152 \%      { SET_COLUMNS; SET_STRING; return TOK_MOD;}
153 \?      { SET_COLUMNS; SET_STRING; return TOK_COND;}
154 \!      { SET_COLUMNS; SET_STRING; return TOK_COMPL;}
155 \:      { SET_COLUMNS; SET_STRING; return TOK_COLON;}
156 \:\:    { SET_COLUMNS; SET_STRING; return TOK_COLONCOLON;}
157 \(      { SET_COLUMNS; SET_STRING; return TOK_LP;}
158 \)      { SET_COLUMNS; SET_STRING; return TOK_RP;}
159 \$\{    {
160                 /* gather the contents of ${} expressions, with trailing stuff,
161                  * into a single TOKEN.
162                  * They are much more complex now than they used to be
163                  */
164                 curlycount = 0;
165                 BEGIN(var);
166                 yymore();
167         }
168
169 [ \t\r]         {}
170 \"[^"]*\"       {SET_COLUMNS; SET_STRING; return TOKEN;}
171
172 [\n]            {/* what to do with eol */}
173 [0-9]+(\.[0-9]+)?               {
174                 SET_COLUMNS;
175                 /* the original behavior of the expression parser was
176                  * to bring in numbers as a numeric string
177                  */
178                 SET_NUMERIC_STRING;
179                 return TOKEN;
180         }
181
182 ([a-zA-Z0-9\.';\\_^#@]|[\x80-\xff]|($[^{]))+   {
183                 SET_COLUMNS;
184                 SET_STRING;
185                 return TOKEN;
186         }
187
188 ([a-zA-Z0-9\.';\\_^#@]|[\x80-\xff]|($[^{]))+\$\{        {
189                 curlycount = 0;
190                 BEGIN(var);
191                 yymore();
192         }
193
194 <var>[^{}]*\}   {
195                 curlycount--;
196                 if (curlycount < 0) {
197                         BEGIN(trail);
198                         yymore();
199                 } else {
200                         yymore();
201                 }
202         }
203         
204 <var>[^{}]*\{   {
205                 curlycount++;
206                 yymore();
207         }
208         
209
210 <trail>[^-\t\r \n$():?%/+=*<>!|&]*      {
211                 BEGIN(0);
212                 SET_COLUMNS;
213                 SET_STRING;
214                 return TOKEN;
215         }
216         
217 <trail>[^-\t\r \n$():?%/+=*<>!|&]*\$\{  {
218                 curlycount = 0;
219                 BEGIN(var);
220                 yymore();
221         }
222         
223 <trail>[-\t\r \n$():?%/+=*<>!|&]        {
224                 char c = yytext[yyleng-1];
225                 BEGIN(0);
226                 unput(c);
227                 SET_COLUMNS;
228                 SET_STRING;
229                 return TOKEN;
230         }
231         
232 <trail><<EOF>>  {
233                 BEGIN(0);
234                 SET_COLUMNS;
235                 SET_STRING;
236                 return TOKEN;
237                 /*actually, if an expr is only a variable ref, this could happen a LOT */
238         }
239
240 %%
241
242 /* I'm putting the interface routine to the whole parse here in the flexer input file
243    mainly because of all the flexer initialization that has to be done. Shouldn't matter
244    where it is, as long as it's somewhere. I didn't want to define a prototype for the
245    ast_yy_scan_string in the .y file, because then, I'd have to define YY_BUFFER_STATE there...
246         UGH! that would be inappropriate. */
247
248 int ast_yyparse(void *); /* need to/should define this prototype for the call to yyparse */
249 int ast_yyerror(const char *, YYLTYPE *, struct parse_io *); /* likewise */
250
251 void ast_yyfree(void *ptr, yyscan_t yyscanner)
252 {
253     /* the normal generated yyfree func just frees its first arg;
254      this get complaints on some systems, as sometimes this
255      arg is a nil ptr! It's usually not fatal, but is irritating! */
256         free( (char *) ptr );
257 }
258
259 int ast_expr(char *expr, char *buf, int length, struct ast_channel *chan)
260 {
261         struct parse_io io = { .string = expr, .chan = chan };
262         int return_value = 0;
263
264         ast_yylex_init(&io.scanner);
265
266         ast_yy_scan_string(expr, io.scanner);
267
268         ast_yyparse ((void *) &io);
269
270         ast_yylex_destroy(io.scanner);
271
272         if (!io.val) {
273                 if (length > 1) {
274                         strcpy(buf, "0");
275                         return_value = 1;
276                 }
277         } else {
278                 if (io.val->type == AST_EXPR_number) {
279                         int res_length;
280
281                         res_length = snprintf(buf, length, FP___PRINTF, io.val->u.i);
282                         return_value = (res_length <= length) ? res_length : length;
283                 } else {
284                         if (io.val->u.s)
285 #if defined(STANDALONE) || defined(LOW_MEMORY) || defined(STANDALONE)
286                                 strncpy(buf, io.val->u.s, length - 1);
287 #else /* !STANDALONE && !LOW_MEMORY */
288                                 ast_copy_string(buf, io.val->u.s, length);
289 #endif /* STANDALONE || LOW_MEMORY */
290                         else
291                                 buf[0] = 0;
292                         return_value = strlen(buf);
293                         free(io.val->u.s);
294                 }
295                 free(io.val);
296         }
297         return return_value;
298 }
299
300 #ifndef STANDALONE
301 int ast_str_expr(struct ast_str **str, ssize_t maxlen, struct ast_channel *chan, char *expr)
302 {
303         struct parse_io io = { .string = expr, .chan = chan };
304
305         ast_yylex_init(&io.scanner);
306         ast_yy_scan_string(expr, io.scanner);
307         ast_yyparse ((void *) &io);
308         ast_yylex_destroy(io.scanner);
309
310         if (!io.val) {
311                 ast_str_set(str, maxlen, "0");
312         } else {
313                 if (io.val->type == AST_EXPR_number) {
314                         int res_length;
315                         ast_str_set(str, maxlen, FP___PRINTF, io.val->u.i);
316                 } else if (io.val->u.s) {
317                         ast_str_set(str, maxlen, "%s", io.val->u.s);
318                         free(io.val->u.s);
319                 }
320                 free(io.val);
321         }
322         return ast_str_strlen(*str);
323 }
324 #endif
325
326
327 char extra_error_message[4095];
328 int extra_error_message_supplied = 0;
329 void  ast_expr_register_extra_error_info(char *message);
330 void  ast_expr_clear_extra_error_info(void);
331
332 void  ast_expr_register_extra_error_info(char *message)
333 {
334        extra_error_message_supplied=1;
335        strcpy(extra_error_message, message);
336 }
337
338 void  ast_expr_clear_extra_error_info(void)
339 {
340        extra_error_message_supplied=0;
341        extra_error_message[0] = 0;
342 }
343
344 static const char * const expr2_token_equivs1[] = 
345 {
346         "TOKEN",
347         "TOK_COND",
348         "TOK_COLONCOLON",
349         "TOK_OR",
350         "TOK_AND",
351         "TOK_EQ",
352         "TOK_GT",
353         "TOK_LT",
354         "TOK_GE",
355         "TOK_LE",
356         "TOK_NE",
357         "TOK_PLUS",
358         "TOK_MINUS",
359         "TOK_MULT",
360         "TOK_DIV",
361         "TOK_MOD",
362         "TOK_COMPL",
363         "TOK_COLON",
364         "TOK_EQTILDE",
365         "TOK_COMMA",
366         "TOK_RP",
367         "TOK_LP"
368 };
369
370 static const char * const expr2_token_equivs2[] = 
371 {
372         "<token>",
373         "?",
374         "::",
375         "|",
376         "&",
377         "=",
378         ">",
379         "<",
380         ">=",
381         "<=",
382         "!=",
383         "+",
384         "-",
385         "*",
386         "/",
387         "%",
388         "!",
389         ":",
390         "=~",
391         ",",
392         ")",
393         "("
394 };
395
396
397 static char *expr2_token_subst(const char *mess)
398 {
399         /* calc a length, malloc, fill, and return; yyerror had better free it! */
400         int len=0,i;
401         const char *p;
402         char *res, *s;
403         const char *t;
404         int expr2_token_equivs_entries = sizeof(expr2_token_equivs1)/sizeof(char*);
405
406         for (p=mess; *p; p++) {
407                 for (i=0; i<expr2_token_equivs_entries; i++) {
408                         if ( strncmp(p,expr2_token_equivs1[i],strlen(expr2_token_equivs1[i])) == 0 )
409                         {
410                                 len+=strlen(expr2_token_equivs2[i])+2;
411                                 p += strlen(expr2_token_equivs1[i])-1;
412                                 break;
413                         }
414                 }
415                 len++;
416         }
417         res = (char*)malloc(len+1);
418         res[0] = 0;
419         s = res;
420         for (p=mess; *p;) {
421                 int found = 0;
422                 for (i=0; i<expr2_token_equivs_entries; i++) {
423                         if ( strncmp(p,expr2_token_equivs1[i],strlen(expr2_token_equivs1[i])) == 0 ) {
424                                 *s++ = '\'';
425                                 for (t=expr2_token_equivs2[i]; *t;) {
426                                         *s++ = *t++;
427                                 }
428                                 *s++ = '\'';
429                                 p += strlen(expr2_token_equivs1[i]);
430                                 found = 1;
431                                 break;
432                         }
433                 }
434                 if( !found )
435                         *s++ = *p++;
436         }
437         *s++ = 0;
438         return res;
439 }
440
441 int ast_yyerror (const char *s,  yyltype *loc, struct parse_io *parseio )
442 {       
443         struct yyguts_t * yyg = (struct yyguts_t*)(parseio->scanner);
444         char spacebuf[8000]; /* best safe than sorry */
445         char spacebuf2[8000]; /* best safe than sorry */
446         int i=0;
447         char *s2 = expr2_token_subst(s);
448         spacebuf[0] = 0;
449         
450         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,
451                                                                                                                                                                                                 which is the same thing as... get this:
452                                                                                                         yyg->yy_buffer_stack[yyg->yy_buffer_stack_top]->yy_bs_column
453                                                                                                         I was tempted to just use yy_buf_pos in the STATE, but..., well:
454                                                                                                                 a. the yy_buf_pos is the current position in the buffer, which
455                                                                                                                         may not relate to the entire string/buffer because of the
456                                                                                                                         buffering.
457                                                                                                                 b. but, analysis of the situation is that when you use the
458                                                                                                                         yy_scan_string func, it creates a single buffer the size of
459                                                                                                                         string, so the two would be the same... 
460                                                                                                         so, in the end, the yycolumn macro is available, shorter, therefore easier. */
461         spacebuf2[i++]='^';
462         spacebuf2[i]= 0;
463
464 #ifdef STANDALONE3
465         /* easier to read in the standalone version */
466         printf("ast_yyerror(): %s syntax error: %s; Input:\n%s\n%s\n",  
467                         (extra_error_message_supplied?extra_error_message:""), s2, parseio->string,spacebuf2);
468 #else
469         ast_log(LOG_WARNING,"ast_yyerror(): %s syntax error: %s; Input:\n%s\n%s\n",  
470                         (extra_error_message_supplied?extra_error_message:""), s2, parseio->string,spacebuf2);
471 #endif
472 #ifndef STANDALONE
473         ast_log(LOG_WARNING,"If you have questions, please refer to https://wiki.asterisk.org/wiki/display/AST/Channel+Variables\n");
474 #endif
475         free(s2);
476         return(0);
477 }