enhance check_expr to check for parsing errors in dialplans (bug #4556)
[asterisk/asterisk.git] / utils / check_expr.c
1 #include <stdio.h>
2 #include <stddef.h>
3 #include <stdarg.h>
4 #include <string.h>
5 #include <stdlib.h>
6
7 int global_lineno = 1;
8 int global_expr_count = 0;
9 int global_expr_max_size = 0;
10 int global_expr_tot_size = 0;
11 int global_warn_count = 0;
12 int global_OK_count = 0;
13
14 struct varz
15 {
16         char varname[100]; /* a really ultra-simple, space-wasting linked list of var=val data */
17         char varval[1000]; /* if any varname is bigger than 100 chars, or val greater than 1000, then **CRASH** */
18         struct varz *next;
19 };
20
21 struct varz *global_varlist;
22
23 /* Our own version of ast_log, since the expr parser uses it. */
24
25 void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...) __attribute__ ((format (printf,5,6)));
26
27 void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
28 {
29         va_list vars;
30         va_start(vars,fmt);
31         
32         printf("LOG: lev:%d file:%s  line:%d func: %s  ",
33                    level, file, line, function);
34         vprintf(fmt, vars);
35         fflush(stdout);
36         va_end(vars);
37 }
38
39 char *find_var(const char *varname) /* the list should be pretty short, if there's any list at all */
40 {
41         struct varz *t;
42         for (t= global_varlist; t; t = t->next) {
43                 if (!strcmp(t->varname, varname)) {
44                         return t->varval;
45                 }
46         }
47         return 0;
48 }
49
50 void set_var(const char *varname, const char *varval)
51 {
52         struct varz *t = calloc(1,sizeof(struct varz));
53         strcpy(t->varname, varname);
54         strcpy(t->varval, varval);
55         t->next = global_varlist;
56         global_varlist = t;
57 }
58
59 int check_expr(char *buffer, char *error_report)
60 {
61         char *cp;
62         int oplen = 0;
63         int warn_found = 0;
64
65         error_report[0] = 0;
66         
67         for (cp=buffer;*cp;cp++) {
68                 
69                 if (*cp == '|' 
70                         || *cp == '&'
71                         || *cp == '='
72                         || *cp == '>'
73                         || *cp == '<'
74                         || *cp == '+'
75                         || *cp == '-'
76                         || *cp == '*'
77                         || *cp == '/'
78                         || *cp == '%'
79                         || *cp == '?'
80                         || *cp == ':'
81                         /*      || *cp == '('
82                                 || *cp == ')' These are pretty hard to track, as they are in funcalls, etc. */
83                         || *cp == '"') {
84                         if (*cp == '"') {
85                                 /* skip to the other end */
86                                 cp++;
87                                 while (*cp && *cp != '"')
88                                         cp++;
89                                 if (*cp == 0) {
90                                         fprintf(stderr,"Trouble? Unterminated double quote found at line %d\n",
91                                                         global_lineno);
92                                 }
93                         }
94                         else {
95                                 if ((*cp == '>'||*cp == '<' ||*cp=='!') && (*(cp+1) == '=')) {
96                                         oplen = 2;
97                                 }
98                                 else {
99                                         oplen = 1;
100                                 }
101                                 
102                                 if ((cp > buffer && *(cp-1) != ' ') || *(cp+oplen) != ' ') {
103                                         char tbuf[1000];
104                                         if (oplen == 1)
105                                                 sprintf(tbuf,"WARNING: line %d,  '%c' operator not separated by spaces. This may lead to confusion. You may wish to use double quotes to quote the grouping it is in. Please check!\n",
106                                                                 global_lineno, *cp);
107                                         else
108                                                 sprintf(tbuf,"WARNING: line %d,  '%c%c' operator not separated by spaces. This may lead to confusion. You may wish to use double quotes to quote the grouping it is in. Please check!\n",
109                                                                 global_lineno, *cp, *(cp+1));
110                                         strcat(error_report,tbuf);
111
112                                         global_warn_count++;
113                                         warn_found++;
114                                 }
115                         }
116                 }
117         }
118         return warn_found;
119 }
120
121 int check_eval(char *buffer, char *error_report)
122 {
123         char *cp, *ep, *xp, *s;
124         char evalbuf[80000];
125         extern char *ast_expr(char *);
126         int oplen = 0;
127         int warn_found = 0;
128
129         error_report[0] = 0;
130         ep = evalbuf;
131
132         for (cp=buffer;*cp;cp++) {
133                 if (*cp == '$' && *(cp+1) == '{') {
134                         int brack_lev = 1;
135                         char *xp= cp+2;
136                         
137                         while (*xp) {
138                                 if (*xp == '{')
139                                         brack_lev++;
140                                 else if (*xp == '}')
141                                         brack_lev--;
142                                 
143                                 if (brack_lev == 0)
144                                         break;
145                                 xp++;
146                         }
147                         if (*xp == '}') {
148                                 char varname[200];
149                                 char *val;
150                                 
151                                 strncpy(varname,cp+2, xp-cp-2);
152                                 varname[xp-cp-2] = 0;
153                                 cp = xp;
154                                 val = find_var(varname);
155                                 if (val) {
156                                         char *z = val;
157                                         while (*z)
158                                                 *ep++ = *z++;
159                                 }
160                                 else {
161                                         *ep++ = '5';  /* why not */
162                                         *ep++ = '5';
163                                         *ep++ = '5';
164                                 }
165                         }
166                         else {
167                                 printf("Unterminated variable reference at line %d\n", global_lineno);
168                                 *ep++ = *cp;
169                         }
170                 }
171                 else if (*cp == '\\') {
172                         /* braindead simple elim of backslash */
173                         cp++;
174                         *ep++ = *cp;
175                 }
176                 else
177                         *ep++ = *cp;
178         }
179         *ep++ = 0;
180
181         /* now, run the test */
182         s = ast_expr(evalbuf);
183         if (s) {
184                 sprintf(error_report,"line %d, evaluation of $[ %s ] result: %s\n", global_lineno, evalbuf, s);
185                 return 1;
186         }
187         else {
188                 sprintf(error_report,"line %d, evaluation of $[ %s ] result: ****SYNTAX ERROR****\n", global_lineno, evalbuf);
189                 return 1;
190         }
191 }
192
193
194 void parse_file(const char *fname)
195 {
196         FILE *f = fopen(fname,"r");
197         FILE *l = fopen("expr2_log","w");
198         int c1;
199         char last_char= 0;
200         char buffer[30000]; /* I sure hope no expr gets this big! */
201         
202         if (!f) {
203                 fprintf(stderr,"Couldn't open %s for reading... need an extensions.conf file to parse!\n");
204                 exit(20);
205         }
206         if (!l) {
207                 fprintf(stderr,"Couldn't open 'expr2_log' file for writing... please fix and re-run!\n");
208                 exit(21);
209         }
210         
211         global_lineno = 1;
212         
213         while ((c1 = fgetc(f)) != EOF) {
214                 if (c1 == '\n')
215                         global_lineno++;
216                 else if (c1 == '[') {
217                         if (last_char == '$') {
218                                 /* bingo, an expr */
219                                 int bracklev = 1;
220                                 int bufcount = 0;
221                                 int retval;
222                                 char error_report[30000];
223                                 
224                                 while ((c1 = fgetc(f)) != EOF) {
225                                         if (c1 == '[')
226                                                 bracklev++;
227                                         else if (c1 == ']')
228                                                 bracklev--;
229                                         if (c1 == '\n') {
230                                                 fprintf(l, "ERROR-- A newline in an expression? Weird! ...at line %d\n", global_lineno);
231                                                 fclose(f);
232                                                 fclose(l);
233                                                 printf("--- ERROR --- A newline in the middle of an expression at line %d!\n", global_lineno);
234                                         }
235                                         
236                                         if (bracklev == 0)
237                                                 break;
238                                         buffer[bufcount++] = c1;
239                                 }
240                                 if (c1 == EOF) {
241                                         fprintf(l, "ERROR-- End of File Reached in the middle of an Expr at line %d\n", global_lineno);
242                                         fclose(f);
243                                         fclose(l);
244                                         printf("--- ERROR --- EOF reached in middle of an expression at line %d!\n", global_lineno);
245                                         exit(22);
246                                 }
247                                 
248                                 buffer[bufcount] = 0;
249                                 /* update stats */
250                                 global_expr_tot_size += bufcount;
251                                 global_expr_count++;
252                                 if (bufcount > global_expr_max_size)
253                                         global_expr_max_size = bufcount;
254                                 
255                                 retval = check_expr(buffer, error_report); /* check_expr should bump the warning counter */
256                                 if (retval != 0) {
257                                         /* print error report */
258                                         printf("Warning(s) at line %d, expression: $[%s]; see expr2_log file for details\n", 
259                                                    global_lineno, buffer);
260                                         fprintf(l, "%s", error_report);
261                                 }
262                                 else {
263                                         printf("OK -- $[%s] at line %d\n", buffer, global_lineno);
264                                         global_OK_count++;
265                                 }
266                                 error_report[0] = 0;
267                                 retval = check_eval(buffer, error_report);
268                                 fprintf(l, "%s", error_report);
269                         }
270                 }
271                 last_char = c1;
272         }
273         printf("Summary:\n  Expressions detected: %d\n  Expressions OK:  %d\n  Total # Warnings:   %d\n  Longest Expr:   %d chars\n  Ave expr len:  %d chars\n",
274                    global_expr_count,
275                    global_OK_count,
276                    global_warn_count,
277                    global_expr_max_size,
278                    (global_expr_count) ? global_expr_tot_size/global_expr_count : 0);
279         
280         fclose(f);
281         fclose(l);
282 }
283
284
285 main(int argc,char **argv)
286 {
287         int argc1;
288         char *eq;
289         
290         if (argc < 2) {
291                 printf("Hey-- give me a path to an extensions.conf file!\n");
292                 exit(19);
293         }
294         global_varlist = 0;
295         for (argc1=2;argc1 < argc; argc1++) {
296                 if ((eq = strchr(argv[argc1],'='))) {
297                         *eq = 0;
298                         set_var(argv[argc1],eq+1);
299                 }
300         }
301
302         /* parse command args for x=y and set varz */
303         
304         parse_file(argv[1]);
305 }