1
0
Fork 0
bind9/lib/isc/lex.c
Daniel Baumann f66ff7eae6
Adding upstream version 1:9.20.9.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-21 13:32:37 +02:00

1133 lines
24 KiB
C

/*
* 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/stdio.h>
#include <isc/string.h>
#include <isc/util.h>
#include "errno2result.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;
}
void
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;
}
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__errno2result(
errno);
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("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;
}