03c6311ae27390249ec6b481de9365a31d989dea
[asterisk/asterisk.git] / pbx / pbx_ael.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*
20  *
21  * Compile symbolic Asterisk Extension Logic into Asterisk extensions
22  * 
23  */
24
25 #include <sys/types.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <ctype.h>
30 #include <errno.h>
31
32 #include "asterisk.h"
33
34 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
35
36 #include "asterisk/pbx.h"
37 #include "asterisk/config.h"
38 #include "asterisk/module.h"
39 #include "asterisk/logger.h"
40 #include "asterisk/cli.h"
41 #include "asterisk/callerid.h"
42
43 struct stringlink {
44         struct stringlink *next;
45         char data[0];
46 };
47
48 #define FILLIN_BREAK            1
49 #define FILLIN_CONTINUE         2
50
51 struct fillin {
52         struct fillin *next;
53         char exten[AST_MAX_EXTENSION];
54         int priority;
55         int type;
56 };
57
58 #ifdef __AST_DEBUG_MALLOC
59 static void FREE(void *ptr)
60 {
61         free(ptr);
62 }
63 #else
64 #define FREE free
65 #endif
66
67 #define DEBUG_READ   (1 << 0)
68 #define DEBUG_TOKENS (1 << 1)
69 #define DEBUG_MACROS (1 << 2)
70 #define DEBUG_CONTEXTS (1 << 3)
71
72 static int aeldebug = 0;
73
74 static char *dtext = "Asterisk Extension Language Compiler";
75 static char *config = "extensions.ael";
76 static char *registrar = "pbx_ael";
77
78 static char *__grab_token(char *src, const char *filename, int lineno, int link)
79 {
80         char *c;
81         char *b;
82         char *a;
83         int level = 0;
84         char *ret;
85 #if 0
86         if (aeldebug || DEBUG_TOKENS) 
87                 ast_verbose("Searching for token in '%s'!\n", src);
88 #endif
89         c = src;
90         while(*c) {
91                 if ((*c == '\\')) {
92                         c++;
93                         if (!*c)
94                                 c--;
95                 } else {
96                         if ((*c == '{') || (*c == '(')) {
97                                 level++;
98                         } else if ((*c == '}') || (*c == ')')) {
99                                 if (level)
100                                         level--;
101                                 else
102                                         ast_log(LOG_WARNING, "Syntax error at line %d of '%s', too many closing braces!\n", lineno, filename);
103                         } else if ((*c == ';') && !level) {
104                                 /* Got a token! */
105                                 *c = '\0';
106                                 b = c;
107                                 b--;
108                                 c++;
109                                 while((b > src) && (*b < 33)) { 
110                                         *b = '\0'; 
111                                         b--; 
112                                 }
113                                 a = src;
114                                 while(*a && (*a < 33))
115                                         a++;
116                                 if (link) {
117                                         ret = malloc(strlen(a) + sizeof(struct stringlink) + 1);
118                                         if (ret)
119                                                 strcpy(ret + sizeof(struct stringlink), a);
120                                 } else
121                                         ret = strdup(a);
122                                 /* Save remainder */
123                                 memmove(src, c, strlen(c) + 1);
124                                 return ret;
125                         }
126                 }
127                 c++;
128         }
129         return NULL;            
130 }
131
132 static char *grab_token(char *src, const char *filename, int lineno)
133 {
134         return __grab_token(src, filename, lineno, 0);
135 }
136
137 static struct stringlink *arg_parse(char *args, const char *filename, int lineno)
138 {
139         struct stringlink *cur, *prev=NULL, *root=NULL;
140         if (args) {
141                 if (aeldebug & DEBUG_TOKENS) 
142                         ast_verbose("Parsing args '%s'!\n", args);
143                 if (args[0] == '{') {
144                         /* Strip mandatory '}' from end */
145                         args[strlen(args) - 1] = '\0';
146                         while ((cur = (struct stringlink *)__grab_token(args + 1, filename, lineno, 1))) {
147                                 cur->next = NULL;
148                                 if (prev)
149                                         prev->next = cur;
150                                 else
151                                         root = cur;
152                                 prev = cur;
153                         }
154                 } else if (*args) {
155                         root = malloc(sizeof(struct stringlink) + strlen(args) + 1);
156                         if (root) {
157                                 strcpy(root->data, args);
158                                 root->next = NULL;
159                         }
160                 }
161         }
162         return root;
163 }
164
165 static char *grab_else(char *args, const char *filename, int lineno)
166 {
167         char *ret = NULL;
168         int level=0;
169         char *c;
170         if (args) {
171                 if (args[0] == '{') {
172                         c = args;
173                         while(*c) {
174                                 if (*c == '{')
175                                         level++;
176                                 else if (*c == '}') {
177                                         level--;
178                                         if (!level) {
179                                                 c++;
180                                                 while(*c && (*c < 33)) { *c = '\0'; c++; };
181                                                 if (!strncasecmp(c, "else", 4) && 
182                                                         ((c[4] == '{') || (c[4] < 33))) {
183                                                                 /* Ladies and gentlemen, we have an else clause */
184                                                         *c = '\0';
185                                                         c += 4;
186                                                         while(*c && (*c < 33)) c++;
187                                                         ret = c;
188                                                         if (aeldebug & DEBUG_TOKENS)
189                                                                 ast_verbose("Returning else clause '%s'\n", c);
190                                                 }
191                                         }
192                                         break;
193                                 }
194                                 c++;
195                         }
196                 }
197         }
198         return ret;
199 }
200
201 static struct stringlink *param_parse(char *parms, const char *macro, const char *filename, int lineno)
202 {
203         char *s, *e;
204         struct stringlink *root = NULL, *prev=NULL, *cur;
205         if (!parms || !*parms)
206                 return NULL;
207         if (*parms != '(') {
208                 ast_log(LOG_NOTICE, "Syntax error in parameter list for macro '%s' at about line %d of %s: Expecting '(' but got '%c'\n", macro, lineno, filename, *parms);
209                 return NULL;
210         }
211         s = parms + 1;
212         while(*s) {
213                 while(*s && (*s < 33)) s++;
214                 e = s;
215                 while(*e &&  (*e != ')') && (*e != ',')) {
216                         if (*e < 33)
217                                 *e = '\0';
218                         e++;
219                 }
220                 if (*e) {
221                         /* Strip token */
222                         *e = '\0';
223                         e++;
224                         /* Skip over whitespace */
225                         while(*e && (*e < 33)) e++;
226                         /* Link */
227                         cur = malloc(strlen(s) + sizeof(struct stringlink) + 1);
228                         if (cur) {
229                                 cur->next = NULL;
230                                 strcpy(cur->data, s);
231                                 if (prev)
232                                         prev->next = cur;
233                                 else
234                                         root = cur;
235                                 prev = cur;
236                         }
237                         s = e;
238                 }
239         }
240         return root;
241 }
242
243 static void arg_free(struct stringlink *cur)
244 {
245         struct stringlink *last;
246         while(cur) {
247                 last = cur;
248                 cur = cur->next;
249                 free(last);
250         }
251 }
252
253 static void handle_globals(struct stringlink *vars)
254 {
255         while(vars) {
256                 pbx_builtin_setvar(NULL, vars->data);
257                 vars = vars->next;
258         }
259 }
260
261 static struct stringlink *split_token(char *token, const char *filename, int lineno)
262 {
263         char *args, *p;
264         struct stringlink *argv;
265         args = token;
266         while (*args && (*args > 32) && (*args != '{') && (*args != '(')) args++;
267         if (*args) {
268                 p = args;
269                 while (*args && (*args < 33))
270                         args++;
271                 if (*args != '(') {
272                         *p = '\0';
273                 } else {
274                         while (*args && (*args != ')')) args++;
275                         if (*args == ')') {
276                                 args++;
277                                 while (*args && (*args < 33)) args++;
278                         }
279                 }
280                 if (!*args)
281                         args = NULL;
282         } else args = NULL;
283         argv = arg_parse(args, filename, lineno);
284         if (args)
285                 *args = '\0';
286         return argv;
287 }
288
289 static int matches_keyword(const char *data, const char *keyword)
290 {
291         char c;
292         if (!strncasecmp(data, keyword, strlen(keyword))) {
293                 c = data[strlen(keyword)];
294                 if ((c < 33) || (c == '(') || (c == '{'))
295                         return 1;
296         }
297         return 0;
298 }
299
300 static struct stringlink *split_params(char *token, const char *filename, int lineno)
301 {
302         char *params;
303         struct stringlink *paramv;
304         params = token;
305         while(*params && (*params > 32) && (*params != '(')) params++;
306         if (*params) {
307                 if (*params != '(') {
308                         *params = '\0';
309                         params++;
310                         while(*params && (*params < 33))
311                                 params++;
312                 }
313                 if (!*params)
314                         params = NULL;
315         } else params = NULL;
316         paramv = param_parse(params, token, filename, lineno);
317         if (params)
318                 *params = '\0';
319         return paramv;
320 }
321
322 static const char *get_case(char *s, char **restout, int *pattern)
323 {
324         char *newcase=NULL;
325         char *rest=NULL;
326         if (!strncasecmp(s, "case", 4) && s[4] && ((s[4] < 33) || (s[4] == ':'))) {
327                 newcase = s + 4;
328                 while (*newcase && (*newcase < 33)) newcase++;
329                 rest = newcase;
330                 *pattern = 0;
331         } else if (!strncasecmp(s, "pattern", 7) && s[7] && ((s[7] < 33) || (s[7] == ':'))) {
332                 newcase = s + 8;
333                 while (*newcase && (*newcase < 33)) newcase++;
334                 rest = newcase;
335                 *pattern = 1;
336         } else if (!strncasecmp(s, "default", 7) && ((s[7] < 33) || (s[7] == ':'))) {
337                 newcase = ".";
338                 rest = s + 7;
339                 while (*rest && (*rest < 33)) rest++;
340                 *pattern = 1;
341         }
342
343         if (rest) {
344                 while (*rest && (*rest > 32) && (*rest != ':')) rest++;
345                 if (*rest) {
346                         *rest = 0;
347                         rest++;
348                         while (*rest && ((*rest == ':') || (*rest < 33))) rest++;
349                         *restout = rest;
350                 } else {
351                         *restout = "";
352                 }
353         } else
354                 *restout = s;
355         if (aeldebug & DEBUG_TOKENS)
356                 ast_verbose("GETCASE: newcase is '%s', rest = '%s'\n", newcase, *restout);
357         return newcase;
358 }
359
360 static void fillin_free(struct fillin *fillin)
361 {
362         struct fillin *cur, *next;
363         cur =  fillin;
364         while(cur) {
365                 next = cur->next;
366                 free(cur);
367                 cur = next;
368         }
369 }
370
371 static void fillin_process(struct ast_context *con, struct fillin *fillin, const char *filename, int lineno, const char *breakexten, int breakprio, const char *contexten, int contprio)
372 {
373         struct fillin *cur;
374         char *app;
375         char mdata[AST_MAX_EXTENSION + 20];
376         cur = fillin;
377         while(cur) {
378                 if (cur->type == FILLIN_BREAK) {
379                         if (breakexten && breakprio) {
380                                 app = "Goto";
381                                 snprintf(mdata, sizeof(mdata), "%s|%d", breakexten, breakprio);
382                         } else {
383                                 app = "NoOp";
384                                 snprintf(mdata, sizeof(mdata), "Invalid break");
385                                 ast_log(LOG_NOTICE, "Ignoring inappropriate break around line %d of %s\n", lineno, filename);
386                         }
387                         if (ast_add_extension2(con, 0, cur->exten, cur->priority, NULL, NULL, app, strdup(mdata), FREE, registrar))
388                                 ast_log(LOG_WARNING, "Unable to add step at priority '%d' of break '%s'\n", cur->priority, cur->exten);
389                 } else if (cur->type == FILLIN_CONTINUE) {
390                         if (contexten && contprio) {
391                                 app = "Goto";
392                                 snprintf(mdata, sizeof(mdata), "%s|%d", contexten, contprio);
393                         } else {
394                                 app = "NoOp";
395                                 snprintf(mdata, sizeof(mdata), "Invalid continue");
396                                 ast_log(LOG_NOTICE, "Ignoring inappropriate continue around line %d of %s\n", lineno, filename);
397                         }
398                         if (ast_add_extension2(con, 0, cur->exten, cur->priority, NULL, NULL, app, strdup(mdata), FREE, registrar))
399                                 ast_log(LOG_WARNING, "Unable to add step at priority '%d' of continue '%s'\n", cur->priority, cur->exten);
400                 } else {
401                         ast_log(LOG_WARNING, "Whoa, unknown fillin type '%d'\n", cur->type);
402                 }
403                 cur = cur->next;
404         }
405 }
406
407 static int match_assignment(char *variable, char **value)
408 {
409         char *c;
410         char *ws;
411         int inpar = 0;
412         c = variable;
413         
414         while (*c && (*c > 32)) {
415                 if(*c == ')' && (inpar > 0)) {
416                         inpar--;
417                 } else if(*c == '(' && (inpar >= 0)) {
418                         inpar++;
419                 } else if(*c == '=' && (inpar == 0)) {
420                         break;
421                 }
422                 c++;
423         } 
424         ws = c;
425         while (*c && (*c < 33)) c++;
426         if (*c == '=') {
427                 *ws = '\0';
428                 *c = '\0';
429                 c++;
430                 while ((*c) && (*c < 33)) c++;
431                 *value = c;
432                 return 1;
433         }
434         return 0;
435 }
436
437 static int matches_label(char *data, char **rest)
438 {
439         char last = 0;
440         char *start = data;
441         while (*data > 32) {
442                 last = *data;
443                 data++;
444         }
445         if (last != ':') {
446                 while (*data && (*data < 33)) data++;
447                 last = *data;
448                 data++;
449         }
450         if (last == ':') {
451                 *rest = data;
452                 /* Go back and trim up the label */
453                 while(*start && ((*start > 32) && (*start != ':'))) start++;
454                 *start = '\0';
455                 return 1;
456         }
457         return 0;
458 }
459
460 static int build_step(const char *what, const char *name, const char *filename, int lineno, struct ast_context *con, char *exten, int *pos, char *data, struct fillin **fillout, char **label);
461 static int __build_step(const char *what, const char *name, const char *filename, int lineno, struct ast_context *con, char *exten, int *pos, char *data, struct fillin **fillout, char **label)
462 {
463         char *app;
464         char *args;
465         char *c;
466         char *margs=NULL;
467         char *oargs;
468         char *rest;
469         const char *curcase, *newcase;
470         struct stringlink *swargs, *cur;
471         int cpos;
472         int mlen;
473         int pattern = 0;
474         struct fillin *fillin;
475         while (*data && (*data < 33)) data++;
476         if (matches_label(data, &c)) {
477                 *label = data;
478                 data = c;
479                 while (*data && (*data < 33)) data++;
480         }
481         if (!data || ast_strlen_zero(data))
482                 return 0;
483         if (matches_keyword(data, "switch")) {
484                 fillin = NULL;
485                 /* Switch */
486                 args = data + strlen("switch");
487                 while ((*args < 33) && (*args != '(')) args++;
488                 if ((*args == '(') && (c = strchr(args, ')'))) {
489                         args++;
490                         *c = '\0';
491                         c++;
492                         if (aeldebug & DEBUG_TOKENS)
493                                 ast_verbose("--SWITCH on : %s\n", args);
494                         mlen = strlen(exten) + 128 + strlen(args) + strlen(name);
495                         margs = alloca(mlen);
496                         app = "Goto";
497                         sprintf(margs, "sw-%s-%d-%s|1", name, *pos, args);
498                         ast_process_quotes_and_slashes(margs, ',', '|');
499                         oargs = args;
500                         args = margs;
501                         if (ast_add_extension2(con, 0, exten, *pos, *label, NULL, app, strdup(args), FREE, registrar))
502                                 ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
503                         else {
504                                 *label = NULL;
505                                 (*pos)++;
506                         }
507                         app = "NoOp";
508                         sprintf(margs, "Finish switch-%s-%d", name, *pos - 1);
509                         if (ast_add_extension2(con, 0, exten, *pos, *label, NULL, app, strdup(args), FREE, registrar))
510                                 ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
511                         else {
512                                 *label = NULL;
513                                 (*pos)++;
514                         }
515                         while(*c && (*c < 33)) c++;
516                         if (aeldebug & DEBUG_TOKENS)
517                                 ast_verbose("ARG Parsing '%s'\n", c);
518                         swargs = arg_parse(c, filename, lineno);
519                         cur = swargs;
520                         curcase = NULL;
521                         while(cur) {
522                                 if ((newcase = get_case(cur->data, &rest, &pattern))) {
523                                         if (aeldebug & DEBUG_TOKENS)
524                                                 ast_verbose("--NEWCASE: '%s'!\n", newcase);
525                                         if (curcase) {
526                                                 /* Handle fall through */
527                                                 char tmp[strlen(newcase) + strlen(name) + 40];
528                                                 sprintf(tmp, "sw-%s-%d-%s|%d", name, *pos - 2, newcase, 1);
529                                                 ast_add_extension2(con, 0, margs, cpos, NULL, NULL, "Goto", strdup(tmp), FREE, registrar);
530                                         }
531                                         curcase = newcase;
532                                         cpos = 1;
533                                         if (pattern)
534                                                 snprintf(margs, mlen, "_sw-%s-%d-%s", name, *pos - 2, curcase);
535                                         else
536                                                 snprintf(margs, mlen, "sw-%s-%d-%s", name, *pos - 2, curcase);
537                                         if (!strcasecmp(rest, "break")) {
538                                                 char tmp[strlen(exten) + 10];
539                                                 sprintf(tmp, "%s|%d", exten, *pos - 1);
540                                                 ast_add_extension2(con, 0, exten, cpos, *label, NULL, "Goto", strdup(tmp), FREE, registrar);
541                                                 curcase = NULL;
542                                                 *label = NULL;
543                                         } else
544                                                 build_step("switch", margs, filename, lineno, con, margs, &cpos, rest, &fillin, label);
545                                 } else if (curcase) {
546                                         if (aeldebug & DEBUG_TOKENS)
547                                                 ast_verbose("Building statement from '%s'\n", rest);
548                                         if (!strcasecmp(rest, "break")) {
549                                                 char tmp[strlen(exten) + 10];
550                                                 sprintf(tmp, "%s|%d", exten, *pos - 1);
551                                                 ast_add_extension2(con, 0, margs, cpos, *label, NULL, "Goto", strdup(tmp), FREE, registrar);
552                                                 curcase = NULL;
553                                                 *label = NULL;
554                                         } else
555                                                 build_step("switch", margs, filename, lineno, con, margs, &cpos, rest, &fillin, label);
556                                 } else 
557                                         ast_log(LOG_WARNING, "Unreachable code in switch at about line %d of %s\n", lineno, filename);
558                                 if (aeldebug & DEBUG_TOKENS)
559                                         ast_verbose("--SWARG: %s\n", cur->data);
560                                 cur = cur->next;
561                         }
562                         /* Can't do anything with these */
563                         fillin_process(con, fillin, filename, lineno, NULL, 0, NULL, 0);
564                         fillin_free(fillin);
565                         arg_free(swargs);
566                 } else
567                         ast_log(LOG_WARNING, "Syntax error in switch declaration in %s around line %d!\n", filename, lineno); 
568                         
569         } else if (matches_keyword(data, "if")) {
570                 /* If... */
571                 args = data + strlen("if");
572                 while ((*args < 33) && (*args != '(')) args++;
573                 if ((*args == '(') && (c = strchr(args, ')'))) {
574                         int ifblock;
575                         int ifstart;
576                         int elsestart;
577                         int ifend;
578                         int ifskip;
579                         char *elses;
580                         char *iflabel;
581                         args++;
582                         *c = '\0';
583                         c++;
584                         while(*c && (*c < 33)) c++;
585                         if (aeldebug & DEBUG_TOKENS)
586                                 ast_verbose("--IF on : '%s' : '%s'\n", args, c);
587                         mlen = strlen(exten) + 128 + strlen(args) + strlen(name);
588                         margs = alloca(mlen);
589                         /* Remember where the ifblock starts, and skip over */
590                         ifblock = (*pos)++;
591                         iflabel = *label;
592                         *label = NULL;
593                         /* Remember where the start of the ifblock is */
594                         ifstart = *pos;
595                         snprintf(margs, mlen, "if-%s-%d", name, ifblock);
596                         /* Now process the block of the if */
597                         if (aeldebug & DEBUG_TOKENS)
598                                 ast_verbose("Searching for elses in '%s'\n", c);
599                         elses = grab_else(c, filename, lineno);
600                         build_step("if", margs, filename, lineno, con, exten, pos, c, fillout, label);
601                         if (elses) {
602                                 /* Reserve a goto to exit the if */
603                                 ifskip = *pos;
604                                 (*pos)++;
605                                 elsestart = *pos;
606                                 build_step("else", margs, filename, lineno, con, exten, pos, elses, fillout, label);
607                         } else {
608                                 elsestart = *pos;
609                                 ifskip = 0;
610                         }
611                         ifend = *pos;
612                         (*pos)++;
613                         app = "NoOp";
614                         snprintf(margs, mlen, "Finish if-%s-%d", name, ifblock);
615                         if (ast_add_extension2(con, 0, exten, ifend, *label, NULL, app, strdup(margs), FREE, registrar))
616                                 ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
617                         *label = NULL;
618                         app = "GotoIf";
619                         snprintf(margs, mlen, "$[ %s ]?%d:%d", args, ifstart, elsestart);
620                         if (ast_add_extension2(con, 0, exten, ifblock, iflabel, NULL, app, strdup(margs), FREE, registrar))
621                                 ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
622                         if (ifskip) {
623                                 /* Skip as appropriate around else clause */
624                                 snprintf(margs, mlen, "%d", ifend);
625                                 if (ast_add_extension2(con, 0, exten, ifskip, NULL, NULL, "Goto", strdup(margs), FREE, registrar))
626                                         ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
627                         }
628                 } else
629                         ast_log(LOG_WARNING, "Syntax error in if declaration in %s around line %d!\n", filename, lineno); 
630         } else if (matches_keyword(data, "while")) {
631                 /* While... */
632                 fillin = NULL;
633                 args = data + strlen("while");
634                 while ((*args < 33) && (*args != '(')) args++;
635                 if ((*args == '(') && (c = strchr(args, ')'))) {
636                         int whileblock;
637                         int whilestart;
638                         int whileend;
639                         char *whilelabel;
640                         args++;
641                         *c = '\0';
642                         c++;
643                         while(*c && (*c < 33)) c++;
644                         if (aeldebug & DEBUG_TOKENS)
645                                 ast_verbose("--WHILE on : '%s' : '%s'\n", args, c);
646                         mlen = strlen(exten) + 128 + strlen(args) + strlen(name);
647                         margs = alloca(mlen);
648                         /* Remember where to put the conditional, and keep its position */
649                         whilestart = (*pos);
650                         whilelabel = *label;
651                         *label = NULL;
652                         (*pos)++;
653                         /* Remember where the whileblock starts */
654                         whileblock = (*pos);
655                         snprintf(margs, mlen, "while-%s-%d", name, whilestart);
656                         build_step("while", margs, filename, lineno, con, exten, pos, c, &fillin, label);
657                         /* Close the loop */
658                         app = "Goto";
659                         snprintf(margs, mlen, "%d", whilestart);
660                         if (ast_add_extension2(con, 0, exten, (*pos)++, *label, NULL, app, strdup(margs), FREE, registrar))
661                                 ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
662                         *label = NULL;
663                         whileend = (*pos);
664                         /* Place trailer */
665                         app = "NoOp";
666                         snprintf(margs, mlen, "Finish while-%s-%d", name, whilestart);
667                         if (ast_add_extension2(con, 0, exten, (*pos)++, *label, NULL, app, strdup(margs), FREE, registrar))
668                                 ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
669                         *label = NULL;
670                         app = "GotoIf";
671                         snprintf(margs, mlen, "$[ %s ]?%d:%d", args, whileblock, whileend);
672                         if (ast_add_extension2(con, 0, exten, whilestart, whilelabel, NULL, app, strdup(margs), FREE, registrar))
673                                 ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
674                         fillin_process(con, fillin, filename, lineno, exten, whileend, exten, whilestart);
675                         fillin_free(fillin);
676                 } else
677                         ast_log(LOG_WARNING, "Syntax error in while declaration in %s around line %d!\n", filename, lineno); 
678         } else if (matches_keyword(data, "jump")) {
679                 char *p;
680                 /* Jump... */
681                 fillin = NULL;
682                 args = data + strlen("jump");
683                 while(*args && (*args < 33)) args++;
684                 if (aeldebug & DEBUG_TOKENS)
685                         ast_verbose("--JUMP to : '%s'\n", args);
686                 p = strchr(args, ',');
687                 if (p) {
688                         *p = '\0';
689                         p++;
690                 } else
691                         p = "1";
692                 c = strchr(args, '@');
693                 if (c) {
694                         *c = '\0';
695                         c++;
696                 }
697                 mlen = strlen(exten) + 128 + strlen(args) + strlen(name) + (c ? strlen(c) : 0);
698                 margs = alloca(mlen);
699                 if (c) 
700                         snprintf(margs, mlen, "%s|%s|%s", c,args, p);
701                 else
702                         snprintf(margs, mlen, "%s|%s", args, p);
703                 app = "Goto";
704                 if (ast_add_extension2(con, 0, exten, (*pos)++, *label, NULL, app, strdup(margs), FREE, registrar))
705                         ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
706                 *label = NULL;
707         } else if (matches_keyword(data, "goto")) {
708                 /* Jump... */
709                 fillin = NULL;
710                 args = data + strlen("goto");
711                 while(*args && (*args < 33)) args++;
712                 if (aeldebug & DEBUG_TOKENS)
713                         ast_verbose("--GOTO to : '%s'\n", args);
714                 app = "Goto";
715                 if (ast_add_extension2(con, 0, exten, (*pos)++, *label, NULL, app, strdup(args), FREE, registrar))
716                         ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
717                 *label = NULL;
718         } else if (matches_keyword(data, "for")) {
719                 /* While... */
720                 fillin = NULL;
721                 args = data + strlen("for");
722                 while ((*args < 33) && (*args != '(')) args++;
723                 if ((*args == '(') && (c = strchr(args, ')'))) {
724                         int forblock;
725                         int forprep;
726                         int forstart;
727                         int forend;
728                         struct stringlink *fields;
729                         char *tmp;
730                         char *forlabel = NULL;
731                         args++;
732                         *c = '\0';
733                         c++;
734                         while(*c && (*c < 33)) c++;
735                         /* Parse arguments first */
736                         tmp = alloca(strlen(args) + 10);
737                         if (tmp) {
738                                 snprintf(tmp, strlen(args) + 10, "{%s;}", args);
739                                 fields = arg_parse(tmp, filename, lineno);
740                         } else
741                                 fields = NULL;
742                         if (fields && fields->next && fields->next->next) {
743                                 if (aeldebug & DEBUG_TOKENS)
744                                         ast_verbose("--FOR ('%s' ; '%s' ; '%s') : '%s'\n", fields->data, fields->next->data, fields->next->next->data, c);
745                                 mlen = strlen(exten) + 128 + strlen(args) + strlen(name);
746                                 margs = alloca(mlen);
747                                 forprep = *pos;
748                                 snprintf(margs, mlen, "for-%s-%d", name, forprep);
749                                 fillin = NULL;
750                                 build_step("while", margs, filename, lineno, con, exten, pos, fields->data, &fillin, label);
751                                 /* Remember where to put the conditional, and keep its position */
752                                 forstart = (*pos);
753                                 forlabel = *label;
754                                 (*pos)++;
755                                 *label = NULL;
756                                 /* Remember where the whileblock starts */
757                                 forblock = (*pos);
758                                 build_step("for", margs, filename, lineno, con, exten, pos, fields->next->next->data, &fillin, label);
759                                 build_step("for", margs, filename, lineno, con, exten, pos, c, &fillin, label);
760                                 /* Close the loop */
761                                 app = "Goto";
762                                 snprintf(margs, mlen, "%d", forstart);
763                                 if (ast_add_extension2(con, 0, exten, (*pos)++, *label, NULL, app, strdup(margs), FREE, registrar))
764                                         ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
765                                 *label = NULL;
766                                 forend = (*pos);
767                                 /* Place trailer */
768                                 app = "NoOp";
769                                 snprintf(margs, mlen, "Finish for-%s-%d", name, forprep);
770                                 if (ast_add_extension2(con, 0, exten, (*pos)++, *label, NULL, app, strdup(margs), FREE, registrar))
771                                         ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
772                                 *label = NULL;
773                                 app = "GotoIf";
774                                 snprintf(margs, mlen, "$[ %s ]?%d:%d", fields->next->data, forblock, forend);
775                                 if (ast_add_extension2(con, 0, exten, forstart, forlabel, NULL, app, strdup(margs), FREE, registrar))
776                                         ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", forstart, what, name);
777                                 fillin_process(con, fillin, filename, lineno, exten, forend, exten, forstart);
778                                 fillin_free(fillin);
779                         } else
780                                 ast_log(LOG_NOTICE, "Improper for declaration in %s around line %d!\n", filename, lineno); 
781                         arg_free(fields);
782                 } else
783                         ast_log(LOG_WARNING, "Syntax error in for declaration in %s around line %d!\n", filename, lineno); 
784                         
785         } else if (!strcasecmp(data, "break") || !strcasecmp(data, "continue")) {
786                 struct fillin *fi;
787                 fi = malloc(sizeof(struct fillin));
788                 if (fi) {
789                         memset(fi, 0, sizeof(struct fillin));
790                         if (!strcasecmp(data, "break"))
791                                 fi->type = FILLIN_BREAK;
792                         else
793                                 fi->type = FILLIN_CONTINUE;
794                         ast_copy_string(fi->exten, exten, sizeof(fi->exten));
795                         fi->priority = (*pos)++;
796                         fi->next = *fillout;
797                         *fillout = fi;
798                 }
799         } else if (match_assignment(data, &rest)) {
800                 if (aeldebug & DEBUG_TOKENS)
801                         ast_verbose("ASSIGN  '%s' = '%s'\n", data, rest);
802                 mlen = strlen(rest) + strlen(data) + 20;
803                 margs = alloca(mlen);
804                 snprintf(margs, mlen, "%s=$[ %s ]", data, rest);
805                 app = "Set";
806                 if (ast_add_extension2(con, 0, exten, *pos, *label, NULL, app, strdup(margs), FREE, registrar))
807                         ast_log(LOG_WARNING, "Unable to add assignment at priority '%d' of %s '%s'\n", *pos, what, name);
808                 else {
809                         *label = NULL;
810                         (*pos)++;
811                 }
812         } else {
813                 app = data;
814                 args = app;
815                 while (*args && (*args > 32) && (*args != '(')) args++;
816                         if (*args != '(') {
817                         while(*args && (*args != '(')) { *args = '\0'; args++; };
818                 }
819                 if (*args == '(') {
820                         *args = '\0';
821                         args++;
822                         /* Got arguments, trim trailing ')' */
823                         c = args + strlen(args) - 1;
824                         while((c >= args) && (*c < 33) && (*c != ')')) { *c = '\0'; c--; };
825                         if ((c >= args) && (*c == ')')) *c = '\0';
826                 } else
827                         args = "";
828                 ast_process_quotes_and_slashes(args, ',', '|');
829                 if (app[0] == '&') {
830                         app++;
831                         margs = alloca(strlen(args) + strlen(app) + 10);
832                         sprintf(margs, "%s|%s", app, args);
833                         args = margs;
834                         app = "Macro";
835                 }
836                 if (aeldebug & DEBUG_TOKENS)
837                         ast_verbose("-- APP: '%s', ARGS: '%s'\n", app, args);
838                 if (ast_add_extension2(con, 0, exten, *pos, *label, NULL, app, strdup(args), FREE, registrar))
839                         ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
840                 else {
841                         (*pos)++;
842                         *label = NULL;
843                 }
844         }
845         return 0;
846 }
847
848 static int build_step(const char *what, const char *name, const char *filename, int lineno, struct ast_context *con, char *exten, int *pos, char *data, struct fillin **fillout, char **label)
849 {
850         struct stringlink *args, *cur;
851         int res=0;
852         struct fillin *fillin=NULL;
853         int dropfill = 0;
854         char *labelin = NULL;
855         if (!fillout) {
856                 fillout = &fillin;
857                 dropfill = 1;
858         }
859         if (!label) {
860                 label = &labelin;
861         };
862         args = arg_parse(data, filename, lineno);
863         cur = args;
864         while(cur) {
865                 res |= __build_step(what, name, filename, lineno, con, exten, pos, cur->data, fillout, label);
866                 cur = cur->next;
867         }
868         arg_free(args);
869         if (dropfill) {
870                 fillin_process(con, fillin, filename, lineno, NULL, 0, NULL, 0);
871                 fillin_free(fillin);
872         }
873         return res;
874 }
875
876 static int parse_catch(char *data, char **catch, char **rest)
877 {
878         /* Skip the word 'catch' */
879         data += 5;
880         while (*data && (*data < 33)) data++;
881         /* Here's the extension */
882         *catch = data;
883         if (!*data)
884                 return 0;
885         while (*data && (*data > 32)) data++;
886         if (!*data)
887                 return 0;
888         /* Trim any trailing spaces */
889         *data = '\0';
890         data++;
891         while(*data && (*data < 33)) data++;
892         if (!*data)
893                 return 0;
894         *rest = data;
895         return 1;
896 }
897
898 static void handle_macro(struct ast_context **local_contexts, struct stringlink *vars, const char *filename, int lineno)
899 {
900         struct stringlink *argv;
901         struct stringlink *paramv;
902         struct stringlink *cur;
903         struct ast_context *con;
904         struct fillin *fillin;
905         char *catch, *rest;
906         char name[256];
907         int pos;
908         int cpos;
909
910         if (aeldebug & DEBUG_MACROS)
911                 ast_verbose("Root macro def is '%s'\n", vars->data);
912         argv = split_token(vars->data, filename, lineno);
913         paramv = split_params(vars->data, filename, lineno);
914         if (aeldebug & DEBUG_MACROS) 
915                 ast_verbose("Found macro '%s'\n", vars->data);
916         snprintf(name, sizeof(name), "macro-%s", vars->data);
917         con = ast_context_create(local_contexts, name, registrar);
918         if (con) {
919                 pos = 1;
920                 cur = paramv;
921                 while(cur) {
922                         if (aeldebug & DEBUG_MACROS)
923                                 ast_verbose("  PARAM => '%s'\n", cur->data);
924                         snprintf(name, sizeof(name), "%s=${ARG%d}", cur->data, pos);
925                         if (ast_add_extension2(con, 0, "s", pos, NULL, NULL, "Set", strdup(name), FREE, registrar))
926                                 ast_log(LOG_WARNING, "Unable to add step at priority '%d' of macro '%s'\n", pos, vars->data);
927                         else
928                                 pos++;
929                         cur = cur->next;
930                 }
931                 cur = argv;
932                 while(cur) {
933                         if (aeldebug & DEBUG_MACROS)
934                                 ast_verbose("  STEP => '%s'\n", cur->data);
935                         if (matches_keyword(cur->data, "catch")) {
936                                 if (aeldebug & DEBUG_MACROS)
937                                         ast_verbose("--CATCH: '%s'\n", cur->data);
938                                 if (parse_catch(cur->data, &catch, &rest)) {
939                                         cpos = 1;
940                                         build_step("catch", catch, filename, lineno, con, catch, &cpos, rest, NULL, NULL);
941                                 } else
942                                         ast_log(LOG_NOTICE, "Parse error for catch at about line %d of %s\n", lineno, filename);
943                         } else {
944                                 fillin = NULL;
945                                 build_step("macro", vars->data, filename, lineno, con, "s", &pos, cur->data, NULL, NULL);
946                         }
947                         cur = cur->next;
948                 }
949         } else
950                 ast_log(LOG_WARNING, "Unable to create context '%s'\n", name);
951         arg_free(argv);
952         if (vars->next)
953                 ast_log(LOG_NOTICE, "Ignoring excess tokens in macro definition around line %d of %s!\n", lineno, filename);
954 }
955
956 static int matches_extension(char *exten, char **extout)
957 {
958         char *c;
959         *extout = NULL;
960         c = exten;
961         while(*c && (*c > 32)) c++;
962         if (*c) {
963                 *c = '\0';
964                 c++;
965                 while(*c && (*c < 33)) c++;
966                 if (*c) {
967                         if (*c == '=') {
968                                 *c = '\0';
969                                 c++;
970                                 if (*c == '>')
971                                         c++;
972                                 while (*c && (*c < 33)) c++;
973                                 *extout = c;
974                                 return 1;
975                         }
976                 }
977         }
978         return 0;
979 }
980
981 static void parse_keyword(char *s, char **o)
982 {
983         char *c;
984         c = s;
985         while((*c) && (*c > 32)) c++;
986         if (*c) {
987                 *c = '\0';
988                 c++;
989                 while(*c && (*c < 33)) c++;
990                 *o = c;
991         } else
992                 *o = NULL;
993 }
994
995 static void handle_context(struct ast_context **local_contexts, struct stringlink *vars, const char *filename, int lineno)
996 {
997         struct stringlink *argv;
998         struct stringlink *paramv;
999         struct stringlink *cur2;
1000         struct stringlink *argv2;
1001         struct stringlink *cur;
1002         struct ast_context *con;
1003         char *rest;
1004         char *c;
1005         char name[256];
1006         int pos;
1007
1008         if (aeldebug & DEBUG_CONTEXTS)
1009                 ast_verbose("Root context def is '%s'\n", vars->data);
1010         argv = split_token(vars->data, filename, lineno);
1011         paramv = split_params(vars->data, filename, lineno);
1012         if (aeldebug & DEBUG_CONTEXTS) 
1013                 ast_verbose("Found context '%s'\n", vars->data);
1014         snprintf(name, sizeof(name), "%s", vars->data);
1015         con = ast_context_create(local_contexts, name, registrar);
1016         if (con) {
1017                 cur = argv;
1018                 while(cur) {
1019                         if (matches_keyword(cur->data, "includes")) {
1020                                 if (aeldebug & DEBUG_CONTEXTS)
1021                                         ast_verbose("--INCLUDES: '%s'\n", cur->data);
1022                                 parse_keyword(cur->data, &rest);
1023                                 if (rest) {
1024                                         argv2 = arg_parse(rest, filename, lineno);
1025                                         cur2 = argv2;
1026                                         while(cur2) {
1027                                                 ast_context_add_include2(con, cur2->data, registrar);
1028                                                 cur2 = cur2->next;
1029                                         }
1030                                         arg_free(argv2);
1031                                 }
1032                         } else if (matches_keyword(cur->data, "ignorepat")) {
1033                                 if (aeldebug & DEBUG_CONTEXTS)
1034                                         ast_verbose("--IGNOREPAT: '%s'\n", cur->data);
1035                                 parse_keyword(cur->data, &rest);
1036                                 if (rest) {
1037                                         argv2 = arg_parse(rest, filename, lineno);
1038                                         cur2 = argv2;
1039                                         while(cur2) {
1040                                                 ast_context_add_ignorepat2(con, cur2->data, registrar);
1041                                                 cur2 = cur2->next;
1042                                         }
1043                                         arg_free(argv2);
1044                                 }
1045                         } else if (matches_keyword(cur->data, "switches") || matches_keyword(cur->data, "eswitches")) {
1046                                 if (aeldebug & DEBUG_CONTEXTS)
1047                                         ast_verbose("--[E]SWITCH: '%s'\n", cur->data);
1048                                 parse_keyword(cur->data, &rest);
1049                                 if (rest) {
1050                                         argv2 = arg_parse(rest, filename, lineno);
1051                                         cur2 = argv2;
1052                                         while(cur2) {
1053                                                 c = strchr(cur2->data, '/');
1054                                                 if (c) {
1055                                                         *c = '\0';
1056                                                         c++;
1057                                                 } else
1058                                                         c = "";
1059                                                 ast_context_add_switch2(con, cur2->data, c, (cur->data[0] == 'e'), registrar);
1060                                                 cur2 = cur2->next;
1061                                         }
1062                                         arg_free(argv2);
1063                                 }
1064                         } else if (matches_extension(cur->data, &rest)) {
1065                                 if (aeldebug & DEBUG_CONTEXTS)
1066                                         ast_verbose("Extension: '%s' => '%s'\n", cur->data, rest);
1067                                 pos = 1;
1068                                 build_step("extension", cur->data, filename, lineno, con, cur->data, &pos, rest, NULL, NULL);
1069                         }
1070                         cur = cur->next;
1071                 }
1072         } else
1073                         ast_log(LOG_WARNING, "Unable to create context '%s'\n", name);
1074         arg_free(argv);
1075         if (vars->next)
1076                 ast_log(LOG_NOTICE, "Ignoring excess tokens in macro definition around line %d of %s!\n", lineno, filename);
1077 }
1078
1079 static int handle_root_token(struct ast_context **local_contexts, char *token, int level, const char *filename, int lineno)
1080 {
1081         struct stringlink *argv, *cur;
1082         argv = split_token(token, filename, lineno);
1083         if (aeldebug & DEBUG_TOKENS) {
1084                 ast_verbose("Found root token '%s' at level %d (%s:%d)!\n", token, level, filename, lineno);
1085                 cur = argv;
1086                 while(cur) {
1087                         ast_verbose("   ARG => '%s'\n", cur->data);
1088                         cur = cur->next;
1089                 }
1090         }
1091         if (!strcasecmp(token, "globals")) {
1092                 handle_globals(argv);
1093         } else if (!strcasecmp(token, "macro")) {
1094                 handle_macro(local_contexts, argv, filename, lineno);
1095         } else if (!strcasecmp(token, "context")) {
1096                 handle_context(local_contexts, argv, filename, lineno);
1097         } else {
1098                 ast_log(LOG_NOTICE, "Unknown root token '%s'\n", token);
1099         }
1100         arg_free(argv);
1101         return 0;
1102 }
1103
1104
1105 static int ast_ael_compile(struct ast_context **local_contexts, const char *filename)
1106 {
1107         char *rfilename;
1108         char *buf, *tbuf;
1109         int bufsiz;
1110         FILE *f;
1111         char *c;
1112         char *token;
1113         int lineno=0;
1114
1115         if (filename[0] == '/')
1116                 rfilename = (char *)filename;
1117         else {
1118                 rfilename = alloca(strlen(filename) + strlen(ast_config_AST_CONFIG_DIR) + 2);
1119                 sprintf(rfilename, "%s/%s", ast_config_AST_CONFIG_DIR, filename);
1120         }
1121         
1122         f = fopen(rfilename, "r");
1123         if (!f) {
1124                 ast_log(LOG_WARNING, "Unable to open '%s': %s\n", rfilename, strerror(errno));
1125                 return -1;
1126         }
1127         buf = malloc(4096);
1128         if (!buf) {
1129                 ast_log(LOG_WARNING, "Out of memory!\n");
1130                 fclose(f);
1131                 return -1;
1132         }
1133         buf[0] = 0;
1134         bufsiz = 4096;
1135         while(!feof(f)) {
1136                 if (bufsiz - strlen(buf) < 2048) {
1137                         bufsiz += 4096;
1138                         tbuf = realloc(buf, bufsiz);
1139                         if (tbuf) {
1140                                 buf = tbuf;
1141                         } else {
1142                                 free(buf);
1143                                 ast_log(LOG_WARNING, "Out of memory!\n");
1144                                 fclose(f);
1145                         }
1146                 }
1147                 if (fgets(buf + strlen(buf), bufsiz - strlen(buf), f)) {
1148                         lineno++;
1149                         while(*buf && buf[strlen(buf) - 1] < 33)
1150                                 buf[strlen(buf) - 1] = '\0';
1151                         c = strstr(buf, "//");
1152                         if (c)
1153                                 *c = '\0';
1154                         if (*buf) {
1155                                 if (aeldebug & DEBUG_READ)
1156                                         ast_verbose("Newly composed line '%s'\n", buf);
1157                                 while((token = grab_token(buf, filename, lineno))) {
1158                                         handle_root_token(local_contexts, token, 0, filename, lineno);
1159                                         free(token);
1160                                 }
1161                         }
1162                 }
1163         };
1164         free(buf);
1165         fclose(f);
1166         return 0;
1167 }
1168
1169 /*
1170  * Standard module functions ...
1171  */
1172 int unload_module(void)
1173 {
1174         ast_context_destroy(NULL, registrar);
1175         return 0;
1176 }
1177
1178 static int pbx_load_module(void)
1179 {
1180         struct ast_context *local_contexts=NULL, *con;
1181         ast_ael_compile(&local_contexts, config);
1182         ast_merge_contexts_and_delete(&local_contexts, registrar);
1183         for (con = ast_walk_contexts(NULL); con; con = ast_walk_contexts(con))
1184                 ast_context_verify_includes(con);
1185
1186         return 0;
1187 }
1188
1189 int load_module(void)
1190 {
1191         if (pbx_load_module()) return -1;
1192  
1193         return 0;
1194 }
1195
1196 int reload(void)
1197 {
1198         ast_context_destroy(NULL, registrar);
1199         /* For martin's global variables, don't clear them on reload */
1200         pbx_load_module();
1201         return 0;
1202 }
1203
1204 int usecount(void)
1205 {
1206         return 0;
1207 }
1208
1209 char *description(void)
1210 {
1211         return dtext;
1212 }
1213
1214 char *key(void)
1215 {
1216         return ASTERISK_GPL_KEY;
1217 }