Code previously added to ast_expr2f.c warranted a change in the source file ast_expr2.fl.
[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 ([a-zA-Z0-9\.';\\_^#@]|[\x80-\xff]|($[^{]))+\$\{        {
182                 curlycount = 0;
183                 BEGIN(var);
184                 yymore();
185         }
186
187 <var>[^{}]*\}   {
188                 curlycount--;
189                 if (curlycount < 0) {
190                         BEGIN(trail);
191                         yymore();
192                 } else {
193                         yymore();
194                 }
195         }
196         
197 <var>[^{}]*\{   {
198                 curlycount++;
199                 yymore();
200         }
201         
202
203 <trail>[^-\t\r \n$():?%/+=*<>!|&]*      {
204                 BEGIN(0);
205                 SET_COLUMNS;
206                 SET_STRING;
207                 return TOKEN;
208         }
209         
210 <trail>[^-\t\r \n$():?%/+=*<>!|&]*\$\{  {
211                 curlycount = 0;
212                 BEGIN(var);
213                 yymore();
214         }
215         
216 <trail>[-\t\r \n$():?%/+=*<>!|&]        {
217                 char c = yytext[yyleng-1];
218                 BEGIN(0);
219                 unput(c);
220                 SET_COLUMNS;
221                 SET_STRING;
222                 return TOKEN;
223         }
224         
225 <trail><<EOF>>  {
226                 BEGIN(0);
227                 SET_COLUMNS;
228                 SET_STRING;
229                 return TOKEN;
230                 /*actually, if an expr is only a variable ref, this could happen a LOT */
231         }
232
233 %%
234
235 /* I'm putting the interface routine to the whole parse here in the flexer input file
236    mainly because of all the flexer initialization that has to be done. Shouldn't matter
237    where it is, as long as it's somewhere. I didn't want to define a prototype for the
238    ast_yy_scan_string in the .y file, because then, I'd have to define YY_BUFFER_STATE there...
239         UGH! that would be inappropriate. */
240
241 int ast_yyparse(void *); /* need to/should define this prototype for the call to yyparse */
242 int ast_yyerror(const char *, YYLTYPE *, struct parse_io *); /* likewise */
243
244 void ast_yyfree(void *ptr, yyscan_t yyscanner)
245 {
246     /* the normal generated yyfree func just frees its first arg;
247      this get complaints on some systems, as sometimes this
248      arg is a nil ptr! It's usually not fatal, but is irritating! */
249         free( (char *) ptr );
250 }
251
252 int ast_expr(char *expr, char *buf, int length, struct ast_channel *chan)
253 {
254         struct parse_io io = { .string = expr, .chan = chan };
255         int return_value = 0;
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)
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                         free(io.val->u.s);
287                 }
288                 free(io.val);
289         }
290         return return_value;
291 }
292
293 #ifndef STANDALONE
294 int ast_str_expr(struct ast_str **str, ssize_t maxlen, struct ast_channel *chan, char *expr)
295 {
296         struct parse_io io = { .string = expr, .chan = chan };
297
298         ast_yylex_init(&io.scanner);
299         ast_yy_scan_string(expr, io.scanner);
300         ast_yyparse ((void *) &io);
301         ast_yylex_destroy(io.scanner);
302
303         if (!io.val) {
304                 ast_str_set(str, maxlen, "0");
305         } else {
306                 if (io.val->type == AST_EXPR_number) {
307                         int res_length;
308                         ast_str_set(str, maxlen, FP___PRINTF, io.val->u.i);
309                 } else if (io.val->u.s) {
310                         ast_str_set(str, maxlen, "%s", io.val->u.s);
311                         free(io.val->u.s);
312                 }
313                 free(io.val);
314         }
315         return ast_str_strlen(*str);
316 }
317 #endif
318
319
320 char extra_error_message[4095];
321 int extra_error_message_supplied = 0;
322 void  ast_expr_register_extra_error_info(char *message);
323 void  ast_expr_clear_extra_error_info(void);
324
325 void  ast_expr_register_extra_error_info(char *message)
326 {
327        extra_error_message_supplied=1;
328        strcpy(extra_error_message, message);
329 }
330
331 void  ast_expr_clear_extra_error_info(void)
332 {
333        extra_error_message_supplied=0;
334        extra_error_message[0] = 0;
335 }
336
337 static const char * const expr2_token_equivs1[] = 
338 {
339         "TOKEN",
340         "TOK_COND",
341         "TOK_COLONCOLON",
342         "TOK_OR",
343         "TOK_AND",
344         "TOK_EQ",
345         "TOK_GT",
346         "TOK_LT",
347         "TOK_GE",
348         "TOK_LE",
349         "TOK_NE",
350         "TOK_PLUS",
351         "TOK_MINUS",
352         "TOK_MULT",
353         "TOK_DIV",
354         "TOK_MOD",
355         "TOK_COMPL",
356         "TOK_COLON",
357         "TOK_EQTILDE",
358         "TOK_COMMA",
359         "TOK_RP",
360         "TOK_LP"
361 };
362
363 static const char * const expr2_token_equivs2[] = 
364 {
365         "<token>",
366         "?",
367         "::",
368         "|",
369         "&",
370         "=",
371         ">",
372         "<",
373         ">=",
374         "<=",
375         "!=",
376         "+",
377         "-",
378         "*",
379         "/",
380         "%",
381         "!",
382         ":",
383         "=~",
384         ",",
385         ")",
386         "("
387 };
388
389
390 static char *expr2_token_subst(const char *mess)
391 {
392         /* calc a length, malloc, fill, and return; yyerror had better free it! */
393         int len=0,i;
394         const char *p;
395         char *res, *s;
396         const char *t;
397         int expr2_token_equivs_entries = sizeof(expr2_token_equivs1)/sizeof(char*);
398
399         for (p=mess; *p; p++) {
400                 for (i=0; i<expr2_token_equivs_entries; i++) {
401                         if ( strncmp(p,expr2_token_equivs1[i],strlen(expr2_token_equivs1[i])) == 0 )
402                         {
403                                 len+=strlen(expr2_token_equivs2[i])+2;
404                                 p += strlen(expr2_token_equivs1[i])-1;
405                                 break;
406                         }
407                 }
408                 len++;
409         }
410         res = (char*)malloc(len+1);
411         res[0] = 0;
412         s = res;
413         for (p=mess; *p;) {
414                 int found = 0;
415                 for (i=0; i<expr2_token_equivs_entries; i++) {
416                         if ( strncmp(p,expr2_token_equivs1[i],strlen(expr2_token_equivs1[i])) == 0 ) {
417                                 *s++ = '\'';
418                                 for (t=expr2_token_equivs2[i]; *t;) {
419                                         *s++ = *t++;
420                                 }
421                                 *s++ = '\'';
422                                 p += strlen(expr2_token_equivs1[i]);
423                                 found = 1;
424                                 break;
425                         }
426                 }
427                 if( !found )
428                         *s++ = *p++;
429         }
430         *s++ = 0;
431         return res;
432 }
433
434 int ast_yyerror (const char *s,  yyltype *loc, struct parse_io *parseio )
435 {       
436         struct yyguts_t * yyg = (struct yyguts_t*)(parseio->scanner);
437         char spacebuf[8000]; /* best safe than sorry */
438         char spacebuf2[8000]; /* best safe than sorry */
439         int i=0;
440         char *s2 = expr2_token_subst(s);
441         spacebuf[0] = 0;
442         
443         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,
444                                                                                                                                                                                                 which is the same thing as... get this:
445                                                                                                         yyg->yy_buffer_stack[yyg->yy_buffer_stack_top]->yy_bs_column
446                                                                                                         I was tempted to just use yy_buf_pos in the STATE, but..., well:
447                                                                                                                 a. the yy_buf_pos is the current position in the buffer, which
448                                                                                                                         may not relate to the entire string/buffer because of the
449                                                                                                                         buffering.
450                                                                                                                 b. but, analysis of the situation is that when you use the
451                                                                                                                         yy_scan_string func, it creates a single buffer the size of
452                                                                                                                         string, so the two would be the same... 
453                                                                                                         so, in the end, the yycolumn macro is available, shorter, therefore easier. */
454         spacebuf2[i++]='^';
455         spacebuf2[i]= 0;
456
457 #ifdef STANDALONE3
458         /* easier to read in the standalone version */
459         printf("ast_yyerror(): %s syntax error: %s; Input:\n%s\n%s\n",  
460                         (extra_error_message_supplied?extra_error_message:""), s2, parseio->string,spacebuf2);
461 #else
462         ast_log(LOG_WARNING,"ast_yyerror(): %s syntax error: %s; Input:\n%s\n%s\n",  
463                         (extra_error_message_supplied?extra_error_message:""), s2, parseio->string,spacebuf2);
464 #endif
465 #ifndef STANDALONE
466         ast_log(LOG_WARNING,"If you have questions, please refer to doc/tex/channelvariables.tex in the asterisk source.\n");
467 #endif
468         free(s2);
469         return(0);
470 }