Annotation of embedaddon/bird2/conf/cf-lex.l, revision 1.1

1.1     ! misho       1: /*
        !             2:  *     BIRD -- Configuration Lexer
        !             3:  *
        !             4:  *     (c) 1998--2000 Martin Mares <mj@ucw.cz>
        !             5:  *
        !             6:  *     Can be freely distributed and used under the terms of the GNU GPL.
        !             7:  */
        !             8: 
        !             9: /**
        !            10:  * DOC: Lexical analyzer
        !            11:  *
        !            12:  * The lexical analyzer used for configuration files and CLI commands
        !            13:  * is generated using the |flex| tool accompanied by a couple of
        !            14:  * functions maintaining the hash tables containing information about
        !            15:  * symbols and keywords.
        !            16:  *
        !            17:  * Each symbol is represented by a &symbol structure containing name
        !            18:  * of the symbol, its lexical scope, symbol class (%SYM_PROTO for a
        !            19:  * name of a protocol, %SYM_CONSTANT for a constant etc.) and class
        !            20:  * dependent data.  When an unknown symbol is encountered, it's
        !            21:  * automatically added to the symbol table with class %SYM_VOID.
        !            22:  *
        !            23:  * The keyword tables are generated from the grammar templates
        !            24:  * using the |gen_keywords.m4| script.
        !            25:  */
        !            26: 
        !            27: %{
        !            28: #undef REJECT     /* Avoid name clashes */
        !            29: 
        !            30: #include <errno.h>
        !            31: #include <stdlib.h>
        !            32: #include <stdarg.h>
        !            33: #include <stdint.h>
        !            34: #include <unistd.h>
        !            35: #include <libgen.h>
        !            36: #include <glob.h>
        !            37: #include <fcntl.h>
        !            38: #include <sys/stat.h>
        !            39: #include <sys/types.h>
        !            40: #include <sys/stat.h>
        !            41: 
        !            42: #define PARSER 1
        !            43: 
        !            44: #include "nest/bird.h"
        !            45: #include "nest/route.h"
        !            46: #include "nest/protocol.h"
        !            47: #include "filter/filter.h"
        !            48: #include "filter/f-inst.h"
        !            49: #include "conf/conf.h"
        !            50: #include "conf/cf-parse.tab.h"
        !            51: #include "lib/string.h"
        !            52: #include "lib/hash.h"
        !            53: 
        !            54: struct keyword {
        !            55:   byte *name;
        !            56:   int value;
        !            57:   struct keyword *next;
        !            58: };
        !            59: 
        !            60: #include "conf/keywords.h"
        !            61: 
        !            62: /* Could be defined by Bison in cf-parse.tab.h, inteferes with SYM hash */
        !            63: #ifdef SYM
        !            64: #undef SYM
        !            65: #endif
        !            66: 
        !            67: 
        !            68: static uint cf_hash(const byte *c);
        !            69: 
        !            70: #define KW_KEY(n)              n->name
        !            71: #define KW_NEXT(n)             n->next
        !            72: #define KW_EQ(a,b)             !strcmp(a,b)
        !            73: #define KW_FN(k)               cf_hash(k)
        !            74: #define KW_ORDER               8 /* Fixed */
        !            75: 
        !            76: #define SYM_KEY(n)             n->name, n->scope->active
        !            77: #define SYM_NEXT(n)            n->next
        !            78: #define SYM_EQ(a,s1,b,s2)      !strcmp(a,b) && s1 == s2
        !            79: #define SYM_FN(k,s)            cf_hash(k)
        !            80: #define SYM_ORDER              6 /* Initial */
        !            81: 
        !            82: #define SYM_REHASH             sym_rehash
        !            83: #define SYM_PARAMS             /8, *1, 2, 2, 6, 20
        !            84: 
        !            85: 
        !            86: HASH_DEFINE_REHASH_FN(SYM, struct symbol)
        !            87: 
        !            88: HASH(struct keyword) kw_hash;
        !            89: 
        !            90: 
        !            91: struct sym_scope *conf_this_scope;
        !            92: 
        !            93: linpool *cfg_mem;
        !            94: 
        !            95: int (*cf_read_hook)(byte *buf, unsigned int max, int fd);
        !            96: struct include_file_stack *ifs;
        !            97: static struct include_file_stack *ifs_head;
        !            98: 
        !            99: #define QUOTED_BUFFER_SIZE  4096
        !           100: static BUFFER_(char) quoted_buffer;
        !           101: static char quoted_buffer_data[QUOTED_BUFFER_SIZE];
        !           102: static inline void quoted_buffer_init(void) {
        !           103:   quoted_buffer.used = 0;
        !           104:   quoted_buffer.size = QUOTED_BUFFER_SIZE;
        !           105:   quoted_buffer.data = quoted_buffer_data;
        !           106: }
        !           107: 
        !           108: #define MAX_INCLUDE_DEPTH 8
        !           109: 
        !           110: #define YY_INPUT(buf,result,max) result = cf_read_hook(buf, max, ifs->fd);
        !           111: #define YY_NO_UNPUT
        !           112: #define YY_FATAL_ERROR(msg) cf_error(msg)
        !           113: #define YY_USER_ACTION ifs->chno += yyleng; ifs->toklen = yyleng;
        !           114: 
        !           115: static void cf_include(char *arg, int alen);
        !           116: static int check_eof(void);
        !           117: 
        !           118: static enum yytokentype cf_lex_symbol(const char *data);
        !           119: 
        !           120: %}
        !           121: 
        !           122: %option noyywrap
        !           123: %option noinput
        !           124: %option nounput
        !           125: %option noreject
        !           126: 
        !           127: %x COMMENT CCOMM CLI QUOTED APOSTROPHED INCLUDE
        !           128: 
        !           129: ALPHA [a-zA-Z_]
        !           130: DIGIT [0-9]
        !           131: XIGIT [0-9a-fA-F]
        !           132: ALNUM [a-zA-Z_0-9]
        !           133: WHITE [ \t]
        !           134: 
        !           135: %%
        !           136: ^{WHITE}*include{WHITE}*\" {
        !           137:   if (!ifs->depth)
        !           138:     cf_error("Include not allowed in CLI");
        !           139: 
        !           140:   BEGIN(INCLUDE);
        !           141: }
        !           142: 
        !           143: <INCLUDE>[^"\n]+["]{WHITE}*; {
        !           144:   char *start, *end;
        !           145: 
        !           146:   start = yytext;
        !           147: 
        !           148:   end = strchr(start, '"');
        !           149:   *end = 0;
        !           150: 
        !           151:   if (start == end)
        !           152:     cf_error("Include with empty argument");
        !           153: 
        !           154:   cf_include(start, end-start);
        !           155: 
        !           156:   BEGIN(INITIAL);
        !           157: }
        !           158: 
        !           159: <INCLUDE>["]           cf_error("Include with empty argument");
        !           160: <INCLUDE>.             cf_error("Unterminated include");
        !           161: <INCLUDE>\n            cf_error("Unterminated include");
        !           162: <INCLUDE><<EOF>>       cf_error("Unterminated include");
        !           163: 
        !           164: 
        !           165: {DIGIT}+:{DIGIT}+ {
        !           166:   uint len1 UNUSED, len2;
        !           167:   u64 l;
        !           168:   char *e;
        !           169: 
        !           170:   errno = 0;
        !           171:   l = bstrtoul10(yytext, &e);
        !           172:   if (e && (*e != ':') || (errno == ERANGE) || (l >> 32))
        !           173:     cf_error("ASN out of range");
        !           174: 
        !           175:   if (l >> 16)
        !           176:   {
        !           177:     len1 = 32;
        !           178:     len2 = 16;
        !           179:     cf_lval.i64 = (2ULL << 48) | (((u64) l) << len2);
        !           180:   }
        !           181:   else
        !           182:   {
        !           183:     len1 = 16;
        !           184:     len2 = 32;
        !           185:     cf_lval.i64 = 0 | (((u64) l) << len2);
        !           186:   }
        !           187: 
        !           188:   errno = 0;
        !           189:   l = bstrtoul10(e+1, &e);
        !           190:   if (e && *e || (errno == ERANGE) || (l >> len2))
        !           191:     cf_error("Number out of range");
        !           192:   cf_lval.i64 |= l;
        !           193: 
        !           194:   return VPN_RD;
        !           195: }
        !           196: 
        !           197: [02]:{DIGIT}+:{DIGIT}+ {
        !           198:   uint len1, len2;
        !           199:   u64 l;
        !           200:   char *e;
        !           201: 
        !           202:   if (yytext[0] == '0')
        !           203:   {
        !           204:     cf_lval.i64 = 0;
        !           205:     len1 = 16;
        !           206:     len2 = 32;
        !           207:   }
        !           208:   else
        !           209:   {
        !           210:     cf_lval.i64 = 2ULL << 48;
        !           211:     len1 = 32;
        !           212:     len2 = 16;
        !           213:   }
        !           214: 
        !           215:   errno = 0;
        !           216:   l = bstrtoul10(yytext+2, &e);
        !           217:   if (e && (*e != ':') || (errno == ERANGE) || (l >> len1))
        !           218:     cf_error("ASN out of range");
        !           219:   cf_lval.i64 |= ((u64) l) << len2;
        !           220: 
        !           221:   errno = 0;
        !           222:   l = bstrtoul10(e+1, &e);
        !           223:   if (e && *e || (errno == ERANGE) || (l >> len2))
        !           224:     cf_error("Number out of range");
        !           225:   cf_lval.i64 |= l;
        !           226: 
        !           227:   return VPN_RD;
        !           228: }
        !           229: 
        !           230: {DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+:{DIGIT}+ {
        !           231:   unsigned long int l;
        !           232:   ip4_addr ip4;
        !           233:   char *e;
        !           234: 
        !           235:   cf_lval.i64 = 1ULL << 48;
        !           236: 
        !           237:   e = strchr(yytext, ':');
        !           238:   *e++ = '\0';
        !           239:   if (!ip4_pton(yytext, &ip4))
        !           240:     cf_error("Invalid IPv4 address %s in Route Distinguisher", yytext);
        !           241:   cf_lval.i64 |= ((u64) ip4_to_u32(ip4)) << 16;
        !           242: 
        !           243:   errno = 0;
        !           244:   l = bstrtoul10(e, &e);
        !           245:   if (e && *e || (errno == ERANGE) || (l >> 16))
        !           246:     cf_error("Number out of range");
        !           247:   cf_lval.i64 |= l;
        !           248: 
        !           249:   return VPN_RD;
        !           250: }
        !           251: 
        !           252: {DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+ {
        !           253:   if (!ip4_pton(yytext, &cf_lval.ip4))
        !           254:     cf_error("Invalid IPv4 address %s", yytext);
        !           255:   return IP4;
        !           256: }
        !           257: 
        !           258: ({XIGIT}*::|({XIGIT}*:){3,})({XIGIT}*|{DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+) {
        !           259:   if (!ip6_pton(yytext, &cf_lval.ip6))
        !           260:     cf_error("Invalid IPv6 address %s", yytext);
        !           261:   return IP6;
        !           262: }
        !           263: 
        !           264: 0x{XIGIT}+ {
        !           265:   char *e;
        !           266:   unsigned long int l;
        !           267:   errno = 0;
        !           268:   l = bstrtoul16(yytext+2, &e);
        !           269:   if (e && *e || errno == ERANGE || (unsigned long int)(unsigned int) l != l)
        !           270:     cf_error("Number out of range");
        !           271:   cf_lval.i = l;
        !           272:   return NUM;
        !           273: }
        !           274: 
        !           275: {DIGIT}+ {
        !           276:   char *e;
        !           277:   unsigned long int l;
        !           278:   errno = 0;
        !           279:   l = bstrtoul10(yytext, &e);
        !           280:   if (e && *e || errno == ERANGE || (unsigned long int)(unsigned int) l != l)
        !           281:     cf_error("Number out of range");
        !           282:   cf_lval.i = l;
        !           283:   return NUM;
        !           284: }
        !           285: 
        !           286: else: {
        !           287:   /* Hack to distinguish if..else from else: in case */
        !           288:   return ELSECOL;
        !           289: }
        !           290: 
        !           291: ['] {
        !           292:   BEGIN(APOSTROPHED);
        !           293:   quoted_buffer_init();
        !           294: }
        !           295: 
        !           296: <APOSTROPHED>{ALNUM}|[-]|[.:]  BUFFER_PUSH(quoted_buffer) = yytext[0];
        !           297: <APOSTROPHED>\n                        cf_error("Unterminated symbol");
        !           298: <APOSTROPHED><<EOF>>           cf_error("Unterminated symbol");
        !           299: <APOSTROPHED>['] {
        !           300:   BEGIN(INITIAL);
        !           301:   BUFFER_PUSH(quoted_buffer) = 0;
        !           302:   return cf_lex_symbol(quoted_buffer_data);
        !           303: }
        !           304: <APOSTROPHED>.                 cf_error("Invalid character in apostrophed symbol");
        !           305: 
        !           306: ({ALPHA}{ALNUM}*) {
        !           307:   return cf_lex_symbol(yytext);
        !           308: }
        !           309: 
        !           310: <CLI>(.|\n) {
        !           311:   BEGIN(INITIAL);
        !           312:   return CLI_MARKER;
        !           313: }
        !           314: 
        !           315: \.\. {
        !           316:   return DDOT;
        !           317: }
        !           318: 
        !           319: [={}:;,.()+*/%<>~\[\]?!\|-] {
        !           320:   return yytext[0];
        !           321: }
        !           322: 
        !           323: ["] {
        !           324:   BEGIN(QUOTED);
        !           325:   quoted_buffer_init();
        !           326: }
        !           327: 
        !           328: <QUOTED>\n     cf_error("Unterminated string");
        !           329: <QUOTED><<EOF>> cf_error("Unterminated string");
        !           330: <QUOTED>["]    {
        !           331:   BEGIN(INITIAL);
        !           332:   BUFFER_PUSH(quoted_buffer) = 0;
        !           333:   cf_lval.t = cfg_strdup(quoted_buffer_data);
        !           334:   return TEXT;
        !           335: }
        !           336: 
        !           337: <QUOTED>.      BUFFER_PUSH(quoted_buffer) = yytext[0];
        !           338: 
        !           339: <INITIAL,COMMENT><<EOF>>       { if (check_eof()) return END; }
        !           340: 
        !           341: {WHITE}+
        !           342: 
        !           343: \n     ifs->lino++; ifs->chno = 0;
        !           344: 
        !           345: #      BEGIN(COMMENT);
        !           346: 
        !           347: \/\*   BEGIN(CCOMM);
        !           348: 
        !           349: .      cf_error("Unknown character");
        !           350: 
        !           351: <COMMENT>\n {
        !           352:   ifs->lino++;
        !           353:   ifs->chno = 0;
        !           354:   BEGIN(INITIAL);
        !           355: }
        !           356: 
        !           357: <COMMENT>.
        !           358: 
        !           359: <CCOMM>\*\/    BEGIN(INITIAL);
        !           360: <CCOMM>\n      ifs->lino++; ifs->chno = 0;
        !           361: <CCOMM>\/\*    cf_error("Comment nesting not supported");
        !           362: <CCOMM><<EOF>> cf_error("Unterminated comment");
        !           363: <CCOMM>.
        !           364: 
        !           365: \!\= return NEQ;
        !           366: \!\~ return NMA;
        !           367: \<\= return LEQ;
        !           368: \>\= return GEQ;
        !           369: \&\& return AND;
        !           370: \|\| return OR;
        !           371: 
        !           372: \[\= return PO;
        !           373: \=\] return PC;
        !           374: 
        !           375: %%
        !           376: 
        !           377: static uint
        !           378: cf_hash(const byte *c)
        !           379: {
        !           380:   uint h = 13 << 24;
        !           381: 
        !           382:   while (*c)
        !           383:     h = h + (h >> 2) + (h >> 5) + ((uint) *c++ << 24);
        !           384:   return h;
        !           385: }
        !           386: 
        !           387: /*
        !           388:  * IFS stack - it contains structures needed for recursive processing
        !           389:  * of include in config files. On the top of the stack is a structure
        !           390:  * for currently processed file. Other structures are either for
        !           391:  * active files interrupted because of include directive (these have
        !           392:  * fd and flex buffer) or for inactive files scheduled to be processed
        !           393:  * later (when parent requested including of several files by wildcard
        !           394:  * match - these do not have fd and flex buffer yet).
        !           395:  *
        !           396:  * FIXME: Most of these ifs and include functions are really sysdep/unix.
        !           397:  */
        !           398: 
        !           399: static struct include_file_stack *
        !           400: push_ifs(struct include_file_stack *old)
        !           401: {
        !           402:   struct include_file_stack *ret;
        !           403:   ret = cfg_allocz(sizeof(struct include_file_stack));
        !           404:   ret->lino = 1;
        !           405:   ret->prev = old;
        !           406:   return ret;
        !           407: }
        !           408: 
        !           409: static struct include_file_stack *
        !           410: pop_ifs(struct include_file_stack *old)
        !           411: {
        !           412:  yy_delete_buffer(old->buffer);
        !           413:  close(old->fd);
        !           414:  return old->prev;
        !           415: }
        !           416: 
        !           417: static void
        !           418: enter_ifs(struct include_file_stack *new)
        !           419: {
        !           420:   if (!new->buffer)
        !           421:     {
        !           422:       new->fd = open(new->file_name, O_RDONLY);
        !           423:       if (new->fd < 0)
        !           424:         {
        !           425:           ifs = ifs->up;
        !           426:          cf_error("Unable to open included file %s: %m", new->file_name);
        !           427:         }
        !           428: 
        !           429:       new->buffer = yy_create_buffer(NULL, YY_BUF_SIZE);
        !           430:     }
        !           431: 
        !           432:   yy_switch_to_buffer(new->buffer);
        !           433: }
        !           434: 
        !           435: /**
        !           436:  * cf_lex_unwind - unwind lexer state during error
        !           437:  *
        !           438:  * cf_lex_unwind() frees the internal state on IFS stack when the lexical
        !           439:  * analyzer is terminated by cf_error().
        !           440:  */
        !           441: void
        !           442: cf_lex_unwind(void)
        !           443: {
        !           444:   struct include_file_stack *n;
        !           445: 
        !           446:   for (n = ifs; n != ifs_head; n = n->prev)
        !           447:     {
        !           448:       /* Memory is freed automatically */
        !           449:       if (n->buffer)
        !           450:        yy_delete_buffer(n->buffer);
        !           451:       if (n->fd)
        !           452:         close(n->fd);
        !           453:     }
        !           454: 
        !           455:   ifs = ifs_head;
        !           456: }
        !           457: 
        !           458: static void
        !           459: cf_include(char *arg, int alen)
        !           460: {
        !           461:   struct include_file_stack *base_ifs = ifs;
        !           462:   int new_depth, rv, i;
        !           463:   char *patt;
        !           464:   glob_t g = {};
        !           465: 
        !           466:   new_depth = ifs->depth + 1;
        !           467:   if (new_depth > MAX_INCLUDE_DEPTH)
        !           468:     cf_error("Max include depth reached");
        !           469: 
        !           470:   /* expand arg to properly handle relative filenames */
        !           471:   if (*arg != '/')
        !           472:     {
        !           473:       int dlen = strlen(ifs->file_name);
        !           474:       char *dir = alloca(dlen + 1);
        !           475:       patt = alloca(dlen + alen + 2);
        !           476:       memcpy(dir, ifs->file_name, dlen + 1);
        !           477:       sprintf(patt, "%s/%s", dirname(dir), arg);
        !           478:     }
        !           479:   else
        !           480:     patt = arg;
        !           481: 
        !           482:   /* Skip globbing if there are no wildcards, mainly to get proper
        !           483:      response when the included config file is missing */
        !           484:   if (!strpbrk(arg, "?*["))
        !           485:     {
        !           486:       ifs = push_ifs(ifs);
        !           487:       ifs->file_name = cfg_strdup(patt);
        !           488:       ifs->depth = new_depth;
        !           489:       ifs->up = base_ifs;
        !           490:       enter_ifs(ifs);
        !           491:       return;
        !           492:     }
        !           493: 
        !           494:   /* Expand the pattern */
        !           495:   rv = glob(patt, GLOB_ERR | GLOB_NOESCAPE, NULL, &g);
        !           496:   if (rv == GLOB_ABORTED)
        !           497:     cf_error("Unable to match pattern %s: %m", patt);
        !           498:   if ((rv != 0) || (g.gl_pathc <= 0))
        !           499:     return;
        !           500: 
        !           501:   /*
        !           502:    * Now we put all found files to ifs stack in reverse order, they
        !           503:    * will be activated and processed in order as ifs stack is popped
        !           504:    * by pop_ifs() and enter_ifs() in check_eof().
        !           505:    */
        !           506:   for(i = g.gl_pathc - 1; i >= 0; i--)
        !           507:     {
        !           508:       char *fname = g.gl_pathv[i];
        !           509:       struct stat fs;
        !           510: 
        !           511:       if (stat(fname, &fs) < 0)
        !           512:        {
        !           513:          globfree(&g);
        !           514:          cf_error("Unable to stat included file %s: %m", fname);
        !           515:        }
        !           516: 
        !           517:       if (fs.st_mode & S_IFDIR)
        !           518:         continue;
        !           519: 
        !           520:       /* Prepare new stack item */
        !           521:       ifs = push_ifs(ifs);
        !           522:       ifs->file_name = cfg_strdup(fname);
        !           523:       ifs->depth = new_depth;
        !           524:       ifs->up = base_ifs;
        !           525:     }
        !           526: 
        !           527:   globfree(&g);
        !           528:   enter_ifs(ifs);
        !           529: }
        !           530: 
        !           531: static int
        !           532: check_eof(void)
        !           533: {
        !           534:   if (ifs == ifs_head)
        !           535:     {
        !           536:       /* EOF in main config file */
        !           537:       ifs->lino = 1; /* Why this? */
        !           538:       return 1;
        !           539:     }
        !           540: 
        !           541:   ifs = pop_ifs(ifs);
        !           542:   enter_ifs(ifs);
        !           543:   return 0;
        !           544: }
        !           545: 
        !           546: static struct symbol *
        !           547: cf_new_symbol(const byte *c)
        !           548: {
        !           549:   struct symbol *s;
        !           550: 
        !           551:   uint l = strlen(c);
        !           552:   if (l > SYM_MAX_LEN)
        !           553:     cf_error("Symbol too long");
        !           554: 
        !           555:   s = cfg_allocz(sizeof(struct symbol) + l + 1);
        !           556:   *s = (struct symbol) { .scope = conf_this_scope, .class = SYM_VOID, };
        !           557:   strcpy(s->name, c);
        !           558: 
        !           559:   if (!new_config->sym_hash.data)
        !           560:     HASH_INIT(new_config->sym_hash, new_config->pool, SYM_ORDER);
        !           561: 
        !           562:   HASH_INSERT2(new_config->sym_hash, SYM, new_config->pool, s);
        !           563: 
        !           564:   add_tail(&(new_config->symbols), &(s->n));
        !           565: 
        !           566:   return s;
        !           567: }
        !           568: 
        !           569: /**
        !           570:  * cf_find_symbol - find a symbol by name
        !           571:  * @cfg: specificed config
        !           572:  * @c: symbol name
        !           573:  *
        !           574:  * This functions searches the symbol table in the config @cfg for a symbol of
        !           575:  * given name. First it examines the current scope, then the second recent one
        !           576:  * and so on until it either finds the symbol and returns a pointer to its
        !           577:  * &symbol structure or reaches the end of the scope chain and returns %NULL to
        !           578:  * signify no match.
        !           579:  */
        !           580: struct symbol *
        !           581: cf_find_symbol(const struct config *cfg, const byte *c)
        !           582: {
        !           583:   struct symbol *s;
        !           584: 
        !           585:   if (cfg->sym_hash.data &&
        !           586:       (s = HASH_FIND(cfg->sym_hash, SYM, c, 1)))
        !           587:     return s;
        !           588: 
        !           589:   /* In CLI command parsing, fallback points to the current config, otherwise it is NULL. */
        !           590:   if (cfg->fallback &&
        !           591:       cfg->fallback->sym_hash.data &&
        !           592:       (s = HASH_FIND(cfg->fallback->sym_hash, SYM, c, 1)))
        !           593:     return s;
        !           594: 
        !           595:   return NULL;
        !           596: }
        !           597: 
        !           598: /**
        !           599:  * cf_get_symbol - get a symbol by name
        !           600:  * @c: symbol name
        !           601:  *
        !           602:  * This functions searches the symbol table of the currently parsed config
        !           603:  * (@new_config) for a symbol of given name. It returns either the already
        !           604:  * existing symbol or a newly allocated undefined (%SYM_VOID) symbol if no
        !           605:  * existing symbol is found.
        !           606:  */
        !           607: struct symbol *
        !           608: cf_get_symbol(const byte *c)
        !           609: {
        !           610:   return cf_find_symbol(new_config, c) ?: cf_new_symbol(c);
        !           611: }
        !           612: 
        !           613: /**
        !           614:  * cf_localize_symbol - get the local instance of given symbol
        !           615:  * @sym: the symbol to localize
        !           616:  *
        !           617:  * This functions finds the symbol that is local to current scope
        !           618:  * for purposes of cf_define_symbol().
        !           619:  */
        !           620: struct symbol *
        !           621: cf_localize_symbol(struct symbol *sym)
        !           622: {
        !           623:   /* If the symbol type is void, it has been recently allocated just in this scope. */
        !           624:   if (!sym->class)
        !           625:     return sym;
        !           626:   
        !           627:   /* If the scope is the current, it is already defined in this scope. */
        !           628:   if (sym->scope == conf_this_scope)
        !           629:     cf_error("Symbol already defined");
        !           630: 
        !           631:   /* Not allocated here yet, doing it now. */
        !           632:   return cf_new_symbol(sym->name);
        !           633: }
        !           634: 
        !           635: struct symbol *
        !           636: cf_default_name(char *template, int *counter)
        !           637: {
        !           638:   char buf[SYM_MAX_LEN];
        !           639:   struct symbol *s;
        !           640:   char *perc = strchr(template, '%');
        !           641: 
        !           642:   for(;;)
        !           643:     {
        !           644:       bsprintf(buf, template, ++(*counter));
        !           645:       s = cf_get_symbol(buf);
        !           646:       if (s->class == SYM_VOID)
        !           647:        return s;
        !           648:       if (!perc)
        !           649:        break;
        !           650:     }
        !           651:   cf_error("Unable to generate default name");
        !           652: }
        !           653: 
        !           654: static enum yytokentype
        !           655: cf_lex_symbol(const char *data)
        !           656: {
        !           657:   /* Have we defined such a symbol? */
        !           658:   struct symbol *sym = cf_get_symbol(data);
        !           659:   cf_lval.s = sym;
        !           660: 
        !           661:   if (sym->class != SYM_VOID)
        !           662:     return CF_SYM_KNOWN;
        !           663: 
        !           664:   /* Is it a keyword? */
        !           665:   struct keyword *k = HASH_FIND(kw_hash, KW, data);
        !           666:   if (k)
        !           667:   {
        !           668:     if (k->value > 0)
        !           669:       return k->value;
        !           670:     else
        !           671:     {
        !           672:       cf_lval.i = -k->value;
        !           673:       return ENUM;
        !           674:     }
        !           675:   }
        !           676: 
        !           677:   /* OK, undefined symbol */
        !           678:   cf_lval.s = sym;
        !           679:   return CF_SYM_UNDEFINED;
        !           680: }
        !           681: 
        !           682: static void
        !           683: cf_lex_init_kh(void)
        !           684: {
        !           685:   HASH_INIT(kw_hash, &root_pool, KW_ORDER);
        !           686: 
        !           687:   struct keyword *k;
        !           688:   for (k=keyword_list; k->name; k++)
        !           689:     HASH_INSERT(kw_hash, KW, k);
        !           690: }
        !           691: 
        !           692: /**
        !           693:  * cf_lex_init - initialize the lexer
        !           694:  * @is_cli: true if we're going to parse CLI command, false for configuration
        !           695:  * @c: configuration structure
        !           696:  *
        !           697:  * cf_lex_init() initializes the lexical analyzer and prepares it for
        !           698:  * parsing of a new input.
        !           699:  */
        !           700: void
        !           701: cf_lex_init(int is_cli, struct config *c)
        !           702: {
        !           703:   if (!kw_hash.data)
        !           704:     cf_lex_init_kh();
        !           705: 
        !           706:   ifs_head = ifs = push_ifs(NULL);
        !           707:   if (!is_cli)
        !           708:     {
        !           709:       ifs->file_name = c->file_name;
        !           710:       ifs->fd = c->file_fd;
        !           711:       ifs->depth = 1;
        !           712:     }
        !           713: 
        !           714:   yyrestart(NULL);
        !           715:   ifs->buffer = YY_CURRENT_BUFFER;
        !           716: 
        !           717:   if (is_cli)
        !           718:     BEGIN(CLI);
        !           719:   else
        !           720:     BEGIN(INITIAL);
        !           721: 
        !           722:   c->root_scope = cfg_allocz(sizeof(struct sym_scope));
        !           723:   conf_this_scope = c->root_scope;
        !           724:   conf_this_scope->active = 1;
        !           725: }
        !           726: 
        !           727: /**
        !           728:  * cf_push_scope - enter new scope
        !           729:  * @sym: symbol representing scope name
        !           730:  *
        !           731:  * If we want to enter a new scope to process declarations inside
        !           732:  * a nested block, we can just call cf_push_scope() to push a new
        !           733:  * scope onto the scope stack which will cause all new symbols to be
        !           734:  * defined in this scope and all existing symbols to be sought for
        !           735:  * in all scopes stored on the stack.
        !           736:  */
        !           737: void
        !           738: cf_push_scope(struct symbol *sym)
        !           739: {
        !           740:   struct sym_scope *s = cfg_alloc(sizeof(struct sym_scope));
        !           741: 
        !           742:   s->next = conf_this_scope;
        !           743:   conf_this_scope = s;
        !           744:   s->active = 1;
        !           745:   s->name = sym;
        !           746:   s->slots = 0;
        !           747: }
        !           748: 
        !           749: /**
        !           750:  * cf_pop_scope - leave a scope
        !           751:  *
        !           752:  * cf_pop_scope() pops the topmost scope from the scope stack,
        !           753:  * leaving all its symbols in the symbol table, but making them
        !           754:  * invisible to the rest of the config.
        !           755:  */
        !           756: void
        !           757: cf_pop_scope(void)
        !           758: {
        !           759:   conf_this_scope->active = 0;
        !           760:   conf_this_scope = conf_this_scope->next;
        !           761: 
        !           762:   ASSERT(conf_this_scope);
        !           763: }
        !           764: 
        !           765: /**
        !           766:  * cf_symbol_class_name - get name of a symbol class
        !           767:  * @sym: symbol
        !           768:  *
        !           769:  * This function returns a string representing the class
        !           770:  * of the given symbol.
        !           771:  */
        !           772: char *
        !           773: cf_symbol_class_name(struct symbol *sym)
        !           774: {
        !           775:   switch (sym->class)
        !           776:     {
        !           777:     case SYM_VOID:
        !           778:       return "undefined";
        !           779:     case SYM_PROTO:
        !           780:       return "protocol";
        !           781:     case SYM_TEMPLATE:
        !           782:       return "protocol template";
        !           783:     case SYM_FUNCTION:
        !           784:       return "function";
        !           785:     case SYM_FILTER:
        !           786:       return "filter";
        !           787:     case SYM_TABLE:
        !           788:       return "routing table";
        !           789:     case SYM_ATTRIBUTE:
        !           790:       return "custom attribute";
        !           791:     case SYM_CONSTANT_RANGE:
        !           792:       return "constant";
        !           793:     case SYM_VARIABLE_RANGE:
        !           794:       return "variable";
        !           795:     default:
        !           796:       return "unknown type";
        !           797:     }
        !           798: }
        !           799: 
        !           800: 
        !           801: /**
        !           802:  * DOC: Parser
        !           803:  *
        !           804:  * Both the configuration and CLI commands are analyzed using a syntax
        !           805:  * driven parser generated by the |bison| tool from a grammar which
        !           806:  * is constructed from information gathered from grammar snippets by
        !           807:  * the |gen_parser.m4| script.
        !           808:  *
        !           809:  * Grammar snippets are files (usually with extension |.Y|) contributed
        !           810:  * by various BIRD modules in order to provide information about syntax of their
        !           811:  * configuration and their CLI commands. Each snipped consists of several
        !           812:  * sections, each of them starting with a special keyword: |CF_HDR| for
        !           813:  * a list of |#include| directives needed by the C code, |CF_DEFINES|
        !           814:  * for a list of C declarations, |CF_DECLS| for |bison| declarations
        !           815:  * including keyword definitions specified as |CF_KEYWORDS|, |CF_GRAMMAR|
        !           816:  * for the grammar rules, |CF_CODE| for auxiliary C code and finally
        !           817:  * |CF_END| at the end of the snippet.
        !           818:  *
        !           819:  * To create references between the snippets, it's possible to define
        !           820:  * multi-part rules by utilizing the |CF_ADDTO| macro which adds a new
        !           821:  * alternative to a multi-part rule.
        !           822:  *
        !           823:  * CLI commands are defined using a |CF_CLI| macro. Its parameters are:
        !           824:  * the list of keywords determining the command, the list of parameters,
        !           825:  * help text for the parameters and help text for the command.
        !           826:  *
        !           827:  * Values of |enum| filter types can be defined using |CF_ENUM| with
        !           828:  * the following parameters: name of filter type, prefix common for all
        !           829:  * literals of this type and names of all the possible values.
        !           830:  */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>