Commit 0116ce0829 for bind
commit 0116ce082922f975585c414746a0391545c014c4
Author: Alessio Podda <alessio@isc.org>
Date: Tue Oct 27 14:55:22 2026 +0100
Use tagged configuration tokens with absolute source lines
We want to stop using the ISC_LEXOPT_EOL hint to harmonize
parse_btokens with the rest of config parsing.
To do so, we introduce a new tagged representation for tokens that
can contain metadata, such as the line count, and pull the line
count from the lexer instead of giving us a special EOL token.
diff --git a/bin/named/include/named/server.h b/bin/named/include/named/server.h
index d3aea2f097..c1a0a4952c 100644
--- a/bin/named/include/named/server.h
+++ b/bin/named/include/named/server.h
@@ -426,4 +426,4 @@ named_server_getmemprof(void);
isc_result_t
named_register_one_plugin(const cfg_obj_t *config, const cfg_obj_t *obj,
cfg_aclconfctx_t *aclctx, const char *plugin_path,
- const char *const *parameters, void *callback_data);
+ const cfg_token_t *parameters, void *callback_data);
diff --git a/bin/named/server.c b/bin/named/server.c
index 19574da0c5..96e3445ca6 100644
--- a/bin/named/server.c
+++ b/bin/named/server.c
@@ -3556,7 +3556,7 @@ create_mapped_acl(void) {
isc_result_t
named_register_one_plugin(const cfg_obj_t *config, const cfg_obj_t *obj,
cfg_aclconfctx_t *aclctx, const char *plugin_path,
- const char *const *parameters, void *callback_data) {
+ const cfg_token_t *parameters, void *callback_data) {
char full_path[PATH_MAX];
isc_result_t result;
ns_hook_data_t *hookdata = callback_data;
diff --git a/bin/plugins/filter-a.c b/bin/plugins/filter-a.c
index 5a99cd3f64..ade56aea83 100644
--- a/bin/plugins/filter-a.c
+++ b/bin/plugins/filter-a.c
@@ -192,7 +192,7 @@ parse_filter_a_on(cfg_tokens_t *tok, const char *name, filter_a_t *dstp) {
}
static isc_result_t
-parse_parameters(const char *const *parameters, const char *cfg_file,
+parse_parameters(const cfg_token_t *parameters, const char *cfg_file,
unsigned long cfg_line, filter_params_t *params) {
isc_result_t result = ISC_R_SUCCESS;
cfg_tokens_t tok;
@@ -284,7 +284,7 @@ cleanup:
}
static isc_result_t
-configure_instance(filter_instance_t *inst, const char *const *parameters,
+configure_instance(filter_instance_t *inst, const cfg_token_t *parameters,
const void *cfg, const char *cfg_file,
unsigned long cfg_line, isc_mem_t *mctx, void *aclctx) {
isc_result_t result = ISC_R_SUCCESS;
@@ -324,7 +324,7 @@ cleanup:
* register hook functions into the view hook table.
*/
isc_result_t
-plugin_register(const char *const *parameters, const void *cfg,
+plugin_register(const cfg_token_t *parameters, const void *cfg,
const char *cfg_file, unsigned long cfg_line, isc_mem_t *mctx,
void *aclctx, ns_hooktable_t *hooktable,
const ns_pluginctx_t *ctx, void **instp) {
@@ -366,7 +366,7 @@ cleanup:
}
isc_result_t
-plugin_check(const char *const *parameters, const void *cfg,
+plugin_check(const cfg_token_t *parameters, const void *cfg,
const char *cfg_file, unsigned long cfg_line, isc_mem_t *mctx,
void *aclctx, const ns_pluginctx_t *ctx ISC_ATTR_UNUSED) {
isc_result_t result = ISC_R_SUCCESS;
diff --git a/bin/plugins/filter-aaaa.c b/bin/plugins/filter-aaaa.c
index 9b01918c0a..557d6ffe71 100644
--- a/bin/plugins/filter-aaaa.c
+++ b/bin/plugins/filter-aaaa.c
@@ -192,7 +192,7 @@ parse_filter_aaaa_on(cfg_tokens_t *tok, const char *name, filter_aaaa_t *dstp) {
}
static isc_result_t
-parse_parameters(const char *const *parameters, const char *cfg_file,
+parse_parameters(const cfg_token_t *parameters, const char *cfg_file,
unsigned long cfg_line, filter_params_t *params) {
isc_result_t result = ISC_R_SUCCESS;
cfg_tokens_t tok;
@@ -286,7 +286,7 @@ cleanup:
}
static isc_result_t
-configure_instance(filter_instance_t *inst, const char *const *parameters,
+configure_instance(filter_instance_t *inst, const cfg_token_t *parameters,
const void *cfg, const char *cfg_file,
unsigned long cfg_line, isc_mem_t *mctx, void *aclctx) {
isc_result_t result = ISC_R_SUCCESS;
@@ -326,7 +326,7 @@ cleanup:
* register hook functions into the view hook table.
*/
isc_result_t
-plugin_register(const char *const *parameters, const void *cfg,
+plugin_register(const cfg_token_t *parameters, const void *cfg,
const char *cfg_file, unsigned long cfg_line, isc_mem_t *mctx,
void *aclctx, ns_hooktable_t *hooktable,
const ns_pluginctx_t *ctx, void **instp) {
@@ -369,7 +369,7 @@ cleanup:
}
isc_result_t
-plugin_check(const char *const *parameters, const void *cfg,
+plugin_check(const cfg_token_t *parameters, const void *cfg,
const char *cfg_file, unsigned long cfg_line, isc_mem_t *mctx,
void *aclctx, const ns_pluginctx_t *ctx ISC_ATTR_UNUSED) {
isc_result_t result = ISC_R_SUCCESS;
diff --git a/bin/plugins/synthrecord.c b/bin/plugins/synthrecord.c
index 337e7bbd2f..f5faf08e09 100644
--- a/bin/plugins/synthrecord.c
+++ b/bin/plugins/synthrecord.c
@@ -408,7 +408,7 @@ typedef struct synthrecord_params {
} synthrecord_params_t;
static isc_result_t
-synthrecord_parseparams(const char *const *parameters, const char *cfgfile,
+synthrecord_parseparams(const cfg_token_t *parameters, const char *cfgfile,
unsigned long cfgline, synthrecord_params_t *params) {
isc_result_t result = ISC_R_SUCCESS;
cfg_tokens_t *tok = ¶ms->tokens;
@@ -582,7 +582,7 @@ synthrecord_parseallowsynth(synthrecord_t *inst, const cfg_obj_t *cfg,
}
static isc_result_t
-synthrecord_parseconfig(synthrecord_t *inst, const char *const *parameters,
+synthrecord_parseconfig(synthrecord_t *inst, const cfg_token_t *parameters,
const cfg_obj_t *cfg, const char *cfgfile,
unsigned long cfgline, cfg_aclconfctx_t *aclctx,
const dns_name_t *zname) {
@@ -607,7 +607,7 @@ cleanup:
}
isc_result_t
-plugin_register(const char *const *parameters, const void *cfg,
+plugin_register(const cfg_token_t *parameters, const void *cfg,
const char *cfgfile, unsigned long cfgline, isc_mem_t *mctx,
void *aclctx, ns_hooktable_t *hooktable,
const ns_pluginctx_t *ctx, void **instp) {
@@ -646,7 +646,7 @@ plugin_register(const char *const *parameters, const void *cfg,
}
isc_result_t
-plugin_check(const char *const *parameters, const void *cfg,
+plugin_check(const cfg_token_t *parameters, const void *cfg,
const char *cfgfile, unsigned long cfgline, isc_mem_t *mctx,
void *aclctx, const ns_pluginctx_t *ctx) {
isc_result_t result;
diff --git a/bin/tests/system/dyndb/driver/driver.c b/bin/tests/system/dyndb/driver/driver.c
index a5f604176f..e3d2b9dea6 100644
--- a/bin/tests/system/dyndb/driver/driver.c
+++ b/bin/tests/system/dyndb/driver/driver.c
@@ -85,7 +85,7 @@ dns_dyndb_version_t dyndb_version;
* (for one dyndb section).
*/
isc_result_t
-dyndb_init(isc_mem_t *mctx, const char *name, const char *const *parameters,
+dyndb_init(isc_mem_t *mctx, const char *name, const cfg_token_t *parameters,
const char *file, unsigned long line, const dns_dyndbctx_t *dctx,
void **instp) {
isc_result_t result = ISC_R_SUCCESS;
@@ -99,7 +99,7 @@ dyndb_init(isc_mem_t *mctx, const char *name, const char *const *parameters,
REQUIRE(dctx != NULL);
/* Every parameter is a plain string: build argv from them. */
- for (const char *const *t = parameters; *t != NULL; t++) {
+ for (const cfg_token_t *t = parameters; t->kind != CFG_TOKEN_EOF; t++) {
size++;
}
argv = isc_mem_cget(mctx, size + 1, sizeof(*argv));
diff --git a/bin/tests/system/hooks/driver/test-async.c b/bin/tests/system/hooks/driver/test-async.c
index 15aab8923b..806594023c 100644
--- a/bin/tests/system/hooks/driver/test-async.c
+++ b/bin/tests/system/hooks/driver/test-async.c
@@ -115,7 +115,7 @@ logmsg(const char *fmt, ...) {
* register hook functions into the view hook table.
*/
isc_result_t
-plugin_register(const char *const *parameters, const void *cfg,
+plugin_register(const cfg_token_t *parameters, const void *cfg,
const char *cfg_file, unsigned long cfg_line, isc_mem_t *mctx,
void *aclctx, ns_hooktable_t *hooktable,
const ns_pluginctx_t *ctx, void **instp) {
@@ -148,7 +148,7 @@ plugin_register(const char *const *parameters, const void *cfg,
}
isc_result_t
-plugin_check(const char *const *parameters, const void *cfg,
+plugin_check(const cfg_token_t *parameters, const void *cfg,
const char *cfg_file, unsigned long cfg_line, isc_mem_t *mctx,
void *aclctx, const ns_pluginctx_t *ctx) {
UNUSED(parameters);
diff --git a/bin/tests/system/hooks/driver/test-syncplugin.c b/bin/tests/system/hooks/driver/test-syncplugin.c
index 2248fd93b6..8bcf8f72c6 100644
--- a/bin/tests/system/hooks/driver/test-syncplugin.c
+++ b/bin/tests/system/hooks/driver/test-syncplugin.c
@@ -66,7 +66,7 @@ typedef struct {
} syncplugin_params_t;
static isc_result_t
-syncplugin__parse_params(const char *const *parameters, const char *cfgfile,
+syncplugin__parse_params(const cfg_token_t *parameters, const char *cfgfile,
unsigned long cfgline, syncplugin_params_t *params) {
cfg_tokens_t *tok = ¶ms->tokens;
@@ -122,7 +122,7 @@ syncplugin__parse_rcode(const char *rcodestr, uint8_t *rcode) {
}
isc_result_t
-plugin_register(const char *const *parameters, const void *cfg,
+plugin_register(const cfg_token_t *parameters, const void *cfg,
const char *cfgfile, unsigned long cfgline, isc_mem_t *mctx,
void *aclctx, ns_hooktable_t *hooktable,
const ns_pluginctx_t *ctx, void **instp) {
@@ -216,7 +216,7 @@ cleanup:
}
isc_result_t
-plugin_check(const char *const *parameters, const void *cfg,
+plugin_check(const cfg_token_t *parameters, const void *cfg,
const char *cfgfile, unsigned long cfgline, isc_mem_t *mctx,
void *aclctx, const ns_pluginctx_t *ctx) {
UNUSED(parameters);
diff --git a/lib/dns/dyndb.c b/lib/dns/dyndb.c
index 6ce26d0d19..677823f723 100644
--- a/lib/dns/dyndb.c
+++ b/lib/dns/dyndb.c
@@ -196,7 +196,7 @@ unload_library(dyndb_implementation_t **impp) {
isc_result_t
dns_dyndb_load(const char *libname, const char *name,
- const char *const *parameters, const char *file,
+ const cfg_token_t *parameters, const char *file,
unsigned long line, isc_mem_t *mctx,
const dns_dyndbctx_t *dctx) {
isc_result_t result;
diff --git a/lib/dns/include/dns/dyndb.h b/lib/dns/include/dns/dyndb.h
index 113988355d..d1a6830982 100644
--- a/lib/dns/include/dns/dyndb.h
+++ b/lib/dns/include/dns/dyndb.h
@@ -19,6 +19,8 @@
#include <dns/types.h>
+typedef struct cfg_token cfg_token_t;
+
/*!
* \brief
* Context for initializing a dyndb module.
@@ -52,13 +54,13 @@ struct dns_dyndbctx {
* if not, set DNS_DYNDB_AGE to 0.
*/
#ifndef DNS_DYNDB_VERSION
-#define DNS_DYNDB_VERSION 4
+#define DNS_DYNDB_VERSION 5
#define DNS_DYNDB_AGE 0
#endif /* ifndef DNS_DYNDB_VERSION */
typedef isc_result_t
dns_dyndb_register_t(isc_mem_t *mctx, const char *name,
- const char *const *parameters, const char *file,
+ const cfg_token_t *parameters, const char *file,
unsigned long line, const dns_dyndbctx_t *dctx,
void **instp);
/*%
@@ -99,7 +101,7 @@ dns_dyndb_version_t(unsigned int *flags);
isc_result_t
dns_dyndb_load(const char *libname, const char *name,
- const char *const *parameters, const char *file,
+ const cfg_token_t *parameters, const char *file,
unsigned long line, isc_mem_t *mctx, const dns_dyndbctx_t *dctx);
/*%
* Load a dyndb module.
diff --git a/lib/isccfg/check.c b/lib/isccfg/check.c
index 34de1e5bfe..f8434aeb61 100644
--- a/lib/isccfg/check.c
+++ b/lib/isccfg/check.c
@@ -3224,7 +3224,7 @@ struct check_one_plugin_data {
static isc_result_t
check_one_plugin(const cfg_obj_t *config, const cfg_obj_t *obj,
cfg_aclconfctx_t *aclctx, const char *plugin_path,
- const char *const *parameters, void *callback_data) {
+ const cfg_token_t *parameters, void *callback_data) {
struct check_one_plugin_data *data = callback_data;
char full_path[PATH_MAX];
isc_result_t result = ISC_R_SUCCESS;
diff --git a/lib/isccfg/include/isccfg/cfg.h b/lib/isccfg/include/isccfg/cfg.h
index 3493cda7ef..51ae849ed2 100644
--- a/lib/isccfg/include/isccfg/cfg.h
+++ b/lib/isccfg/include/isccfg/cfg.h
@@ -40,6 +40,7 @@
***/
typedef struct cfg_aclconfctx cfg_aclconfctx_t;
+typedef struct cfg_token cfg_token_t;
/*%
* A configuration parser.
@@ -365,10 +366,10 @@ cfg_obj_istokens(const cfg_obj_t *obj);
* Return true iff 'obj' is a bracketed token list.
*/
-const char *const *
+const cfg_token_t *
cfg_obj_astokens(const cfg_obj_t *obj);
/*%<
- * Returns the value of a bracketed token list as a NULL-terminated
+ * Returns the value of a bracketed token list as a CFG_TOKEN_EOF-terminated
* array of tokens, encoded as described in <isccfg/tokens.h>.
*
* Requires:
@@ -589,7 +590,7 @@ typedef isc_result_t(pluginlist_cb_t)(const cfg_obj_t *config,
const cfg_obj_t *obj,
cfg_aclconfctx_t *aclctx,
const char *plugin_path,
- const char *const *parameters,
+ const cfg_token_t *parameters,
void *callback_data);
/*%<
* Function prototype for the callback used with cfg_pluginlist_foreach().
diff --git a/lib/isccfg/include/isccfg/grammar.h b/lib/isccfg/include/isccfg/grammar.h
index 74b73991fc..e367707187 100644
--- a/lib/isccfg/include/isccfg/grammar.h
+++ b/lib/isccfg/include/isccfg/grammar.h
@@ -228,7 +228,7 @@ struct cfg_obj {
uint32_t uint32;
uint64_t uint64;
char *string; /*%< null terminated */
- char **tokens; /*%< see <isccfg/tokens.h> */
+ cfg_token_t *tokens; /*%< see <isccfg/tokens.h> */
bool boolean;
cfg_map_t *map;
cfg_list_t *list;
@@ -510,12 +510,13 @@ cfg_print_cstr(cfg_printer_t *pctx, const char *s);
/*%< Print the null-terminated string 's' */
void
-cfg_print_tokens(cfg_printer_t *pctx, const char *const *tokens,
- const char *const *end);
+cfg_print_tokens(cfg_printer_t *pctx, const cfg_token_t *tokens,
+ const cfg_token_t *end, unsigned long line);
/*%<
* Print the token array 'tokens' (see <isccfg/tokens.h>) as a
- * bracketed block, stopping at 'end' or at the terminating NULL,
- * whichever comes first. 'end' may be NULL.
+ * bracketed block, stopping at 'end' or at the terminating CFG_TOKEN_EOF,
+ * whichever comes first. 'end' may be NULL. 'line' is the source line
+ * of the opening bracket.
*/
isc_result_t
diff --git a/lib/isccfg/include/isccfg/tokens.h b/lib/isccfg/include/isccfg/tokens.h
index 62af5ef769..fd14475e26 100644
--- a/lib/isccfg/include/isccfg/tokens.h
+++ b/lib/isccfg/include/isccfg/tokens.h
@@ -19,13 +19,12 @@
*
* The contents of the "{ ... }" block of a "plugin" or "dyndb" statement
* are lexed, but not parsed, by named. They are handed to the plugin or
- * driver as a NULL-terminated array of tokens, in which:
+ * driver as a CFG_TOKEN_EOF-terminated array of tokens, in which:
*
* \li CFG_TOKEN_OPEN, CFG_TOKEN_CLOSE, CFG_TOKEN_END, CFG_TOKEN_SLASH
* and CFG_TOKEN_BANG stand for '{', '}', ';', '/' and '!' respectively;
- * \li CFG_TOKEN_NEWLINE marks a line break in the original text, so
- * that line numbers can be tracked;
- * \li every other element preserves the token's original spelling,
+ * \li CFG_TOKEN_LINE sets the absolute source line for following tokens;
+ * \li CFG_TOKEN_STRING preserves the token's original spelling,
* including quotes and escapes. For example, "10.0.0.0/8" is three
* tokens: "10.0.0.0", CFG_TOKEN_SLASH and "8".
*
@@ -41,30 +40,36 @@
#include <isccfg/cfg.h>
-#define CFG_TOKEN_OPEN ((const char *)(uintptr_t)-1)
-#define CFG_TOKEN_NEWLINE ((const char *)(uintptr_t)-2)
-#define CFG_TOKEN_CLOSE ((const char *)(uintptr_t)-3)
-#define CFG_TOKEN_END ((const char *)(uintptr_t)-4)
-#define CFG_TOKEN_SLASH ((const char *)(uintptr_t)-5)
-#define CFG_TOKEN_BANG ((const char *)(uintptr_t)-6)
-
-/*%
- * True if the (non-NULL) token 't' is a string rather than one of the
- * CFG_TOKEN_* markers.
- */
-#define CFG_TOKEN_ISSTRING(t) ((uintptr_t)(t) < (uintptr_t)CFG_TOKEN_BANG)
+typedef enum {
+ CFG_TOKEN_EOF = 0,
+ CFG_TOKEN_STRING,
+ CFG_TOKEN_LINE,
+ CFG_TOKEN_OPEN,
+ CFG_TOKEN_CLOSE,
+ CFG_TOKEN_END,
+ CFG_TOKEN_SLASH,
+ CFG_TOKEN_BANG,
+} cfg_token_kind_t;
+
+struct cfg_token {
+ cfg_token_kind_t kind;
+ union {
+ const char *text;
+ unsigned long line;
+ };
+};
typedef struct cfg_tokenstring cfg_tokenstring_t;
typedef struct cfg_tokens {
- const char *const *next;
+ const cfg_token_t *next;
const char *file;
unsigned long line;
cfg_tokenstring_t *strings;
} cfg_tokens_t;
void
-cfg_tokens_init(cfg_tokens_t *tok, const char *const *tokens, const char *file,
+cfg_tokens_init(cfg_tokens_t *tok, const cfg_token_t *tokens, const char *file,
unsigned long line);
/*%<
* Initialize the cursor 'tok' to walk 'tokens'. 'file' and 'line' are
@@ -79,21 +84,21 @@ cfg_tokens_clear(cfg_tokens_t *tok);
* with the cursor and all values returned by cfg_tokens_getstring().
*/
-const char *
+const cfg_token_t *
cfg_tokens_peek(cfg_tokens_t *tok);
/*%<
- * Return the next token without consuming it, skipping (and counting)
- * line breaks. Returns NULL at the end of the list.
+ * Return the next token without consuming it, skipping line markers and
+ * updating the source line. Returns NULL at the end of the list.
*/
-const char *
+const cfg_token_t *
cfg_tokens_next(cfg_tokens_t *tok);
/*%<
* Like cfg_tokens_peek(), but consume the token.
*/
isc_result_t
-cfg_tokens_expect(cfg_tokens_t *tok, const char *marker);
+cfg_tokens_expect(cfg_tokens_t *tok, cfg_token_kind_t marker);
/*%<
* Consume the next token, which must be 'marker' (one of CFG_TOKEN_OPEN,
* CFG_TOKEN_CLOSE, CFG_TOKEN_END, CFG_TOKEN_SLASH or CFG_TOKEN_BANG).
diff --git a/lib/isccfg/parser.c b/lib/isccfg/parser.c
index 306e5dc449..79101e351d 100644
--- a/lib/isccfg/parser.c
+++ b/lib/isccfg/parser.c
@@ -241,18 +241,18 @@ static void
copy_tokens(cfg_obj_t *to, const cfg_obj_t *from) {
size_t n = 0;
- while (from->value.tokens[n] != NULL) {
+ while (from->value.tokens[n].kind != CFG_TOKEN_EOF) {
n++;
}
- to->value.tokens = isc_mem_allocate(isc_g_mctx,
- (n + 1) * sizeof(char *));
- for (size_t i = 0; i < n; i++) {
- char *t = from->value.tokens[i];
- to->value.tokens[i] = CFG_TOKEN_ISSTRING(t)
- ? isc_mem_strdup(isc_g_mctx, t)
- : t;
+ to->value.tokens = isc_mem_allocate(
+ isc_g_mctx, (n + 1) * sizeof(*to->value.tokens));
+ for (size_t i = 0; i <= n; i++) {
+ to->value.tokens[i] = from->value.tokens[i];
+ if (from->value.tokens[i].kind == CFG_TOKEN_STRING) {
+ to->value.tokens[i].text = isc_mem_strdup(
+ isc_g_mctx, from->value.tokens[i].text);
+ }
}
- to->value.tokens[n] = NULL;
}
static void
@@ -1497,7 +1497,8 @@ cleanup:
}
static void
-tokens_append(cfg_obj_t *obj, size_t *countp, size_t *sizep, char *token) {
+tokens_append(cfg_obj_t *obj, size_t *countp, size_t *sizep,
+ cfg_token_t token) {
if (*countp + 1 >= *sizep) {
*sizep = (*sizep == 0) ? 16 : *sizep * 2;
obj->value.tokens = isc_mem_reallocate(
@@ -1505,7 +1506,19 @@ tokens_append(cfg_obj_t *obj, size_t *countp, size_t *sizep, char *token) {
*sizep * sizeof(obj->value.tokens[0]));
}
obj->value.tokens[(*countp)++] = token;
- obj->value.tokens[*countp] = NULL;
+ obj->value.tokens[*countp] = (cfg_token_t){ .kind = CFG_TOKEN_EOF };
+}
+
+static size_t
+count_newlines(const char *text, size_t length) {
+ size_t count = 0;
+
+ for (size_t i = 0; i < length; i++) {
+ if (text[i] == '\n') {
+ count++;
+ }
+ }
+ return count;
}
static isc_result_t
@@ -1525,37 +1538,53 @@ parse_btokens(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
/*
* Create the object now, so that it carries the line number of
- * the opening bracket. Start with an empty, NULL-terminated array.
+ * the opening bracket. Start with an empty, EOF-terminated array.
*/
cfg_obj_create(cfg_parser_currentfile(pctx), pctx->line, type, &obj);
obj->value.tokens = NULL;
- tokens_append(obj, &count, &size, NULL);
+ tokens_append(obj, &count, &size,
+ (cfg_token_t){ .kind = CFG_TOKEN_EOF });
count = 0;
+ unsigned long line = pctx->line;
for (;;) {
- char *token = NULL;
+ cfg_token_t token = { 0 };
+ isc_region_t region = { 0 };
+
+ CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
+ /* cfg_gettoken() closes the input source at EOF. */
+ if (pctx->token.type != isc_tokentype_eof) {
+ isc_lex_getlasttokentext(pctx->lexer, &pctx->token,
+ ®ion);
+ }
+ const size_t next = pctx->line -
+ count_newlines((const char *)region.base,
+ region.length);
+
+ /* Set the starting line, including after multiline strings. */
+ if (next != line) {
+ tokens_append(obj, &count, &size,
+ (cfg_token_t){ .kind = CFG_TOKEN_LINE,
+ .line = next });
+ line = next;
+ }
- CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING | ISC_LEXOPT_EOL));
switch (pctx->token.type) {
- case isc_tokentype_eol:
- token = UNCONST(CFG_TOKEN_NEWLINE);
- break;
case isc_tokentype_string:
case isc_tokentype_qstring: {
- isc_region_t region;
-
- isc_lex_getlasttokentext(pctx->lexer, &pctx->token,
- ®ion);
- token = isc_mem_allocate(isc_g_mctx, region.length + 1);
- memmove(token, region.base, region.length);
- token[region.length] = '\0';
+ char *text = isc_mem_allocate(isc_g_mctx,
+ region.length + 1);
+ memmove(text, region.base, region.length);
+ text[region.length] = '\0';
+ token = (cfg_token_t){ .kind = CFG_TOKEN_STRING,
+ .text = text };
break;
}
case isc_tokentype_special:
switch (pctx->token.value.as_char) {
case '{':
depth++;
- token = UNCONST(CFG_TOKEN_OPEN);
+ token.kind = CFG_TOKEN_OPEN;
break;
case '}':
if (depth == 0) {
@@ -1563,16 +1592,16 @@ parse_btokens(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
return ISC_R_SUCCESS;
}
depth--;
- token = UNCONST(CFG_TOKEN_CLOSE);
+ token.kind = CFG_TOKEN_CLOSE;
break;
case ';':
- token = UNCONST(CFG_TOKEN_END);
+ token.kind = CFG_TOKEN_END;
break;
case '/':
- token = UNCONST(CFG_TOKEN_SLASH);
+ token.kind = CFG_TOKEN_SLASH;
break;
case '!':
- token = UNCONST(CFG_TOKEN_BANG);
+ token.kind = CFG_TOKEN_BANG;
break;
default:
UNREACHABLE();
@@ -1598,53 +1627,57 @@ cleanup:
}
void
-cfg_print_tokens(cfg_printer_t *pctx, const char *const *tokens,
- const char *const *end) {
+cfg_print_tokens(cfg_printer_t *pctx, const cfg_token_t *tokens,
+ const cfg_token_t *end, unsigned long line) {
bool oneline = (pctx->flags & CFG_PRINTER_ONELINE) != 0;
bool newline = false;
/*
- * Line breaks are reproduced from the CFG_TOKEN_NEWLINE markers,
- * so the output has the same line structure as the input; the
- * indentation is derived from the bracket depth.
+ * Preserve source line spacing for ACL reconstruction and derive
+ * indentation from the bracket depth.
*/
pctx->indent++;
cfg_print_cstr(pctx, "{");
- for (; tokens != end && *tokens != NULL; tokens++) {
- const char *t = *tokens;
+ for (; tokens != end && tokens->kind != CFG_TOKEN_EOF; tokens++) {
+ const cfg_token_t *t = tokens;
- if (t == CFG_TOKEN_NEWLINE) {
+ if (t->kind == CFG_TOKEN_LINE) {
if (!oneline) {
- cfg_print_cstr(pctx, "\n");
- newline = true;
+ while (line < t->line) {
+ cfg_print_cstr(pctx, "\n");
+ newline = true;
+ line++;
+ }
}
+ line = t->line;
continue;
}
- if (t == CFG_TOKEN_CLOSE) {
+ if (t->kind == CFG_TOKEN_CLOSE) {
pctx->indent--;
}
if (newline) {
cfg_print_indent(pctx);
newline = false;
- } else if (t != CFG_TOKEN_END) {
+ } else if (t->kind != CFG_TOKEN_END) {
cfg_print_cstr(pctx, " ");
}
- if (t == CFG_TOKEN_OPEN) {
+ if (t->kind == CFG_TOKEN_OPEN) {
cfg_print_cstr(pctx, "{");
pctx->indent++;
- } else if (t == CFG_TOKEN_CLOSE) {
+ } else if (t->kind == CFG_TOKEN_CLOSE) {
cfg_print_cstr(pctx, "}");
- } else if (t == CFG_TOKEN_END) {
+ } else if (t->kind == CFG_TOKEN_END) {
cfg_print_cstr(pctx, ";");
- } else if (t == CFG_TOKEN_SLASH) {
+ } else if (t->kind == CFG_TOKEN_SLASH) {
cfg_print_cstr(pctx, "/");
- } else if (t == CFG_TOKEN_BANG) {
+ } else if (t->kind == CFG_TOKEN_BANG) {
cfg_print_cstr(pctx, "!");
} else {
- cfg_print_cstr(pctx, t);
+ cfg_print_cstr(pctx, t->text);
+ line += count_newlines(t->text, strlen(t->text));
}
}
@@ -1659,7 +1692,7 @@ cfg_print_tokens(cfg_printer_t *pctx, const char *const *tokens,
static void
print_btokens(cfg_printer_t *pctx, const cfg_obj_t *obj) {
- cfg_print_tokens(pctx, cfg_obj_astokens(obj), NULL);
+ cfg_print_tokens(pctx, cfg_obj_astokens(obj), NULL, obj->line);
}
static void
@@ -1673,11 +1706,11 @@ cfg_obj_istokens(const cfg_obj_t *obj) {
return obj->type->rep == &cfg_rep_tokens;
}
-const char *const *
+const cfg_token_t *
cfg_obj_astokens(const cfg_obj_t *obj) {
REQUIRE(VALID_CFGOBJ(obj));
REQUIRE(obj->type->rep == &cfg_rep_tokens);
- return (const char *const *)obj->value.tokens;
+ return obj->value.tokens;
}
bool
@@ -1845,9 +1878,12 @@ free_tokens(cfg_obj_t *obj) {
if (obj->value.tokens == NULL) {
return;
}
- for (char **t = obj->value.tokens; *t != NULL; t++) {
- if (CFG_TOKEN_ISSTRING(*t)) {
- isc_mem_free(isc_g_mctx, *t);
+ for (cfg_token_t *t = obj->value.tokens; t->kind != CFG_TOKEN_EOF; t++)
+ {
+ if (t->kind == CFG_TOKEN_STRING) {
+ char *text = UNCONST(t->text);
+ isc_mem_free(isc_g_mctx, text);
+ t->text = NULL;
}
}
isc_mem_free(isc_g_mctx, obj->value.tokens);
@@ -4340,7 +4376,7 @@ cfg_pluginlist_foreach(const cfg_obj_t *config, const cfg_obj_t *list,
const cfg_obj_t *plugin = cfg_listelt_value(element);
const cfg_obj_t *obj;
const char *type, *library;
- const char *const *parameters = NULL;
+ const cfg_token_t *parameters = NULL;
/* Get the path to the plugin module. */
obj = cfg_tuple_get(plugin, "type");
diff --git a/lib/isccfg/tokens.c b/lib/isccfg/tokens.c
index 37f125dc38..73238d7131 100644
--- a/lib/isccfg/tokens.c
+++ b/lib/isccfg/tokens.c
@@ -30,10 +30,10 @@ struct cfg_tokenstring {
cfg_tokenstring_t *next;
};
-static const char *const empty[] = { NULL };
+static const cfg_token_t empty[] = { { .kind = CFG_TOKEN_EOF } };
void
-cfg_tokens_init(cfg_tokens_t *tok, const char *const *tokens, const char *file,
+cfg_tokens_init(cfg_tokens_t *tok, const cfg_token_t *tokens, const char *file,
unsigned long line) {
REQUIRE(tok != NULL);
@@ -55,20 +55,20 @@ cfg_tokens_clear(cfg_tokens_t *tok) {
}
}
-const char *
+const cfg_token_t *
cfg_tokens_peek(cfg_tokens_t *tok) {
REQUIRE(tok != NULL);
- while (*tok->next == CFG_TOKEN_NEWLINE) {
+ while (tok->next->kind == CFG_TOKEN_LINE) {
+ tok->line = tok->next->line;
tok->next++;
- tok->line++;
}
- return *tok->next;
+ return tok->next->kind == CFG_TOKEN_EOF ? NULL : tok->next;
}
-const char *
+const cfg_token_t *
cfg_tokens_next(cfg_tokens_t *tok) {
- const char *token = cfg_tokens_peek(tok);
+ const cfg_token_t *token = cfg_tokens_peek(tok);
if (token != NULL) {
tok->next++;
@@ -97,7 +97,7 @@ cfg_tokens_log(const cfg_tokens_t *tok, int level, const char *fmt, ...) {
}
static const char *
-markername(const char *marker) {
+markername(cfg_token_kind_t marker) {
if (marker == CFG_TOKEN_OPEN) {
return "'{'";
} else if (marker == CFG_TOKEN_CLOSE) {
@@ -113,8 +113,8 @@ markername(const char *marker) {
}
isc_result_t
-cfg_tokens_expect(cfg_tokens_t *tok, const char *marker) {
- const char *token = cfg_tokens_next(tok);
+cfg_tokens_expect(cfg_tokens_t *tok, cfg_token_kind_t marker) {
+ const cfg_token_t *token = cfg_tokens_next(tok);
if (token == NULL) {
cfg_tokens_log(tok, ISC_LOG_ERROR,
@@ -122,7 +122,7 @@ cfg_tokens_expect(cfg_tokens_t *tok, const char *marker) {
markername(marker));
return ISC_R_UNEXPECTEDEND;
}
- if (token != marker) {
+ if (token->kind != marker) {
cfg_tokens_log(tok, ISC_LOG_ERROR, "expected %s",
markername(marker));
return ISC_R_UNEXPECTEDTOKEN;
@@ -132,7 +132,7 @@ cfg_tokens_expect(cfg_tokens_t *tok, const char *marker) {
isc_result_t
cfg_tokens_getstring(cfg_tokens_t *tok, const char **strp) {
- const char *token = NULL;
+ const cfg_token_t *token = NULL;
REQUIRE(strp != NULL);
@@ -142,16 +142,16 @@ cfg_tokens_getstring(cfg_tokens_t *tok, const char **strp) {
"expected string, found end of input");
return ISC_R_UNEXPECTEDEND;
}
- if (!CFG_TOKEN_ISSTRING(token)) {
+ if (token->kind != CFG_TOKEN_STRING) {
cfg_tokens_log(tok, ISC_LOG_ERROR, "expected string");
return ISC_R_UNEXPECTEDTOKEN;
}
- if (token[0] == '"') {
+ if (token->text[0] == '"') {
cfg_obj_t *obj = NULL;
isc_buffer_t buffer;
- size_t length = strlen(token);
+ size_t length = strlen(token->text);
- isc_buffer_constinit(&buffer, token, length);
+ isc_buffer_constinit(&buffer, token->text, length);
isc_buffer_add(&buffer, length);
RETERR(cfg_parse_buffer(&buffer, tok->file, tok->line,
&cfg_type_qstring, 0, &obj));
@@ -163,7 +163,7 @@ cfg_tokens_getstring(cfg_tokens_t *tok, const char **strp) {
tok->strings = string;
*strp = cfg_obj_asstring(obj);
} else {
- *strp = token;
+ *strp = token->text;
}
return ISC_R_SUCCESS;
}
@@ -176,7 +176,7 @@ print_tobuffer(void *closure, const char *text, int textlen) {
isc_result_t
cfg_tokens_getaml(cfg_tokens_t *tok, cfg_obj_t **objp) {
isc_result_t result;
- const char *const *end = NULL;
+ const cfg_token_t *end = NULL;
unsigned long line;
unsigned int depth = 0;
isc_buffer_t *b = NULL;
@@ -189,13 +189,13 @@ cfg_tokens_getaml(cfg_tokens_t *tok, cfg_obj_t **objp) {
/* Find the matching closing bracket. */
for (end = tok->next;; end++) {
- if (*end == NULL) {
+ if (end->kind == CFG_TOKEN_EOF) {
cfg_tokens_log(tok, ISC_LOG_ERROR,
"expected '}', found end of input");
return ISC_R_UNEXPECTEDEND;
- } else if (*end == CFG_TOKEN_OPEN) {
+ } else if (end->kind == CFG_TOKEN_OPEN) {
depth++;
- } else if (*end == CFG_TOKEN_CLOSE) {
+ } else if (end->kind == CFG_TOKEN_CLOSE) {
if (depth == 0) {
break;
}
@@ -211,15 +211,15 @@ cfg_tokens_getaml(cfg_tokens_t *tok, cfg_obj_t **objp) {
*/
isc_buffer_allocate(isc_g_mctx, &b, 256);
pctx = (cfg_printer_t){ .f = print_tobuffer, .closure = b };
- cfg_print_tokens(&pctx, tok->next, end);
+ cfg_print_tokens(&pctx, tok->next, end, line);
result = cfg_parse_buffer(b, tok->file, line, &cfg_type_bracketed_aml,
0, objp);
isc_buffer_free(&b);
for (; tok->next != end; tok->next++) {
- if (*tok->next == CFG_TOKEN_NEWLINE) {
- tok->line++;
+ if (tok->next->kind == CFG_TOKEN_LINE) {
+ tok->line = tok->next->line;
}
}
tok->next++;
diff --git a/lib/ns/hooks.c b/lib/ns/hooks.c
index 53dbabe312..efaef4227e 100644
--- a/lib/ns/hooks.c
+++ b/lib/ns/hooks.c
@@ -211,7 +211,7 @@ unload_plugin(ns_plugin_t **pluginp) {
}
isc_result_t
-ns_plugin_register(const char *modpath, const char *const *parameters,
+ns_plugin_register(const char *modpath, const cfg_token_t *parameters,
const void *cfg, const char *cfg_file,
unsigned long cfg_line, isc_mem_t *mctx, void *aclctx,
ns_hook_data_t *hookdata) {
@@ -248,7 +248,7 @@ cleanup:
}
isc_result_t
-ns_plugin_check(const char *modpath, const char *const *parameters,
+ns_plugin_check(const char *modpath, const cfg_token_t *parameters,
const void *cfg, const char *cfg_file, unsigned long cfg_line,
isc_mem_t *mctx, void *aclctx, const ns_pluginctx_t *ctx) {
isc_result_t result;
diff --git a/lib/ns/include/ns/hooks.h b/lib/ns/include/ns/hooks.h
index c530f23e28..837f4c331c 100644
--- a/lib/ns/include/ns/hooks.h
+++ b/lib/ns/include/ns/hooks.h
@@ -26,6 +26,8 @@
#include <ns/client.h>
#include <ns/query.h>
+
+typedef struct cfg_token cfg_token_t;
/*
* "Hooks" are a mechanism to call a defined function or set of functions once
* a certain place in code is reached. Hook actions can inspect and alter the
@@ -515,12 +517,12 @@ typedef struct ns_hook_data {
* as well; if not, set NS_PLUGIN_AGE to 0.
*/
#ifndef NS_PLUGIN_VERSION
-#define NS_PLUGIN_VERSION 5
+#define NS_PLUGIN_VERSION 6
#define NS_PLUGIN_AGE 0
#endif /* ifndef NS_PLUGIN_VERSION */
typedef isc_result_t
-ns_plugin_register_t(const char *const *parameters, const void *cfg,
+ns_plugin_register_t(const cfg_token_t *parameters, const void *cfg,
const char *file, unsigned long line, isc_mem_t *mctx,
void *aclctx, ns_hooktable_t *hooktable,
const ns_pluginctx_t *ctx, void **instp);
@@ -549,7 +551,7 @@ ns_plugin_destroy_t(void **instp);
*/
typedef isc_result_t
-ns_plugin_check_t(const char *const *parameters, const void *cfg,
+ns_plugin_check_t(const cfg_token_t *parameters, const void *cfg,
const char *file, unsigned long line, isc_mem_t *mctx,
void *aclctx, const ns_pluginctx_t *ctx);
/*%<
@@ -597,7 +599,7 @@ ns_plugin_expandpath(const char *src, char *dst, size_t dstsize);
*/
isc_result_t
-ns_plugin_register(const char *modpath, const char *const *parameters,
+ns_plugin_register(const char *modpath, const cfg_token_t *parameters,
const void *cfg, const char *cfg_file,
unsigned long cfg_line, isc_mem_t *mctx, void *aclctx,
ns_hook_data_t *hookdata);
@@ -617,7 +619,7 @@ ns_plugin_register(const char *modpath, const char *const *parameters,
*/
isc_result_t
-ns_plugin_check(const char *modpath, const char *const *parameters,
+ns_plugin_check(const char *modpath, const cfg_token_t *parameters,
const void *cfg, const char *cfg_file, unsigned long cfg_line,
isc_mem_t *mctx, void *aclctx, const ns_pluginctx_t *ctx);
/*%<
diff --git a/tests/isccfg/parser_test.c b/tests/isccfg/parser_test.c
index ae8f8886de..41066d38a8 100644
--- a/tests/isccfg/parser_test.c
+++ b/tests/isccfg/parser_test.c
@@ -60,30 +60,51 @@ append(void *arg, const char *str, int len) {
/* Preserve lexical spelling, including quoted punctuation and escapes. */
ISC_RUN_TEST_IMPL(tokens_roundtrip) {
- const char *spelling[] = {
- "plain", "\"plain\"", "\"\"", "\"a\\ b\"",
- "\"a\\;b\"", "\"a\\#b\"", "\"a\\/b\"", "\"a\\!b\"",
- "\"a\\{b\"", "\"a\\}b\"", "\"a\\\tb\"", "\"a\\\"b\"",
- "tail\\", "tail\\\\", "\"tail\\\\\"", "\"a\\\\\\\"b\"",
- "\"/\"", "\"/\"", "\"/\"", "\"*\"",
- "\"!\"", CFG_TOKEN_SLASH, CFG_TOKEN_BANG, CFG_TOKEN_OPEN,
- "nested", CFG_TOKEN_END, CFG_TOKEN_CLOSE, CFG_TOKEN_END,
- NULL
+ const cfg_token_t spelling[] = {
+ { .kind = CFG_TOKEN_STRING, .text = "plain" },
+ { .kind = CFG_TOKEN_STRING, .text = "\"plain\"" },
+ { .kind = CFG_TOKEN_STRING, .text = "\"\"" },
+ { .kind = CFG_TOKEN_STRING, .text = "\"a\\ b\"" },
+ { .kind = CFG_TOKEN_STRING, .text = "\"a\\;b\"" },
+ { .kind = CFG_TOKEN_STRING, .text = "\"a\\#b\"" },
+ { .kind = CFG_TOKEN_STRING, .text = "\"a\\/b\"" },
+ { .kind = CFG_TOKEN_STRING, .text = "\"a\\!b\"" },
+ { .kind = CFG_TOKEN_STRING, .text = "\"a\\{b\"" },
+ { .kind = CFG_TOKEN_STRING, .text = "\"a\\}b\"" },
+ { .kind = CFG_TOKEN_STRING, .text = "\"a\\\tb\"" },
+ { .kind = CFG_TOKEN_STRING, .text = "\"a\\\"b\"" },
+ { .kind = CFG_TOKEN_STRING, .text = "tail\\" },
+ { .kind = CFG_TOKEN_STRING, .text = "tail\\\\" },
+ { .kind = CFG_TOKEN_STRING, .text = "\"tail\\\\\"" },
+ { .kind = CFG_TOKEN_STRING, .text = "\"a\\\\\\\"b\"" },
+ { .kind = CFG_TOKEN_STRING, .text = "\"/\"" },
+ { .kind = CFG_TOKEN_STRING, .text = "\"/\"" },
+ { .kind = CFG_TOKEN_STRING, .text = "\"/\"" },
+ { .kind = CFG_TOKEN_STRING, .text = "\"*\"" },
+ { .kind = CFG_TOKEN_STRING, .text = "\"!\"" },
+ { .kind = CFG_TOKEN_SLASH },
+ { .kind = CFG_TOKEN_BANG },
+ { .kind = CFG_TOKEN_OPEN },
+ { .kind = CFG_TOKEN_STRING, .text = "nested" },
+ { .kind = CFG_TOKEN_END },
+ { .kind = CFG_TOKEN_CLOSE },
+ { .kind = CFG_TOKEN_END },
+ { .kind = CFG_TOKEN_EOF }
};
unsigned int flags[] = { 0, CFG_PRINTER_ONELINE };
char input[1024] = "{ ";
- for (const char *const *t = spelling; *t != NULL; t++) {
- const char *text = *t;
- if (text == CFG_TOKEN_OPEN) {
+ for (const cfg_token_t *t = spelling; t->kind != CFG_TOKEN_EOF; t++) {
+ const char *text = t->text;
+ if (t->kind == CFG_TOKEN_OPEN) {
text = "{";
- } else if (text == CFG_TOKEN_CLOSE) {
+ } else if (t->kind == CFG_TOKEN_CLOSE) {
text = "}";
- } else if (text == CFG_TOKEN_END) {
+ } else if (t->kind == CFG_TOKEN_END) {
text = ";";
- } else if (text == CFG_TOKEN_SLASH) {
+ } else if (t->kind == CFG_TOKEN_SLASH) {
text = "/";
- } else if (text == CFG_TOKEN_BANG) {
+ } else if (t->kind == CFG_TOKEN_BANG) {
text = "!";
}
strlcat(input, text, sizeof(input));
@@ -110,35 +131,155 @@ ISC_RUN_TEST_IMPL(tokens_roundtrip) {
©),
ISC_R_SUCCESS);
- const char *const *original = cfg_obj_astokens(obj);
- const char *const *reparsed = cfg_obj_astokens(copy);
+ const cfg_token_t *original = cfg_obj_astokens(obj);
+ const cfg_token_t *reparsed = cfg_obj_astokens(copy);
for (size_t j = 0; j < ARRAY_SIZE(spelling); j++) {
- if (spelling[j] != NULL &&
- CFG_TOKEN_ISSTRING(spelling[j]))
- {
- assert_non_null(original[j]);
- assert_non_null(reparsed[j]);
- assert_true(CFG_TOKEN_ISSTRING(original[j]));
- assert_true(CFG_TOKEN_ISSTRING(reparsed[j]));
- assert_string_equal(original[j], spelling[j]);
- assert_string_equal(reparsed[j], spelling[j]);
- } else {
- assert_ptr_equal(original[j], spelling[j]);
- assert_ptr_equal(reparsed[j], spelling[j]);
+ assert_int_equal(original[j].kind, spelling[j].kind);
+ assert_int_equal(reparsed[j].kind, spelling[j].kind);
+ if (spelling[j].kind == CFG_TOKEN_STRING) {
+ assert_string_equal(original[j].text,
+ spelling[j].text);
+ assert_string_equal(reparsed[j].text,
+ spelling[j].text);
}
}
+
cfg_obj_detach(&obj);
cfg_obj_detach(©);
}
}
+/* Preserve skipped lines without duplicating newlines inside strings. */
+ISC_RUN_TEST_IMPL(tokens_newlines) {
+ const char *input = "{\r\n\n/* comment\ncontinued */ plain;\n"
+ "// comment\n# comment\n\"first\nsecond\";\n"
+ "{ nested;\n}; /* trailing\ncomment */\n}";
+ const cfg_token_t expected[] = {
+ { .kind = CFG_TOKEN_LINE, .line = 103 },
+ { .kind = CFG_TOKEN_STRING, .text = "plain" },
+ { .kind = CFG_TOKEN_END },
+ { .kind = CFG_TOKEN_LINE, .line = 106 },
+ { .kind = CFG_TOKEN_STRING, .text = "\"first\nsecond\"" },
+ { .kind = CFG_TOKEN_LINE, .line = 107 },
+ { .kind = CFG_TOKEN_END },
+ { .kind = CFG_TOKEN_LINE, .line = 108 },
+ { .kind = CFG_TOKEN_OPEN },
+ { .kind = CFG_TOKEN_STRING, .text = "nested" },
+ { .kind = CFG_TOKEN_END },
+ { .kind = CFG_TOKEN_LINE, .line = 109 },
+ { .kind = CFG_TOKEN_CLOSE },
+ { .kind = CFG_TOKEN_END },
+ { .kind = CFG_TOKEN_LINE, .line = 111 },
+ { .kind = CFG_TOKEN_EOF },
+ };
+ char printed[1024] = "";
+
+ for (size_t pass = 0; pass < 2; pass++) {
+ cfg_obj_t *obj = NULL;
+ isc_buffer_t b;
+
+ isc_buffer_constinit(&b, input, strlen(input));
+ isc_buffer_add(&b, strlen(input));
+ assert_int_equal(cfg_parse_buffer(&b, "newlines", 100,
+ &cfg_type_bracketed_tokens, 0,
+ &obj),
+ ISC_R_SUCCESS);
+
+ const cfg_token_t *tokens = cfg_obj_astokens(obj);
+ for (size_t i = 0; i < ARRAY_SIZE(expected); i++) {
+ assert_int_equal(tokens[i].kind, expected[i].kind);
+ if (expected[i].kind == CFG_TOKEN_STRING) {
+ assert_string_equal(tokens[i].text,
+ expected[i].text);
+ } else if (expected[i].kind == CFG_TOKEN_LINE) {
+ assert_int_equal(tokens[i].line,
+ expected[i].line);
+ }
+ }
+
+ if (pass == 0) {
+ cfg_printx(obj, 0, append, printed);
+ input = printed;
+ }
+ cfg_obj_detach(&obj);
+ }
+}
+
+/* Absolute line markers locate tokens after multiline strings and ACLs. */
+ISC_RUN_TEST_IMPL(tokens_cursor_lines) {
+ const char *input = "{\n\"first\nsecond\nthird\";\n"
+ "{ \"acl\nname\"; any;\n}\n{ any; }\n}";
+ isc_buffer_t b;
+ cfg_obj_t *tokens = NULL, *copy = NULL;
+
+ isc_buffer_constinit(&b, input, strlen(input));
+ isc_buffer_add(&b, strlen(input));
+ assert_int_equal(cfg_parse_buffer(&b, "lines", 100,
+ &cfg_type_bracketed_tokens, 0,
+ &tokens),
+ ISC_R_SUCCESS);
+ cfg_obj_clone(tokens, ©);
+ cfg_obj_detach(&tokens);
+
+ for (size_t pass = 0; pass < 2; pass++) {
+ cfg_tokens_t tok;
+ cfg_obj_t *aml = NULL;
+ const char *value = NULL;
+
+ cfg_tokens_init(&tok, cfg_obj_astokens(copy), "lines", 100);
+ for (size_t i = 0; i < 2; i++) {
+ const cfg_token_t *token = cfg_tokens_peek(&tok);
+ assert_non_null(token);
+ assert_int_equal(token->kind, CFG_TOKEN_STRING);
+ assert_string_equal(token->text,
+ "\"first\nsecond\nthird\"");
+ assert_int_equal(tok.line, 101);
+ }
+ if (pass == 0) {
+ assert_string_equal(cfg_tokens_next(&tok)->text,
+ "\"first\nsecond\nthird\"");
+ } else {
+ assert_int_equal(cfg_tokens_getstring(&tok, &value),
+ ISC_R_SUCCESS);
+ assert_string_equal(value, "first\nsecond\nthird");
+ }
+ assert_int_equal(tok.line, 101);
+ assert_int_equal(cfg_tokens_expect(&tok, CFG_TOKEN_END),
+ ISC_R_SUCCESS);
+ assert_int_equal(tok.line, 103);
+ assert_int_equal(cfg_tokens_getaml(&tok, &aml), ISC_R_SUCCESS);
+ assert_int_equal(cfg_obj_line(aml), 104);
+ const cfg_listelt_t *second =
+ cfg_list_next(cfg_list_first(aml));
+ assert_non_null(second);
+ assert_int_equal(cfg_obj_line(cfg_listelt_value(second)), 105);
+ assert_int_equal(tok.line, 106);
+ cfg_obj_detach(&aml);
+ assert_int_equal(cfg_tokens_getaml(&tok, &aml), ISC_R_SUCCESS);
+ assert_int_equal(cfg_obj_line(aml), 107);
+ assert_int_equal(tok.line, 107);
+ cfg_obj_detach(&aml);
+ assert_null(cfg_tokens_next(&tok));
+ assert_int_equal(tok.line, 108);
+ cfg_tokens_clear(&tok);
+ }
+ cfg_obj_detach(©);
+}
+
/* Decoding leaves lexical tokens intact and previous results valid. */
ISC_RUN_TEST_IMPL(tokens_strings) {
- const char *tokens[] = { "\"dynamic-\"", "\"\"",
- "\"a\\\"b\"", "\"a\\ b\"",
- "\"tail\\\\\"", "\"a\\\\\\\"b\"",
- "a\\\"b", "\"a\nb\"",
- "plain", NULL };
+ const cfg_token_t tokens[] = {
+ { .kind = CFG_TOKEN_STRING, .text = "\"dynamic-\"" },
+ { .kind = CFG_TOKEN_STRING, .text = "\"\"" },
+ { .kind = CFG_TOKEN_STRING, .text = "\"a\\\"b\"" },
+ { .kind = CFG_TOKEN_STRING, .text = "\"a\\ b\"" },
+ { .kind = CFG_TOKEN_STRING, .text = "\"tail\\\\\"" },
+ { .kind = CFG_TOKEN_STRING, .text = "\"a\\\\\\\"b\"" },
+ { .kind = CFG_TOKEN_STRING, .text = "a\\\"b" },
+ { .kind = CFG_TOKEN_STRING, .text = "\"a\nb\"" },
+ { .kind = CFG_TOKEN_STRING, .text = "plain" },
+ { .kind = CFG_TOKEN_EOF },
+ };
const char *expected[] = { "dynamic-", "", "a\"b",
"a\\ b", "tail\\\\", "a\\\\\"b",
"a\\\"b", "a\nb", "plain" };
@@ -147,7 +288,7 @@ ISC_RUN_TEST_IMPL(tokens_strings) {
cfg_tokens_init(&tok, tokens, "strings", 1);
for (size_t i = 0; i < ARRAY_SIZE(expected); i++) {
- assert_ptr_equal(cfg_tokens_peek(&tok), tokens[i]);
+ assert_ptr_equal(cfg_tokens_peek(&tok), &tokens[i]);
assert_int_equal(cfg_tokens_getstring(&tok, &values[i]),
ISC_R_SUCCESS);
}
@@ -155,7 +296,7 @@ ISC_RUN_TEST_IMPL(tokens_strings) {
for (size_t i = 0; i < ARRAY_SIZE(expected); i++) {
assert_string_equal(values[i], expected[i]);
}
- assert_string_equal(tokens[0], "\"dynamic-\"");
+ assert_string_equal(tokens[0].text, "\"dynamic-\"");
cfg_tokens_clear(&tok);
}
@@ -500,6 +641,8 @@ ISC_RUN_TEST_IMPL(cfg_map_findclause_empty) {
ISC_TEST_LIST_START
ISC_TEST_ENTRY(tokens_roundtrip)
+ISC_TEST_ENTRY(tokens_newlines)
+ISC_TEST_ENTRY(tokens_cursor_lines)
ISC_TEST_ENTRY(tokens_strings)
ISC_TEST_ENTRY(tokens_aml)
ISC_TEST_ENTRY(addzoneconf)