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