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