aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2016-03-05 23:40:11 +0200
committerSergey Poznyakoff <gray@gnu.org>2016-03-05 23:44:50 +0200
commit2dbad2727f1d00b223353965b326cfffa96fc776 (patch)
tree3e76daff8a9bf0a1268e183acce13d6ef828ccf7
parent8241dc04501f62b613f17f5c165f915a216ba30c (diff)
downloadgrecs-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.y123
-rw-r--r--src/dhcpd-lex.l77
-rw-r--r--src/parsertab.c4
-rw-r--r--tests/Makefile.am6
-rw-r--r--tests/dhcpd.conf61
-rw-r--r--tests/parser-dhcpd.at59
-rw-r--r--tests/testsuite.at5
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])

Return to:

Send suggestions and report system problems to the System administrator.