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