PageRenderTime 67ms CodeModel.GetById 16ms app.highlight 44ms RepoModel.GetById 1ms app.codeStats 0ms

/contrib/bind9/lib/isc/lex.c

https://bitbucket.org/freebsd/freebsd-head/
C | 959 lines | 820 code | 81 blank | 58 comment | 122 complexity | 366f42a2570ee17fc44dce58eb54e462 MD5 | raw file
  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
 39typedef 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
 56struct 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
 71static inline isc_result_t
 72grow_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
 89isc_result_t
 90isc_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
124void
125isc_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
146unsigned int
147isc_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
157void
158isc_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
168void
169isc_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
179void
180isc_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
191static inline isc_result_t
192new_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
226isc_result_t
227isc_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
247isc_result_t
248isc_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
262isc_result_t
263isc_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
277isc_result_t
278isc_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
303typedef 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
317static void
318pushback(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
329static isc_result_t
330pushandgrow(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
352isc_result_t
353isc_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
795isc_result_t
796isc_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
831isc_result_t
832isc_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
858void
859isc_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
880void
881isc_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
902char *
903isc_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
915unsigned long
916isc_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
929isc_result_t
930isc_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
947isc_boolean_t
948isc_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}