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