In regards to changes for 9508, expr2 system choking on floating point numbers, I...
[asterisk/asterisk.git] / utils / ael_main.c
1 #include <sys/types.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <stdarg.h>
5 #include <string.h>
6 #include <locale.h>
7 #include <ctype.h>
8 #include <errno.h>
9 #include <regex.h>
10 #include <limits.h>
11
12 #include "asterisk/ast_expr.h"
13 #include "asterisk/channel.h"
14 #include "asterisk/module.h"
15 #include "asterisk/app.h"
16 #include "asterisk/ael_structs.h"
17
18 struct namelist
19 {
20         char name[100];
21         char name2[100];
22         struct namelist *next;
23 };
24
25 struct ast_context 
26 {
27         int extension_count;
28         char name[100];
29         char registrar[100];
30         struct namelist *includes;
31         struct namelist *ignorepats;
32         struct namelist *switches;
33         struct namelist *eswitches;
34
35         struct namelist *includes_last;
36         struct namelist *ignorepats_last;
37         struct namelist *switches_last;
38         struct namelist *eswitches_last;
39
40         struct ast_context *next;
41 };
42
43 #define ADD_LAST(headptr,memptr) if(!headptr){ headptr=(memptr); (headptr##_last)=(memptr);} else {(headptr##_last)->next = (memptr); (headptr##_last) = (memptr);}
44
45 void destroy_namelist(struct namelist *x);
46 void destroy_namelist(struct namelist *x)
47 {
48         struct namelist *z,*z2;
49         for(z=x; z; z = z2)
50         {
51                 z2 = z->next;
52                 z->next = 0;
53                 free(z);
54         }
55 }
56
57 struct namelist *create_name(char *name);
58 struct namelist *create_name(char *name)
59 {
60         struct namelist *x = calloc(1, sizeof(*x));
61         if (!x)
62                 return NULL;
63         strncpy(x->name, name, sizeof(x->name) - 1);
64         return x;
65 }
66
67 struct ast_context *context_list;
68 struct ast_context *last_context;
69 struct namelist *globalvars;
70 struct namelist *globalvars_last;
71
72 int conts=0, extens=0, priors=0;
73 char last_exten[18000];
74 char ast_config_AST_CONFIG_DIR[PATH_MAX];
75 char ast_config_AST_VAR_DIR[PATH_MAX];
76
77 void ast_add_profile(void);
78 void ast_cli_register_multiple(void);
79 void ast_register_file_version(void);
80 void ast_unregister_file_version(void);
81 int ast_add_extension2(struct ast_context *con,
82                                            int replace, const char *extension, int priority, const char *label, const char *callerid,
83                                                 const char *application, void *data, void (*datad)(void *),
84                                            const char *registrar);
85 void pbx_builtin_setvar(void *chan, void *data);
86 struct ast_context * ast_context_create(void **extcontexts, const char *name, const char *registrar);
87 void ast_context_add_ignorepat2(struct ast_context *con, const char *value, const char *registrar);
88 void ast_context_add_include2(struct ast_context *con, const char *value, const char *registrar);
89 void ast_context_add_switch2(struct ast_context *con, const char *value, const char *data, int eval, const char *registrar);
90 void ast_merge_contexts_and_delete(void);
91 void ast_context_verify_includes(void);
92 struct ast_context * ast_walk_contexts(void);
93 void ast_cli_unregister_multiple(void);
94 void ast_context_destroy(void);
95 void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...);
96 char *ast_process_quotes_and_slashes(char *start, char find, char replace_with);
97 void ast_verbose(const char *fmt, ...);
98 struct ast_app *pbx_findapp(const char *app);
99 void filter_leading_space_from_exprs(char *str);
100 void filter_newlines(char *str);
101 static int quiet = 0;
102 static int no_comp = 0;
103 static int use_curr_dir = 0;
104 static int dump_extensions = 0;
105 static int FIRST_TIME = 0;
106 static FILE *dumpfile;
107
108 struct ast_app *pbx_findapp(const char *app)
109 {
110         return (struct ast_app*)1; /* so as not to trigger an error */
111 }
112
113 struct ast_custom_function *ast_custom_function_find(const char *name);
114
115
116 struct ast_custom_function *ast_custom_function_find(const char *name)
117 {
118         return 0; /* in "standalone" mode, functions are just not avail */
119 }
120
121
122 void ast_add_profile(void)
123 {
124         if (!no_comp)
125                 printf("Executed ast_add_profile();\n");
126 }
127
128 int ast_loader_register(int (*updater)(void))
129 {
130         return 1;
131 }
132
133 int ast_loader_unregister(int (*updater)(void))
134 {
135         return 1;
136 }
137 void ast_module_register(const struct ast_module_info *x)
138 {
139 }
140
141 void ast_module_unregister(const struct ast_module_info *x)
142 {
143 }
144
145
146 void ast_cli_register_multiple(void)
147 {
148         if(!no_comp)
149                 printf("Executed ast_cli_register_multiple();\n");
150 }
151
152 void ast_register_file_version(void)
153 {
154         /* if(!no_comp)
155                 printf("Executed ast_register_file_version();\n"); */
156         /* I'm erasing this, because I don't think anyone really ever needs to see it anyway */
157 }
158
159 void ast_unregister_file_version(void)
160 {
161         /* if(!no_comp)
162                 printf("Executed ast_unregister_file_version();\n"); */
163         /* I'm erasing this, because I don't think anyone really ever needs to see it anyway */
164
165 }
166 int ast_add_extension2(struct ast_context *con,
167                         int replace, const char *extension, int priority, const char *label, const char *callerid,
168                         const char *application, void *data, void (*datad)(void *),
169                         const char *registrar)
170 {
171         priors++;
172         con->extension_count++;
173         if (strcmp(extension,last_exten) != 0) {
174                 extens++;
175                 strcpy(last_exten, extension);
176         }
177         if (!label) {
178                 label = "(null)";
179         }
180         if (!callerid) {
181                 callerid = "(null)";
182         }
183         if (!application) {
184                 application = "(null)";
185         }
186
187         if(!no_comp)
188                 printf("Executed ast_add_extension2(context=%s, rep=%d, exten=%s, priority=%d, label=%s, callerid=%s, appl=%s, data=%s, FREE, registrar=%s);\n",
189                            con->name, replace, extension, priority, label, callerid, application, (data?(char*)data:"(null)"), registrar);
190
191         if( dump_extensions && dumpfile ) {
192                 struct namelist *n;
193                 char *data2,*data3=0;
194                 int commacount = 0;
195
196                 if( FIRST_TIME ) {
197                         FIRST_TIME = 0;
198                         
199                         if( globalvars )
200                                 fprintf(dumpfile,"[globals]\n");
201                         
202                         for(n=globalvars;n;n=n->next) {
203                                 fprintf(dumpfile, "%s\n", n->name);
204                         }
205                 }
206                 
207                 /* print out each extension , possibly the context header also */
208                 if( con != last_context ) {
209                         fprintf(dumpfile,"\n\n[%s]\n", con->name);
210                         last_context = con;
211                         for(n=con->ignorepats;n;n=n->next) {
212                                 fprintf(dumpfile, "ignorepat => %s\n", n->name);
213                         }
214                         for(n=con->includes;n;n=n->next) {
215                                 fprintf(dumpfile, "include => %s\n", n->name);
216                         }
217                         for(n=con->switches;n;n=n->next) {
218                                 fprintf(dumpfile, "switch => %s/%s\n", n->name, n->name2);
219                         }
220                         for(n=con->eswitches;n;n=n->next) {
221                                 fprintf(dumpfile, "eswitch => %s/%s\n", n->name, n->name2);
222                         }
223                         
224                 }
225                 if( data ) {
226                         filter_newlines((char*)data);
227                         filter_leading_space_from_exprs((char*)data);
228
229                         /* compiling turns commas into vertical bars in the app data, and also removes the backslash from before escaped commas;
230                            we have to restore the escaping backslash in front of any commas; the vertical bars are OK to leave as-is */
231                         for (data2 = data; *data2; data2++) {
232                                 if (*data2 == ',')
233                                         commacount++;  /* we need to know how much bigger the string will grow-- one backslash for each comma  */
234                         }
235                         if (commacount) 
236                         {
237                                 char *d3,*d4;
238                                 
239                                 data2 = (char*)malloc(strlen(data)+commacount+1);
240                                 data3 = data;
241                                 d3 = data;
242                                 d4 = data2;
243                                 while (*d3) {
244                                         if (*d3 == ',') {
245                                                 *d4++ = '\\'; /* put a backslash in front of each comma */
246                                                 *d4++ = *d3++;
247                                         } else
248                                                 *d4++ = *d3++;  /* or just copy the char */
249                                 }
250                                 *d4++ = 0;  /* cap off the new string */
251                                 data = data2;
252                         } else
253                                 data2 = 0;
254                         
255                         if( strcmp(label,"(null)") != 0  )
256                                 fprintf(dumpfile,"exten => %s,%d(%s),%s(%s)\n", extension, priority, label, application, (char*)data);
257                         else
258                                 fprintf(dumpfile,"exten => %s,%d,%s(%s)\n", extension, priority, application, (char*)data);
259
260                         if (data2) {
261                                 free(data2);
262                                 data2 = 0;
263                                 data = data3; /* restore data to pre-messedup state */
264                         }
265
266                 } else {
267
268                         if( strcmp(label,"(null)") != 0  )
269                                 fprintf(dumpfile,"exten => %s,%d(%s),%s\n", extension, priority, label, application);
270                         else
271                                 fprintf(dumpfile,"exten => %s,%d,%s\n", extension, priority, application);
272                 }
273         }
274         
275         /* since add_extension2 is responsible for the malloc'd data stuff */
276         if( data )
277                 free(data);
278         return 0;
279 }
280
281 void pbx_builtin_setvar(void *chan, void *data)
282 {
283         struct namelist *x = create_name((char*)data);
284         if(!no_comp)
285                 printf("Executed pbx_builtin_setvar(chan, data=%s);\n", (char*)data);
286
287         if( dump_extensions ) {
288                 x = create_name((char*)data);
289                 ADD_LAST(globalvars,x);
290         }
291 }
292         
293
294 struct ast_context * ast_context_create(void **extcontexts, const char *name, const char *registrar)
295 {
296         struct ast_context *x = calloc(1, sizeof(*x));
297         if (!x)
298                 return NULL;
299         x->next = context_list;
300         context_list = x;
301         if (!no_comp)
302                 printf("Executed ast_context_create(conts, name=%s, registrar=%s);\n", name, registrar);
303         conts++;
304         strncpy(x->name, name, sizeof(x->name) - 1);
305         strncpy(x->registrar, registrar, sizeof(x->registrar) - 1);
306         return x;
307 }
308
309 void ast_context_add_ignorepat2(struct ast_context *con, const char *value, const char *registrar)
310 {
311         if(!no_comp)
312                 printf("Executed ast_context_add_ignorepat2(con, value=%s, registrar=%s);\n", value, registrar);
313         if( dump_extensions ) {
314                 struct namelist *x;
315                 x = create_name((char*)value);
316                 ADD_LAST(con->ignorepats,x);
317         }
318 }
319
320 void ast_context_add_include2(struct ast_context *con, const char *value, const char *registrar)
321 {
322         if(!no_comp)
323                 printf("Executed ast_context_add_include2(con, value=%s, registrar=%s);\n", value, registrar);
324         if( dump_extensions ) {
325                 struct namelist *x;
326                 x = create_name((char*)value);
327                 ADD_LAST(con->includes,x);
328         }
329 }
330
331 void ast_context_add_switch2(struct ast_context *con, const char *value, const char *data, int eval, const char *registrar)
332 {
333         if(!no_comp)
334                 printf("Executed ast_context_add_switch2(con, value=%s, data=%s, eval=%d, registrar=%s);\n", value, data, eval, registrar);
335         if( dump_extensions ) {
336                 struct namelist *x;
337                 x = create_name((char*)value);
338                 strncpy(x->name2,data,100);
339                 if( eval ) {
340
341                         ADD_LAST(con->switches,x);
342
343                 } else {
344
345                         ADD_LAST(con->eswitches,x);
346                 }
347         }
348 }
349
350 void ast_merge_contexts_and_delete(void)
351 {
352         if(!no_comp)
353                 printf("Executed ast_merge_contexts_and_delete();\n");
354 }
355
356 void ast_context_verify_includes(void)
357 {
358         if(!no_comp)
359                 printf("Executed ast_context_verify_includes();\n");
360 }
361
362 struct ast_context * ast_walk_contexts(void)
363 {
364         if(!no_comp)
365                 printf("Executed ast_walk_contexts();\n");
366         return 0;
367 }
368
369 void ast_cli_unregister_multiple(void)
370 {
371         if(!no_comp)
372                 printf("Executed ast_cli_unregister_multiple();\n");
373 }
374
375 void ast_context_destroy(void)
376 {
377         if( !no_comp)
378                 printf("Executed ast_context_destroy();\n");
379 }
380
381 void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
382 {
383         va_list vars;
384         va_start(vars,fmt);
385         if( !quiet || level > 2 ) {
386             printf("LOG: lev:%d file:%s  line:%d func: %s  ",
387                    level, file, line, function);
388             vprintf(fmt, vars);
389             fflush(stdout);
390             va_end(vars);
391         }
392 }
393
394 void ast_verbose(const char *fmt, ...)
395 {
396         va_list vars;
397         va_start(vars,fmt);
398
399         printf("VERBOSE: ");
400         vprintf(fmt, vars);
401         fflush(stdout);
402         va_end(vars);
403 }
404
405 char *ast_process_quotes_and_slashes(char *start, char find, char replace_with)
406 {
407         char *dataPut = start;
408         int inEscape = 0;
409         int inQuotes = 0;
410
411         for (; *start; start++) {
412                 if (inEscape) {
413                         *dataPut++ = *start;       /* Always goes verbatim */
414                         inEscape = 0;
415                 } else {
416                         if (*start == '\\') {
417                                 inEscape = 1;      /* Do not copy \ into the data */
418                         } else if (*start == '\'') {
419                                 inQuotes = 1-inQuotes;   /* Do not copy ' into the data */
420                         } else {
421                                 /* Replace , with |, unless in quotes */
422                                 *dataPut++ = inQuotes ? *start : ((*start==find) ? replace_with : *start);
423                         }
424                 }
425         }
426         if (start != dataPut)
427                 *dataPut = 0;
428         return dataPut;
429 }
430
431 void filter_leading_space_from_exprs(char *str)
432 {
433         /*  Mainly for aesthetics */
434         char *t, *v, *u = str;
435         
436         while ( u && *u ) {
437
438                 if( *u == '$' && *(u+1) == '[' ) {
439                         t = u+2;
440                         while( *t == '\n' || *t == '\r' || *t == '\t' || *t == ' ' ) {
441                                 v = t;
442                                 while ( *v ) {
443                                         *v = *(v+1);
444                                         v++;
445                                 }
446                         }
447                 }
448                 
449                 u++;
450         }
451 }
452
453 void filter_newlines(char *str)
454 {
455         /* remove all newlines, returns  */
456         char *t=str;
457         while( t && *t ) {
458                 if( *t == '\n' || *t == '\r' ) {
459                         *t = ' '; /* just replace newlines and returns with spaces; they act as
460                                                  token separators, and just blindly removing them could be
461                                                  harmful. */
462                 }
463                 t++;
464         }
465 }
466
467
468 extern struct module_symbols mod_data;
469 extern int ael_external_load_module(void);
470
471 int main(int argc, char **argv)
472 {
473         int i;
474         struct namelist *n;
475         struct ast_context *lp,*lp2;
476         
477         for(i=1;i<argc;i++) {
478                 if( argv[i][0] == '-' && argv[i][1] == 'n' )
479                         no_comp =1;
480                 if( argv[i][0] == '-' && argv[i][1] == 'q' ) {
481                         quiet = 1;
482                         no_comp =1;
483                 }
484                 if( argv[i][0] == '-' && argv[i][1] == 'd' )
485                         use_curr_dir =1;
486                 if( argv[i][0] == '-' && argv[i][1] == 'w' )
487                         dump_extensions =1;
488         }
489         
490         if( !quiet ) {
491                 printf("\n(If you find progress and other non-error messages irritating, you can use -q to suppress them)\n");
492                 if( !no_comp )
493                         printf("\n(You can use the -n option if you aren't interested in seeing all the instructions generated by the compiler)\n\n");
494                 if( !use_curr_dir )
495                         printf("\n(You can use the -d option if you want to use the current working directory as the CONFIG_DIR. I will look in this dir for extensions.ael* and its included files)\n\n");
496                 if( !dump_extensions )
497                         printf("\n(You can use the -w option to dump extensions.conf format to extensions.conf.aeldump)\n");
498         }
499
500         if( use_curr_dir ) {
501                 strcpy(ast_config_AST_CONFIG_DIR, ".");
502         }
503         else {
504                 strcpy(ast_config_AST_CONFIG_DIR, "/etc/asterisk");
505         }
506         strcpy(ast_config_AST_VAR_DIR, "/var/lib/asterisk");
507         
508         if( dump_extensions ) {
509                 dumpfile = fopen("extensions.conf.aeldump","w");
510                 if( !dumpfile ) {
511                         printf("\n\nSorry, cannot open extensions.conf.aeldump for writing! Correct the situation and try again!\n\n");
512                         exit(10);
513                 }
514                 
515         }
516
517         FIRST_TIME = 1;
518         
519         ael_external_load_module();
520         
521         ast_log(4, "ael2_parse", __LINE__, "main", "%d contexts, %d extensions, %d priorities\n", conts, extens, priors);
522
523         if( dump_extensions && dumpfile ) {
524         
525                 for( lp = context_list; lp; lp = lp->next ) { /* print out any contexts that didn't have any
526                                                                                                                  extensions in them */
527                         if( lp->extension_count == 0 ) {
528                                 
529                                 fprintf(dumpfile,"\n\n[%s]\n", lp->name);
530                                 
531                                 for(n=lp->ignorepats;n;n=n->next) {
532                                         fprintf(dumpfile, "ignorepat => %s\n", n->name);
533                                 }
534                                 for(n=lp->includes;n;n=n->next) {
535                                         fprintf(dumpfile, "include => %s\n", n->name);
536                                 }
537                                 for(n=lp->switches;n;n=n->next) {
538                                         fprintf(dumpfile, "switch => %s/%s\n", n->name, n->name2);
539                                 }
540                                 for(n=lp->eswitches;n;n=n->next) {
541                                         fprintf(dumpfile, "eswitch => %s/%s\n", n->name, n->name2);
542                                 }
543                         }
544                 }
545         }
546         
547         if( dump_extensions && dumpfile )
548                 fclose(dumpfile);
549         
550         for( lp = context_list; lp; lp = lp2 ) { /* free the ast_context structs */
551                 lp2 = lp->next;
552                 lp->next = 0;
553
554                 destroy_namelist(lp->includes);
555                 destroy_namelist(lp->ignorepats);
556                 destroy_namelist(lp->switches);
557                 destroy_namelist(lp->eswitches);
558
559                 free(lp);
560         }
561         
562     return 0;
563 }