Home | History | Annotate | Line # | Download | only in isc
lex.c revision 1.1.2.2
      1 /*	$NetBSD: lex.c,v 1.1.2.2 2024/02/24 13:07:20 martin Exp $	*/
      2 
      3 /*
      4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5  *
      6  * SPDX-License-Identifier: MPL-2.0
      7  *
      8  * This Source Code Form is subject to the terms of the Mozilla Public
      9  * License, v. 2.0. If a copy of the MPL was not distributed with this
     10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
     11  *
     12  * See the COPYRIGHT file distributed with this work for additional
     13  * information regarding copyright ownership.
     14  */
     15 
     16 /*! \file */
     17 
     18 #include <ctype.h>
     19 #include <errno.h>
     20 #include <inttypes.h>
     21 #include <stdbool.h>
     22 #include <stdlib.h>
     23 
     24 #include <isc/buffer.h>
     25 #include <isc/file.h>
     26 #include <isc/lex.h>
     27 #include <isc/mem.h>
     28 #include <isc/parseint.h>
     29 #include <isc/print.h>
     30 #include <isc/stdio.h>
     31 #include <isc/string.h>
     32 #include <isc/util.h>
     33 
     34 typedef struct inputsource {
     35 	isc_result_t result;
     36 	bool is_file;
     37 	bool need_close;
     38 	bool at_eof;
     39 	bool last_was_eol;
     40 	isc_buffer_t *pushback;
     41 	unsigned int ignored;
     42 	void *input;
     43 	char *name;
     44 	unsigned long line;
     45 	unsigned long saved_line;
     46 	ISC_LINK(struct inputsource) link;
     47 } inputsource;
     48 
     49 #define LEX_MAGIC    ISC_MAGIC('L', 'e', 'x', '!')
     50 #define VALID_LEX(l) ISC_MAGIC_VALID(l, LEX_MAGIC)
     51 
     52 struct isc_lex {
     53 	/* Unlocked. */
     54 	unsigned int magic;
     55 	isc_mem_t *mctx;
     56 	size_t max_token;
     57 	char *data;
     58 	unsigned int comments;
     59 	bool comment_ok;
     60 	bool last_was_eol;
     61 	unsigned int brace_count;
     62 	unsigned int paren_count;
     63 	unsigned int saved_paren_count;
     64 	isc_lexspecials_t specials;
     65 	LIST(struct inputsource) sources;
     66 };
     67 
     68 static isc_result_t
     69 grow_data(isc_lex_t *lex, size_t *remainingp, char **currp, char **prevp) {
     70 	char *tmp;
     71 
     72 	tmp = isc_mem_get(lex->mctx, lex->max_token * 2 + 1);
     73 	memmove(tmp, lex->data, lex->max_token + 1);
     74 	*currp = tmp + (*currp - lex->data);
     75 	if (*prevp != NULL) {
     76 		*prevp = tmp + (*prevp - lex->data);
     77 	}
     78 	isc_mem_put(lex->mctx, lex->data, lex->max_token + 1);
     79 	lex->data = tmp;
     80 	*remainingp += lex->max_token;
     81 	lex->max_token *= 2;
     82 	return (ISC_R_SUCCESS);
     83 }
     84 
     85 isc_result_t
     86 isc_lex_create(isc_mem_t *mctx, size_t max_token, isc_lex_t **lexp) {
     87 	isc_lex_t *lex;
     88 
     89 	/*
     90 	 * Create a lexer.
     91 	 */
     92 	REQUIRE(lexp != NULL && *lexp == NULL);
     93 
     94 	if (max_token == 0U) {
     95 		max_token = 1;
     96 	}
     97 
     98 	lex = isc_mem_get(mctx, sizeof(*lex));
     99 	lex->data = isc_mem_get(mctx, max_token + 1);
    100 	lex->mctx = mctx;
    101 	lex->max_token = max_token;
    102 	lex->comments = 0;
    103 	lex->comment_ok = true;
    104 	lex->last_was_eol = true;
    105 	lex->brace_count = 0;
    106 	lex->paren_count = 0;
    107 	lex->saved_paren_count = 0;
    108 	memset(lex->specials, 0, 256);
    109 	INIT_LIST(lex->sources);
    110 	lex->magic = LEX_MAGIC;
    111 
    112 	*lexp = lex;
    113 
    114 	return (ISC_R_SUCCESS);
    115 }
    116 
    117 void
    118 isc_lex_destroy(isc_lex_t **lexp) {
    119 	isc_lex_t *lex;
    120 
    121 	/*
    122 	 * Destroy the lexer.
    123 	 */
    124 
    125 	REQUIRE(lexp != NULL);
    126 	lex = *lexp;
    127 	*lexp = NULL;
    128 	REQUIRE(VALID_LEX(lex));
    129 
    130 	while (!EMPTY(lex->sources)) {
    131 		RUNTIME_CHECK(isc_lex_close(lex) == ISC_R_SUCCESS);
    132 	}
    133 	if (lex->data != NULL) {
    134 		isc_mem_put(lex->mctx, lex->data, lex->max_token + 1);
    135 	}
    136 	lex->magic = 0;
    137 	isc_mem_put(lex->mctx, lex, sizeof(*lex));
    138 }
    139 
    140 unsigned int
    141 isc_lex_getcomments(isc_lex_t *lex) {
    142 	/*
    143 	 * Return the current lexer commenting styles.
    144 	 */
    145 
    146 	REQUIRE(VALID_LEX(lex));
    147 
    148 	return (lex->comments);
    149 }
    150 
    151 void
    152 isc_lex_setcomments(isc_lex_t *lex, unsigned int comments) {
    153 	/*
    154 	 * Set allowed lexer commenting styles.
    155 	 */
    156 
    157 	REQUIRE(VALID_LEX(lex));
    158 
    159 	lex->comments = comments;
    160 }
    161 
    162 void
    163 isc_lex_getspecials(isc_lex_t *lex, isc_lexspecials_t specials) {
    164 	/*
    165 	 * Put the current list of specials into 'specials'.
    166 	 */
    167 
    168 	REQUIRE(VALID_LEX(lex));
    169 
    170 	memmove(specials, lex->specials, 256);
    171 }
    172 
    173 void
    174 isc_lex_setspecials(isc_lex_t *lex, isc_lexspecials_t specials) {
    175 	/*
    176 	 * The characters in 'specials' are returned as tokens.  Along with
    177 	 * whitespace, they delimit strings and numbers.
    178 	 */
    179 
    180 	REQUIRE(VALID_LEX(lex));
    181 
    182 	memmove(lex->specials, specials, 256);
    183 }
    184 
    185 static isc_result_t
    186 new_source(isc_lex_t *lex, bool is_file, bool need_close, void *input,
    187 	   const char *name) {
    188 	inputsource *source;
    189 
    190 	source = isc_mem_get(lex->mctx, sizeof(*source));
    191 	source->result = ISC_R_SUCCESS;
    192 	source->is_file = is_file;
    193 	source->need_close = need_close;
    194 	source->at_eof = false;
    195 	source->last_was_eol = lex->last_was_eol;
    196 	source->input = input;
    197 	source->name = isc_mem_strdup(lex->mctx, name);
    198 	source->pushback = NULL;
    199 	isc_buffer_allocate(lex->mctx, &source->pushback,
    200 			    (unsigned int)lex->max_token);
    201 	source->ignored = 0;
    202 	source->line = 1;
    203 	ISC_LIST_INITANDPREPEND(lex->sources, source, link);
    204 
    205 	return (ISC_R_SUCCESS);
    206 }
    207 
    208 isc_result_t
    209 isc_lex_openfile(isc_lex_t *lex, const char *filename) {
    210 	isc_result_t result;
    211 	FILE *stream = NULL;
    212 
    213 	/*
    214 	 * Open 'filename' and make it the current input source for 'lex'.
    215 	 */
    216 
    217 	REQUIRE(VALID_LEX(lex));
    218 
    219 	result = isc_stdio_open(filename, "r", &stream);
    220 	if (result != ISC_R_SUCCESS) {
    221 		return (result);
    222 	}
    223 
    224 	result = new_source(lex, true, true, stream, filename);
    225 	if (result != ISC_R_SUCCESS) {
    226 		(void)fclose(stream);
    227 	}
    228 	return (result);
    229 }
    230 
    231 isc_result_t
    232 isc_lex_openstream(isc_lex_t *lex, FILE *stream) {
    233 	char name[128];
    234 
    235 	/*
    236 	 * Make 'stream' the current input source for 'lex'.
    237 	 */
    238 
    239 	REQUIRE(VALID_LEX(lex));
    240 
    241 	snprintf(name, sizeof(name), "stream-%p", stream);
    242 
    243 	return (new_source(lex, true, false, stream, name));
    244 }
    245 
    246 isc_result_t
    247 isc_lex_openbuffer(isc_lex_t *lex, isc_buffer_t *buffer) {
    248 	char name[128];
    249 
    250 	/*
    251 	 * Make 'buffer' the current input source for 'lex'.
    252 	 */
    253 
    254 	REQUIRE(VALID_LEX(lex));
    255 
    256 	snprintf(name, sizeof(name), "buffer-%p", buffer);
    257 
    258 	return (new_source(lex, false, false, buffer, name));
    259 }
    260 
    261 isc_result_t
    262 isc_lex_close(isc_lex_t *lex) {
    263 	inputsource *source;
    264 
    265 	/*
    266 	 * Close the most recently opened object (i.e. file or buffer).
    267 	 */
    268 
    269 	REQUIRE(VALID_LEX(lex));
    270 
    271 	source = HEAD(lex->sources);
    272 	if (source == NULL) {
    273 		return (ISC_R_NOMORE);
    274 	}
    275 
    276 	ISC_LIST_UNLINK(lex->sources, source, link);
    277 	lex->last_was_eol = source->last_was_eol;
    278 	if (source->is_file) {
    279 		if (source->need_close) {
    280 			(void)fclose((FILE *)(source->input));
    281 		}
    282 	}
    283 	isc_mem_free(lex->mctx, source->name);
    284 	isc_buffer_free(&source->pushback);
    285 	isc_mem_put(lex->mctx, source, sizeof(*source));
    286 
    287 	return (ISC_R_SUCCESS);
    288 }
    289 
    290 typedef enum {
    291 	lexstate_start,
    292 	lexstate_crlf,
    293 	lexstate_string,
    294 	lexstate_number,
    295 	lexstate_maybecomment,
    296 	lexstate_ccomment,
    297 	lexstate_ccommentend,
    298 	lexstate_eatline,
    299 	lexstate_qstring,
    300 	lexstate_btext,
    301 	lexstate_vpair,
    302 	lexstate_vpairstart,
    303 	lexstate_qvpair,
    304 } lexstate;
    305 
    306 #define IWSEOL (ISC_LEXOPT_INITIALWS | ISC_LEXOPT_EOL)
    307 
    308 static void
    309 pushback(inputsource *source, int c) {
    310 	REQUIRE(source->pushback->current > 0);
    311 	if (c == EOF) {
    312 		source->at_eof = false;
    313 		return;
    314 	}
    315 	source->pushback->current--;
    316 	if (c == '\n') {
    317 		source->line--;
    318 	}
    319 }
    320 
    321 static isc_result_t
    322 pushandgrow(isc_lex_t *lex, inputsource *source, int c) {
    323 	if (isc_buffer_availablelength(source->pushback) == 0) {
    324 		isc_buffer_t *tbuf = NULL;
    325 		unsigned int oldlen;
    326 		isc_region_t used;
    327 		isc_result_t result;
    328 
    329 		oldlen = isc_buffer_length(source->pushback);
    330 		isc_buffer_allocate(lex->mctx, &tbuf, oldlen * 2);
    331 		isc_buffer_usedregion(source->pushback, &used);
    332 		result = isc_buffer_copyregion(tbuf, &used);
    333 		INSIST(result == ISC_R_SUCCESS);
    334 		tbuf->current = source->pushback->current;
    335 		isc_buffer_free(&source->pushback);
    336 		source->pushback = tbuf;
    337 	}
    338 	isc_buffer_putuint8(source->pushback, (uint8_t)c);
    339 	return (ISC_R_SUCCESS);
    340 }
    341 
    342 isc_result_t
    343 isc_lex_gettoken(isc_lex_t *lex, unsigned int options, isc_token_t *tokenp) {
    344 	inputsource *source;
    345 	int c;
    346 	bool done = false;
    347 	bool no_comments = false;
    348 	bool escaped = false;
    349 	lexstate state = lexstate_start;
    350 	lexstate saved_state = lexstate_start;
    351 	isc_buffer_t *buffer;
    352 	FILE *stream;
    353 	char *curr, *prev;
    354 	size_t remaining;
    355 	uint32_t as_ulong;
    356 	unsigned int saved_options;
    357 	isc_result_t result;
    358 
    359 	/*
    360 	 * Get the next token.
    361 	 */
    362 
    363 	REQUIRE(VALID_LEX(lex));
    364 	source = HEAD(lex->sources);
    365 	REQUIRE(tokenp != NULL);
    366 
    367 	if (source == NULL) {
    368 		if ((options & ISC_LEXOPT_NOMORE) != 0) {
    369 			tokenp->type = isc_tokentype_nomore;
    370 			return (ISC_R_SUCCESS);
    371 		}
    372 		return (ISC_R_NOMORE);
    373 	}
    374 
    375 	if (source->result != ISC_R_SUCCESS) {
    376 		return (source->result);
    377 	}
    378 
    379 	lex->saved_paren_count = lex->paren_count;
    380 	source->saved_line = source->line;
    381 
    382 	if (isc_buffer_remaininglength(source->pushback) == 0 && source->at_eof)
    383 	{
    384 		if ((options & ISC_LEXOPT_DNSMULTILINE) != 0 &&
    385 		    lex->paren_count != 0)
    386 		{
    387 			lex->paren_count = 0;
    388 			return (ISC_R_UNBALANCED);
    389 		}
    390 		if ((options & ISC_LEXOPT_BTEXT) != 0 && lex->brace_count != 0)
    391 		{
    392 			lex->brace_count = 0;
    393 			return (ISC_R_UNBALANCED);
    394 		}
    395 		if ((options & ISC_LEXOPT_EOF) != 0) {
    396 			tokenp->type = isc_tokentype_eof;
    397 			return (ISC_R_SUCCESS);
    398 		}
    399 		return (ISC_R_EOF);
    400 	}
    401 
    402 	isc_buffer_compact(source->pushback);
    403 
    404 	saved_options = options;
    405 	if ((options & ISC_LEXOPT_DNSMULTILINE) != 0 && lex->paren_count > 0) {
    406 		options &= ~IWSEOL;
    407 	}
    408 
    409 	curr = lex->data;
    410 	*curr = '\0';
    411 
    412 	prev = NULL;
    413 	remaining = lex->max_token;
    414 
    415 #ifdef HAVE_FLOCKFILE
    416 	if (source->is_file) {
    417 		flockfile(source->input);
    418 	}
    419 #endif /* ifdef HAVE_FLOCKFILE */
    420 
    421 	do {
    422 		if (isc_buffer_remaininglength(source->pushback) == 0) {
    423 			if (source->is_file) {
    424 				stream = source->input;
    425 
    426 #if defined(HAVE_FLOCKFILE) && defined(HAVE_GETC_UNLOCKED)
    427 				c = getc_unlocked(stream);
    428 #else  /* if defined(HAVE_FLOCKFILE) && defined(HAVE_GETC_UNLOCKED) */
    429 				c = getc(stream);
    430 #endif /* if defined(HAVE_FLOCKFILE) && defined(HAVE_GETC_UNLOCKED) */
    431 				if (c == EOF) {
    432 					if (ferror(stream)) {
    433 						source->result = ISC_R_IOERROR;
    434 						result = source->result;
    435 						goto done;
    436 					}
    437 					source->at_eof = true;
    438 				}
    439 			} else {
    440 				buffer = source->input;
    441 
    442 				if (buffer->current == buffer->used) {
    443 					c = EOF;
    444 					source->at_eof = true;
    445 				} else {
    446 					c = *((unsigned char *)buffer->base +
    447 					      buffer->current);
    448 					buffer->current++;
    449 				}
    450 			}
    451 			if (c != EOF) {
    452 				source->result = pushandgrow(lex, source, c);
    453 				if (source->result != ISC_R_SUCCESS) {
    454 					result = source->result;
    455 					goto done;
    456 				}
    457 			}
    458 		}
    459 
    460 		if (!source->at_eof) {
    461 			if (state == lexstate_start) {
    462 				/* Token has not started yet. */
    463 				source->ignored = isc_buffer_consumedlength(
    464 					source->pushback);
    465 			}
    466 			c = isc_buffer_getuint8(source->pushback);
    467 		} else {
    468 			c = EOF;
    469 		}
    470 
    471 		if (c == '\n') {
    472 			source->line++;
    473 		}
    474 
    475 		if (lex->comment_ok && !no_comments) {
    476 			if (!escaped && c == ';' &&
    477 			    ((lex->comments & ISC_LEXCOMMENT_DNSMASTERFILE) !=
    478 			     0))
    479 			{
    480 				saved_state = state;
    481 				state = lexstate_eatline;
    482 				no_comments = true;
    483 				continue;
    484 			} else if (c == '/' &&
    485 				   (lex->comments &
    486 				    (ISC_LEXCOMMENT_C |
    487 				     ISC_LEXCOMMENT_CPLUSPLUS)) != 0)
    488 			{
    489 				saved_state = state;
    490 				state = lexstate_maybecomment;
    491 				no_comments = true;
    492 				continue;
    493 			} else if (c == '#' && ((lex->comments &
    494 						 ISC_LEXCOMMENT_SHELL) != 0))
    495 			{
    496 				saved_state = state;
    497 				state = lexstate_eatline;
    498 				no_comments = true;
    499 				continue;
    500 			}
    501 		}
    502 
    503 	no_read:
    504 		/* INSIST(c == EOF || (c >= 0 && c <= 255)); */
    505 		switch (state) {
    506 		case lexstate_start:
    507 			if (c == EOF) {
    508 				lex->last_was_eol = false;
    509 				if ((options & ISC_LEXOPT_DNSMULTILINE) != 0 &&
    510 				    lex->paren_count != 0)
    511 				{
    512 					lex->paren_count = 0;
    513 					result = ISC_R_UNBALANCED;
    514 					goto done;
    515 				}
    516 				if ((options & ISC_LEXOPT_BTEXT) != 0 &&
    517 				    lex->brace_count != 0)
    518 				{
    519 					lex->brace_count = 0;
    520 					result = ISC_R_UNBALANCED;
    521 					goto done;
    522 				}
    523 				if ((options & ISC_LEXOPT_EOF) == 0) {
    524 					result = ISC_R_EOF;
    525 					goto done;
    526 				}
    527 				tokenp->type = isc_tokentype_eof;
    528 				done = true;
    529 			} else if (c == ' ' || c == '\t') {
    530 				if (lex->last_was_eol &&
    531 				    (options & ISC_LEXOPT_INITIALWS) != 0)
    532 				{
    533 					lex->last_was_eol = false;
    534 					tokenp->type = isc_tokentype_initialws;
    535 					tokenp->value.as_char = c;
    536 					done = true;
    537 				}
    538 			} else if (c == '\n') {
    539 				if ((options & ISC_LEXOPT_EOL) != 0) {
    540 					tokenp->type = isc_tokentype_eol;
    541 					done = true;
    542 				}
    543 				lex->last_was_eol = true;
    544 			} else if (c == '\r') {
    545 				if ((options & ISC_LEXOPT_EOL) != 0) {
    546 					state = lexstate_crlf;
    547 				}
    548 			} else if (c == '"' &&
    549 				   (options & ISC_LEXOPT_QSTRING) != 0)
    550 			{
    551 				lex->last_was_eol = false;
    552 				no_comments = true;
    553 				state = lexstate_qstring;
    554 			} else if (lex->specials[c]) {
    555 				lex->last_was_eol = false;
    556 				if ((c == '(' || c == ')') &&
    557 				    (options & ISC_LEXOPT_DNSMULTILINE) != 0)
    558 				{
    559 					if (c == '(') {
    560 						if (lex->paren_count == 0) {
    561 							options &= ~IWSEOL;
    562 						}
    563 						lex->paren_count++;
    564 					} else {
    565 						if (lex->paren_count == 0) {
    566 							result =
    567 								ISC_R_UNBALANCED;
    568 							goto done;
    569 						}
    570 						lex->paren_count--;
    571 						if (lex->paren_count == 0) {
    572 							options = saved_options;
    573 						}
    574 					}
    575 					continue;
    576 				} else if (c == '{' &&
    577 					   (options & ISC_LEXOPT_BTEXT) != 0)
    578 				{
    579 					if (lex->brace_count != 0) {
    580 						result = ISC_R_UNBALANCED;
    581 						goto done;
    582 					}
    583 					lex->brace_count++;
    584 					options &= ~IWSEOL;
    585 					state = lexstate_btext;
    586 					no_comments = true;
    587 					continue;
    588 				}
    589 				tokenp->type = isc_tokentype_special;
    590 				tokenp->value.as_char = c;
    591 				done = true;
    592 			} else if (isdigit((unsigned char)c) &&
    593 				   (options & ISC_LEXOPT_NUMBER) != 0)
    594 			{
    595 				lex->last_was_eol = false;
    596 				if ((options & ISC_LEXOPT_OCTAL) != 0 &&
    597 				    (c == '8' || c == '9'))
    598 				{
    599 					state = lexstate_string;
    600 				} else {
    601 					state = lexstate_number;
    602 				}
    603 				goto no_read;
    604 			} else {
    605 				lex->last_was_eol = false;
    606 				state = lexstate_string;
    607 				goto no_read;
    608 			}
    609 			break;
    610 		case lexstate_crlf:
    611 			if (c != '\n') {
    612 				pushback(source, c);
    613 			}
    614 			tokenp->type = isc_tokentype_eol;
    615 			done = true;
    616 			lex->last_was_eol = true;
    617 			break;
    618 		case lexstate_number:
    619 			if (c == EOF || !isdigit((unsigned char)c)) {
    620 				if (c == ' ' || c == '\t' || c == '\r' ||
    621 				    c == '\n' || c == EOF || lex->specials[c])
    622 				{
    623 					int base;
    624 					if ((options & ISC_LEXOPT_OCTAL) != 0) {
    625 						base = 8;
    626 					} else if ((options &
    627 						    ISC_LEXOPT_CNUMBER) != 0)
    628 					{
    629 						base = 0;
    630 					} else {
    631 						base = 10;
    632 					}
    633 					pushback(source, c);
    634 
    635 					result = isc_parse_uint32(
    636 						&as_ulong, lex->data, base);
    637 					if (result == ISC_R_SUCCESS) {
    638 						tokenp->type =
    639 							isc_tokentype_number;
    640 						tokenp->value.as_ulong =
    641 							as_ulong;
    642 					} else if (result == ISC_R_BADNUMBER) {
    643 						isc_tokenvalue_t *v;
    644 
    645 						tokenp->type =
    646 							isc_tokentype_string;
    647 						v = &(tokenp->value);
    648 						v->as_textregion.base =
    649 							lex->data;
    650 						v->as_textregion.length =
    651 							(unsigned int)(lex->max_token -
    652 								       remaining);
    653 					} else {
    654 						goto done;
    655 					}
    656 					done = true;
    657 					continue;
    658 				} else if ((options & ISC_LEXOPT_CNUMBER) ==
    659 						   0 ||
    660 					   ((c != 'x' && c != 'X') ||
    661 					    (curr != &lex->data[1]) ||
    662 					    (lex->data[0] != '0')))
    663 				{
    664 					/* Above test supports hex numbers */
    665 					state = lexstate_string;
    666 				}
    667 			} else if ((options & ISC_LEXOPT_OCTAL) != 0 &&
    668 				   (c == '8' || c == '9'))
    669 			{
    670 				state = lexstate_string;
    671 			}
    672 			if (remaining == 0U) {
    673 				result = grow_data(lex, &remaining, &curr,
    674 						   &prev);
    675 				if (result != ISC_R_SUCCESS) {
    676 					goto done;
    677 				}
    678 			}
    679 			INSIST(remaining > 0U);
    680 			*curr++ = c;
    681 			*curr = '\0';
    682 			remaining--;
    683 			break;
    684 		case lexstate_string:
    685 			if (!escaped && c == '=' &&
    686 			    (options & ISC_LEXOPT_VPAIR) != 0)
    687 			{
    688 				if (remaining == 0U) {
    689 					result = grow_data(lex, &remaining,
    690 							   &curr, &prev);
    691 					if (result != ISC_R_SUCCESS) {
    692 						goto done;
    693 					}
    694 				}
    695 				INSIST(remaining > 0U);
    696 				*curr++ = c;
    697 				*curr = '\0';
    698 				remaining--;
    699 				state = lexstate_vpairstart;
    700 				break;
    701 			}
    702 			FALLTHROUGH;
    703 		case lexstate_vpairstart:
    704 			if (state == lexstate_vpairstart) {
    705 				if (c == '"' &&
    706 				    (options & ISC_LEXOPT_QVPAIR) != 0)
    707 				{
    708 					no_comments = true;
    709 					state = lexstate_qvpair;
    710 					break;
    711 				}
    712 				state = lexstate_vpair;
    713 			}
    714 			FALLTHROUGH;
    715 		case lexstate_vpair:
    716 			/*
    717 			 * EOF needs to be checked before lex->specials[c]
    718 			 * as lex->specials[EOF] is not a good idea.
    719 			 */
    720 			if (c == '\r' || c == '\n' || c == EOF ||
    721 			    (!escaped &&
    722 			     (c == ' ' || c == '\t' || lex->specials[c])))
    723 			{
    724 				pushback(source, c);
    725 				if (source->result != ISC_R_SUCCESS) {
    726 					result = source->result;
    727 					goto done;
    728 				}
    729 				if (escaped && c == EOF) {
    730 					result = ISC_R_UNEXPECTEDEND;
    731 					goto done;
    732 				}
    733 				tokenp->type = (state == lexstate_string)
    734 						       ? isc_tokentype_string
    735 						       : isc_tokentype_vpair;
    736 				tokenp->value.as_textregion.base = lex->data;
    737 				tokenp->value.as_textregion.length =
    738 					(unsigned int)(lex->max_token -
    739 						       remaining);
    740 				done = true;
    741 				continue;
    742 			}
    743 			if ((options & ISC_LEXOPT_ESCAPE) != 0) {
    744 				escaped = (!escaped && c == '\\') ? true
    745 								  : false;
    746 			}
    747 			if (remaining == 0U) {
    748 				result = grow_data(lex, &remaining, &curr,
    749 						   &prev);
    750 				if (result != ISC_R_SUCCESS) {
    751 					goto done;
    752 				}
    753 			}
    754 			INSIST(remaining > 0U);
    755 			*curr++ = c;
    756 			*curr = '\0';
    757 			remaining--;
    758 			break;
    759 		case lexstate_maybecomment:
    760 			if (c == '*' && (lex->comments & ISC_LEXCOMMENT_C) != 0)
    761 			{
    762 				state = lexstate_ccomment;
    763 				continue;
    764 			} else if (c == '/' && (lex->comments &
    765 						ISC_LEXCOMMENT_CPLUSPLUS) != 0)
    766 			{
    767 				state = lexstate_eatline;
    768 				continue;
    769 			}
    770 			pushback(source, c);
    771 			c = '/';
    772 			no_comments = false;
    773 			state = saved_state;
    774 			goto no_read;
    775 		case lexstate_ccomment:
    776 			if (c == EOF) {
    777 				result = ISC_R_UNEXPECTEDEND;
    778 				goto done;
    779 			}
    780 			if (c == '*') {
    781 				state = lexstate_ccommentend;
    782 			}
    783 			break;
    784 		case lexstate_ccommentend:
    785 			if (c == EOF) {
    786 				result = ISC_R_UNEXPECTEDEND;
    787 				goto done;
    788 			}
    789 			if (c == '/') {
    790 				/*
    791 				 * C-style comments become a single space.
    792 				 * We do this to ensure that a comment will
    793 				 * act as a delimiter for strings and
    794 				 * numbers.
    795 				 */
    796 				c = ' ';
    797 				no_comments = false;
    798 				state = saved_state;
    799 				goto no_read;
    800 			} else if (c != '*') {
    801 				state = lexstate_ccomment;
    802 			}
    803 			break;
    804 		case lexstate_eatline:
    805 			if ((c == '\n') || (c == EOF)) {
    806 				no_comments = false;
    807 				state = saved_state;
    808 				goto no_read;
    809 			}
    810 			break;
    811 		case lexstate_qstring:
    812 		case lexstate_qvpair:
    813 			if (c == EOF) {
    814 				result = ISC_R_UNEXPECTEDEND;
    815 				goto done;
    816 			}
    817 			if (c == '"') {
    818 				if (escaped) {
    819 					escaped = false;
    820 					/*
    821 					 * Overwrite the preceding backslash.
    822 					 */
    823 					INSIST(prev != NULL);
    824 					*prev = '"';
    825 				} else {
    826 					tokenp->type =
    827 						(state == lexstate_qstring)
    828 							? isc_tokentype_qstring
    829 							: isc_tokentype_qvpair;
    830 					tokenp->value.as_textregion.base =
    831 						lex->data;
    832 					tokenp->value.as_textregion.length =
    833 						(unsigned int)(lex->max_token -
    834 							       remaining);
    835 					no_comments = false;
    836 					done = true;
    837 				}
    838 			} else {
    839 				if (c == '\n' && !escaped &&
    840 				    (options & ISC_LEXOPT_QSTRINGMULTILINE) ==
    841 					    0)
    842 				{
    843 					pushback(source, c);
    844 					result = ISC_R_UNBALANCEDQUOTES;
    845 					goto done;
    846 				}
    847 				if (c == '\\' && !escaped) {
    848 					escaped = true;
    849 				} else {
    850 					escaped = false;
    851 				}
    852 				if (remaining == 0U) {
    853 					result = grow_data(lex, &remaining,
    854 							   &curr, &prev);
    855 					if (result != ISC_R_SUCCESS) {
    856 						goto done;
    857 					}
    858 				}
    859 				INSIST(remaining > 0U);
    860 				prev = curr;
    861 				*curr++ = c;
    862 				*curr = '\0';
    863 				remaining--;
    864 			}
    865 			break;
    866 		case lexstate_btext:
    867 			if (c == EOF) {
    868 				result = ISC_R_UNEXPECTEDEND;
    869 				goto done;
    870 			}
    871 			if (c == '{') {
    872 				if (escaped) {
    873 					escaped = false;
    874 				} else {
    875 					lex->brace_count++;
    876 				}
    877 			} else if (c == '}') {
    878 				if (escaped) {
    879 					escaped = false;
    880 				} else {
    881 					INSIST(lex->brace_count > 0);
    882 					lex->brace_count--;
    883 				}
    884 
    885 				if (lex->brace_count == 0) {
    886 					tokenp->type = isc_tokentype_btext;
    887 					tokenp->value.as_textregion.base =
    888 						lex->data;
    889 					tokenp->value.as_textregion.length =
    890 						(unsigned int)(lex->max_token -
    891 							       remaining);
    892 					no_comments = false;
    893 					done = true;
    894 					break;
    895 				}
    896 			}
    897 
    898 			if (c == '\\' && !escaped) {
    899 				escaped = true;
    900 			} else {
    901 				escaped = false;
    902 			}
    903 
    904 			if (remaining == 0U) {
    905 				result = grow_data(lex, &remaining, &curr,
    906 						   &prev);
    907 				if (result != ISC_R_SUCCESS) {
    908 					goto done;
    909 				}
    910 			}
    911 			INSIST(remaining > 0U);
    912 			prev = curr;
    913 			*curr++ = c;
    914 			*curr = '\0';
    915 			remaining--;
    916 			break;
    917 		default:
    918 			FATAL_ERROR(__FILE__, __LINE__, "Unexpected state %d",
    919 				    state);
    920 		}
    921 	} while (!done);
    922 
    923 	result = ISC_R_SUCCESS;
    924 done:
    925 #ifdef HAVE_FLOCKFILE
    926 	if (source->is_file) {
    927 		funlockfile(source->input);
    928 	}
    929 #endif /* ifdef HAVE_FLOCKFILE */
    930 	return (result);
    931 }
    932 
    933 isc_result_t
    934 isc_lex_getmastertoken(isc_lex_t *lex, isc_token_t *token,
    935 		       isc_tokentype_t expect, bool eol) {
    936 	unsigned int options = ISC_LEXOPT_EOL | ISC_LEXOPT_EOF |
    937 			       ISC_LEXOPT_DNSMULTILINE | ISC_LEXOPT_ESCAPE;
    938 	isc_result_t result;
    939 
    940 	if (expect == isc_tokentype_vpair) {
    941 		options |= ISC_LEXOPT_VPAIR;
    942 	} else if (expect == isc_tokentype_qvpair) {
    943 		options |= ISC_LEXOPT_VPAIR;
    944 		options |= ISC_LEXOPT_QVPAIR;
    945 	} else if (expect == isc_tokentype_qstring) {
    946 		options |= ISC_LEXOPT_QSTRING;
    947 	} else if (expect == isc_tokentype_number) {
    948 		options |= ISC_LEXOPT_NUMBER;
    949 	}
    950 	result = isc_lex_gettoken(lex, options, token);
    951 	if (result == ISC_R_RANGE) {
    952 		isc_lex_ungettoken(lex, token);
    953 	}
    954 	if (result != ISC_R_SUCCESS) {
    955 		return (result);
    956 	}
    957 
    958 	if (eol && ((token->type == isc_tokentype_eol) ||
    959 		    (token->type == isc_tokentype_eof)))
    960 	{
    961 		return (ISC_R_SUCCESS);
    962 	}
    963 	if (token->type == isc_tokentype_string &&
    964 	    (expect == isc_tokentype_qstring || expect == isc_tokentype_qvpair))
    965 	{
    966 		return (ISC_R_SUCCESS);
    967 	}
    968 	if (token->type == isc_tokentype_vpair &&
    969 	    expect == isc_tokentype_qvpair)
    970 	{
    971 		return (ISC_R_SUCCESS);
    972 	}
    973 	if (token->type != expect) {
    974 		isc_lex_ungettoken(lex, token);
    975 		if (token->type == isc_tokentype_eol ||
    976 		    token->type == isc_tokentype_eof)
    977 		{
    978 			return (ISC_R_UNEXPECTEDEND);
    979 		}
    980 		if (expect == isc_tokentype_number) {
    981 			return (ISC_R_BADNUMBER);
    982 		}
    983 		return (ISC_R_UNEXPECTEDTOKEN);
    984 	}
    985 	return (ISC_R_SUCCESS);
    986 }
    987 
    988 isc_result_t
    989 isc_lex_getoctaltoken(isc_lex_t *lex, isc_token_t *token, bool eol) {
    990 	unsigned int options = ISC_LEXOPT_EOL | ISC_LEXOPT_EOF |
    991 			       ISC_LEXOPT_DNSMULTILINE | ISC_LEXOPT_ESCAPE |
    992 			       ISC_LEXOPT_NUMBER | ISC_LEXOPT_OCTAL;
    993 	isc_result_t result;
    994 
    995 	result = isc_lex_gettoken(lex, options, token);
    996 	if (result == ISC_R_RANGE) {
    997 		isc_lex_ungettoken(lex, token);
    998 	}
    999 	if (result != ISC_R_SUCCESS) {
   1000 		return (result);
   1001 	}
   1002 
   1003 	if (eol && ((token->type == isc_tokentype_eol) ||
   1004 		    (token->type == isc_tokentype_eof)))
   1005 	{
   1006 		return (ISC_R_SUCCESS);
   1007 	}
   1008 	if (token->type != isc_tokentype_number) {
   1009 		isc_lex_ungettoken(lex, token);
   1010 		if (token->type == isc_tokentype_eol ||
   1011 		    token->type == isc_tokentype_eof)
   1012 		{
   1013 			return (ISC_R_UNEXPECTEDEND);
   1014 		}
   1015 		return (ISC_R_BADNUMBER);
   1016 	}
   1017 	return (ISC_R_SUCCESS);
   1018 }
   1019 
   1020 void
   1021 isc_lex_ungettoken(isc_lex_t *lex, isc_token_t *tokenp) {
   1022 	inputsource *source;
   1023 	/*
   1024 	 * Unget the current token.
   1025 	 */
   1026 
   1027 	REQUIRE(VALID_LEX(lex));
   1028 	source = HEAD(lex->sources);
   1029 	REQUIRE(source != NULL);
   1030 	REQUIRE(tokenp != NULL);
   1031 	REQUIRE(isc_buffer_consumedlength(source->pushback) != 0 ||
   1032 		tokenp->type == isc_tokentype_eof);
   1033 
   1034 	UNUSED(tokenp);
   1035 
   1036 	isc_buffer_first(source->pushback);
   1037 	lex->paren_count = lex->saved_paren_count;
   1038 	source->line = source->saved_line;
   1039 	source->at_eof = false;
   1040 }
   1041 
   1042 void
   1043 isc_lex_getlasttokentext(isc_lex_t *lex, isc_token_t *tokenp, isc_region_t *r) {
   1044 	inputsource *source;
   1045 
   1046 	REQUIRE(VALID_LEX(lex));
   1047 	source = HEAD(lex->sources);
   1048 	REQUIRE(source != NULL);
   1049 	REQUIRE(tokenp != NULL);
   1050 	REQUIRE(isc_buffer_consumedlength(source->pushback) != 0 ||
   1051 		tokenp->type == isc_tokentype_eof);
   1052 
   1053 	UNUSED(tokenp);
   1054 
   1055 	INSIST(source->ignored <= isc_buffer_consumedlength(source->pushback));
   1056 	r->base = (unsigned char *)isc_buffer_base(source->pushback) +
   1057 		  source->ignored;
   1058 	r->length = isc_buffer_consumedlength(source->pushback) -
   1059 		    source->ignored;
   1060 }
   1061 
   1062 char *
   1063 isc_lex_getsourcename(isc_lex_t *lex) {
   1064 	inputsource *source;
   1065 
   1066 	REQUIRE(VALID_LEX(lex));
   1067 	source = HEAD(lex->sources);
   1068 
   1069 	if (source == NULL) {
   1070 		return (NULL);
   1071 	}
   1072 
   1073 	return (source->name);
   1074 }
   1075 
   1076 unsigned long
   1077 isc_lex_getsourceline(isc_lex_t *lex) {
   1078 	inputsource *source;
   1079 
   1080 	REQUIRE(VALID_LEX(lex));
   1081 	source = HEAD(lex->sources);
   1082 
   1083 	if (source == NULL) {
   1084 		return (0);
   1085 	}
   1086 
   1087 	return (source->line);
   1088 }
   1089 
   1090 isc_result_t
   1091 isc_lex_setsourcename(isc_lex_t *lex, const char *name) {
   1092 	inputsource *source;
   1093 	char *newname;
   1094 
   1095 	REQUIRE(VALID_LEX(lex));
   1096 	source = HEAD(lex->sources);
   1097 
   1098 	if (source == NULL) {
   1099 		return (ISC_R_NOTFOUND);
   1100 	}
   1101 	newname = isc_mem_strdup(lex->mctx, name);
   1102 	isc_mem_free(lex->mctx, source->name);
   1103 	source->name = newname;
   1104 	return (ISC_R_SUCCESS);
   1105 }
   1106 
   1107 isc_result_t
   1108 isc_lex_setsourceline(isc_lex_t *lex, unsigned long line) {
   1109 	inputsource *source;
   1110 
   1111 	REQUIRE(VALID_LEX(lex));
   1112 	source = HEAD(lex->sources);
   1113 
   1114 	if (source == NULL) {
   1115 		return (ISC_R_NOTFOUND);
   1116 	}
   1117 
   1118 	source->line = line;
   1119 	return (ISC_R_SUCCESS);
   1120 }
   1121 
   1122 bool
   1123 isc_lex_isfile(isc_lex_t *lex) {
   1124 	inputsource *source;
   1125 
   1126 	REQUIRE(VALID_LEX(lex));
   1127 
   1128 	source = HEAD(lex->sources);
   1129 
   1130 	if (source == NULL) {
   1131 		return (false);
   1132 	}
   1133 
   1134 	return (source->is_file);
   1135 }
   1136