From e3511e3cac32253a64965f98126bd3d424c9b7e3 Mon Sep 17 00:00:00 2001
From: Jan Vcelak <jan.vcelak@nic.cz>
Date: Sat, 6 Apr 2013 00:47:15 +0200
Subject: [PATCH] config scanner: separate includes structures

refs #2381
---
 Knot.files               |  4 ++
 src/Makefile.am          |  8 +++-
 src/knot/conf/cf-lex.l   | 88 +++++++--------------------------------
 src/knot/conf/conf.c     | 19 ++++++---
 src/knot/conf/extra.c    | 54 ++++++++++++++++++++++++
 src/knot/conf/extra.h    | 31 ++++++++++++++
 src/knot/conf/includes.c | 89 ++++++++++++++++++++++++++++++++++++++++
 src/knot/conf/includes.h | 32 +++++++++++++++
 8 files changed, 244 insertions(+), 81 deletions(-)
 create mode 100644 src/knot/conf/extra.c
 create mode 100644 src/knot/conf/extra.h
 create mode 100644 src/knot/conf/includes.c
 create mode 100644 src/knot/conf/includes.h

diff --git a/Knot.files b/Knot.files
index c4c386e0ec..1cd5fb5d7e 100644
--- a/Knot.files
+++ b/Knot.files
@@ -88,6 +88,10 @@ src/knot/conf/cf-lex.l
 src/knot/conf/cf-parse.y
 src/knot/conf/conf.c
 src/knot/conf/conf.h
+src/knot/conf/extra.c
+src/knot/conf/extra.h
+src/knot/conf/includes.c
+src/knot/conf/includes.h
 src/knot/conf/logconf.c
 src/knot/conf/logconf.h
 src/knot/ctl/knotc_main.c
diff --git a/src/Makefile.am b/src/Makefile.am
index e4e2c019f8..c589900049 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -264,12 +264,16 @@ libknotd_la_SOURCES =				\
 	knot/stat/stat.h			\
 	knot/common.h				\
 	knot/other/debug.h			\
-	knot/conf/cf-parse.y			\
 	knot/conf/cf-lex.l			\
+	knot/conf/cf-parse.y			\
 	knot/conf/conf.c			\
+	knot/conf/conf.h			\
+	knot/conf/extra.c			\
+	knot/conf/extra.h			\
+	knot/conf/includes.c			\
+	knot/conf/includes.h			\
 	knot/conf/logconf.c			\
 	knot/conf/logconf.h			\
-	knot/conf/conf.h			\
 	knot/ctl/process.c			\
 	knot/ctl/process.h			\
 	knot/ctl/remote.c			\
diff --git a/src/knot/conf/cf-lex.l b/src/knot/conf/cf-lex.l
index dc940a60a5..a94dc20168 100644
--- a/src/knot/conf/cf-lex.l
+++ b/src/knot/conf/cf-lex.l
@@ -16,6 +16,8 @@
 
 #include "common/sockaddr.h"
 #include "knot/conf/conf.h"
+#include "knot/conf/includes.h"
+#include "knot/conf/extra.h"
 #include "common/log.h"
 #include "libknotd_la-cf-parse.h" /* Automake generated header. */
 
@@ -42,69 +44,6 @@ int hex2bin(const char* src, char *dst, size_t len) {
 	return 0;
 }
 
