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