xref: /netbsd-src/external/mpl/bind/dist/lib/isc/lex.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
1 /*	$NetBSD: lex.c,v 1.12 2025/01/26 16:25:37 christos 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/stdio.h>
30 #include <isc/string.h>
31 #include <isc/util.h>
32 
33 #include "errno2result.h"
34 
35 typedef struct inputsource {
36 	isc_result_t result;
37 	bool is_file;
38 	bool need_close;
39 	bool at_eof;
40 	bool last_was_eol;
41 	isc_buffer_t *pushback;
42 	unsigned int ignored;
43 	void *input;
44 	char *name;
45 	unsigned long line;
46 	unsigned long saved_line;
47 	ISC_LINK(struct inputsource) link;
48 } inputsource;
49 
50 #define LEX_MAGIC    ISC_MAGIC('L', 'e', 'x', '!')
51 #define VALID_LEX(l) ISC_MAGIC_VALID(l, LEX_MAGIC)
52 
53 struct isc_lex {
54 	/* Unlocked. */
55 	unsigned int magic;
56 	isc_mem_t *mctx;
57 	size_t max_token;
58 	char *data;
59 	unsigned int comments;
60 	bool comment_ok;
61 	bool last_was_eol;
62 	unsigned int brace_count;
63 	unsigned int paren_count;
64 	unsigned int saved_paren_count;
65 	isc_lexspecials_t specials;
66 	LIST(struct inputsource) sources;
67 };
68 
69 static isc_result_t
70 grow_data(isc_lex_t *lex, size_t *remainingp, char **currp, char **prevp) {
71 	char *tmp;
72 
73 	tmp = isc_mem_get(lex->mctx, lex->max_token * 2 + 1);
74 	memmove(tmp, lex->data, lex->max_token + 1);
75 	*currp = tmp + (*currp - lex->data);
76 	if (*prevp != NULL) {
77 		*prevp = tmp + (*prevp - lex->data);
78 	}
79 	isc_mem_put(lex->mctx, lex->data, lex->max_token + 1);
80 	lex->data = tmp;
81 	*remainingp += lex->max_token;
82 	lex->max_token *= 2;
83 	return ISC_R_SUCCESS;
84 }
85 
86 void
87 isc_lex_create(isc_mem_t *mctx, size_t max_token, isc_lex_t **lexp) {
88 	isc_lex_t *lex;
89 
90 	/*
91 	 * Create a lexer.
92 	 */
93 	REQUIRE(lexp != NULL && *lexp == NULL);
94 
95 	if (max_token == 0U) {
96 		max_token = 1;
97 	}
98 
99 	lex = isc_mem_get(mctx, sizeof(*lex));
100 	lex->data = isc_mem_get(mctx, max_token + 1);
101 	lex->mctx = mctx;
102 	lex->max_token = max_token;
103 	lex->comments = 0;
104 	lex->comment_ok = true;
105 	lex->last_was_eol = true;
106 	lex->brace_count = 0;
107 	lex->paren_count = 0;
108 	lex->saved_paren_count = 0;
109 	memset(lex->specials, 0, 256);
110 	INIT_LIST(lex->sources);
111 	lex->magic = LEX_MAGIC;
112 
113 	*lexp = lex;
114 }
115 
116 void
117 isc_lex_destroy(isc_lex_t **lexp) {
118 	isc_lex_t *lex;
119 
120 	/*
121 	 * Destroy the lexer.
122 	 */
123 
124 	REQUIRE(lexp != NULL);
125 	lex = *lexp;
126 	*lexp = NULL;
127 	REQUIRE(VALID_LEX(lex));
128 
129 	while (!EMPTY(lex->sources)) {
130 		RUNTIME_CHECK(isc_lex_close(lex) == ISC_R_SUCCESS);
131 	}
132 	if (lex->data != NULL) {
133 		isc_mem_put(lex->mctx, lex->data, lex->max_token + 1);
134 	}
135 	lex->magic = 0;
136 	isc_mem_put(lex->mctx, lex, sizeof(*lex));
137 }
138 
139 unsigned int
140 isc_lex_getcomments(isc_lex_t *lex) {
141 	/*
142 	 * Return the current lexer commenting styles.
143 	 */
144 
145 	REQUIRE(VALID_LEX(lex));
146 
147 	return lex->comments;
148 }
149 
150 void
151 isc_lex_setcomments(isc_lex_t *lex, unsigned int comments) {
152 	/*
153 	 * Set allowed lexer commenting styles.
154 	 */
155 
156 	REQUIRE(VALID_LEX(lex));
157 
158 	lex->comments = comments;
159 }
160 
161 void
162 isc_lex_getspecials(isc_lex_t *lex, isc_lexspecials_t specials) {
163 	/*
164 	 * Put the current list of specials into 'specials'.
165 	 */
166 
167 	REQUIRE(VALID_LEX(lex));
168 
169 	memmove(specials, lex->specials, 256);
170 }
171 
172 void
173 isc_lex_setspecials(isc_lex_t *lex, isc_lexspecials_t specials) {
174 	/*
175 	 * The characters in 'specials' are returned as tokens.  Along with
176 	 * whitespace, they delimit strings and numbers.
177 	 */
178 
179 	REQUIRE(VALID_LEX(lex));
180 
181 	memmove(lex->specials, specials, 256);
182 }
183 
184 static isc_result_t
185 new_source(isc_lex_t *lex, bool is_file, bool need_close, void *input,
186 	   const char *name) {
187 	inputsource *source;
188 
189 	source = isc_mem_get(lex->mctx, sizeof(*source));
190 	source->result = ISC_R_SUCCESS;
191 	source->is_file = is_file;
192 	source->need_close = need_close;
193 	source->at_eof = false;
194 	source->last_was_eol = lex->last_was_eol;
195 	source->input = input;
196 	source->name = isc_mem_strdup(lex->mctx, name);
197 	source->pushback = NULL;
198 	isc_buffer_allocate(lex->mctx, &source->pushback,
199 			    (unsigned int)lex->max_token);
200 	source->ignored = 0;
201 	source->line = 1;
202 	ISC_LIST_INITANDPREPEND(lex->sources, source, link);
203 
204 	return ISC_R_SUCCESS;
205 }
206 
207 isc_result_t
208 isc_lex_openfile(isc_lex_t *lex, const char *filename) {
209 	isc_result_t result;
210 	FILE *stream = NULL;
211 
212 	/*
213 	 * Open 'filename' and make it the current input source for 'lex'.
214 	 */
215 
216 	REQUIRE(VALID_LEX(lex));
217 
218 	result = isc_stdio_open(filename, "r", &stream);
219 	if (result != ISC_R_SUCCESS) {
220 		return result;
221 	}
222 
223 	result = new_source(lex, true, true, stream, filename);
224 	if (result != ISC_R_SUCCESS) {
225 		(void)fclose(stream);
226 	}
227 	return result;
228 }
229 
230 isc_result_t
231 isc_lex_openstream(isc_lex_t *lex, FILE *stream) {
232 	char name[128];
233 
234 	/*
235 	 * Make 'stream' the current input source for 'lex'.
236 	 */
237 
238 	REQUIRE(VALID_LEX(lex));
239 
240 	snprintf(name, sizeof(name), "stream-%p", stream);
241 
242 	return new_source(lex, true, false, stream, name);
243 }
244 
245 isc_result_t
246 isc_lex_openbuffer(isc_lex_t *lex, isc_buffer_t *buffer) {
247 	char name[128];
248 
249 	/*
250 	 * Make 'buffer' the current input source for 'lex'.
251 	 */
252 
253 	REQUIRE(VALID_LEX(lex));
254 
255 	snprintf(name, sizeof(name), "buffer-%p", buffer);
256 
257 	return new_source(lex, false, false, buffer, name);
258 }
259 
260 isc_result_t
261 isc_lex_close(isc_lex_t *lex) {
262 	inputsource *source;
263 
264 	/*
265 	 * Close the most recently opened object (i.e. file or buffer).
266 	 */
267 
268 	REQUIRE(VALID_LEX(lex));
269 
270 	source = HEAD(lex->sources);
271 	if (source == NULL) {
272 		return ISC_R_NOMORE;
273 	}
274 
275 	ISC_LIST_UNLINK(lex->sources, source, link);
276 	lex->last_was_eol = source->last_was_eol;
277 	if (source->is_file) {
278 		if (source->need_close) {
279 			(void)fclose((FILE *)(source->input));
280 		}
281 	}
282 	isc_mem_free(lex->mctx, source->name);
283 	isc_buffer_free(&source->pushback);
284 	isc_mem_put(lex->mctx, source, sizeof(*source));
285 
286 	return ISC_R_SUCCESS;
287 }
288 
289 typedef enum {
290 	lexstate_start,
291 	lexstate_crlf,
292 	lexstate_string,
293 	lexstate_number,
294 	lexstate_maybecomment,
295 	lexstate_ccomment,
296 	lexstate_ccommentend,
297 	lexstate_eatline,
298 	lexstate_qstring,
299 	lexstate_btext,
300 	lexstate_vpair,
301 	lexstate_vpairstart,
302 	lexstate_qvpair,
303 } lexstate;
304 
305 #define IWSEOL (ISC_LEXOPT_INITIALWS | ISC_LEXOPT_EOL)
306 
307 static void
308 pushback(inputsource *source, int c) {
309 	REQUIRE(source->pushback->current > 0);
310 	if (c == EOF) {
311 		source->at_eof = false;
312 		return;
313 	}
314 	source->pushback->current--;
315 	if (c == '\n') {
316 		source->line--;
317 	}
318 }
319 
320 static isc_result_t
321 pushandgrow(isc_lex_t *lex, inputsource *source, int c) {
322 	if (isc_buffer_availablelength(source->pushback) == 0) {
323 		isc_buffer_t *tbuf = NULL;
324 		unsigned int oldlen;
325 		isc_region_t used;
326 		isc_result_t result;
327 
328 		oldlen = isc_buffer_length(source->pushback);
329 		isc_buffer_allocate(lex->mctx, &tbuf, oldlen * 2);
330 		isc_buffer_usedregion(source->pushback, &used);
331 		result = isc_buffer_copyregion(tbuf, &used);
332 		INSIST(result == ISC_R_SUCCESS);
333 		tbuf->current = source->pushback->current;
334 		isc_buffer_free(&source->pushback);
335 		source->pushback = tbuf;
336 	}
337 	isc_buffer_putuint8(source->pushback, (uint8_t)c);
338 	return ISC_R_SUCCESS;
339 }
340 
341 isc_result_t
342 isc_lex_gettoken(isc_lex_t *lex, unsigned int options, isc_token_t *tokenp) {
343 	inputsource *source;
344 	int c;
345 	bool done = false;
346 	bool no_comments = false;
347 	bool escaped = false;
348 	lexstate state = lexstate_start;
349 	lexstate saved_state = lexstate_start;
350 	isc_buffer_t *buffer;
351 	FILE *stream;
352 	char *curr, *prev;
353 	size_t remaining;
354 	uint32_t as_ulong;
355 	unsigned int saved_options;
356 	isc_result_t result;
357 
358 	/*
359 	 * Get the next token.
360 	 */
361 
362 	REQUIRE(VALID_LEX(lex));
363 	source = HEAD(lex->sources);
364 	REQUIRE(tokenp != NULL);
365 
366 	if (source == NULL) {
367 		if ((options & ISC_LEXOPT_NOMORE) != 0) {
368 			tokenp->type = isc_tokentype_nomore;
369 			return ISC_R_SUCCESS;
370 		}
371 		return ISC_R_NOMORE;
372 	}
373 
374 	if (source->result != ISC_R_SUCCESS) {
375 		return source->result;
376 	}
377 
378 	lex->saved_paren_count = lex->paren_count;
379 	source->saved_line = source->line;
380 
381 	if (isc_buffer_remaininglength(source->pushback) == 0 && source->at_eof)
382 	{
383 		if ((options & ISC_LEXOPT_DNSMULTILINE) != 0 &&
384 		    lex->paren_count != 0)
385 		{
386 			lex->paren_count = 0;
387 			return ISC_R_UNBALANCED;
388 		}
389 		if ((options & ISC_LEXOPT_BTEXT) != 0 && lex->brace_count != 0)
390 		{
391 			lex->brace_count = 0;
392 			return ISC_R_UNBALANCED;
393 		}
394 		if ((options & ISC_LEXOPT_EOF) != 0) {
395 			tokenp->type = isc_tokentype_eof;
396 			return ISC_R_SUCCESS;
397 		}
398 		return ISC_R_EOF;
399 	}
400 
401 	isc_buffer_compact(source->pushback);
402 
403 	saved_options = options;
404 	if ((options & ISC_LEXOPT_DNSMULTILINE) != 0 && lex->paren_count > 0) {
405 		options &= ~IWSEOL;
406 	}
407 
408 	curr = lex->data;
409 	*curr = '\0';
410 
411 	prev = NULL;
412 	remaining = lex->max_token;
413 
414 #ifdef HAVE_FLOCKFILE
415 	if (source->is_file) {
416 		flockfile(source->input);
417 	}
418 #endif /* ifdef HAVE_FLOCKFILE */
419 
420 	do {
421 		if (isc_buffer_remaininglength(source->pushback) == 0) {
422 			if (source->is_file) {
423 				stream = source->input;
424 
425 #if defined(HAVE_FLOCKFILE) && defined(HAVE_GETC_UNLOCKED)
426 				c = getc_unlocked(stream);
427 #else  /* if defined(HAVE_FLOCKFILE) && defined(HAVE_GETC_UNLOCKED) */
428 				c = getc(stream);
429 #endif /* if defined(HAVE_FLOCKFILE) && defined(HAVE_GETC_UNLOCKED) */
430 				if (c == EOF) {
431 					if (ferror(stream)) {
432 						source->result =
433 							isc__errno2result(
434 								errno);
435 						result = source->result;
436 						goto done;
437 					}
438 					source->at_eof = true;
439 				}
440 			} else {
441 				buffer = source->input;
442 
443 				if (buffer->current == buffer->used) {
444 					c = EOF;
445 					source->at_eof = true;
446 				} else {
447 					c = *((unsigned char *)buffer->base +
448 					      buffer->current);
449 					buffer->current++;
450 				}
451 			}
452 			if (c != EOF) {
453 				source->result = pushandgrow(lex, source, c);
454 				if (source->result != ISC_R_SUCCESS) {
455 					result = source->result;
456 					goto done;
457 				}
458 			}
459 		}
460 
461 		if (!source->at_eof) {
462 			if (state == lexstate_start) {
463 				/* Token has not started yet. */
464 				source->ignored = isc_buffer_consumedlength(
465 					source->pushback);
466 			}
467 			c = isc_buffer_getuint8(source->pushback);
468 		} else {
469 			c = EOF;
470 		}
471 
472 		if (c == '\n') {
473 			source->line++;
474 		}
475 
476 		if (lex->comment_ok && !no_comments) {
477 			if (!escaped && c == ';' &&
478 			    ((lex->comments & ISC_LEXCOMMENT_DNSMASTERFILE) !=
479 			     0))
480 			{
481 				saved_state = state;
482 				state = lexstate_eatline;
483 				no_comments = true;
484 				continue;
485 			} else if (c == '/' &&
486 				   (lex->comments &
487 				    (ISC_LEXCOMMENT_C |
488 				     ISC_LEXCOMMENT_CPLUSPLUS)) != 0)
489 			{
490 				saved_state = state;
491 				state = lexstate_maybecomment;
492 				no_comments = true;
493 				continue;
494 			} else if (c == '#' && ((lex->comments &
495 						 ISC_LEXCOMMENT_SHELL) != 0))
496 			{
497 				saved_state = state;
498 				state = lexstate_eatline;
499 				no_comments = true;
500 				continue;
501 			}
502 		}
503 
504 	no_read:
505 		/* INSIST(c == EOF || (c >= 0 && c <= 255)); */
506 		switch (state) {
507 		case lexstate_start:
508 			if (c == EOF) {
509 				lex->last_was_eol = false;
510 				if ((options & ISC_LEXOPT_DNSMULTILINE) != 0 &&
511 				    lex->paren_count != 0)
512 				{
513 					lex->paren_count = 0;
514 					result = ISC_R_UNBALANCED;
515 					goto done;
516 				}
517 				if ((options & ISC_LEXOPT_BTEXT) != 0 &&
518 				    lex->brace_count != 0)
519 				{
520 					lex->brace_count = 0;
521 					result = ISC_R_UNBALANCED;
522 					goto done;
523 				}
524 				if ((options & ISC_LEXOPT_EOF) == 0) {
525 					result = ISC_R_EOF;
526 					goto done;
527 				}
528 				tokenp->type = isc_tokentype_eof;
529 				done = true;
530 			} else if (c == ' ' || c == '\t') {
531 				if (lex->last_was_eol &&
532 				    (options & ISC_LEXOPT_INITIALWS) != 0)
533 				{
534 					lex->last_was_eol = false;
535 					tokenp->type = isc_tokentype_initialws;
536 					tokenp->value.as_char = c;
537 					done = true;
538 				}
539 			} else if (c == '\n') {
540 				if ((options & ISC_LEXOPT_EOL) != 0) {
541 					tokenp->type = isc_tokentype_eol;
542 					done = true;
543 				}
544 				lex->last_was_eol = true;
545 			} else if (c == '\r') {
546 				if ((options & ISC_LEXOPT_EOL) != 0) {
547 					state = lexstate_crlf;
548 				}
549 			} else if (c == '"' &&
550 				   (options & ISC_LEXOPT_QSTRING) != 0)
551 			{
552 				lex->last_was_eol = false;
553 				no_comments = true;
554 				state = lexstate_qstring;
555 			} else if (lex->specials[c]) {
556 				lex->last_was_eol = false;
557 				if ((c == '(' || c == ')') &&
558 				    (options & ISC_LEXOPT_DNSMULTILINE) != 0)
559 				{
560 					if (c == '(') {
561 						if (lex->paren_count == 0) {
562 							options &= ~IWSEOL;
563 						}
564 						lex->paren_count++;
565 					} else {
566 						if (lex->paren_count == 0) {
567 							result =
568 								ISC_R_UNBALANCED;
569 							goto done;
570 						}
571 						lex->paren_count--;
572 						if (lex->paren_count == 0) {
573 							options = saved_options;
574 						}
575 					}
576 					continue;
577 				} else if (c == '{' &&
578 					   (options & ISC_LEXOPT_BTEXT) != 0)
579 				{
580 					if (lex->brace_count != 0) {
581 						result = ISC_R_UNBALANCED;
582 						goto done;
583 					}
584 					lex->brace_count++;
585 					options &= ~IWSEOL;
586 					state = lexstate_btext;
587 					no_comments = true;
588 					continue;
589 				}
590 				tokenp->type = isc_tokentype_special;
591 				tokenp->value.as_char = c;
592 				done = true;
593 			} else if (isdigit((unsigned char)c) &&
594 				   (options & ISC_LEXOPT_NUMBER) != 0)
595 			{
596 				lex->last_was_eol = false;
597 				if ((options & ISC_LEXOPT_OCTAL) != 0 &&
598 				    (c == '8' || c == '9'))
599 				{
600 					state = lexstate_string;
601 				} else {
602 					state = lexstate_number;
603 				}
604 				goto no_read;
605 			} else {
606 				lex->last_was_eol = false;
607 				state = lexstate_string;
608 				goto no_read;
609 			}
610 			break;
611 		case lexstate_crlf:
612 			if (c != '\n') {
613 				pushback(source, c);
614 			}
615 			tokenp->type = isc_tokentype_eol;
616 			done = true;
617 			lex->last_was_eol = true;
618 			break;
619 		case lexstate_number:
620 			if (c == EOF || !isdigit((unsigned char)c)) {
621 				if (c == ' ' || c == '\t' || c == '\r' ||
622 				    c == '\n' || c == EOF || lex->specials[c])
623 				{
624 					int base;
625 					if ((options & ISC_LEXOPT_OCTAL) != 0) {
626 						base = 8;
627 					} else if ((options &
628 						    ISC_LEXOPT_CNUMBER) != 0)
629 					{
630 						base = 0;
631 					} else {
632 						base = 10;
633 					}
634 					pushback(source, c);
635 
636 					result = isc_parse_uint32(
637 						&as_ulong, lex->data, base);
638 					if (result == ISC_R_SUCCESS) {
639 						tokenp->type =
640 							isc_tokentype_number;
641 						tokenp->value.as_ulong =
642 							as_ulong;
643 					} else if (result == ISC_R_BADNUMBER) {
644 						isc_tokenvalue_t *v;
645 
646 						tokenp->type =
647 							isc_tokentype_string;
648 						v = &(tokenp->value);
649 						v->as_textregion.base =
650 							lex->data;
651 						v->as_textregion.length =
652 							(unsigned int)(lex->max_token -
653 								       remaining);
654 					} else {
655 						goto done;
656 					}
657 					done = true;
658 					continue;
659 				} else if ((options & ISC_LEXOPT_CNUMBER) ==
660 						   0 ||
661 					   ((c != 'x' && c != 'X') ||
662 					    (curr != &lex->data[1]) ||
663 					    (lex->data[0] != '0')))
664 				{
665 					/* Above test supports hex numbers */
666 					state = lexstate_string;
667 				}
668 			} else if ((options & ISC_LEXOPT_OCTAL) != 0 &&
669 				   (c == '8' || c == '9'))
670 			{
671 				state = lexstate_string;
672 			}
673 			if (remaining == 0U) {
674 				result = grow_data(lex, &remaining, &curr,
675 						   &prev);
676 				if (result != ISC_R_SUCCESS) {
677 					goto done;
678 				}
679 			}
680 			INSIST(remaining > 0U);
681 			*curr++ = c;
682 			*curr = '\0';
683 			remaining--;
684 			break;
685 		case lexstate_string:
686 			if (!escaped && c == '=' &&
687 			    (options & ISC_LEXOPT_VPAIR) != 0)
688 			{
689 				if (remaining == 0U) {
690 					result = grow_data(lex, &remaining,
691 							   &curr, &prev);
692 					if (result != ISC_R_SUCCESS) {
693 						goto done;
694 					}
695 				}
696 				INSIST(remaining > 0U);
697 				*curr++ = c;
698 				*curr = '\0';
699 				remaining--;
700 				state = lexstate_vpairstart;
701 				break;
702 			}
703 			FALLTHROUGH;
704 		case lexstate_vpairstart:
705 			if (state == lexstate_vpairstart) {
706 				if (c == '"' &&
707 				    (options & ISC_LEXOPT_QVPAIR) != 0)
708 				{
709 					no_comments = true;
710 					state = lexstate_qvpair;
711 					break;
712 				}
713 				state = lexstate_vpair;
714 			}
715 			FALLTHROUGH;
716 		case lexstate_vpair:
717 			/*
718 			 * EOF needs to be checked before lex->specials[c]
719 			 * as lex->specials[EOF] is not a good idea.
720 			 */
721 			if (c == '\r' || c == '\n' || c == EOF ||
722 			    (!escaped &&
723 			     (c == ' ' || c == '\t' || lex->specials[c])))
724 			{
725 				pushback(source, c);
726 				if (source->result != ISC_R_SUCCESS) {
727 					result = source->result;
728 					goto done;
729 				}
730 				if (escaped && c == EOF) {
731 					result = ISC_R_UNEXPECTEDEND;
732 					goto done;
733 				}
734 				tokenp->type = (state == lexstate_string)
735 						       ? isc_tokentype_string
736 						       : isc_tokentype_vpair;
737 				tokenp->value.as_textregion.base = lex->data;
738 				tokenp->value.as_textregion.length =
739 					(unsigned int)(lex->max_token -
740 						       remaining);
741 				done = true;
742 				continue;
743 			}
744 			if ((options & ISC_LEXOPT_ESCAPE) != 0) {
745 				escaped = (!escaped && c == '\\') ? true
746 								  : false;
747 			}
748 			if (remaining == 0U) {
749 				result = grow_data(lex, &remaining, &curr,
750 						   &prev);
751 				if (result != ISC_R_SUCCESS) {
752 					goto done;
753 				}
754 			}
755 			INSIST(remaining > 0U);
756 			*curr++ = c;
757 			*curr = '\0';
758 			remaining--;
759 			break;
760 		case lexstate_maybecomment:
761 			if (c == '*' && (lex->comments & ISC_LEXCOMMENT_C) != 0)
762 			{
763 				state = lexstate_ccomment;
764 				continue;
765 			} else if (c == '/' && (lex->comments &
766 						ISC_LEXCOMMENT_CPLUSPLUS) != 0)
767 			{
768 				state = lexstate_eatline;
769 				continue;
770 			}
771 			pushback(source, c);
772 			c = '/';
773 			no_comments = false;
774 			state = saved_state;
775 			goto no_read;
776 		case lexstate_ccomment:
777 			if (c == EOF) {
778 				result = ISC_R_UNEXPECTEDEND;
779 				goto done;
780 			}
781 			if (c == '*') {
782 				state = lexstate_ccommentend;
783 			}
784 			break;
785 		case lexstate_ccommentend:
786 			if (c == EOF) {
787 				result = ISC_R_UNEXPECTEDEND;
788 				goto done;
789 			}
790 			if (c == '/') {
791 				/*
792 				 * C-style comments become a single space.
793 				 * We do this to ensure that a comment will
794 				 * act as a delimiter for strings and
795 				 * numbers.
796 				 */
797 				c = ' ';
798 				no_comments = false;
799 				state = saved_state;
800 				goto no_read;
801 			} else if (c != '*') {
802 				state = lexstate_ccomment;
803 			}
804 			break;
805 		case lexstate_eatline:
806 			if ((c == '\n') || (c == EOF)) {
807 				no_comments = false;
808 				state = saved_state;
809 				goto no_read;
810 			}
811 			break;
812 		case lexstate_qstring:
813 		case lexstate_qvpair:
814 			if (c == EOF) {
815 				result = ISC_R_UNEXPECTEDEND;
816 				goto done;
817 			}
818 			if (c == '"') {
819 				if (escaped) {
820 					escaped = false;
821 					/*
822 					 * Overwrite the preceding backslash.
823 					 */
824 					INSIST(prev != NULL);
825 					*prev = '"';
826 				} else {
827 					tokenp->type =
828 						(state == lexstate_qstring)
829 							? isc_tokentype_qstring
830 							: isc_tokentype_qvpair;
831 					tokenp->value.as_textregion.base =
832 						lex->data;
833 					tokenp->value.as_textregion.length =
834 						(unsigned int)(lex->max_token -
835 							       remaining);
836 					no_comments = false;
837 					done = true;
838 				}
839 			} else {
840 				if (c == '\n' && !escaped &&
841 				    (options & ISC_LEXOPT_QSTRINGMULTILINE) ==
842 					    0)
843 				{
844 					pushback(source, c);
845 					result = ISC_R_UNBALANCEDQUOTES;
846 					goto done;
847 				}
848 				if (c == '\\' && !escaped) {
849 					escaped = true;
850 				} else {
851 					escaped = false;
852 				}
853 				if (remaining == 0U) {
854 					result = grow_data(lex, &remaining,
855 							   &curr, &prev);
856 					if (result != ISC_R_SUCCESS) {
857 						goto done;
858 					}
859 				}
860 				INSIST(remaining > 0U);
861 				prev = curr;
862 				*curr++ = c;
863 				*curr = '\0';
864 				remaining--;
865 			}
866 			break;
867 		case lexstate_btext:
868 			if (c == EOF) {
869 				result = ISC_R_UNEXPECTEDEND;
870 				goto done;
871 			}
872 			if (c == '{') {
873 				if (escaped) {
874 					escaped = false;
875 				} else {
876 					lex->brace_count++;
877 				}
878 			} else if (c == '}') {
879 				if (escaped) {
880 					escaped = false;
881 				} else {
882 					INSIST(lex->brace_count > 0);
883 					lex->brace_count--;
884 				}
885 
886 				if (lex->brace_count == 0) {
887 					tokenp->type = isc_tokentype_btext;
888 					tokenp->value.as_textregion.base =
889 						lex->data;
890 					tokenp->value.as_textregion.length =
891 						(unsigned int)(lex->max_token -
892 							       remaining);
893 					no_comments = false;
894 					done = true;
895 					break;
896 				}
897 			}
898 
899 			if (c == '\\' && !escaped) {
900 				escaped = true;
901 			} else {
902 				escaped = false;
903 			}
904 
905 			if (remaining == 0U) {
906 				result = grow_data(lex, &remaining, &curr,
907 						   &prev);
908 				if (result != ISC_R_SUCCESS) {
909 					goto done;
910 				}
911 			}
912 			INSIST(remaining > 0U);
913 			prev = curr;
914 			*curr++ = c;
915 			*curr = '\0';
916 			remaining--;
917 			break;
918 		default:
919 			FATAL_ERROR("Unexpected state %d", 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