diff options
author | markster <markster@f38db490-d61c-443f-a65b-d21fe96a405b> | 2005-06-16 08:47:06 +0000 |
---|---|---|
committer | markster <markster@f38db490-d61c-443f-a65b-d21fe96a405b> | 2005-06-16 08:47:06 +0000 |
commit | 20cc8387a96393f0583cae71150c6fb3146a0f9c (patch) | |
tree | 1f680371a147be0a27779ccbad3d383df7c98226 /pbx/pbx_ael.c | |
parent | 09f5c8d4e6500cf8e46b600a3db64fb110086f46 (diff) |
Add Asterisk Extension Language support (AEL) from Astricon talk
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@5918 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'pbx/pbx_ael.c')
-rwxr-xr-x | pbx/pbx_ael.c | 1376 |
1 files changed, 1376 insertions, 0 deletions
diff --git a/pbx/pbx_ael.c b/pbx/pbx_ael.c new file mode 100755 index 000000000..f688e214b --- /dev/null +++ b/pbx/pbx_ael.c @@ -0,0 +1,1376 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * Compile symbolic Asterisk Extension Logic into Asterisk extensions + * + * Copyright (C) 2005, Digium, Inc. + * + * Mark Spencer <markster@digium.com> + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +#include <sys/types.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +/* For where to put dynamic tables */ +#include "asterisk.h" +#include "asterisk/pbx.h" +#include "asterisk/config.h" +#include "asterisk/module.h" +#include "asterisk/logger.h" +#include "asterisk/cli.h" +#include "asterisk/callerid.h" + +struct stringlink { + struct stringlink *next; + unsigned char data[0]; +}; + +#define FILLIN_BREAK 1 +#define FILLIN_CONTINUE 2 + +struct fillin { + struct fillin *next; + char exten[AST_MAX_EXTENSION]; + int priority; + int type; +}; + +#ifdef __AST_DEBUG_MALLOC +static void FREE(void *ptr) +{ + free(ptr); +} +#else +#define FREE free +#endif + +#define DEBUG_READ (1 << 0) +#define DEBUG_TOKENS (1 << 1) +#define DEBUG_MACROS (1 << 2) +#define DEBUG_CONTEXTS (1 << 3) + +static int aeldebug = 0; + +static char *dtext = "Asterisk Extension Logic Compiler"; +static char *config = "extensions.ael"; +static char *registrar = "pbx_ael"; + +/* + * Static code + */ +static char *process_quotes_and_slashes(char *start, char find, char replace_with) +{ + char *dataPut = start; + int inEscape = 0; + int inQuotes = 0; + + for (; *start; start++) { + if (inEscape) { + *dataPut++ = *start; /* Always goes verbatim */ + inEscape = 0; + } else { + if (*start == '\\') { + inEscape = 1; /* Do not copy \ into the data */ + } else if (*start == '\'') { + inQuotes = 1-inQuotes; /* Do not copy ' into the data */ + } else { + /* Replace , with |, unless in quotes */ + *dataPut++ = inQuotes ? *start : ((*start==find) ? replace_with : *start); + } + } + } + if (start != dataPut) + *dataPut = 0; + return dataPut; +} + +/* + * Standard module functions ... + */ +int unload_module(void) +{ + ast_context_destroy(NULL, registrar); + return 0; +} + +static char *__grab_token(char *src, const char *filename, int lineno, int link) +{ + char *c; + char *b; + char *a; + int level = 0; + char *ret; +#if 0 + if (aeldebug || DEBUG_TOKENS) + ast_verbose("Searching for token in '%s'!\n", src); +#endif + c = src; + while(*c) { + if ((*c == '\\')) { + c++; + if (!*c) + c--; + } else { + if ((*c == '{') || (*c == '(')) { + level++; + } else if ((*c == '}') || (*c == ')')) { + if (level) + level--; + else + ast_log(LOG_WARNING, "Syntax error at line %d of '%s', too many closing braces!\n", lineno, filename); + } else if ((*c == ';') && !level) { + /* Got a token! */ + *c = '\0'; + b = c; + b--; + c++; + while((b > src) && (*b < 33)) { + *b = '\0'; + b--; + } + a = src; + while(*a && (*a < 33)) + a++; + if (link) { + ret = malloc(strlen(a) + sizeof(struct stringlink) + 1); + if (ret) + strcpy(ret + sizeof(struct stringlink), a); + } else + ret = strdup(a); + /* Save remainder */ + memmove(src, c, strlen(c) + 1); + return ret; + } + } + c++; + } + return NULL; +} + +static char *grab_token(char *src, const char *filename, int lineno) +{ + return __grab_token(src, filename, lineno, 0); +} + +static struct stringlink *arg_parse(char *args, const char *filename, int lineno) +{ + struct stringlink *cur, *prev=NULL, *root=NULL; + if (args) { + if (aeldebug & DEBUG_TOKENS) + ast_verbose("Parsing args '%s'!\n", args); + if (args[0] == '{') { + /* Strip mandatory '}' from end */ + args[strlen(args) - 1] = '\0'; + while ((cur = (struct stringlink *)__grab_token(args + 1, filename, lineno, 1))) { + cur->next = NULL; + if (prev) + prev->next = cur; + else + root = cur; + prev = cur; + } + } else if (*args) { + root = malloc(sizeof(struct stringlink) + strlen(args) + 1); + if (root) { + strcpy(root->data, args); + root->next = NULL; + } + } + } + return root; +} + +static char *grab_else(char *args, const char *filename, int lineno) +{ + char *ret = NULL; + int level=0; + char *c; + if (args) { + if (args[0] == '{') { + c = args; + while(*c) { + if (*c == '{') + level++; + else if (*c == '}') { + level--; + if (!level) { + c++; + while(*c && (*c < 33)) { *c = '\0'; c++; }; + if (!strncasecmp(c, "else", 4) && + ((c[4] == '{') || (c[4] < 33))) { + /* Ladies and gentlemen, we have an else clause */ + *c = '\0'; + c += 4; + while(*c && (*c < 33)) c++; + ret = c; + if (aeldebug & DEBUG_TOKENS) + ast_verbose("Returning else clause '%s'\n", c); + } + } + break; + } + c++; + } + } + } + return ret; +} + +static struct stringlink *param_parse(char *parms, const char *macro, const char *filename, int lineno) +{ + char *s, *e; + struct stringlink *root = NULL, *prev=NULL, *cur; + if (!parms || !*parms) + return NULL; + if (*parms != '(') { + 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); + return NULL; + } + s = parms + 1; + while(*s) { + while(*s && (*s < 33)) s++; + e = s; + while(*e && (*e != ')') && (*e != ',')) { + if (*e < 33) + *e = '\0'; + e++; + } + if (*e) { + /* Strip token */ + *e = '\0'; + e++; + /* Skip over whitespace */ + while(*e && (*e < 33)) e++; + /* Link */ + cur = malloc(strlen(s) + sizeof(struct stringlink) + 1); + if (cur) { + cur->next = NULL; + strcpy(cur->data, s); + if (prev) + prev->next = cur; + else + root = cur; + prev = cur; + } + s = e; + } + } + return root; +} + +static void arg_free(struct stringlink *cur) +{ + struct stringlink *last; + while(cur) { + last = cur; + cur = cur->next; + free(last); + } +} + +static void handle_globals(struct stringlink *vars) +{ + while(vars) { + pbx_builtin_setvar(NULL, vars->data); + vars = vars->next; + } +} + +static struct stringlink *split_token(char *token, const char *filename, int lineno) +{ + char *args, *p; + struct stringlink *argv; + args = token; + while (*args && (*args > 32) && (*args != '{') && (*args != '(')) args++; + if (*args) { + p = args; + while (*args && (*args < 33)) + args++; + if (*args != '(') { + *p = '\0'; + } else { + while (*args && (*args != ')')) args++; + if (*args == ')') { + args++; + while (*args && (*args < 33)) args++; + } + } + if (!*args) + args = NULL; + } else args = NULL; + argv = arg_parse(args, filename, lineno); + if (args) + *args = '\0'; + return argv; +} + +static int matches_keyword(const char *data, const char *keyword) +{ + char c; + if (!strncasecmp(data, keyword, strlen(keyword))) { + c = data[strlen(keyword)]; + if ((c < 33) || (c == '(') || (c == '{')) + return 1; + } + return 0; +} + +static struct stringlink *split_params(char *token, const char *filename, int lineno) +{ + char *params; + struct stringlink *paramv; + params = token; + while(*params && (*params > 32) && (*params != '(')) params++; + if (*params) { + if (*params != '(') { + *params = '\0'; + params++; + while(*params && (*params < 33)) + params++; + } + if (!*params) + params = NULL; + } else params = NULL; + paramv = param_parse(params, token, filename, lineno); + if (params) + *params = '\0'; + return paramv; +} + +static const char *get_case(char *s, char **restout, int *pattern) +{ + char *newcase=NULL; + char *rest=NULL; + if (!strncasecmp(s, "case", 4) && s[4] && ((s[4] < 33) || (s[4] == ':'))) { + newcase = s + 4; + while (*newcase && (*newcase < 33)) newcase++; + rest = newcase; + *pattern = 0; + } else if (!strncasecmp(s, "pattern", 7) && s[7] && ((s[7] < 33) || (s[7] == ':'))) { + newcase = s + 8; + while (*newcase && (*newcase < 33)) newcase++; + rest = newcase; + *pattern = 1; + } else if (!strncasecmp(s, "default", 7) && ((s[7] < 33) || (s[7] == ':'))) { + newcase = "."; + rest = s + 7; + while (*rest && (*rest < 33)) rest++; + *pattern = 1; + } + + if (rest) { + while (*rest && (*rest > 32) && (*rest != ':')) rest++; + if (*rest) { + *rest = 0; + rest++; + while (*rest && ((*rest == ':') || (*rest < 33))) rest++; + *restout = rest; + } else { + *restout = ""; + } + } else + *restout = s; + if (aeldebug & DEBUG_TOKENS) + ast_verbose("GETCASE: newcase is '%s', rest = '%s'\n", newcase, *restout); + return newcase; +} + +static void fillin_free(struct fillin *fillin) +{ + struct fillin *cur, *next; + cur = fillin; + while(cur) { + next = cur->next; + free(cur); + cur = next; + } +} + +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) +{ + struct fillin *cur; + char *app; + char mdata[AST_MAX_EXTENSION + 20]; + cur = fillin; + while(cur) { + if (cur->type == FILLIN_BREAK) { + if (breakexten && breakprio) { + app = "Goto"; + snprintf(mdata, sizeof(mdata), "%s|%d", breakexten, breakprio); + } else { + app = "NoOp"; + snprintf(mdata, sizeof(mdata), "Invalid break"); + ast_log(LOG_NOTICE, "Ignoring inappropriate break around line %d of %s\n", lineno, filename); + } + if (ast_add_extension2(con, 0, cur->exten, cur->priority, NULL, NULL, app, strdup(mdata), FREE, registrar)) + ast_log(LOG_WARNING, "Unable to add step at priority '%d' of break '%s'\n", cur->priority, cur->exten); + } else if (cur->type == FILLIN_CONTINUE) { + if (contexten && contprio) { + app = "Goto"; + snprintf(mdata, sizeof(mdata), "%s|%d", contexten, contprio); + } else { + app = "NoOp"; + snprintf(mdata, sizeof(mdata), "Invalid continue"); + ast_log(LOG_NOTICE, "Ignoring inappropriate continue around line %d of %s\n", lineno, filename); + } + if (ast_add_extension2(con, 0, cur->exten, cur->priority, NULL, NULL, app, strdup(mdata), FREE, registrar)) + ast_log(LOG_WARNING, "Unable to add step at priority '%d' of continue '%s'\n", cur->priority, cur->exten); + } else { + ast_log(LOG_WARNING, "Whoa, unknown fillin type '%d'\n", cur->type); + } + cur = cur->next; + } +} + +static int match_assignment(char *variable, char **value) +{ + char *c; + char *ws; + c = variable; + while(*c && (*c > 32) && (*c != '=')) c++; + ws = c; + while(*c && (*c < 33)) c++; + if (*c == '=') { + *ws = '\0'; + *c = '\0'; + c++; + while ((*c) && (*c < 33)) c++; + *value = c; + return 1; + } + return 0; +} + +static int matches_label(char *data, char **rest) +{ + char last = 0; + char *start = data; + while (*data > 32) { + last = *data; + data++; + } + if (last != ':') { + while (*data && (*data < 33)) data++; + last = *data; + data++; + } + if (last == ':') { + *rest = data; + /* Go back and trim up the label */ + while(*start && ((*start > 32) && (*start != ':'))) start++; + *start = '\0'; + return 1; + } + return 0; +} + +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); +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) +{ + char *app; + char *args; + char *c; + char *margs=NULL; + char *oargs; + char *rest; + const char *curcase, *newcase; + struct stringlink *swargs, *cur; + int cpos; + int mlen; + int pattern; + struct fillin *fillin; + while (*data && (*data < 33)) data++; + if (matches_label(data, &c)) { + *label = data; + data = c; + while (*data && (*data < 33)) data++; + } + if (!data || ast_strlen_zero(data)) + return 0; + if (matches_keyword(data, "switch")) { + fillin = NULL; + /* Switch */ + args = data + strlen("switch"); + while ((*args < 33) && (*args != '(')) args++; + if ((*args == '(') && (c = strchr(args, ')'))) { + args++; + *c = '\0'; + c++; + if (aeldebug & DEBUG_TOKENS) + ast_verbose("--SWITCH on : %s\n", args); + mlen = strlen(exten) + 128 + strlen(args) + strlen(name); + margs = alloca(mlen); + app = "Goto"; + sprintf(margs, "sw-%s-%d-%s|1", name, *pos, args); + process_quotes_and_slashes(margs, ',', '|'); + oargs = args; + args = margs; + if (ast_add_extension2(con, 0, exten, *pos, *label, NULL, app, strdup(args), FREE, registrar)) + ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name); + else { + *label = NULL; + (*pos)++; + } + app = "NoOp"; + sprintf(margs, "Finish switch-%s-%d", name, *pos - 1); + if (ast_add_extension2(con, 0, exten, *pos, *label, NULL, app, strdup(args), FREE, registrar)) + ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name); + else { + *label = NULL; + (*pos)++; + } + while(*c && (*c < 33)) c++; + if (aeldebug & DEBUG_TOKENS) + ast_verbose("ARG Parsing '%s'\n", c); + swargs = arg_parse(c, filename, lineno); + cur = swargs; + curcase = NULL; + while(cur) { + if ((newcase = get_case(cur->data, &rest, &pattern))) { + if (aeldebug & DEBUG_TOKENS) + ast_verbose("--NEWCASE: '%s'!\n", newcase); + if (curcase) { + /* Handle fall through */ + char tmp[strlen(newcase) + strlen(name) + 40]; + sprintf(tmp, "sw-%s-%d-%s|%d", name, *pos - 2, newcase, 1); + ast_add_extension2(con, 0, margs, cpos, NULL, NULL, "Goto", strdup(tmp), FREE, registrar); + } + curcase = newcase; + cpos = 1; + if (pattern) + snprintf(margs, mlen, "_sw-%s-%d-%s", name, *pos - 2, curcase); + else + snprintf(margs, mlen, "sw-%s-%d-%s", name, *pos - 2, curcase); + if (!strcasecmp(rest, "break")) { + char tmp[strlen(exten) + 10]; + sprintf(tmp, "%s|%d", exten, *pos - 1); + ast_add_extension2(con, 0, exten, cpos, *label, NULL, "Goto", strdup(tmp), FREE, registrar); + curcase = NULL; + *label = NULL; + } else + build_step("switch", margs, filename, lineno, con, margs, &cpos, rest, &fillin, label); + } else if (curcase) { + if (aeldebug & DEBUG_TOKENS) + ast_verbose("Building statement from '%s'\n", rest); + if (!strcasecmp(rest, "break")) { + char tmp[strlen(exten) + 10]; + sprintf(tmp, "%s|%d", exten, *pos - 1); + ast_add_extension2(con, 0, margs, cpos, *label, NULL, "Goto", strdup(tmp), FREE, registrar); + curcase = NULL; + *label = NULL; + } else + build_step("switch", margs, filename, lineno, con, margs, &cpos, rest, &fillin, label); + } else + ast_log(LOG_WARNING, "Unreachable code in switch at about line %d of %s\n", lineno, filename); + if (aeldebug & DEBUG_TOKENS) + ast_verbose("--SWARG: %s\n", cur->data); + cur = cur->next; + } + /* Can't do anything with these */ + fillin_process(con, fillin, filename, lineno, NULL, 0, NULL, 0); + fillin_free(fillin); + arg_free(swargs); + } else + ast_log(LOG_WARNING, "Syntax error in switch declaration in %s around line %d!\n", filename, lineno); + + } else if (matches_keyword(data, "if")) { + /* If... */ + args = data + strlen("if"); + while ((*args < 33) && (*args != '(')) args++; + if ((*args == '(') && (c = strchr(args, ')'))) { + int ifblock; + int ifstart; + int elsestart; + int ifend; + int ifskip; + char *elses; + char *iflabel; + args++; + *c = '\0'; + c++; + while(*c && (*c < 33)) c++; + if (aeldebug & DEBUG_TOKENS) + ast_verbose("--IF on : '%s' : '%s'\n", args, c); + mlen = strlen(exten) + 128 + strlen(args) + strlen(name); + margs = alloca(mlen); + /* Remember where the ifblock starts, and skip over */ + ifblock = (*pos)++; + iflabel = *label; + *label = NULL; + /* Remember where the start of the ifblock is */ + ifstart = *pos; + snprintf(margs, mlen, "if-%s-%d", name, ifblock); + /* Now process the block of the if */ + if (aeldebug & DEBUG_TOKENS) + ast_verbose("Searching for elses in '%s'\n", c); + elses = grab_else(c, filename, lineno); + build_step("if", margs, filename, lineno, con, exten, pos, c, fillout, label); + if (elses) { + /* Reserve a goto to exit the if */ + ifskip = *pos; + (*pos)++; + elsestart = *pos; + build_step("else", margs, filename, lineno, con, exten, pos, elses, fillout, label); + } else { + elsestart = *pos; + ifskip = 0; + } + ifend = *pos; + (*pos)++; + app = "NoOp"; + snprintf(margs, mlen, "Finish if-%s-%d", name, ifblock); + if (ast_add_extension2(con, 0, exten, ifend, *label, NULL, app, strdup(margs), FREE, registrar)) + ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name); + *label = NULL; + app = "Goto"; + snprintf(margs, mlen, "${IF($[ %s ]?%d:%d)}", args, ifstart, elsestart); + if (ast_add_extension2(con, 0, exten, ifblock, iflabel, NULL, app, strdup(margs), FREE, registrar)) + ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name); + if (ifskip) { + /* Skip as appropriate around else clause */ + snprintf(margs, mlen, "%d", ifend); + if (ast_add_extension2(con, 0, exten, ifskip, NULL, NULL, app, strdup(margs), FREE, registrar)) + ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name); + } + } else + ast_log(LOG_WARNING, "Syntax error in if declaration in %s around line %d!\n", filename, lineno); + } else if (matches_keyword(data, "while")) { + /* While... */ + fillin = NULL; + args = data + strlen("while"); + while ((*args < 33) && (*args != '(')) args++; + if ((*args == '(') && (c = strchr(args, ')'))) { + int whileblock; + int whilestart; + int whileend; + char *whilelabel; + args++; + *c = '\0'; + c++; + while(*c && (*c < 33)) c++; + if (aeldebug & DEBUG_TOKENS) + ast_verbose("--WHILE on : '%s' : '%s'\n", args, c); + mlen = strlen(exten) + 128 + strlen(args) + strlen(name); + margs = alloca(mlen); + /* Remember where to put the conditional, and keep its position */ + whilestart = (*pos); + whilelabel = *label; + *label = NULL; + (*pos)++; + /* Remember where the whileblock starts */ + whileblock = (*pos); + snprintf(margs, mlen, "while-%s-%d", name, whilestart); + build_step("while", margs, filename, lineno, con, exten, pos, c, &fillin, label); + /* Close the loop */ + app = "Goto"; + snprintf(margs, mlen, "%d", whilestart); + if (ast_add_extension2(con, 0, exten, (*pos)++, *label, NULL, app, strdup(margs), FREE, registrar)) + ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name); + *label = NULL; + whileend = (*pos); + /* Place trailer */ + app = "NoOp"; + snprintf(margs, mlen, "Finish while-%s-%d", name, whilestart); + if (ast_add_extension2(con, 0, exten, (*pos)++, *label, NULL, app, strdup(margs), FREE, registrar)) + ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name); + *label = NULL; + app = "Goto"; + snprintf(margs, mlen, "${IF($[ %s ]?%d:%d)}", args, whileblock, whileend); + if (ast_add_extension2(con, 0, exten, whilestart, whilelabel, NULL, app, strdup(margs), FREE, registrar)) + ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name); + fillin_process(con, fillin, filename, lineno, exten, whileend, exten, whilestart); + fillin_free(fillin); + } else + ast_log(LOG_WARNING, "Syntax error in while declaration in %s around line %d!\n", filename, lineno); + } else if (matches_keyword(data, "jump")) { + char *p; + /* Jump... */ + fillin = NULL; + args = data + strlen("jump"); + while(*args && (*args < 33)) args++; + if (aeldebug & DEBUG_TOKENS) + ast_verbose("--JUMP to : '%s'\n", args); + mlen = strlen(exten) + 128 + strlen(args) + strlen(name) + (c ? strlen(c) : 0); + margs = alloca(mlen); + p = strchr(args, ','); + if (p) { + *p = '\0'; + p++; + } else + p = "1"; + c = strchr(args, '@'); + if (c) { + *c = '\0'; + c++; + } + if (c) + snprintf(margs, mlen, "%s|%s|%s", c,args, p); + else + snprintf(margs, mlen, "%s|%s", args, p); + app = "Goto"; + if (ast_add_extension2(con, 0, exten, (*pos)++, *label, NULL, app, strdup(margs), FREE, registrar)) + ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name); + *label = NULL; + } else if (matches_keyword(data, "goto")) { + /* Jump... */ + fillin = NULL; + args = data + strlen("goto"); + while(*args && (*args < 33)) args++; + if (aeldebug & DEBUG_TOKENS) + ast_verbose("--GOTO to : '%s'\n", args); + app = "Goto"; + if (ast_add_extension2(con, 0, exten, (*pos)++, *label, NULL, app, strdup(args), FREE, registrar)) + ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name); + *label = NULL; + } else if (matches_keyword(data, "for")) { + /* While... */ + fillin = NULL; + args = data + strlen("for"); + while ((*args < 33) && (*args != '(')) args++; + if ((*args == '(') && (c = strchr(args, ')'))) { + int forblock; + int forprep; + int forstart; + int forend; + struct stringlink *fields; + char *tmp; + char *forlabel = NULL; + args++; + *c = '\0'; + c++; + while(*c && (*c < 33)) c++; + /* Parse arguments first */ + tmp = alloca(strlen(args) + 10); + if (tmp) { + snprintf(tmp, strlen(args) + 10, "{%s;}", args); + fields = arg_parse(tmp, filename, lineno); + } else + fields = NULL; + if (fields && fields->next && fields->next->next) { + if (aeldebug & DEBUG_TOKENS) + ast_verbose("--FOR ('%s' ; '%s' ; '%s') : '%s'\n", fields->data, fields->next->data, fields->next->next->data, c); + mlen = strlen(exten) + 128 + strlen(args) + strlen(name); + margs = alloca(mlen); + forprep = *pos; + snprintf(margs, mlen, "for-%s-%d", name, forprep); + fillin = NULL; + build_step("while", margs, filename, lineno, con, exten, pos, fields->data, &fillin, label); + /* Remember where to put the conditional, and keep its position */ + forstart = (*pos); + forlabel = *label; + (*pos)++; + *label = NULL; + /* Remember where the whileblock starts */ + forblock = (*pos); + build_step("for", margs, filename, lineno, con, exten, pos, fields->next->next->data, &fillin, label); + build_step("for", margs, filename, lineno, con, exten, pos, c, &fillin, label); + /* Close the loop */ + app = "Goto"; + snprintf(margs, mlen, "%d", forstart); + if (ast_add_extension2(con, 0, exten, (*pos)++, *label, NULL, app, strdup(margs), FREE, registrar)) + ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name); + *label = NULL; + forend = (*pos); + /* Place trailer */ + app = "NoOp"; + snprintf(margs, mlen, "Finish for-%s-%d", name, forprep); + if (ast_add_extension2(con, 0, exten, (*pos)++, *label, NULL, app, strdup(margs), FREE, registrar)) + ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name); + *label = NULL; + app = "Goto"; + snprintf(margs, mlen, "${IF($[ %s ]?%d:%d)}", fields->next->data, forblock, forend); + if (ast_add_extension2(con, 0, exten, forstart, forlabel, NULL, app, strdup(margs), FREE, registrar)) + ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", forstart, what, name); + fillin_process(con, fillin, filename, lineno, exten, forend, exten, forstart); + fillin_free(fillin); + } else + ast_log(LOG_NOTICE, "Improper for declaration in %s around line %d!\n", filename, lineno); + arg_free(fields); + } else + ast_log(LOG_WARNING, "Syntax error in for declaration in %s around line %d!\n", filename, lineno); + + } else if (!strcasecmp(data, "break") || !strcasecmp(data, "continue")) { + struct fillin *fi; + fi = malloc(sizeof(struct fillin)); + if (fi) { + memset(fi, 0, sizeof(struct fillin)); + if (!strcasecmp(data, "break")) + fi->type = FILLIN_BREAK; + else + fi->type = FILLIN_CONTINUE; + ast_copy_string(fi->exten, exten, sizeof(fi->exten)); + fi->priority = (*pos)++; + fi->next = *fillout; + *fillout = fi; + } + } else if (match_assignment(data, &rest)) { + if (aeldebug & DEBUG_TOKENS) + ast_verbose("ASSIGN '%s' = '%s'\n", data, rest); + mlen = strlen(rest) + strlen(data) + 20; + margs = alloca(mlen); + snprintf(margs, mlen, "%s=$[ %s ]", data, rest); + app = "Set"; + if (ast_add_extension2(con, 0, exten, *pos, *label, NULL, app, strdup(margs), FREE, registrar)) + ast_log(LOG_WARNING, "Unable to add assignment at priority '%d' of %s '%s'\n", *pos, what, name); + else { + *label = NULL; + (*pos)++; + } + } else { + app = data; + args = app; + while (*args && (*args > 32) && (*args != '(')) args++; + if (*args != '(') { + while(*args && (*args != '(')) { *args = '\0'; args++; }; + } + if (*args == '(') { + *args = '\0'; + args++; + /* Got arguments, trim trailing ')' */ + c = args + strlen(args) - 1; + while((c >= args) && (*c < 33) && (*c != ')')) { *c = '\0'; c--; }; + if ((c >= args) && (*c == ')')) *c = '\0'; + } else + args = ""; + process_quotes_and_slashes(args, ',', '|'); + if (app[0] == '&') { + app++; + margs = alloca(strlen(args) + strlen(app) + 10); + sprintf(margs, "%s|%s", app, args); + args = margs; + app = "Macro"; + } + if (aeldebug & DEBUG_TOKENS) + ast_verbose("-- APP: '%s', ARGS: '%s'\n", app, args); + if (ast_add_extension2(con, 0, exten, *pos, *label, NULL, app, strdup(args), FREE, registrar)) + ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name); + else { + (*pos)++; + *label = NULL; + } + } + return 0; +} + +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) +{ + struct stringlink *args, *cur; + int res=0; + struct fillin *fillin=NULL; + int dropfill = 0; + char *labelin = NULL; + if (!fillout) { + fillout = &fillin; + dropfill = 1; + } + if (!label) { + label = &labelin; + }; + args = arg_parse(data, filename, lineno); + cur = args; + while(cur) { + res |= __build_step(what, name, filename, lineno, con, exten, pos, cur->data, fillout, label); + cur = cur->next; + } + arg_free(args); + if (dropfill) { + fillin_process(con, fillin, filename, lineno, NULL, 0, NULL, 0); + fillin_free(fillin); + } + return res; +} + +static int parse_catch(char *data, char **catch, char **rest) +{ + /* Skip the word 'catch' */ + data += 5; + while (*data && (*data < 33)) data++; + /* Here's the extension */ + *catch = data; + if (!*data) + return 0; + while (*data && (*data > 32)) data++; + if (!*data) + return 0; + /* Trim any trailing spaces */ + *data = '\0'; + data++; + while(*data && (*data < 33)) data++; + if (!*data) + return 0; + *rest = data; + return 1; +} + +static void handle_macro(struct ast_context **local_contexts, struct stringlink *vars, const char *filename, int lineno) +{ + struct stringlink *argv; + struct stringlink *paramv; + struct stringlink *cur; + struct ast_context *con; + struct fillin *fillin; + char *catch, *rest; + char name[256]; + int pos; + int cpos; + + if (aeldebug & DEBUG_MACROS) + ast_verbose("Root macro def is '%s'\n", vars->data); + argv = split_token(vars->data, filename, lineno); + paramv = split_params(vars->data, filename, lineno); + if (aeldebug & DEBUG_MACROS) + ast_verbose("Found macro '%s'\n", vars->data); + snprintf(name, sizeof(name), "macro-%s", vars->data); + con = ast_context_create(local_contexts, name, registrar); + if (con) { + pos = 1; + cur = paramv; + while(cur) { + if (aeldebug & DEBUG_MACROS) + ast_verbose(" PARAM => '%s'\n", cur->data); + snprintf(name, sizeof(name), "%s=${ARG%d}", cur->data, pos); + if (ast_add_extension2(con, 0, "s", pos, NULL, NULL, "Set", strdup(name), FREE, registrar)) + ast_log(LOG_WARNING, "Unable to add step at priority '%d' of macro '%s'\n", pos, vars->data); + else + pos++; + cur = cur->next; + } + cur = argv; + while(cur) { + if (aeldebug & DEBUG_MACROS) + ast_verbose(" STEP => '%s'\n", cur->data); + if (matches_keyword(cur->data, "catch")) { + if (aeldebug & DEBUG_MACROS) + ast_verbose("--CATCH: '%s'\n", cur->data); + if (parse_catch(cur->data, &catch, &rest)) { + cpos = 1; + build_step("catch", catch, filename, lineno, con, catch, &cpos, rest, NULL, NULL); + } else + ast_log(LOG_NOTICE, "Parse error for catch at about line %d of %s\n", lineno, filename); + } else { + fillin = NULL; + build_step("macro", vars->data, filename, lineno, con, "s", &pos, cur->data, NULL, NULL); + } + cur = cur->next; + } + } else + ast_log(LOG_WARNING, "Unable to create context '%s'\n", name); + arg_free(argv); + if (vars->next) + ast_log(LOG_NOTICE, "Ignoring excess tokens in macro definition around line %d of %s!\n", lineno, filename); +} + +static int matches_extension(char *exten, char **extout) +{ + char *c; + *extout = NULL; + c = exten; + while(*c && (*c > 32)) c++; + if (*c) { + *c = '\0'; + c++; + while(*c && (*c < 33)) c++; + if (*c) { + if (*c == '=') { + *c = '\0'; + c++; + if (*c == '>') + c++; + while (*c && (*c < 33)) c++; + *extout = c; + return 1; + } + } + } + return 0; +} + +static void parse_keyword(char *s, char **o) +{ + char *c; + c = s; + while((*c) && (*c > 32)) c++; + if (*c) { + *c = '\0'; + c++; + while(*c && (*c < 33)) c++; + *o = c; + } else + *o = NULL; +} + +static void handle_context(struct ast_context **local_contexts, struct stringlink *vars, const char *filename, int lineno) +{ + struct stringlink *argv; + struct stringlink *paramv; + struct stringlink *cur2; + struct stringlink *argv2; + struct stringlink *cur; + struct ast_context *con; + char *rest; + char *c; + char name[256]; + int pos; + + if (aeldebug & DEBUG_CONTEXTS) + ast_verbose("Root context def is '%s'\n", vars->data); + argv = split_token(vars->data, filename, lineno); + paramv = split_params(vars->data, filename, lineno); + if (aeldebug & DEBUG_CONTEXTS) + ast_verbose("Found context '%s'\n", vars->data); + snprintf(name, sizeof(name), "%s", vars->data); + con = ast_context_create(local_contexts, name, registrar); + if (con) { + cur = argv; + while(cur) { + if (matches_keyword(cur->data, "includes")) { + if (aeldebug & DEBUG_CONTEXTS) + ast_verbose("--INCLUDES: '%s'\n", cur->data); + parse_keyword(cur->data, &rest); + if (rest) { + argv2 = arg_parse(rest, filename, lineno); + cur2 = argv2; + while(cur2) { + ast_context_add_include2(con, cur2->data, registrar); + cur2 = cur2->next; + } + arg_free(argv2); + } + } else if (matches_keyword(cur->data, "ignorepat")) { + if (aeldebug & DEBUG_CONTEXTS) + ast_verbose("--IGNOREPAT: '%s'\n", cur->data); + parse_keyword(cur->data, &rest); + if (rest) { + argv2 = arg_parse(rest, filename, lineno); + cur2 = argv2; + while(cur2) { + ast_context_add_ignorepat2(con, cur2->data, registrar); + cur2 = cur2->next; + } + arg_free(argv2); + } + } else if (matches_keyword(cur->data, "switches") || matches_keyword(cur->data, "eswitches")) { + if (aeldebug & DEBUG_CONTEXTS) + ast_verbose("--[E]SWITCH: '%s'\n", cur->data); + parse_keyword(cur->data, &rest); + if (rest) { + argv2 = arg_parse(rest, filename, lineno); + cur2 = argv2; + while(cur2) { + c = strchr(cur2->data, '/'); + if (c) { + *c = '\0'; + c++; + } else + c = ""; + ast_context_add_switch2(con, cur2->data, c, (cur->data[0] == 'e'), registrar); + cur2 = cur2->next; + } + arg_free(argv2); + } + } else if (matches_extension(cur->data, &rest)) { + if (aeldebug & DEBUG_CONTEXTS) + ast_verbose("Extension: '%s' => '%s'\n", cur->data, rest); + pos = 1; + build_step("extension", cur->data, filename, lineno, con, cur->data, &pos, rest, NULL, NULL); + } + cur = cur->next; + } + } else + ast_log(LOG_WARNING, "Unable to create context '%s'\n", name); + arg_free(argv); + if (vars->next) + ast_log(LOG_NOTICE, "Ignoring excess tokens in macro definition around line %d of %s!\n", lineno, filename); +} + +static int handle_root_token(struct ast_context **local_contexts, char *token, int level, const char *filename, int lineno) +{ + struct stringlink *argv, *cur; + argv = split_token(token, filename, lineno); + if (aeldebug & DEBUG_TOKENS) { + ast_verbose("Found root token '%s' at level %d (%s:%d)!\n", token, level, filename, lineno); + cur = argv; + while(cur) { + ast_verbose(" ARG => '%s'\n", cur->data); + cur = cur->next; + } + } + if (!strcasecmp(token, "globals")) { + handle_globals(argv); + } else if (!strcasecmp(token, "macro")) { + handle_macro(local_contexts, argv, filename, lineno); + } else if (!strcasecmp(token, "context")) { + handle_context(local_contexts, argv, filename, lineno); + } else { + ast_log(LOG_NOTICE, "Unknown root token '%s'\n", token); + } + arg_free(argv); + return 0; +} + + +static int ast_ael_compile(struct ast_context **local_contexts, const char *filename) +{ + char *rfilename; + char *buf, *tbuf; + int bufsiz; + FILE *f; + char *c; + char *token; + int lineno=0; + + if (filename[0] == '/') + rfilename = (char *)filename; + else { + rfilename = alloca(strlen(filename) + strlen(ast_config_AST_CONFIG_DIR) + 2); + sprintf(rfilename, "%s/%s", ast_config_AST_CONFIG_DIR, filename); + } + + f = fopen(rfilename, "r"); + if (!f) { + ast_log(LOG_WARNING, "Unable to open '%s': %s\n", rfilename, strerror(errno)); + return -1; + } + buf = malloc(4096); + if (!buf) { + ast_log(LOG_WARNING, "Out of memory!\n"); + fclose(f); + return -1; + } + buf[0] = 0; + bufsiz = 4096; + while(!feof(f)) { + if (strlen(buf) - bufsiz < 2048) { + bufsiz += 4096; + tbuf = realloc(buf, bufsiz); + if (tbuf) { + buf = tbuf; + } else { + free(buf); + ast_log(LOG_WARNING, "Out of memory!\n"); + fclose(f); + } + } + if (fgets(buf + strlen(buf), bufsiz - strlen(buf), f)) { + lineno++; + while(*buf && buf[strlen(buf) - 1] < 33) + buf[strlen(buf) - 1] = '\0'; + c = strstr(buf, "//"); + if (c) + *c = '\0'; + if (*buf) { + if (aeldebug & DEBUG_READ) + ast_verbose("Newly composed line '%s'\n", buf); + while((token = grab_token(buf, filename, lineno))) { + handle_root_token(local_contexts, token, 0, filename, lineno); + free(token); + } + } + } + }; + free(buf); + fclose(f); + return 0; +} + +static int pbx_load_module(void) +{ + struct ast_context *local_contexts=NULL, *con; + ast_ael_compile(&local_contexts, config); + ast_merge_contexts_and_delete(&local_contexts, registrar); + for (con = ast_walk_contexts(NULL); con; con = ast_walk_contexts(con)) + ast_context_verify_includes(con); + pbx_set_autofallthrough(1); + +#if 0 + v = ast_variable_browse(cfg, "globals"); + while(v) { + memset(realvalue, 0, sizeof(realvalue)); + pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1); + pbx_builtin_setvar_helper(NULL, v->name, realvalue); + v = v->next; + } + cxt = ast_category_browse(cfg, NULL); + while(cxt) { + /* All categories but "general" or "globals" are considered contexts */ + if (!strcasecmp(cxt, "general") || !strcasecmp(cxt, "globals")) { + cxt = ast_category_browse(cfg, cxt); + continue; + } + if ((con=ast_context_create(&local_contexts,cxt, registrar))) { + v = ast_variable_browse(cfg, cxt); + while(v) { + if (!strcasecmp(v->name, "exten")) { + char *stringp=NULL; + int ipri = -2; + char realext[256]=""; + char *plus; + tc = strdup(v->value); + if(tc!=NULL){ + stringp=tc; + ext = strsep(&stringp, ","); + if (!ext) + ext=""; + cidmatch = strchr(ext, '/'); + if (cidmatch) { + *cidmatch = '\0'; + cidmatch++; + ast_shrink_phone_number(cidmatch); + } + pri = strsep(&stringp, ","); + if (!pri) + pri=""; + label = strchr(pri, '('); + if (label) { + *label = '\0'; + label++; + end = strchr(label, ')'); + if (end) + *end = '\0'; + else + ast_log(LOG_WARNING, "Label missing trailing ')' at line %d\n", v->lineno); + } + plus = strchr(pri, '+'); + if (plus) { + *plus = '\0'; + plus++; + } + if (!strcmp(pri,"hint")) + ipri=PRIORITY_HINT; + else if (!strcmp(pri, "next") || !strcmp(pri, "n")) { + if (lastpri > -2) + ipri = lastpri + 1; + else + ast_log(LOG_WARNING, "Can't use 'next' priority on the first entry!\n"); + } else if (!strcmp(pri, "same") || !strcmp(pri, "s")) { + if (lastpri > -2) + ipri = lastpri; + else + ast_log(LOG_WARNING, "Can't use 'same' priority on the first entry!\n"); + } else { + if (sscanf(pri, "%i", &ipri) != 1) { + if ((ipri = ast_findlabel_extension2(NULL, con, ext, pri, cidmatch)) < 1) { + ast_log(LOG_WARNING, "Invalid priority/label '%s' at line %d\n", pri, v->lineno); + ipri = 0; + } + } + } + appl = stringp; + if (!appl) + appl=""; + if (!(start = strchr(appl, '('))) { + if (stringp) + appl = strsep(&stringp, ","); + else + appl = ""; + } + if (start && (end = strrchr(appl, ')'))) { + *start = *end = '\0'; + data = start + 1; + process_quotes_and_slashes(data, ',', '|'); + } else if (stringp!=NULL && *stringp=='"') { + stringp++; + data = strsep(&stringp, "\""); + stringp++; + } else { + if (stringp) + data = strsep(&stringp, ","); + else + data = ""; + } + + if (!data) + data=""; + while(*appl && (*appl < 33)) appl++; + pbx_substitute_variables_helper(NULL, ext, realext, sizeof(realext) - 1); + if (ipri) { + if (plus) + ipri += atoi(plus); + lastpri = ipri; + if (!strcmp(realext, "_.")) + ast_log(LOG_WARNING, "The use of '_.' for an extension is strongly discouraged and can have unexpected behavior. Please use '_X.' instead at line %d\n", v->lineno); + if (ast_add_extension2(con, 0, realext, ipri, label, cidmatch, appl, strdup(data), FREE, registrar)) { + ast_log(LOG_WARNING, "Unable to register extension at line %d\n", v->lineno); + } + } + free(tc); + } else fprintf(stderr,"Error strdup returned NULL in %s\n",__PRETTY_FUNCTION__); + } else if(!strcasecmp(v->name, "include")) { + memset(realvalue, 0, sizeof(realvalue)); + pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1); + if (ast_context_add_include2(con, realvalue, registrar)) + ast_log(LOG_WARNING, "Unable to include context '%s' in context '%s'\n", v->value, cxt); + } else if(!strcasecmp(v->name, "ignorepat")) { + memset(realvalue, 0, sizeof(realvalue)); + pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1); + if (ast_context_add_ignorepat2(con, realvalue, registrar)) + ast_log(LOG_WARNING, "Unable to include ignorepat '%s' in context '%s'\n", v->value, cxt); + } else if (!strcasecmp(v->name, "switch") || !strcasecmp(v->name, "lswitch") || !strcasecmp(v->name, "eswitch")) { + char *stringp=NULL; + memset(realvalue, 0, sizeof(realvalue)); + if (!strcasecmp(v->name, "switch")) + pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1); + else + strncpy(realvalue, v->value, sizeof(realvalue) - 1); + tc = realvalue; + stringp=tc; + appl = strsep(&stringp, "/"); + data = strsep(&stringp, ""); + if (!data) + data = ""; + if (ast_context_add_switch2(con, appl, data, !strcasecmp(v->name, "eswitch"), registrar)) + ast_log(LOG_WARNING, "Unable to include switch '%s' in context '%s'\n", v->value, cxt); + } + v = v->next; + } + } + cxt = ast_category_browse(cfg, cxt); + } + ast_config_destroy(cfg); + } + ast_merge_contexts_and_delete(&local_contexts,registrar); + + for (con = ast_walk_contexts(NULL); con; con = ast_walk_contexts(con)) + ast_context_verify_includes(con); + + pbx_set_autofallthrough(autofallthrough_config); +#endif + + return 0; +} + +int load_module(void) +{ + if (pbx_load_module()) return -1; + + return 0; +} + +int reload(void) +{ + ast_context_destroy(NULL, registrar); + /* For martin's global variables, don't clear them on reload */ + pbx_load_module(); + return 0; +} + +int usecount(void) +{ + return 0; +} + +char *description(void) +{ + return dtext; +} + +char *key(void) +{ + return ASTERISK_GPL_KEY; +} |