00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
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: 45134 $")
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
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
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
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
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
00220 *e = '\0';
00221 e++;
00222
00223 e = ast_skip_blanks(e);
00224
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
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 void gen_match_to_pattern(const char *pattern, char *result)
00478 {
00479
00480 char *p=(char *)pattern, *t=result;
00481 while (*p) {
00482 if (*p == 'x' || *p == 'n' || *p == 'z' || *p == 'X' || *p == 'N' || *p == 'Z')
00483 *t++ = '9';
00484 else if (*p == '[') {
00485 char *z = p+1;
00486 while (*z != ']')
00487 z++;
00488 if (*(z+1)== ']')
00489 z++;
00490 *t++=*(p+1);
00491 p = z;
00492 } else {
00493 *t++ = *p;
00494 }
00495 p++;
00496 }
00497 *t++ = 0;
00498 }
00499
00500 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);
00501 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)
00502 {
00503 char *app;
00504 char *args;
00505 char *c;
00506 char *margs=NULL;
00507 char *oargs;
00508 char *rest;
00509 const char *curcase, *newcase;
00510 struct stringlink *swargs, *cur;
00511 int cpos;
00512 int mlen;
00513 int pattern = 0;
00514 struct fillin *fillin;
00515
00516 data = ast_skip_blanks(data);
00517 if (matches_label(data, &c)) {
00518 *label = data;
00519 data = c;
00520 data = ast_skip_blanks(data);
00521 }
00522 if (ast_strlen_zero(data))
00523 return 0;
00524 if (matches_keyword(data, "switch")) {
00525 fillin = NULL;
00526
00527 args = data + strlen("switch");
00528 while ((*args < 33) && (*args != '(')) args++;
00529 if ((*args == '(') && (c = argument_end(args))) {
00530 args++;
00531 *c = '\0';
00532 c++;
00533 if (aeldebug & DEBUG_TOKENS)
00534 ast_verbose("--SWITCH on : %s\n", args);
00535 mlen = strlen(exten) + 128 + strlen(args) + strlen(name);
00536 margs = alloca(mlen);
00537 app = "Goto";
00538 sprintf(margs, "sw-%d-%s|1", *pos, args);
00539 ast_process_quotes_and_slashes(margs, ',', '|');
00540 oargs = args;
00541 args = margs;
00542 if (ast_add_extension2(con, 0, exten, *pos, *label, NULL, app, strdup(args), FREE, registrar))
00543 ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
00544 else {
00545 *label = NULL;
00546 (*pos)++;
00547 }
00548 app = "NoOp";
00549 sprintf(margs, "Finish switch-%d", *pos - 1);
00550 if (ast_add_extension2(con, 0, exten, *pos, *label, NULL, app, strdup(args), FREE, registrar))
00551 ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
00552 else {
00553 *label = NULL;
00554 (*pos)++;
00555 }
00556 c = ast_skip_blanks(c);
00557 if (aeldebug & DEBUG_TOKENS)
00558 ast_verbose("ARG Parsing '%s'\n", c);
00559 swargs = arg_parse(c, filename, lineno);
00560 cur = swargs;
00561 curcase = NULL;
00562 while(cur) {
00563 if ((newcase = get_case(cur->data, &rest, &pattern))) {
00564 if (aeldebug & DEBUG_TOKENS)
00565 ast_verbose("--NEWCASE: '%s'!\n", newcase);
00566 if (curcase) {
00567 char zbuf[256];
00568
00569
00570 char tmp[strlen(newcase) + strlen(name) + 40];
00571 gen_match_to_pattern(newcase,zbuf);
00572 sprintf(tmp, "sw-%d-%s|%d", *pos - 2, zbuf, 1);
00573 ast_add_extension2(con, 0, margs, cpos, NULL, NULL, "Goto", strdup(tmp), FREE, registrar);
00574 }
00575 curcase = newcase;
00576 cpos = 1;
00577 if (pattern)
00578 snprintf(margs, mlen, "_sw-%d-%s", *pos - 2, curcase);
00579 else
00580 snprintf(margs, mlen, "sw-%d-%s", *pos - 2, curcase);
00581 if (!strcasecmp(rest, "break")) {
00582 char tmp[strlen(exten) + 10];
00583 sprintf(tmp, "%s|%d", exten, *pos - 1);
00584 ast_add_extension2(con, 0, exten, cpos, *label, NULL, "Goto", strdup(tmp), FREE, registrar);
00585 curcase = NULL;
00586 *label = NULL;
00587 } else
00588 build_step("switch", margs, filename, lineno, con, margs, &cpos, rest, &fillin, label);
00589 } else if (curcase) {
00590 if (aeldebug & DEBUG_TOKENS)
00591 ast_verbose("Building statement from '%s'\n", rest);
00592 if (!strcasecmp(rest, "break")) {
00593 char tmp[strlen(exten) + 10];
00594 sprintf(tmp, "%s|%d", exten, *pos - 1);
00595 ast_add_extension2(con, 0, margs, cpos, *label, NULL, "Goto", strdup(tmp), FREE, registrar);
00596 curcase = NULL;
00597 *label = NULL;
00598 } else
00599 build_step("switch", margs, filename, lineno, con, margs, &cpos, rest, &fillin, label);
00600 } else
00601 ast_log(LOG_WARNING, "Unreachable code in switch at about line %d of %s\n", lineno, filename);
00602 if (aeldebug & DEBUG_TOKENS)
00603 ast_verbose("--SWARG: %s\n", cur->data);
00604 cur = cur->next;
00605 }
00606
00607 fillin_process(con, fillin, filename, lineno, NULL, 0, NULL, 0);
00608 fillin_free(fillin);
00609 arg_free(swargs);
00610 } else
00611 ast_log(LOG_WARNING, "Syntax error in switch declaration in %s around line %d!\n", filename, lineno);
00612
00613 } else if (matches_keyword(data, "if")) {
00614
00615 args = data + strlen("if");
00616 while ((*args < 33) && (*args != '(')) args++;
00617 if ((*args == '(') && (c = argument_end(args))) {
00618 int ifblock;
00619 int ifstart;
00620 int elsestart;
00621 int ifend;
00622 int ifskip;
00623 char *elses;
00624 char *iflabel;
00625 args++;
00626 *c = '\0';
00627 c++;
00628 c = ast_skip_blanks(c);
00629 if (aeldebug & DEBUG_TOKENS)
00630 ast_verbose("--IF on : '%s' : '%s'\n", args, c);
00631 mlen = strlen(exten) + 128 + strlen(args) + strlen(name);
00632 margs = alloca(mlen);
00633
00634 ifblock = (*pos)++;
00635 iflabel = *label;
00636 *label = NULL;
00637
00638 ifstart = *pos;
00639 snprintf(margs, mlen, "if-%s-%d", name, ifblock);
00640
00641 if (aeldebug & DEBUG_TOKENS)
00642 ast_verbose("Searching for elses in '%s'\n", c);
00643 elses = grab_else(c, filename, lineno);
00644 build_step("if", margs, filename, lineno, con, exten, pos, c, fillout, label);
00645 if (elses) {
00646
00647 ifskip = *pos;
00648 (*pos)++;
00649 elsestart = *pos;
00650 build_step("else", margs, filename, lineno, con, exten, pos, elses, fillout, label);
00651 } else {
00652 elsestart = *pos;
00653 ifskip = 0;
00654 }
00655 ifend = *pos;
00656 (*pos)++;
00657 app = "NoOp";
00658 snprintf(margs, mlen, "Finish if-%s-%d", name, ifblock);
00659 if (ast_add_extension2(con, 0, exten, ifend, *label, NULL, app, strdup(margs), FREE, registrar))
00660 ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
00661 *label = NULL;
00662 app = "GotoIf";
00663 snprintf(margs, mlen, "$[ %s ]?%d:%d", args, ifstart, elsestart);
00664 if (ast_add_extension2(con, 0, exten, ifblock, iflabel, NULL, app, strdup(margs), FREE, registrar))
00665 ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
00666 if (ifskip) {
00667
00668 snprintf(margs, mlen, "%d", ifend);
00669 if (ast_add_extension2(con, 0, exten, ifskip, NULL, NULL, "Goto", strdup(margs), FREE, registrar))
00670 ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
00671 }
00672 } else
00673 ast_log(LOG_WARNING, "Syntax error in if declaration in %s around line %d!\n", filename, lineno);
00674 } else if (matches_keyword(data, "while")) {
00675
00676 fillin = NULL;
00677 args = data + strlen("while");
00678 while ((*args < 33) && (*args != '(')) args++;
00679 if ((*args == '(') && (c = argument_end(args))) {
00680 int whileblock;
00681 int whilestart;
00682 int whileend;
00683 char *whilelabel;
00684 args++;
00685 *c = '\0';
00686 c++;
00687 c = ast_skip_blanks(c);
00688 if (aeldebug & DEBUG_TOKENS)
00689 ast_verbose("--WHILE on : '%s' : '%s'\n", args, c);
00690 mlen = strlen(exten) + 128 + strlen(args) + strlen(name);
00691 margs = alloca(mlen);
00692
00693 whilestart = (*pos);
00694 whilelabel = *label;
00695 *label = NULL;
00696 (*pos)++;
00697
00698 whileblock = (*pos);
00699 snprintf(margs, mlen, "while-%s-%d", name, whilestart);
00700 build_step("while", margs, filename, lineno, con, exten, pos, c, &fillin, label);
00701
00702 app = "Goto";
00703 snprintf(margs, mlen, "%d", whilestart);
00704 if (ast_add_extension2(con, 0, exten, (*pos)++, *label, NULL, app, strdup(margs), FREE, registrar))
00705 ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
00706 *label = NULL;
00707 whileend = (*pos);
00708
00709 app = "NoOp";
00710 snprintf(margs, mlen, "Finish while-%s-%d", name, whilestart);
00711 if (ast_add_extension2(con, 0, exten, (*pos)++, *label, NULL, app, strdup(margs), FREE, registrar))
00712 ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
00713 *label = NULL;
00714 app = "GotoIf";
00715 snprintf(margs, mlen, "$[ %s ]?%d:%d", args, whileblock, whileend);
00716 if (ast_add_extension2(con, 0, exten, whilestart, whilelabel, NULL, app, strdup(margs), FREE, registrar))
00717 ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
00718 fillin_process(con, fillin, filename, lineno, exten, whileend, exten, whilestart);
00719 fillin_free(fillin);
00720 } else
00721 ast_log(LOG_WARNING, "Syntax error in while declaration in %s around line %d!\n", filename, lineno);
00722 } else if (matches_keyword(data, "jump")) {
00723 char *p;
00724
00725 fillin = NULL;
00726 args = data + strlen("jump");
00727 args = ast_skip_blanks(args);
00728 if (aeldebug & DEBUG_TOKENS)
00729 ast_verbose("--JUMP to : '%s'\n", args);
00730 p = strchr(args, ',');
00731 if (p) {
00732 *p = '\0';
00733 p++;
00734 } else
00735 p = "1";
00736 c = strchr(args, '@');
00737 if (c) {
00738 *c = '\0';
00739 c++;
00740 }
00741 mlen = strlen(exten) + 128 + strlen(args) + strlen(name) + (c ? strlen(c) : 0);
00742 margs = alloca(mlen);
00743 if (c)
00744 snprintf(margs, mlen, "%s|%s|%s", c,args, p);
00745 else
00746 snprintf(margs, mlen, "%s|%s", args, p);
00747 app = "Goto";
00748 if (ast_add_extension2(con, 0, exten, (*pos)++, *label, NULL, app, strdup(margs), FREE, registrar))
00749 ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
00750 *label = NULL;
00751 } else if (matches_keyword(data, "goto")) {
00752
00753 fillin = NULL;
00754 args = data + strlen("goto");
00755 args = ast_skip_blanks(args);
00756 if (aeldebug & DEBUG_TOKENS)
00757 ast_verbose("--GOTO to : '%s'\n", args);
00758 app = "Goto";
00759 if (args[0] == '(' && args[strlen(args) - 1] == ')') {
00760 args[0] = '\0';
00761 args++;
00762 args[strlen(args) - 1] = '\0';
00763 }
00764 if (ast_add_extension2(con, 0, exten, (*pos)++, *label, NULL, app, strdup(args), FREE, registrar))
00765 ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
00766 *label = NULL;
00767 } else if (matches_keyword(data, "for")) {
00768
00769 fillin = NULL;
00770 args = data + strlen("for");
00771 while ((*args < 33) && (*args != '(')) args++;
00772 if ((*args == '(') && (c = argument_end(args))) {
00773 int forblock;
00774 int forprep;
00775 int forstart;
00776 int forend;
00777 struct stringlink *fields;
00778 char *tmp;
00779 char *forlabel = NULL;
00780 args++;
00781 *c = '\0';
00782 c++;
00783 c = ast_skip_blanks(c);
00784
00785 tmp = alloca(strlen(args) + 10);
00786 if (tmp) {
00787 snprintf(tmp, strlen(args) + 10, "{%s;}", args);
00788 fields = arg_parse(tmp, filename, lineno);
00789 } else
00790 fields = NULL;
00791 if (fields && fields->next && fields->next->next) {
00792 if (aeldebug & DEBUG_TOKENS)
00793 ast_verbose("--FOR ('%s' ; '%s' ; '%s') : '%s'\n", fields->data, fields->next->data, fields->next->next->data, c);
00794 mlen = strlen(exten) + 128 + strlen(args) + strlen(name);
00795 margs = alloca(mlen);
00796 forprep = *pos;
00797 snprintf(margs, mlen, "for-%s-%d", name, forprep);
00798 fillin = NULL;
00799 build_step("while", margs, filename, lineno, con, exten, pos, fields->data, &fillin, label);
00800
00801 forstart = (*pos);
00802 forlabel = *label;
00803 (*pos)++;
00804 *label = NULL;
00805
00806 forblock = (*pos);
00807 build_step("for", margs, filename, lineno, con, exten, pos, c, &fillin, label);
00808 build_step("for", margs, filename, lineno, con, exten, pos, fields->next->next->data, &fillin, label);
00809
00810 app = "Goto";
00811 snprintf(margs, mlen, "%d", forstart);
00812 if (ast_add_extension2(con, 0, exten, (*pos)++, *label, NULL, app, strdup(margs), FREE, registrar))
00813 ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
00814 *label = NULL;
00815 forend = (*pos);
00816
00817 app = "NoOp";
00818 snprintf(margs, mlen, "Finish for-%s-%d", name, forprep);
00819 if (ast_add_extension2(con, 0, exten, (*pos)++, *label, NULL, app, strdup(margs), FREE, registrar))
00820 ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
00821 *label = NULL;
00822 app = "GotoIf";
00823 snprintf(margs, mlen, "$[ %s ]?%d:%d", fields->next->data, forblock, forend);
00824 if (ast_add_extension2(con, 0, exten, forstart, forlabel, NULL, app, strdup(margs), FREE, registrar))
00825 ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", forstart, what, name);
00826 fillin_process(con, fillin, filename, lineno, exten, forend, exten, forstart);
00827 fillin_free(fillin);
00828 } else
00829 ast_log(LOG_NOTICE, "Improper for declaration in %s around line %d!\n", filename, lineno);
00830 arg_free(fields);
00831 } else
00832 ast_log(LOG_WARNING, "Syntax error in for declaration in %s around line %d!\n", filename, lineno);
00833
00834 } else if (!strcasecmp(data, "break") || !strcasecmp(data, "continue")) {
00835 struct fillin *fi;
00836 fi = malloc(sizeof(struct fillin));
00837 if (fi) {
00838 memset(fi, 0, sizeof(struct fillin));
00839 if (!strcasecmp(data, "break"))
00840 fi->type = FILLIN_BREAK;
00841 else
00842 fi->type = FILLIN_CONTINUE;
00843 ast_copy_string(fi->exten, exten, sizeof(fi->exten));
00844 fi->priority = (*pos)++;
00845 fi->next = *fillout;
00846 *fillout = fi;
00847 }
00848 } else if (match_assignment(data, &rest)) {
00849 if (aeldebug & DEBUG_TOKENS)
00850 ast_verbose("ASSIGN '%s' = '%s'\n", data, rest);
00851 mlen = strlen(rest) + strlen(data) + 20;
00852 margs = alloca(mlen);
00853 snprintf(margs, mlen, "%s=$[ %s ]", data, rest);
00854 app = "Set";
00855 if (ast_add_extension2(con, 0, exten, *pos, *label, NULL, app, strdup(margs), FREE, registrar))
00856 ast_log(LOG_WARNING, "Unable to add assignment at priority '%d' of %s '%s'\n", *pos, what, name);
00857 else {
00858 *label = NULL;
00859 (*pos)++;
00860 }
00861 } else {
00862 app = data;
00863 args = app;
00864 while (*args && (*args > 32) && (*args != '(')) args++;
00865 if (*args != '(') {
00866 while(*args && (*args != '(')) { *args = '\0'; args++; };
00867 }
00868 if (*args == '(') {
00869 *args = '\0';
00870 args++;
00871
00872 c = args + strlen(args) - 1;
00873 while((c >= args) && (*c < 33) && (*c != ')')) { *c = '\0'; c--; };
00874 if ((c >= args) && (*c == ')')) *c = '\0';
00875 } else
00876 args = "";
00877 ast_process_quotes_and_slashes(args, ',', '|');
00878 if (app[0] == '&') {
00879 app++;
00880 margs = alloca(strlen(args) + strlen(app) + 10);
00881 sprintf(margs, "%s|%s", app, args);
00882 args = margs;
00883 app = "Macro";
00884 }
00885 if (aeldebug & DEBUG_TOKENS)
00886 ast_verbose("-- APP: '%s', ARGS: '%s'\n", app, args);
00887 if (ast_add_extension2(con, 0, exten, *pos, *label, NULL, app, strdup(args), FREE, registrar))
00888 ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name);
00889 else {
00890 (*pos)++;
00891 *label = NULL;
00892 }
00893 }
00894 return 0;
00895 }
00896
00897 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)
00898 {
00899 struct stringlink *args, *cur;
00900 int res=0;
00901 struct fillin *fillin=NULL;
00902 int dropfill = 0;
00903 char *labelin = NULL;
00904 if (!fillout) {
00905 fillout = &fillin;
00906 dropfill = 1;
00907 }
00908 if (!label) {
00909 label = &labelin;
00910 };
00911 args = arg_parse(data, filename, lineno);
00912 cur = args;
00913 while(cur) {
00914 res |= __build_step(what, name, filename, lineno, con, exten, pos, cur->data, fillout, label);
00915 cur = cur->next;
00916 }
00917 arg_free(args);
00918 if (dropfill) {
00919 fillin_process(con, fillin, filename, lineno, NULL, 0, NULL, 0);
00920 fillin_free(fillin);
00921 }
00922 return res;
00923 }
00924
00925 static int parse_catch(char *data, char **catch, char **rest)
00926 {
00927
00928 data += 5;
00929 data = ast_skip_blanks(data);
00930
00931 *catch = data;
00932 if (!*data)
00933 return 0;
00934 while (*data && (*data > 32)) data++;
00935 if (!*data)
00936 return 0;
00937
00938 *data = '\0';
00939 data++;
00940 data = ast_skip_blanks(data);
00941 if (!*data)
00942 return 0;
00943 *rest = data;
00944 return 1;
00945 }
00946
00947 static void handle_macro(struct ast_context **local_contexts, struct stringlink *vars, const char *filename, int lineno)
00948 {
00949 struct stringlink *argv;
00950 struct stringlink *paramv;
00951 struct stringlink *cur;
00952 struct ast_context *con;
00953 struct fillin *fillin;
00954 char *catch, *rest;
00955 char name[256];
00956 int pos;
00957 int cpos;
00958
00959 if (aeldebug & DEBUG_MACROS)
00960 ast_verbose("Root macro def is '%s'\n", vars->data);
00961 argv = split_token(vars->data, filename, lineno);
00962 paramv = split_params(vars->data, filename, lineno);
00963 if (aeldebug & DEBUG_MACROS)
00964 ast_verbose("Found macro '%s'\n", vars->data);
00965 snprintf(name, sizeof(name), "macro-%s", vars->data);
00966 con = ast_context_create(local_contexts, name, registrar);
00967 if (con) {
00968 pos = 1;
00969 cur = paramv;
00970 while(cur) {
00971 if (aeldebug & DEBUG_MACROS)
00972 ast_verbose(" PARAM => '%s'\n", cur->data);
00973 snprintf(name, sizeof(name), "%s=${ARG%d}", cur->data, pos);
00974 if (ast_add_extension2(con, 0, "s", pos, NULL, NULL, "Set", strdup(name), FREE, registrar))
00975 ast_log(LOG_WARNING, "Unable to add step at priority '%d' of macro '%s'\n", pos, vars->data);
00976 else
00977 pos++;
00978 cur = cur->next;
00979 }
00980 cur = argv;
00981 while(cur) {
00982 if (aeldebug & DEBUG_MACROS)
00983 ast_verbose(" STEP => '%s'\n", cur->data);
00984 if (matches_keyword(cur->data, "catch")) {
00985 if (aeldebug & DEBUG_MACROS)
00986 ast_verbose("--CATCH: '%s'\n", cur->data);
00987 if (parse_catch(cur->data, &catch, &rest)) {
00988 cpos = 1;
00989 build_step("catch", catch, filename, lineno, con, catch, &cpos, rest, NULL, NULL);
00990 } else
00991 ast_log(LOG_NOTICE, "Parse error for catch at about line %d of %s\n", lineno, filename);
00992 } else {
00993 fillin = NULL;
00994 build_step("macro", vars->data, filename, lineno, con, "s", &pos, cur->data, NULL, NULL);
00995 }
00996 cur = cur->next;
00997 }
00998 } else
00999 ast_log(LOG_WARNING, "Unable to create context '%s'\n", name);
01000 arg_free(paramv);
01001 arg_free(argv);
01002 if (vars->next)
01003 ast_log(LOG_NOTICE, "Ignoring excess tokens in macro definition around line %d of %s!\n", lineno, filename);
01004 }
01005
01006 static int matches_extension(char *exten, char **extout)
01007 {
01008 char *c;
01009 *extout = NULL;
01010 c = exten;
01011 while(*c && (*c > 32)) c++;
01012 if (*c) {
01013 *c = '\0';
01014 c++;
01015 c = ast_skip_blanks(c);
01016 if (*c) {
01017 if (*c == '=') {
01018 *c = '\0';
01019 c++;
01020 if (*c == '>')
01021 c++;
01022 c = ast_skip_blanks(c);
01023 *extout = c;
01024 return 1;
01025 }
01026 }
01027 }
01028 return 0;
01029 }
01030
01031 static void parse_keyword(char *s, char **o)
01032 {
01033 char *c;
01034 c = s;
01035 while((*c) && (*c > 32)) c++;
01036 if (*c) {
01037 *c = '\0';
01038 c++;
01039 c = ast_skip_blanks(c);
01040 *o = c;
01041 } else
01042 *o = NULL;
01043 }
01044
01045 static void handle_context(struct ast_context **local_contexts, struct stringlink *vars, const char *filename, int lineno)
01046 {
01047 struct stringlink *argv;
01048 struct stringlink *cur2;
01049 struct stringlink *argv2;
01050 struct stringlink *cur;
01051 struct ast_context *con;
01052 char *rest;
01053 char *c;
01054 char name[256];
01055 int pos;
01056
01057 if (aeldebug & DEBUG_CONTEXTS)
01058 ast_verbose("Root context def is '%s'\n", vars->data);
01059 argv = split_token(vars->data, filename, lineno);
01060 if (aeldebug & DEBUG_CONTEXTS)
01061 ast_verbose("Found context '%s'\n", vars->data);
01062 snprintf(name, sizeof(name), "%s", vars->data);
01063 con = ast_context_create(local_contexts, name, registrar);
01064 if (con) {
01065 cur = argv;
01066 while(cur) {
01067 if (matches_keyword(cur->data, "includes")) {
01068 if (aeldebug & DEBUG_CONTEXTS)
01069 ast_verbose("--INCLUDES: '%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 ast_context_add_include2(con, cur2->data, registrar);
01076 cur2 = cur2->next;
01077 }
01078 arg_free(argv2);
01079 }
01080 } else if (matches_keyword(cur->data, "ignorepat")) {
01081 if (aeldebug & DEBUG_CONTEXTS)
01082 ast_verbose("--IGNOREPAT: '%s'\n", cur->data);
01083 parse_keyword(cur->data, &rest);
01084 if (rest) {
01085 argv2 = arg_parse(rest, filename, lineno);
01086 cur2 = argv2;
01087 while(cur2) {
01088 ast_context_add_ignorepat2(con, cur2->data, registrar);
01089 cur2 = cur2->next;
01090 }
01091 arg_free(argv2);
01092 }
01093 } else if (matches_keyword(cur->data, "switches") || matches_keyword(cur->data, "eswitches")) {
01094 if (aeldebug & DEBUG_CONTEXTS)
01095 ast_verbose("--[E]SWITCH: '%s'\n", cur->data);
01096 parse_keyword(cur->data, &rest);
01097 if (rest) {
01098 argv2 = arg_parse(rest, filename, lineno);
01099 cur2 = argv2;
01100 while(cur2) {
01101 c = strchr(cur2->data, '/');
01102 if (c) {
01103 *c = '\0';
01104 c++;
01105 } else
01106 c = "";
01107 ast_context_add_switch2(con, cur2->data, c, (cur->data[0] == 'e'), registrar);
01108 cur2 = cur2->next;
01109 }
01110 arg_free(argv2);
01111 }
01112 } else if (matches_extension(cur->data, &rest)) {
01113 if (aeldebug & DEBUG_CONTEXTS)
01114 ast_verbose("Extension: '%s' => '%s'\n", cur->data, rest);
01115 pos = 1;
01116 build_step("extension", cur->data, filename, lineno, con, cur->data, &pos, rest, NULL, NULL);
01117 }
01118 cur = cur->next;
01119 }
01120 } else
01121 ast_log(LOG_WARNING, "Unable to create context '%s'\n", name);
01122 arg_free(argv);
01123 if (vars->next)
01124 ast_log(LOG_NOTICE, "Ignoring excess tokens in macro definition around line %d of %s!\n", lineno, filename);
01125 }
01126
01127 static int handle_root_token(struct ast_context **local_contexts, char *token, int level, const char *filename, int lineno)
01128 {
01129 struct stringlink *argv, *cur;
01130 argv = split_token(token, filename, lineno);
01131 if (aeldebug & DEBUG_TOKENS) {
01132 ast_verbose("Found root token '%s' at level %d (%s:%d)!\n", token, level, filename, lineno);
01133 cur = argv;
01134 while(cur) {
01135 ast_verbose(" ARG => '%s'\n", cur->data);
01136 cur = cur->next;
01137 }
01138 }
01139 if (!strcasecmp(token, "globals")) {
01140 handle_globals(argv);
01141 } else if (!strcasecmp(token, "macro")) {
01142 handle_macro(local_contexts, argv, filename, lineno);
01143 } else if (!strcasecmp(token, "context")) {
01144 handle_context(local_contexts, argv, filename, lineno);
01145 } else {
01146 ast_log(LOG_NOTICE, "Unknown root token '%s'\n", token);
01147 }
01148 arg_free(argv);
01149 return 0;
01150 }
01151
01152
01153 static int ast_ael_compile(struct ast_context **local_contexts, const char *filename)
01154 {
01155 char *rfilename;
01156 char *buf, *tbuf;
01157 int bufsiz;
01158 FILE *f;
01159 char *c;
01160 char *token;
01161 int lineno=0;
01162
01163 if (filename[0] == '/')
01164 rfilename = (char *)filename;
01165 else {
01166 rfilename = alloca(strlen(filename) + strlen(ast_config_AST_CONFIG_DIR) + 2);
01167 sprintf(rfilename, "%s/%s", ast_config_AST_CONFIG_DIR, filename);
01168 }
01169
01170 f = fopen(rfilename, "r");
01171 if (!f) {
01172 ast_log(LOG_WARNING, "Unable to open '%s': %s\n", rfilename, strerror(errno));
01173 return -1;
01174 }
01175 buf = malloc(4096);
01176 if (!buf) {
01177 ast_log(LOG_WARNING, "Out of memory!\n");
01178 fclose(f);
01179 return -1;
01180 }
01181 buf[0] = 0;
01182 bufsiz = 4096;
01183 while(!feof(f)) {
01184 if (bufsiz - strlen(buf) < 2048) {
01185 bufsiz += 4096;
01186 tbuf = realloc(buf, bufsiz);
01187 if (tbuf) {
01188 buf = tbuf;
01189 } else {
01190 free(buf);
01191 ast_log(LOG_WARNING, "Out of memory!\n");
01192 fclose(f);
01193 }
01194 }
01195 if (fgets(buf + strlen(buf), bufsiz - strlen(buf), f)) {
01196 lineno++;
01197 while(*buf && buf[strlen(buf) - 1] < 33)
01198 buf[strlen(buf) - 1] = '\0';
01199 c = strstr(buf, "//");
01200 if (c)
01201 *c = '\0';
01202 if (*buf) {
01203 if (aeldebug & DEBUG_READ)
01204 ast_verbose("Newly composed line '%s'\n", buf);
01205 while((token = grab_token(buf, filename, lineno))) {
01206 handle_root_token(local_contexts, token, 0, filename, lineno);
01207 free(token);
01208 }
01209 }
01210 }
01211 };
01212 free(buf);
01213 fclose(f);
01214 return 0;
01215 }
01216
01217 static int pbx_load_module(void)
01218 {
01219 struct ast_context *local_contexts=NULL, *con;
01220 ast_ael_compile(&local_contexts, config);
01221 ast_merge_contexts_and_delete(&local_contexts, registrar);
01222 for (con = ast_walk_contexts(NULL); con; con = ast_walk_contexts(con))
01223 ast_context_verify_includes(con);
01224
01225 return 0;
01226 }
01227
01228
01229 static int ael_debug_read(int fd, int argc, char *argv[])
01230 {
01231 aeldebug |= DEBUG_READ;
01232 return 0;
01233 }
01234
01235 static int ael_debug_tokens(int fd, int argc, char *argv[])
01236 {
01237 aeldebug |= DEBUG_TOKENS;
01238 return 0;
01239 }
01240
01241 static int ael_debug_macros(int fd, int argc, char *argv[])
01242 {
01243 aeldebug |= DEBUG_MACROS;
01244 return 0;
01245 }
01246
01247 static int ael_debug_contexts(int fd, int argc, char *argv[])
01248 {
01249 aeldebug |= DEBUG_CONTEXTS;
01250 return 0;
01251 }
01252
01253 static int ael_no_debug(int fd, int argc, char *argv[])
01254 {
01255 aeldebug = 0;
01256 return 0;
01257 }
01258
01259 static int ael_reload(int fd, int argc, char *argv[])
01260 {
01261 ast_context_destroy(NULL, registrar);
01262 return (pbx_load_module());
01263 }
01264
01265 static struct ast_cli_entry ael_cli[] = {
01266 { { "ael", "reload", NULL }, ael_reload, "Reload AEL configuration"},
01267 { { "ael", "debug", "read", NULL }, ael_debug_read, "Enable AEL read debug"},
01268 { { "ael", "debug", "tokens", NULL }, ael_debug_tokens, "Enable AEL tokens debug"},
01269 { { "ael", "debug", "macros", NULL }, ael_debug_macros, "Enable AEL macros debug"},
01270 { { "ael", "debug", "contexts", NULL }, ael_debug_contexts, "Enable AEL contexts debug"},
01271 { { "ael", "no", "debug", NULL }, ael_no_debug, "Disable AEL debug messages"},
01272 };
01273
01274
01275
01276
01277 int unload_module(void)
01278 {
01279 ast_context_destroy(NULL, registrar);
01280 ast_cli_unregister_multiple(ael_cli, sizeof(ael_cli)/ sizeof(ael_cli[0]));
01281 return 0;
01282 }
01283
01284
01285 int load_module(void)
01286 {
01287 ast_cli_register_multiple(ael_cli, sizeof(ael_cli)/ sizeof(ael_cli[0]));
01288 return (pbx_load_module());
01289 }
01290
01291 int reload(void)
01292 {
01293 ast_context_destroy(NULL, registrar);
01294 return pbx_load_module();
01295 }
01296
01297 int usecount(void)
01298 {
01299 return 0;
01300 }
01301
01302 char *description(void)
01303 {
01304 return dtext;
01305 }
01306
01307 char *key(void)
01308 {
01309 return ASTERISK_GPL_KEY;
01310 }