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