Asterisk - The Open Source Telephony Project GIT-master-5782b03
pval.c
Go to the documentation of this file.
1
2/*
3 * Asterisk -- An open source telephony toolkit.
4 *
5 * Copyright (C) 2006, Digium, Inc.
6 *
7 * Steve Murphy <murf@parsetree.com>
8 *
9 * See http://www.asterisk.org for more information about
10 * the Asterisk project. Please do not directly contact
11 * any of the maintainers of this project for assistance;
12 * the project provides a web site, mailing lists and IRC
13 * channels for your use.
14 *
15 * This program is free software, distributed under the terms of
16 * the GNU General Public License Version 2. See the LICENSE file
17 * at the top of the source tree.
18 */
19
20/*! \file
21 *
22 * \brief Compile symbolic Asterisk Extension Logic into Asterisk extensions, version 2.
23 *
24 */
25
26/*** MODULEINFO
27 <support_level>extended</support_level>
28 ***/
29
30#define ASTMM_LIBC ASTMM_REDIRECT
31#include "asterisk.h"
32
33#include <sys/types.h>
34#include <stdlib.h>
35#include <unistd.h>
36#include <stdio.h>
37#include <string.h>
38#include <ctype.h>
39#include <errno.h>
40#include <regex.h>
41#include <sys/stat.h>
42
43#include "asterisk/pbx.h"
44#include "asterisk/config.h"
45#include "asterisk/module.h"
46#include "asterisk/logger.h"
47#include "asterisk/cli.h"
48#include "asterisk/app.h"
49#include "asterisk/channel.h"
50#include "asterisk/callerid.h"
51#include "asterisk/pval.h"
53#ifdef AAL_ARGCHECK
54#include "asterisk/argdesc.h"
55#endif
56#include "asterisk/utils.h"
57
58extern int localized_pbx_load_module(void);
59
60static char expr_output[2096];
61#define BUF_SIZE 2000
62
63/* these functions are in ../ast_expr2.fl */
64
65static int errs, warns;
66static int notes;
67#ifdef STANDALONE
68static int extensions_dot_conf_loaded = 0;
69#endif
70static char *registrar = "pbx_ael";
71
75
76static const char *match_context;
77static const char *match_exten;
78static const char *match_label;
80static int count_labels; /* true, put matcher in label counting mode */
81static int label_count; /* labels are only meant to be counted in a context or exten */
84struct pval *match_pval(pval *item);
85static void check_timerange(pval *p);
86static void check_dow(pval *DOW);
87static void check_day(pval *DAY);
88static void check_month(pval *MON);
89static void check_expr2_input(pval *expr, char *str);
90static int extension_matches(pval *here, const char *exten, const char *pattern);
91static void check_goto(pval *item);
92static void find_pval_goto_item(pval *item, int lev);
93static void find_pval_gotos(pval *item, int lev);
94static int check_break(pval *item);
95static int check_continue(pval *item);
96static void check_label(pval *item);
97static void check_macro_returns(pval *macro);
98
99static struct pval *find_label_in_current_context(char *exten, char *label, pval *curr_cont);
100static struct pval *find_first_label_in_current_context(char *label, pval *curr_cont);
101static void print_pval_list(FILE *fin, pval *item, int depth);
102
103static struct pval *find_label_in_current_extension(const char *label, pval *curr_ext);
104static struct pval *find_label_in_current_db(const char *context, const char *exten, const char *label);
105static pval *get_goto_target(pval *item);
106static int label_inside_case(pval *label);
107static void attach_exten(struct ael_extension **list, struct ael_extension *newmem);
108static void fix_gotos_in_extensions(struct ael_extension *exten);
110static pval *get_contxt(pval *p);
111static void remove_spaces_before_equals(char *str);
112
113/* PRETTY PRINTER FOR AEL: ============================================================================= */
114
115static void print_pval(FILE *fin, pval *item, int depth)
116{
117 int i;
118 pval *lp;
119
120 for (i=0; i<depth; i++) {
121 fprintf(fin, "\t"); /* depth == indentation */
122 }
123
124 switch ( item->type ) {
125 case PV_WORD:
126 fprintf(fin,"%s;\n", item->u1.str); /* usually, words are encapsulated in something else */
127 break;
128
129 case PV_MACRO:
130 fprintf(fin,"macro %s(", item->u1.str);
131 for (lp=item->u2.arglist; lp; lp=lp->next) {
132 if (lp != item->u2.arglist )
133 fprintf(fin,", ");
134 fprintf(fin,"%s", lp->u1.str);
135 }
136 fprintf(fin,") {\n");
137 print_pval_list(fin,item->u3.macro_statements,depth+1);
138 for (i=0; i<depth; i++) {
139 fprintf(fin,"\t"); /* depth == indentation */
140 }
141 fprintf(fin,"};\n\n");
142 break;
143
144 case PV_CONTEXT:
145 if ( item->u3.abstract )
146 fprintf(fin,"abstract context %s {\n", item->u1.str);
147 else
148 fprintf(fin,"context %s {\n", item->u1.str);
149 print_pval_list(fin,item->u2.statements,depth+1);
150 for (i=0; i<depth; i++) {
151 fprintf(fin,"\t"); /* depth == indentation */
152 }
153 fprintf(fin,"};\n\n");
154 break;
155
156 case PV_MACRO_CALL:
157 fprintf(fin,"&%s(", item->u1.str);
158 for (lp=item->u2.arglist; lp; lp=lp->next) {
159 if ( lp != item->u2.arglist )
160 fprintf(fin,", ");
161 fprintf(fin,"%s", lp->u1.str);
162 }
163 fprintf(fin,");\n");
164 break;
165
167 fprintf(fin,"%s(", item->u1.str);
168 for (lp=item->u2.arglist; lp; lp=lp->next) {
169 if ( lp != item->u2.arglist )
170 fprintf(fin,",");
171 fprintf(fin,"%s", lp->u1.str);
172 }
173 fprintf(fin,");\n");
174 break;
175
176 case PV_CASE:
177 fprintf(fin,"case %s:\n", item->u1.str);
178 print_pval_list(fin,item->u2.statements, depth+1);
179 break;
180
181 case PV_PATTERN:
182 fprintf(fin,"pattern %s:\n", item->u1.str);
183 print_pval_list(fin,item->u2.statements, depth+1);
184 break;
185
186 case PV_DEFAULT:
187 fprintf(fin,"default:\n");
188 print_pval_list(fin,item->u2.statements, depth+1);
189 break;
190
191 case PV_CATCH:
192 fprintf(fin,"catch %s {\n", item->u1.str);
193 print_pval_list(fin,item->u2.statements, depth+1);
194 for (i=0; i<depth; i++) {
195 fprintf(fin,"\t"); /* depth == indentation */
196 }
197 fprintf(fin,"};\n");
198 break;
199
200 case PV_SWITCHES:
201 fprintf(fin,"switches {\n");
202 print_pval_list(fin,item->u1.list,depth+1);
203 for (i=0; i<depth; i++) {
204 fprintf(fin,"\t"); /* depth == indentation */
205 }
206 fprintf(fin,"};\n");
207 break;
208
209 case PV_ESWITCHES:
210 fprintf(fin,"eswitches {\n");
211 print_pval_list(fin,item->u1.list,depth+1);
212 for (i=0; i<depth; i++) {
213 fprintf(fin,"\t"); /* depth == indentation */
214 }
215 fprintf(fin,"};\n");
216 break;
217
218 case PV_INCLUDES:
219 fprintf(fin,"includes {\n");
220 for (lp=item->u1.list; lp; lp=lp->next) {
221 for (i=0; i<depth+1; i++) {
222 fprintf(fin,"\t"); /* depth == indentation */
223 }
224 fprintf(fin,"%s", lp->u1.str); /* usually, words are encapsulated in something else */
225 if (lp->u2.arglist)
226 fprintf(fin,"|%s|%s|%s|%s",
227 lp->u2.arglist->u1.str,
228 lp->u2.arglist->next->u1.str,
229 lp->u2.arglist->next->next->u1.str,
230 lp->u2.arglist->next->next->next->u1.str
231 );
232 fprintf(fin,";\n"); /* usually, words are encapsulated in something else */
233 }
234
235 for (i=0; i<depth; i++) {
236 fprintf(fin,"\t"); /* depth == indentation */
237 }
238 fprintf(fin,"};\n");
239 break;
240
242 fprintf(fin,"{\n");
243 print_pval_list(fin,item->u1.list, depth+1);
244 for (i=0; i<depth; i++) {
245 fprintf(fin,"\t"); /* depth == indentation */
246 }
247 fprintf(fin,"}\n");
248 break;
249
250 case PV_VARDEC:
251 fprintf(fin,"%s=%s;\n", item->u1.str, item->u2.val);
252 break;
253
254 case PV_LOCALVARDEC:
255 fprintf(fin,"local %s=%s;\n", item->u1.str, item->u2.val);
256 break;
257
258 case PV_GOTO:
259 fprintf(fin,"goto %s", item->u1.list->u1.str);
260 if ( item->u1.list->next )
261 fprintf(fin,",%s", item->u1.list->next->u1.str);
262 if ( item->u1.list->next && item->u1.list->next->next )
263 fprintf(fin,",%s", item->u1.list->next->next->u1.str);
264 fprintf(fin,"\n");
265 break;
266
267 case PV_LABEL:
268 fprintf(fin,"%s:\n", item->u1.str);
269 break;
270
271 case PV_FOR:
272 fprintf(fin,"for (%s; %s; %s)\n", item->u1.for_init, item->u2.for_test, item->u3.for_inc);
273 print_pval_list(fin,item->u4.for_statements,depth+1);
274 break;
275
276 case PV_WHILE:
277 fprintf(fin,"while (%s)\n", item->u1.str);
278 print_pval_list(fin,item->u2.statements,depth+1);
279 break;
280
281 case PV_BREAK:
282 fprintf(fin,"break;\n");
283 break;
284
285 case PV_RETURN:
286 fprintf(fin,"return;\n");
287 break;
288
289 case PV_CONTINUE:
290 fprintf(fin,"continue;\n");
291 break;
292
293 case PV_RANDOM:
294 case PV_IFTIME:
295 case PV_IF:
296 if ( item->type == PV_IFTIME ) {
297
298 fprintf(fin,"ifTime ( %s|%s|%s|%s )\n",
299 item->u1.list->u1.str,
300 item->u1.list->next->u1.str,
301 item->u1.list->next->next->u1.str,
302 item->u1.list->next->next->next->u1.str
303 );
304 } else if ( item->type == PV_RANDOM ) {
305 fprintf(fin,"random ( %s )\n", item->u1.str );
306 } else
307 fprintf(fin,"if ( %s )\n", item->u1.str);
308 if ( item->u2.statements && item->u2.statements->next ) {
309 for (i=0; i<depth; i++) {
310 fprintf(fin,"\t"); /* depth == indentation */
311 }
312 fprintf(fin,"{\n");
313 print_pval_list(fin,item->u2.statements,depth+1);
314 for (i=0; i<depth; i++) {
315 fprintf(fin,"\t"); /* depth == indentation */
316 }
317 if ( item->u3.else_statements )
318 fprintf(fin,"}\n");
319 else
320 fprintf(fin,"};\n");
321 } else if (item->u2.statements ) {
322 print_pval_list(fin,item->u2.statements,depth+1);
323 } else {
324 if (item->u3.else_statements )
325 fprintf(fin, " {} ");
326 else
327 fprintf(fin, " {}; ");
328 }
329 if ( item->u3.else_statements ) {
330 for (i=0; i<depth; i++) {
331 fprintf(fin,"\t"); /* depth == indentation */
332 }
333 fprintf(fin,"else\n");
334 print_pval_list(fin,item->u3.else_statements, depth);
335 }
336 break;
337
338 case PV_SWITCH:
339 fprintf(fin,"switch( %s ) {\n", item->u1.str);
340 print_pval_list(fin,item->u2.statements,depth+1);
341 for (i=0; i<depth; i++) {
342 fprintf(fin,"\t"); /* depth == indentation */
343 }
344 fprintf(fin,"}\n");
345 break;
346
347 case PV_EXTENSION:
348 if ( item->u4.regexten )
349 fprintf(fin, "regexten ");
350 if ( item->u3.hints )
351 fprintf(fin,"hints(%s) ", item->u3.hints);
352
353 fprintf(fin,"%s => ", item->u1.str);
354 print_pval_list(fin,item->u2.statements,depth+1);
355 fprintf(fin,"\n");
356 break;
357
358 case PV_IGNOREPAT:
359 fprintf(fin,"ignorepat => %s;\n", item->u1.str);
360 break;
361
362 case PV_GLOBALS:
363 fprintf(fin,"globals {\n");
364 print_pval_list(fin,item->u1.statements,depth+1);
365 for (i=0; i<depth; i++) {
366 fprintf(fin,"\t"); /* depth == indentation */
367 }
368 fprintf(fin,"}\n");
369 break;
370 }
371}
372
373static void print_pval_list(FILE *fin, pval *item, int depth)
374{
375 pval *i;
376
377 for (i=item; i; i=i->next) {
378 print_pval(fin, i, depth);
379 }
380}
381
382void ael2_print(char *fname, pval *tree)
383{
384 FILE *fin = fopen(fname,"w");
385 if ( !fin ) {
386 ast_log(LOG_ERROR, "Couldn't open %s for writing.\n", fname);
387 return;
388 }
389 print_pval_list(fin, tree, 0);
390 fclose(fin);
391}
392
393
394/* EMPTY TEMPLATE FUNCS FOR AEL TRAVERSAL: ============================================================================= */
395
396void traverse_pval_template(pval *item, int depth);
397void traverse_pval_item_template(pval *item, int depth);
398
399
400void traverse_pval_item_template(pval *item, int depth)/* depth comes in handy for a pretty print (indentation),
401 but you may not need it */
402{
403 pval *lp;
404
405 switch ( item->type ) {
406 case PV_WORD:
407 /* fields: item->u1.str == string associated with this (word). */
408 break;
409
410 case PV_MACRO:
411 /* fields: item->u1.str == name of macro
412 item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
413 item->u2.arglist->u1.str == argument
414 item->u2.arglist->next == next arg
415
416 item->u3.macro_statements == pval list of statements in macro body.
417 */
418 for (lp=item->u2.arglist; lp; lp=lp->next) {
419
420 }
421 traverse_pval_item_template(item->u3.macro_statements,depth+1);
422 break;
423
424 case PV_CONTEXT:
425 /* fields: item->u1.str == name of context
426 item->u2.statements == pval list of statements in context body
427 item->u3.abstract == int 1 if an abstract keyword were present
428 */
429 traverse_pval_item_template(item->u2.statements,depth+1);
430 break;
431
432 case PV_MACRO_CALL:
433 /* fields: item->u1.str == name of macro to call
434 item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
435 item->u2.arglist->u1.str == argument
436 item->u2.arglist->next == next arg
437 */
438 for (lp=item->u2.arglist; lp; lp=lp->next) {
439 }
440 break;
441
443 /* fields: item->u1.str == name of application to call
444 item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
445 item->u2.arglist->u1.str == argument
446 item->u2.arglist->next == next arg
447 */
448 for (lp=item->u2.arglist; lp; lp=lp->next) {
449 }
450 break;
451
452 case PV_CASE:
453 /* fields: item->u1.str == value of case
454 item->u2.statements == pval list of statements under the case
455 */
456 traverse_pval_item_template(item->u2.statements,depth+1);
457 break;
458
459 case PV_PATTERN:
460 /* fields: item->u1.str == value of case
461 item->u2.statements == pval list of statements under the case
462 */
463 traverse_pval_item_template(item->u2.statements,depth+1);
464 break;
465
466 case PV_DEFAULT:
467 /* fields:
468 item->u2.statements == pval list of statements under the case
469 */
470 traverse_pval_item_template(item->u2.statements,depth+1);
471 break;
472
473 case PV_CATCH:
474 /* fields: item->u1.str == name of extension to catch
475 item->u2.statements == pval list of statements in context body
476 */
477 traverse_pval_item_template(item->u2.statements,depth+1);
478 break;
479
480 case PV_SWITCHES:
481 /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
482 */
483 traverse_pval_item_template(item->u1.list,depth+1);
484 break;
485
486 case PV_ESWITCHES:
487 /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
488 */
489 traverse_pval_item_template(item->u1.list,depth+1);
490 break;
491
492 case PV_INCLUDES:
493 /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
494 item->u2.arglist == pval list of 4 PV_WORD elements for time values
495 */
496 traverse_pval_item_template(item->u1.list,depth+1);
497 traverse_pval_item_template(item->u2.arglist,depth+1);
498 break;
499
501 /* fields: item->u1.list == pval list of statements in block, one per entry in the list
502 */
503 traverse_pval_item_template(item->u1.list,depth+1);
504 break;
505
506 case PV_LOCALVARDEC:
507 case PV_VARDEC:
508 /* fields: item->u1.str == variable name
509 item->u2.val == variable value to assign
510 */
511 break;
512
513 case PV_GOTO:
514 /* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user.
515 item->u1.list->u1.str == where the data on a PV_WORD will always be.
516 */
517
518 if ( item->u1.list->next )
519 ;
520 if ( item->u1.list->next && item->u1.list->next->next )
521 ;
522
523 break;
524
525 case PV_LABEL:
526 /* fields: item->u1.str == label name
527 */
528 break;
529
530 case PV_FOR:
531 /* fields: item->u1.for_init == a string containing the initializer
532 item->u2.for_test == a string containing the loop test
533 item->u3.for_inc == a string containing the loop increment
534
535 item->u4.for_statements == a pval list of statements in the for ()
536 */
537 traverse_pval_item_template(item->u4.for_statements,depth+1);
538 break;
539
540 case PV_WHILE:
541 /* fields: item->u1.str == the while conditional, as supplied by user
542
543 item->u2.statements == a pval list of statements in the while ()
544 */
545 traverse_pval_item_template(item->u2.statements,depth+1);
546 break;
547
548 case PV_BREAK:
549 /* fields: none
550 */
551 break;
552
553 case PV_RETURN:
554 /* fields: none
555 */
556 break;
557
558 case PV_CONTINUE:
559 /* fields: none
560 */
561 break;
562
563 case PV_IFTIME:
564 /* fields: item->u1.list == there are 4 linked PV_WORDs here.
565
566 item->u2.statements == a pval list of statements in the if ()
567 item->u3.else_statements == a pval list of statements in the else
568 (could be zero)
569 */
570 traverse_pval_item_template(item->u2.statements,depth+1);
571 if ( item->u3.else_statements ) {
572 traverse_pval_item_template(item->u3.else_statements,depth+1);
573 }
574 break;
575
576 case PV_RANDOM:
577 /* fields: item->u1.str == the random number expression, as supplied by user
578
579 item->u2.statements == a pval list of statements in the if ()
580 item->u3.else_statements == a pval list of statements in the else
581 (could be zero)
582 */
583 traverse_pval_item_template(item->u2.statements,depth+1);
584 if ( item->u3.else_statements ) {
585 traverse_pval_item_template(item->u3.else_statements,depth+1);
586 }
587 break;
588
589 case PV_IF:
590 /* fields: item->u1.str == the if conditional, as supplied by user
591
592 item->u2.statements == a pval list of statements in the if ()
593 item->u3.else_statements == a pval list of statements in the else
594 (could be zero)
595 */
596 traverse_pval_item_template(item->u2.statements,depth+1);
597 if ( item->u3.else_statements ) {
598 traverse_pval_item_template(item->u3.else_statements,depth+1);
599 }
600 break;
601
602 case PV_SWITCH:
603 /* fields: item->u1.str == the switch expression
604
605 item->u2.statements == a pval list of statements in the switch,
606 (will be case statements, most likely!)
607 */
608 traverse_pval_item_template(item->u2.statements,depth+1);
609 break;
610
611 case PV_EXTENSION:
612 /* fields: item->u1.str == the extension name, label, whatever it's called
613
614 item->u2.statements == a pval list of statements in the extension
615 item->u3.hints == a char * hint argument
616 item->u4.regexten == an int boolean. non-zero says that regexten was specified
617 */
618 traverse_pval_item_template(item->u2.statements,depth+1);
619 break;
620
621 case PV_IGNOREPAT:
622 /* fields: item->u1.str == the ignorepat data
623 */
624 break;
625
626 case PV_GLOBALS:
627 /* fields: item->u1.statements == pval list of statements, usually vardecs
628 */
629 traverse_pval_item_template(item->u1.statements,depth+1);
630 break;
631 }
632}
633
634void traverse_pval_template(pval *item, int depth) /* depth comes in handy for a pretty print (indentation),
635 but you may not need it */
636{
637 pval *i;
638
639 for (i=item; i; i=i->next) {
641 }
642}
643
644
645/* SEMANTIC CHECKING FOR AEL: ============================================================================= */
646
647/* (not all that is syntactically legal is good! */
648
649
650static void check_macro_returns(pval *macro)
651{
652 pval *i;
653 if (!macro->u3.macro_statements)
654 {
655 pval *z = calloc(1, sizeof(struct pval));
656 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The macro %s is empty! I will insert a return.\n",
657 macro->filename, macro->startline, macro->endline, macro->u1.str);
658
659 z->type = PV_RETURN;
660 z->startline = macro->startline;
661 z->endline = macro->endline;
662 z->startcol = macro->startcol;
663 z->endcol = macro->endcol;
664 z->filename = strdup(macro->filename);
665
666 macro->u3.macro_statements = z;
667 return;
668 }
669 for (i=macro->u3.macro_statements; i; i=i->next) {
670 /* if the last statement in the list is not return, then insert a return there */
671 if (i->next == NULL) {
672 if (i->type != PV_RETURN) {
673 pval *z = calloc(1, sizeof(struct pval));
674 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The macro %s does not end with a return; I will insert one.\n",
675 macro->filename, macro->startline, macro->endline, macro->u1.str);
676
677 z->type = PV_RETURN;
678 z->startline = macro->startline;
679 z->endline = macro->endline;
680 z->startcol = macro->startcol;
681 z->endcol = macro->endcol;
682 z->filename = strdup(macro->filename);
683
684 i->next = z;
685 return;
686 }
687 }
688 }
689 return;
690}
691
692
693
694static int extension_matches(pval *here, const char *exten, const char *pattern)
695{
696 int err1;
697 regex_t preg;
698
699 /* simple case, they match exactly, the pattern and exten name */
700 if (strcmp(pattern,exten) == 0)
701 return 1;
702
703 if (pattern[0] == '_') {
704 char reg1[2000];
705 const char *p;
706 char *r = reg1;
707
708 if ( strlen(pattern)*5 >= 2000 ) /* safety valve */ {
709 ast_log(LOG_ERROR,"Error: The pattern %s is way too big. Pattern matching cancelled.\n",
710 pattern);
711 return 0;
712 }
713 /* form a regular expression from the pattern, and then match it against exten */
714 *r++ = '^'; /* what if the extension is a pattern ?? */
715 *r++ = '_'; /* what if the extension is a pattern ?? */
716 *r++ = '?';
717 for (p=pattern+1; *p; p++) {
718 switch ( *p ) {
719 case 'X':
720 *r++ = '[';
721 *r++ = '0';
722 *r++ = '-';
723 *r++ = '9';
724 *r++ = 'X';
725 *r++ = ']';
726 break;
727
728 case 'Z':
729 *r++ = '[';
730 *r++ = '1';
731 *r++ = '-';
732 *r++ = '9';
733 *r++ = 'Z';
734 *r++ = ']';
735 break;
736
737 case 'N':
738 *r++ = '[';
739 *r++ = '2';
740 *r++ = '-';
741 *r++ = '9';
742 *r++ = 'N';
743 *r++ = ']';
744 break;
745
746 case '[':
747 while ( *p && *p != ']' ) {
748 *r++ = *p++;
749 }
750 *r++ = ']';
751 if ( *p != ']') {
752 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The extension pattern '%s' is missing a closing bracket \n",
753 here->filename, here->startline, here->endline, pattern);
754 }
755 break;
756
757 case '.':
758 case '!':
759 *r++ = '.';
760 *r++ = '*';
761 break;
762 case '*': /* regex metacharacter */
763 case '+': /* regex metacharacter */
764 *r++ = '\\';
765 /* fall through */
766 default:
767 *r++ = *p;
768 break;
769
770 }
771 }
772 *r++ = '$'; /* what if the extension is a pattern ?? */
773 *r++ = *p++; /* put in the closing null */
774 err1 = regcomp(&preg, reg1, REG_NOSUB|REG_EXTENDED);
775 if ( err1 ) {
776 char errmess[500];
777 regerror(err1,&preg,errmess,sizeof(errmess));
778 regfree(&preg);
779 ast_log(LOG_WARNING, "Regcomp of %s failed, error code %d\n",
780 reg1, err1);
781 return 0;
782 }
783 err1 = regexec(&preg, exten, 0, 0, 0);
784 regfree(&preg);
785
786 if ( err1 ) {
787 /* ast_log(LOG_NOTICE,"*****************************[%d]Extension %s did not match %s(%s)\n",
788 err1,exten, pattern, reg1); */
789 return 0; /* no match */
790 } else {
791 /* ast_log(LOG_NOTICE,"*****************************Extension %s matched %s\n",
792 exten, pattern); */
793 return 1;
794 }
795 }
796
797 return 0;
798}
799
800
801static void check_expr2_input(pval *expr, char *str)
802{
803 int spaces = strspn(str,"\t \n");
804 if ( !strncmp(str+spaces,"$[",2) ) {
805 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The expression '%s' is redundantly wrapped in '$[ ]'. \n",
806 expr->filename, expr->startline, expr->endline, str);
807 warns++;
808 }
809}
810
811static void check_includes(pval *includes)
812{
813 struct pval *p4;
814 for (p4=includes->u1.list; p4; p4=p4->next) {
815 /* for each context pointed to, find it, then find a context/label that matches the
816 target here! */
817 char *incl_context = p4->u1.str;
818 /* find a matching context name */
819 struct pval *that_other_context = find_context(incl_context);
820 if (!that_other_context && strcmp(incl_context, "parkedcalls") != 0) {
821 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The included context '%s' cannot be found.\n\
822 (You may ignore this warning if '%s' exists in extensions.conf, or is created by another module. I cannot check for those.)\n",
823 includes->filename, includes->startline, includes->endline, incl_context, incl_context);
824 warns++;
825 }
826 }
827}
828
829
830static void check_timerange(pval *p)
831{
832 char *times;
833 char *e;
834 int s1, s2;
835 int e1, e2;
836
837 times = ast_strdupa(p->u1.str);
838
839 /* Star is all times */
840 if (ast_strlen_zero(times) || !strcmp(times, "*")) {
841 return;
842 }
843 /* Otherwise expect a range */
844 e = strchr(times, '-');
845 if (!e) {
846 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The time range format (%s) requires a '-' surrounded by two 24-hour times of day!\n",
847 p->filename, p->startline, p->endline, times);
848 warns++;
849 return;
850 }
851 *e = '\0';
852 e++;
853 while (*e && !isdigit(*e))
854 e++;
855 if (!*e) {
856 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The time range format (%s) is missing the end time!\n",
857 p->filename, p->startline, p->endline, p->u1.str);
858 warns++;
859 }
860 if (sscanf(times, "%2d:%2d", &s1, &s2) != 2) {
861 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start time (%s) isn't quite right!\n",
862 p->filename, p->startline, p->endline, times);
863 warns++;
864 }
865 if (sscanf(e, "%2d:%2d", &e1, &e2) != 2) {
866 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end time (%s) isn't quite right!\n",
867 p->filename, p->startline, p->endline, times);
868 warns++;
869 }
870
871 s1 = s1 * 30 + s2/2;
872 if ((s1 < 0) || (s1 >= 24*30)) {
873 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start time (%s) is out of range!\n",
874 p->filename, p->startline, p->endline, times);
875 warns++;
876 }
877 e1 = e1 * 30 + e2/2;
878 if ((e1 < 0) || (e1 >= 24*30)) {
879 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end time (%s) is out of range!\n",
880 p->filename, p->startline, p->endline, e);
881 warns++;
882 }
883 return;
884}
885
886static char *days[] =
887{
888 "sun",
889 "mon",
890 "tue",
891 "wed",
892 "thu",
893 "fri",
894 "sat",
895};
896
897/*! \brief get_dow: Get day of week */
898static void check_dow(pval *DOW)
899{
900 char *dow;
901 char *c;
902 /* The following line is coincidence, really! */
903 int s, e;
904
905 dow = ast_strdupa(DOW->u1.str);
906
907 /* Check for all days */
908 if (ast_strlen_zero(dow) || !strcmp(dow, "*"))
909 return;
910 /* Get start and ending days */
911 c = strchr(dow, '-');
912 if (c) {
913 *c = '\0';
914 c++;
915 } else
916 c = NULL;
917 /* Find the start */
918 s = 0;
919 while ((s < 7) && strcasecmp(dow, days[s])) s++;
920 if (s >= 7) {
921 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The day (%s) must be one of 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', or 'sat'!\n",
922 DOW->filename, DOW->startline, DOW->endline, dow);
923 warns++;
924 }
925 if (c) {
926 e = 0;
927 while ((e < 7) && strcasecmp(c, days[e])) e++;
928 if (e >= 7) {
929 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end day (%s) must be one of 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', or 'sat'!\n",
930 DOW->filename, DOW->startline, DOW->endline, c);
931 warns++;
932 }
933 } else
934 e = s;
935}
936
937static void check_day(pval *DAY)
938{
939 char *day;
940 char *c;
941 /* The following line is coincidence, really! */
942 int s, e;
943
944 day = ast_strdupa(DAY->u1.str);
945
946 /* Check for all days */
947 if (ast_strlen_zero(day) || !strcmp(day, "*")) {
948 return;
949 }
950 /* Get start and ending days */
951 c = strchr(day, '-');
952 if (c) {
953 *c = '\0';
954 c++;
955 }
956 /* Find the start */
957 if (sscanf(day, "%2d", &s) != 1) {
958 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start day of month (%s) must be a number!\n",
959 DAY->filename, DAY->startline, DAY->endline, day);
960 warns++;
961 }
962 else if ((s < 1) || (s > 31)) {
963 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start day of month (%s) must be a number in the range [1-31]!\n",
964 DAY->filename, DAY->startline, DAY->endline, day);
965 warns++;
966 }
967 s--;
968 if (c) {
969 if (sscanf(c, "%2d", &e) != 1) {
970 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end day of month (%s) must be a number!\n",
971 DAY->filename, DAY->startline, DAY->endline, c);
972 warns++;
973 }
974 else if ((e < 1) || (e > 31)) {
975 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end day of month (%s) must be a number in the range [1-31]!\n",
976 DAY->filename, DAY->startline, DAY->endline, day);
977 warns++;
978 }
979 e--;
980 } else
981 e = s;
982}
983
984static char *months[] =
985{
986 "jan",
987 "feb",
988 "mar",
989 "apr",
990 "may",
991 "jun",
992 "jul",
993 "aug",
994 "sep",
995 "oct",
996 "nov",
997 "dec",
998};
999
1000static void check_month(pval *MON)
1001{
1002 char *mon;
1003 char *c;
1004 /* The following line is coincidence, really! */
1005 int s, e;
1006
1007 mon = ast_strdupa(MON->u1.str);
1008
1009 /* Check for all days */
1010 if (ast_strlen_zero(mon) || !strcmp(mon, "*"))
1011 return ;
1012 /* Get start and ending days */
1013 c = strchr(mon, '-');
1014 if (c) {
1015 *c = '\0';
1016 c++;
1017 }
1018 /* Find the start */
1019 s = 0;
1020 while ((s < 12) && strcasecmp(mon, months[s])) s++;
1021 if (s >= 12) {
1022 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start month (%s) must be a one of: 'jan', 'feb', ..., 'dec'!\n",
1023 MON->filename, MON->startline, MON->endline, mon);
1024 warns++;
1025 }
1026 if (c) {
1027 e = 0;
1028 while ((e < 12) && strcasecmp(mon, months[e])) e++;
1029 if (e >= 12) {
1030 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end month (%s) must be a one of: 'jan', 'feb', ..., 'dec'!\n",
1031 MON->filename, MON->startline, MON->endline, c);
1032 warns++;
1033 }
1034 } else
1035 e = s;
1036}
1037
1039{
1040 pval *p = item;
1041
1042 while( p && p->type != PV_MACRO && p->type != PV_CONTEXT ) /* early cutout, sort of */ {
1043 /* a break is allowed in WHILE, FOR, CASE, DEFAULT, PATTERN; otherwise, it don't make
1044 no sense */
1045 if( p->type == PV_CASE || p->type == PV_DEFAULT || p->type == PV_PATTERN
1046 || p->type == PV_WHILE || p->type == PV_FOR ) {
1047 return 1;
1048 }
1049 p = p->dad;
1050 }
1051 ast_log(LOG_ERROR,"Error: file %s, line %d-%d: 'break' not in switch, for, or while statement!\n",
1052 item->filename, item->startline, item->endline);
1053 errs++;
1054
1055 return 0;
1056}
1057
1059{
1060 pval *p = item;
1061
1062 while( p && p->type != PV_MACRO && p->type != PV_CONTEXT ) /* early cutout, sort of */ {
1063 /* a break is allowed in WHILE, FOR, CASE, DEFAULT, PATTERN; otherwise, it don't make
1064 no sense */
1065 if( p->type == PV_WHILE || p->type == PV_FOR ) {
1066 return 1;
1067 }
1068 p = p->dad;
1069 }
1070 ast_log(LOG_ERROR,"Error: file %s, line %d-%d: 'continue' not in 'for' or 'while' statement!\n",
1071 item->filename, item->startline, item->endline);
1072 errs++;
1073
1074 return 0;
1075}
1076
1077static struct pval *in_macro(pval *item)
1078{
1079 struct pval *curr;
1080 curr = item;
1081 while( curr ) {
1082 if( curr->type == PV_MACRO ) {
1083 return curr;
1084 }
1085 curr = curr->dad;
1086 }
1087 return 0;
1088}
1089
1090static struct pval *in_context(pval *item)
1091{
1092 struct pval *curr;
1093 curr = item;
1094 while( curr ) {
1095 if( curr->type == PV_MACRO || curr->type == PV_CONTEXT ) {
1096 return curr;
1097 }
1098 curr = curr->dad;
1099 }
1100 return 0;
1101}
1102
1103
1104/* general purpose goto finder */
1105
1106static void check_label(pval *item)
1107{
1108 struct pval *curr;
1109 struct pval *x;
1110 int alright = 0;
1111
1112 /* A label outside an extension just plain does not make sense! */
1113
1114 curr = item;
1115
1116 while( curr ) {
1117 if( curr->type == PV_MACRO || curr->type == PV_EXTENSION ) {
1118 alright = 1;
1119 break;
1120 }
1121 curr = curr->dad;
1122 }
1123 if( !alright )
1124 {
1125 ast_log(LOG_ERROR,"Error: file %s, line %d-%d: Label %s is not within an extension or macro!\n",
1126 item->filename, item->startline, item->endline, item->u1.str);
1127 errs++;
1128 }
1129
1130
1131 /* basically, ensure that a label is not repeated in a context. Period.
1132 The method: well, for each label, find the first label in the context
1133 with the same name. If it's not the current label, then throw an error. */
1134
1135
1136 /* printf("==== check_label: ====\n"); */
1137 if( !current_extension )
1138 curr = current_context;
1139 else
1140 curr = current_extension;
1141
1142 x = find_first_label_in_current_context((char *)item->u1.str, curr);
1143 /* printf("Hey, check_label found with item = %x, and x is %x, and currcont is %x, label name is %s\n", item,x, current_context, (char *)item->u1.str); */
1144 if( x && x != item )
1145 {
1146 ast_log(LOG_ERROR,"Error: file %s, line %d-%d: Duplicate label %s! Previously defined at file %s, line %d.\n",
1147 item->filename, item->startline, item->endline, item->u1.str, x->filename, x->startline);
1148 errs++;
1149 }
1150 /* printf("<<<<< check_label: ====\n"); */
1151}
1152
1154{
1155 /* just one item-- the label should be in the current extension */
1156 pval *curr_ext = get_extension_or_contxt(item); /* containing exten, or macro */
1157 pval *curr_cont;
1158
1159 if (!item->u1.list) {
1160 return NULL;
1161 }
1162
1163 if (!item->u1.list->next && !strstr((item->u1.list)->u1.str,"${")) {
1164 struct pval *x = find_label_in_current_extension((char*)((item->u1.list)->u1.str), curr_ext);
1165 return x;
1166 }
1167
1168 curr_cont = get_contxt(item);
1169
1170 /* TWO items */
1171 if (item->u1.list->next && !item->u1.list->next->next) {
1172 if (!strstr((item->u1.list)->u1.str,"${")
1173 && !strstr(item->u1.list->next->u1.str,"${") ) /* Don't try to match variables */ {
1174 struct pval *x = find_label_in_current_context((char *)item->u1.list->u1.str, (char *)item->u1.list->next->u1.str, curr_cont);
1175 return x;
1176 }
1177 }
1178
1179 /* All 3 items! */
1180 if (item->u1.list->next && item->u1.list->next->next) {
1181 /* all three */
1182 pval *first = item->u1.list;
1183 pval *second = item->u1.list->next;
1184 pval *third = item->u1.list->next->next;
1185
1186 if (!strstr((item->u1.list)->u1.str,"${")
1187 && !strstr(item->u1.list->next->u1.str,"${")
1188 && !strstr(item->u1.list->next->next->u1.str,"${")) /* Don't try to match variables */ {
1189 struct pval *x = find_label_in_current_db((char*)first->u1.str, (char*)second->u1.str, (char*)third->u1.str);
1190 if (!x) {
1191
1192 struct pval *p3;
1193 struct pval *that_context = find_context(item->u1.list->u1.str);
1194
1195 /* the target of the goto could be in an included context!! Fancy that!! */
1196 /* look for includes in the current context */
1197 if (that_context) {
1198 for (p3=that_context->u2.statements; p3; p3=p3->next) {
1199 if (p3->type == PV_INCLUDES) {
1200 struct pval *p4;
1201 for (p4=p3->u1.list; p4; p4=p4->next) {
1202 /* for each context pointed to, find it, then find a context/label that matches the
1203 target here! */
1204 char *incl_context = p4->u1.str;
1205 /* find a matching context name */
1206 struct pval *that_other_context = find_context(incl_context);
1207 if (that_other_context) {
1208 struct pval *x3;
1209 x3 = find_label_in_current_context((char *)item->u1.list->next->u1.str, (char *)item->u1.list->next->next->u1.str, that_other_context);
1210 if (x3) {
1211 return x3;
1212 }
1213 }
1214 }
1215 }
1216 }
1217 }
1218 }
1219 return x;
1220 }
1221 }
1222 return NULL;
1223}
1224
1225static void check_goto(pval *item)
1226{
1227 if (!item->u1.list) {
1228 return;
1229 }
1230
1231 /* check for the target of the goto-- does it exist? */
1232 if ( !(item->u1.list)->next && !(item->u1.list)->u1.str ) {
1233 ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto: empty label reference found!\n",
1234 item->filename, item->startline, item->endline);
1235 errs++;
1236 }
1237
1238 /* just one item-- the label should be in the current extension */
1239 if (!item->u1.list->next && !strstr(item->u1.list->u1.str,"${")) {
1240 struct pval *z = get_extension_or_contxt(item);
1241 struct pval *x = 0;
1242 if (z)
1243 x = find_label_in_current_extension((char*)((item->u1.list)->u1.str), z); /* if in macro, use current context instead */
1244 /* printf("Called find_label_in_current_extension with arg %s; current_extension is %x: %d\n",
1245 (char*)((item->u1.list)->u1.str), current_extension?current_extension:current_context, current_extension?current_extension->type:current_context->type); */
1246 if (!x) {
1247 ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto: no label %s exists in the current extension!\n",
1248 item->filename, item->startline, item->endline, item->u1.list->u1.str);
1249 errs++;
1250 }
1251 else
1252 return;
1253 }
1254
1255 /* TWO items */
1256 if (item->u1.list->next && !item->u1.list->next->next) {
1257 /* two items */
1258 /* printf("Calling find_label_in_current_context with args %s, %s\n",
1259 (char*)((item->u1.list)->u1.str), (char *)item->u1.list->next->u1.str); */
1260 if (!strstr((item->u1.list)->u1.str,"${")
1261 && !strstr(item->u1.list->next->u1.str,"${") ) /* Don't try to match variables */ {
1262 struct pval *z = get_contxt(item);
1263 struct pval *x = 0;
1264
1265 if (z)
1266 x = find_label_in_current_context((char *)item->u1.list->u1.str, (char *)item->u1.list->next->u1.str, z);
1267
1268 if (!x) {
1269 ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto: no label '%s,%s' exists in the current context, or any of its inclusions!\n",
1270 item->filename, item->startline, item->endline, item->u1.list->u1.str, item->u1.list->next->u1.str );
1271 errs++;
1272 }
1273 else
1274 return;
1275 }
1276 }
1277
1278 /* All 3 items! */
1279 if (item->u1.list->next && item->u1.list->next->next) {
1280 /* all three */
1281 pval *first = item->u1.list;
1282 pval *second = item->u1.list->next;
1283 pval *third = item->u1.list->next->next;
1284
1285 /* printf("Calling find_label_in_current_db with args %s, %s, %s\n",
1286 (char*)first->u1.str, (char*)second->u1.str, (char*)third->u1.str); */
1287 if (!strstr((item->u1.list)->u1.str,"${")
1288 && !strstr(item->u1.list->next->u1.str,"${")
1289 && !strstr(item->u1.list->next->next->u1.str,"${")) /* Don't try to match variables */ {
1290 struct pval *x = find_label_in_current_db((char*)first->u1.str, (char*)second->u1.str, (char*)third->u1.str);
1291 if (!x) {
1292 struct pval *p3;
1293 struct pval *found = 0;
1294 struct pval *that_context = find_context(item->u1.list->u1.str);
1295
1296 /* the target of the goto could be in an included context!! Fancy that!! */
1297 /* look for includes in the current context */
1298 if (that_context) {
1299 for (p3=that_context->u2.statements; p3; p3=p3->next) {
1300 if (p3->type == PV_INCLUDES) {
1301 struct pval *p4;
1302 for (p4=p3->u1.list; p4; p4=p4->next) {
1303 /* for each context pointed to, find it, then find a context/label that matches the
1304 target here! */
1305 char *incl_context = p4->u1.str;
1306 /* find a matching context name */
1307 struct pval *that_other_context = find_context(incl_context);
1308 if (that_other_context) {
1309 struct pval *x3;
1310 x3 = find_label_in_current_context((char *)item->u1.list->next->u1.str, (char *)item->u1.list->next->next->u1.str, that_other_context);
1311 if (x3) {
1312 found = x3;
1313 break;
1314 }
1315 }
1316 }
1317 }
1318 }
1319 if (!found) {
1320 ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto: no label %s|%s exists in the context %s or its inclusions!\n",
1321 item->filename, item->startline, item->endline, item->u1.list->next->u1.str, item->u1.list->next->next->u1.str, item->u1.list->u1.str );
1322 errs++;
1323 } else {
1324 struct pval *mac = in_macro(item); /* is this goto inside a macro? */
1325 if( mac ) { /* yes! */
1326 struct pval *targ = in_context(found);
1327 if( mac != targ )
1328 {
1329 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: It's bad form to have a goto in a macro to a target outside the macro!\n",
1330 item->filename, item->startline, item->endline);
1331 warns++;
1332 }
1333 }
1334 }
1335 } else {
1336 /* here is where code would go to check for target existence in extensions.conf files */
1337#ifdef STANDALONE
1338 struct pbx_find_info pfiq = {.stacklen = 0 };
1339 extern int localized_pbx_load_module(void);
1340 /* if this is a standalone, we will need to make sure the
1341 localized load of extensions.conf is done */
1342 if (!extensions_dot_conf_loaded) {
1344 extensions_dot_conf_loaded++;
1345 }
1346
1347 pbx_find_extension(NULL, NULL, &pfiq, first->u1.str, second->u1.str, atoi(third->u1.str),
1348 atoi(third->u1.str) ? NULL : third->u1.str, NULL,
1349 atoi(third->u1.str) ? E_MATCH : E_FINDLABEL);
1350
1351 if (pfiq.status != STATUS_SUCCESS) {
1352 ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: goto: Couldn't find goto target %s|%s|%s, not even in extensions.conf!\n",
1353 item->filename, item->startline, item->endline, first->u1.str, second->u1.str, third->u1.str);
1354 warns++;
1355 }
1356#else
1357 ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: goto: Couldn't find goto target %s|%s|%s in the AEL code!\n",
1358 item->filename, item->startline, item->endline, first->u1.str, second->u1.str, third->u1.str);
1359 warns++;
1360#endif
1361 }
1362 } else {
1363 struct pval *mac = in_macro(item); /* is this goto inside a macro? */
1364 if( mac ) { /* yes! */
1365 struct pval *targ = in_context(x);
1366 if( mac != targ )
1367 {
1368 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: It's bad form to have a goto in a macro to a target outside the macro!\n",
1369 item->filename, item->startline, item->endline);
1370 warns++;
1371 }
1372 }
1373 }
1374 }
1375 }
1376}
1377
1378
1379static void find_pval_goto_item(pval *item, int lev)
1380{
1381 struct pval *p4;
1382
1383 if (lev>100) {
1384 ast_log(LOG_ERROR,"find_pval_goto in infinite loop! item_type: %u\n\n", item->type);
1385 return;
1386 }
1387
1388 switch ( item->type ) {
1389 case PV_MACRO:
1390 /* fields: item->u1.str == name of macro
1391 item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
1392 item->u2.arglist->u1.str == argument
1393 item->u2.arglist->next == next arg
1394
1395 item->u3.macro_statements == pval list of statements in macro body.
1396 */
1397
1398 /* printf("Descending into macro %s at line %d\n", item->u1.str, item->startline); */
1399 find_pval_gotos(item->u3.macro_statements,lev+1); /* if we're just searching for a context, don't bother descending into them */
1400
1401 break;
1402
1403 case PV_CONTEXT:
1404 /* fields: item->u1.str == name of context
1405 item->u2.statements == pval list of statements in context body
1406 item->u3.abstract == int 1 if an abstract keyword were present
1407 */
1408 break;
1409
1410 case PV_CASE:
1411 /* fields: item->u1.str == value of case
1412 item->u2.statements == pval list of statements under the case
1413 */
1414 /* printf("Descending into Case of %s\n", item->u1.str); */
1415 find_pval_gotos(item->u2.statements,lev+1);
1416 break;
1417
1418 case PV_PATTERN:
1419 /* fields: item->u1.str == value of case
1420 item->u2.statements == pval list of statements under the case
1421 */
1422 /* printf("Descending into Pattern of %s\n", item->u1.str); */
1423 find_pval_gotos(item->u2.statements,lev+1);
1424 break;
1425
1426 case PV_DEFAULT:
1427 /* fields:
1428 item->u2.statements == pval list of statements under the case
1429 */
1430 /* printf("Descending into default\n"); */
1431 find_pval_gotos(item->u2.statements,lev+1);
1432 break;
1433
1434 case PV_CATCH:
1435 /* fields: item->u1.str == name of extension to catch
1436 item->u2.statements == pval list of statements in context body
1437 */
1438 /* printf("Descending into catch of %s\n", item->u1.str); */
1439 find_pval_gotos(item->u2.statements,lev+1);
1440 break;
1441
1442 case PV_STATEMENTBLOCK:
1443 /* fields: item->u1.list == pval list of statements in block, one per entry in the list
1444 */
1445 /* printf("Descending into statement block\n"); */
1446 find_pval_gotos(item->u1.list,lev+1);
1447 break;
1448
1449 case PV_GOTO:
1450 /* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user.
1451 item->u1.list->u1.str == where the data on a PV_WORD will always be.
1452 */
1453 check_goto(item); /* THE WHOLE FUNCTION OF THIS ENTIRE ROUTINE!!!! */
1454 break;
1455
1456 case PV_INCLUDES:
1457 /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
1458 */
1459 for (p4=item->u1.list; p4; p4=p4->next) {
1460 /* for each context pointed to, find it, then find a context/label that matches the
1461 target here! */
1462 char *incl_context = p4->u1.str;
1463 /* find a matching context name */
1464 struct pval *that_context = find_context(incl_context);
1465 if (that_context && that_context->u2.statements) {
1466 /* printf("Descending into include of '%s' at line %d; that_context=%s, that_context type=%d\n", incl_context, item->startline, that_context->u1.str, that_context->type); */
1467 find_pval_gotos(that_context->u2.statements,lev+1); /* keep working up the includes */
1468 }
1469 }
1470 break;
1471
1472 case PV_FOR:
1473 /* fields: item->u1.for_init == a string containing the initializer
1474 item->u2.for_test == a string containing the loop test
1475 item->u3.for_inc == a string containing the loop increment
1476
1477 item->u4.for_statements == a pval list of statements in the for ()
1478 */
1479 /* printf("Descending into for at line %d\n", item->startline); */
1480 find_pval_gotos(item->u4.for_statements,lev+1);
1481 break;
1482
1483 case PV_WHILE:
1484 /* fields: item->u1.str == the while conditional, as supplied by user
1485
1486 item->u2.statements == a pval list of statements in the while ()
1487 */
1488 /* printf("Descending into while at line %d\n", item->startline); */
1489 find_pval_gotos(item->u2.statements,lev+1);
1490 break;
1491
1492 case PV_RANDOM:
1493 /* fields: item->u1.str == the random number expression, as supplied by user
1494
1495 item->u2.statements == a pval list of statements in the if ()
1496 item->u3.else_statements == a pval list of statements in the else
1497 (could be zero)
1498 fall thru to PV_IF */
1499
1500 case PV_IFTIME:
1501 /* fields: item->u1.list == the time values, 4 of them, as PV_WORD structs in a list
1502
1503 item->u2.statements == a pval list of statements in the if ()
1504 item->u3.else_statements == a pval list of statements in the else
1505 (could be zero)
1506 fall thru to PV_IF*/
1507 case PV_IF:
1508 /* fields: item->u1.str == the if conditional, as supplied by user
1509
1510 item->u2.statements == a pval list of statements in the if ()
1511 item->u3.else_statements == a pval list of statements in the else
1512 (could be zero)
1513 */
1514 /* printf("Descending into random/iftime/if at line %d\n", item->startline); */
1515 find_pval_gotos(item->u2.statements,lev+1);
1516
1517 if (item->u3.else_statements) {
1518 /* printf("Descending into random/iftime/if's ELSE at line %d\n", item->startline); */
1519 find_pval_gotos(item->u3.else_statements,lev+1);
1520 }
1521 break;
1522
1523 case PV_SWITCH:
1524 /* fields: item->u1.str == the switch expression
1525
1526 item->u2.statements == a pval list of statements in the switch,
1527 (will be case statements, most likely!)
1528 */
1529 /* printf("Descending into switch at line %d\n", item->startline); */
1530 find_pval_gotos(item->u3.else_statements,lev+1);
1531 break;
1532
1533 case PV_EXTENSION:
1534 /* fields: item->u1.str == the extension name, label, whatever it's called
1535
1536 item->u2.statements == a pval list of statements in the extension
1537 item->u3.hints == a char * hint argument
1538 item->u4.regexten == an int boolean. non-zero says that regexten was specified
1539 */
1540
1541 /* printf("Descending into extension %s at line %d\n", item->u1.str, item->startline); */
1542 find_pval_gotos(item->u2.statements,lev+1);
1543 break;
1544
1545 default:
1546 break;
1547 }
1548}
1549
1550static void find_pval_gotos(pval *item,int lev)
1551{
1552 pval *i;
1553
1554 for (i=item; i; i=i->next) {
1555 /* printf("About to call pval_goto_item, itemcount=%d, itemtype=%d\n", item_count, i->type); */
1556 find_pval_goto_item(i, lev);
1557 }
1558}
1559
1560
1561
1562/* general purpose label finder */
1564{
1565 pval *x;
1566
1567 switch ( item->type ) {
1568 case PV_MACRO:
1569 /* fields: item->u1.str == name of macro
1570 item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
1571 item->u2.arglist->u1.str == argument
1572 item->u2.arglist->next == next arg
1573
1574 item->u3.macro_statements == pval list of statements in macro body.
1575 */
1576 /* printf(" matching in MACRO %s, match_context=%s; retoncontmtch=%d; \n", item->u1.str, match_context, return_on_context_match); */
1577 if (!strcmp(match_context,"*") || !strcmp(item->u1.str, match_context)) {
1578
1579 /* printf("MACRO: match context is: %s\n", match_context); */
1580
1581 if (return_on_context_match && !strcmp(item->u1.str, match_context)) /* if we're just searching for a context, don't bother descending into them */ {
1582 /* printf("Returning on matching macro %s\n", match_context); */
1583 return item;
1584 }
1585
1586
1588 /* printf("Descending into matching macro %s/%s\n", match_context, item->u1.str); */
1589 if ((x=match_pval(item->u3.macro_statements))) {
1590 /* printf("Responded with pval match %x\n", x); */
1591 return x;
1592 }
1593 }
1594 } else {
1595 /* printf("Skipping context/macro %s\n", item->u1.str); */
1596 }
1597
1598 break;
1599
1600 case PV_CONTEXT:
1601 /* fields: item->u1.str == name of context
1602 item->u2.statements == pval list of statements in context body
1603 item->u3.abstract == int 1 if an abstract keyword were present
1604 */
1605 /* printf(" matching in CONTEXT\n"); */
1606 if (!strcmp(match_context,"*") || !strcmp(item->u1.str, match_context)) {
1607 if (return_on_context_match && !strcmp(item->u1.str, match_context)) {
1608 /* printf("Returning on matching context %s\n", match_context); */
1609 /* printf("non-CONTEXT: Responded with pval match %x\n", x); */
1610 return item;
1611 }
1612
1614 /* printf("Descending into matching context %s\n", match_context); */
1615 if ((x=match_pval(item->u2.statements))) /* if we're just searching for a context, don't bother descending into them */ {
1616 /* printf("CONTEXT: Responded with pval match %x\n", x); */
1617 return x;
1618 }
1619 }
1620 } else {
1621 /* printf("Skipping context/macro %s\n", item->u1.str); */
1622 }
1623 break;
1624
1625 case PV_CASE:
1626 /* fields: item->u1.str == value of case
1627 item->u2.statements == pval list of statements under the case
1628 */
1629 /* printf(" matching in CASE\n"); */
1630 if ((x=match_pval(item->u2.statements))) {
1631 /* printf("CASE: Responded with pval match %x\n", x); */
1632 return x;
1633 }
1634 break;
1635
1636 case PV_PATTERN:
1637 /* fields: item->u1.str == value of case
1638 item->u2.statements == pval list of statements under the case
1639 */
1640 /* printf(" matching in PATTERN\n"); */
1641 if ((x=match_pval(item->u2.statements))) {
1642 /* printf("PATTERN: Responded with pval match %x\n", x); */
1643 return x;
1644 }
1645 break;
1646
1647 case PV_DEFAULT:
1648 /* fields:
1649 item->u2.statements == pval list of statements under the case
1650 */
1651 /* printf(" matching in DEFAULT\n"); */
1652 if ((x=match_pval(item->u2.statements))) {
1653 /* printf("DEFAULT: Responded with pval match %x\n", x); */
1654 return x;
1655 }
1656 break;
1657
1658 case PV_CATCH:
1659 /* fields: item->u1.str == name of extension to catch
1660 item->u2.statements == pval list of statements in context body
1661 */
1662 /* printf(" matching in CATCH\n"); */
1663 if ((x=match_pval(item->u2.statements))) {
1664 /* printf("CATCH: Responded with pval match %x\n", x); */
1665 return x;
1666 }
1667 break;
1668
1669 case PV_STATEMENTBLOCK:
1670 /* fields: item->u1.list == pval list of statements in block, one per entry in the list
1671 */
1672 /* printf(" matching in STATEMENTBLOCK\n"); */
1673 if ((x=match_pval(item->u1.list))) {
1674 /* printf("STATEMENTBLOCK: Responded with pval match %x\n", x); */
1675 return x;
1676 }
1677 break;
1678
1679 case PV_LABEL:
1680 /* fields: item->u1.str == label name
1681 */
1682 /* printf("PV_LABEL %s (cont=%s, exten=%s\n",
1683 item->u1.str, current_context->u1.str, (current_extension?current_extension->u1.str:"<macro>"));*/
1684
1685 if (count_labels) {
1686 if (!strcmp(match_label, item->u1.str)) {
1687 label_count++;
1689 }
1690
1691 } else {
1692 if (!strcmp(match_label, item->u1.str)) {
1693 /* printf("LABEL: Responded with pval match %x\n", x); */
1694 return item;
1695 }
1696 }
1697 break;
1698
1699 case PV_FOR:
1700 /* fields: item->u1.for_init == a string containing the initializer
1701 item->u2.for_test == a string containing the loop test
1702 item->u3.for_inc == a string containing the loop increment
1703
1704 item->u4.for_statements == a pval list of statements in the for ()
1705 */
1706 /* printf(" matching in FOR\n"); */
1707 if ((x=match_pval(item->u4.for_statements))) {
1708 /* printf("FOR: Responded with pval match %x\n", x);*/
1709 return x;
1710 }
1711 break;
1712
1713 case PV_WHILE:
1714 /* fields: item->u1.str == the while conditional, as supplied by user
1715
1716 item->u2.statements == a pval list of statements in the while ()
1717 */
1718 /* printf(" matching in WHILE\n"); */
1719 if ((x=match_pval(item->u2.statements))) {
1720 /* printf("WHILE: Responded with pval match %x\n", x); */
1721 return x;
1722 }
1723 break;
1724
1725 case PV_RANDOM:
1726 /* fields: item->u1.str == the random number expression, as supplied by user
1727
1728 item->u2.statements == a pval list of statements in the if ()
1729 item->u3.else_statements == a pval list of statements in the else
1730 (could be zero)
1731 fall thru to PV_IF */
1732
1733 case PV_IFTIME:
1734 /* fields: item->u1.list == the time values, 4 of them, as PV_WORD structs in a list
1735
1736 item->u2.statements == a pval list of statements in the if ()
1737 item->u3.else_statements == a pval list of statements in the else
1738 (could be zero)
1739 fall thru to PV_IF*/
1740 case PV_IF:
1741 /* fields: item->u1.str == the if conditional, as supplied by user
1742
1743 item->u2.statements == a pval list of statements in the if ()
1744 item->u3.else_statements == a pval list of statements in the else
1745 (could be zero)
1746 */
1747 /* printf(" matching in IF/IFTIME/RANDOM\n"); */
1748 if ((x=match_pval(item->u2.statements))) {
1749 return x;
1750 }
1751 if (item->u3.else_statements) {
1752 if ((x=match_pval(item->u3.else_statements))) {
1753 /* printf("IF/IFTIME/RANDOM: Responded with pval match %x\n", x); */
1754 return x;
1755 }
1756 }
1757 break;
1758
1759 case PV_SWITCH:
1760 /* fields: item->u1.str == the switch expression
1761
1762 item->u2.statements == a pval list of statements in the switch,
1763 (will be case statements, most likely!)
1764 */
1765 /* printf(" matching in SWITCH\n"); */
1766 if ((x=match_pval(item->u2.statements))) {
1767 /* printf("SWITCH: Responded with pval match %x\n", x); */
1768 return x;
1769 }
1770 break;
1771
1772 case PV_EXTENSION:
1773 /* fields: item->u1.str == the extension name, label, whatever it's called
1774
1775 item->u2.statements == a pval list of statements in the extension
1776 item->u3.hints == a char * hint argument
1777 item->u4.regexten == an int boolean. non-zero says that regexten was specified
1778 */
1779 /* printf(" matching in EXTENSION\n"); */
1780 if (!strcmp(match_exten,"*") || extension_matches(item, match_exten, item->u1.str) ) {
1781 /* printf("Descending into matching exten %s => %s\n", match_exten, item->u1.str); */
1782 if (strcmp(match_label,"1") == 0) {
1783 if (item->u2.statements) {
1784 struct pval *p5 = item->u2.statements;
1785 while (p5 && p5->type == PV_LABEL) /* find the first non-label statement in this context. If it exists, there's a "1" */
1786 p5 = p5->next;
1787 if (p5)
1788 return p5;
1789 else
1790 return 0;
1791 }
1792 else
1793 return 0;
1794 }
1795
1796 if ((x=match_pval(item->u2.statements))) {
1797 /* printf("EXTENSION: Responded with pval match %x\n", x); */
1798 return x;
1799 }
1800 } else {
1801 /* printf("Skipping exten %s\n", item->u1.str); */
1802 }
1803 break;
1804 default:
1805 /* printf(" matching in default = %d\n", item->type); */
1806 break;
1807 }
1808 return 0;
1809}
1810
1812{
1813 pval *i;
1814
1815 for (i=item; i; i=i->next) {
1816 pval *x;
1817 /* printf(" -- match pval: item %d\n", i->type); */
1818
1819 if ((x = match_pval_item(i))) {
1820 /* printf("match_pval: returning x=%x\n", (int)x); */
1821 return x; /* cut the search short */
1822 }
1823 }
1824 return 0;
1825}
1826
1827#if 0
1828int count_labels_in_current_context(char *label)
1829{
1830 label_count = 0;
1831 count_labels = 1;
1834
1835 return label_count;
1836}
1837#endif
1838
1839struct pval *find_first_label_in_current_context(char *label, pval *curr_cont)
1840{
1841 /* printf(" --- Got args %s, %s\n", exten, label); */
1842 struct pval *ret;
1843 struct pval *p3;
1844
1845 count_labels = 0;
1847 match_context = "*";
1848 match_exten = "*";
1849 match_label = label;
1850
1851 ret = match_pval(curr_cont);
1852 if (ret)
1853 return ret;
1854
1855 /* the target of the goto could be in an included context!! Fancy that!! */
1856 /* look for includes in the current context */
1857 for (p3=curr_cont->u2.statements; p3; p3=p3->next) {
1858 if (p3->type == PV_INCLUDES) {
1859 struct pval *p4;
1860 for (p4=p3->u1.list; p4; p4=p4->next) {
1861 /* for each context pointed to, find it, then find a context/label that matches the
1862 target here! */
1863 char *incl_context = p4->u1.str;
1864 /* find a matching context name */
1865 struct pval *that_context = find_context(incl_context);
1866 if (that_context) {
1867 struct pval *x3;
1868 x3 = find_first_label_in_current_context(label, that_context);
1869 if (x3) {
1870 return x3;
1871 }
1872 }
1873 }
1874 }
1875 }
1876 return 0;
1877}
1878
1879struct pval *find_label_in_current_context(char *exten, char *label, pval *curr_cont)
1880{
1881 /* printf(" --- Got args %s, %s\n", exten, label); */
1882 struct pval *ret;
1883 struct pval *p3;
1884
1885 count_labels = 0;
1887 match_context = "*";
1888 match_exten = exten;
1889 match_label = label;
1890 ret = match_pval(curr_cont->u2.statements);
1891 if (ret)
1892 return ret;
1893
1894 /* the target of the goto could be in an included context!! Fancy that!! */
1895 /* look for includes in the current context */
1896 for (p3=curr_cont->u2.statements; p3; p3=p3->next) {
1897 if (p3->type == PV_INCLUDES) {
1898 struct pval *p4;
1899 for (p4=p3->u1.list; p4; p4=p4->next) {
1900 /* for each context pointed to, find it, then find a context/label that matches the
1901 target here! */
1902 char *incl_context = p4->u1.str;
1903 /* find a matching context name */
1904 struct pval *that_context = find_context(incl_context);
1905 if (that_context) {
1906 struct pval *x3;
1907 x3 = find_label_in_current_context(exten, label, that_context);
1908 if (x3) {
1909 return x3;
1910 }
1911 }
1912 }
1913 }
1914 }
1915 return 0;
1916}
1917
1918static struct pval *find_label_in_current_extension(const char *label, pval *curr_ext)
1919{
1920 /* printf(" --- Got args %s\n", label); */
1921 count_labels = 0;
1923 match_context = "*";
1924 match_exten = "*";
1925 match_label = label;
1926 return match_pval(curr_ext);
1927}
1928
1929static struct pval *find_label_in_current_db(const char *context, const char *exten, const char *label)
1930{
1931 /* printf(" --- Got args %s, %s, %s\n", context, exten, label); */
1932 count_labels = 0;
1934
1936 match_exten = exten;
1937 match_label = label;
1938
1939 return match_pval(current_db);
1940}
1941
1942
1943struct pval *find_macro(char *name)
1944{
1946 count_labels = 0;
1948 match_exten = "*"; /* don't really need to set these, shouldn't be reached */
1949 match_label = "*";
1950 return match_pval(current_db);
1951}
1952
1953struct pval *find_context(char *name)
1954{
1956 count_labels = 0;
1958 match_exten = "*"; /* don't really need to set these, shouldn't be reached */
1959 match_label = "*";
1960 return match_pval(current_db);
1961}
1962
1963int is_float(char *arg )
1964{
1965 char *s;
1966 for (s=arg; *s; s++) {
1967 if (*s != '.' && (*s < '0' || *s > '9'))
1968 return 0;
1969 }
1970 return 1;
1971}
1972int is_int(char *arg )
1973{
1974 char *s;
1975 for (s=arg; *s; s++) {
1976 if (*s < '0' || *s > '9')
1977 return 0;
1978 }
1979 return 1;
1980}
1981int is_empty(char *arg)
1982{
1983 if (!arg)
1984 return 1;
1985 if (*arg == 0)
1986 return 1;
1987 while (*arg) {
1988 if (*arg != ' ' && *arg != '\t')
1989 return 0;
1990 arg++;
1991 }
1992 return 1;
1993}
1994
1995#ifdef AAL_ARGCHECK
1996int option_matches_j( struct argdesc *should, pval *is, struct argapp *app)
1997{
1998 struct argchoice *ac;
1999 char *opcop,*q,*p;
2000
2001 switch (should->dtype) {
2002 case ARGD_OPTIONSET:
2003 if ( strstr(is->u1.str,"${") )
2004 return 0; /* no checking anything if there's a var reference in there! */
2005
2006 opcop = ast_strdupa(is->u1.str);
2007
2008 for (q=opcop;*q;q++) { /* erase the innards of X(innard) type arguments, so we don't get confused later */
2009 if ( *q == '(' ) {
2010 p = q+1;
2011 while (*p && *p != ')' )
2012 *p++ = '+';
2013 q = p+1;
2014 }
2015 }
2016
2017 for (ac=app->opts; ac; ac=ac->next) {
2018 if (strlen(ac->name)>1 && strchr(ac->name,'(') == 0 && strcmp(ac->name,is->u1.str) == 0) /* multichar option, no parens, and a match? */
2019 return 0;
2020 }
2021 for (ac=app->opts; ac; ac=ac->next) {
2022 if (strlen(ac->name)==1 || strchr(ac->name,'(')) {
2023 char *p = strchr(opcop,ac->name[0]); /* wipe out all matched options in the user-supplied string */
2024
2025 if (p && *p == 'j') {
2026 ast_log(LOG_ERROR, "Error: file %s, line %d-%d: The j option in the %s application call is not appropriate for AEL!\n",
2027 is->filename, is->startline, is->endline, app->name);
2028 errs++;
2029 }
2030
2031 if (p) {
2032 *p = '+';
2033 if (ac->name[1] == '(') {
2034 if (*(p+1) != '(') {
2035 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The %c option in the %s application call should have an (argument), but doesn't!\n",
2036 is->filename, is->startline, is->endline, ac->name[0], app->name);
2037 warns++;
2038 }
2039 }
2040 }
2041 }
2042 }
2043 for (q=opcop; *q; q++) {
2044 if ( *q != '+' && *q != '(' && *q != ')') {
2045 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The %c option in the %s application call is not available as an option!\n",
2046 is->filename, is->startline, is->endline, *q, app->name);
2047 warns++;
2048 }
2049 }
2050 return 1;
2051 break;
2052 default:
2053 return 0;
2054 }
2055
2056}
2057
2058int option_matches( struct argdesc *should, pval *is, struct argapp *app)
2059{
2060 struct argchoice *ac;
2061 char *opcop;
2062
2063 switch (should->dtype) {
2064 case ARGD_STRING:
2065 if (is_empty(is->u1.str) && should->type == ARGD_REQUIRED)
2066 return 0;
2067 if (is->u1.str && strlen(is->u1.str) > 0) /* most will match */
2068 return 1;
2069 break;
2070
2071 case ARGD_INT:
2072 if (is_int(is->u1.str))
2073 return 1;
2074 else
2075 return 0;
2076 break;
2077
2078 case ARGD_FLOAT:
2079 if (is_float(is->u1.str))
2080 return 1;
2081 else
2082 return 0;
2083 break;
2084
2085 case ARGD_ENUM:
2086 if( !is->u1.str || strlen(is->u1.str) == 0 )
2087 return 1; /* a null arg in the call will match an enum, I guess! */
2088 for (ac=should->choices; ac; ac=ac->next) {
2089 if (strcmp(ac->name,is->u1.str) == 0)
2090 return 1;
2091 }
2092 return 0;
2093 break;
2094
2095 case ARGD_OPTIONSET:
2096 opcop = ast_strdupa(is->u1.str);
2097
2098 for (ac=app->opts; ac; ac=ac->next) {
2099 if (strlen(ac->name)>1 && strchr(ac->name,'(') == 0 && strcmp(ac->name,is->u1.str) == 0) /* multichar option, no parens, and a match? */
2100 return 1;
2101 }
2102 for (ac=app->opts; ac; ac=ac->next) {
2103 if (strlen(ac->name)==1 || strchr(ac->name,'(')) {
2104 char *p = strchr(opcop,ac->name[0]); /* wipe out all matched options in the user-supplied string */
2105
2106 if (p) {
2107 *p = '+';
2108 if (ac->name[1] == '(') {
2109 if (*(p+1) == '(') {
2110 char *q = p+1;
2111 while (*q && *q != ')') {
2112 *q++ = '+';
2113 }
2114 *q = '+';
2115 }
2116 }
2117 }
2118 }
2119 }
2120 return 1;
2121 break;
2122 case ARGD_VARARG:
2123 return 1; /* matches anything */
2124 break;
2125 }
2126 return 1; /* unless some for-sure match or non-match returns, then it must be close enough ... */
2127}
2128#endif
2129
2130int check_app_args(pval* appcall, pval *arglist, struct argapp *app)
2131{
2132#ifdef AAL_ARGCHECK
2133 struct argdesc *ad = app->args;
2134 pval *pa;
2135 int z;
2136
2137 for (pa = arglist; pa; pa=pa->next) {
2138 if (!ad) {
2139 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: Extra argument %s not in application call to %s !\n",
2140 arglist->filename, arglist->startline, arglist->endline, pa->u1.str, app->name);
2141 warns++;
2142 return 1;
2143 } else {
2144 /* find the first entry in the ad list that will match */
2145 do {
2146 if ( ad->dtype == ARGD_VARARG ) /* once we hit the VARARG, all bets are off. Discontinue the comparisons */
2147 break;
2148
2149 z= option_matches( ad, pa, app);
2150 if (!z) {
2151 if ( !arglist )
2152 arglist=appcall;
2153
2154 if (ad->type == ARGD_REQUIRED) {
2155 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: Required argument %s not in application call to %s !\n",
2156 arglist->filename, arglist->startline, arglist->endline, ad->dtype==ARGD_OPTIONSET?"options":ad->name, app->name);
2157 warns++;
2158 return 1;
2159 }
2160 } else if (z && ad->dtype == ARGD_OPTIONSET) {
2161 option_matches_j( ad, pa, app);
2162 }
2163 ad = ad->next;
2164 } while (ad && !z);
2165 }
2166 }
2167 /* any app nodes left, that are not optional? */
2168 for ( ; ad; ad=ad->next) {
2169 if (ad->type == ARGD_REQUIRED && ad->dtype != ARGD_VARARG) {
2170 if ( !arglist )
2171 arglist=appcall;
2172 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: Required argument %s not in application call to %s !\n",
2173 arglist->filename, arglist->startline, arglist->endline, ad->dtype==ARGD_OPTIONSET?"options":ad->name, app->name);
2174 warns++;
2175 return 1;
2176 }
2177 }
2178 return 0;
2179#else
2180 return 0;
2181#endif
2182}
2183
2185{
2186#ifdef AAL_ARGCHECK
2187 /* get and clean the variable name */
2188 char *buff1, *p;
2189 struct argapp *a,*a2;
2190 struct appsetvar *v,*v2;
2191 struct argchoice *c;
2192 pval *t;
2193
2194 p = item->u1.str;
2195 while (p && *p && (*p == ' ' || *p == '\t' || *p == '$' || *p == '{' ) )
2196 p++;
2197
2198 buff1 = ast_strdupa(p);
2199
2200 while (strlen(buff1) > 0 && ( buff1[strlen(buff1)-1] == '}' || buff1[strlen(buff1)-1] == ' ' || buff1[strlen(buff1)-1] == '\t'))
2201 buff1[strlen(buff1)-1] = 0;
2202 /* buff1 now contains the variable name */
2203 v = 0;
2204 for (a=apps; a; a=a->next) {
2205 for (v=a->setvars;v;v=v->next) {
2206 if (strcmp(v->name,buff1) == 0) {
2207 break;
2208 }
2209 }
2210 if ( v )
2211 break;
2212 }
2213 if (v && v->vals) {
2214 /* we have a match, to a variable that has a set of determined values */
2215 int def= 0;
2216 int pat = 0;
2217 int f1 = 0;
2218
2219 /* first of all, does this switch have a default case ? */
2220 for (t=item->u2.statements; t; t=t->next) {
2221 if (t->type == PV_DEFAULT) {
2222 def =1;
2223 break;
2224 }
2225 if (t->type == PV_PATTERN) {
2226 pat++;
2227 }
2228 }
2229 if (def || pat) /* nothing to check. All cases accounted for! */
2230 return;
2231 for (c=v->vals; c; c=c->next) {
2232 f1 = 0;
2233 for (t=item->u2.statements; t; t=t->next) {
2234 if (t->type == PV_CASE || t->type == PV_PATTERN) {
2235 if (!strcmp(t->u1.str,c->name)) {
2236 f1 = 1;
2237 break;
2238 }
2239 }
2240 }
2241 if (!f1) {
2242 ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: switch with expression(%s) does not handle the case of %s !\n",
2243 item->filename, item->startline, item->endline, item->u1.str, c->name);
2244 warns++;
2245 }
2246 }
2247 /* next, is there an app call in the current exten, that would set this var? */
2248 f1 = 0;
2250 if ( t && t->type == PV_STATEMENTBLOCK )
2251 t = t->u1.statements;
2252 for (; t && t != item; t=t->next) {
2253 if (t->type == PV_APPLICATION_CALL) {
2254 /* find the application that matches the u1.str */
2255 for (a2=apps; a2; a2=a2->next) {
2256 if (strcasecmp(a2->name, t->u1.str)==0) {
2257 for (v2=a2->setvars; v2; v2=v2->next) {
2258 if (strcmp(v2->name, buff1) == 0) {
2259 /* found an app that sets the var */
2260 f1 = 1;
2261 break;
2262 }
2263 }
2264 }
2265 if (f1)
2266 break;
2267 }
2268 }
2269 if (f1)
2270 break;
2271 }
2272
2273 /* see if it sets the var */
2274 if (!f1) {
2275 ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: Couldn't find an application call in this extension that sets the expression (%s) value!\n",
2276 item->filename, item->startline, item->endline, item->u1.str);
2277 warns++;
2278 }
2279 }
2280#else
2281 pval *t,*tl=0,*p2;
2282 int def= 0;
2283
2284 /* first of all, does this switch have a default case ? */
2285 for (t=item->u2.statements; t; t=t->next) {
2286 if (t->type == PV_DEFAULT) {
2287 def =1;
2288 break;
2289 }
2290 tl = t;
2291 }
2292 if (def) /* nothing to check. All cases accounted for! */
2293 return;
2294 /* if no default, warn and insert a default case at the end */
2295 p2 = tl->next = calloc(1, sizeof(struct pval));
2296
2297 p2->type = PV_DEFAULT;
2298 p2->startline = tl->startline;
2299 p2->endline = tl->endline;
2300 p2->startcol = tl->startcol;
2301 p2->endcol = tl->endcol;
2302 p2->filename = strdup(tl->filename);
2303 ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: A default case was automatically added to the switch.\n",
2304 p2->filename, p2->startline, p2->endline);
2305 warns++;
2306
2307#endif
2308}
2309
2310static void check_context_names(void)
2311{
2312 pval *i,*j;
2313 for (i=current_db; i; i=i->next) {
2314 if (i->type == PV_CONTEXT || i->type == PV_MACRO) {
2315 for (j=i->next; j; j=j->next) {
2316 if ( j->type == PV_CONTEXT || j->type == PV_MACRO ) {
2317 if ( !strcmp(i->u1.str, j->u1.str) && !(i->u3.abstract&2) && !(j->u3.abstract&2) )
2318 {
2319 ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: The context name (%s) is also declared in file %s, line %d-%d! (and neither is marked 'extend')\n",
2320 i->filename, i->startline, i->endline, i->u1.str, j->filename, j->startline, j->endline);
2321 warns++;
2322 }
2323 }
2324 }
2325 }
2326 }
2327}
2328
2329static void check_abstract_reference(pval *abstract_context)
2330{
2331 pval *i,*j;
2332 /* find some context includes that reference this context */
2333
2334
2335 /* otherwise, print out a warning */
2336 for (i=current_db; i; i=i->next) {
2337 if (i->type == PV_CONTEXT) {
2338 for (j=i->u2. statements; j; j=j->next) {
2339 if ( j->type == PV_INCLUDES ) {
2340 struct pval *p4;
2341 for (p4=j->u1.list; p4; p4=p4->next) {
2342 /* for each context pointed to, find it, then find a context/label that matches the
2343 target here! */
2344 if ( !strcmp(p4->u1.str, abstract_context->u1.str) )
2345 return; /* found a match! */
2346 }
2347 }
2348 }
2349 }
2350 }
2351 ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: Couldn't find a reference to this abstract context (%s) in any other context!\n",
2352 abstract_context->filename, abstract_context->startline, abstract_context->endline, abstract_context->u1.str);
2353 warns++;
2354}
2355
2356
2357void check_pval_item(pval *item, struct argapp *apps, int in_globals)
2358{
2359 pval *lp;
2360#ifdef AAL_ARGCHECK
2361 struct argapp *app, *found;
2362#endif
2363 struct pval *macro_def;
2364 struct pval *app_def;
2365
2366 char errmsg[4096];
2367 char *strp;
2368
2369 switch (item->type) {
2370 case PV_WORD:
2371 /* fields: item->u1.str == string associated with this (word).
2372 item->u2.arglist == pval list of 4 PV_WORD elements for time values (only in PV_INCLUDES) */
2373 break;
2374
2375 case PV_MACRO:
2376 /* fields: item->u1.str == name of macro
2377 item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
2378 item->u2.arglist->u1.str == argument
2379 item->u2.arglist->next == next arg
2380
2381 item->u3.macro_statements == pval list of statements in macro body.
2382 */
2386
2388
2389 for (lp=item->u2.arglist; lp; lp=lp->next) {
2390
2391 }
2392 check_pval(item->u3.macro_statements, apps,in_globals);
2393 break;
2394
2395 case PV_CONTEXT:
2396 /* fields: item->u1.str == name of context
2397 item->u2.statements == pval list of statements in context body
2398 item->u3.abstract == int 1 if an abstract keyword were present
2399 */
2402 if ( item->u3.abstract ) {
2405 } else
2407 check_pval(item->u2.statements, apps,in_globals);
2408 break;
2409
2410 case PV_MACRO_CALL:
2411 /* fields: item->u1.str == name of macro to call
2412 item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
2413 item->u2.arglist->u1.str == argument
2414 item->u2.arglist->next == next arg
2415 */
2416#ifdef STANDALONE
2417 /* if this is a standalone, we will need to make sure the
2418 localized load of extensions.conf is done */
2419 if (!extensions_dot_conf_loaded) {
2421 extensions_dot_conf_loaded++;
2422 }
2423#endif
2424 macro_def = find_macro(item->u1.str);
2425 if (!macro_def) {
2426#ifdef STANDALONE
2427 struct pbx_find_info pfiq = {.stacklen = 0 };
2428 struct pbx_find_info pfiq2 = {.stacklen = 0 };
2429
2430 /* look for the macro in the extensions.conf world */
2431 pbx_find_extension(NULL, NULL, &pfiq, item->u1.str, "s", 1, NULL, NULL, E_MATCH);
2432
2433 if (pfiq.status != STATUS_SUCCESS) {
2434 char namebuf2[256];
2435 snprintf(namebuf2, 256, "macro-%s", item->u1.str);
2436
2437 /* look for the macro in the extensions.conf world */
2438 pbx_find_extension(NULL, NULL, &pfiq2, namebuf2, "s", 1, NULL, NULL, E_MATCH);
2439
2440 if (pfiq2.status == STATUS_SUCCESS) {
2441 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: macro call to non-existent %s! (macro-%s was found in the extensions.conf stuff, but we are using gosubs!)\n",
2442 item->filename, item->startline, item->endline, item->u1.str, item->u1.str);
2443 warns++;
2444 } else {
2445 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: macro call to non-existent %s! (Not even in the extensions.conf stuff!)\n",
2446 item->filename, item->startline, item->endline, item->u1.str);
2447 warns++;
2448 }
2449 }
2450#else
2451 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: macro call to %s cannot be found in the AEL code!\n",
2452 item->filename, item->startline, item->endline, item->u1.str);
2453 warns++;
2454
2455#endif
2456#ifdef THIS_IS_1DOT4
2457 char namebuf2[256];
2458 snprintf(namebuf2, 256, "macro-%s", item->u1.str);
2459
2460 /* look for the macro in the extensions.conf world */
2461 pbx_find_extension(NULL, NULL, &pfiq, namebuf2, "s", 1, NULL, NULL, E_MATCH);
2462
2463 if (pfiq.status != STATUS_SUCCESS) {
2464 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: macro call to %s was not found in the AEL, nor the extensions.conf !\n",
2465 item->filename, item->startline, item->endline, item->u1.str);
2466 warns++;
2467 }
2468
2469#endif
2470
2471 } else if (macro_def->type != PV_MACRO) {
2472 ast_log(LOG_ERROR,"Error: file %s, line %d-%d: macro call to %s references a context, not a macro!\n",
2473 item->filename, item->startline, item->endline, item->u1.str);
2474 errs++;
2475 } else {
2476 /* macro_def is a MACRO, so do the args match in number? */
2477 int hereargs = 0;
2478 int thereargs = 0;
2479
2480 for (lp=item->u2.arglist; lp; lp=lp->next) {
2481 hereargs++;
2482 }
2483 for (lp=macro_def->u2.arglist; lp; lp=lp->next) {
2484 thereargs++;
2485 }
2486 if (hereargs != thereargs ) {
2487 ast_log(LOG_ERROR, "Error: file %s, line %d-%d: The macro call to %s has %d arguments, but the macro definition has %d arguments\n",
2488 item->filename, item->startline, item->endline, item->u1.str, hereargs, thereargs);
2489 errs++;
2490 }
2491 }
2492 break;
2493
2495 /* fields: item->u1.str == name of application to call
2496 item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
2497 item->u2.arglist->u1.str == argument
2498 item->u2.arglist->next == next arg
2499 */
2500 /* Need to check to see if the application is available! */
2501 app_def = find_context(item->u1.str);
2502 if (app_def && app_def->type == PV_MACRO) {
2503 ast_log(LOG_ERROR,"Error: file %s, line %d-%d: application call to %s references an existing macro, but had no & preceding it!\n",
2504 item->filename, item->startline, item->endline, item->u1.str);
2505 errs++;
2506 }
2507 if (strcasecmp(item->u1.str,"GotoIf") == 0
2508 || strcasecmp(item->u1.str,"GotoIfTime") == 0
2509 || strcasecmp(item->u1.str,"while") == 0
2510 || strcasecmp(item->u1.str,"endwhile") == 0
2511 || strcasecmp(item->u1.str,"random") == 0
2512 || strcasecmp(item->u1.str,"gosub") == 0
2513 || strcasecmp(item->u1.str,"gosubif") == 0
2514 || strcasecmp(item->u1.str,"continuewhile") == 0
2515 || strcasecmp(item->u1.str,"endwhile") == 0
2516 || strcasecmp(item->u1.str,"execif") == 0
2517 || strcasecmp(item->u1.str,"execiftime") == 0
2518 || strcasecmp(item->u1.str,"exitwhile") == 0
2519 || strcasecmp(item->u1.str,"goto") == 0
2520 || strcasecmp(item->u1.str,"macro") == 0
2521 || strcasecmp(item->u1.str,"macroexclusive") == 0
2522 || strcasecmp(item->u1.str,"macroif") == 0
2523 || strcasecmp(item->u1.str,"stackpop") == 0
2524 || strcasecmp(item->u1.str,"execIf") == 0 ) {
2525 ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: application call to %s affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead!\n",
2526 item->filename, item->startline, item->endline, item->u1.str);
2527 warns++;
2528 }
2529 if (strcasecmp(item->u1.str,"macroexit") == 0) {
2530 ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: I am converting the MacroExit call here to a return statement.\n",
2531 item->filename, item->startline, item->endline);
2532 item->type = PV_RETURN;
2533 free(item->u1.str);
2534 item->u1.str = 0;
2535 }
2536
2537#ifdef AAL_ARGCHECK
2538 found = 0;
2539 for (app=apps; app; app=app->next) {
2540 if (strcasecmp(app->name, item->u1.str) == 0) {
2541 found =app;
2542 break;
2543 }
2544 }
2545 if (!found) {
2546 ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: application call to %s not listed in applist database!\n",
2547 item->filename, item->startline, item->endline, item->u1.str);
2548 warns++;
2549 } else
2550 check_app_args(item, item->u2.arglist, app);
2551#endif
2552 break;
2553
2554 case PV_CASE:
2555 /* fields: item->u1.str == value of case
2556 item->u2.statements == pval list of statements under the case
2557 */
2558 /* Make sure sequence of statements under case is terminated with goto, return, or break */
2559 /* find the last statement */
2560 check_pval(item->u2.statements, apps,in_globals);
2561 break;
2562
2563 case PV_PATTERN:
2564 /* fields: item->u1.str == value of case
2565 item->u2.statements == pval list of statements under the case
2566 */
2567 /* Make sure sequence of statements under case is terminated with goto, return, or break */
2568 /* find the last statement */
2569
2570 check_pval(item->u2.statements, apps,in_globals);
2571 break;
2572
2573 case PV_DEFAULT:
2574 /* fields:
2575 item->u2.statements == pval list of statements under the case
2576 */
2577
2578 check_pval(item->u2.statements, apps,in_globals);
2579 break;
2580
2581 case PV_CATCH:
2582 /* fields: item->u1.str == name of extension to catch
2583 item->u2.statements == pval list of statements in context body
2584 */
2585 check_pval(item->u2.statements, apps,in_globals);
2586 break;
2587
2588 case PV_SWITCHES:
2589 /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
2590 */
2591 check_pval(item->u1.list, apps,in_globals);
2592 break;
2593
2594 case PV_ESWITCHES:
2595 /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
2596 */
2597 check_pval(item->u1.list, apps,in_globals);
2598 break;
2599
2600 case PV_INCLUDES:
2601 /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
2602 */
2603 check_pval(item->u1.list, apps,in_globals);
2605 for (lp=item->u1.list; lp; lp=lp->next){
2606 char *incl_context = lp->u1.str;
2607 struct pval *that_context = find_context(incl_context);
2608
2609 if ( lp->u2.arglist ) {
2611 check_dow(lp->u2.arglist->next);
2612 check_day(lp->u2.arglist->next->next);
2614 }
2615
2616 if (that_context) {
2617 find_pval_gotos(that_context->u2.statements,0);
2618
2619 }
2620 }
2621 break;
2622
2623 case PV_STATEMENTBLOCK:
2624 /* fields: item->u1.list == pval list of statements in block, one per entry in the list
2625 */
2626 check_pval(item->u1.list, apps,in_globals);
2627 break;
2628
2629 case PV_VARDEC:
2630 /* fields: item->u1.str == variable name
2631 item->u2.val == variable value to assign
2632 */
2633 /* the RHS of a vardec is encapsulated in a $[] expr. Is it legal? */
2634 if( !in_globals ) { /* don't check stuff inside the globals context; no wrapping in $[ ] there... */
2635 snprintf(errmsg,sizeof(errmsg), "file %s, line %d, columns %d-%d, variable declaration expr '%s':", item->filename, item->startline, item->startcol, item->endcol, item->u2.val);
2637 ast_expr(item->u2.val, expr_output, sizeof(expr_output),NULL);
2639 if ( strpbrk(item->u2.val,"~!-+<>=*/&^") && !strstr(item->u2.val,"${") ) {
2640 ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n",
2641 item->filename, item->startline, item->endline, item->u2.val);
2642 warns++;
2643 }
2644 check_expr2_input(item,item->u2.val);
2645 }
2646 break;
2647
2648 case PV_LOCALVARDEC:
2649 /* fields: item->u1.str == variable name
2650 item->u2.val == variable value to assign
2651 */
2652 /* the RHS of a vardec is encapsulated in a $[] expr. Is it legal? */
2653 snprintf(errmsg,sizeof(errmsg), "file %s, line %d, columns %d-%d, variable declaration expr '%s':", item->filename, item->startline, item->startcol, item->endcol, item->u2.val);
2655 ast_expr(item->u2.val, expr_output, sizeof(expr_output),NULL);
2657 if ( strpbrk(item->u2.val,"~!-+<>=*/&^") && !strstr(item->u2.val,"${") ) {
2658 ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n",
2659 item->filename, item->startline, item->endline, item->u2.val);
2660 warns++;
2661 }
2662 check_expr2_input(item,item->u2.val);
2663 break;
2664
2665 case PV_GOTO:
2666 /* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user.
2667 item->u1.list->u1.str == where the data on a PV_WORD will always be.
2668 */
2669 /* don't check goto's in abstract contexts */
2670 if ( in_abstract_context )
2671 break;
2672
2674 break;
2675
2676 case PV_LABEL:
2677 /* fields: item->u1.str == label name
2678 */
2679 if ( strspn(item->u1.str, "0123456789") == strlen(item->u1.str) ) {
2680 ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: label '%s' is numeric, this is bad practice!\n",
2681 item->filename, item->startline, item->endline, item->u1.str);
2682 warns++;
2683 }
2684
2686 break;
2687
2688 case PV_FOR:
2689 /* fields: item->u1.for_init == a string containing the initializer
2690 item->u2.for_test == a string containing the loop test
2691 item->u3.for_inc == a string containing the loop increment
2692
2693 item->u4.for_statements == a pval list of statements in the for ()
2694 */
2695 snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, for test expr '%s':", item->filename, item->startline, item->startcol, item->endcol, item->u2.for_test);
2697
2698 strp = strchr(item->u1.for_init, '=');
2699 if (strp) {
2700 ast_expr(strp+1, expr_output, sizeof(expr_output),NULL);
2701 }
2702 ast_expr(item->u2.for_test, expr_output, sizeof(expr_output),NULL);
2703 strp = strchr(item->u3.for_inc, '=');
2704 if (strp) {
2705 ast_expr(strp+1, expr_output, sizeof(expr_output),NULL);
2706 }
2707 if ( strpbrk(item->u2.for_test,"~!-+<>=*/&^") && !strstr(item->u2.for_test,"${") ) {
2708 ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n",
2709 item->filename, item->startline, item->endline, item->u2.for_test);
2710 warns++;
2711 }
2712 if ( strpbrk(item->u3.for_inc,"~!-+<>=*/&^") && !strstr(item->u3.for_inc,"${") ) {
2713 ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n",
2714 item->filename, item->startline, item->endline, item->u3.for_inc);
2715 warns++;
2716 }
2717 check_expr2_input(item,item->u2.for_test);
2718 check_expr2_input(item,item->u3.for_inc);
2719
2721 check_pval(item->u4.for_statements, apps,in_globals);
2722 break;
2723
2724 case PV_WHILE:
2725 /* fields: item->u1.str == the while conditional, as supplied by user
2726
2727 item->u2.statements == a pval list of statements in the while ()
2728 */
2729 snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, while expr '%s':", item->filename, item->startline, item->startcol, item->endcol, item->u1.str);
2731 ast_expr(item->u1.str, expr_output, sizeof(expr_output),NULL);
2733 if ( strpbrk(item->u1.str,"~!-+<>=*/&^") && !strstr(item->u1.str,"${") ) {
2734 ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n",
2735 item->filename, item->startline, item->endline, item->u1.str);
2736 warns++;
2737 }
2738 check_expr2_input(item,item->u1.str);
2739 check_pval(item->u2.statements, apps,in_globals);
2740 break;
2741
2742 case PV_BREAK:
2743 /* fields: none
2744 */
2746 break;
2747
2748 case PV_RETURN:
2749 /* fields: none
2750 */
2751 break;
2752
2753 case PV_CONTINUE:
2754 /* fields: none
2755 */
2757 break;
2758
2759 case PV_RANDOM:
2760 /* fields: item->u1.str == the random number expression, as supplied by user
2761
2762 item->u2.statements == a pval list of statements in the if ()
2763 item->u3.else_statements == a pval list of statements in the else
2764 (could be zero)
2765 */
2766 snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, random expr '%s':", item->filename, item->startline, item->startcol, item->endcol, item->u1.str);
2768 ast_expr(item->u1.str, expr_output, sizeof(expr_output),NULL);
2770 if ( strpbrk(item->u1.str,"~!-+<>=*/&^") && !strstr(item->u1.str,"${") ) {
2771 ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: random expression '%s' has operators, but no variables. Interesting...\n",
2772 item->filename, item->startline, item->endline, item->u1.str);
2773 warns++;
2774 }
2775 check_expr2_input(item,item->u1.str);
2776 check_pval(item->u2.statements, apps,in_globals);
2777 if (item->u3.else_statements) {
2778 check_pval(item->u3.else_statements, apps,in_globals);
2779 }
2780 break;
2781
2782 case PV_IFTIME:
2783 /* fields: item->u1.list == the if time values, 4 of them, each in PV_WORD, linked list
2784
2785 item->u2.statements == a pval list of statements in the if ()
2786 item->u3.else_statements == a pval list of statements in the else
2787 (could be zero)
2788 */
2789 if ( item->u2.arglist ) {
2790 check_timerange(item->u1.list);
2791 check_dow(item->u1.list->next);
2792 check_day(item->u1.list->next->next);
2793 check_month(item->u1.list->next->next->next);
2794 }
2795
2796 check_pval(item->u2.statements, apps,in_globals);
2797 if (item->u3.else_statements) {
2798 check_pval(item->u3.else_statements, apps,in_globals);
2799 }
2800 break;
2801
2802 case PV_IF:
2803 /* fields: item->u1.str == the if conditional, as supplied by user
2804
2805 item->u2.statements == a pval list of statements in the if ()
2806 item->u3.else_statements == a pval list of statements in the else
2807 (could be zero)
2808 */
2809 snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, if expr '%s':", item->filename, item->startline, item->startcol, item->endcol, item->u1.str);
2811 ast_expr(item->u1.str, expr_output, sizeof(expr_output),NULL);
2813 if ( strpbrk(item->u1.str,"~!-+<>=*/&^") && !strstr(item->u1.str,"${") ) {
2814 ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression '%s' has operators, but no variables. Interesting...\n",
2815 item->filename, item->startline, item->endline, item->u1.str);
2816 warns++;
2817 }
2818 check_expr2_input(item,item->u1.str);
2819 check_pval(item->u2.statements, apps,in_globals);
2820 if (item->u3.else_statements) {
2821 check_pval(item->u3.else_statements, apps,in_globals);
2822 }
2823 break;
2824
2825 case PV_SWITCH:
2826 /* fields: item->u1.str == the switch expression
2827
2828 item->u2.statements == a pval list of statements in the switch,
2829 (will be case statements, most likely!)
2830 */
2831 /* we can check the switch expression, see if it matches any of the app variables...
2832 if it does, then, are all the possible cases accounted for? */
2834 check_pval(item->u2.statements, apps,in_globals);
2835 break;
2836
2837 case PV_EXTENSION:
2838 /* fields: item->u1.str == the extension name, label, whatever it's called
2839
2840 item->u2.statements == a pval list of statements in the extension
2841 item->u3.hints == a char * hint argument
2842 item->u4.regexten == an int boolean. non-zero says that regexten was specified
2843 */
2845
2846 check_pval(item->u2.statements, apps,in_globals);
2847 break;
2848
2849 case PV_IGNOREPAT:
2850 /* fields: item->u1.str == the ignorepat data
2851 */
2852 break;
2853
2854 case PV_GLOBALS:
2855 /* fields: item->u1.statements == pval list of statements, usually vardecs
2856 */
2858 check_pval(item->u1.statements, apps, 1);
2859 break;
2860 default:
2861 break;
2862 }
2863}
2864
2865void check_pval(pval *item, struct argapp *apps, int in_globals)
2866{
2867 pval *i;
2868
2869 /* checks to do:
2870 1. Do goto's point to actual labels?
2871 2. Do macro calls reference a macro?
2872 3. Does the number of macro args match the definition?
2873 4. Is a macro call missing its & at the front?
2874 5. Application calls-- we could check syntax for existing applications,
2875 but I need some sort of universal description bnf for a general
2876 sort of method for checking arguments, in number, maybe even type, at least.
2877 Don't want to hand code checks for hundreds of applications.
2878 */
2879
2880 for (i=item; i; i=i->next) {
2881 check_pval_item(i,apps,in_globals);
2882 }
2883}
2884
2885void ael2_semantic_check(pval *item, int *arg_errs, int *arg_warns, int *arg_notes)
2886{
2887
2888#ifdef AAL_ARGCHECK
2889 int argapp_errs =0;
2890 char *rfilename;
2891#endif
2892 struct argapp *apps=0;
2893
2894 if (!item)
2895 return; /* don't check an empty tree */
2896#ifdef AAL_ARGCHECK
2897 rfilename = ast_alloca(10 + strlen(ast_config_AST_VAR_DIR));
2898 sprintf(rfilename, "%s/applist", ast_config_AST_VAR_DIR);
2899
2900 apps = argdesc_parse(rfilename, &argapp_errs); /* giveth */
2901#endif
2902 current_db = item;
2903 errs = warns = notes = 0;
2904
2906 check_pval(item, apps, 0);
2907
2908#ifdef AAL_ARGCHECK
2909 argdesc_destroy(apps); /* taketh away */
2910#endif
2911 current_db = 0;
2912
2913 *arg_errs = errs;
2914 *arg_warns = warns;
2915 *arg_notes = notes;
2916}
2917
2918/* =============================================================================================== */
2919/* "CODE" GENERATOR -- Convert the AEL representation to asterisk extension language */
2920/* =============================================================================================== */
2921
2923
2925{
2926 struct ael_priority *x = (struct ael_priority *)calloc(sizeof(struct ael_priority),1);
2927 return x;
2928}
2929
2931{
2932 struct ael_extension *x = (struct ael_extension *)calloc(sizeof(struct ael_extension),1);
2933 return x;
2934}
2935
2936void linkprio(struct ael_extension *exten, struct ael_priority *prio, struct ael_extension *mother_exten)
2937{
2938 char *p1, *p2;
2939
2940 if (!exten->plist) {
2941 exten->plist = prio;
2942 exten->plist_last = prio;
2943 } else {
2944 exten->plist_last->next = prio;
2945 exten->plist_last = prio;
2946 }
2947 if( !prio->exten )
2948 prio->exten = exten; /* don't override the switch value */
2949 /* The following code will cause all priorities within an extension
2950 to have ${EXTEN} or ${EXTEN: replaced with ~~EXTEN~~, which is
2951 set just before the first switch in an exten. The switches
2952 will muck up the original ${EXTEN} value, so we save it away
2953 and the user accesses this copy instead. */
2954 if (prio->appargs && ((mother_exten && mother_exten->has_switch) || exten->has_switch) ) {
2955 while ((p1 = strstr(prio->appargs, "${EXTEN}"))) {
2956 p2 = malloc(strlen(prio->appargs)+5);
2957 *p1 = 0;
2958 strcpy(p2, prio->appargs);
2959 strcat(p2, "${~~EXTEN~~}");
2960 if (*(p1+8))
2961 strcat(p2, p1+8);
2962 free(prio->appargs);
2963 prio->appargs = p2;
2964 }
2965 while ((p1 = strstr(prio->appargs, "${EXTEN:"))) {
2966 p2 = malloc(strlen(prio->appargs)+5);
2967 *p1 = 0;
2968 strcpy(p2, prio->appargs);
2969 strcat(p2, "${~~EXTEN~~:");
2970 if (*(p1+8))
2971 strcat(p2, p1+8);
2972 free(prio->appargs);
2973 prio->appargs = p2;
2974 }
2975 }
2976}
2977
2979{
2980 struct ael_extension *ne, *nen;
2981 for (ne=exten; ne; ne=nen) {
2982 struct ael_priority *pe, *pen;
2983
2984 if (ne->name)
2985 free(ne->name);
2986
2987 /* cidmatch fields are allocated with name, and freed when
2988 the name field is freed. Don't do a free for this field,
2989 unless you LIKE to see a crash! */
2990
2991 if (ne->hints)
2992 free(ne->hints);
2993
2994 for (pe=ne->plist; pe; pe=pen) {
2995 pen = pe->next;
2996 if (pe->app)
2997 free(pe->app);
2998 pe->app = 0;
2999 if (pe->appargs)
3000 free(pe->appargs);
3001 pe->appargs = 0;
3002 pe->origin = 0;
3003 pe->goto_true = 0;
3004 pe->goto_false = 0;
3005 free(pe);
3006 }
3007 nen = ne->next_exten;
3008 ne->next_exten = 0;
3009 ne->plist =0;
3010 ne->plist_last = 0;
3011 ne->next_exten = 0;
3012 ne->loop_break = 0;
3013 ne->loop_continue = 0;
3014 free(ne);
3015 }
3016}
3017
3018static int label_inside_case(pval *label)
3019{
3020 pval *p = label;
3021
3022 while( p && p->type != PV_MACRO && p->type != PV_CONTEXT ) /* early cutout, sort of */ {
3023 if( p->type == PV_CASE || p->type == PV_DEFAULT || p->type == PV_PATTERN ) {
3024 return 1;
3025 }
3026
3027 p = p->dad;
3028 }
3029 return 0;
3030}
3031
3032static void linkexten(struct ael_extension *exten, struct ael_extension *add)
3033{
3034 add->next_exten = exten->next_exten; /* this will reverse the order. Big deal. */
3035 exten->next_exten = add;
3036}
3037
3039{
3040 char *p;
3041 while( str && *str && *str != '=' )
3042 {
3043 if( *str == ' ' || *str == '\n' || *str == '\r' || *str == '\t' )
3044 {
3045 p = str;
3046 while( *p )
3047 {
3048 *p = *(p+1);
3049 p++;
3050 }
3051 }
3052 else
3053 str++;
3054 }
3055}
3056
3057/* =============================================================================================== */
3058/* "CODE" GENERATOR -- Convert the AEL representation to asterisk extension language */
3059/* =============================================================================================== */
3060
3061static void gen_match_to_pattern(char *pattern, char *result)
3062{
3063 /* the result will be a string that will be matched by pattern */
3064 char *p=pattern, *t=result;
3065 while (*p) {
3066 if (*p == 'x' || *p == 'n' || *p == 'z' || *p == 'X' || *p == 'N' || *p == 'Z')
3067 *t++ = '9';
3068 else if (*p == '[') {
3069 char *z = p+1;
3070 while (*z != ']')
3071 z++;
3072 if (*(z+1)== ']')
3073 z++;
3074 *t++=*(p+1); /* use the first char in the set */
3075 p = z;
3076 } else {
3077 *t++ = *p;
3078 }
3079 p++;
3080 }
3081 *t++ = 0; /* cap it off */
3082}
3083
3084/* ==== a set of routines to search for a switch statement contained in the pval description */
3085
3088
3089
3091{
3092 switch ( item->type ) {
3093 case PV_LOCALVARDEC:
3094 /* fields: item->u1.str == string associated with this (word). */
3095 break;
3096
3097 case PV_WORD:
3098 /* fields: item->u1.str == string associated with this (word). */
3099 break;
3100
3101 case PV_MACRO:
3102 /* fields: item->u1.str == name of macro
3103 item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
3104 item->u2.arglist->u1.str == argument
3105 item->u2.arglist->next == next arg
3106
3107 item->u3.macro_statements == pval list of statements in macro body.
3108 */
3109 /* had better not see this */
3110 if (contains_switch(item->u3.macro_statements))
3111 return 1;
3112 break;
3113
3114 case PV_CONTEXT:
3115 /* fields: item->u1.str == name of context
3116 item->u2.statements == pval list of statements in context body
3117 item->u3.abstract == int 1 if an abstract keyword were present
3118 */
3119 /* had better not see this */
3120 if (contains_switch(item->u2.statements))
3121 return 1;
3122 break;
3123
3124 case PV_MACRO_CALL:
3125 /* fields: item->u1.str == name of macro to call
3126 item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
3127 item->u2.arglist->u1.str == argument
3128 item->u2.arglist->next == next arg
3129 */
3130 break;
3131
3133 /* fields: item->u1.str == name of application to call
3134 item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
3135 item->u2.arglist->u1.str == argument
3136 item->u2.arglist->next == next arg
3137 */
3138 break;
3139
3140 case PV_CASE:
3141 /* fields: item->u1.str == value of case
3142 item->u2.statements == pval list of statements under the case
3143 */
3144 /* had better not see this */
3145 if (contains_switch(item->u2.statements))
3146 return 1;
3147 break;
3148
3149 case PV_PATTERN:
3150 /* fields: item->u1.str == value of case
3151 item->u2.statements == pval list of statements under the case
3152 */
3153 /* had better not see this */
3154 if (contains_switch(item->u2.statements))
3155 return 1;
3156 break;
3157
3158 case PV_DEFAULT:
3159 /* fields:
3160 item->u2.statements == pval list of statements under the case
3161 */
3162 /* had better not see this */
3163 if (contains_switch(item->u2.statements))
3164 return 1;
3165 break;
3166
3167 case PV_CATCH:
3168 /* fields: item->u1.str == name of extension to catch
3169 item->u2.statements == pval list of statements in context body
3170 */
3171 /* had better not see this */
3172 if (contains_switch(item->u2.statements))
3173 return 1;
3174 break;
3175
3176 case PV_SWITCHES:
3177 /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
3178 */
3179 break;
3180
3181 case PV_ESWITCHES:
3182 /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
3183 */
3184 break;
3185
3186 case PV_INCLUDES:
3187 /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
3188 item->u2.arglist == pval list of 4 PV_WORD elements for time values
3189 */
3190 break;
3191
3192 case PV_STATEMENTBLOCK:
3193 /* fields: item->u1.list == pval list of statements in block, one per entry in the list
3194 */
3195 if (contains_switch(item->u1.list) )
3196 return 1;
3197 break;
3198
3199 case PV_VARDEC:
3200 /* fields: item->u1.str == variable name
3201 item->u2.val == variable value to assign
3202 */
3203 break;
3204
3205 case PV_GOTO:
3206 /* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user.
3207 item->u1.list->u1.str == where the data on a PV_WORD will always be.
3208 */
3209 break;
3210
3211 case PV_LABEL:
3212 /* fields: item->u1.str == label name
3213 */
3214 break;
3215
3216 case PV_FOR:
3217 /* fields: item->u1.for_init == a string containing the initializer
3218 item->u2.for_test == a string containing the loop test
3219 item->u3.for_inc == a string containing the loop increment
3220
3221 item->u4.for_statements == a pval list of statements in the for ()
3222 */
3223 if (contains_switch(item->u4.for_statements))
3224 return 1;
3225 break;
3226
3227 case PV_WHILE:
3228 /* fields: item->u1.str == the while conditional, as supplied by user
3229
3230 item->u2.statements == a pval list of statements in the while ()
3231 */
3232 if (contains_switch(item->u2.statements))
3233 return 1;
3234 break;
3235
3236 case PV_BREAK:
3237 /* fields: none
3238 */
3239 break;
3240
3241 case PV_RETURN:
3242 /* fields: none
3243 */
3244 break;
3245
3246 case PV_CONTINUE:
3247 /* fields: none
3248 */
3249 break;
3250
3251 case PV_IFTIME:
3252 /* fields: item->u1.list == there are 4 linked PV_WORDs here.
3253
3254 item->u2.statements == a pval list of statements in the if ()
3255 item->u3.else_statements == a pval list of statements in the else
3256 (could be zero)
3257 */
3258 if (contains_switch(item->u2.statements))
3259 return 1;
3260 if ( item->u3.else_statements ) {
3261 if (contains_switch(item->u3.else_statements))
3262 return 1;
3263 }
3264 break;
3265
3266 case PV_RANDOM:
3267 /* fields: item->u1.str == the random number expression, as supplied by user
3268
3269 item->u2.statements == a pval list of statements in the if ()
3270 item->u3.else_statements == a pval list of statements in the else
3271 (could be zero)
3272 */
3273 if (contains_switch(item->u2.statements))
3274 return 1;
3275 if ( item->u3.else_statements ) {
3276 if (contains_switch(item->u3.else_statements))
3277 return 1;
3278 }
3279 break;
3280
3281 case PV_IF:
3282 /* fields: item->u1.str == the if conditional, as supplied by user
3283
3284 item->u2.statements == a pval list of statements in the if ()
3285 item->u3.else_statements == a pval list of statements in the else
3286 (could be zero)
3287 */
3288 if (contains_switch(item->u2.statements))
3289 return 1;
3290 if ( item->u3.else_statements ) {
3291 if (contains_switch(item->u3.else_statements))
3292 return 1;
3293 }
3294 break;
3295
3296 case PV_SWITCH:
3297 /* fields: item->u1.str == the switch expression
3298
3299 item->u2.statements == a pval list of statements in the switch,
3300 (will be case statements, most likely!)
3301 */
3302 return 1; /* JACKPOT */
3303 break;
3304
3305 case PV_EXTENSION:
3306 /* fields: item->u1.str == the extension name, label, whatever it's called
3307
3308 item->u2.statements == a pval list of statements in the extension
3309 item->u3.hints == a char * hint argument
3310 item->u4.regexten == an int boolean. non-zero says that regexten was specified
3311 */
3312 if (contains_switch(item->u2.statements))
3313 return 1;
3314 break;
3315
3316 case PV_IGNOREPAT:
3317 /* fields: item->u1.str == the ignorepat data
3318 */
3319 break;
3320
3321 case PV_GLOBALS:
3322 /* fields: item->u1.statements == pval list of statements, usually vardecs
3323 */
3324 break;
3325 }
3326 return 0;
3327}
3328
3330{
3331 pval *i;
3332
3333 for (i=item; i; i=i->next) {
3334 if (find_switch_item(i))
3335 return 1;
3336 }
3337 return 0;
3338}
3339
3340
3341static int gen_prios(struct ael_extension *exten, char *label, pval *statement, struct ael_extension *mother_exten, struct ast_context *this_context )
3342{
3343 pval *p,*p2,*p3;
3344 struct ael_priority *pr;
3345 struct ael_priority *for_init, *for_test, *for_inc, *for_loop, *for_end;
3346 struct ael_priority *while_test, *while_loop, *while_end;
3347 struct ael_priority *switch_set, *switch_test, *switch_end, *fall_thru, *switch_empty;
3348 struct ael_priority *if_test, *if_end, *if_skip, *if_false;
3349#ifdef OLD_RAND_ACTION
3350 struct ael_priority *rand_test, *rand_end, *rand_skip;
3351#endif
3352 RAII_VAR(char *, buf1, NULL, free);
3353 RAII_VAR(char *, buf2, NULL, free);
3354 RAII_VAR(char *, new_label, NULL, free);
3355 char *strp, *strp2;
3356 int default_exists;
3357 int local_control_statement_count;
3358 int first;
3359 struct ael_priority *loop_break_save;
3360 struct ael_priority *loop_continue_save;
3361 struct ael_extension *switch_case,*switch_null;
3362
3363 if (!(buf1 = malloc(BUF_SIZE))) {
3364 return -1;
3365 }
3366 if (!(buf2 = malloc(BUF_SIZE))) {
3367 return -1;
3368 }
3369 if (!(new_label = malloc(BUF_SIZE))) {
3370 return -1;
3371 }
3372
3373 if ((mother_exten && !mother_exten->checked_switch) || (exten && !exten->checked_switch)) {
3374 if (contains_switch(statement)) { /* only run contains_switch if you haven't checked before */
3375 if (mother_exten) {
3376 if (!mother_exten->has_switch) {
3377 for (first = 1; first >= 0; first--) {
3378 switch_set = new_prio();
3379 switch_set->type = AEL_APPCALL;
3380 switch_set->app = strdup("MSet");
3381 /* Are we likely inside a gosub subroutine? */
3382 if (!strcmp(mother_exten->name, "~~s~~") && first) {
3383 /* If we're not actually within a gosub, this will fail, but the
3384 * second time through, it will get set. If we are within gosub,
3385 * the second time through is redundant, but acceptable. */
3386 switch_set->appargs = strdup("LOCAL(~~EXTEN~~)=${EXTEN}");
3387 } else {
3388 switch_set->appargs = strdup("~~EXTEN~~=${EXTEN}");
3389 first = 0;
3390 }
3391 linkprio(exten, switch_set, mother_exten);
3392 mother_exten->has_switch = 1;
3393 mother_exten->checked_switch = 1;
3394 if (exten) {
3395 exten->has_switch = 1;
3396 exten->checked_switch = 1;
3397 }
3398 }
3399 }
3400 } else if (exten) {
3401 if (!exten->has_switch) {
3402 for (first = 1; first >= 0; first--) {
3403 switch_set = new_prio();
3404 switch_set->type = AEL_APPCALL;
3405 switch_set->app = strdup("MSet");
3406 /* Are we likely inside a gosub subroutine? */
3407 if (!strcmp(exten->name, "~~s~~")) {
3408 /* If we're not actually within a gosub, this will fail, but the
3409 * second time through, it will get set. If we are within gosub,
3410 * the second time through is redundant, but acceptable. */
3411 switch_set->appargs = strdup("LOCAL(~~EXTEN~~)=${EXTEN}");
3412 } else {
3413 switch_set->appargs = strdup("~~EXTEN~~=${EXTEN}");
3414 first = 0;
3415 }
3416 linkprio(exten, switch_set, mother_exten);
3417 exten->has_switch = 1;
3418 exten->checked_switch = 1;
3419 if (mother_exten) {
3420 mother_exten->has_switch = 1;
3421 mother_exten->checked_switch = 1;
3422 }
3423 }
3424 }
3425 }
3426 } else {
3427 if (mother_exten) {
3428 mother_exten->checked_switch = 1;
3429 }
3430 if (exten) {
3431 exten->checked_switch = 1;
3432 }
3433 }
3434 }
3435 for (p=statement; p; p=p->next) {
3436 switch (p->type) {
3437 case PV_VARDEC:
3438 pr = new_prio();
3439 pr->type = AEL_APPCALL;
3440 snprintf(buf1, BUF_SIZE, "%s=$[%s]", p->u1.str, p->u2.val);
3441 pr->app = strdup("MSet");
3443 pr->appargs = strdup(buf1);
3444 pr->origin = p;
3445 linkprio(exten, pr, mother_exten);
3446 break;
3447
3448 case PV_LOCALVARDEC:
3449 pr = new_prio();
3450 pr->type = AEL_APPCALL;
3451 snprintf(buf1, BUF_SIZE, "LOCAL(%s)=$[%s]", p->u1.str, p->u2.val);
3452 pr->app = strdup("MSet");
3454 pr->appargs = strdup(buf1);
3455 pr->origin = p;
3456 linkprio(exten, pr, mother_exten);
3457 break;
3458
3459 case PV_GOTO:
3460 pr = new_prio();
3461 pr->type = AEL_APPCALL;
3463 if( p->u2.goto_target ) {
3465 }
3466
3467 if (!p->u1.list->next) /* just one */ {
3468 pr->app = strdup("Goto");
3469 if (!mother_exten)
3470 pr->appargs = strdup(p->u1.list->u1.str);
3471 else { /* for the case of simple within-extension gotos in case/pattern/default statement blocks: */
3472 snprintf(buf1, BUF_SIZE, "%s,%s", mother_exten->name, p->u1.list->u1.str);
3473 pr->appargs = strdup(buf1);
3474 }
3475
3476 } else if (p->u1.list->next && !p->u1.list->next->next) /* two */ {
3477 snprintf(buf1, BUF_SIZE, "%s,%s", p->u1.list->u1.str, p->u1.list->next->u1.str);
3478 pr->app = strdup("Goto");
3479 pr->appargs = strdup(buf1);
3480 } else if (p->u1.list->next && p->u1.list->next->next) {
3481 snprintf(buf1, BUF_SIZE, "%s,%s,%s", p->u1.list->u1.str,
3482 p->u1.list->next->u1.str,
3483 p->u1.list->next->next->u1.str);
3484 pr->app = strdup("Goto");
3485 pr->appargs = strdup(buf1);
3486 }
3487 pr->origin = p;
3488 linkprio(exten, pr, mother_exten);
3489 break;
3490
3491 case PV_LABEL:
3492 pr = new_prio();
3493 pr->type = AEL_LABEL;
3494 pr->origin = p;
3495 p->u3.compiled_label = exten;
3496 linkprio(exten, pr, mother_exten);
3497 break;
3498
3499 case PV_FOR:
3501 loop_break_save = exten->loop_break; /* save them, then restore before leaving */
3502 loop_continue_save = exten->loop_continue;
3503 snprintf(new_label, BUF_SIZE, "for_%s_%d", label, control_statement_count);
3504 for_init = new_prio();
3505 for_inc = new_prio();
3506 for_test = new_prio();
3507 for_loop = new_prio();
3508 for_end = new_prio();
3509 for_init->type = AEL_APPCALL;
3510 for_inc->type = AEL_APPCALL;
3511 for_test->type = AEL_FOR_CONTROL;
3512 for_test->goto_false = for_end;
3513 for_loop->type = AEL_CONTROL1; /* simple goto */
3514 for_end->type = AEL_APPCALL;
3515 for_init->app = strdup("MSet");
3516
3517 strcpy(buf2,p->u1.for_init);
3519 strp = strchr(buf2, '=');
3520 if (strp) {
3521 strp2 = strchr(p->u1.for_init, '=');
3522 *(strp+1) = 0;
3523 strcat(buf2,"$[");
3524 strncat(buf2,strp2+1, BUF_SIZE-strlen(strp2+1)-2);
3525 strcat(buf2,"]");
3526 for_init->appargs = strdup(buf2);
3527 } else {
3528 strp2 = p->u1.for_init;
3529 while (*strp2 && isspace(*strp2))
3530 strp2++;
3531 if (*strp2 == '&') { /* itsa macro call */
3532 char *strp3 = strp2+1;
3533 while (*strp3 && isspace(*strp3))
3534 strp3++;
3535 strcpy(buf2, strp3);
3536 strp3 = strchr(buf2,'(');
3537 if (strp3) {
3538 *strp3 = ',';
3539 }
3540 strp3 = strrchr(buf2, ')');
3541 if (strp3)
3542 *strp3 = 0; /* remove the closing paren */
3543 for_init->appargs = strdup(buf2);
3544 free(for_init->app);
3545 for_init->app = strdup("Gosub");
3546 } else { /* must be a regular app call */
3547 char *strp3;
3548 strcpy(buf2, strp2);
3549 strp3 = strchr(buf2,'(');
3550 if (strp3) {
3551 *strp3 = 0;
3552 free(for_init->app);
3553 for_init->app = strdup(buf2);
3554 for_init->appargs = strdup(strp3+1);
3555 strp3 = strrchr(for_init->appargs, ')');
3556 if (strp3)
3557 *strp3 = 0; /* remove the closing paren */
3558 }
3559 }
3560 }
3561
3562 strcpy(buf2,p->u3.for_inc);
3564 strp = strchr(buf2, '=');
3565 if (strp) { /* there's an = in this part; that means an assignment. set it up */
3566 strp2 = strchr(p->u3.for_inc, '=');
3567 *(strp+1) = 0;
3568 strcat(buf2,"$[");
3569 strncat(buf2,strp2+1, BUF_SIZE-strlen(strp2+1)-2);
3570 strcat(buf2,"]");
3571 for_inc->appargs = strdup(buf2);
3572 for_inc->app = strdup("MSet");
3573 } else {
3574 strp2 = p->u3.for_inc;
3575 while (*strp2 && isspace(*strp2))
3576 strp2++;
3577 if (*strp2 == '&') { /* itsa macro call */
3578 char *strp3 = strp2+1;
3579 while (*strp3 && isspace(*strp3))
3580 strp3++;
3581 strcpy(buf2, strp3);
3582 strp3 = strchr(buf2,'(');
3583 if (strp3) {
3584 *strp3 = ',';
3585 }
3586 strp3 = strrchr(buf2, ')');
3587 if (strp3)
3588 *strp3 = 0; /* remove the closing paren */
3589
3590 for_inc->appargs = strdup(buf2);
3591
3592 for_inc->app = strdup("Gosub");
3593 } else { /* must be a regular app call */
3594 char *strp3;
3595 strcpy(buf2, strp2);
3596 strp3 = strchr(buf2,'(');
3597 if (strp3) {
3598 *strp3 = 0;
3599 for_inc->app = strdup(buf2);
3600 for_inc->appargs = strdup(strp3+1);
3601 strp3 = strrchr(for_inc->appargs, ')');
3602 if (strp3)
3603 *strp3 = 0; /* remove the closing paren */
3604 }
3605 }
3606 }
3607 snprintf(buf1, BUF_SIZE, "$[%s]",p->u2.for_test);
3608 for_test->app = 0;
3609 for_test->appargs = strdup(buf1);
3610 for_loop->goto_true = for_test;
3611 snprintf(buf1, BUF_SIZE, "Finish for_%s_%d", label, control_statement_count);
3612 for_end->app = strdup("NoOp");
3613 for_end->appargs = strdup(buf1);
3614 /* link & load! */
3615 linkprio(exten, for_init, mother_exten);
3616 linkprio(exten, for_test, mother_exten);
3617
3618 /* now, put the body of the for loop here */
3619 exten->loop_break = for_end;
3620 exten->loop_continue = for_inc;
3621
3622 if (gen_prios(exten, new_label, p->u4.for_statements, mother_exten, this_context)) { /* this will link in all the statements here */
3623 return -1;
3624 }
3625
3626 linkprio(exten, for_inc, mother_exten);
3627 linkprio(exten, for_loop, mother_exten);
3628 linkprio(exten, for_end, mother_exten);
3629
3630
3631 exten->loop_break = loop_break_save;
3632 exten->loop_continue = loop_continue_save;
3633 for_loop->origin = p;
3634 break;
3635
3636 case PV_WHILE:
3638 loop_break_save = exten->loop_break; /* save them, then restore before leaving */
3639 loop_continue_save = exten->loop_continue;
3640 snprintf(new_label, BUF_SIZE, "while_%s_%d", label, control_statement_count);
3641 while_test = new_prio();
3642 while_loop = new_prio();
3643 while_end = new_prio();
3644 while_test->type = AEL_FOR_CONTROL;
3645 while_test->goto_false = while_end;
3646 while_loop->type = AEL_CONTROL1; /* simple goto */
3647 while_end->type = AEL_APPCALL;
3648 snprintf(buf1, BUF_SIZE, "$[%s]",p->u1.str);
3649 while_test->app = 0;
3650 while_test->appargs = strdup(buf1);
3651 while_loop->goto_true = while_test;
3652 snprintf(buf1, BUF_SIZE, "Finish while_%s_%d", label, control_statement_count);
3653 while_end->app = strdup("NoOp");
3654 while_end->appargs = strdup(buf1);
3655
3656 linkprio(exten, while_test, mother_exten);
3657
3658 /* now, put the body of the for loop here */
3659 exten->loop_break = while_end;
3660 exten->loop_continue = while_test;
3661
3662 if (gen_prios(exten, new_label, p->u2.statements, mother_exten, this_context)) { /* this will link in all the while body statements here */
3663 return -1;
3664 }
3665
3666 linkprio(exten, while_loop, mother_exten);
3667 linkprio(exten, while_end, mother_exten);
3668
3669
3670 exten->loop_break = loop_break_save;
3671 exten->loop_continue = loop_continue_save;
3672 while_loop->origin = p;
3673 break;
3674
3675 case PV_SWITCH:
3677 local_control_statement_count = control_statement_count;
3678 loop_break_save = exten->loop_break; /* save them, then restore before leaving */
3679 loop_continue_save = exten->loop_continue;
3680 snprintf(new_label, BUF_SIZE, "sw_%s_%d", label, control_statement_count);
3681 switch_test = new_prio();
3682 switch_end = new_prio();
3683 switch_test->type = AEL_APPCALL;
3684 switch_end->type = AEL_APPCALL;
3685 snprintf(buf1, BUF_SIZE, "sw_%d_%s,10", control_statement_count, p->u1.str);
3686 switch_test->app = strdup("Goto");
3687 switch_test->appargs = strdup(buf1);
3688 snprintf(buf1, BUF_SIZE, "Finish switch_%s_%d", label, control_statement_count);
3689 switch_end->app = strdup("NoOp");
3690 switch_end->appargs = strdup(buf1);
3691 switch_end->origin = p;
3692 switch_end->exten = exten;
3693
3694 linkprio(exten, switch_test, mother_exten);
3695 linkprio(exten, switch_end, mother_exten);
3696
3697 exten->loop_break = switch_end;
3698 exten->loop_continue = 0;
3699 default_exists = 0;
3700
3701 for (p2=p->u2.statements; p2; p2=p2->next) {
3702 /* now, for each case/default put the body of the for loop here */
3703 if (p2->type == PV_CASE) {
3704 /* ok, generate a extension and link it in */
3705 switch_case = new_exten();
3706 if (mother_exten && mother_exten->checked_switch) {
3707 switch_case->has_switch = mother_exten->has_switch;
3708 switch_case->checked_switch = mother_exten->checked_switch;
3709 }
3710 if (exten && exten->checked_switch) {
3711 switch_case->has_switch = exten->has_switch;
3712 switch_case->checked_switch = exten->checked_switch;
3713 }
3714 switch_case->context = this_context;
3715 switch_case->is_switch = 1;
3716 /* the break/continue locations are inherited from parent */
3717 switch_case->loop_break = exten->loop_break;
3718 switch_case->loop_continue = exten->loop_continue;
3719
3720 linkexten(exten,switch_case);
3721 snprintf(buf1, BUF_SIZE, "sw_%d_%s", local_control_statement_count, p2->u1.str);
3722 switch_case->name = strdup(buf1);
3723 snprintf(new_label, BUF_SIZE, "sw_%s_%s_%d", label, p2->u1.str, local_control_statement_count);
3724
3725 if (gen_prios(switch_case, new_label, p2->u2.statements, exten, this_context)) { /* this will link in all the case body statements here */
3726 return -1;
3727 }
3728
3729 /* here is where we write code to "fall thru" to the next case... if there is one... */
3730 for (p3=p2->u2.statements; p3; p3=p3->next) {
3731 if (!p3->next)
3732 break;
3733 }
3734 /* p3 now points the last statement... */
3735 if (!p3 || ( p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN) ) {
3736 /* is there a following CASE/PATTERN/DEFAULT? */
3737 if (p2->next && p2->next->type == PV_CASE) {
3738 fall_thru = new_prio();
3739 fall_thru->type = AEL_APPCALL;
3740 fall_thru->app = strdup("Goto");
3741 snprintf(buf1, BUF_SIZE, "sw_%d_%s,10", local_control_statement_count, p2->next->u1.str);
3742 fall_thru->appargs = strdup(buf1);
3743 linkprio(switch_case, fall_thru, mother_exten);
3744 } else if (p2->next && p2->next->type == PV_PATTERN) {
3745 fall_thru = new_prio();
3746 fall_thru->type = AEL_APPCALL;
3747 fall_thru->app = strdup("Goto");
3749 snprintf(buf1, BUF_SIZE, "sw_%d_%s,10", local_control_statement_count, buf2);
3750 fall_thru->appargs = strdup(buf1);
3751 linkprio(switch_case, fall_thru, mother_exten);
3752 } else if (p2->next && p2->next->type == PV_DEFAULT) {
3753 fall_thru = new_prio();
3754 fall_thru->type = AEL_APPCALL;
3755 fall_thru->app = strdup("Goto");
3756 snprintf(buf1, BUF_SIZE, "sw_%d_.,10", local_control_statement_count);
3757 fall_thru->appargs = strdup(buf1);
3758 linkprio(switch_case, fall_thru, mother_exten);
3759 } else if (!p2->next) {
3760 fall_thru = new_prio();
3761 fall_thru->type = AEL_CONTROL1;
3762 fall_thru->goto_true = switch_end;
3763 fall_thru->app = strdup("Goto");
3764 linkprio(switch_case, fall_thru, mother_exten);
3765 }
3766 }
3767 if (switch_case->return_needed) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */
3768 char buf[2000];
3769 struct ael_priority *np2 = new_prio();
3770 np2->type = AEL_APPCALL;
3771 np2->app = strdup("NoOp");
3772 snprintf(buf, BUF_SIZE, "End of Extension %s", switch_case->name);
3773 np2->appargs = strdup(buf);
3774 linkprio(switch_case, np2, mother_exten);
3775 switch_case-> return_target = np2;
3776 }
3777 } else if (p2->type == PV_PATTERN) {
3778 /* ok, generate a extension and link it in */
3779 switch_case = new_exten();
3780 if (mother_exten && mother_exten->checked_switch) {
3781 switch_case->has_switch = mother_exten->has_switch;
3782 switch_case->checked_switch = mother_exten->checked_switch;
3783 }
3784 if (exten && exten->checked_switch) {
3785 switch_case->has_switch = exten->has_switch;
3786 switch_case->checked_switch = exten->checked_switch;
3787 }
3788 switch_case->context = this_context;
3789 switch_case->is_switch = 1;
3790 /* the break/continue locations are inherited from parent */
3791 switch_case->loop_break = exten->loop_break;
3792 switch_case->loop_continue = exten->loop_continue;
3793
3794 linkexten(exten,switch_case);
3795 snprintf(buf1, BUF_SIZE, "_sw_%d_%s", local_control_statement_count, p2->u1.str);
3796 switch_case->name = strdup(buf1);
3797 snprintf(new_label, BUF_SIZE, "sw_%s_%s_%d", label, p2->u1.str, local_control_statement_count);
3798
3799 if (gen_prios(switch_case, new_label, p2->u2.statements, exten, this_context)) { /* this will link in all the while body statements here */
3800 return -1;
3801 }
3802 /* here is where we write code to "fall thru" to the next case... if there is one... */
3803 for (p3=p2->u2.statements; p3; p3=p3->next) {
3804 if (!p3->next)
3805 break;
3806 }
3807 /* p3 now points the last statement... */
3808 if (!p3 || ( p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN)) {
3809 /* is there a following CASE/PATTERN/DEFAULT? */
3810 if (p2->next && p2->next->type == PV_CASE) {
3811 fall_thru = new_prio();
3812 fall_thru->type = AEL_APPCALL;
3813 fall_thru->app = strdup("Goto");
3814 snprintf(buf1, BUF_SIZE, "sw_%d_%s,10", local_control_statement_count, p2->next->u1.str);
3815 fall_thru->appargs = strdup(buf1);
3816 linkprio(switch_case, fall_thru, mother_exten);
3817 } else if (p2->next && p2->next->type == PV_PATTERN) {
3818 fall_thru = new_prio();
3819 fall_thru->type = AEL_APPCALL;
3820 fall_thru->app = strdup("Goto");
3822 snprintf(buf1, BUF_SIZE, "sw_%d_%s,10", local_control_statement_count, buf2);
3823 fall_thru->appargs = strdup(buf1);
3824 linkprio(switch_case, fall_thru, mother_exten);
3825 } else if (p2->next && p2->next->type == PV_DEFAULT) {
3826 fall_thru = new_prio();
3827 fall_thru->type = AEL_APPCALL;
3828 fall_thru->app = strdup("Goto");
3829 snprintf(buf1, BUF_SIZE, "sw_%d_.,10", local_control_statement_count);
3830 fall_thru->appargs = strdup(buf1);
3831 linkprio(switch_case, fall_thru, mother_exten);
3832 } else if (!p2->next) {
3833 fall_thru = new_prio();
3834 fall_thru->type = AEL_CONTROL1;
3835 fall_thru->goto_true = switch_end;
3836 fall_thru->app = strdup("Goto");
3837 linkprio(switch_case, fall_thru, mother_exten);
3838 }
3839 }
3840 if (switch_case->return_needed) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */
3841 char buf[2000];
3842 struct ael_priority *np2 = new_prio();
3843 np2->type = AEL_APPCALL;
3844 np2->app = strdup("NoOp");
3845 snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name);
3846 np2->appargs = strdup(buf);
3847 linkprio(switch_case, np2, mother_exten);
3848 switch_case-> return_target = np2;
3849 }
3850 } else if (p2->type == PV_DEFAULT) {
3851 /* ok, generate a extension and link it in */
3852 switch_case = new_exten();
3853 if (mother_exten && mother_exten->checked_switch) {
3854 switch_case->has_switch = mother_exten->has_switch;
3855 switch_case->checked_switch = mother_exten->checked_switch;
3856 }
3857 if (exten && exten->checked_switch) {
3858 switch_case->has_switch = exten->has_switch;
3859 switch_case->checked_switch = exten->checked_switch;
3860 }
3861 switch_case->context = this_context;
3862 switch_case->is_switch = 1;
3863
3864 /* new: the default case intros a pattern with ., which covers ALMOST everything.
3865 but it doesn't cover a NULL pattern. So, we'll define a null extension to match
3866 that goto's the default extension. */
3867
3868 default_exists++;
3869 switch_null = new_exten();
3870 if (mother_exten && mother_exten->checked_switch) {
3871 switch_null->has_switch = mother_exten->has_switch;
3872 switch_null->checked_switch = mother_exten->checked_switch;
3873 }
3874 if (exten && exten->checked_switch) {
3875 switch_null->has_switch = exten->has_switch;
3876 switch_null->checked_switch = exten->checked_switch;
3877 }
3878 switch_null->context = this_context;
3879 switch_null->is_switch = 1;
3880 switch_empty = new_prio();
3881 snprintf(buf1, BUF_SIZE, "sw_%d_.,10", local_control_statement_count);
3882 switch_empty->app = strdup("Goto");
3883 switch_empty->appargs = strdup(buf1);
3884 linkprio(switch_null, switch_empty, mother_exten);
3885 snprintf(buf1, BUF_SIZE, "sw_%d_", local_control_statement_count);
3886 switch_null->name = strdup(buf1);
3887 switch_null->loop_break = exten->loop_break;
3888 switch_null->loop_continue = exten->loop_continue;
3889 linkexten(exten,switch_null);
3890
3891 /* the break/continue locations are inherited from parent */
3892 switch_case->loop_break = exten->loop_break;
3893 switch_case->loop_continue = exten->loop_continue;
3894 linkexten(exten,switch_case);
3895 snprintf(buf1, BUF_SIZE, "_sw_%d_.", local_control_statement_count);
3896 switch_case->name = strdup(buf1);
3897
3898 snprintf(new_label, BUF_SIZE, "sw_%s_default_%d", label, local_control_statement_count);
3899
3900 if (gen_prios(switch_case, new_label, p2->u2.statements, exten, this_context)) { /* this will link in all the default: body statements here */
3901 return -1;
3902 }
3903
3904 /* here is where we write code to "fall thru" to the next case... if there is one... */
3905 for (p3=p2->u2.statements; p3; p3=p3->next) {
3906 if (!p3->next)
3907 break;
3908 }
3909 /* p3 now points the last statement... */
3910 if (!p3 || (p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN)) {
3911 /* is there a following CASE/PATTERN/DEFAULT? */
3912 if (p2->next && p2->next->type == PV_CASE) {
3913 fall_thru = new_prio();
3914 fall_thru->type = AEL_APPCALL;
3915 fall_thru->app = strdup("Goto");
3916 snprintf(buf1, BUF_SIZE, "sw_%d_%s,10", local_control_statement_count, p2->next->u1.str);
3917 fall_thru->appargs = strdup(buf1);
3918 linkprio(switch_case, fall_thru, mother_exten);
3919 } else if (p2->next && p2->next->type == PV_PATTERN) {
3920 fall_thru = new_prio();
3921 fall_thru->type = AEL_APPCALL;
3922 fall_thru->app = strdup("Goto");
3924 snprintf(buf1, BUF_SIZE, "sw_%d_%s,10", local_control_statement_count, buf2);
3925 fall_thru->appargs = strdup(buf1);
3926 linkprio(switch_case, fall_thru, mother_exten);
3927 } else if (p2->next && p2->next->type == PV_DEFAULT) {
3928 fall_thru = new_prio();
3929 fall_thru->type = AEL_APPCALL;
3930 fall_thru->app = strdup("Goto");
3931 snprintf(buf1, BUF_SIZE, "sw_%d_.,10", local_control_statement_count);
3932 fall_thru->appargs = strdup(buf1);
3933 linkprio(switch_case, fall_thru, mother_exten);
3934 } else if (!p2->next) {
3935 fall_thru = new_prio();
3936 fall_thru->type = AEL_CONTROL1;
3937 fall_thru->goto_true = switch_end;
3938 fall_thru->app = strdup("Goto");
3939 linkprio(switch_case, fall_thru, mother_exten);
3940 }
3941 }
3942 if (switch_case->return_needed) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */
3943 char buf[2000];
3944 struct ael_priority *np2 = new_prio();
3945 np2->type = AEL_APPCALL;
3946 np2->app = strdup("NoOp");
3947 snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name);
3948 np2->appargs = strdup(buf);
3949 linkprio(switch_case, np2, mother_exten);
3950 switch_case-> return_target = np2;
3951 }
3952 } else {
3953 /* what could it be??? */
3954 }
3955 }
3956
3957 exten->loop_break = loop_break_save;
3958 exten->loop_continue = loop_continue_save;
3959 switch_test->origin = p;
3960 switch_end->origin = p;
3961 break;
3962
3963 case PV_MACRO_CALL:
3964 pr = new_prio();
3965 pr->type = AEL_APPCALL;
3966 snprintf(buf1, BUF_SIZE, "%s,~~s~~,1", p->u1.str);
3967 first = 1;
3968 for (p2 = p->u2.arglist; p2; p2 = p2->next) {
3969 if (first)
3970 {
3971 strcat(buf1,"(");
3972 first = 0;
3973 }
3974 else
3975 strcat(buf1,",");
3976 strcat(buf1,p2->u1.str);
3977 }
3978 if (!first)
3979 strcat(buf1,")");
3980
3981 pr->app = strdup("Gosub");
3982 pr->appargs = strdup(buf1);
3983 pr->origin = p;
3984 linkprio(exten, pr, mother_exten);
3985 break;
3986
3988 pr = new_prio();
3989 pr->type = AEL_APPCALL;
3990 buf1[0] = 0;
3991 for (p2 = p->u2.arglist; p2; p2 = p2->next) {
3992 if (p2 != p->u2.arglist )
3993 strcat(buf1,",");
3994 strcat(buf1,p2->u1.str);
3995 }
3996 pr->app = strdup(p->u1.str);
3997 pr->appargs = strdup(buf1);
3998 pr->origin = p;
3999 linkprio(exten, pr, mother_exten);
4000 break;
4001
4002 case PV_BREAK:
4003 pr = new_prio();
4004 pr->type = AEL_CONTROL1; /* simple goto */
4005 pr->goto_true = exten->loop_break;
4006 pr->origin = p;
4007 linkprio(exten, pr, mother_exten);
4008 break;
4009
4010 case PV_RETURN: /* hmmmm */
4011 pr = new_prio();
4012 pr->type = AEL_RETURN; /* simple Return */
4013 /* exten->return_needed++; */
4014 pr->app = strdup("Return");
4015 pr->appargs = strdup("");
4016 pr->origin = p;
4017 linkprio(exten, pr, mother_exten);
4018 break;
4019
4020 case PV_CONTINUE:
4021 pr = new_prio();
4022 pr->type = AEL_CONTROL1; /* simple goto */
4024 pr->origin = p;
4025 linkprio(exten, pr, mother_exten);
4026 break;
4027
4028 case PV_IFTIME:
4030 snprintf(new_label, BUF_SIZE, "iftime_%s_%d", label, control_statement_count);
4031
4032 if_test = new_prio();
4033 if_test->type = AEL_IFTIME_CONTROL;
4034 snprintf(buf1, BUF_SIZE, "%s,%s,%s,%s",
4035 p->u1.list->u1.str,
4036 p->u1.list->next->u1.str,
4037 p->u1.list->next->next->u1.str,
4038 p->u1.list->next->next->next->u1.str);
4039 if_test->app = 0;
4040 if_test->appargs = strdup(buf1);
4041 if_test->origin = p;
4042
4043 if_end = new_prio();
4044 if_end->type = AEL_APPCALL;
4045 snprintf(buf1, BUF_SIZE, "Finish iftime_%s_%d", label, control_statement_count);
4046 if_end->app = strdup("NoOp");
4047 if_end->appargs = strdup(buf1);
4048
4049 if (p->u3.else_statements) {
4050 if_skip = new_prio();
4051 if_skip->type = AEL_CONTROL1; /* simple goto */
4052 if_skip->goto_true = if_end;
4053 if_skip->origin = p;
4054
4055 } else {
4056 if_skip = 0;
4057
4058 if_test->goto_false = if_end;
4059 }
4060
4061 if_false = new_prio();
4062 if_false->type = AEL_CONTROL1;
4063 if (p->u3.else_statements) {
4064 if_false->goto_true = if_skip; /* +1 */
4065 } else {
4066 if_false->goto_true = if_end;
4067 }
4068
4069 /* link & load! */
4070 linkprio(exten, if_test, mother_exten);
4071 linkprio(exten, if_false, mother_exten);
4072
4073 /* now, put the body of the if here */
4074
4075 if (gen_prios(exten, new_label, p->u2.statements, mother_exten, this_context)) { /* this will link in all the statements here */
4076 return -1;
4077 }
4078
4079 if (p->u3.else_statements) {
4080 linkprio(exten, if_skip, mother_exten);
4081 if (gen_prios(exten, new_label, p->u3.else_statements, mother_exten, this_context)) { /* this will link in all the statements here */
4082 return -1;
4083 }
4084 }
4085
4086 linkprio(exten, if_end, mother_exten);
4087
4088 break;
4089
4090 case PV_RANDOM:
4091 case PV_IF:
4093 snprintf(new_label, BUF_SIZE, "if_%s_%d", label, control_statement_count);
4094
4095 if_test = new_prio();
4096 if_end = new_prio();
4097 if_test->type = AEL_IF_CONTROL;
4098 if_end->type = AEL_APPCALL;
4099 if ( p->type == PV_RANDOM )
4100 snprintf(buf1, BUF_SIZE, "$[${RAND(0,99)} < (%s)]", p->u1.str);
4101 else
4102 snprintf(buf1, BUF_SIZE, "$[%s]", p->u1.str);
4103 if_test->app = 0;
4104 if_test->appargs = strdup(buf1);
4105 snprintf(buf1, BUF_SIZE, "Finish if_%s_%d", label, control_statement_count);
4106 if_end->app = strdup("NoOp");
4107 if_end->appargs = strdup(buf1);
4108 if_test->origin = p;
4109
4110 if (p->u3.else_statements) {
4111 if_skip = new_prio();
4112 if_skip->type = AEL_CONTROL1; /* simple goto */
4113 if_skip->goto_true = if_end;
4114 if_test->goto_false = if_skip;;
4115 } else {
4116 if_skip = 0;
4117 if_test->goto_false = if_end;;
4118 }
4119
4120 /* link & load! */
4121 linkprio(exten, if_test, mother_exten);
4122
4123 /* now, put the body of the if here */
4124
4125 if (gen_prios(exten, new_label, p->u2.statements, mother_exten, this_context)) { /* this will link in all the statements here */
4126 return -1;
4127 }
4128
4129 if (p->u3.else_statements) {
4130 linkprio(exten, if_skip, mother_exten);
4131 if (gen_prios(exten, new_label, p->u3.else_statements, mother_exten, this_context)) { /* this will link in all the statements here */
4132 return -1;
4133 }
4134 }
4135
4136 linkprio(exten, if_end, mother_exten);
4137
4138 break;
4139
4140 case PV_STATEMENTBLOCK:
4141 if (gen_prios(exten, label, p->u1.list, mother_exten, this_context)) { /* recurse into the block */
4142 return -1;
4143 }
4144 break;
4145
4146 case PV_CATCH:
4148 /* generate an extension with name of catch, put all catch stats
4149 into this exten! */
4150 switch_case = new_exten();
4151 if (mother_exten && mother_exten->checked_switch) {
4152 switch_case->has_switch = mother_exten->has_switch;
4153 switch_case->checked_switch = mother_exten->checked_switch;
4154 }
4155 if (exten && exten->checked_switch) {
4156 switch_case->has_switch = exten->has_switch;
4157 switch_case->checked_switch = exten->checked_switch;
4158 }
4159
4160 switch_case->context = this_context;
4161 linkexten(exten,switch_case);
4162 switch_case->name = strdup(p->u1.str);
4163 snprintf(new_label, BUF_SIZE, "catch_%s_%d",p->u1.str, control_statement_count);
4164
4165 if (gen_prios(switch_case, new_label, p->u2.statements, mother_exten,this_context)) { /* this will link in all the catch body statements here */
4166 return -1;
4167 }
4168 if (switch_case->return_needed) { /* returns now generate a Return() app call, no longer a goto to the end of the exten */
4169 char buf[2000];
4170 struct ael_priority *np2 = new_prio();
4171 np2->type = AEL_APPCALL;
4172 np2->app = strdup("NoOp");
4173 snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name);
4174 np2->appargs = strdup(buf);
4175 linkprio(switch_case, np2, mother_exten);
4176 switch_case-> return_target = np2;
4177 }
4178
4179 break;
4180 default:
4181 break;
4182 }
4183 }
4184 return 0;
4185}
4186
4188{
4189 int i;
4190 struct ael_priority *pr;
4191 do {
4192 if (exten->is_switch)
4193 i = 10;
4194 else if (exten->regexten)
4195 i=2;
4196 else
4197 i=1;
4198
4199 for (pr=exten->plist; pr; pr=pr->next) {
4200 pr->priority_num = i;
4201
4202 if (!pr->origin || (pr->origin && pr->origin->type != PV_LABEL) ) /* Labels don't show up in the dialplan,
4203 but we want them to point to the right
4204 priority, which would be the next line
4205 after the label; */
4206 i++;
4207 }
4208
4210 } while ( exten );
4211}
4212
4214{
4215 struct ael_priority *pr;
4216 char *label=0;
4217 char realext[AST_MAX_EXTENSION];
4218 if (!exten) {
4219 ast_log(LOG_WARNING, "This file is Empty!\n" );
4220 return;
4221 }
4222 do {
4223 struct ael_priority *last = 0;
4224
4225 pbx_substitute_variables_helper(NULL, exten->name, realext, sizeof(realext) - 1);
4226 if (exten->hints) {
4227 if (ast_add_extension2(exten->context, 0 /*no replace*/, realext, PRIORITY_HINT, NULL, exten->cidmatch,
4229 ast_log(LOG_WARNING, "Unable to add step at priority 'hint' of extension '%s'\n",
4230 exten->name);
4231 }
4232 }
4233
4234 for (pr=exten->plist; pr; pr=pr->next) {
4235 char app[2000];
4236 char appargs[2000];
4237
4238 /* before we can add the extension, we need to prep the app/appargs;
4239 the CONTROL types need to be done after the priority numbers are calculated.
4240 */
4241 if (pr->type == AEL_LABEL) /* don't try to put labels in the dialplan! */ {
4242 last = pr;
4243 continue;
4244 }
4245
4246 if (pr->app)
4247 strcpy(app, pr->app);
4248 else
4249 app[0] = 0;
4250 if (pr->appargs )
4251 strcpy(appargs, pr->appargs);
4252 else
4253 appargs[0] = 0;
4254 switch( pr->type ) {
4255 case AEL_APPCALL:
4256 /* easy case. Everything is all set up */
4257 break;
4258
4259 case AEL_CONTROL1: /* FOR loop, WHILE loop, BREAK, CONTINUE, IF, IFTIME */
4260 /* simple, unconditional goto. */
4261 strcpy(app,"Goto");
4262 if (pr->goto_true->origin && pr->goto_true->origin->type == PV_SWITCH ) {
4263 snprintf(appargs,sizeof(appargs),"%s,%d", pr->goto_true->exten->name, pr->goto_true->priority_num);
4264 } else if (pr->goto_true->origin && pr->goto_true->origin->type == PV_IFTIME && pr->goto_true->origin->u3.else_statements ) {
4265 snprintf(appargs,sizeof(appargs),"%d", pr->goto_true->priority_num+1);
4266 } else
4267 snprintf(appargs,sizeof(appargs),"%d", pr->goto_true->priority_num);
4268 break;
4269
4270 case AEL_FOR_CONTROL: /* WHILE loop test, FOR loop test */
4271 strcpy(app,"GotoIf");
4272 snprintf(appargs,sizeof(appargs),"%s?%d:%d", pr->appargs, pr->priority_num+1, pr->goto_false->priority_num);
4273 break;
4274
4275 case AEL_IF_CONTROL:
4276 strcpy(app,"GotoIf");
4277 if (pr->origin->u3.else_statements )
4278 snprintf(appargs,sizeof(appargs),"%s?%d:%d", pr->appargs, pr->priority_num+1, pr->goto_false->priority_num+1);
4279 else
4280 snprintf(appargs,sizeof(appargs),"%s?%d:%d", pr->appargs, pr->priority_num+1, pr->goto_false->priority_num);
4281 break;
4282
4283 case AEL_RAND_CONTROL:
4284 strcpy(app,"Random");
4285 snprintf(appargs,sizeof(appargs),"%s:%d", pr->appargs, pr->goto_true->priority_num+1);
4286 break;
4287
4288 case AEL_IFTIME_CONTROL:
4289 strcpy(app,"GotoIfTime");
4290 snprintf(appargs,sizeof(appargs),"%s?%d", pr->appargs, pr->priority_num+2);
4291 break;
4292
4293 case AEL_RETURN:
4294 strcpy(app,"Return");
4295 appargs[0] = 0;
4296 break;
4297
4298 default:
4299 break;
4300 }
4301 if (last && last->type == AEL_LABEL ) {
4302 label = last->origin->u1.str;
4303 }
4304 else
4305 label = 0;
4306
4307 if (ast_add_extension2(exten->context, 0 /*no replace*/, realext, pr->priority_num, (label?label:NULL), exten->cidmatch,
4309 ast_log(LOG_WARNING, "Unable to add step at priority '%d' of extension '%s'\n", pr->priority_num,
4310 exten->name);
4311 }
4312 last = pr;
4313 }
4315 } while ( exten );
4316}
4317
4318static void attach_exten(struct ael_extension **list, struct ael_extension *newmem)
4319{
4320 /* travel to the end of the list... */
4321 struct ael_extension *lptr;
4322 if( !*list ) {
4323 *list = newmem;
4324 return;
4325 }
4326 lptr = *list;
4327
4328 while( lptr->next_exten ) {
4329 lptr = lptr->next_exten;
4330 }
4331 /* lptr should now pointing to the last element in the list; it has a null next_exten pointer */
4332 lptr->next_exten = newmem;
4333}
4334
4336{
4337 while( p && p->type != PV_EXTENSION && p->type != PV_CONTEXT && p->type != PV_MACRO ) {
4338
4339 p = p->dad;
4340 }
4341
4342 return p;
4343}
4344
4346{
4347 while( p && p->type != PV_CONTEXT && p->type != PV_MACRO ) {
4348
4349 p = p->dad;
4350 }
4351
4352 return p;
4353}
4354
4355static void fix_gotos_in_extensions(struct ael_extension *exten)
4356{
4357 struct ael_extension *e;
4358 for(e=exten;e;e=e->next_exten) {
4359
4360 struct ael_priority *p;
4361 for(p=e->plist;p;p=p->next) {
4362
4363 if( p->origin && p->origin->type == PV_GOTO && p->origin->u3.goto_target_in_case ) {
4364
4365 /* fix the extension of the goto target to the actual extension in the post-compiled dialplan */
4366
4367 pval *target = p->origin->u2.goto_target;
4368 struct ael_extension *z = target->u3.compiled_label;
4369 pval *pv2 = p->origin;
4370 char buf1[500];
4371 char *apparg_save = p->appargs;
4372
4373 p->appargs = 0;
4374 if (!pv2->u1.list->next) /* just one -- it won't hurt to repeat the extension */ {
4375 snprintf(buf1,sizeof(buf1),"%s,%s", z->name, pv2->u1.list->u1.str);
4376 p->appargs = strdup(buf1);
4377
4378 } else if (pv2->u1.list->next && !pv2->u1.list->next->next) /* two */ {
4379 snprintf(buf1,sizeof(buf1),"%s,%s", z->