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