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