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