diff options
author | Sergey Poznyakoff <gray@gnu.org> | 2016-03-05 23:40:11 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org> | 2016-03-05 23:44:50 +0200 |
commit | 2dbad2727f1d00b223353965b326cfffa96fc776 (patch) | |
tree | 3e76daff8a9bf0a1268e183acce13d6ef828ccf7 | |
parent | 8241dc04501f62b613f17f5c165f915a216ba30c (diff) | |
download | grecs-2dbad2727f1d00b223353965b326cfffa96fc776.tar.gz grecs-2dbad2727f1d00b223353965b326cfffa96fc776.tar.bz2 |
Improve dhcpd parser
* src/dhcpd-gram.y: Support assignments and conditional
expressions.
* src/dhcpd-lex.l: New exclusive states BOOL and EXPR.
* src/parsertab.c (parser_tab) [ENABLE_DHCPD_PARSER]: Define
dhcpd parser.
* tests/parser-dhcpd.at: New file.
* tests/Makefile.am: Add parser-dhcpd.at
* tests/testsuite.at: Likewise.
-rw-r--r-- | src/dhcpd-gram.y | 123 | ||||
-rw-r--r-- | src/dhcpd-lex.l | 77 | ||||
-rw-r--r-- | src/parsertab.c | 4 | ||||
-rw-r--r-- | tests/Makefile.am | 6 | ||||
-rw-r--r-- | tests/dhcpd.conf | 61 | ||||
-rw-r--r-- | tests/parser-dhcpd.at | 59 | ||||
-rw-r--r-- | tests/testsuite.at | 5 |
7 files changed, 329 insertions, 6 deletions
diff --git a/src/dhcpd-gram.y b/src/dhcpd-gram.y index b4f1476..e40f473 100644 --- a/src/dhcpd-gram.y +++ b/src/dhcpd-gram.y @@ -34,6 +34,20 @@ extern int yy_flex_debug; extern int grecs_dhcpd_new_source(const char *name, grecs_locus_t *loc); extern void grecs_dhcpd_close_sources(void); +extern void grecs_dhcpd_begin_bool(void); +extern void grecs_dhcpd_begin_expr(void); + +/* NOTE: STRING must be allocated */ +static grecs_value_t * +make_string_value(char *string) +{ + grecs_value_t *val; + val = grecs_malloc(sizeof(val[0])); + val->type = GRECS_TYPE_STRING; + val->v.string = string; + return val; +} + %} %error-verbose @@ -48,13 +62,16 @@ extern void grecs_dhcpd_close_sources(void); struct { struct grecs_node *head, *tail; } node_list; } -%token <string> DHCPD_STRING DHCPD_IDENT +%token <string> DHCPD_IF DHCPD_ELSIF DHCPD_EXPR +%token DHCPD_ELSE +%token <string> DHCPD_STRING DHCPD_IDENT %type <string> string %type <svalue> value %type <pvalue> vallist tag %type <list> vlist strlist %type <node> stmt simple block maybe_stmtlist -%type <node_list> stmtlist +%type <node> cond elsecond opt_elsecond elsifcond +%type <node_list> stmtlist elsifchain opt_elsifchain %% @@ -95,6 +112,7 @@ stmtlist: stmt stmt : simple | block + | cond ; simple : DHCPD_IDENT vallist ';' @@ -111,6 +129,14 @@ simple : DHCPD_IDENT vallist ';' $$->v.value = $2; } } + | DHCPD_IDENT '=' { grecs_dhcpd_begin_expr(); } DHCPD_EXPR ';' + { + $$ = grecs_node_create_points(grecs_node_stmt, + @1.beg, @5.end); + $$->ident = $1; + $$->idloc = @1; + $$->v.value = make_string_value($4); + } | string ';' { $$ = grecs_node_create(grecs_node_stmt, &@1); @@ -222,6 +248,99 @@ strlist : DHCPD_STRING ',' DHCPD_STRING } ; +cond : if DHCPD_EXPR '{' maybe_stmtlist '}' opt_elsifchain opt_elsecond + { + $$ = grecs_node_create_points(grecs_node_block, + @1.beg, @7.end); + + grecs_line_begin(); + grecs_line_add("if", 2); + + $$->ident = grecs_line_finish(); + $$->idloc = @1; + + $$->v.value = make_string_value ($2); + grecs_node_bind($$, $4, 1); + + if ($6.head) { + grecs_node_bind($6.tail, $7, 0); + grecs_node_bind($$, $6.head, 0); + } + } + ; + +if : DHCPD_IF + { + grecs_dhcpd_begin_bool(); + } + ; + +elsif : DHCPD_ELSIF + { + grecs_dhcpd_begin_bool(); + } + ; + +opt_elsifchain: /* empty */ + { + $$.head = $$.tail = NULL; + } + | elsifchain + ; + +elsifchain: elsifcond + { + $$.head = $$.tail = $1; + } + | elsifchain elsifcond + { + if ($2) { + if (!$1.head) + $1.head = $1.tail = $2; + else + grecs_node_bind($1.tail, $2, 0); + } + $$ = $1; + } + ; + +elsifcond: elsif DHCPD_EXPR '{' maybe_stmtlist '}' + { + $$ = grecs_node_create_points(grecs_node_block, + @1.beg, @5.end); + + grecs_line_begin(); + grecs_line_add("elsif", 5); + + $$->ident = grecs_line_finish(); + $$->idloc = @1; + $$->v.value = make_string_value ($2); + grecs_node_bind($$, $4, 1); + } + ; + +opt_elsecond: /* empty */ + { + $$ = NULL; + } + | elsecond + ; + +elsecond: DHCPD_ELSE '{' maybe_stmtlist '}' + { + $$ = grecs_node_create_points(grecs_node_block, + @1.beg, @4.end); + + grecs_line_begin(); + grecs_line_add("else", 4); + + $$->ident = grecs_line_finish(); + $$->idloc = @1; + $$->v.value = NULL; + grecs_node_bind($$, $3, 1); + } + ; + %% int diff --git a/src/dhcpd-lex.l b/src/dhcpd-lex.l index 12e64c6..450a54b 100644 --- a/src/dhcpd-lex.l +++ b/src/dhcpd-lex.l @@ -1,6 +1,5 @@ /* grecs - Gray's Extensible Configuration System -*- c -*- */ %option noinput -%option nounput %top { #ifdef HAVE_CONFIG_H # include <config.h> @@ -43,10 +42,13 @@ grecs_current_locus_point.col += yyleng; \ yylloc.end = grecs_current_locus_point; \ } while (0); + +#define ISWS(c) ((c)==' '||(c)=='\t') %} - -%x COMMENT STR +%x COMMENT STR BOOL EXPR + +OWS [ \t\f]* WS [ \t\f][ \t\f]* ID [a-zA-Z_][a-zA-Z_0-9-]* P [1-9][0-9]* @@ -66,6 +68,61 @@ P [1-9][0-9]* #.* /* end-of-file comment */; "//".*\n { grecs_locus_point_advance_line(grecs_current_locus_point); } "//".* /* end-of-file comment */; +"if" return DHCPD_IF; +"elsif" return DHCPD_ELSIF; +"else" return DHCPD_ELSE; + +<BOOL>[^\{\n]*\n { + char *p; + size_t len; + for (p = yytext, len = yyleng - 1; ISWS(*p) && len > 0; p++, len--) + ; + if (len) { + grecs_line_add(p, len); + grecs_line_add(" ", 1); + } + grecs_locus_point_advance_line(grecs_current_locus_point); +} +<BOOL>[^\{\n]*\{ { + char *p; + size_t len; + + unput('{'); + for (p = yytext, len = yyleng - 1; ISWS(*p) && len > 0; p++, len--) + ; + for (; len > 0 && ISWS(p[len-1]); len--) + ; + grecs_line_add(p, len); + BEGIN(INITIAL); + yylval.string = grecs_line_finish(); + return DHCPD_EXPR; +} + +<EXPR>[^;\n]*\n { + char *p; + size_t len; + for (p = yytext, len = yyleng - 1; ISWS(*p) && len > 0; p++, len--) + ; + if (len) { + grecs_line_add(p, len); + grecs_line_add(" ", 1); + } + grecs_locus_point_advance_line(grecs_current_locus_point); +} +<EXPR>[^;\n]*; { + char *p; + size_t len; + + unput(';'); + for (p = yytext, len = yyleng - 1; ISWS(*p) && len > 0; p++, len--) + ; + for (; len > 0 && ISWS(p[len-1]); len--) + ; + grecs_line_add(p, len); + BEGIN(INITIAL); + yylval.string = grecs_line_finish(); + return DHCPD_EXPR; +} /* Identifiers */ {ID} { grecs_line_begin(); grecs_line_add(yytext, yyleng); @@ -103,7 +160,7 @@ P [1-9][0-9]* {WS} ; /* Other tokens */ \n { grecs_locus_point_advance_line(grecs_current_locus_point); } -[,;{}()!] return yytext[0]; +[,;{}()!=] return yytext[0]; . { if (isascii(yytext[0]) && isprint(yytext[0])) grecs_error(&yylloc, 0, _("stray character %c"), yytext[0]); @@ -112,6 +169,18 @@ P [1-9][0-9]* (unsigned char) yytext[0]); } %% +void +grecs_dhcpd_begin_bool(void) +{ + BEGIN(BOOL); +} + +void +grecs_dhcpd_begin_expr(void) +{ + BEGIN(EXPR); +} + struct dhcpd_input_context { ino_t i_node; dev_t i_dev; diff --git a/src/parsertab.c b/src/parsertab.c index 8baec2e..0f3544f 100644 --- a/src/parsertab.c +++ b/src/parsertab.c @@ -34,6 +34,10 @@ static struct parser_tab { #ifdef ENABLE_BIND_PARSER { "BIND", grecs_bind_parser }, #endif +#ifdef ENABLE_DHCPD_PARSER + { "DHCPD", grecs_dhcpd_parser }, + { "DHCP", grecs_dhcpd_parser }, +#endif #ifdef ENABLE_GIT_PARSER { "GIT", grecs_git_parser }, #endif diff --git a/tests/Makefile.am b/tests/Makefile.am index 7014b17..71498a6 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -24,6 +24,7 @@ EXTRA_DIST = \ bind.int.conf\ bind.ext.conf\ bind.dlz\ + dhcpd.conf\ meta1.conf\ git.conf @@ -40,6 +41,10 @@ if GRECS_COND_BIND_PARSER PARSER_DEFS += ENABLE_BIND_PARSER endif +if GRECS_COND_DHCPD_PARSER + PARSER_DEFS += ENABLE_DHCPD_PARSER +endif + if GRECS_COND_GIT_PARSER PARSER_DEFS += ENABLE_GIT_PARSER endif @@ -100,6 +105,7 @@ TESTSUITE_AT = \ locus-bind.at\ locus-git.at\ locus-meta1.at\ + parser-dhcpd.at\ parser-bind.at\ parser-git.at\ parser-meta1.at\ diff --git a/tests/dhcpd.conf b/tests/dhcpd.conf new file mode 100644 index 0000000..654add7 --- /dev/null +++ b/tests/dhcpd.conf @@ -0,0 +1,61 @@ +# Sample DHCPD configuration file for Grecs testsuite. + +authoritative; +pid-file-name "/var/run/dhcpd.pid"; +log-facility daemon; + +default-lease-time 28800; +max-lease-time 86400; + +ddns-update-style none; +get-lease-hostnames yes; +ddns-update-style interim; +ignore client-updates; # Overwrite client configured FQHNs +ddns-rev-domainname "in-addr.arpa."; +use-host-decl-names on; + +include "bind.keys"; + +zone lan.example.com. { + primary 127.0.0.1; + key "foo"; +} + +option domain-search "example.com", "example.org", "example.net"; + +if option dhcp-user-class = "accounting" { + max-lease-time 17600; + option domain-name "accounting.example.org"; + option domain-name-servers ns1.accounting.example.org, + ns2.accounting.example.org; +} elsif option dhcp-user-class = "sales" { + max-lease-time 17600; + option domain-name "sales.example.org"; + option domain-name-servers ns1.sales.example.org, + ns2.sales.example.org; +} elsif option dhcp-user-class = "engineering" { + max-lease-time 17600; + option domain-name "engineering.example.org"; + option domain-name-servers ns1.engineering.example.org, + ns2.engineering.example.org; +} else { + max-lease-time 600; + option domain-name "misc.example.org"; + option domain-name-servers ns1.misc.example.org, + ns2.misc.example.org; +} + +shared-network "LAN" { + subnet 192.168.0.0 netmask 255.255.255.0 { + option routers 192.168.0.1; + option domain-name-servers 192.168.0.1; + update-static-leases on; + + pool { + range 192.168.0.1 192.168.0.254; + deny known-clients; + ddns-hostname = binary-to-ascii(10, 8, "-", leased-address); + } + } +} + diff --git a/tests/parser-dhcpd.at b/tests/parser-dhcpd.at new file mode 100644 index 0000000..1a05147 --- /dev/null +++ b/tests/parser-dhcpd.at @@ -0,0 +1,59 @@ +# This file is part of grecs -*- Autotest -*- +# Copyright (C) 2011-2012, 2016 Sergey Poznyakoff +# +# Grecs is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# Grecs is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Grecs. If not, see <http://www.gnu.org/licenses/>. + +AT_SETUP([DHCPD-style parser]) +AT_KEYWORDS([parser dhcpd]) + +AT_CHECK([gcffmt -type=dhcpd -I$abs_srcdir $abs_srcdir/dhcpd.conf|sed 's/ *$//'], +[0], +[.authoritative: +.pid-file-name: "/var/run/dhcpd.pid" +.log-facility: "daemon" +.default-lease-time: "28800" +.max-lease-time: "86400" +.ddns-update-style: "none" +.get-lease-hostnames: "yes" +.ddns-update-style: "interim" +.ignore: "client-updates" +.ddns-rev-domainname: "in-addr.arpa." +.use-host-decl-names: "on" +.key="rndc-key".algorithm: "hmac-md5" +.key="rndc-key".secret: "1111111111111111111111==" +.zone="lan.example.com.".primary: "127.0.0.1" +.zone="lan.example.com.".key: "foo" +.option: "domain-search" ("example.com", "example.org", "example.net") +.if="option dhcp-user-class = \"accounting\"".max-lease-time: "17600" +.if="option dhcp-user-class = \"accounting\"".option: "domain-name" "accounting.example.org" +.if="option dhcp-user-class = \"accounting\"".option: "domain-name-servers" ("ns1.accounting.example.org", "ns2.accounting.example.org") +.elsif="option dhcp-user-class = \"sales\"".max-lease-time: "17600" +.elsif="option dhcp-user-class = \"sales\"".option: "domain-name" "sales.example.org" +.elsif="option dhcp-user-class = \"sales\"".option: "domain-name-servers" ("ns1.sales.example.org", "ns2.sales.example.org") +.elsif="option dhcp-user-class = \"engineering\"".max-lease-time: "17600" +.elsif="option dhcp-user-class = \"engineering\"".option: "domain-name" "engineering.example.org" +.elsif="option dhcp-user-class = \"engineering\"".option: "domain-name-servers" ("ns1.engineering.example.org", "ns2.engineering.example.org") +.else.max-lease-time: "600" +.else.option: "domain-name" "misc.example.org" +.else.option: "domain-name-servers" ("ns1.misc.example.org", "ns2.misc.example.org") +.shared-network="LAN".subnet="192.168.0.0" "netmask" "255.255.255.0".option: "routers" "192.168.0.1" +.shared-network="LAN".subnet="192.168.0.0" "netmask" "255.255.255.0".option: "domain-name-servers" "192.168.0.1" +.shared-network="LAN".subnet="192.168.0.0" "netmask" "255.255.255.0".update-static-leases: "on" +.shared-network="LAN".subnet="192.168.0.0" "netmask" "255.255.255.0".pool.range: "192.168.0.1" "192.168.0.254" +.shared-network="LAN".subnet="192.168.0.0" "netmask" "255.255.255.0".pool.deny: "known-clients" +.shared-network="LAN".subnet="192.168.0.0" "netmask" "255.255.255.0".pool.ddns-hostname: "binary-to-ascii(10, 8, \"-\", leased-address)" +]) + +AT_CLEANUP + diff --git a/tests/testsuite.at b/tests/testsuite.at index 67fc54a..79aa0f8 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -94,6 +94,11 @@ m4_include([bind00.at]) m4_include([locus-bind.at]) ]) +m4_ifdef([ENABLE_DHCPD_PARSER],[ +AT_BANNER([DHCPD Parser]) +m4_include([parser-dhcpd.at]) +]) + m4_ifdef([ENABLE_META1_PARSER],[ AT_BANNER([MeTA1 Parser]) m4_include([parser-meta1.at]) |