8eaea639faa2866f10cbb8d5cdad12e9d97f08bc
[asterisk/asterisk.git] / 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 Parser
23  */
24
25 #include <sys/types.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <locale.h>
30 #include <ctype.h>
31 #if !defined(SOLARIS) && !defined(__CYGWIN__)
32 #include <err.h>
33 #else
34 #define quad_t int64_t
35 #endif
36 #include <errno.h>
37 #include <regex.h>
38 #include <limits.h>
39
40 #include "asterisk.h"
41
42 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
43
44 #include "asterisk/ast_expr.h"
45 #include "asterisk/logger.h"
46 #include "asterisk/strings.h"
47
48 enum valtype {
49         AST_EXPR_integer, AST_EXPR_numeric_string, AST_EXPR_string
50 } ;
51
52 struct val {
53         enum valtype type;
54         union {
55                 char *s;
56                 quad_t i;
57         } u;
58 } ;
59
60 #include "ast_expr2.h" /* the o/p of the bison on ast_expr2.y */
61
62 #define SET_COLUMNS yylloc_param->first_column = (int)(yyg->yytext_r - YY_CURRENT_BUFFER_LVALUE->yy_ch_buf);yylloc_param->last_column = yylloc_param->last_column + yyleng - 1; yylloc_param->first_line = yylloc_param->last_line = 1
63 #define SET_STRING yylval_param->val = (struct val *)calloc(sizeof(struct val),1); yylval_param->val->type = AST_EXPR_string; yylval_param->val->u.s = strdup(yytext);
64 #define SET_NUMERIC_STRING yylval_param->val = (struct val *)calloc(sizeof(struct val),1); yylval_param->val->type = AST_EXPR_numeric_string; yylval_param->val->u.s = strdup(yytext);
65
66 struct parse_io
67 {
68         char *string;
69         struct val *val;
70         yyscan_t scanner;
71 };
72  
73 void ast_yyset_column(int column_no, yyscan_t yyscanner);
74 int ast_yyget_column(yyscan_t yyscanner);
75 static int curlycount = 0;
76 %}
77
78 %option prefix="ast_yy"
79 %option batch
80 %option outfile="ast_expr2f.c"
81 %option reentrant
82 %option bison-bridge
83 %option bison-locations
84 %option noyywrap
85 %x var trail
86
87 %%
88
89 \|      { SET_COLUMNS; SET_STRING; return TOK_OR;}
90 \&      { SET_COLUMNS; SET_STRING; return TOK_AND;}
91 \=      { SET_COLUMNS; SET_STRING; return TOK_EQ;}
92 \>      { SET_COLUMNS; SET_STRING; return TOK_GT;}
93 \<      { SET_COLUMNS; SET_STRING; return TOK_LT;}
94 \>\=    { SET_COLUMNS; SET_STRING; return TOK_GE;}
95 \<\=    { SET_COLUMNS; SET_STRING; return TOK_LE;}
96 \!\=    { SET_COLUMNS; SET_STRING; return TOK_NE;}
97 \+      { SET_COLUMNS; SET_STRING; return TOK_PLUS;}
98 \-      { SET_COLUMNS; SET_STRING; return TOK_MINUS;}
99 \*      { SET_COLUMNS; SET_STRING; return TOK_MULT;}
100 \/      { SET_COLUMNS; SET_STRING; return TOK_DIV;}
101 \%      { SET_COLUMNS; SET_STRING; return TOK_MOD;}
102 \?      { SET_COLUMNS; SET_STRING; return TOK_COND;}
103 \:      { SET_COLUMNS; SET_STRING; return TOK_COLON;}
104 \:\:    { SET_COLUMNS; SET_STRING; return TOK_COLONCOLON;}
105 \(      { SET_COLUMNS; SET_STRING; return TOK_LP;}
106 \)      { SET_COLUMNS; SET_STRING; return TOK_RP;}
107 \$\{   {/* gather the contents of ${} expressions, with trailing stuff, into a single TOKEN. They are much more complex now than they used to be */
108                        curlycount = 0; BEGIN(var); yymore();}
109
110 [       \r]             {}
111 \"[^"]*\"   {SET_COLUMNS; SET_STRING; return TOKEN;}
112
113 [\n]    {/* what to do with eol */}
114 [0-9]+          {   SET_COLUMNS;  /* the original behavior of the expression parser was to bring in numbers as a numeric string */
115                                 SET_NUMERIC_STRING;
116                                 return TOKEN;}
117 [a-zA-Z0-9,.';\\_^%$#@!]+       {SET_COLUMNS; SET_STRING; return TOKEN;}
118
119 <var>[^{}]*\}  {curlycount--; if(curlycount < 0){ BEGIN(trail);  yymore();} else {  yymore();}}
120 <var>[^{}]*\{  {curlycount++; yymore();  }
121 <trail>[^-\t\r \n$():?%/+=*<>!|&]* {BEGIN(0); SET_COLUMNS; SET_STRING; return TOKEN;}
122 <trail>[-\t\r \n$():?%/+=*<>!|&]        {char c = yytext[yyleng-1]; BEGIN(0); unput(c); SET_COLUMNS; SET_STRING; return TOKEN;}
123 <trail>\$\{            {curlycount = 0; BEGIN(var); yymore();  }
124 <trail><<EOF>>          {BEGIN(0); SET_COLUMNS; SET_STRING; return TOKEN; /* actually, if an expr is only a variable ref, this could happen a LOT */}
125
126 %%
127
128 /* I'm putting the interface routine to the whole parse here in the flexer input file
129    mainly because of all the flexer initialization that has to be done. Shouldn't matter
130    where it is, as long as it's somewhere. I didn't want to define a prototype for the
131    ast_yy_scan_string in the .y file, because then, I'd have to define YY_BUFFER_STATE there...
132         UGH! that would be inappropriate. */
133
134 int ast_yyparse(void *); /* need to/should define this prototype for the call to yyparse */
135 int ast_yyerror(const char *, YYLTYPE *, struct parse_io *); /* likewise */
136
137 int ast_expr(char *expr, char *buf, int length)
138 {
139         struct parse_io io;
140         int return_value = 0;
141         
142         memset(&io, 0, sizeof(io));
143         io.string = expr;  /* to pass to the error routine */
144         
145         ast_yylex_init(&io.scanner);
146         
147         ast_yy_scan_string(expr, io.scanner);
148         
149         ast_yyparse ((void *) &io);
150
151         ast_yylex_destroy(io.scanner);
152
153         if (!io.val) {
154                 if (length > 1) {
155                         strcpy(buf, "0");
156                         return_value = 1;
157                 }
158         } else {
159                 if (io.val->type == AST_EXPR_integer) {
160                         int res_length;
161
162                         res_length = snprintf(buf, length, "%ld", (long int) io.val->u.i);
163                         return_value = (res_length <= length) ? res_length : length;
164                 } else {
165 #ifdef STANDALONE
166                         strncpy(buf, io.val->u.s, length - 1);
167 #else /* !STANDALONE */
168                         ast_copy_string(buf, io.val->u.s, length);
169 #endif /* STANDALONE */
170                         return_value = strlen(buf);
171                         free(io.val->u.s);
172                 }
173                 free(io.val);
174         }
175         return return_value;
176 }
177
178 int ast_yyerror (const char *s,  yyltype *loc, struct parse_io *parseio )
179 {       
180         struct yyguts_t * yyg = (struct yyguts_t*)(parseio->scanner);
181         char spacebuf[8000]; /* best safe than sorry */
182         char spacebuf2[8000]; /* best safe than sorry */
183         int i=0;
184         spacebuf[0] = 0;
185         
186         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,
187                                                                                                         which is the same thing as... get this:
188                                                                                                         yyg->yy_buffer_stack[yyg->yy_buffer_stack_top]->yy_bs_column
189                                                                                                         I was tempted to just use yy_buf_pos in the STATE, but..., well:
190                                                                                                                 a. the yy_buf_pos is the current position in the buffer, which
191                                                                                                                         may not relate to the entire string/buffer because of the
192                                                                                                                         buffering.
193                                                                                                                 b. but, analysis of the situation is that when you use the
194                                                                                                                         yy_scan_string func, it creates a single buffer the size of
195                                                                                                                         string, so the two would be the same... 
196                                                                                                         so, in the end, the yycolumn macro is available, shorter, therefore easier. */
197         spacebuf2[i++]='^';
198         spacebuf2[i]= 0;
199
200 #ifdef STANDALONE3
201         /* easier to read in the standalone version */
202         printf("ast_yyerror(): syntax error: %s; Input:\n%s\n%s\n",  
203                         s, parseio->string,spacebuf2);
204 #else
205         ast_log(LOG_WARNING,"ast_yyerror(): syntax error: %s; Input:\n%s\n%s\n",  
206                         s, parseio->string,spacebuf2);
207 #endif
208 #ifndef STANDALONE
209         ast_log(LOG_WARNING,"If you have questions, please refer to doc/channelvariables.txt in the asterisk source.\n");
210 #endif
211         return(0);
212 }