-/* Scanner extra data. */
-
-#define MAX_INCLUSION_DEPTH 8
-
-typedef struct {
-	char *names[MAX_INCLUSION_DEPTH];
-	int free_index;
-} cf_includes_t;
-
-typedef struct {
-	bool error;
-	cf_includes_t includes;
-} cf_extra_t;
-
-#define YY_USER_INIT yyextra = calloc(1, sizeof(cf_extra_t))
-
-/* Functions to control scanner extra data. */
-
-static bool names_stack_has_space(cf_extra_t *extra)
-{
-	if (!extra)
-		return false;
-
-	return extra->includes.free_index < MAX_INCLUSION_DEPTH;
-}
-
-static void names_stack_push(cf_extra_t *extra, char *name)
-{
-	if (!extra)
-		return;
-
-	extra->includes.names[extra->includes.free_index] = name;
-	extra->includes.free_index += 1;
-}
-
-static char *names_stack_pop(cf_extra_t *extra)
-{
-	if (!extra || extra->includes.free_index == 0)
-		return NULL;
-
-	extra->includes.free_index -= 1;
-	return extra->includes.names[extra->includes.free_index];
-}
-
-/* Public interface to extra data in scanner. */
-
-extern cf_extra_t *cf_get_extra(yyscan_t scanner);
-
-void cf_set_error(yyscan_t scanner)
-{
-	cf_extra_t *extra = cf_get_extra(scanner);
-	if (!extra)
-		return;
-
-	extra->error = true;
-}
-
-char *cf_current_filename(yyscan_t scanner)
-{
-	cf_extra_t *extra = cf_get_extra(scanner);
-	return names_stack_pop(extra);
-}
-
 //#define YY_INPUT(buf,result,max) result = cf_read_hook(buf, max);
 #define YY_NO_UNPUT
 
@@ -119,7 +58,7 @@ char *cf_current_filename(yyscan_t scanner)
 %option yylineno
 %option prefix = "cf_"
 %option outfile = "lex.yy.c"
-%option extra-type = "cf_extra_t *"
+%option extra-type = "conf_extra_t *"
 
 %x include
 
@@ -341,13 +280,11 @@ hmac-sha512     { lval.alg = KNOT_TSIG_ALG_HMAC_SHA512; return TSIG_ALGO_NAME; }
 : /* Optional : in assignments. */;
 
 <<EOF>> {
-	char *name = names_stack_pop(yyextra);
+	char *name = conf_includes_pop(yyextra->includes);
 	free(name);
 	yypop_buffer_state(yyscanner);
-	if (!YY_CURRENT_BUFFER) {
-		free(yyextra);
+	if (!YY_CURRENT_BUFFER)
 		return END;
-	}
 }
 
 <include>{BLANK}+
@@ -359,22 +296,25 @@ hmac-sha512     { lval.alg = KNOT_TSIG_ALG_HMAC_SHA512; return TSIG_ALGO_NAME; }
 		return END;
 	}
 
-	if (!names_stack_has_space(yyextra)) {
+	// remove quotes
+	yytext += 1;
+	yyleng -= 2;
+	yytext[yyleng] = '\0';
+
+	if (!conf_includes_can_push(yyextra->includes)) {
 		cf_error(yyscanner, "includes nested too deeply");
 		return END;
 	}
 
-	char *filename = strndup(yytext + 1, yyleng - 2);
-	FILE *included = fopen(filename, "r");
+	FILE *included = fopen(yytext, "r");
 	if (!included) {
-		cf_error(yyscanner, "cannot open file '%s'", filename);
-		free(filename);
+		cf_error(yyscanner, "cannot open file '%s'", yytext);
 		return END;
 	}
 
+	conf_includes_push(yyextra->includes, yytext);
 	YY_BUFFER_STATE bs = yy_create_buffer(included, YY_BUF_SIZE, yyscanner);
 	yypush_buffer_state(bs, yyscanner);
-	names_stack_push(yyextra, filename);
 }
 
 <include>["][^"\n]*\n cf_error(yyscanner, "Unterminated string.");
diff --git a/src/knot/conf/conf.c b/src/knot/conf/conf.c
index edf676cbee..a1e41612b2 100644
--- a/src/knot/conf/conf.c
+++ b/src/knot/conf/conf.c
@@ -14,6 +14,7 @@
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <assert.h>
 #include <stdarg.h>
 #include <string.h>
 #include <stdlib.h>
@@ -25,6 +26,7 @@
 
 #include <urcu.h>
 #include "knot/conf/conf.h"
