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