0674ba8ff16407d835b160ce6fabc989ab3a1c68
[asterisk/asterisk.git] / res / ael / ael.flex
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2006, Digium, Inc.
5  *
6  * Steve Murphy <murf@parsetree.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18 /*! \file
19  *
20  * \brief Flex scanner description of tokens used in AEL2 .
21  *
22  */
23
24 /*
25  * Start with flex options:
26  *
27  * %x describes the contexts we have: paren, semic and argg, plus INITIAL
28  */
29 %x paren semic argg  comment curlystate wordstate brackstate
30
31 /* prefix used for various globally-visible functions and variables.
32  * This renames also yywrap, but since we do not use it, we just
33  * add option noyywrap to remove it.
34  */
35 %option prefix="ael_yy"
36 %option noyywrap 8bit
37
38 /* I specify this option to suppress flex generating code with ECHO
39   in it. This generates compiler warnings in some systems; We've
40   seen the fwrite generate Unused variable warnings with 4.1.2 gcc.
41   Some systems have tweaked flex ECHO macro to keep the compiler
42   happy.  To keep the warning message from getting output, I added
43   a default rule at the end of the patterns section */
44 %option nodefault
45
46 /* yyfree normally just frees its arg. It can be null sometimes,
47    which some systems will complain about, so, we'll define our own version */
48 %option noyyfree
49
50 /* batch gives a bit more performance if we are using it in
51  * a non-interactive mode. We probably don't care much.
52  */
53 %option batch
54
55 /* outfile is the filename to be used instead of lex.yy.c */
56 %option outfile="ael_lex.c"
57
58 /*
59  * These are not supported in flex 2.5.4, but we need them
60  * at the moment:
61  * reentrant produces a thread-safe parser. Not 100% sure that
62  * we require it, though.
63  * bison-bridge passes an additional yylval argument to yylex().
64  * bison-locations is probably not needed.
65  */
66 %option reentrant
67 %option bison-bridge
68 %option bison-locations
69
70 %{
71 #include "asterisk.h"
72 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
73
74 #include <sys/types.h>
75 #include <sys/stat.h>
76 #include <unistd.h>
77 #include <glob.h>
78
79 #if !defined(GLOB_ABORTED)
80 #define GLOB_ABORTED GLOB_ABEND
81 #endif
82
83 #include "asterisk/logger.h"
84 #include "asterisk/utils.h"
85 #include "asterisk/lock.h"
86 #include "asterisk/hashtab.h"
87 #include "ael/ael.tab.h"
88 #include "asterisk/ael_structs.h"
89
90 /*
91  * A stack to keep track of matching brackets ( [ { } ] )
92  */
93 static char pbcstack[400];      /* XXX missing size checks */
94 static int pbcpos = 0;
95 static void pbcpush(char x);
96 static int pbcpop(char x);
97 static int parencount = 0;
98
99 /*
100  * A similar stack to keep track of matching brackets ( [ { } ] ) in word tokens surrounded by ${ ... }
101  */
102 static char pbcstack2[400];     /* XXX missing size checks */
103 static int pbcpos2 = 0;
104 static void pbcpush2(char x);
105 static int pbcpop2(char x);
106 static int parencount2 = 0;
107
108 /*
109  * A similar stack to keep track of matching brackets ( [ { } ] ) in word tokens surrounded by $[ ... ]
110  */
111 static char pbcstack3[400];     /* XXX missing size checks */
112 static int pbcpos3 = 0;
113 static void pbcpush3(char x);
114 static int pbcpop3(char x);
115 static int parencount3 = 0;
116
117
118 /*
119  * current line, column and filename, updated as we read the input.
120  */
121 static int my_lineno = 1;       /* current line in the source */
122 static int my_col = 1;          /* current column in the source */
123 char *my_file = 0;              /* used also in the bison code */
124 char *prev_word;                /* XXX document it */
125
126 #define MAX_INCLUDE_DEPTH 50
127
128 /*
129  * flex is not too smart, and generates global functions
130  * without prototypes so the compiler may complain.
131  * To avoid that, we declare the prototypes here,
132  * even though these functions are not used.
133  */
134 int ael_yyget_column  (yyscan_t yyscanner);
135 void ael_yyset_column (int  column_no , yyscan_t yyscanner);
136
137 int ael_yyparse (struct parse_io *);
138
139 /*
140  * A stack to process include files.
141  * As we switch into the new file we need to store the previous
142  * state to restore it later.
143  */
144 struct stackelement {
145         char *fname;
146         int lineno;
147         int colno;
148         glob_t globbuf;        /* the current globbuf */
149         int globbuf_pos;   /* where we are in the current globbuf */
150         YY_BUFFER_STATE bufstate;
151 };
152
153 static struct stackelement  include_stack[MAX_INCLUDE_DEPTH];
154 static int include_stack_index = 0;
155 static void setup_filestack(char *fnamebuf, int fnamebuf_siz, glob_t *globbuf, int globpos, yyscan_t xscan, int create);
156
157 /*
158  * if we use the @n feature of bison, we must supply the start/end
159  * location of tokens in the structure pointed by yylloc.
160  * Simple tokens are just assumed to be on the same line, so
161  * the line number is constant, and the column is incremented
162  * by the length of the token.
163  */
164 #ifdef FLEX_BETA        /* set for 2.5.33 */
165
166 /* compute the total number of lines and columns in the text
167  * passed as argument.
168  */
169 static void pbcwhere(const char *text, int *line, int *col )
170 {
171         int loc_line = *line;
172         int loc_col = *col;
173         char c;
174         while ( (c = *text++) ) {
175                 if ( c == '\t' ) {
176                         loc_col += 8 - (loc_col % 8);
177                 } else if ( c == '\n' ) {
178                         loc_line++;
179                         loc_col = 1;
180                 } else
181                         loc_col++;
182         }
183         *line = loc_line;
184         *col = loc_col;
185 }
186
187 #define STORE_POS do {                                                  \
188                 yylloc->first_line = yylloc->last_line = my_lineno;     \
189                 yylloc->first_column=my_col;                            \
190                 yylloc->last_column=my_col+yyleng-1;                    \
191                 my_col+=yyleng;                                         \
192         } while (0)
193
194 #define STORE_LOC do {                                  \
195                 yylloc->first_line = my_lineno;         \
196                 yylloc->first_column=my_col;            \
197                 pbcwhere(yytext, &my_lineno, &my_col);  \
198                 yylloc->last_line = my_lineno;          \
199                 yylloc->last_column = my_col - 1;       \
200         } while (0)
201 #else
202 #define STORE_POS
203 #define STORE_LOC
204 #endif
205 %}
206
207 KEYWORD     (context|abstract|extend|macro|globals|local|ignorepat|switch|if|ifTime|random|regexten|hint|else|goto|jump|return|break|continue|for|while|case|default|pattern|catch|switches|eswitches|includes)
208
209 NOPARENS        ([^()\[\]\{\}]|\\[()\[\]\{\}])*
210
211 NOARGG          ([^(),\{\}\[\]]|\\[,()\[\]\{\}])*
212
213 NOSEMIC         ([^;()\{\}\[\]]|\\[;()\[\]\{\}])*
214
215 HIBIT           [\x80-\xff]
216
217 %%
218
219 \{              { STORE_POS; return LC;}
220 \}              { STORE_POS; return RC;}
221 \(              { STORE_POS; return LP;}
222 \)              { STORE_POS; return RP;}
223 \;              { STORE_POS; return SEMI;}
224 \=              { STORE_POS; return EQ;}
225 \,              { STORE_POS; return COMMA;}
226 \:              { STORE_POS; return COLON;}
227 \&              { STORE_POS; return AMPER;}
228 \|              { STORE_POS; return BAR;}
229 \=\>            { STORE_POS; return EXTENMARK;}
230 \@              { STORE_POS; return AT;}
231 \/\/[^\n]*      {/*comment*/}
232 context         { STORE_POS; return KW_CONTEXT;}
233 abstract        { STORE_POS; return KW_ABSTRACT;}
234 extend          { STORE_POS; return KW_EXTEND;}
235 macro           { STORE_POS; return KW_MACRO;};
236 globals         { STORE_POS; return KW_GLOBALS;}
237 local           { STORE_POS; return KW_LOCAL;}
238 ignorepat       { STORE_POS; return KW_IGNOREPAT;}
239 switch          { STORE_POS; return KW_SWITCH;}
240 if              { STORE_POS; return KW_IF;}
241 ifTime          { STORE_POS; return KW_IFTIME;}
242 random          { STORE_POS; return KW_RANDOM;}
243 regexten        { STORE_POS; return KW_REGEXTEN;}
244 hint            { STORE_POS; return KW_HINT;}
245 else            { STORE_POS; return KW_ELSE;}
246 goto            { STORE_POS; return KW_GOTO;}
247 jump            { STORE_POS; return KW_JUMP;}
248 return          { STORE_POS; return KW_RETURN;}
249 break           { STORE_POS; return KW_BREAK;}
250 continue        { STORE_POS; return KW_CONTINUE;}
251 for             { STORE_POS; return KW_FOR;}
252 while           { STORE_POS; return KW_WHILE;}
253 case            { STORE_POS; return KW_CASE;}
254 default         { STORE_POS; return KW_DEFAULT;}
255 pattern         { STORE_POS; return KW_PATTERN;}
256 catch           { STORE_POS; return KW_CATCH;}
257 switches        { STORE_POS; return KW_SWITCHES;}
258 eswitches       { STORE_POS; return KW_ESWITCHES;}
259 includes        { STORE_POS; return KW_INCLUDES;}
260 "/*"            { BEGIN(comment); my_col += 2; }
261
262 <comment>[^*\n]*        { my_col += yyleng; }
263 <comment>[^*\n]*\n      { ++my_lineno; my_col=1;}
264 <comment>"*"+[^*/\n]*   { my_col += yyleng; }
265 <comment>"*"+[^*/\n]*\n         { ++my_lineno; my_col=1;}
266 <comment>"*/"           { my_col += 2; BEGIN(INITIAL); } /* the nice thing about comments is that you know exactly what ends them */
267
268 \n              { my_lineno++; my_col = 1; }
269 [ ]+            { my_col += yyleng; }
270 [\t]+           { my_col += (yyleng*8)-(my_col%8); }
271
272 ({KEYWORD}?[-a-zA-Z0-9'"_/.\<\>\*\+!$#\[\]]|{HIBIT}|(\\.)|(\$\{)|(\$\[)) { 
273       /* boy did I open a can of worms when I changed the lexical token "word". 
274                  all the above keywords can be used as a beginning to a "word".-
275                  before, a "word" would match a longer sequence than the above   
276              keywords, and all would be well. But now "word" is a single char           
277              and feeds into a statemachine sort of sequence from there on. So...
278                  I added the {KEYWORD}? to the beginning of the word match sequence */
279
280                 if (!strcmp(yytext,"${")) {
281                         parencount2 = 0;
282                         pbcpos2 = 0;
283                         pbcpush2('{');  /* push '{' so the last pcbpop (parencount2 = -1) will succeed */
284                         BEGIN(curlystate);
285                         yymore();
286                 } else if (!strcmp(yytext,"$[")) {
287                         parencount3 = 0;
288                         pbcpos3 = 0;
289                         pbcpush3('[');  /* push '[' so the last pcbpop (parencount3 = -1) will succeed */
290                         BEGIN(brackstate);
291                         yymore();
292                 } else {
293                     BEGIN(wordstate);
294                         yymore();
295                 }
296         }
297
298 <wordstate>[-a-zA-Z0-9'"_/.\<\>\*\+!$#\[\]] { yymore(); /* Keep going */ }
299 <wordstate>{HIBIT} { yymore(); /* Keep going */ }
300 <wordstate>(\\.)  { yymore(); /* Keep Going */ }
301 <wordstate>(\$\{)  { /* the beginning of a ${} construct. prepare and pop into curlystate */
302                 parencount2 = 0;
303                 pbcpos2 = 0;
304                 pbcpush2('{');  /* push '{' so the last pcbpop (parencount2 = -1) will succeed */
305                 BEGIN(curlystate);
306                 yymore();
307         }
308 <wordstate>(\$\[)  { /* the beginning of a $[] construct. prepare and pop into brackstate */
309                 parencount3 = 0;
310                 pbcpos3 = 0;
311                 pbcpush3('[');  /* push '[' so the last pcbpop (parencount3 = -1) will succeed */
312                 BEGIN(brackstate);
313                 yymore();
314         }
315 <wordstate>([^a-zA-Z0-9\x80-\xff\x2d'"_/.\<\>\*\+!$#\[\]]) {
316                 /* a non-word constituent char, like a space, tab, curly, paren, etc */
317                 char c = yytext[yyleng-1];
318                 STORE_POS;
319                 yylval->str = malloc(yyleng);
320                 strncpy(yylval->str, yytext, yyleng);
321                 yylval->str[yyleng-1] = 0;
322                 unput(c);  /* put this ending char back in the stream */
323                 BEGIN(0);
324                 return word;
325         }
326
327
328 <curlystate>{NOPARENS}\}        {
329                 if ( pbcpop2('}') ) {   /* error */
330                         STORE_LOC;
331                         ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Mismatched ')' in expression: %s !\n", my_file, my_lineno, my_col, yytext);
332                         BEGIN(0);
333                         yylval->str = malloc(yyleng+1);
334                         strncpy(yylval->str, yytext, yyleng);
335                         yylval->str[yyleng] = 0;
336                         return word;
337                 }
338                 parencount2--;
339                 if ( parencount2 >= 0) {
340                         yymore();
341                 } else {
342                         BEGIN(wordstate); /* Finished with the current ${} construct. Return to word gathering state */
343                         yymore();
344                 }
345         }
346
347 <curlystate>{NOPARENS}[\(\[\{]  { 
348                 char c = yytext[yyleng-1];
349                 if (c == '{')
350                         parencount2++;
351                 pbcpush2(c);
352                 yymore();
353         }
354
355 <curlystate>{NOPARENS}[\]\)]    { 
356                 char c = yytext[yyleng-1];
357                 if ( pbcpop2(c))  { /* error */
358                         STORE_LOC;
359                         ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Mismatched '%c' in expression!\n",
360                                 my_file, my_lineno, my_col, c);
361                         BEGIN(0);
362                         yylval->str = malloc(yyleng+1);
363                         strncpy(yylval->str, yytext, yyleng);
364                         yylval->str[yyleng] = 0;
365                         return word;
366                 }
367                 yymore();
368         }
369
370
371 <brackstate>{NOPARENS}\]        {
372                 if ( pbcpop3(']') ) {   /* error */
373                         STORE_LOC;
374                         ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Mismatched ')' in expression: %s !\n", my_file, my_lineno, my_col, yytext);
375                         BEGIN(0);
376                         yylval->str = malloc(yyleng+1);
377                         strncpy(yylval->str, yytext, yyleng);
378                         yylval->str[yyleng] = 0;
379                         return word;
380                 }
381                 parencount3--;
382                 if ( parencount3 >= 0) {
383                         yymore();
384                 } else {
385                         BEGIN(wordstate); /* Finished with the current ${} construct. Return to word gathering state */
386                         yymore();
387                 }
388         }
389
390 <brackstate>{NOPARENS}[\(\[\{]  { 
391                 char c = yytext[yyleng-1];
392                 if (c == '[')
393                         parencount3++;
394                 pbcpush3(c);
395                 yymore();
396         }
397
398 <brackstate>{NOPARENS}[\}\)]    { 
399                 char c = yytext[yyleng-1];
400                 if ( pbcpop3(c))  { /* error */
401                         STORE_LOC;
402                         ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Mismatched '%c' in expression!\n",
403                                 my_file, my_lineno, my_col, c);
404                         BEGIN(0);
405                         yylval->str = malloc(yyleng+1);
406                         strncpy(yylval->str, yytext, yyleng);
407                         yylval->str[yyleng] = 0;
408                         return word;
409                 }
410                 yymore();
411         }
412
413
414         /*
415          * context used for arguments of if_head, random_head, switch_head,
416          * for (last statement), while (XXX why not iftime_head ?).
417          * End with the matching parentheses.
418          * A comma at the top level is valid here, unlike in argg where it
419          * is an argument separator so it must be returned as a token.
420          */
421 <paren>{NOPARENS}\)     {
422                 if ( pbcpop(')') ) {    /* error */
423                         STORE_LOC;
424                         ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Mismatched ')' in expression: %s !\n", my_file, my_lineno, my_col, yytext);
425                         BEGIN(0);
426                         yylval->str = malloc(yyleng+1);
427                         strncpy(yylval->str, yytext, yyleng);
428                         yylval->str[yyleng] = 0;
429                         prev_word = 0;
430                         return word;
431                 }
432                 parencount--;
433                 if ( parencount >= 0) {
434                         yymore();
435                 } else {
436                         STORE_LOC;
437                         yylval->str = malloc(yyleng);
438                         strncpy(yylval->str, yytext, yyleng);
439                         yylval->str[yyleng-1] = 0;
440                         unput(')');
441                         BEGIN(0);
442                         return word;
443                 }
444         }
445
446 <paren>{NOPARENS}[\(\[\{]       {
447                 char c = yytext[yyleng-1];
448                 if (c == '(')
449                         parencount++;
450                 pbcpush(c);
451                 yymore();
452         }
453
454 <paren>{NOPARENS}[\]\}] {
455                 char c = yytext[yyleng-1];
456                 if ( pbcpop(c))  { /* error */
457                         STORE_LOC;
458                         ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Mismatched '%c' in expression!\n",
459                                 my_file, my_lineno, my_col, c);
460                         BEGIN(0);
461                         yylval->str = malloc(yyleng+1);
462                         strncpy(yylval->str, yytext, yyleng);
463                         yylval->str[yyleng] = 0;
464                         return word;
465                 }
466                 yymore();
467         }
468
469
470         /*
471          * handlers for arguments to a macro or application calls.
472          * We enter this context when we find the initial '(' and
473          * stay here until we close all matching parentheses,
474          * and find the comma (argument separator) or the closing ')'
475          * of the (external) call, which happens when parencount == 0
476          * before the decrement.
477          */
478 <argg>{NOARGG}[\(\[\{]    {
479                 char c = yytext[yyleng-1];
480                 if (c == '(')
481                         parencount++;
482                 pbcpush(c);
483                 yymore();
484         }
485
486 <argg>{NOARGG}\)        {
487                 if ( pbcpop(')') ) { /* error */
488                         STORE_LOC;
489                         ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Mismatched ')' in expression!\n", my_file, my_lineno, my_col);
490                         BEGIN(0);
491                         yylval->str = malloc(yyleng+1);
492                         strncpy(yylval->str, yytext, yyleng);
493                         yylval->str[yyleng] = 0;
494                         return word;
495                 }
496
497                 parencount--;
498                 if( parencount >= 0){
499                         yymore();
500                 } else {
501                         STORE_LOC;
502                         BEGIN(0);
503                         if ( !strcmp(yytext, ")") )
504                                 return RP;
505                         yylval->str = malloc(yyleng);
506                         strncpy(yylval->str, yytext, yyleng);
507                         yylval->str[yyleng-1] = '\0'; /* trim trailing ')' */
508                         unput(')');
509                         return word;
510                 }
511         }
512
513 <argg>{NOARGG}\,        {
514                 if( parencount != 0) { /* ast_log(LOG_NOTICE,"Folding in a comma!\n"); */
515                         yymore();
516                 } else  {
517                         STORE_LOC;
518                         if( !strcmp(yytext,"," ) )
519                                 return COMMA;
520                         yylval->str = malloc(yyleng);
521                         strncpy(yylval->str, yytext, yyleng);
522                         yylval->str[yyleng-1] = '\0'; /* trim trailing ',' */
523                         unput(',');
524                         return word;
525                 }
526         }
527
528 <argg>{NOARGG}[\]\}]    {
529                 char c = yytext[yyleng-1];
530                 if ( pbcpop(c) ) { /* error */
531                         STORE_LOC;
532                         ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Mismatched '%c' in expression!\n", my_file, my_lineno, my_col, c);
533                         BEGIN(0);
534                         yylval->str = malloc(yyleng+1);
535                         strncpy(yylval->str, yytext, yyleng);
536                         yylval->str[yyleng] = '\0';
537                         return word;
538                 }
539                 yymore();
540         }
541
542         /*
543          * context used to find tokens in the right hand side of assignments,
544          * or in the first and second operand of a 'for'. As above, match
545          * commas and use ';' as a separator (hence return it as a separate token).
546          */
547 <semic>{NOSEMIC}[\(\[\{]        {
548                 char c = yytext[yyleng-1];
549                 yymore();
550                 pbcpush(c);
551         }
552
553 <semic>{NOSEMIC}[\)\]\}]        {
554                 char c = yytext[yyleng-1];
555                 if ( pbcpop(c) ) { /* error */
556                         STORE_LOC;
557                         ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Mismatched '%c' in expression!\n", my_file, my_lineno, my_col, c);
558                         BEGIN(0);
559                         yylval->str = malloc(yyleng+1);
560                         strncpy(yylval->str, yytext, yyleng);
561                         yylval->str[yyleng] = '\0';
562                         return word;
563                 }
564                 yymore();
565         }
566
567 <semic>{NOSEMIC};       {
568                 STORE_LOC;
569                 yylval->str = malloc(yyleng);
570                 strncpy(yylval->str, yytext, yyleng);
571                 yylval->str[yyleng-1] = '\0'; /* trim trailing ';' */
572                 unput(';');
573                 BEGIN(0);
574                 return word;
575         }
576
577 \#include[ \t]+\"[^\"]+\" {
578                 char fnamebuf[1024],*p1,*p2;
579                 int glob_ret;
580                 glob_t globbuf;        /* the current globbuf */
581                 int globbuf_pos = -1;   /* where we are in the current globbuf */
582                 globbuf.gl_offs = 0;    /* initialize it to silence gcc */
583                 
584                 p1 = strchr(yytext,'"');
585                 p2 = strrchr(yytext,'"');
586                 if ( include_stack_index >= MAX_INCLUDE_DEPTH ) {
587                         ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Includes nested too deeply! Wow!!! How did you do that?\n", my_file, my_lineno, my_col);
588                 } else if ( (int)(p2-p1) > sizeof(fnamebuf) - 1 ) {
589                         ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Filename is incredibly way too long (%d chars!). Inclusion ignored!\n", my_file, my_lineno, my_col, yyleng - 10);
590                 } else {
591                         strncpy(fnamebuf, p1+1, p2-p1-1);
592                         fnamebuf[p2-p1-1] = 0;
593                 if (fnamebuf[0] != '/') {
594                    char fnamebuf2[1024];
595                    snprintf(fnamebuf2,sizeof(fnamebuf2), "%s/%s", (char *)ast_config_AST_CONFIG_DIR, fnamebuf);
596                    ast_copy_string(fnamebuf,fnamebuf2,sizeof(fnamebuf));
597                 }
598 #ifdef SOLARIS
599                         glob_ret = glob(fnamebuf, GLOB_NOCHECK, NULL, &globbuf);
600 #else
601                         glob_ret = glob(fnamebuf, GLOB_NOMAGIC|GLOB_BRACE, NULL, &globbuf);
602 #endif
603                         if (glob_ret == GLOB_NOSPACE) {
604                                 ast_log(LOG_WARNING,
605                                         "Glob Expansion of pattern '%s' failed: Not enough memory\n", fnamebuf);
606                         } else if (glob_ret  == GLOB_ABORTED) {
607                                 ast_log(LOG_WARNING,
608                                         "Glob Expansion of pattern '%s' failed: Read error\n", fnamebuf);
609                         } else if (glob_ret  == GLOB_NOMATCH) {
610                                 ast_log(LOG_WARNING,
611                                         "Glob Expansion of pattern '%s' failed: No matches!\n", fnamebuf);
612                         } else {
613                           globbuf_pos = 0;
614                         }
615                 }
616                 if (globbuf_pos > -1) {
617                         setup_filestack(fnamebuf, sizeof(fnamebuf), &globbuf, 0, yyscanner, 1);
618                 }
619         }
620
621
622 <<EOF>>         {
623                 char fnamebuf[2048];
624                 if (include_stack_index > 0 && include_stack[include_stack_index-1].globbuf_pos < include_stack[include_stack_index-1].globbuf.gl_pathc-1) {
625                         yy_delete_buffer( YY_CURRENT_BUFFER, yyscanner );
626                         include_stack[include_stack_index-1].globbuf_pos++;
627                         setup_filestack(fnamebuf, sizeof(fnamebuf), &include_stack[include_stack_index-1].globbuf, include_stack[include_stack_index-1].globbuf_pos, yyscanner, 0);
628                         /* finish this */                       
629                         
630                 } else {
631                         if (include_stack[include_stack_index].fname) {
632                                 free(include_stack[include_stack_index].fname);
633                                 include_stack[include_stack_index].fname = 0;
634                         }
635                         if (my_file) {
636                                 free(my_file);
637                                 my_file = 0;
638                         }
639                         if ( --include_stack_index < 0 ) {
640                                 yyterminate();
641                         } else {
642                                 globfree(&include_stack[include_stack_index].globbuf);
643                                 include_stack[include_stack_index].globbuf_pos = -1;
644                                 
645                                 yy_delete_buffer( YY_CURRENT_BUFFER, yyscanner );
646                                 yy_switch_to_buffer(include_stack[include_stack_index].bufstate, yyscanner );
647                                 my_lineno = include_stack[include_stack_index].lineno;
648                                 my_col    = include_stack[include_stack_index].colno;
649                                 my_file   = strdup(include_stack[include_stack_index].fname);
650                         }
651                 }
652         }
653
654 <*>.|\n         { /* default rule */ ast_log(LOG_ERROR,"Unhandled char(s): %s\n", yytext); }
655
656 %%
657
658 static void pbcpush(char x)
659 {
660         pbcstack[pbcpos++] = x;
661 }
662
663 void ael_yyfree(void *ptr, yyscan_t yyscanner)
664 {
665         if (ptr)
666                 free( (char*) ptr );
667 }
668
669 static int pbcpop(char x)
670 {
671         if (   ( x == ')' && pbcstack[pbcpos-1] == '(' )
672                 || ( x == ']' && pbcstack[pbcpos-1] == '[' )
673                 || ( x == '}' && pbcstack[pbcpos-1] == '{' )) {
674                 pbcpos--;
675                 return 0;
676         }
677         return 1; /* error */
678 }
679
680 static void pbcpush2(char x)
681 {
682         pbcstack2[pbcpos2++] = x;
683 }
684
685 static int pbcpop2(char x)
686 {
687         if (   ( x == ')' && pbcstack2[pbcpos2-1] == '(' )
688                 || ( x == ']' && pbcstack2[pbcpos2-1] == '[' )
689                 || ( x == '}' && pbcstack2[pbcpos2-1] == '{' )) {
690                 pbcpos2--;
691                 return 0;
692         }
693         return 1; /* error */
694 }
695
696 static void pbcpush3(char x)
697 {
698         pbcstack3[pbcpos3++] = x;
699 }
700
701 static int pbcpop3(char x)
702 {
703         if (   ( x == ')' && pbcstack3[pbcpos3-1] == '(' )
704                 || ( x == ']' && pbcstack3[pbcpos3-1] == '[' )
705                 || ( x == '}' && pbcstack3[pbcpos3-1] == '{' )) {
706                 pbcpos3--;
707                 return 0;
708         }
709         return 1; /* error */
710 }
711
712 static int c_prevword(void)
713 {
714         char *c = prev_word;
715         if (c == NULL)
716                 return 0;
717         while ( *c ) {
718                 switch (*c) {
719                 case '{':
720                 case '[':
721                 case '(':
722                         pbcpush(*c);
723                         break;
724                 case '}':
725                 case ']':
726                 case ')':
727                         if (pbcpop(*c))
728                                 return 1;
729                         break;
730                 }
731                 c++;
732         }
733         return 0;
734 }
735
736
737 /*
738  * The following three functions, reset_*, are used in the bison
739  * code to switch context. As a consequence, we need to
740  * declare them global and add a prototype so that the
741  * compiler does not complain.
742  *
743  * NOTE: yyg is declared because it is used in the BEGIN macros,
744  * though that should be hidden as the macro changes
745  * depending on the flex options that we use - in particular,
746  * %reentrant changes the way the macro is declared;
747  * without %reentrant, BEGIN uses yystart instead of yyg
748  */
749
750 void reset_parencount(yyscan_t yyscanner );
751 void reset_parencount(yyscan_t yyscanner )
752 {
753         struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
754         parencount = 0;
755         pbcpos = 0;
756         pbcpush('(');   /* push '(' so the last pcbpop (parencount= -1) will succeed */
757         c_prevword();
758         BEGIN(paren);
759 }
760
761 void reset_semicount(yyscan_t yyscanner );
762 void reset_semicount(yyscan_t yyscanner )
763 {
764         struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
765         pbcpos = 0;
766         BEGIN(semic);
767 }
768
769 void reset_argcount(yyscan_t yyscanner );
770 void reset_argcount(yyscan_t yyscanner )
771 {
772         struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
773         parencount = 0;
774         pbcpos = 0;
775         pbcpush('(');   /* push '(' so the last pcbpop (parencount= -1) will succeed */
776         c_prevword();
777         BEGIN(argg);
778 }
779
780 /* used elsewhere, but some local vars */
781 struct pval *ael2_parse(char *filename, int *errors)
782 {
783         struct pval *pvalue;
784         struct parse_io *io;
785         char *buffer;
786         struct stat stats;
787         FILE *fin;
788
789         /* extern int ael_yydebug; */
790
791         io = calloc(sizeof(struct parse_io),1);
792         /* reset the global counters */
793         prev_word = 0;
794         my_lineno = 1;
795         include_stack_index=0;
796         my_col = 0;
797         /* ael_yydebug = 1; */
798         ael_yylex_init(&io->scanner);
799         fin = fopen(filename,"r");
800         if ( !fin ) {
801                 ast_log(LOG_ERROR,"File %s could not be opened\n", filename);
802                 *errors = 1;
803                 return 0;
804         }
805         if (my_file)
806                 free(my_file);
807         my_file = strdup(filename);
808         stat(filename, &stats);
809         buffer = (char*)malloc(stats.st_size+2);
810         if (fread(buffer, 1, stats.st_size, fin) != stats.st_size) {
811                 ast_log(LOG_ERROR, "fread() failed: %s\n", strerror(errno));
812         }                       
813         buffer[stats.st_size]=0;
814         fclose(fin);
815
816         ael_yy_scan_string (buffer ,io->scanner);
817         ael_yyset_lineno(1 , io->scanner);
818
819         /* ael_yyset_in (fin , io->scanner);    OLD WAY */
820
821         ael_yyparse(io);
822
823
824         pvalue = io->pval;
825         *errors = io->syntax_error_count;
826
827         ael_yylex_destroy(io->scanner);
828         free(buffer);
829         free(io);
830
831         return pvalue;
832 }
833
834 static void setup_filestack(char *fnamebuf2, int fnamebuf_siz, glob_t *globbuf, int globpos, yyscan_t yyscanner, int create)
835 {
836         struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
837         int error, i;
838         FILE *in1;
839         char fnamebuf[2048];
840
841         if (globbuf && globbuf->gl_pathv && globbuf->gl_pathc > 0)
842 #if defined(STANDALONE) || defined(LOW_MEMORY) || defined(STANDALONE_AEL)
843                         strncpy(fnamebuf, globbuf->gl_pathv[globpos], fnamebuf_siz);
844 #else
845                         ast_copy_string(fnamebuf, globbuf->gl_pathv[globpos], fnamebuf_siz);
846 #endif
847         else {
848                 ast_log(LOG_ERROR,"Include file name not present!\n");
849                 return;
850         }
851         for (i=0; i<include_stack_index; i++) {
852                 if ( !strcmp(fnamebuf,include_stack[i].fname )) {
853                         ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Nice Try!!! But %s has already been included (perhaps by another file), and would cause an infinite loop of file inclusions!!! Include directive ignored\n",
854                                 my_file, my_lineno, my_col, fnamebuf);
855                         break;
856                 }
857         }
858         error = 1;
859         if (i == include_stack_index)
860                 error = 0;      /* we can use this file */
861         if ( !error ) { /* valid file name */
862                 /* relative vs. absolute */
863                 if (fnamebuf[0] != '/')
864                         snprintf(fnamebuf2, fnamebuf_siz, "%s/%s", ast_config_AST_CONFIG_DIR, fnamebuf);
865                 else
866 #if defined(STANDALONE) || defined(LOW_MEMORY) || defined(STANDALONE_AEL)
867                         strncpy(fnamebuf2, fnamebuf, fnamebuf_siz);
868 #else
869                         ast_copy_string(fnamebuf2, fnamebuf, fnamebuf_siz);
870 #endif
871                 in1 = fopen( fnamebuf2, "r" );
872
873                 if ( ! in1 ) {
874                         ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Couldn't find the include file: %s; ignoring the Include directive!\n", my_file, my_lineno, my_col, fnamebuf2);
875                 } else {
876                         char *buffer;
877                         struct stat stats;
878                         stat(fnamebuf2, &stats);
879                         buffer = (char*)malloc(stats.st_size+1);
880                         if (fread(buffer, 1, stats.st_size, in1) != stats.st_size) {
881                                 ast_log(LOG_ERROR, "fread() failed: %s\n", strerror(errno));
882                         }                       
883                         buffer[stats.st_size] = 0;
884                         ast_log(LOG_NOTICE,"  --Read in included file %s, %d chars\n",fnamebuf2, (int)stats.st_size);
885                         fclose(in1);
886                         if (include_stack[include_stack_index].fname) {
887                                 free(include_stack[include_stack_index].fname);
888                                 include_stack[include_stack_index].fname = 0;
889                         }
890                         include_stack[include_stack_index].fname = strdup(my_file);
891                         include_stack[include_stack_index].lineno = my_lineno;
892                         include_stack[include_stack_index].colno = my_col+yyleng;
893                         if (my_file)
894                                 free(my_file);
895                         my_file = strdup(fnamebuf2);
896                         if (create)
897                                 include_stack[include_stack_index].globbuf = *globbuf;
898
899                         include_stack[include_stack_index].globbuf_pos = 0;
900
901                         include_stack[include_stack_index].bufstate = YY_CURRENT_BUFFER;
902                         if (create)
903                                 include_stack_index++;
904                         yy_switch_to_buffer(ael_yy_scan_string (buffer ,yyscanner),yyscanner);
905                         free(buffer);
906                         my_lineno = 1;
907                         my_col = 1;
908                         BEGIN(INITIAL);
909                 }
910         }
911 }