+#include "knot/conf/extra.h"
 #include "knot/common.h"
 #include "knot/ctl/remote.h"
 
@@ -39,6 +41,7 @@ static const char *DEFAULT_CONFIG[] = {
 
 #define DEFAULT_CONF_COUNT 1 /*!< \brief Number of default config paths. */
 #define ERROR_BUFFER_SIZE 512 /*!< \brief Error buffer size. */
+#define INCLUDES_MAX_DEPTH 8 /*!< \brief Max depth of config inclusion. */
 
 /*
  * Utilities.
@@ -49,7 +52,8 @@ extern int cf_parse(void *scanner);
 extern int cf_get_lineno(void *scanner);
 extern void cf_set_error(void *scanner);
 extern char *cf_get_text(void *scanner);
-extern int cf_lex_init(void *scanner);
+extern conf_extra_t *cf_get_extra(void *scanner);
+extern int cf_lex_init_extra(void *, void *scanner);
 extern void cf_set_in(FILE *f, void *scanner);
 extern void cf_lex_destroy(void *scanner);
 extern void switch_input(const char *str, void *scanner);
@@ -68,14 +72,15 @@ static void cf_print_error(void *scanner, const char *msg)
 		text = cf_get_text(scanner);
 	}
 
-	char *filename = cf_current_filename(scanner);
+	conf_extra_t *extra = cf_get_extra(scanner);
+	char *filename = conf_includes_top(extra->includes);
 	if (!filename)
 		filename = new_config->filename;
 
 	log_server_error("Config error in '%s' (line %d token '%s') - %s\n",
 			 filename, lineno, text, msg);
 
-	cf_set_error(scanner);
+	extra->error = true;
 	_parser_res = KNOT_EPARSEFAIL;
 }
 
@@ -444,10 +449,12 @@ static int conf_fparser(conf_t *conf)
 	_parser_res = KNOT_EOK;
 	new_config->filename = conf->filename;
 	void *sc = NULL;
-	cf_lex_init(&sc);
+	conf_extra_t *extra = conf_extra_init(conf->filename, INCLUDES_MAX_DEPTH);
+	cf_lex_init_extra(extra, &sc);
 	cf_set_in(f, sc);
 	cf_parse(sc);
 	cf_lex_destroy(sc);
+	conf_extra_free(extra);
 	ret = _parser_res;
 	fclose(f);
 	// }
@@ -476,10 +483,12 @@ static int conf_strparser(conf_t *conf, const char *src)
 	char *oldfn = new_config->filename;
 	new_config->filename = "(stdin)";
 	void *sc = NULL;
-	cf_lex_init(&sc);
+	conf_extra_t *extra = conf_extra_init("", INCLUDES_MAX_DEPTH);
+	cf_lex_init_extra(extra, &sc);
 	switch_input(src, sc);
 	cf_parse(sc);
 	cf_lex_destroy(sc);
+	conf_extra_free(extra);
 	new_config->filename = oldfn;
 	ret = _parser_res;
 	// }
diff --git a/src/knot/conf/extra.c b/src/knot/conf/extra.c
new file mode 100644
index 0000000000..a01117a463
--- /dev/null
+++ b/src/knot/conf/extra.c
@@ -0,0 +1,54 @@
+/*  Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+    This program 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 of the License, or
+    (at your option) any later version.
+
+    This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "knot/conf/includes.h"
+#include "knot/conf/extra.h"
+
+conf_extra_t *conf_extra_init(const char *file, int includes_capacity)
+{
+	conf_extra_t *extra = calloc(1, sizeof(conf_extra_t));
+	if (!extra)
+		return NULL;
+
+	conf_includes_t *includes = conf_includes_init(includes_capacity);
+	if (!includes) {
+		free(extra);
+		return NULL;
+	}
+
+	if (!conf_includes_push(includes, file)) {
+		free(extra);
+		return NULL;
+	}
+
+	extra->error = false;
+	extra->includes = includes;
+
+	return extra;
+}
+
+void conf_extra_free(conf_extra_t *extra)
+{
+	if (!extra)
+		return;
+
+	conf_includes_free(extra->includes);
+	free(extra);
+}
diff --git a/src/knot/conf/extra.h b/src/knot/conf/extra.h
new file mode 100644
index 0000000000..23108355a9
--- /dev/null
+++ b/src/knot/conf/extra.h
@@ -0,0 +1,31 @@
+/*  Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+    This program 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 of the License, or
+    (at your option) any later version.
+
+    This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef _KNOT_CONF_EXTRA_H_
+#define _KNOT_CONF_EXTRA_H_
+
+#include <stdbool.h>
+
+#include "knot/conf/includes.h"
+
+typedef struct {
+	bool error;
+	conf_includes_t *includes;
+} conf_extra_t;
+
+conf_extra_t *conf_extra_init(const char *file, int includes_capacity);
+void conf_extra_free(conf_extra_t *extra);
+
+#endif /* _KNOT_CONF_EXTRA_H_ */
diff --git a/src/knot/conf/includes.c b/src/knot/conf/includes.c
new file mode 100644
index 0000000000..522502607e
--- /dev/null
+++ b/src/knot/conf/includes.c
@@ -0,0 +1,89 @@
+/*  Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+    This program 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 of the License, or
+    (at your option) any later version.
+
+    This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "knot/conf/includes.h"
+
+struct conf_includes {
+	int free_index;
+	int capacity;
+	char *names[0];
+};
+
+conf_includes_t *conf_includes_init(int capacity)
+{
+	if (capacity <= 0)
+		return NULL;
+
+	size_t size = sizeof(conf_includes_t) + (capacity * sizeof(char *));
+	conf_includes_t *result = calloc(1, size);
+	if (!result)
+		return NULL;
+
+	result->capacity = capacity;
+	return result;
+}
+
+void conf_includes_free(conf_includes_t *includes)
+{
+	if (!includes)
+		return;
+
+	for (int i = 0; i < includes->free_index; i++)
+		free(includes->names[i]);
+
+	free(includes);
+}
+
+bool conf_includes_can_push(conf_includes_t *includes)
+{
+	if (!includes)
+		return false;
+
+	return includes->free_index < includes->capacity;
+}
+
+bool conf_includes_push(conf_includes_t *includes, const char *filename)
+{
+	if (!includes || !filename)
+		return false;
+
+	if (!conf_includes_can_push(includes))
+		return false;
+
+	includes->names[includes->free_index++] = strdup(filename);
+	return true;
+}
+
+char *conf_includes_top(conf_includes_t *includes)
+{
+	if (!includes || includes->free_index == 0)
+		return NULL;
+
+	return includes->names[includes->free_index - 1];
+}
+
+char *conf_includes_pop(conf_includes_t *includes)
+{
+	char *result = conf_includes_top(includes);
+	if (result)
+		includes->free_index -= 1;
+
+	return result;
+}
diff --git a/src/knot/conf/includes.h b/src/knot/conf/includes.h
new file mode 100644
index 0000000000..9e1b4f78c2
--- /dev/null
+++ b/src/knot/conf/includes.h
@@ -0,0 +1,32 @@
+/*  Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+    This program 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 of the License, or
+    (at your option) any later version.
+
+    This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef _KNOT_CONF_INCLUDES_H_
+#define _KNOT_CONF_INCLUDES_H_
+
+#include <stdbool.h>
+
+struct conf_includes;
+typedef struct conf_includes conf_includes_t;
+
+conf_includes_t *conf_includes_init(int capacity);
+void conf_includes_free(conf_includes_t *includes);
+
+bool conf_includes_can_push(conf_includes_t *includes);
+bool conf_includes_push(conf_includes_t *includes, const char *filename);
+char *conf_includes_top(conf_includes_t *includes);
+char *conf_includes_pop(conf_includes_t *includes);
+
+#endif /* _KNOT_CONF_INCLUDES_H_ */
-- 
GitLab