summaryrefslogtreecommitdiffstats
path: root/lib/isc/lex.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--lib/isc/lex.c1133
1 files changed, 1133 insertions, 0 deletions
diff --git a/lib/isc/lex.c b/lib/isc/lex.c
new file mode 100644
index 0000000..5878d53
--- /dev/null
+++ b/lib/isc/lex.c
@@ -0,0 +1,1133 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <ctype.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include <isc/buffer.h>
+#include <isc/file.h>
+#include <isc/lex.h>
+#include <isc/mem.h>
+#include <isc/parseint.h>
+#include <isc/print.h>
+#include <isc/stdio.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+typedef struct inputsource {
+ isc_result_t result;
+ bool is_file;
+ bool need_close;
+ bool at_eof;
+ bool last_was_eol;
+ isc_buffer_t *pushback;
+ unsigned int ignored;
+ void *input;
+ char *name;
+ unsigned long line;
+ unsigned long saved_line;
+ ISC_LINK(struct inputsource) link;
+} inputsource;
+
+#define LEX_MAGIC ISC_MAGIC('L', 'e', 'x', '!')
+#define VALID_LEX(l) ISC_MAGIC_VALID(l, LEX_MAGIC)
+
+struct isc_lex {
+ /* Unlocked. */
+ unsigned int magic;
+ isc_mem_t *mctx;
+ size_t max_token;
+ char *data;
+ unsigned int comments;
+ bool comment_ok;
+ bool last_was_eol;
+ unsigned int brace_count;
+ unsigned int paren_count;
+ unsigned int saved_paren_count;
+ isc_lexspecials_t specials;
+ LIST(struct inputsource) sources;
+};
+
+static isc_result_t
+grow_data(isc_lex_t *lex, size_t *remainingp, char **currp, char **prevp) {
+ char *tmp;
+
+ tmp = isc_mem_get(lex->mctx, lex->max_token * 2 + 1);
+ memmove(tmp, lex->data, lex->max_token + 1);
+ *currp = tmp + (*currp - lex->data);
+ if (*prevp != NULL) {
+ *prevp = tmp + (*prevp - lex->data);
+ }
+ isc_mem_put(lex->mctx, lex->data, lex->max_token + 1);
+ lex->data = tmp;
+ *remainingp += lex->max_token;
+ lex->max_token *= 2;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_lex_create(isc_mem_t *mctx, size_t max_token, isc_lex_t **lexp) {
+ isc_lex_t *lex;
+
+ /*
+ * Create a lexer.
+ */
+ REQUIRE(lexp != NULL && *lexp == NULL);
+
+ if (max_token == 0U) {
+ max_token = 1;
+ }
+
+ lex = isc_mem_get(mctx, sizeof(*lex));
+ lex->data = isc_mem_get(mctx, max_token + 1);
+ lex->mctx = mctx;
+ lex->max_token = max_token;
+ lex->comments = 0;
+ lex->comment_ok = true;
+ lex->last_was_eol = true;
+ lex->brace_count = 0;
+ lex->paren_count = 0;
+ lex->saved_paren_count = 0;
+ memset(lex->specials, 0, 256);
+ INIT_LIST(lex->sources);
+ lex->magic = LEX_MAGIC;
+
+ *lexp = lex;
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+isc_lex_destroy(isc_lex_t **lexp) {
+ isc_lex_t *lex;
+
+ /*
+ * Destroy the lexer.
+ */
+
+ REQUIRE(lexp != NULL);
+ lex = *lexp;
+ *lexp = NULL;
+ REQUIRE(VALID_LEX(lex));
+
+ while (!EMPTY(lex->sources)) {
+ RUNTIME_CHECK(isc_lex_close(lex) == ISC_R_SUCCESS);
+ }
+ if (lex->data != NULL) {
+ isc_mem_put(lex->mctx, lex->data, lex->max_token + 1);
+ }
+ lex->magic = 0;
+ isc_mem_put(lex->mctx, lex, sizeof(*lex));
+}
+
+unsigned int
+isc_lex_getcomments(isc_lex_t *lex) {
+ /*
+ * Return the current lexer commenting styles.
+ */
+
+ REQUIRE(VALID_LEX(lex));
+
+ return (lex->comments);
+}
+
+void
+isc_lex_setcomments(isc_lex_t *lex, unsigned int comments) {
+ /*
+ * Set allowed lexer commenting styles.
+ */
+
+ REQUIRE(VALID_LEX(lex));
+
+ lex->comments = comments;
+}
+
+void
+isc_lex_getspecials(isc_lex_t *lex, isc_lexspecials_t specials) {
+ /*
+ * Put the current list of specials into 'specials'.
+ */
+
+ REQUIRE(VALID_LEX(lex));
+
+ memmove(specials, lex->specials, 256);
+}
+
+void
+isc_lex_setspecials(isc_lex_t *lex, isc_lexspecials_t specials) {
+ /*
+ * The characters in 'specials' are returned as tokens. Along with
+ * whitespace, they delimit strings and numbers.
+ */
+
+ REQUIRE(VALID_LEX(lex));
+
+ memmove(lex->specials, specials, 256);
+}
+
+static isc_result_t
+new_source(isc_lex_t *lex, bool is_file, bool need_close, void *input,
+ const char *name) {
+ inputsource *source;
+
+ source = isc_mem_get(lex->mctx, sizeof(*source));
+ source->result = ISC_R_SUCCESS;
+ source->is_file = is_file;
+ source->need_close = need_close;
+ source->at_eof = false;
+ source->last_was_eol = lex->last_was_eol;
+ source->input = input;
+ source->name = isc_mem_strdup(lex->mctx, name);
+ source->pushback = NULL;
+ isc_buffer_allocate(lex->mctx, &source->pushback,
+ (unsigned int)lex->max_token);
+ source->ignored = 0;
+ source->line = 1;
+ ISC_LIST_INITANDPREPEND(lex->sources, source, link);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_lex_openfile(isc_lex_t *lex, const char *filename) {
+ isc_result_t result;
+ FILE *stream = NULL;
+
+ /*
+ * Open 'filename' and make it the current input source for 'lex'.
+ */
+
+ REQUIRE(VALID_LEX(lex));
+
+ result = isc_stdio_open(filename, "r", &stream);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ result = new_source(lex, true, true, stream, filename);
+ if (result != ISC_R_SUCCESS) {
+ (void)fclose(stream);
+ }
+ return (result);
+}
+
+isc_result_t
+isc_lex_openstream(isc_lex_t *lex, FILE *stream) {
+ char name[128];
+
+ /*
+ * Make 'stream' the current input source for 'lex'.
+ */
+
+ REQUIRE(VALID_LEX(lex));
+
+ snprintf(name, sizeof(name), "stream-%p", stream);
+
+ return (new_source(lex, true, false, stream, name));
+}
+
+isc_result_t
+isc_lex_openbuffer(isc_lex_t *lex, isc_buffer_t *buffer) {
+ char name[128];
+
+ /*
+ * Make 'buffer' the current input source for 'lex'.
+ */
+
+ REQUIRE(VALID_LEX(lex));
+
+ snprintf(name, sizeof(name), "buffer-%p", buffer);
+
+ return (new_source(lex, false, false, buffer, name));
+}
+
+isc_result_t
+isc_lex_close(isc_lex_t *lex) {
+ inputsource *source;
+
+ /*
+ * Close the most recently opened object (i.e. file or buffer).
+ */
+
+ REQUIRE(VALID_LEX(lex));
+
+ source = HEAD(lex->sources);
+ if (source == NULL) {
+ return (ISC_R_NOMORE);
+ }
+
+ ISC_LIST_UNLINK(lex->sources, source, link);
+ lex->last_was_eol = source->last_was_eol;
+ if (source->is_file) {
+ if (source->need_close) {
+ (void)fclose((FILE *)(source->input));
+ }
+ }
+ isc_mem_free(lex->mctx, source->name);
+ isc_buffer_free(&source->pushback);
+ isc_mem_put(lex->mctx, source, sizeof(*source));
+
+ return (ISC_R_SUCCESS);
+}
+
+typedef enum {
+ lexstate_start,
+ lexstate_crlf,
+ lexstate_string,
+ lexstate_number,
+ lexstate_maybecomment,
+ lexstate_ccomment,
+ lexstate_ccommentend,
+ lexstate_eatline,
+ lexstate_qstring,
+ lexstate_btext,
+ lexstate_vpair,
+ lexstate_vpairstart,
+ lexstate_qvpair,
+} lexstate;
+
+#define IWSEOL (ISC_LEXOPT_INITIALWS | ISC_LEXOPT_EOL)
+
+static void
+pushback(inputsource *source, int c) {
+ REQUIRE(source->pushback->current > 0);
+ if (c == EOF) {
+ source->at_eof = false;
+ return;
+ }
+ source->pushback->current--;
+ if (c == '\n') {
+ source->line--;
+ }
+}
+
+static isc_result_t
+pushandgrow(isc_lex_t *lex, inputsource *source, int c) {
+ if (isc_buffer_availablelength(source->pushback) == 0) {
+ isc_buffer_t *tbuf = NULL;
+ unsigned int oldlen;
+ isc_region_t used;
+ isc_result_t result;
+
+ oldlen = isc_buffer_length(source->pushback);
+ isc_buffer_allocate(lex->mctx, &tbuf, oldlen * 2);
+ isc_buffer_usedregion(source->pushback, &used);
+ result = isc_buffer_copyregion(tbuf, &used);
+ INSIST(result == ISC_R_SUCCESS);
+ tbuf->current = source->pushback->current;
+ isc_buffer_free(&source->pushback);
+ source->pushback = tbuf;
+ }
+ isc_buffer_putuint8(source->pushback, (uint8_t)c);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_lex_gettoken(isc_lex_t *lex, unsigned int options, isc_token_t *tokenp) {
+ inputsource *source;
+ int c;
+ bool done = false;
+ bool no_comments = false;
+ bool escaped = false;
+ lexstate state = lexstate_start;
+ lexstate saved_state = lexstate_start;
+ isc_buffer_t *buffer;
+ FILE *stream;
+ char *curr, *prev;
+ size_t remaining;
+ uint32_t as_ulong;
+ unsigned int saved_options;
+ isc_result_t result;
+
+ /*
+ * Get the next token.
+ */
+
+ REQUIRE(VALID_LEX(lex));
+ source = HEAD(lex->sources);
+ REQUIRE(tokenp != NULL);
+
+ if (source == NULL) {
+ if ((options & ISC_LEXOPT_NOMORE) != 0) {
+ tokenp->type = isc_tokentype_nomore;
+ return (ISC_R_SUCCESS);
+ }
+ return (ISC_R_NOMORE);
+ }
+
+ if (source->result != ISC_R_SUCCESS) {
+ return (source->result);
+ }
+
+ lex->saved_paren_count = lex->paren_count;
+ source->saved_line = source->line;
+
+ if (isc_buffer_remaininglength(source->pushback) == 0 && source->at_eof)
+ {
+ if ((options & ISC_LEXOPT_DNSMULTILINE) != 0 &&
+ lex->paren_count != 0)
+ {
+ lex->paren_count = 0;
+ return (ISC_R_UNBALANCED);
+ }
+ if ((options & ISC_LEXOPT_BTEXT) != 0 && lex->brace_count != 0)
+ {
+ lex->brace_count = 0;
+ return (ISC_R_UNBALANCED);
+ }
+ if ((options & ISC_LEXOPT_EOF) != 0) {
+ tokenp->type = isc_tokentype_eof;
+ return (ISC_R_SUCCESS);
+ }
+ return (ISC_R_EOF);
+ }
+
+ isc_buffer_compact(source->pushback);
+
+ saved_options = options;
+ if ((options & ISC_LEXOPT_DNSMULTILINE) != 0 && lex->paren_count > 0) {
+ options &= ~IWSEOL;
+ }
+
+ curr = lex->data;
+ *curr = '\0';
+
+ prev = NULL;
+ remaining = lex->max_token;
+
+#ifdef HAVE_FLOCKFILE
+ if (source->is_file) {
+ flockfile(source->input);
+ }
+#endif /* ifdef HAVE_FLOCKFILE */
+
+ do {
+ if (isc_buffer_remaininglength(source->pushback) == 0) {
+ if (source->is_file) {
+ stream = source->input;
+
+#if defined(HAVE_FLOCKFILE) && defined(HAVE_GETC_UNLOCKED)
+ c = getc_unlocked(stream);
+#else /* if defined(HAVE_FLOCKFILE) && defined(HAVE_GETC_UNLOCKED) */
+ c = getc(stream);
+#endif /* if defined(HAVE_FLOCKFILE) && defined(HAVE_GETC_UNLOCKED) */
+ if (c == EOF) {
+ if (ferror(stream)) {
+ source->result = ISC_R_IOERROR;
+ result = source->result;
+ goto done;
+ }
+ source->at_eof = true;
+ }
+ } else {
+ buffer = source->input;
+
+ if (buffer->current == buffer->used) {
+ c = EOF;
+ source->at_eof = true;
+ } else {
+ c = *((unsigned char *)buffer->base +
+ buffer->current);
+ buffer->current++;
+ }
+ }
+ if (c != EOF) {
+ source->result = pushandgrow(lex, source, c);
+ if (source->result != ISC_R_SUCCESS) {
+ result = source->result;
+ goto done;
+ }
+ }
+ }
+
+ if (!source->at_eof) {
+ if (state == lexstate_start) {
+ /* Token has not started yet. */
+ source->ignored = isc_buffer_consumedlength(
+ source->pushback);
+ }
+ c = isc_buffer_getuint8(source->pushback);
+ } else {
+ c = EOF;
+ }
+
+ if (c == '\n') {
+ source->line++;
+ }
+
+ if (lex->comment_ok && !no_comments) {
+ if (!escaped && c == ';' &&
+ ((lex->comments & ISC_LEXCOMMENT_DNSMASTERFILE) !=
+ 0))
+ {
+ saved_state = state;
+ state = lexstate_eatline;
+ no_comments = true;
+ continue;
+ } else if (c == '/' &&
+ (lex->comments &
+ (ISC_LEXCOMMENT_C |
+ ISC_LEXCOMMENT_CPLUSPLUS)) != 0)
+ {
+ saved_state = state;
+ state = lexstate_maybecomment;
+ no_comments = true;
+ continue;
+ } else if (c == '#' && ((lex->comments &
+ ISC_LEXCOMMENT_SHELL) != 0))
+ {
+ saved_state = state;
+ state = lexstate_eatline;
+ no_comments = true;
+ continue;
+ }
+ }
+
+ no_read:
+ /* INSIST(c == EOF || (c >= 0 && c <= 255)); */
+ switch (state) {
+ case lexstate_start:
+ if (c == EOF) {
+ lex->last_was_eol = false;
+ if ((options & ISC_LEXOPT_DNSMULTILINE) != 0 &&
+ lex->paren_count != 0)
+ {
+ lex->paren_count = 0;
+ result = ISC_R_UNBALANCED;
+ goto done;
+ }
+ if ((options & ISC_LEXOPT_BTEXT) != 0 &&
+ lex->brace_count != 0)
+ {
+ lex->brace_count = 0;
+ result = ISC_R_UNBALANCED;
+ goto done;
+ }
+ if ((options & ISC_LEXOPT_EOF) == 0) {
+ result = ISC_R_EOF;
+ goto done;
+ }
+ tokenp->type = isc_tokentype_eof;
+ done = true;
+ } else if (c == ' ' || c == '\t') {
+ if (lex->last_was_eol &&
+ (options & ISC_LEXOPT_INITIALWS) != 0)
+ {
+ lex->last_was_eol = false;
+ tokenp->type = isc_tokentype_initialws;
+ tokenp->value.as_char = c;
+ done = true;
+ }
+ } else if (c == '\n') {
+ if ((options & ISC_LEXOPT_EOL) != 0) {
+ tokenp->type = isc_tokentype_eol;
+ done = true;
+ }
+ lex->last_was_eol = true;
+ } else if (c == '\r') {
+ if ((options & ISC_LEXOPT_EOL) != 0) {
+ state = lexstate_crlf;
+ }
+ } else if (c == '"' &&
+ (options & ISC_LEXOPT_QSTRING) != 0)
+ {
+ lex->last_was_eol = false;
+ no_comments = true;
+ state = lexstate_qstring;
+ } else if (lex->specials[c]) {
+ lex->last_was_eol = false;
+ if ((c == '(' || c == ')') &&
+ (options & ISC_LEXOPT_DNSMULTILINE) != 0)
+ {
+ if (c == '(') {
+ if (lex->paren_count == 0) {
+ options &= ~IWSEOL;
+ }
+ lex->paren_count++;
+ } else {
+ if (lex->paren_count == 0) {
+ result =
+ ISC_R_UNBALANCED;
+ goto done;
+ }
+ lex->paren_count--;
+ if (lex->paren_count == 0) {
+ options = saved_options;
+ }
+ }
+ continue;
+ } else if (c == '{' &&
+ (options & ISC_LEXOPT_BTEXT) != 0)
+ {
+ if (lex->brace_count != 0) {
+ result = ISC_R_UNBALANCED;
+ goto done;
+ }
+ lex->brace_count++;
+ options &= ~IWSEOL;
+ state = lexstate_btext;
+ no_comments = true;
+ continue;
+ }
+ tokenp->type = isc_tokentype_special;
+ tokenp->value.as_char = c;
+ done = true;
+ } else if (isdigit((unsigned char)c) &&
+ (options & ISC_LEXOPT_NUMBER) != 0)
+ {
+ lex->last_was_eol = false;
+ if ((options & ISC_LEXOPT_OCTAL) != 0 &&
+ (c == '8' || c == '9'))
+ {
+ state = lexstate_string;
+ } else {
+ state = lexstate_number;
+ }
+ goto no_read;
+ } else {
+ lex->last_was_eol = false;
+ state = lexstate_string;
+ goto no_read;
+ }
+ break;
+ case lexstate_crlf:
+ if (c != '\n') {
+ pushback(source, c);
+ }
+ tokenp->type = isc_tokentype_eol;
+ done = true;
+ lex->last_was_eol = true;
+ break;
+ case lexstate_number:
+ if (c == EOF || !isdigit((unsigned char)c)) {
+ if (c == ' ' || c == '\t' || c == '\r' ||
+ c == '\n' || c == EOF || lex->specials[c])
+ {
+ int base;
+ if ((options & ISC_LEXOPT_OCTAL) != 0) {
+ base = 8;
+ } else if ((options &
+ ISC_LEXOPT_CNUMBER) != 0)
+ {
+ base = 0;
+ } else {
+ base = 10;
+ }
+ pushback(source, c);
+
+ result = isc_parse_uint32(
+ &as_ulong, lex->data, base);
+ if (result == ISC_R_SUCCESS) {
+ tokenp->type =
+ isc_tokentype_number;
+ tokenp->value.as_ulong =
+ as_ulong;
+ } else if (result == ISC_R_BADNUMBER) {
+ isc_tokenvalue_t *v;
+
+ tokenp->type =
+ isc_tokentype_string;
+ v = &(tokenp->value);
+ v->as_textregion.base =
+ lex->data;
+ v->as_textregion.length =
+ (unsigned int)(lex->max_token -
+ remaining);
+ } else {
+ goto done;
+ }
+ done = true;
+ continue;
+ } else if ((options & ISC_LEXOPT_CNUMBER) ==
+ 0 ||
+ ((c != 'x' && c != 'X') ||
+ (curr != &lex->data[1]) ||
+ (lex->data[0] != '0')))
+ {
+ /* Above test supports hex numbers */
+ state = lexstate_string;
+ }
+ } else if ((options & ISC_LEXOPT_OCTAL) != 0 &&
+ (c == '8' || c == '9'))
+ {
+ state = lexstate_string;
+ }
+ if (remaining == 0U) {
+ result = grow_data(lex, &remaining, &curr,
+ &prev);
+ if (result != ISC_R_SUCCESS) {
+ goto done;
+ }
+ }
+ INSIST(remaining > 0U);
+ *curr++ = c;
+ *curr = '\0';
+ remaining--;
+ break;
+ case lexstate_string:
+ if (!escaped && c == '=' &&
+ (options & ISC_LEXOPT_VPAIR) != 0)
+ {
+ if (remaining == 0U) {
+ result = grow_data(lex, &remaining,
+ &curr, &prev);
+ if (result != ISC_R_SUCCESS) {
+ goto done;
+ }
+ }
+ INSIST(remaining > 0U);
+ *curr++ = c;
+ *curr = '\0';
+ remaining--;
+ state = lexstate_vpairstart;
+ break;
+ }
+ FALLTHROUGH;
+ case lexstate_vpairstart:
+ if (state == lexstate_vpairstart) {
+ if (c == '"' &&
+ (options & ISC_LEXOPT_QVPAIR) != 0)
+ {
+ no_comments = true;
+ state = lexstate_qvpair;
+ break;
+ }
+ state = lexstate_vpair;
+ }
+ FALLTHROUGH;
+ case lexstate_vpair:
+ /*
+ * EOF needs to be checked before lex->specials[c]
+ * as lex->specials[EOF] is not a good idea.
+ */
+ if (c == '\r' || c == '\n' || c == EOF ||
+ (!escaped &&
+ (c == ' ' || c == '\t' || lex->specials[c])))
+ {
+ pushback(source, c);
+ if (source->result != ISC_R_SUCCESS) {
+ result = source->result;
+ goto done;
+ }
+ if (escaped && c == EOF) {
+ result = ISC_R_UNEXPECTEDEND;
+ goto done;
+ }
+ tokenp->type = (state == lexstate_string)
+ ? isc_tokentype_string
+ : isc_tokentype_vpair;
+ tokenp->value.as_textregion.base = lex->data;
+ tokenp->value.as_textregion.length =
+ (unsigned int)(lex->max_token -
+ remaining);
+ done = true;
+ continue;
+ }
+ if ((options & ISC_LEXOPT_ESCAPE) != 0) {
+ escaped = (!escaped && c == '\\') ? true
+ : false;
+ }
+ if (remaining == 0U) {
+ result = grow_data(lex, &remaining, &curr,
+ &prev);
+ if (result != ISC_R_SUCCESS) {
+ goto done;
+ }
+ }
+ INSIST(remaining > 0U);
+ *curr++ = c;
+ *curr = '\0';
+ remaining--;
+ break;
+ case lexstate_maybecomment:
+ if (c == '*' && (lex->comments & ISC_LEXCOMMENT_C) != 0)
+ {
+ state = lexstate_ccomment;
+ continue;
+ } else if (c == '/' && (lex->comments &
+ ISC_LEXCOMMENT_CPLUSPLUS) != 0)
+ {
+ state = lexstate_eatline;
+ continue;
+ }
+ pushback(source, c);
+ c = '/';
+ no_comments = false;
+ state = saved_state;
+ goto no_read;
+ case lexstate_ccomment:
+ if (c == EOF) {
+ result = ISC_R_UNEXPECTEDEND;
+ goto done;
+ }
+ if (c == '*') {
+ state = lexstate_ccommentend;
+ }
+ break;
+ case lexstate_ccommentend:
+ if (c == EOF) {
+ result = ISC_R_UNEXPECTEDEND;
+ goto done;
+ }
+ if (c == '/') {
+ /*
+ * C-style comments become a single space.
+ * We do this to ensure that a comment will
+ * act as a delimiter for strings and
+ * numbers.
+ */
+ c = ' ';
+ no_comments = false;
+ state = saved_state;
+ goto no_read;
+ } else if (c != '*') {
+ state = lexstate_ccomment;
+ }
+ break;
+ case lexstate_eatline:
+ if ((c == '\n') || (c == EOF)) {
+ no_comments = false;
+ state = saved_state;
+ goto no_read;
+ }
+ break;
+ case lexstate_qstring:
+ case lexstate_qvpair:
+ if (c == EOF) {
+ result = ISC_R_UNEXPECTEDEND;
+ goto done;
+ }
+ if (c == '"') {
+ if (escaped) {
+ escaped = false;
+ /*
+ * Overwrite the preceding backslash.
+ */
+ INSIST(prev != NULL);
+ *prev = '"';
+ } else {
+ tokenp->type =
+ (state == lexstate_qstring)
+ ? isc_tokentype_qstring
+ : isc_tokentype_qvpair;
+ tokenp->value.as_textregion.base =
+ lex->data;
+ tokenp->value.as_textregion.length =
+ (unsigned int)(lex->max_token -
+ remaining);
+ no_comments = false;
+ done = true;
+ }
+ } else {
+ if (c == '\n' && !escaped &&
+ (options & ISC_LEXOPT_QSTRINGMULTILINE) ==
+ 0)
+ {
+ pushback(source, c);
+ result = ISC_R_UNBALANCEDQUOTES;
+ goto done;
+ }
+ if (c == '\\' && !escaped) {
+ escaped = true;
+ } else {
+ escaped = false;
+ }
+ if (remaining == 0U) {
+ result = grow_data(lex, &remaining,
+ &curr, &prev);
+ if (result != ISC_R_SUCCESS) {
+ goto done;
+ }
+ }
+ INSIST(remaining > 0U);
+ prev = curr;
+ *curr++ = c;
+ *curr = '\0';
+ remaining--;
+ }
+ break;
+ case lexstate_btext:
+ if (c == EOF) {
+ result = ISC_R_UNEXPECTEDEND;
+ goto done;
+ }
+ if (c == '{') {
+ if (escaped) {
+ escaped = false;
+ } else {
+ lex->brace_count++;
+ }
+ } else if (c == '}') {
+ if (escaped) {
+ escaped = false;
+ } else {
+ INSIST(lex->brace_count > 0);
+ lex->brace_count--;
+ }
+
+ if (lex->brace_count == 0) {
+ tokenp->type = isc_tokentype_btext;
+ tokenp->value.as_textregion.base =
+ lex->data;
+ tokenp->value.as_textregion.length =
+ (unsigned int)(lex->max_token -
+ remaining);
+ no_comments = false;
+ done = true;
+ break;
+ }
+ }
+
+ if (c == '\\' && !escaped) {
+ escaped = true;
+ } else {
+ escaped = false;
+ }
+
+ if (remaining == 0U) {
+ result = grow_data(lex, &remaining, &curr,
+ &prev);
+ if (result != ISC_R_SUCCESS) {
+ goto done;
+ }
+ }
+ INSIST(remaining > 0U);
+ prev = curr;
+ *curr++ = c;
+ *curr = '\0';
+ remaining--;
+ break;
+ default:
+ FATAL_ERROR(__FILE__, __LINE__, "Unexpected state %d",
+ state);
+ }
+ } while (!done);
+
+ result = ISC_R_SUCCESS;
+done:
+#ifdef HAVE_FLOCKFILE
+ if (source->is_file) {
+ funlockfile(source->input);
+ }
+#endif /* ifdef HAVE_FLOCKFILE */
+ return (result);
+}
+
+isc_result_t
+isc_lex_getmastertoken(isc_lex_t *lex, isc_token_t *token,
+ isc_tokentype_t expect, bool eol) {
+ unsigned int options = ISC_LEXOPT_EOL | ISC_LEXOPT_EOF |
+ ISC_LEXOPT_DNSMULTILINE | ISC_LEXOPT_ESCAPE;
+ isc_result_t result;
+
+ if (expect == isc_tokentype_vpair) {
+ options |= ISC_LEXOPT_VPAIR;
+ } else if (expect == isc_tokentype_qvpair) {
+ options |= ISC_LEXOPT_VPAIR;
+ options |= ISC_LEXOPT_QVPAIR;
+ } else if (expect == isc_tokentype_qstring) {
+ options |= ISC_LEXOPT_QSTRING;
+ } else if (expect == isc_tokentype_number) {
+ options |= ISC_LEXOPT_NUMBER;
+ }
+ result = isc_lex_gettoken(lex, options, token);
+ if (result == ISC_R_RANGE) {
+ isc_lex_ungettoken(lex, token);
+ }
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ if (eol && ((token->type == isc_tokentype_eol) ||
+ (token->type == isc_tokentype_eof)))
+ {
+ return (ISC_R_SUCCESS);
+ }
+ if (token->type == isc_tokentype_string &&
+ (expect == isc_tokentype_qstring || expect == isc_tokentype_qvpair))
+ {
+ return (ISC_R_SUCCESS);
+ }
+ if (token->type == isc_tokentype_vpair &&
+ expect == isc_tokentype_qvpair)
+ {
+ return (ISC_R_SUCCESS);
+ }
+ if (token->type != expect) {
+ isc_lex_ungettoken(lex, token);
+ if (token->type == isc_tokentype_eol ||
+ token->type == isc_tokentype_eof)
+ {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+ if (expect == isc_tokentype_number) {
+ return (ISC_R_BADNUMBER);
+ }
+ return (ISC_R_UNEXPECTEDTOKEN);
+ }
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_lex_getoctaltoken(isc_lex_t *lex, isc_token_t *token, bool eol) {
+ unsigned int options = ISC_LEXOPT_EOL | ISC_LEXOPT_EOF |
+ ISC_LEXOPT_DNSMULTILINE | ISC_LEXOPT_ESCAPE |
+ ISC_LEXOPT_NUMBER | ISC_LEXOPT_OCTAL;
+ isc_result_t result;
+
+ result = isc_lex_gettoken(lex, options, token);
+ if (result == ISC_R_RANGE) {
+ isc_lex_ungettoken(lex, token);
+ }
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ if (eol && ((token->type == isc_tokentype_eol) ||
+ (token->type == isc_tokentype_eof)))
+ {
+ return (ISC_R_SUCCESS);
+ }
+ if (token->type != isc_tokentype_number) {
+ isc_lex_ungettoken(lex, token);
+ if (token->type == isc_tokentype_eol ||
+ token->type == isc_tokentype_eof)
+ {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+ return (ISC_R_BADNUMBER);
+ }
+ return (ISC_R_SUCCESS);
+}
+
+void
+isc_lex_ungettoken(isc_lex_t *lex, isc_token_t *tokenp) {
+ inputsource *source;
+ /*
+ * Unget the current token.
+ */
+
+ REQUIRE(VALID_LEX(lex));
+ source = HEAD(lex->sources);
+ REQUIRE(source != NULL);
+ REQUIRE(tokenp != NULL);
+ REQUIRE(isc_buffer_consumedlength(source->pushback) != 0 ||
+ tokenp->type == isc_tokentype_eof);
+
+ UNUSED(tokenp);
+
+ isc_buffer_first(source->pushback);
+ lex->paren_count = lex->saved_paren_count;
+ source->line = source->saved_line;
+ source->at_eof = false;
+}
+
+void
+isc_lex_getlasttokentext(isc_lex_t *lex, isc_token_t *tokenp, isc_region_t *r) {
+ inputsource *source;
+
+ REQUIRE(VALID_LEX(lex));
+ source = HEAD(lex->sources);
+ REQUIRE(source != NULL);
+ REQUIRE(tokenp != NULL);
+ REQUIRE(isc_buffer_consumedlength(source->pushback) != 0 ||
+ tokenp->type == isc_tokentype_eof);
+
+ UNUSED(tokenp);
+
+ INSIST(source->ignored <= isc_buffer_consumedlength(source->pushback));
+ r->base = (unsigned char *)isc_buffer_base(source->pushback) +
+ source->ignored;
+ r->length = isc_buffer_consumedlength(source->pushback) -
+ source->ignored;
+}
+
+char *
+isc_lex_getsourcename(isc_lex_t *lex) {
+ inputsource *source;
+
+ REQUIRE(VALID_LEX(lex));
+ source = HEAD(lex->sources);
+
+ if (source == NULL) {
+ return (NULL);
+ }
+
+ return (source->name);
+}
+
+unsigned long
+isc_lex_getsourceline(isc_lex_t *lex) {
+ inputsource *source;
+
+ REQUIRE(VALID_LEX(lex));
+ source = HEAD(lex->sources);
+
+ if (source == NULL) {
+ return (0);
+ }
+
+ return (source->line);
+}
+
+isc_result_t
+isc_lex_setsourcename(isc_lex_t *lex, const char *name) {
+ inputsource *source;
+ char *newname;
+
+ REQUIRE(VALID_LEX(lex));
+ source = HEAD(lex->sources);
+
+ if (source == NULL) {
+ return (ISC_R_NOTFOUND);
+ }
+ newname = isc_mem_strdup(lex->mctx, name);
+ isc_mem_free(lex->mctx, source->name);
+ source->name = newname;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_lex_setsourceline(isc_lex_t *lex, unsigned long line) {
+ inputsource *source;
+
+ REQUIRE(VALID_LEX(lex));
+ source = HEAD(lex->sources);
+
+ if (source == NULL) {
+ return (ISC_R_NOTFOUND);
+ }
+
+ source->line = line;
+ return (ISC_R_SUCCESS);
+}
+
+bool
+isc_lex_isfile(isc_lex_t *lex) {
+ inputsource *source;
+
+ REQUIRE(VALID_LEX(lex));
+
+ source = HEAD(lex->sources);
+
+ if (source == NULL) {
+ return (false);
+ }
+
+ return (source->is_file);
+}