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: 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
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 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
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
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
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
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
00608 ifblock = (*pos)++;
00609 iflabel = *label;
00610 *label = NULL;
00611
00612 ifstart = *pos;
00613 snprintf(margs, mlen, "if-%s-%d", name, ifblock);
00614
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
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
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
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
00667 whilestart = (*pos);
00668 whilelabel = *label;
00669 *label = NULL;
00670 (*pos)++;
00671
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
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
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
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
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
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
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
00775 forstart = (*pos);
00776 forlabel = *label;
00777 (*pos)++;
00778 *label = NULL;
00779
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
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
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
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
00902 data += 5;
00903 data = ast_skip_blanks(data);
00904
00905 *catch = data;
00906 if (!*data)
00907 return 0;
00908 while (*data && (*data > 32)) data++;
00909 if (!*data)
00910 return 0;
00911
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
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
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 }