Mon Mar 20 08:20:13 2006

Asterisk developer's documentation


Main Page | Modules | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals | Related Pages

pbx_ael.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2005, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Compile symbolic Asterisk Extension Logic into Asterisk extensions
00022  * 
00023  */
00024 
00025 #include <sys/types.h>
00026 #include <stdlib.h>
00027 #include <stdio.h>
00028 #include <string.h>
00029 #include <ctype.h>
00030 #include <errno.h>
00031 
00032 #include "asterisk.h"
00033 
00034 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 7577 $")
00035 
00036 #include "asterisk/pbx.h"
00037 #include "asterisk/config.h"
00038 #include "asterisk/module.h"
00039 #include "asterisk/logger.h"
00040 #include "asterisk/cli.h"
00041 #include "asterisk/callerid.h"
00042 
00043 struct stringlink {
00044    struct stringlink *next;
00045    char data[0];
00046 };
00047 
00048 #define FILLIN_BREAK    1
00049 #define FILLIN_CONTINUE    2
00050 
00051 struct fillin {
00052    struct fillin *next;
00053    char exten[AST_MAX_EXTENSION];
00054    int priority;
00055    int type;
00056 };
00057 
00058 #ifdef __AST_DEBUG_MALLOC
00059 static void FREE(void *ptr)
00060 {
00061    free(ptr);
00062 }
00063 #else
00064 #define FREE free
00065 #endif
00066 
00067 #define DEBUG_READ   (1 << 0)
00068 #define DEBUG_TOKENS (1 << 1)
00069 #define DEBUG_MACROS (1 << 2)
00070 #define DEBUG_CONTEXTS (1 << 3)
00071 
00072 static int aeldebug = 0;
00073 
00074 static char *dtext = "Asterisk Extension Language Compiler";
00075 static char *config = "extensions.ael";
00076 static char *registrar = "pbx_ael";
00077 
00078 static char *__grab_token(char *src, const char *filename, int lineno, int link)
00079 {
00080    char *c;
00081    char *b;
00082    char *a;
00083    int level = 0;
00084    char *ret;
00085 #if 0
00086    if (aeldebug || DEBUG_TOKENS) 
00087       ast_verbose("Searching for token in '%s'!\n", src);
00088 #endif
00089    c = src;
00090    while(*c) {
00091       if ((*c == '\\')) {
00092          c++;
00093          if (!*c)
00094             c--;
00095       } else {
00096          if ((*c == '{') || (*c == '(')) {
00097             level++;
00098          } else if ((*c == '}') || (*c == ')')) {
00099             if (level)
00100                level--;
00101             else
00102                ast_log(LOG_WARNING, "Syntax error at line %d of '%s', too many closing braces!\n", lineno, filename);
00103          } else if ((*c == ';') && !level) {
00104             /* Got a token! */
00105             *c = '\0';
00106             b = c;
00107             b--;
00108             c++;
00109             while((b > src) && (*b < 33)) { 
00110                *b = '\0'; 
00111                b--; 
00112             }
00113             a = ast_skip_blanks(src);
00114             if (link) {
00115                ret = malloc(strlen(a) + sizeof(struct stringlink) + 1);
00116                if (ret)
00117                   strcpy(ret + sizeof(struct stringlink), a);
00118             } else
00119                ret = strdup(a);
00120             /* Save remainder */
00121             memmove(src, c, strlen(c) + 1);
00122             return ret;
00123          }
00124       }
00125       c++;
00126    }
00127    return NULL;      
00128 }
00129 
00130 static char *grab_token(char *src, const char *filename, int lineno)
00131 {
00132    return __grab_token(src, filename, lineno, 0);
00133 }
00134 
00135 static struct stringlink *arg_parse(char *args, const char *filename, int lineno)
00136 {
00137    struct stringlink *cur, *prev=NULL, *root=NULL;
00138    if (args) {
00139       if (aeldebug & DEBUG_TOKENS) 
00140          ast_verbose("Parsing args '%s'!\n", args);
00141       if (args[0] == '{') {
00142          /* Strip mandatory '}' from end */
00143          args[strlen(args) - 1] = '\0';
00144          while ((cur = (struct stringlink *)__grab_token(args + 1, filename, lineno, 1))) {
00145             cur->next = NULL;
00146             if (prev)
00147                prev->next = cur;
00148             else
00149                root = cur;
00150             prev = cur;
00151          }
00152       } else if (*args) {
00153          root = malloc(sizeof(struct stringlink) + strlen(args) + 1);
00154          if (root) {
00155             strcpy(root->data, args);
00156             root->next = NULL;
00157          }
00158       }
00159    }
00160    return root;
00161 }
00162 
00163 static char *grab_else(char *args, const char *filename, int lineno)
00164 {
00165    char *ret = NULL;
00166    int level=0;
00167    char *c;
00168    if (args) {
00169       if (args[0] == '{') {
00170          c = args;
00171          while(*c) {
00172             if (*c == '{')
00173                level++;
00174             else if (*c == '}') {
00175                level--;
00176                if (!level) {
00177                   c++;
00178                   while(*c && (*c < 33)) { *c = '\0'; c++; };
00179                   if (!strncasecmp(c, "else", 4) && 
00180                      ((c[4] == '{') || (c[4] < 33))) {
00181                         /* Ladies and gentlemen, we have an else clause */
00182                      *c = '\0';
00183                      c += 4;
00184                      c = ast_skip_blanks(c);
00185                      ret = c;
00186                      if (aeldebug & DEBUG_TOKENS)
00187                         ast_verbose("Returning else clause '%s'\n", c);
00188                   }
00189                   break;
00190                }
00191             }
00192             c++;
00193          }
00194       }
00195    }
00196    return ret;
00197 }
00198 
00199 static struct stringlink *param_parse(char *parms, const char *macro, const char *filename, int lineno)
00200 {
00201    char *s, *e;
00202    struct stringlink *root = NULL, *prev=NULL, *cur;
00203    if (!parms || !*parms)
00204       return NULL;
00205    if (*parms != '(') {
00206       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);
00207       return NULL;
00208    }
00209    s = parms + 1;
00210    while(*s) {
00211       s = ast_skip_blanks(s);
00212       e = s;
00213       while(*e &&  (*e != ')') && (*e != ',')) {
00214          if (*e < 33)
00215             *e = '\0';
00216          e++;
00217       }
00218       if (*e) {
00219          /* Strip token */
00220          *e = '\0';
00221          e++;
00222          /* Skip over whitespace */
00223          e = ast_skip_blanks(e);
00224          /* Link */
00225          cur = malloc(strlen(s) + sizeof(struct stringlink) + 1);
00226          if (cur) {
00227             cur->next = NULL;
00228             strcpy(cur->data, s);
00229             if (prev)
00230                prev->next = cur;
00231             else
00232                root = cur;
00233             prev = cur;
00234          }
00235          s = e;
00236       }
00237    }
00238    return root;
00239 }
00240 
00241 static void arg_free(struct stringlink *cur)
00242 {
00243    struct stringlink *last;
00244    while(cur) {
00245       last = cur;
00246       cur = cur->next;
00247       free(last);
00248    }
00249 }
00250 
00251 static void handle_globals(struct stringlink *vars)
00252 {
00253    while(vars) {
00254       pbx_builtin_setvar(NULL, vars->data);
00255       vars = vars->next;
00256    }
00257 }
00258 
00259 static struct stringlink *split_token(char *token, const char *filename, int lineno)
00260 {
00261    char *args, *p;
00262    struct stringlink *argv;
00263    args = token;
00264    while (*args && (*args > 32) && (*args != '{') && (*args != '(')) args++;
00265    if (*args) {
00266       p = args;
00267       args = ast_skip_blanks(args);
00268       if (*args != '(') {
00269          *p = '\0';
00270       } else {
00271          while (*args && (*args != ')')) args++;
00272          if (*args == ')') {
00273             args++;
00274             args = ast_skip_blanks(args);
00275          }
00276       }
00277       if (!*args)
00278          args = NULL;
00279    } else args = NULL;
00280    argv = arg_parse(args, filename, lineno);
00281    if (args)
00282       *args = '\0';
00283    return argv;
00284 }
00285 
00286 static int matches_keyword(const char *data, const char *keyword)
00287 {
00288    char c;
00289    if (!strncasecmp(data, keyword, strlen(keyword))) {
00290       c = data[strlen(keyword)];
00291       if ((c < 33) || (c == '(') || (c == '{'))
00292          return 1;
00293    }
00294    return 0;
00295 }
00296 
00297 static struct stringlink *split_params(char *token, const char *filename, int lineno)
00298 {
00299    char *params;
00300    struct stringlink *paramv;
00301    params = token;
00302    while(*params && (*params > 32) && (*params != '(')) params++;
00303    if (*params) {
00304       if (*params != '(') {
00305          *params = '\0';
00306          params++;
00307          params = ast_skip_blanks(params);
00308       }
00309       if (!*params)
00310          params = NULL;
00311    } else params = NULL;
00312    paramv = param_parse(params, token, filename, lineno);
00313    if (params)
00314       *params = '\0';
00315    return paramv;
00316 }
00317 
00318 static const char *get_case(char *s, char **restout, int *pattern)
00319 {
00320    char *newcase=NULL;
00321    char *rest=NULL;
00322    if (!strncasecmp(s, "case", 4) && s[4] && ((s[4] < 33) || (s[4] == ':'))) {
00323       newcase = s + 4;
00324       newcase = ast_skip_blanks(newcase);
00325       rest = newcase;
00326       *pattern = 0;
00327    } else if (!strncasecmp(s, "pattern", 7) && s[7] && ((s[7] < 33) || (s[7] == ':'))) {
00328       newcase = s + 8;
00329       newcase = ast_skip_blanks(newcase);
00330       rest = newcase;
00331       *pattern = 1;
00332    } else if (!strncasecmp(s, "default", 7) && ((s[7] < 33) || (s[7] == ':'))) {
00333       newcase = ".";
00334       rest = s + 7;
00335       rest = ast_skip_blanks(rest);
00336       *pattern = 1;
00337    }
00338 
00339    if (rest) {
00340       while (*rest && (*rest > 32) && (*rest != ':')) rest++;
00341       if (*rest) {
00342          *rest = 0;
00343          rest++;
00344          while (*rest && ((*rest == ':') || (*rest < 33))) rest++;
00345          *restout = rest;
00346       } else {
00347          *restout = "";
00348       }
00349    } else
00350       *restout = s;
00351    if (aeldebug & DEBUG_TOKENS)
00352       ast_verbose("GETCASE: newcase is '%s', rest = '%s'\n", newcase, *restout);
00353    return newcase;
00354 }
00355 
00356 static void fillin_free(struct fillin *fillin)
00357 {
00358    struct fillin *cur, *next;
00359    cur =  fillin;
00360    while(cur) {
00361       next = cur->next;
00362       free(cur);
00363       cur = next;
00364    }
00365 }
00366 
00367 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)
00368 {
00369    struct fillin *cur;
00370    char *app;
00371    char mdata[AST_MAX_EXTENSION + 20];
00372    cur = fillin;
00373    while(cur) {
00374       if (cur->type == FILLIN_BREAK) {
00375          if (breakexten && breakprio) {
00376             app = "Goto";
00377             snprintf(mdata, sizeof(mdata), "%s|%d", breakexten, breakprio);
00378          } else {
00379             app = "NoOp";
00380             snprintf(mdata, sizeof(mdata), "Invalid break");
00381             ast_log(LOG_NOTICE, "Ignoring inappropriate break around line %d of %s\n", lineno, filename);
00382          }
00383          if (ast_add_extension2(con, 0, cur->exten, cur->priority, NULL, NULL, app, strdup(mdata), FREE, registrar))
00384             ast_log(LOG_WARNING, "Unable to add step at priority '%d' of break '%s'\n", cur->priority, cur->exten);
00385       } else if (cur->type == FILLIN_CONTINUE) {
00386          if (contexten && contprio) {
00387             app = "Goto";
00388             snprintf(mdata, sizeof(mdata), "%s|%d", contexten, contprio);
00389          } else {
00390             app = "NoOp";
00391             snprintf(mdata, sizeof(mdata), "Invalid continue");
00392             ast_log(LOG_NOTICE, "Ignoring inappropriate continue around line %d of %s\n", lineno, filename);
00393          }
00394          if (ast_add_extension2(con, 0, cur->exten, cur->priority, NULL, NULL, app, strdup(mdata), FREE, registrar))
00395             ast_log(LOG_WARNING, "Unable to add step at priority '%d' of continue '%s'\n", cur->priority, cur->exten);
00396       } else {
00397          ast_log(LOG_WARNING, "Whoa, unknown fillin type '%d'\n", cur->type);
00398       }
00399       cur = cur->next;
00400    }
00401 }
00402 
00403 static int match_assignment(char *variable, char **value)
00404 {
00405    char *c;
00406    char *ws;
00407    int inpar = 0;
00408    c = variable;
00409    
00410    while (*c) {
00411       if(*c == ')' && (inpar > 0)) {
00412          inpar--;
00413       } else if(*c == '(' && (inpar >= 0)) {
00414          inpar++;
00415       } else if(*c == '=' && (inpar == 0)) {
00416          break;
00417       }
00418       c++;
00419    } 
00420    ws = c;
00421    c = ast_skip_blanks(c);
00422    if (*c == '=') {
00423       *ws = '\0';
00424       *c = '\0';
00425       c++;
00426       c = ast_skip_blanks(c);
00427       *value = c;
00428       return 1;
00429    }
00430    return 0;
00431 }
00432 
00433 static int matches_label(char *data, char **rest)
00434 {
00435    char last = 0;
00436    char *start = data;
00437    while (*data > 32) {
00438       last = *data;
00439       data++;
00440    }
00441    if (last != ':') {
00442       data = ast_skip_blanks(data);
00443       last = *data;
00444       data++;
00445    }
00446    if (last == ':') {
00447       *rest = data;
00448       /* Go back and trim up the label */
00449       while(*start && ((*start > 32) && (*start != ':'))) start++;
00450       *start = '\0';
00451       return 1;
00452    }
00453    return 0;
00454 }
00455 
00456 static char *argument_end(char *str)
00457 {
00458    int level=0;
00459    while(*++str) {
00460       switch(*str) {
00461       case '(':
00462          level++;
00463          break;
00464       case ')':
00465          if(level)
00466             level--;
00467          else
00468             return str;
00469          break;
00470       default:
00471          break;
00472       }
00473    }
00474    return NULL;
00475 }
00476 
00477 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);
00478 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)
00479 {
00480    char *app;
00481    char *args;
00482    char *c;
00483    char *margs=NULL;
00484    char *oargs;
00485    char *rest;
00486    const char *curcase, *newcase;
00487    struct stringlink *swargs, *cur;
00488    int cpos;
00489    int mlen;
00490    int pattern = 0;
00491    struct fillin *fillin;
00492    
00493    data = ast_skip_blanks(data);
00494    if (matches_label(data, &c)) {
00495       *label = data;
00496       data = c;
00497       data = ast_skip_blanks(data);
00498    }
00499    if (ast_strlen_zero(data))
00500       return 0;
00501    if (matches_keyword(data, "switch")) {
00502       fillin = NULL;
00503       /* Switch */
00504       args = data + strlen("switch");
00505       while ((*args < 33) && (*args != '(')) args++;
00506       if ((*args == '(') && (c = argument_end(args))) {
00507          args++;
00508          *c = '\0';
00509          c++;
00510          if (aeldebug & DEBUG_TOKENS)
00511             ast_verbose("--SWITCH on : %s\n", args);
00512          mlen = strlen(exten) + 128 + strlen(args) + strlen(name);
00513          margs = alloca(mlen);
00514          app = "Goto";
00515          sprintf(margs, "sw-%d-%s|1", *pos, args);
00516          ast_process_quotes_and_slashes(margs, ',', '|');
00517          oargs = args;
00518          args = margs;
00519          if (ast_add_extension2(con, 0, exten, *pos, *label, NULL, app, strdup(args), FREE, registrar))
00520             ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
00521          else {
00522             *label = NULL;
00523             (*pos)++;
00524          }
00525          app = "NoOp";
00526          sprintf(margs, "Finish switch-%d", *pos - 1);
00527          if (ast_add_extension2(con, 0, exten, *pos, *label, NULL, app, strdup(args), FREE, registrar))
00528             ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
00529          else {
00530             *label = NULL;
00531             (*pos)++;
00532          }
00533          c = ast_skip_blanks(c);
00534          if (aeldebug & DEBUG_TOKENS)
00535             ast_verbose("ARG Parsing '%s'\n", c);
00536          swargs = arg_parse(c, filename, lineno);
00537          cur = swargs;
00538          curcase = NULL;
00539          while(cur) {
00540             if ((newcase = get_case(cur->data, &rest, &pattern))) {
00541                if (aeldebug & DEBUG_TOKENS)
00542                   ast_verbose("--NEWCASE: '%s'!\n", newcase);
00543                if (curcase) {
00544                   /* Handle fall through */
00545                   char tmp[strlen(newcase) + strlen(name) + 40];
00546                   sprintf(tmp, "sw-%d-%s|%d", *pos - 2, newcase, 1);
00547                   ast_add_extension2(con, 0, margs, cpos, NULL, NULL, "Goto", strdup(tmp), FREE, registrar);
00548                }
00549                curcase = newcase;
00550                cpos = 1;
00551                if (pattern)
00552                   snprintf(margs, mlen, "_sw-%d-%s", *pos - 2, curcase);
00553                else
00554                   snprintf(margs, mlen, "sw-%d-%s", *pos - 2, curcase);
00555                if (!strcasecmp(rest, "break")) {
00556                   char tmp[strlen(exten) + 10];
00557                   sprintf(tmp, "%s|%d", exten, *pos - 1);
00558                   ast_add_extension2(con, 0, exten, cpos, *label, NULL, "Goto", strdup(tmp), FREE, registrar);
00559                   curcase = NULL;
00560                   *label = NULL;
00561                } else
00562                   build_step("switch", margs, filename, lineno, con, margs, &cpos, rest, &fillin, label);
00563             } else if (curcase) {
00564                if (aeldebug & DEBUG_TOKENS)
00565                   ast_verbose("Building statement from '%s'\n", rest);
00566                if (!strcasecmp(rest, "break")) {
00567                   char tmp[strlen(exten) + 10];
00568                   sprintf(tmp, "%s|%d", exten, *pos - 1);
00569                   ast_add_extension2(con, 0, margs, cpos, *label, NULL, "Goto", strdup(tmp), FREE, registrar);
00570                   curcase = NULL;
00571                   *label = NULL;
00572                } else
00573                   build_step("switch", margs, filename, lineno, con, margs, &cpos, rest, &fillin, label);
00574             } else 
00575                ast_log(LOG_WARNING, "Unreachable code in switch at about line %d of %s\n", lineno, filename);
00576             if (aeldebug & DEBUG_TOKENS)
00577                ast_verbose("--SWARG: %s\n", cur->data);
00578             cur = cur->next;
00579          }
00580          /* Can't do anything with these */
00581          fillin_process(con, fillin, filename, lineno, NULL, 0, NULL, 0);
00582          fillin_free(fillin);
00583          arg_free(swargs);
00584       } else
00585          ast_log(LOG_WARNING, "Syntax error in switch declaration in %s around line %d!\n", filename, lineno); 
00586          
00587    } else if (matches_keyword(data, "if")) {
00588       /* If... */
00589       args = data + strlen("if");
00590       while ((*args < 33) && (*args != '(')) args++;
00591       if ((*args == '(') && (c = argument_end(args))) {
00592          int ifblock;
00593          int ifstart;
00594          int elsestart;
00595          int ifend;
00596          int ifskip;
00597          char *elses;
00598          char *iflabel;
00599          args++;
00600          *c = '\0';
00601          c++;
00602          c = ast_skip_blanks(c);
00603          if (aeldebug & DEBUG_TOKENS)
00604             ast_verbose("--IF on : '%s' : '%s'\n", args, c);
00605          mlen = strlen(exten) + 128 + strlen(args) + strlen(name);
00606          margs = alloca(mlen);
00607          /* Remember where the ifblock starts, and skip over */
00608          ifblock = (*pos)++;
00609          iflabel = *label;
00610          *label = NULL;
00611          /* Remember where the start of the ifblock is */
00612          ifstart = *pos;
00613          snprintf(margs, mlen, "if-%s-%d", name, ifblock);
00614          /* Now process the block of the if */
00615          if (aeldebug & DEBUG_TOKENS)
00616             ast_verbose("Searching for elses in '%s'\n", c);
00617          elses = grab_else(c, filename, lineno);
00618          build_step("if", margs, filename, lineno, con, exten, pos, c, fillout, label);
00619          if (elses) {
00620             /* Reserve a goto to exit the if */
00621             ifskip = *pos;
00622             (*pos)++;
00623             elsestart = *pos;
00624             build_step("else", margs, filename, lineno, con, exten, pos, elses, fillout, label);
00625          } else {
00626             elsestart = *pos;
00627             ifskip = 0;
00628          }
00629          ifend = *pos;
00630          (*pos)++;
00631          app = "NoOp";
00632          snprintf(margs, mlen, "Finish if-%s-%d", name, ifblock);
00633          if (ast_add_extension2(con, 0, exten, ifend, *label, NULL, app, strdup(margs), FREE, registrar))
00634             ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
00635          *label = NULL;
00636          app = "GotoIf";
00637          snprintf(margs, mlen, "$[ %s ]?%d:%d", args, ifstart, elsestart);
00638          if (ast_add_extension2(con, 0, exten, ifblock, iflabel, NULL, app, strdup(margs), FREE, registrar))
00639             ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
00640          if (ifskip) {
00641             /* Skip as appropriate around else clause */
00642             snprintf(margs, mlen, "%d", ifend);
00643             if (ast_add_extension2(con, 0, exten, ifskip, NULL, NULL, "Goto", strdup(margs), FREE, registrar))
00644                ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
00645          }
00646       } else
00647          ast_log(LOG_WARNING, "Syntax error in if declaration in %s around line %d!\n", filename, lineno); 
00648    } else if (matches_keyword(data, "while")) {
00649       /* While... */
00650       fillin = NULL;
00651       args = data + strlen("while");
00652       while ((*args < 33) && (*args != '(')) args++;
00653       if ((*args == '(') && (c = argument_end(args))) {
00654          int whileblock;
00655          int whilestart;
00656          int whileend;
00657          char *whilelabel;
00658          args++;
00659          *c = '\0';
00660          c++;
00661          c = ast_skip_blanks(c);
00662          if (aeldebug & DEBUG_TOKENS)
00663             ast_verbose("--WHILE on : '%s' : '%s'\n", args, c);
00664          mlen = strlen(exten) + 128 + strlen(args) + strlen(name);
00665          margs = alloca(mlen);
00666          /* Remember where to put the conditional, and keep its position */
00667          whilestart = (*pos);
00668          whilelabel = *label;
00669          *label = NULL;
00670          (*pos)++;
00671          /* Remember where the whileblock starts */
00672          whileblock = (*pos);
00673          snprintf(margs, mlen, "while-%s-%d", name, whilestart);
00674          build_step("while", margs, filename, lineno, con, exten, pos, c, &fillin, label);
00675          /* Close the loop */
00676          app = "Goto";
00677          snprintf(margs, mlen, "%d", whilestart);
00678          if (ast_add_extension2(con, 0, exten, (*pos)++, *label, NULL, app, strdup(margs), FREE, registrar))
00679             ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
00680          *label = NULL;
00681          whileend = (*pos);
00682          /* Place trailer */
00683          app = "NoOp";
00684          snprintf(margs, mlen, "Finish while-%s-%d", name, whilestart);
00685          if (ast_add_extension2(con, 0, exten, (*pos)++, *label, NULL, app, strdup(margs), FREE, registrar))
00686             ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
00687          *label = NULL;
00688          app = "GotoIf";
00689          snprintf(margs, mlen, "$[ %s ]?%d:%d", args, whileblock, whileend);
00690          if (ast_add_extension2(con, 0, exten, whilestart, whilelabel, NULL, app, strdup(margs), FREE, registrar))
00691             ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
00692          fillin_process(con, fillin, filename, lineno, exten, whileend, exten, whilestart);
00693          fillin_free(fillin);
00694       } else
00695          ast_log(LOG_WARNING, "Syntax error in while declaration in %s around line %d!\n", filename, lineno); 
00696    } else if (matches_keyword(data, "jump")) {
00697       char *p;
00698       /* Jump... */
00699       fillin = NULL;
00700       args = data + strlen("jump");
00701       args = ast_skip_blanks(args);
00702       if (aeldebug & DEBUG_TOKENS)
00703          ast_verbose("--JUMP to : '%s'\n", args);
00704       p = strchr(args, ',');
00705       if (p) {
00706          *p = '\0';
00707          p++;
00708       } else
00709          p = "1";
00710       c = strchr(args, '@');
00711       if (c) {
00712          *c = '\0';
00713          c++;
00714       }
00715       mlen = strlen(exten) + 128 + strlen(args) + strlen(name) + (c ? strlen(c) : 0);
00716       margs = alloca(mlen);
00717       if (c) 
00718          snprintf(margs, mlen, "%s|%s|%s", c,args, p);
00719       else
00720          snprintf(margs, mlen, "%s|%s", args, p);
00721       app = "Goto";
00722       if (ast_add_extension2(con, 0, exten, (*pos)++, *label, NULL, app, strdup(margs), FREE, registrar))
00723          ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
00724       *label = NULL;
00725    } else if (matches_keyword(data, "goto")) {
00726       /* Jump... */
00727       fillin = NULL;
00728       args = data + strlen("goto");
00729       args = ast_skip_blanks(args);
00730       if (aeldebug & DEBUG_TOKENS)
00731          ast_verbose("--GOTO to : '%s'\n", args);
00732       app = "Goto";
00733       if (args[0] == '(' && args[strlen(args) - 1] == ')') {
00734          args[0] = '\0';
00735          args++;
00736          args[strlen(args) - 1] = '\0';
00737       }
00738       if (ast_add_extension2(con, 0, exten, (*pos)++, *label, NULL, app, strdup(args), FREE, registrar))
00739          ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
00740       *label = NULL;
00741    } else if (matches_keyword(data, "for")) {
00742       /* While... */
00743       fillin = NULL;
00744       args = data + strlen("for");
00745       while ((*args < 33) && (*args != '(')) args++;
00746       if ((*args == '(') && (c = argument_end(args))) {
00747          int forblock;
00748          int forprep;
00749          int forstart;
00750          int forend;
00751          struct stringlink *fields;
00752          char *tmp;
00753          char *forlabel = NULL;
00754          args++;
00755          *c = '\0';
00756          c++;
00757          c = ast_skip_blanks(c);
00758          /* Parse arguments first */
00759          tmp = alloca(strlen(args) + 10);
00760          if (tmp) {
00761             snprintf(tmp, strlen(args) + 10, "{%s;}", args);
00762             fields = arg_parse(tmp, filename, lineno);
00763          } else
00764             fields = NULL;
00765          if (fields && fields->next && fields->next->next) {
00766             if (aeldebug & DEBUG_TOKENS)
00767                ast_verbose("--FOR ('%s' ; '%s' ; '%s') : '%s'\n", fields->data, fields->next->data, fields->next->next->data, c);
00768             mlen = strlen(exten) + 128 + strlen(args) + strlen(name);
00769             margs = alloca(mlen);
00770             forprep = *pos;
00771             snprintf(margs, mlen, "for-%s-%d", name, forprep);
00772             fillin = NULL;
00773             build_step("while", margs, filename, lineno, con, exten, pos, fields->data, &fillin, label);
00774             /* Remember where to put the conditional, and keep its position */
00775             forstart = (*pos);
00776             forlabel = *label;
00777             (*pos)++;
00778             *label = NULL;
00779             /* Remember where the whileblock starts */
00780             forblock = (*pos);
00781             build_step("for", margs, filename, lineno, con, exten, pos, fields->next->next->data, &fillin, label);
00782             build_step("for", margs, filename, lineno, con, exten, pos, c, &fillin, label);
00783             /* Close the loop */
00784             app = "Goto";
00785             snprintf(margs, mlen, "%d", forstart);
00786             if (ast_add_extension2(con, 0, exten, (*pos)++, *label, NULL, app, strdup(margs), FREE, registrar))
00787                ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
00788             *label = NULL;
00789             forend = (*pos);
00790             /* Place trailer */
00791             app = "NoOp";
00792             snprintf(margs, mlen, "Finish for-%s-%d", name, forprep);
00793             if (ast_add_extension2(con, 0, exten, (*pos)++, *label, NULL, app, strdup(margs), FREE, registrar))
00794                ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
00795             *label = NULL;
00796             app = "GotoIf";
00797             snprintf(margs, mlen, "$[ %s ]?%d:%d", fields->next->data, forblock, forend);
00798             if (ast_add_extension2(con, 0, exten, forstart, forlabel, NULL, app, strdup(margs), FREE, registrar))
00799                ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", forstart, what, name);
00800             fillin_process(con, fillin, filename, lineno, exten, forend, exten, forstart);
00801             fillin_free(fillin);
00802          } else
00803             ast_log(LOG_NOTICE, "Improper for declaration in %s around line %d!\n", filename, lineno); 
00804          arg_free(fields);
00805       } else
00806          ast_log(LOG_WARNING, "Syntax error in for declaration in %s around line %d!\n", filename, lineno); 
00807          
00808    } else if (!strcasecmp(data, "break") || !strcasecmp(data, "continue")) {
00809       struct fillin *fi;
00810       fi = malloc(sizeof(struct fillin));
00811       if (fi) {
00812          memset(fi, 0, sizeof(struct fillin));
00813          if (!strcasecmp(data, "break"))
00814             fi->type = FILLIN_BREAK;
00815          else
00816             fi->type = FILLIN_CONTINUE;
00817          ast_copy_string(fi->exten, exten, sizeof(fi->exten));
00818          fi->priority = (*pos)++;
00819          fi->next = *fillout;
00820          *fillout = fi;
00821       }
00822    } else if (match_assignment(data, &rest)) {
00823       if (aeldebug & DEBUG_TOKENS)
00824          ast_verbose("ASSIGN  '%s' = '%s'\n", data, rest);
00825       mlen = strlen(rest) + strlen(data) + 20;
00826       margs = alloca(mlen);
00827       snprintf(margs, mlen, "%s=$[ %s ]", data, rest);
00828       app = "Set";
00829       if (ast_add_extension2(con, 0, exten, *pos, *label, NULL, app, strdup(margs), FREE, registrar))
00830          ast_log(LOG_WARNING, "Unable to add assignment at priority '%d' of %s '%s'\n", *pos, what, name);
00831       else {
00832          *label = NULL;
00833          (*pos)++;
00834       }
00835    } else {
00836       app = data;
00837       args = app;
00838       while (*args && (*args > 32) && (*args != '(')) args++;
00839          if (*args != '(') {
00840          while(*args && (*args != '(')) { *args = '\0'; args++; };
00841       }
00842       if (*args == '(') {
00843          *args = '\0';
00844          args++;
00845          /* Got arguments, trim trailing ')' */
00846          c = args + strlen(args) - 1;
00847          while((c >= args) && (*c < 33) && (*c != ')')) { *c = '\0'; c--; };
00848          if ((c >= args) && (*c == ')')) *c = '\0';
00849       } else
00850          args = "";
00851       ast_process_quotes_and_slashes(args, ',', '|');
00852       if (app[0] == '&') {
00853          app++;
00854          margs = alloca(strlen(args) + strlen(app) + 10);
00855          sprintf(margs, "%s|%s", app, args);
00856          args = margs;
00857          app = "Macro";
00858       }
00859       if (aeldebug & DEBUG_TOKENS)
00860          ast_verbose("-- APP: '%s', ARGS: '%s'\n", app, args);
00861       if (ast_add_extension2(con, 0, exten, *pos, *label, NULL, app, strdup(args), FREE, registrar))
00862          ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
00863       else {
00864          (*pos)++;
00865          *label = NULL;
00866       }
00867    }
00868    return 0;
00869 }
00870 
00871 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)
00872 {
00873    struct stringlink *args, *cur;
00874    int res=0;
00875    struct fillin *fillin=NULL;
00876    int dropfill = 0;
00877    char *labelin = NULL;
00878    if (!fillout) {
00879       fillout = &fillin;
00880       dropfill = 1;
00881    }
00882    if (!label) {
00883       label = &labelin;
00884    };
00885    args = arg_parse(data, filename, lineno);
00886    cur = args;
00887    while(cur) {
00888       res |= __build_step(what, name, filename, lineno, con, exten, pos, cur->data, fillout, label);
00889       cur = cur->next;
00890    }
00891    arg_free(args);
00892    if (dropfill) {
00893       fillin_process(con, fillin, filename, lineno, NULL, 0, NULL, 0);
00894       fillin_free(fillin);
00895    }
00896    return res;
00897 }
00898 
00899 static int parse_catch(char *data, char **catch, char **rest)
00900 {
00901    /* Skip the word 'catch' */
00902    data += 5;
00903    data = ast_skip_blanks(data);
00904    /* Here's the extension */
00905    *catch = data;
00906    if (!*data)
00907       return 0;
00908    while (*data && (*data > 32)) data++;
00909    if (!*data)
00910       return 0;
00911    /* Trim any trailing spaces */
00912    *data = '\0';
00913    data++;
00914    data = ast_skip_blanks(data);
00915    if (!*data)
00916       return 0;
00917    *rest = data;
00918    return 1;
00919 }
00920 
00921 static void handle_macro(struct ast_context **local_contexts, struct stringlink *vars, const char *filename, int lineno)
00922 {
00923    struct stringlink *argv;
00924    struct stringlink *paramv;
00925    struct stringlink *cur;
00926    struct ast_context *con;
00927    struct fillin *fillin;
00928    char *catch, *rest;
00929    char name[256];
00930    int pos;
00931    int cpos;
00932 
00933    if (aeldebug & DEBUG_MACROS)
00934       ast_verbose("Root macro def is '%s'\n", vars->data);
00935    argv = split_token(vars->data, filename, lineno);
00936    paramv = split_params(vars->data, filename, lineno);
00937    if (aeldebug & DEBUG_MACROS) 
00938       ast_verbose("Found macro '%s'\n", vars->data);
00939    snprintf(name, sizeof(name), "macro-%s", vars->data);
00940    con = ast_context_create(local_contexts, name, registrar);
00941    if (con) {
00942       pos = 1;
00943       cur = paramv;
00944       while(cur) {
00945          if (aeldebug & DEBUG_MACROS)
00946             ast_verbose("  PARAM => '%s'\n", cur->data);
00947          snprintf(name, sizeof(name), "%s=${ARG%d}", cur->data, pos);
00948          if (ast_add_extension2(con, 0, "s", pos, NULL, NULL, "Set", strdup(name), FREE, registrar))
00949             ast_log(LOG_WARNING, "Unable to add step at priority '%d' of macro '%s'\n", pos, vars->data);
00950          else
00951             pos++;
00952          cur = cur->next;
00953       }
00954       cur = argv;
00955       while(cur) {
00956          if (aeldebug & DEBUG_MACROS)
00957             ast_verbose("  STEP => '%s'\n", cur->data);
00958          if (matches_keyword(cur->data, "catch")) {
00959             if (aeldebug & DEBUG_MACROS)
00960                ast_verbose("--CATCH: '%s'\n", cur->data);
00961             if (parse_catch(cur->data, &catch, &rest)) {
00962                cpos = 1;
00963                build_step("catch", catch, filename, lineno, con, catch, &cpos, rest, NULL, NULL);
00964             } else
00965                ast_log(LOG_NOTICE, "Parse error for catch at about line %d of %s\n", lineno, filename);
00966          } else {
00967             fillin = NULL;
00968             build_step("macro", vars->data, filename, lineno, con, "s", &pos, cur->data, NULL, NULL);
00969          }
00970          cur = cur->next;
00971       }
00972    } else
00973       ast_log(LOG_WARNING, "Unable to create context '%s'\n", name);
00974    arg_free(paramv);
00975    arg_free(argv);
00976    if (vars->next)
00977       ast_log(LOG_NOTICE, "Ignoring excess tokens in macro definition around line %d of %s!\n", lineno, filename);
00978 }
00979 
00980 static int matches_extension(char *exten, char **extout)
00981 {
00982    char *c;
00983    *extout = NULL;
00984    c = exten;
00985    while(*c && (*c > 32)) c++;
00986    if (*c) {
00987       *c = '\0';
00988       c++;
00989       c = ast_skip_blanks(c);
00990       if (*c) {
00991          if (*c == '=') {
00992             *c = '\0';
00993             c++;
00994             if (*c == '>')
00995                c++;
00996             c = ast_skip_blanks(c);
00997             *extout = c;
00998             return 1;
00999          }
01000       }
01001    }
01002    return 0;
01003 }
01004 
01005 static void parse_keyword(char *s, char **o)
01006 {
01007    char *c;
01008    c = s;
01009    while((*c) && (*c > 32)) c++;
01010    if (*c) {
01011       *c = '\0';
01012       c++;
01013       c = ast_skip_blanks(c);
01014       *o = c;
01015    } else
01016       *o = NULL;
01017 }
01018 
01019 static void handle_context(struct ast_context **local_contexts, struct stringlink *vars, const char *filename, int lineno)
01020 {
01021    struct stringlink *argv;
01022    struct stringlink *cur2;
01023    struct stringlink *argv2;
01024    struct stringlink *cur;
01025    struct ast_context *con;
01026    char *rest;
01027    char *c;
01028    char name[256];
01029    int pos;
01030 
01031    if (aeldebug & DEBUG_CONTEXTS)
01032       ast_verbose("Root context def is '%s'\n", vars->data);
01033    argv = split_token(vars->data, filename, lineno);
01034    if (aeldebug & DEBUG_CONTEXTS) 
01035       ast_verbose("Found context '%s'\n", vars->data);
01036    snprintf(name, sizeof(name), "%s", vars->data);
01037    con = ast_context_create(local_contexts, name, registrar);
01038    if (con) {
01039       cur = argv;
01040       while(cur) {
01041          if (matches_keyword(cur->data, "includes")) {
01042             if (aeldebug & DEBUG_CONTEXTS)
01043                ast_verbose("--INCLUDES: '%s'\n", cur->data);
01044             parse_keyword(cur->data, &rest);
01045             if (rest) {
01046                argv2 = arg_parse(rest, filename, lineno);
01047                cur2 = argv2;
01048                while(cur2) {
01049                   ast_context_add_include2(con, cur2->data, registrar);
01050                   cur2 = cur2->next;
01051                }
01052                arg_free(argv2);
01053             }
01054          } else if (matches_keyword(cur->data, "ignorepat")) {
01055             if (aeldebug & DEBUG_CONTEXTS)
01056                ast_verbose("--IGNOREPAT: '%s'\n", cur->data);
01057             parse_keyword(cur->data, &rest);
01058             if (rest) {
01059                argv2 = arg_parse(rest, filename, lineno);
01060                cur2 = argv2;
01061                while(cur2) {
01062                   ast_context_add_ignorepat2(con, cur2->data, registrar);
01063                   cur2 = cur2->next;
01064                }
01065                arg_free(argv2);
01066             }
01067          } else if (matches_keyword(cur->data, "switches") || matches_keyword(cur->data, "eswitches")) {
01068             if (aeldebug & DEBUG_CONTEXTS)
01069                ast_verbose("--[E]SWITCH: '%s'\n", cur->data);
01070             parse_keyword(cur->data, &rest);
01071             if (rest) {
01072                argv2 = arg_parse(rest, filename, lineno);
01073                cur2 = argv2;
01074                while(cur2) {
01075                   c = strchr(cur2->data, '/');
01076                   if (c) {
01077                      *c = '\0';
01078                      c++;
01079                   } else
01080                      c = "";
01081                   ast_context_add_switch2(con, cur2->data, c, (cur->data[0] == 'e'), registrar);
01082                   cur2 = cur2->next;
01083                }
01084                arg_free(argv2);
01085             }
01086          } else if (matches_extension(cur->data, &rest)) {
01087             if (aeldebug & DEBUG_CONTEXTS)
01088                ast_verbose("Extension: '%s' => '%s'\n", cur->data, rest);
01089             pos = 1;
01090             build_step("extension", cur->data, filename, lineno, con, cur->data, &pos, rest, NULL, NULL);
01091          }
01092          cur = cur->next;
01093       }
01094    } else
01095          ast_log(LOG_WARNING, "Unable to create context '%s'\n", name);
01096    arg_free(argv);
01097    if (vars->next)
01098       ast_log(LOG_NOTICE, "Ignoring excess tokens in macro definition around line %d of %s!\n", lineno, filename);
01099 }
01100 
01101 static int handle_root_token(struct ast_context **local_contexts, char *token, int level, const char *filename, int lineno)
01102 {
01103    struct stringlink *argv, *cur;
01104    argv = split_token(token, filename, lineno);
01105    if (aeldebug & DEBUG_TOKENS) {
01106       ast_verbose("Found root token '%s' at level %d (%s:%d)!\n", token, level, filename, lineno);
01107       cur = argv;
01108       while(cur) {
01109          ast_verbose("   ARG => '%s'\n", cur->data);
01110          cur = cur->next;
01111       }
01112    }
01113    if (!strcasecmp(token, "globals")) {
01114       handle_globals(argv);
01115    } else if (!strcasecmp(token, "macro")) {
01116       handle_macro(local_contexts, argv, filename, lineno);
01117    } else if (!strcasecmp(token, "context")) {
01118       handle_context(local_contexts, argv, filename, lineno);
01119    } else {
01120       ast_log(LOG_NOTICE, "Unknown root token '%s'\n", token);
01121    }
01122    arg_free(argv);
01123    return 0;
01124 }
01125 
01126 
01127 static int ast_ael_compile(struct ast_context **local_contexts, const char *filename)
01128 {
01129    char *rfilename;
01130    char *buf, *tbuf;
01131    int bufsiz;
01132    FILE *f;
01133    char *c;
01134    char *token;
01135    int lineno=0;
01136 
01137    if (filename[0] == '/')
01138       rfilename = (char *)filename;
01139    else {
01140       rfilename = alloca(strlen(filename) + strlen(ast_config_AST_CONFIG_DIR) + 2);
01141       sprintf(rfilename, "%s/%s", ast_config_AST_CONFIG_DIR, filename);
01142    }
01143    
01144    f = fopen(rfilename, "r");
01145    if (!f) {
01146       ast_log(LOG_WARNING, "Unable to open '%s': %s\n", rfilename, strerror(errno));
01147       return -1;
01148    }
01149    buf = malloc(4096);
01150    if (!buf) {
01151       ast_log(LOG_WARNING, "Out of memory!\n");
01152       fclose(f);
01153       return -1;
01154    }
01155    buf[0] = 0;
01156    bufsiz = 4096;
01157    while(!feof(f)) {
01158       if (bufsiz - strlen(buf) < 2048) {
01159          bufsiz += 4096;
01160          tbuf = realloc(buf, bufsiz);
01161          if (tbuf) {
01162             buf = tbuf;
01163          } else {
01164             free(buf);
01165             ast_log(LOG_WARNING, "Out of memory!\n");
01166             fclose(f);
01167          }
01168       }
01169       if (fgets(buf + strlen(buf), bufsiz - strlen(buf), f)) {
01170          lineno++;
01171          while(*buf && buf[strlen(buf) - 1] < 33)
01172             buf[strlen(buf) - 1] = '\0';
01173          c = strstr(buf, "//");
01174          if (c)
01175             *c = '\0';
01176          if (*buf) {
01177             if (aeldebug & DEBUG_READ)
01178                ast_verbose("Newly composed line '%s'\n", buf);
01179             while((token = grab_token(buf, filename, lineno))) {
01180                handle_root_token(local_contexts, token, 0, filename, lineno);
01181                free(token);
01182             }
01183          }
01184       }
01185    };
01186    free(buf);
01187    fclose(f);
01188    return 0;
01189 }
01190 
01191 static int pbx_load_module(void)
01192 {
01193    struct ast_context *local_contexts=NULL, *con;
01194    ast_ael_compile(&local_contexts, config);
01195    ast_merge_contexts_and_delete(&local_contexts, registrar);
01196    for (con = ast_walk_contexts(NULL); con; con = ast_walk_contexts(con))
01197       ast_context_verify_includes(con);
01198 
01199    return 0;
01200 }
01201 
01202 /* CLI interface */
01203 static int ael_debug_read(int fd, int argc, char *argv[])
01204 {
01205    aeldebug |= DEBUG_READ;
01206    return 0;
01207 }
01208 
01209 static int ael_debug_tokens(int fd, int argc, char *argv[])
01210 {
01211    aeldebug |= DEBUG_TOKENS;
01212    return 0;
01213 }
01214 
01215 static int ael_debug_macros(int fd, int argc, char *argv[])
01216 {
01217    aeldebug |= DEBUG_MACROS;
01218    return 0;
01219 }
01220 
01221 static int ael_debug_contexts(int fd, int argc, char *argv[])
01222 {
01223    aeldebug |= DEBUG_CONTEXTS;
01224    return 0;
01225 }
01226 
01227 static int ael_no_debug(int fd, int argc, char *argv[])
01228 {
01229    aeldebug = 0;
01230    return 0;
01231 }
01232 
01233 static int ael_reload(int fd, int argc, char *argv[])
01234 {
01235    ast_context_destroy(NULL, registrar);
01236    return (pbx_load_module());
01237 }
01238 
01239 static struct ast_cli_entry  ael_cli[] = {
01240    { { "ael", "reload", NULL }, ael_reload, "Reload AEL configuration"},
01241    { { "ael", "debug", "read", NULL }, ael_debug_read, "Enable AEL read debug"},
01242    { { "ael", "debug", "tokens", NULL }, ael_debug_tokens, "Enable AEL tokens debug"},
01243    { { "ael", "debug", "macros", NULL }, ael_debug_macros, "Enable AEL macros debug"},
01244    { { "ael", "debug", "contexts", NULL }, ael_debug_contexts, "Enable AEL contexts debug"},
01245    { { "ael", "no", "debug", NULL }, ael_no_debug, "Disable AEL debug messages"},
01246 };
01247 
01248 /*
01249  * Standard module functions ...
01250  */
01251 int unload_module(void)
01252 {
01253    ast_context_destroy(NULL, registrar);
01254    ast_cli_unregister_multiple(ael_cli, sizeof(ael_cli)/ sizeof(ael_cli[0]));
01255    return 0;
01256 }
01257 
01258 
01259 int load_module(void)
01260 {
01261    ast_cli_register_multiple(ael_cli, sizeof(ael_cli)/ sizeof(ael_cli[0]));
01262    return (pbx_load_module());
01263 }
01264 
01265 int reload(void)
01266 {
01267    ast_context_destroy(NULL, registrar);
01268    return pbx_load_module();
01269 }
01270 
01271 int usecount(void)
01272 {
01273    return 0;
01274 }
01275 
01276 char *description(void)
01277 {
01278    return dtext;
01279 }
01280 
01281 char *key(void)
01282 {
01283    return ASTERISK_GPL_KEY;
01284 }

Generated on Mon Mar 20 08:20:13 2006 for Asterisk - the Open Source PBX by  doxygen 1.3.9.1