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