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