use double quotes for asterisk header ...
[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 /*! \file
20  *
21  * \brief 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                                                 break;
192                                         }
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 char *argument_end(char *str)
461 {
462         int level=0;
463         while(*++str) {
464                 switch(*str) {
465                 case '(':
466                         level++;
467                         break;
468                 case ')':
469                         if(level)
470                                 level--;
471                         else
472                                 return str;
473                         break;
474                 default:
475                         break;
476                 }
477         }
478         return NULL;
479 }
480
481 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);
482 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)
483 {
484         char *app;
485         char *args;
486         char *c;
487         char *margs=NULL;
488         char *oargs;
489         char *rest;
490         const char *curcase, *newcase;
491         struct stringlink *swargs, *cur;
492         int cpos;
493         int mlen;
494         int pattern = 0;
495         struct fillin *fillin;
496         while (*data && (*data < 33)) data++;
497         if (matches_label(data, &c)) {
498                 *label = data;
499                 data = c;
500                 while (*data && (*data < 33)) data++;
501         }
502         if (!data || ast_strlen_zero(data))
503                 return 0;
504         if (matches_keyword(data, "switch")) {
505                 fillin = NULL;
506                 /* Switch */
507                 args = data + strlen("switch");
508                 while ((*args < 33) && (*args != '(')) args++;
509                 if ((*args == '(') && (c = argument_end(args))) {
510                         args++;
511                         *c = '\0';
512                         c++;
513                         if (aeldebug & DEBUG_TOKENS)
514                                 ast_verbose("--SWITCH on : %s\n", args);
515                         mlen = strlen(exten) + 128 + strlen(args) + strlen(name);
516                         margs = alloca(mlen);
517                         app = "Goto";
518                         sprintf(margs, "sw-%s-%d-%s|1", name, *pos, args);
519                         ast_process_quotes_and_slashes(margs, ',', '|');
520                         oargs = args;
521                         args = margs;
522                         if (ast_add_extension2(con, 0, exten, *pos, *label, NULL, app, strdup(args), FREE, registrar))
523                                 ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
524                         else {
525                                 *label = NULL;
526                                 (*pos)++;
527                         }
528                         app = "NoOp";
529                         sprintf(margs, "Finish switch-%s-%d", name, *pos - 1);
530                         if (ast_add_extension2(con, 0, exten, *pos, *label, NULL, app, strdup(args), FREE, registrar))
531                                 ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
532                         else {
533                                 *label = NULL;
534                                 (*pos)++;
535                         }
536                         while(*c && (*c < 33)) c++;
537                         if (aeldebug & DEBUG_TOKENS)
538                                 ast_verbose("ARG Parsing '%s'\n", c);
539                         swargs = arg_parse(c, filename, lineno);
540                         cur = swargs;
541                         curcase = NULL;
542                         while(cur) {
543                                 if ((newcase = get_case(cur->data, &rest, &pattern))) {
544                                         if (aeldebug & DEBUG_TOKENS)
545                                                 ast_verbose("--NEWCASE: '%s'!\n", newcase);
546                                         if (curcase) {
547                                                 /* Handle fall through */
548                                                 char tmp[strlen(newcase) + strlen(name) + 40];
549                                                 sprintf(tmp, "sw-%s-%d-%s|%d", name, *pos - 2, newcase, 1);
550                                                 ast_add_extension2(con, 0, margs, cpos, NULL, NULL, "Goto", strdup(tmp), FREE, registrar);
551                                         }
552                                         curcase = newcase;
553                                         cpos = 1;
554                                         if (pattern)
555                                                 snprintf(margs, mlen, "_sw-%s-%d-%s", name, *pos - 2, curcase);
556                                         else
557                                                 snprintf(margs, mlen, "sw-%s-%d-%s", name, *pos - 2, curcase);
558                                         if (!strcasecmp(rest, "break")) {
559                                                 char tmp[strlen(exten) + 10];
560                                                 sprintf(tmp, "%s|%d", exten, *pos - 1);
561                                                 ast_add_extension2(con, 0, exten, cpos, *label, NULL, "Goto", strdup(tmp), FREE, registrar);
562                                                 curcase = NULL;
563                                                 *label = NULL;
564                                         } else
565                                                 build_step("switch", margs, filename, lineno, con, margs, &cpos, rest, &fillin, label);
566                                 } else if (curcase) {
567                                         if (aeldebug & DEBUG_TOKENS)
568                                                 ast_verbose("Building statement from '%s'\n", rest);
569                                         if (!strcasecmp(rest, "break")) {
570                                                 char tmp[strlen(exten) + 10];
571                                                 sprintf(tmp, "%s|%d", exten, *pos - 1);
572                                                 ast_add_extension2(con, 0, margs, cpos, *label, NULL, "Goto", strdup(tmp), FREE, registrar);
573                                                 curcase = NULL;
574                                                 *label = NULL;
575                                         } else
576                                                 build_step("switch", margs, filename, lineno, con, margs, &cpos, rest, &fillin, label);
577                                 } else 
578                                         ast_log(LOG_WARNING, "Unreachable code in switch at about line %d of %s\n", lineno, filename);
579                                 if (aeldebug & DEBUG_TOKENS)
580                                         ast_verbose("--SWARG: %s\n", cur->data);
581                                 cur = cur->next;
582                         }
583                         /* Can't do anything with these */
584                         fillin_process(con, fillin, filename, lineno, NULL, 0, NULL, 0);
585                         fillin_free(fillin);
586                         arg_free(swargs);
587                 } else
588                         ast_log(LOG_WARNING, "Syntax error in switch declaration in %s around line %d!\n", filename, lineno); 
589                         
590         } else if (matches_keyword(data, "if")) {
591                 /* If... */
592                 args = data + strlen("if");
593                 while ((*args < 33) && (*args != '(')) args++;
594                 if ((*args == '(') && (c = argument_end(args))) {
595                         int ifblock;
596                         int ifstart;
597                         int elsestart;
598                         int ifend;
599                         int ifskip;
600                         char *elses;
601                         char *iflabel;
602                         args++;
603                         *c = '\0';
604                         c++;
605                         while(*c && (*c < 33)) c++;
606                         if (aeldebug & DEBUG_TOKENS)
607                                 ast_verbose("--IF on : '%s' : '%s'\n", args, c);
608                         mlen = strlen(exten) + 128 + strlen(args) + strlen(name);
609                         margs = alloca(mlen);
610                         /* Remember where the ifblock starts, and skip over */
611                         ifblock = (*pos)++;
612                         iflabel = *label;
613                         *label = NULL;
614                         /* Remember where the start of the ifblock is */
615                         ifstart = *pos;
616                         snprintf(margs, mlen, "if-%s-%d", name, ifblock);
617                         /* Now process the block of the if */
618                         if (aeldebug & DEBUG_TOKENS)
619                                 ast_verbose("Searching for elses in '%s'\n", c);
620                         elses = grab_else(c, filename, lineno);
621                         build_step("if", margs, filename, lineno, con, exten, pos, c, fillout, label);
622                         if (elses) {
623                                 /* Reserve a goto to exit the if */
624                                 ifskip = *pos;
625                                 (*pos)++;
626                                 elsestart = *pos;
627                                 build_step("else", margs, filename, lineno, con, exten, pos, elses, fillout, label);
628                         } else {
629                                 elsestart = *pos;
630                                 ifskip = 0;
631                         }
632                         ifend = *pos;
633                         (*pos)++;
634                         app = "NoOp";
635                         snprintf(margs, mlen, "Finish if-%s-%d", name, ifblock);
636                         if (ast_add_extension2(con, 0, exten, ifend, *label, NULL, app, strdup(margs), FREE, registrar))
637                                 ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
638                         *label = NULL;
639                         app = "GotoIf";
640                         snprintf(margs, mlen, "$[ %s ]?%d:%d", args, ifstart, elsestart);
641                         if (ast_add_extension2(con, 0, exten, ifblock, iflabel, NULL, app, strdup(margs), FREE, registrar))
642                                 ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
643                         if (ifskip) {
644                                 /* Skip as appropriate around else clause */
645                                 snprintf(margs, mlen, "%d", ifend);
646                                 if (ast_add_extension2(con, 0, exten, ifskip, NULL, NULL, "Goto", strdup(margs), FREE, registrar))
647                                         ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
648                         }
649                 } else
650                         ast_log(LOG_WARNING, "Syntax error in if declaration in %s around line %d!\n", filename, lineno); 
651         } else if (matches_keyword(data, "while")) {
652                 /* While... */
653                 fillin = NULL;
654                 args = data + strlen("while");
655                 while ((*args < 33) && (*args != '(')) args++;
656                 if ((*args == '(') && (c = argument_end(args))) {
657                         int whileblock;
658                         int whilestart;
659                         int whileend;
660                         char *whilelabel;
661                         args++;
662                         *c = '\0';
663                         c++;
664                         while(*c && (*c < 33)) c++;
665                         if (aeldebug & DEBUG_TOKENS)
666                                 ast_verbose("--WHILE on : '%s' : '%s'\n", args, c);
667                         mlen = strlen(exten) + 128 + strlen(args) + strlen(name);
668                         margs = alloca(mlen);
669                         /* Remember where to put the conditional, and keep its position */
670                         whilestart = (*pos);
671                         whilelabel = *label;
672                         *label = NULL;
673                         (*pos)++;
674                         /* Remember where the whileblock starts */
675                         whileblock = (*pos);
676                         snprintf(margs, mlen, "while-%s-%d", name, whilestart);
677                         build_step("while", margs, filename, lineno, con, exten, pos, c, &fillin, label);
678                         /* Close the loop */
679                         app = "Goto";
680                         snprintf(margs, mlen, "%d", whilestart);
681                         if (ast_add_extension2(con, 0, exten, (*pos)++, *label, NULL, app, strdup(margs), FREE, registrar))
682                                 ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
683                         *label = NULL;
684                         whileend = (*pos);
685                         /* Place trailer */
686                         app = "NoOp";
687                         snprintf(margs, mlen, "Finish while-%s-%d", name, whilestart);
688                         if (ast_add_extension2(con, 0, exten, (*pos)++, *label, NULL, app, strdup(margs), FREE, registrar))
689                                 ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
690                         *label = NULL;
691                         app = "GotoIf";
692                         snprintf(margs, mlen, "$[ %s ]?%d:%d", args, whileblock, whileend);
693                         if (ast_add_extension2(con, 0, exten, whilestart, whilelabel, NULL, app, strdup(margs), FREE, registrar))
694                                 ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
695                         fillin_process(con, fillin, filename, lineno, exten, whileend, exten, whilestart);
696                         fillin_free(fillin);
697                 } else
698                         ast_log(LOG_WARNING, "Syntax error in while declaration in %s around line %d!\n", filename, lineno); 
699         } else if (matches_keyword(data, "jump")) {
700                 char *p;
701                 /* Jump... */
702                 fillin = NULL;
703                 args = data + strlen("jump");
704                 while(*args && (*args < 33)) args++;
705                 if (aeldebug & DEBUG_TOKENS)
706                         ast_verbose("--JUMP to : '%s'\n", args);
707                 p = strchr(args, ',');
708                 if (p) {
709                         *p = '\0';
710                         p++;
711                 } else
712                         p = "1";
713                 c = strchr(args, '@');
714                 if (c) {
715                         *c = '\0';
716                         c++;
717                 }
718                 mlen = strlen(exten) + 128 + strlen(args) + strlen(name) + (c ? strlen(c) : 0);
719                 margs = alloca(mlen);
720                 if (c) 
721                         snprintf(margs, mlen, "%s|%s|%s", c,args, p);
722                 else
723                         snprintf(margs, mlen, "%s|%s", args, p);
724                 app = "Goto";
725                 if (ast_add_extension2(con, 0, exten, (*pos)++, *label, NULL, app, strdup(margs), FREE, registrar))
726                         ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
727                 *label = NULL;
728         } else if (matches_keyword(data, "goto")) {
729                 /* Jump... */
730                 fillin = NULL;
731                 args = data + strlen("goto");
732                 while(*args && (*args < 33)) args++;
733                 if (aeldebug & DEBUG_TOKENS)
734                         ast_verbose("--GOTO to : '%s'\n", args);
735                 app = "Goto";
736                 if (ast_add_extension2(con, 0, exten, (*pos)++, *label, NULL, app, strdup(args), FREE, registrar))
737                         ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
738                 *label = NULL;
739         } else if (matches_keyword(data, "for")) {
740                 /* While... */
741                 fillin = NULL;
742                 args = data + strlen("for");
743                 while ((*args < 33) && (*args != '(')) args++;
744                 if ((*args == '(') && (c = argument_end(args))) {
745                         int forblock;
746                         int forprep;
747                         int forstart;
748                         int forend;
749                         struct stringlink *fields;
750                         char *tmp;
751                         char *forlabel = NULL;
752                         args++;
753                         *c = '\0';
754                         c++;
755                         while(*c && (*c < 33)) c++;
756                         /* Parse arguments first */
757                         tmp = alloca(strlen(args) + 10);
758                         if (tmp) {
759                                 snprintf(tmp, strlen(args) + 10, "{%s;}", args);
760                                 fields = arg_parse(tmp, filename, lineno);
761                         } else
762                                 fields = NULL;
763                         if (fields && fields->next && fields->next->next) {
764                                 if (aeldebug & DEBUG_TOKENS)
765                                         ast_verbose("--FOR ('%s' ; '%s' ; '%s') : '%s'\n", fields->data, fields->next->data, fields->next->next->data, c);
766                                 mlen = strlen(exten) + 128 + strlen(args) + strlen(name);
767                                 margs = alloca(mlen);
768                                 forprep = *pos;
769                                 snprintf(margs, mlen, "for-%s-%d", name, forprep);
770                                 fillin = NULL;
771                                 build_step("while", margs, filename, lineno, con, exten, pos, fields->data, &fillin, label);
772                                 /* Remember where to put the conditional, and keep its position */
773                                 forstart = (*pos);
774                                 forlabel = *label;
775                                 (*pos)++;
776                                 *label = NULL;
777                                 /* Remember where the whileblock starts */
778                                 forblock = (*pos);
779                                 build_step("for", margs, filename, lineno, con, exten, pos, fields->next->next->data, &fillin, label);
780                                 build_step("for", margs, filename, lineno, con, exten, pos, c, &fillin, label);
781                                 /* Close the loop */
782                                 app = "Goto";
783                                 snprintf(margs, mlen, "%d", forstart);
784                                 if (ast_add_extension2(con, 0, exten, (*pos)++, *label, NULL, app, strdup(margs), FREE, registrar))
785                                         ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
786                                 *label = NULL;
787                                 forend = (*pos);
788                                 /* Place trailer */
789                                 app = "NoOp";
790                                 snprintf(margs, mlen, "Finish for-%s-%d", name, forprep);
791                                 if (ast_add_extension2(con, 0, exten, (*pos)++, *label, NULL, app, strdup(margs), FREE, registrar))
792                                         ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
793                                 *label = NULL;
794                                 app = "GotoIf";
795                                 snprintf(margs, mlen, "$[ %s ]?%d:%d", fields->next->data, forblock, forend);
796                                 if (ast_add_extension2(con, 0, exten, forstart, forlabel, NULL, app, strdup(margs), FREE, registrar))
797                                         ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", forstart, what, name);
798                                 fillin_process(con, fillin, filename, lineno, exten, forend, exten, forstart);
799                                 fillin_free(fillin);
800                         } else
801                                 ast_log(LOG_NOTICE, "Improper for declaration in %s around line %d!\n", filename, lineno); 
802                         arg_free(fields);
803                 } else
804                         ast_log(LOG_WARNING, "Syntax error in for declaration in %s around line %d!\n", filename, lineno); 
805                         
806         } else if (!strcasecmp(data, "break") || !strcasecmp(data, "continue")) {
807                 struct fillin *fi;
808                 fi = malloc(sizeof(struct fillin));
809                 if (fi) {
810                         memset(fi, 0, sizeof(struct fillin));
811                         if (!strcasecmp(data, "break"))
812                                 fi->type = FILLIN_BREAK;
813                         else
814                                 fi->type = FILLIN_CONTINUE;
815                         ast_copy_string(fi->exten, exten, sizeof(fi->exten));
816                         fi->priority = (*pos)++;
817                         fi->next = *fillout;
818                         *fillout = fi;
819                 }
820         } else if (match_assignment(data, &rest)) {
821                 if (aeldebug & DEBUG_TOKENS)
822                         ast_verbose("ASSIGN  '%s' = '%s'\n", data, rest);
823                 mlen = strlen(rest) + strlen(data) + 20;
824                 margs = alloca(mlen);
825                 snprintf(margs, mlen, "%s=$[ %s ]", data, rest);
826                 app = "Set";
827                 if (ast_add_extension2(con, 0, exten, *pos, *label, NULL, app, strdup(margs), FREE, registrar))
828                         ast_log(LOG_WARNING, "Unable to add assignment at priority '%d' of %s '%s'\n", *pos, what, name);
829                 else {
830                         *label = NULL;
831                         (*pos)++;
832                 }
833         } else {
834                 app = data;
835                 args = app;
836                 while (*args && (*args > 32) && (*args != '(')) args++;
837                         if (*args != '(') {
838                         while(*args && (*args != '(')) { *args = '\0'; args++; };
839                 }
840                 if (*args == '(') {
841                         *args = '\0';
842                         args++;
843                         /* Got arguments, trim trailing ')' */
844                         c = args + strlen(args) - 1;
845                         while((c >= args) && (*c < 33) && (*c != ')')) { *c = '\0'; c--; };
846                         if ((c >= args) && (*c == ')')) *c = '\0';
847                 } else
848                         args = "";
849                 ast_process_quotes_and_slashes(args, ',', '|');
850                 if (app[0] == '&') {
851                         app++;
852                         margs = alloca(strlen(args) + strlen(app) + 10);
853                         sprintf(margs, "%s|%s", app, args);
854                         args = margs;
855                         app = "Macro";
856                 }
857                 if (aeldebug & DEBUG_TOKENS)
858                         ast_verbose("-- APP: '%s', ARGS: '%s'\n", app, args);
859                 if (ast_add_extension2(con, 0, exten, *pos, *label, NULL, app, strdup(args), FREE, registrar))
860                         ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
861                 else {
862                         (*pos)++;
863                         *label = NULL;
864                 }
865         }
866         return 0;
867 }
868
869 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)
870 {
871         struct stringlink *args, *cur;
872         int res=0;
873         struct fillin *fillin=NULL;
874         int dropfill = 0;
875         char *labelin = NULL;
876         if (!fillout) {
877                 fillout = &fillin;
878                 dropfill = 1;
879         }
880         if (!label) {
881                 label = &labelin;
882         };
883         args = arg_parse(data, filename, lineno);
884         cur = args;
885         while(cur) {
886                 res |= __build_step(what, name, filename, lineno, con, exten, pos, cur->data, fillout, label);
887                 cur = cur->next;
888         }
889         arg_free(args);
890         if (dropfill) {
891                 fillin_process(con, fillin, filename, lineno, NULL, 0, NULL, 0);
892                 fillin_free(fillin);
893         }
894         return res;
895 }
896
897 static int parse_catch(char *data, char **catch, char **rest)
898 {
899         /* Skip the word 'catch' */
900         data += 5;
901         while (*data && (*data < 33)) data++;
902         /* Here's the extension */
903         *catch = data;
904         if (!*data)
905                 return 0;
906         while (*data && (*data > 32)) data++;
907         if (!*data)
908                 return 0;
909         /* Trim any trailing spaces */
910         *data = '\0';
911         data++;
912         while(*data && (*data < 33)) data++;
913         if (!*data)
914                 return 0;
915         *rest = data;
916         return 1;
917 }
918
919 static void handle_macro(struct ast_context **local_contexts, struct stringlink *vars, const char *filename, int lineno)
920 {
921         struct stringlink *argv;
922         struct stringlink *paramv;
923         struct stringlink *cur;
924         struct ast_context *con;
925         struct fillin *fillin;
926         char *catch, *rest;
927         char name[256];
928         int pos;
929         int cpos;
930
931         if (aeldebug & DEBUG_MACROS)
932                 ast_verbose("Root macro def is '%s'\n", vars->data);
933         argv = split_token(vars->data, filename, lineno);
934         paramv = split_params(vars->data, filename, lineno);
935         if (aeldebug & DEBUG_MACROS) 
936                 ast_verbose("Found macro '%s'\n", vars->data);
937         snprintf(name, sizeof(name), "macro-%s", vars->data);
938         con = ast_context_create(local_contexts, name, registrar);
939         if (con) {
940                 pos = 1;
941                 cur = paramv;
942                 while(cur) {
943                         if (aeldebug & DEBUG_MACROS)
944                                 ast_verbose("  PARAM => '%s'\n", cur->data);
945                         snprintf(name, sizeof(name), "%s=${ARG%d}", cur->data, pos);
946                         if (ast_add_extension2(con, 0, "s", pos, NULL, NULL, "Set", strdup(name), FREE, registrar))
947                                 ast_log(LOG_WARNING, "Unable to add step at priority '%d' of macro '%s'\n", pos, vars->data);
948                         else
949                                 pos++;
950                         cur = cur->next;
951                 }
952                 cur = argv;
953                 while(cur) {
954                         if (aeldebug & DEBUG_MACROS)
955                                 ast_verbose("  STEP => '%s'\n", cur->data);
956                         if (matches_keyword(cur->data, "catch")) {
957                                 if (aeldebug & DEBUG_MACROS)
958                                         ast_verbose("--CATCH: '%s'\n", cur->data);
959                                 if (parse_catch(cur->data, &catch, &rest)) {
960                                         cpos = 1;
961                                         build_step("catch", catch, filename, lineno, con, catch, &cpos, rest, NULL, NULL);
962                                 } else
963                                         ast_log(LOG_NOTICE, "Parse error for catch at about line %d of %s\n", lineno, filename);
964                         } else {
965                                 fillin = NULL;
966                                 build_step("macro", vars->data, filename, lineno, con, "s", &pos, cur->data, NULL, NULL);
967                         }
968                         cur = cur->next;
969                 }
970         } else
971                 ast_log(LOG_WARNING, "Unable to create context '%s'\n", name);
972         arg_free(argv);
973         if (vars->next)
974                 ast_log(LOG_NOTICE, "Ignoring excess tokens in macro definition around line %d of %s!\n", lineno, filename);
975 }
976
977 static int matches_extension(char *exten, char **extout)
978 {
979         char *c;
980         *extout = NULL;
981         c = exten;
982         while(*c && (*c > 32)) c++;
983         if (*c) {
984                 *c = '\0';
985                 c++;
986                 while(*c && (*c < 33)) c++;
987                 if (*c) {
988                         if (*c == '=') {
989                                 *c = '\0';
990                                 c++;
991                                 if (*c == '>')
992                                         c++;
993                                 while (*c && (*c < 33)) c++;
994                                 *extout = c;
995                                 return 1;
996                         }
997                 }
998         }
999         return 0;
1000 }
1001
1002 static void parse_keyword(char *s, char **o)
1003 {
1004         char *c;
1005         c = s;
1006         while((*c) && (*c > 32)) c++;
1007         if (*c) {
1008                 *c = '\0';
1009                 c++;
1010                 while(*c && (*c < 33)) c++;
1011                 *o = c;
1012         } else
1013                 *o = NULL;
1014 }
1015
1016 static void handle_context(struct ast_context **local_contexts, struct stringlink *vars, const char *filename, int lineno)
1017 {
1018         struct stringlink *argv;
1019         struct stringlink *paramv;
1020         struct stringlink *cur2;
1021         struct stringlink *argv2;
1022         struct stringlink *cur;
1023         struct ast_context *con;
1024         char *rest;
1025         char *c;
1026         char name[256];
1027         int pos;
1028
1029         if (aeldebug & DEBUG_CONTEXTS)
1030                 ast_verbose("Root context def is '%s'\n", vars->data);
1031         argv = split_token(vars->data, filename, lineno);
1032         paramv = split_params(vars->data, filename, lineno);
1033         if (aeldebug & DEBUG_CONTEXTS) 
1034                 ast_verbose("Found context '%s'\n", vars->data);
1035         snprintf(name, sizeof(name), "%s", vars->data);
1036         con = ast_context_create(local_contexts, name, registrar);
1037         if (con) {
1038                 cur = argv;
1039                 while(cur) {
1040                         if (matches_keyword(cur->data, "includes")) {
1041                                 if (aeldebug & DEBUG_CONTEXTS)
1042                                         ast_verbose("--INCLUDES: '%s'\n", cur->data);
1043                                 parse_keyword(cur->data, &rest);
1044                                 if (rest) {
1045                                         argv2 = arg_parse(rest, filename, lineno);
1046                                         cur2 = argv2;
1047                                         while(cur2) {
1048                                                 ast_context_add_include2(con, cur2->data, registrar);
1049                                                 cur2 = cur2->next;
1050                                         }
1051                                         arg_free(argv2);
1052                                 }
1053                         } else if (matches_keyword(cur->data, "ignorepat")) {
1054                                 if (aeldebug & DEBUG_CONTEXTS)
1055                                         ast_verbose("--IGNOREPAT: '%s'\n", cur->data);
1056                                 parse_keyword(cur->data, &rest);
1057                                 if (rest) {
1058                                         argv2 = arg_parse(rest, filename, lineno);
1059                                         cur2 = argv2;
1060                                         while(cur2) {
1061                                                 ast_context_add_ignorepat2(con, cur2->data, registrar);
1062                                                 cur2 = cur2->next;
1063                                         }
1064                                         arg_free(argv2);
1065                                 }
1066                         } else if (matches_keyword(cur->data, "switches") || matches_keyword(cur->data, "eswitches")) {
1067                                 if (aeldebug & DEBUG_CONTEXTS)
1068                                         ast_verbose("--[E]SWITCH: '%s'\n", cur->data);
1069                                 parse_keyword(cur->data, &rest);
1070                                 if (rest) {
1071                                         argv2 = arg_parse(rest, filename, lineno);
1072                                         cur2 = argv2;
1073                                         while(cur2) {
1074                                                 c = strchr(cur2->data, '/');
1075                                                 if (c) {
1076                                                         *c = '\0';
1077                                                         c++;
1078                                                 } else
1079                                                         c = "";
1080                                                 ast_context_add_switch2(con, cur2->data, c, (cur->data[0] == 'e'), registrar);
1081                                                 cur2 = cur2->next;
1082                                         }
1083                                         arg_free(argv2);
1084                                 }
1085                         } else if (matches_extension(cur->data, &rest)) {
1086                                 if (aeldebug & DEBUG_CONTEXTS)
1087                                         ast_verbose("Extension: '%s' => '%s'\n", cur->data, rest);
1088                                 pos = 1;
1089                                 build_step("extension", cur->data, filename, lineno, con, cur->data, &pos, rest, NULL, NULL);
1090                         }
1091                         cur = cur->next;
1092                 }
1093         } else
1094                         ast_log(LOG_WARNING, "Unable to create context '%s'\n", name);
1095         arg_free(argv);
1096         if (vars->next)
1097                 ast_log(LOG_NOTICE, "Ignoring excess tokens in macro definition around line %d of %s!\n", lineno, filename);
1098 }
1099
1100 static int handle_root_token(struct ast_context **local_contexts, char *token, int level, const char *filename, int lineno)
1101 {
1102         struct stringlink *argv, *cur;
1103         argv = split_token(token, filename, lineno);
1104         if (aeldebug & DEBUG_TOKENS) {
1105                 ast_verbose("Found root token '%s' at level %d (%s:%d)!\n", token, level, filename, lineno);
1106                 cur = argv;
1107                 while(cur) {
1108                         ast_verbose("   ARG => '%s'\n", cur->data);
1109                         cur = cur->next;
1110                 }
1111         }
1112         if (!strcasecmp(token, "globals")) {
1113                 handle_globals(argv);
1114         } else if (!strcasecmp(token, "macro")) {
1115                 handle_macro(local_contexts, argv, filename, lineno);
1116         } else if (!strcasecmp(token, "context")) {
1117                 handle_context(local_contexts, argv, filename, lineno);
1118         } else {
1119                 ast_log(LOG_NOTICE, "Unknown root token '%s'\n", token);
1120         }
1121         arg_free(argv);
1122         return 0;
1123 }
1124
1125
1126 static int ast_ael_compile(struct ast_context **local_contexts, const char *filename)
1127 {
1128         char *rfilename;
1129         char *buf, *tbuf;
1130         int bufsiz;
1131         FILE *f;
1132         char *c;
1133         char *token;
1134         int lineno=0;
1135
1136         if (filename[0] == '/')
1137                 rfilename = (char *)filename;
1138         else {
1139                 rfilename = alloca(strlen(filename) + strlen(ast_config_AST_CONFIG_DIR) + 2);
1140                 sprintf(rfilename, "%s/%s", ast_config_AST_CONFIG_DIR, filename);
1141         }
1142         
1143         f = fopen(rfilename, "r");
1144         if (!f) {
1145                 ast_log(LOG_WARNING, "Unable to open '%s': %s\n", rfilename, strerror(errno));
1146                 return -1;
1147         }
1148         buf = malloc(4096);
1149         if (!buf) {
1150                 ast_log(LOG_WARNING, "Out of memory!\n");
1151                 fclose(f);
1152                 return -1;
1153         }
1154         buf[0] = 0;
1155         bufsiz = 4096;
1156         while(!feof(f)) {
1157                 if (bufsiz - strlen(buf) < 2048) {
1158                         bufsiz += 4096;
1159                         tbuf = realloc(buf, bufsiz);
1160                         if (tbuf) {
1161                                 buf = tbuf;
1162                         } else {
1163                                 free(buf);
1164                                 ast_log(LOG_WARNING, "Out of memory!\n");
1165                                 fclose(f);
1166                         }
1167                 }
1168                 if (fgets(buf + strlen(buf), bufsiz - strlen(buf), f)) {
1169                         lineno++;
1170                         while(*buf && buf[strlen(buf) - 1] < 33)
1171                                 buf[strlen(buf) - 1] = '\0';
1172                         c = strstr(buf, "//");
1173                         if (c)
1174                                 *c = '\0';
1175                         if (*buf) {
1176                                 if (aeldebug & DEBUG_READ)
1177                                         ast_verbose("Newly composed line '%s'\n", buf);
1178                                 while((token = grab_token(buf, filename, lineno))) {
1179                                         handle_root_token(local_contexts, token, 0, filename, lineno);
1180                                         free(token);
1181                                 }
1182                         }
1183                 }
1184         };
1185         free(buf);
1186         fclose(f);
1187         return 0;
1188 }
1189
1190 static int pbx_load_module(void)
1191 {
1192         struct ast_context *local_contexts=NULL, *con;
1193         ast_ael_compile(&local_contexts, config);
1194         ast_merge_contexts_and_delete(&local_contexts, registrar);
1195         for (con = ast_walk_contexts(NULL); con; con = ast_walk_contexts(con))
1196                 ast_context_verify_includes(con);
1197
1198         return 0;
1199 }
1200
1201 /* CLI interface */
1202 static int ael_debug_read(int fd, int argc, char *argv[])
1203 {
1204         aeldebug |= DEBUG_READ;
1205         return 0;
1206 }
1207
1208 static int ael_debug_tokens(int fd, int argc, char *argv[])
1209 {
1210         aeldebug |= DEBUG_TOKENS;
1211         return 0;
1212 }
1213
1214 static int ael_debug_macros(int fd, int argc, char *argv[])
1215 {
1216         aeldebug |= DEBUG_MACROS;
1217         return 0;
1218 }
1219
1220 static int ael_debug_contexts(int fd, int argc, char *argv[])
1221 {
1222         aeldebug |= DEBUG_CONTEXTS;
1223         return 0;
1224 }
1225
1226 static int ael_no_debug(int fd, int argc, char *argv[])
1227 {
1228         aeldebug = 0;
1229         return 0;
1230 }
1231
1232 static int ael_reload(int fd, int argc, char *argv[])
1233 {
1234         ast_context_destroy(NULL, registrar);
1235         return (pbx_load_module());
1236 }
1237
1238 static struct ast_cli_entry  ael_cli[] = {
1239         { { "ael", "reload", NULL }, ael_reload, "Reload AEL configuration"},
1240         { { "ael", "debug", "read", NULL }, ael_debug_read, "Enable AEL read debug"},
1241         { { "ael", "debug", "tokens", NULL }, ael_debug_tokens, "Enable AEL tokens debug"},
1242         { { "ael", "debug", "macros", NULL }, ael_debug_macros, "Enable AEL macros debug"},
1243         { { "ael", "debug", "contexts", NULL }, ael_debug_contexts, "Enable AEL contexts debug"},
1244         { { "ael", "no", "debug", NULL }, ael_no_debug, "Disable AEL debug messages"},
1245 };
1246
1247 /*
1248  * Standard module functions ...
1249  */
1250 int unload_module(void)
1251 {
1252         ast_context_destroy(NULL, registrar);
1253         ast_cli_unregister_multiple(ael_cli, sizeof(ael_cli)/ sizeof(ael_cli[0]));
1254         return 0;
1255 }
1256
1257
1258 int load_module(void)
1259 {
1260         ast_cli_register_multiple(ael_cli, sizeof(ael_cli)/ sizeof(ael_cli[0]));
1261         return (pbx_load_module());
1262 }
1263
1264 int reload(void)
1265 {
1266         unload_module();
1267         return (load_module());
1268 }
1269
1270 int usecount(void)
1271 {
1272         return 0;
1273 }
1274
1275 char *description(void)
1276 {
1277         return dtext;
1278 }
1279
1280 char *key(void)
1281 {
1282         return ASTERISK_GPL_KEY;
1283 }