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