1/*	$NetBSD: lex.c,v 1.6 2020/05/25 20:47:20 christos Exp $	*/
2
3/*
4 * Copyright (C) 2004, 2005, 2007  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
41typedef 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
58struct 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
73static inline isc_result_t
74grow_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	memcpy(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
91isc_result_t
92isc_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
126void
127isc_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
148unsigned int
149isc_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
159void
160isc_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
170void
171isc_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	memcpy(specials, lex->specials, 256);
179}
180
181void
182isc_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	memcpy(lex->specials, specials, 256);
191}
192
193static inline isc_result_t
194new_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				     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
228isc_result_t
229isc_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
249isc_result_t
250isc_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
264isc_result_t
265isc_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
279isc_result_t
280isc_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
305typedef 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
319static void
320pushback(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
331static isc_result_t
332pushandgrow(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
354isc_result_t
355isc_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 = *((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							lex->max_token -
621							remaining;
622					} else
623						goto done;
624					done = ISC_TRUE;
625					continue;
626				} else if (!(options & ISC_LEXOPT_CNUMBER) ||
627					   ((c != 'x' && c != 'X') ||
628					   (curr != &lex->data[1]) ||
629					   (lex->data[0] != '0'))) {
630					/* Above test supports hex numbers */
631					state = lexstate_string;
632				}
633			} else if ((options & ISC_LEXOPT_OCTAL) != 0 &&
634				   (c == '8' || c == '9')) {
635				state = lexstate_string;
636			}
637			if (remaining == 0U) {
638				result = grow_data(lex, &remaining,
639						   &curr, &prev);
640				if (result != ISC_R_SUCCESS)
641					goto done;
642			}
643			INSIST(remaining > 0U);
644			*curr++ = c;
645			*curr = '\0';
646			remaining--;
647			break;
648		case lexstate_string:
649			/*
650			 * EOF needs to be checked before lex->specials[c]
651			 * as lex->specials[EOF] is not a good idea.
652			 */
653			if (c == '\r' || c == '\n' || c == EOF ||
654			    (!escaped &&
655			     (c == ' ' || c == '\t' || lex->specials[c]))) {
656				pushback(source, c);
657				if (source->result != ISC_R_SUCCESS) {
658					result = source->result;
659					goto done;
660				}
661				tokenp->type = isc_tokentype_string;
662				tokenp->value.as_textregion.base = lex->data;
663				tokenp->value.as_textregion.length =
664					lex->max_token - remaining;
665				done = ISC_TRUE;
666				continue;
667			}
668			if ((options & ISC_LEXOPT_ESCAPE) != 0)
669				escaped = (!escaped && c == '\\') ?
670						ISC_TRUE : ISC_FALSE;
671			if (remaining == 0U) {
672				result = grow_data(lex, &remaining,
673						   &curr, &prev);
674				if (result != ISC_R_SUCCESS)
675					goto done;
676			}
677			INSIST(remaining > 0U);
678			*curr++ = c;
679			*curr = '\0';
680			remaining--;
681			break;
682		case lexstate_maybecomment:
683			if (c == '*' &&
684			    (lex->comments & ISC_LEXCOMMENT_C) != 0) {
685				state = lexstate_ccomment;
686				continue;
687			} else if (c == '/' &&
688			    (lex->comments & ISC_LEXCOMMENT_CPLUSPLUS) != 0) {
689				state = lexstate_eatline;
690				continue;
691			}
692			pushback(source, c);
693			c = '/';
694			no_comments = ISC_FALSE;
695			state = saved_state;
696			goto no_read;
697		case lexstate_ccomment:
698			if (c == EOF) {
699				result = ISC_R_UNEXPECTEDEND;
700				goto done;
701			}
702			if (c == '*')
703				state = lexstate_ccommentend;
704			break;
705		case lexstate_ccommentend:
706			if (c == EOF) {
707				result = ISC_R_UNEXPECTEDEND;
708				goto done;
709			}
710			if (c == '/') {
711				/*
712				 * C-style comments become a single space.
713				 * We do this to ensure that a comment will
714				 * act as a delimiter for strings and
715				 * numbers.
716				 */
717				c = ' ';
718				no_comments = ISC_FALSE;
719				state = saved_state;
720				goto no_read;
721			} else if (c != '*')
722				state = lexstate_ccomment;
723			break;
724		case lexstate_eatline:
725			if ((c == '\n') || (c == EOF)) {
726				no_comments = ISC_FALSE;
727				state = saved_state;
728				goto no_read;
729			}
730			break;
731		case lexstate_qstring:
732			if (c == EOF) {
733				result = ISC_R_UNEXPECTEDEND;
734				goto done;
735			}
736			if (c == '"') {
737				if (escaped) {
738					escaped = ISC_FALSE;
739					/*
740					 * Overwrite the preceding backslash.
741					 */
742					INSIST(prev != NULL);
743					*prev = '"';
744				} else {
745					tokenp->type = isc_tokentype_qstring;
746					tokenp->value.as_textregion.base =
747						lex->data;
748					tokenp->value.as_textregion.length =
749						lex->max_token - remaining;
750					no_comments = ISC_FALSE;
751					done = ISC_TRUE;
752				}
753			} else {
754				if (c == '\n' && !escaped &&
755			    (options & ISC_LEXOPT_QSTRINGMULTILINE) == 0) {
756					pushback(source, c);
757					result = ISC_R_UNBALANCEDQUOTES;
758					goto done;
759				}
760				if (c == '\\' && !escaped)
761					escaped = ISC_TRUE;
762				else
763					escaped = ISC_FALSE;
764				if (remaining == 0U) {
765					result = grow_data(lex, &remaining,
766							   &curr, &prev);
767					if (result != ISC_R_SUCCESS)
768						goto done;
769				}
770				INSIST(remaining > 0U);
771				prev = curr;
772				*curr++ = c;
773				*curr = '\0';
774				remaining--;
775			}
776			break;
777		default:
778			FATAL_ERROR(__FILE__, __LINE__,
779				    isc_msgcat_get(isc_msgcat, ISC_MSGSET_LEX,
780						   ISC_MSG_UNEXPECTEDSTATE,
781						   "Unexpected state %d"),
782				    state);
783			/* Does not return. */
784		}
785
786	} while (!done);
787
788	result = ISC_R_SUCCESS;
789 done:
790#ifdef HAVE_FLOCKFILE
791	if (source->is_file)
792		funlockfile(source->input);
793#endif
794	return (result);
795}
796
797isc_result_t
798isc_lex_getmastertoken(isc_lex_t *lex, isc_token_t *token,
799		       isc_tokentype_t expect, isc_boolean_t eol)
800{
801	unsigned int options = ISC_LEXOPT_EOL | ISC_LEXOPT_EOF |
802			       ISC_LEXOPT_DNSMULTILINE | ISC_LEXOPT_ESCAPE;
803	isc_result_t result;
804
805	if (expect == isc_tokentype_qstring)
806		options |= ISC_LEXOPT_QSTRING;
807	else if (expect == isc_tokentype_number)
808		options |= ISC_LEXOPT_NUMBER;
809	result = isc_lex_gettoken(lex, options, token);
810	if (result == ISC_R_RANGE)
811		isc_lex_ungettoken(lex, token);
812	if (result != ISC_R_SUCCESS)
813		return (result);
814
815	if (eol && ((token->type == isc_tokentype_eol) ||
816		    (token->type == isc_tokentype_eof)))
817		return (ISC_R_SUCCESS);
818	if (token->type == isc_tokentype_string &&
819	    expect == isc_tokentype_qstring)
820		return (ISC_R_SUCCESS);
821	if (token->type != expect) {
822		isc_lex_ungettoken(lex, token);
823		if (token->type == isc_tokentype_eol ||
824		    token->type == isc_tokentype_eof)
825			return (ISC_R_UNEXPECTEDEND);
826		if (expect == isc_tokentype_number)
827			return (ISC_R_BADNUMBER);
828		return (ISC_R_UNEXPECTEDTOKEN);
829	}
830	return (ISC_R_SUCCESS);
831}
832
833isc_result_t
834isc_lex_getoctaltoken(isc_lex_t *lex, isc_token_t *token, isc_boolean_t eol)
835{
836	unsigned int options = ISC_LEXOPT_EOL | ISC_LEXOPT_EOF |
837			       ISC_LEXOPT_DNSMULTILINE | ISC_LEXOPT_ESCAPE|
838			       ISC_LEXOPT_NUMBER | ISC_LEXOPT_OCTAL;
839	isc_result_t result;
840
841	result = isc_lex_gettoken(lex, options, token);
842	if (result == ISC_R_RANGE)
843		isc_lex_ungettoken(lex, token);
844	if (result != ISC_R_SUCCESS)
845		return (result);
846
847	if (eol && ((token->type == isc_tokentype_eol) ||
848		    (token->type == isc_tokentype_eof)))
849		return (ISC_R_SUCCESS);
850	if (token->type != isc_tokentype_number) {
851		isc_lex_ungettoken(lex, token);
852		if (token->type == isc_tokentype_eol ||
853		    token->type == isc_tokentype_eof)
854			return (ISC_R_UNEXPECTEDEND);
855		return (ISC_R_BADNUMBER);
856	}
857	return (ISC_R_SUCCESS);
858}
859
860void
861isc_lex_ungettoken(isc_lex_t *lex, isc_token_t *tokenp) {
862	inputsource *source;
863	/*
864	 * Unget the current token.
865	 */
866
867	REQUIRE(VALID_LEX(lex));
868	source = HEAD(lex->sources);
869	REQUIRE(source != NULL);
870	REQUIRE(tokenp != NULL);
871	REQUIRE(isc_buffer_consumedlength(source->pushback) != 0 ||
872		tokenp->type == isc_tokentype_eof);
873
874	UNUSED(tokenp);
875
876	isc_buffer_first(source->pushback);
877	lex->paren_count = lex->saved_paren_count;
878	source->line = source->saved_line;
879	source->at_eof = ISC_FALSE;
880}
881
882void
883isc_lex_getlasttokentext(isc_lex_t *lex, isc_token_t *tokenp, isc_region_t *r)
884{
885	inputsource *source;
886
887	REQUIRE(VALID_LEX(lex));
888	source = HEAD(lex->sources);
889	REQUIRE(source != NULL);
890	REQUIRE(tokenp != NULL);
891	REQUIRE(isc_buffer_consumedlength(source->pushback) != 0 ||
892		tokenp->type == isc_tokentype_eof);
893
894	UNUSED(tokenp);
895
896	INSIST(source->ignored <= isc_buffer_consumedlength(source->pushback));
897	r->base = (unsigned char *)isc_buffer_base(source->pushback) +
898		  source->ignored;
899	r->length = isc_buffer_consumedlength(source->pushback) -
900		    source->ignored;
901}
902
903
904char *
905isc_lex_getsourcename(isc_lex_t *lex) {
906	inputsource *source;
907
908	REQUIRE(VALID_LEX(lex));
909	source = HEAD(lex->sources);
910
911	if (source == NULL)
912		return (NULL);
913
914	return (source->name);
915}
916
917unsigned long
918isc_lex_getsourceline(isc_lex_t *lex) {
919	inputsource *source;
920
921	REQUIRE(VALID_LEX(lex));
922	source = HEAD(lex->sources);
923
924	if (source == NULL)
925		return (0);
926
927	return (source->line);
928}
929
930
931isc_result_t
932isc_lex_setsourcename(isc_lex_t *lex, const char *name) {
933	inputsource *source;
934	char *newname;
935
936	REQUIRE(VALID_LEX(lex));
937	source = HEAD(lex->sources);
938
939	if (source == NULL)
940		return(ISC_R_NOTFOUND);
941	newname = isc_mem_strdup(lex->mctx, name);
942	if (newname == NULL)
943		return (ISC_R_NOMEMORY);
944	isc_mem_free(lex->mctx, source->name);
945	source->name = newname;
946	return (ISC_R_SUCCESS);
947}
948
949isc_boolean_t
950isc_lex_isfile(isc_lex_t *lex) {
951	inputsource *source;
952
953	REQUIRE(VALID_LEX(lex));
954
955	source = HEAD(lex->sources);
956
957	if (source == NULL)
958		return (ISC_FALSE);
959
960	return (source->is_file);
961}
962