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