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