xref: /openbsd-src/usr.bin/dig/lib/isccfg/parser.c (revision d2c5a4743fb945f45b034a3a830a96f7e1bc695d)
1 /*
2  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3  *
4  * Permission to use, copy, modify, and/or distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14  * PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 /*! \file */
18 
19 #include <limits.h>
20 #include <stdlib.h>
21 #include <string.h>
22 
23 #include <isc/lex.h>
24 #include <isc/log.h>
25 #include <isc/symtab.h>
26 #include <isc/util.h>
27 
28 #include <isccfg/cfg.h>
29 #include <isccfg/grammar.h>
30 
31 /*!
32  * Pass one of these flags to cfg_parser_error() to include the
33  * token text in log message.
34  */
35 #define CFG_LOG_NEAR    0x00000001	/*%< Say "near <token>" */
36 #define CFG_LOG_BEFORE  0x00000002	/*%< Say "before <token>" */
37 #define CFG_LOG_NOPREP  0x00000004	/*%< Say just "<token>" */
38 
39 isc_logcategory_t cfg_category = { "config",     0 };
40 isc_logmodule_t cfg_module = { "isccfg/parser",      0 };
41 
42 /* Shorthand */
43 #define CAT &cfg_category
44 #define MOD &cfg_module
45 
46 #define MAP_SYM 1 	/* Unique type for isc_symtab */
47 
48 #define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base)
49 
50 #define CFG_LEXOPT_QSTRING (ISC_LEXOPT_QSTRING | ISC_LEXOPT_QSTRINGMULTILINE)
51 
52 /* Check a return value. */
53 #define CHECK(op) 						\
54 	do { result = (op); 					\
55 		if (result != ISC_R_SUCCESS) goto cleanup; 	\
56 	} while (0)
57 
58 /* Clean up a configuration object if non-NULL. */
59 #define CLEANUP_OBJ(obj) \
60 	do { if ((obj) != NULL) cfg_obj_destroy(pctx, &(obj)); } while (0)
61 
62 /* Forward declarations of variables */
63 cfg_rep_t cfg_rep_string;
64 cfg_rep_t cfg_rep_list;
65 
66 cfg_type_t cfg_type_qstring;
67 cfg_type_t cfg_type_sstring;
68 cfg_type_t cfg_type_token;
69 cfg_type_t cfg_type_unsupported;
70 
71 /*
72  * Forward declarations of static functions.
73  */
74 
75 static isc_result_t
76 cfg_gettoken(cfg_parser_t *pctx, int options);
77 
78 static isc_result_t
79 cfg_peektoken(cfg_parser_t *pctx, int options);
80 
81 static void
82 cfg_ungettoken(cfg_parser_t *pctx);
83 
84 static isc_result_t
85 cfg_create_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
86 
87 static isc_result_t
88 cfg_parse_qstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
89 
90 static isc_result_t
91 cfg_parse_astring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
92 
93 static isc_result_t
94 cfg_parse_sstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
95 
96 static isc_result_t
97 cfg_parse_special(cfg_parser_t *pctx, int special);
98 /*%< Parse a required special character 'special'. */
99 
100 static isc_result_t
101 cfg_create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
102 
103 static isc_result_t
104 cfg_parse_listelt(cfg_parser_t *pctx, const cfg_type_t *elttype,
105 		  cfg_listelt_t **ret);
106 
107 static isc_result_t
108 cfg_parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
109 
110 static isc_result_t
111 cfg_parse_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
112 
113 static void
114 cfg_parser_error(cfg_parser_t *pctx, unsigned int flags,
115 		 const char *fmt, ...) __attribute__((__format__(__printf__, 3, 4)));
116 
117 static void
118 free_list(cfg_parser_t *pctx, cfg_obj_t *obj);
119 
120 static isc_result_t
121 create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp);
122 
123 static isc_result_t
124 create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
125 	      cfg_obj_t **ret);
126 
127 static void
128 free_string(cfg_parser_t *pctx, cfg_obj_t *obj);
129 
130 static isc_result_t
131 create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
132 
133 static void
134 free_map(cfg_parser_t *pctx, cfg_obj_t *obj);
135 
136 static isc_result_t
137 parse_symtab_elt(cfg_parser_t *pctx, const char *name,
138 		 cfg_type_t *elttype, isc_symtab_t *symtab);
139 
140 static isc_result_t
141 cfg_getstringtoken(cfg_parser_t *pctx);
142 
143 static void
144 parser_complain(cfg_parser_t *pctx, int is_warning,
145 		unsigned int flags, const char *format, va_list args);
146 
147 /*
148  * Data representations.  These correspond to members of the
149  * "value" union in struct cfg_obj (except "void", which does
150  * not need a union member).
151  */
152 
153 cfg_rep_t cfg_rep_string = { "string", free_string };
154 cfg_rep_t cfg_rep_map = { "map", free_map };
155 cfg_rep_t cfg_rep_list = { "list", free_list };
156 
157 /*
158  * Configuration type definitions.
159  */
160 
161 /* Functions. */
162 
163 static isc_result_t
164 cfg_parse_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
165 	isc_result_t result;
166 
167 	REQUIRE(pctx != NULL);
168 	REQUIRE(type != NULL);
169 	REQUIRE(ret != NULL && *ret == NULL);
170 
171 	result = type->parse(pctx, type, ret);
172 	if (result != ISC_R_SUCCESS)
173 		return (result);
174 	ENSURE(*ret != NULL);
175 	return (ISC_R_SUCCESS);
176 }
177 
178 static isc_result_t
179 cfg_parse_special(cfg_parser_t *pctx, int special) {
180 	isc_result_t result;
181 
182 	REQUIRE(pctx != NULL);
183 
184 	CHECK(cfg_gettoken(pctx, 0));
185 	if (pctx->token.type == isc_tokentype_special &&
186 	    pctx->token.value.as_char == special)
187 		return (ISC_R_SUCCESS);
188 
189 	cfg_parser_error(pctx, CFG_LOG_NEAR, "'%c' expected", special);
190 	return (ISC_R_UNEXPECTEDTOKEN);
191  cleanup:
192 	return (result);
193 }
194 
195 /*
196  * Parse a required semicolon.  If it is not there, log
197  * an error and increment the error count but continue
198  * parsing.  Since the next token is pushed back,
199  * care must be taken to make sure it is eventually
200  * consumed or an infinite loop may result.
201  */
202 static isc_result_t
203 parse_semicolon(cfg_parser_t *pctx) {
204 	isc_result_t result;
205 
206 	CHECK(cfg_gettoken(pctx, 0));
207 	if (pctx->token.type == isc_tokentype_special &&
208 	    pctx->token.value.as_char == ';')
209 		return (ISC_R_SUCCESS);
210 
211 	cfg_parser_error(pctx, CFG_LOG_BEFORE, "missing ';'");
212 	cfg_ungettoken(pctx);
213  cleanup:
214 	return (result);
215 }
216 
217 /*
218  * Parse EOF, logging and returning an error if not there.
219  */
220 static isc_result_t
221 parse_eof(cfg_parser_t *pctx) {
222 	isc_result_t result;
223 
224 	CHECK(cfg_gettoken(pctx, 0));
225 
226 	if (pctx->token.type == isc_tokentype_eof)
227 		return (ISC_R_SUCCESS);
228 
229 	cfg_parser_error(pctx, CFG_LOG_NEAR, "syntax error");
230 	return (ISC_R_UNEXPECTEDTOKEN);
231  cleanup:
232 	return (result);
233 }
234 
235 /* A list of files, used internally for pctx->files. */
236 
237 static cfg_type_t cfg_type_filelist = {
238 	"filelist", NULL, &cfg_rep_list,
239 	&cfg_type_qstring
240 };
241 
242 isc_result_t
243 cfg_parser_create(isc_log_t *lctx, cfg_parser_t **ret) {
244 	isc_result_t result;
245 	cfg_parser_t *pctx;
246 	isc_lexspecials_t specials;
247 
248 	REQUIRE(ret != NULL && *ret == NULL);
249 
250 	pctx = malloc(sizeof(*pctx));
251 	if (pctx == NULL)
252 		return (ISC_R_NOMEMORY);
253 
254 	pctx->lctx = lctx;
255 	pctx->lexer = NULL;
256 	pctx->seen_eof = 0;
257 	pctx->ungotten = 0;
258 	pctx->errors = 0;
259 	pctx->open_files = NULL;
260 	pctx->closed_files = NULL;
261 	pctx->line = 0;
262 	pctx->token.type = isc_tokentype_unknown;
263 	pctx->flags = 0;
264 
265 	memset(specials, 0, sizeof(specials));
266 	specials['{'] = 1;
267 	specials['}'] = 1;
268 	specials[';'] = 1;
269 	specials['/'] = 1;
270 	specials['"'] = 1;
271 	specials['!'] = 1;
272 
273 	CHECK(isc_lex_create(1024, &pctx->lexer));
274 
275 	isc_lex_setspecials(pctx->lexer, specials);
276 	isc_lex_setcomments(pctx->lexer, (ISC_LEXCOMMENT_C |
277 					 ISC_LEXCOMMENT_CPLUSPLUS |
278 					 ISC_LEXCOMMENT_SHELL));
279 
280 	CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->open_files));
281 	CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->closed_files));
282 
283 	*ret = pctx;
284 	return (ISC_R_SUCCESS);
285 
286  cleanup:
287 	if (pctx->lexer != NULL)
288 		isc_lex_destroy(&pctx->lexer);
289 	CLEANUP_OBJ(pctx->open_files);
290 	CLEANUP_OBJ(pctx->closed_files);
291 	free(pctx);
292 	return (result);
293 }
294 
295 static isc_result_t
296 parser_openfile(cfg_parser_t *pctx, const char *filename) {
297 	isc_result_t result;
298 	cfg_listelt_t *elt = NULL;
299 	cfg_obj_t *stringobj = NULL;
300 
301 	result = isc_lex_openfile(pctx->lexer, filename);
302 	if (result != ISC_R_SUCCESS) {
303 		cfg_parser_error(pctx, 0, "open: %s: %s",
304 			     filename, isc_result_totext(result));
305 		goto cleanup;
306 	}
307 
308 	CHECK(create_string(pctx, filename, &cfg_type_qstring, &stringobj));
309 	CHECK(create_listelt(pctx, &elt));
310 	elt->obj = stringobj;
311 	ISC_LIST_APPEND(pctx->open_files->value.list, elt, link);
312 
313 	return (ISC_R_SUCCESS);
314  cleanup:
315 	CLEANUP_OBJ(stringobj);
316 	return (result);
317 }
318 
319 /*
320  * Parse a configuration using a pctx where a lexer has already
321  * been set up with a source.
322  */
323 static isc_result_t
324 parse2(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
325 	isc_result_t result;
326 	cfg_obj_t *obj = NULL;
327 
328 	result = cfg_parse_obj(pctx, type, &obj);
329 
330 	if (pctx->errors != 0) {
331 		/* Errors have been logged. */
332 		if (result == ISC_R_SUCCESS)
333 			result = ISC_R_FAILURE;
334 		goto cleanup;
335 	}
336 
337 	if (result != ISC_R_SUCCESS) {
338 		/* Parsing failed but no errors have been logged. */
339 		cfg_parser_error(pctx, 0, "parsing failed: %s",
340 				 isc_result_totext(result));
341 		goto cleanup;
342 	}
343 
344 	CHECK(parse_eof(pctx));
345 
346 	*ret = obj;
347 	return (ISC_R_SUCCESS);
348 
349  cleanup:
350 	CLEANUP_OBJ(obj);
351 	return (result);
352 }
353 
354 isc_result_t
355 cfg_parse_file(cfg_parser_t *pctx, const char *filename,
356 	       const cfg_type_t *type, cfg_obj_t **ret)
357 {
358 	isc_result_t result;
359 
360 	REQUIRE(pctx != NULL);
361 	REQUIRE(filename != NULL);
362 	REQUIRE(type != NULL);
363 	REQUIRE(ret != NULL && *ret == NULL);
364 
365 	CHECK(parser_openfile(pctx, filename));
366 	CHECK(parse2(pctx, type, ret));
367  cleanup:
368 	return (result);
369 }
370 
371 void
372 cfg_parser_destroy(cfg_parser_t **pctxp) {
373 	cfg_parser_t *pctx;
374 
375 	REQUIRE(pctxp != NULL && *pctxp != NULL);
376 
377 	pctx = *pctxp;
378 	*pctxp = NULL;
379 
380 	isc_lex_destroy(&pctx->lexer);
381 	/*
382 	 * Cleaning up open_files does not
383 	 * close the files; that was already done
384 	 * by closing the lexer.
385 	 */
386 	CLEANUP_OBJ(pctx->open_files);
387 	CLEANUP_OBJ(pctx->closed_files);
388 	free(pctx);
389 }
390 
391 /*
392  * qstring (quoted string), ustring (unquoted string), astring
393  * (any string)
394  */
395 
396 /* Create a string object from a null-terminated C string. */
397 static isc_result_t
398 create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
399 	      cfg_obj_t **ret)
400 {
401 	isc_result_t result;
402 	cfg_obj_t *obj = NULL;
403 	int len;
404 
405 	CHECK(cfg_create_obj(pctx, type, &obj));
406 	len = strlen(contents);
407 	obj->value.string.length = len;
408 	obj->value.string.base = malloc(len + 1);
409 	if (obj->value.string.base == NULL) {
410 		free(obj);
411 		return (ISC_R_NOMEMORY);
412 	}
413 	memmove(obj->value.string.base, contents, len);
414 	obj->value.string.base[len] = '\0';
415 
416 	*ret = obj;
417  cleanup:
418 	return (result);
419 }
420 
421 static isc_result_t
422 cfg_parse_qstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
423 	isc_result_t result;
424 
425 	REQUIRE(pctx != NULL);
426 	REQUIRE(ret != NULL && *ret == NULL);
427 
428 	UNUSED(type);
429 
430 	CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
431 	if (pctx->token.type != isc_tokentype_qstring) {
432 		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected quoted string");
433 		return (ISC_R_UNEXPECTEDTOKEN);
434 	}
435 	return (create_string(pctx, TOKEN_STRING(pctx),
436 			      &cfg_type_qstring, ret));
437  cleanup:
438 	return (result);
439 }
440 
441 static isc_result_t
442 cfg_parse_astring(cfg_parser_t *pctx, const cfg_type_t *type,
443 		  cfg_obj_t **ret)
444 {
445 	isc_result_t result;
446 
447 	REQUIRE(pctx != NULL);
448 	REQUIRE(ret != NULL && *ret == NULL);
449 
450 	UNUSED(type);
451 
452 	CHECK(cfg_getstringtoken(pctx));
453 	return (create_string(pctx,
454 			      TOKEN_STRING(pctx),
455 			      &cfg_type_qstring,
456 			      ret));
457  cleanup:
458 	return (result);
459 }
460 
461 static isc_result_t
462 cfg_parse_sstring(cfg_parser_t *pctx, const cfg_type_t *type,
463 		  cfg_obj_t **ret)
464 {
465 	isc_result_t result;
466 
467 	REQUIRE(pctx != NULL);
468 	REQUIRE(ret != NULL && *ret == NULL);
469 
470 	UNUSED(type);
471 
472 	CHECK(cfg_getstringtoken(pctx));
473 	return (create_string(pctx,
474 			      TOKEN_STRING(pctx),
475 			      &cfg_type_sstring,
476 			      ret));
477  cleanup:
478 	return (result);
479 }
480 
481 static void
482 free_string(cfg_parser_t *pctx, cfg_obj_t *obj) {
483 	UNUSED(pctx);
484 	free(obj->value.string.base);
485 }
486 
487 const char *
488 cfg_obj_asstring(const cfg_obj_t *obj) {
489 	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_string);
490 	return (obj->value.string.base);
491 }
492 
493 /* Quoted string only */
494 cfg_type_t cfg_type_qstring = {
495 	"quoted_string", cfg_parse_qstring, &cfg_rep_string, NULL
496 };
497 
498 /* Any string (quoted or unquoted); printed with quotes */
499 cfg_type_t cfg_type_astring = {
500 	"string", cfg_parse_astring, &cfg_rep_string, NULL
501 };
502 
503 /*
504  * Any string (quoted or unquoted); printed with quotes.
505  * If CFG_PRINTER_XKEY is set when printing the string will be '?' out.
506  */
507 cfg_type_t cfg_type_sstring = {
508 	"string", cfg_parse_sstring, &cfg_rep_string, NULL
509 };
510 
511 /*
512  * Booleans
513  */
514 
515 /*
516  * Lists.
517  */
518 
519 static isc_result_t
520 cfg_create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **obj) {
521 	isc_result_t result;
522 
523 	REQUIRE(pctx != NULL);
524 	REQUIRE(type != NULL);
525 	REQUIRE(obj != NULL && *obj == NULL);
526 
527 	CHECK(cfg_create_obj(pctx, type, obj));
528 	ISC_LIST_INIT((*obj)->value.list);
529  cleanup:
530 	return (result);
531 }
532 
533 static isc_result_t
534 create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp) {
535 	UNUSED(pctx);
536 	cfg_listelt_t *elt;
537 
538 	elt = malloc(sizeof(*elt));
539 	if (elt == NULL)
540 		return (ISC_R_NOMEMORY);
541 	elt->obj = NULL;
542 	ISC_LINK_INIT(elt, link);
543 	*eltp = elt;
544 	return (ISC_R_SUCCESS);
545 }
546 
547 static void
548 free_list_elt(cfg_parser_t *pctx, cfg_listelt_t *elt) {
549 	cfg_obj_destroy(pctx, &elt->obj);
550 	free(elt);
551 }
552 
553 static void
554 free_list(cfg_parser_t *pctx, cfg_obj_t *obj) {
555 	cfg_listelt_t *elt, *next;
556 	for (elt = ISC_LIST_HEAD(obj->value.list);
557 	     elt != NULL;
558 	     elt = next)
559 	{
560 		next = ISC_LIST_NEXT(elt, link);
561 		free_list_elt(pctx, elt);
562 	}
563 }
564 
565 static isc_result_t
566 cfg_parse_listelt(cfg_parser_t *pctx, const cfg_type_t *elttype,
567 		  cfg_listelt_t **ret)
568 {
569 	isc_result_t result;
570 	cfg_listelt_t *elt = NULL;
571 	cfg_obj_t *value = NULL;
572 
573 	REQUIRE(pctx != NULL);
574 	REQUIRE(elttype != NULL);
575 	REQUIRE(ret != NULL && *ret == NULL);
576 
577 	CHECK(create_listelt(pctx, &elt));
578 
579 	result = cfg_parse_obj(pctx, elttype, &value);
580 	if (result != ISC_R_SUCCESS)
581 		goto cleanup;
582 
583 	elt->obj = value;
584 
585 	*ret = elt;
586 	return (ISC_R_SUCCESS);
587 
588  cleanup:
589 	free(elt);
590 	return (result);
591 }
592 
593 int
594 cfg_obj_islist(const cfg_obj_t *obj) {
595 	REQUIRE(obj != NULL);
596 	return (obj->type->rep == &cfg_rep_list);
597 }
598 
599 const cfg_listelt_t *
600 cfg_list_first(const cfg_obj_t *obj) {
601 	REQUIRE(obj == NULL || obj->type->rep == &cfg_rep_list);
602 	if (obj == NULL)
603 		return (NULL);
604 	return (ISC_LIST_HEAD(obj->value.list));
605 }
606 
607 const cfg_listelt_t *
608 cfg_list_next(const cfg_listelt_t *elt) {
609 	REQUIRE(elt != NULL);
610 	return (ISC_LIST_NEXT(elt, link));
611 }
612 
613 /*
614  * Return the length of a list object.  If obj is NULL or is not
615  * a list, return 0.
616  */
617 unsigned int
618 cfg_list_length(const cfg_obj_t *obj, int recurse) {
619 	const cfg_listelt_t *elt;
620 	unsigned int count = 0;
621 
622 	if (obj == NULL || !cfg_obj_islist(obj))
623 		return (0U);
624 	for (elt = cfg_list_first(obj);
625 	     elt != NULL;
626 	     elt = cfg_list_next(elt)) {
627 		if (recurse && cfg_obj_islist(elt->obj)) {
628 			count += cfg_list_length(elt->obj, recurse);
629 		} else {
630 			count++;
631 		}
632 	}
633 	return (count);
634 }
635 
636 /*
637  * Maps.
638  */
639 
640 /*
641  * Parse a map body.  That's something like
642  *
643  *   "foo 1; bar { glub; }; zap true; zap false;"
644  *
645  * i.e., a sequence of option names followed by values and
646  * terminated by semicolons.  Used for the top level of
647  * the named.conf syntax, as well as for the body of the
648  * options, view, zone, and other statements.
649  */
650 isc_result_t
651 cfg_parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
652 {
653 	const cfg_clausedef_t * const *clausesets = type->of;
654 	isc_result_t result;
655 	const cfg_clausedef_t * const *clauseset;
656 	const cfg_clausedef_t *clause;
657 	cfg_obj_t *value = NULL;
658 	cfg_obj_t *obj = NULL;
659 	cfg_obj_t *eltobj = NULL;
660 	cfg_obj_t *includename = NULL;
661 	isc_symvalue_t symval;
662 
663 	REQUIRE(pctx != NULL);
664 	REQUIRE(type != NULL);
665 	REQUIRE(ret != NULL && *ret == NULL);
666 
667 	CHECK(create_map(pctx, type, &obj));
668 
669 	obj->value.map.clausesets = clausesets;
670 
671 	for (;;) {
672 	redo:
673 		/*
674 		 * Parse the option name and see if it is known.
675 		 */
676 		CHECK(cfg_gettoken(pctx, 0));
677 
678 		if (pctx->token.type != isc_tokentype_string) {
679 			cfg_ungettoken(pctx);
680 			break;
681 		}
682 
683 		/*
684 		 * We accept "include" statements wherever a map body
685 		 * clause can occur.
686 		 */
687 		if (strcasecmp(TOKEN_STRING(pctx), "include") == 0) {
688 			/*
689 			 * Turn the file name into a temporary configuration
690 			 * object just so that it is not overwritten by the
691 			 * semicolon token.
692 			 */
693 			CHECK(cfg_parse_obj(pctx, &cfg_type_qstring, &includename));
694 			CHECK(parse_semicolon(pctx));
695 			CHECK(parser_openfile(pctx, includename->
696 					      value.string.base));
697 			 cfg_obj_destroy(pctx, &includename);
698 			 goto redo;
699 		}
700 
701 		clause = NULL;
702 		for (clauseset = clausesets; *clauseset != NULL; clauseset++) {
703 			for (clause = *clauseset;
704 			     clause->name != NULL;
705 			     clause++) {
706 				if (strcasecmp(TOKEN_STRING(pctx),
707 					   clause->name) == 0)
708 					goto done;
709 			}
710 		}
711 	done:
712 		if (clause == NULL || clause->name == NULL) {
713 			cfg_parser_error(pctx, CFG_LOG_NOPREP,
714 					 "unknown option");
715 			/*
716 			 * Try to recover by parsing this option as an unknown
717 			 * option and discarding it.
718 			 */
719 			CHECK(cfg_parse_obj(pctx, &cfg_type_unsupported,
720 					    &eltobj));
721 			cfg_obj_destroy(pctx, &eltobj);
722 			CHECK(parse_semicolon(pctx));
723 			continue;
724 		}
725 
726 		/* Clause is known. */
727 
728 		/* See if the clause already has a value; if not create one. */
729 		result = isc_symtab_lookup(obj->value.map.symtab,
730 					   clause->name, 0, &symval);
731 
732 		/* Single-valued clause */
733 		if (result == ISC_R_NOTFOUND) {
734 			CHECK(parse_symtab_elt(pctx, clause->name,
735 					       clause->type,
736 					       obj->value.map.symtab));
737 			CHECK(parse_semicolon(pctx));
738 		} else if (result == ISC_R_SUCCESS) {
739 			cfg_parser_error(pctx, CFG_LOG_NEAR, "'%s' redefined",
740 				     clause->name);
741 			result = ISC_R_EXISTS;
742 			goto cleanup;
743 		} else {
744 			cfg_parser_error(pctx, CFG_LOG_NEAR,
745 				     "isc_symtab_define() failed");
746 			goto cleanup;
747 		}
748 	}
749 
750 	*ret = obj;
751 	return (ISC_R_SUCCESS);
752 
753  cleanup:
754 	CLEANUP_OBJ(value);
755 	CLEANUP_OBJ(obj);
756 	CLEANUP_OBJ(eltobj);
757 	CLEANUP_OBJ(includename);
758 	return (result);
759 }
760 
761 static isc_result_t
762 parse_symtab_elt(cfg_parser_t *pctx, const char *name,
763 		 cfg_type_t *elttype, isc_symtab_t *symtab)
764 {
765 	isc_result_t result;
766 	cfg_obj_t *obj = NULL;
767 	isc_symvalue_t symval;
768 
769 	CHECK(cfg_parse_obj(pctx, elttype, &obj));
770 
771 	symval.as_pointer = obj;
772 	CHECK(isc_symtab_define(symtab, name,
773 				1, symval,
774 				isc_symexists_reject));
775 	return (ISC_R_SUCCESS);
776 
777  cleanup:
778 	CLEANUP_OBJ(obj);
779 	return (result);
780 }
781 
782 /*
783  * Parse a map; e.g., "{ foo 1; bar { glub; }; zap true; zap false; }"
784  */
785 static isc_result_t
786 cfg_parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
787 	isc_result_t result;
788 
789 	REQUIRE(pctx != NULL);
790 	REQUIRE(type != NULL);
791 	REQUIRE(ret != NULL && *ret == NULL);
792 
793 	CHECK(cfg_parse_special(pctx, '{'));
794 	CHECK(cfg_parse_mapbody(pctx, type, ret));
795 	CHECK(cfg_parse_special(pctx, '}'));
796  cleanup:
797 	return (result);
798 }
799 
800 /*
801  * Subroutine for cfg_parse_named_map() and cfg_parse_addressed_map().
802  */
803 static isc_result_t
804 parse_any_named_map(cfg_parser_t *pctx, cfg_type_t *nametype,
805 		    const cfg_type_t *type, cfg_obj_t **ret)
806 {
807 	isc_result_t result;
808 	cfg_obj_t *idobj = NULL;
809 	cfg_obj_t *mapobj = NULL;
810 
811 	REQUIRE(pctx != NULL);
812 	REQUIRE(nametype != NULL);
813 	REQUIRE(type != NULL);
814 	REQUIRE(ret != NULL && *ret == NULL);
815 
816 	CHECK(cfg_parse_obj(pctx, nametype, &idobj));
817 	CHECK(cfg_parse_map(pctx, type, &mapobj));
818 	mapobj->value.map.id = idobj;
819 	*ret = mapobj;
820 	return (result);
821  cleanup:
822 	CLEANUP_OBJ(idobj);
823 	CLEANUP_OBJ(mapobj);
824 	return (result);
825 }
826 
827 /*
828  * Parse a map identified by a string name.  E.g., "name { foo 1; }".
829  * Used for the "key" and "channel" statements.
830  */
831 isc_result_t
832 cfg_parse_named_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
833 	return (parse_any_named_map(pctx, &cfg_type_astring, type, ret));
834 }
835 
836 isc_result_t
837 cfg_map_get(const cfg_obj_t *mapobj, const char* name, const cfg_obj_t **obj) {
838 	isc_result_t result;
839 	isc_symvalue_t val;
840 	const cfg_map_t *map;
841 
842 	REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
843 	REQUIRE(name != NULL);
844 	REQUIRE(obj != NULL && *obj == NULL);
845 
846 	map = &mapobj->value.map;
847 
848 	result = isc_symtab_lookup(map->symtab, name, MAP_SYM, &val);
849 	if (result != ISC_R_SUCCESS)
850 		return (result);
851 	*obj = val.as_pointer;
852 	return (ISC_R_SUCCESS);
853 }
854 
855 const cfg_obj_t *
856 cfg_map_getname(const cfg_obj_t *mapobj) {
857 	REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
858 	return (mapobj->value.map.id);
859 }
860 
861 /* Parse an arbitrary token, storing its raw text representation. */
862 static isc_result_t
863 parse_token(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
864 	cfg_obj_t *obj = NULL;
865 	isc_result_t result;
866 	isc_region_t r;
867 
868 	UNUSED(type);
869 
870 	CHECK(cfg_create_obj(pctx, &cfg_type_token, &obj));
871 	CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
872 	if (pctx->token.type == isc_tokentype_eof) {
873 		cfg_ungettoken(pctx);
874 		result = ISC_R_EOF;
875 		goto cleanup;
876 	}
877 
878 	isc_lex_getlasttokentext(pctx->lexer, &pctx->token, &r);
879 
880 	obj->value.string.base = malloc(r.length + 1);
881 	if (obj->value.string.base == NULL) {
882 		result = ISC_R_NOMEMORY;
883 		goto cleanup;
884 	}
885 	obj->value.string.length = r.length;
886 	memmove(obj->value.string.base, r.base, r.length);
887 	obj->value.string.base[r.length] = '\0';
888 	*ret = obj;
889 	return (result);
890 
891  cleanup:
892 	if (obj != NULL)
893 		free(obj);
894 	return (result);
895 }
896 
897 cfg_type_t cfg_type_token = {
898 	"token", parse_token, &cfg_rep_string, NULL
899 };
900 
901 /*
902  * An unsupported option.  This is just a list of tokens with balanced braces
903  * ending in a semicolon.
904  */
905 
906 static isc_result_t
907 parse_unsupported(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
908 	cfg_obj_t *listobj = NULL;
909 	isc_result_t result;
910 	int braces = 0;
911 
912 	CHECK(cfg_create_list(pctx, type, &listobj));
913 
914 	for (;;) {
915 		cfg_listelt_t *elt = NULL;
916 
917 		CHECK(cfg_peektoken(pctx, 0));
918 		if (pctx->token.type == isc_tokentype_special) {
919 			if (pctx->token.value.as_char == '{')
920 				braces++;
921 			else if (pctx->token.value.as_char == '}')
922 				braces--;
923 			else if (pctx->token.value.as_char == ';')
924 				if (braces == 0)
925 					break;
926 		}
927 		if (pctx->token.type == isc_tokentype_eof || braces < 0) {
928 			cfg_parser_error(pctx, CFG_LOG_NEAR, "unexpected token");
929 			result = ISC_R_UNEXPECTEDTOKEN;
930 			goto cleanup;
931 		}
932 
933 		CHECK(cfg_parse_listelt(pctx, &cfg_type_token, &elt));
934 		ISC_LIST_APPEND(listobj->value.list, elt, link);
935 	}
936 	INSIST(braces == 0);
937 	*ret = listobj;
938 	return (ISC_R_SUCCESS);
939 
940  cleanup:
941 	CLEANUP_OBJ(listobj);
942 	return (result);
943 }
944 
945 cfg_type_t cfg_type_unsupported = {
946 	"unsupported", parse_unsupported, &cfg_rep_list, NULL
947 };
948 
949 static isc_result_t
950 cfg_gettoken(cfg_parser_t *pctx, int options) {
951 	isc_result_t result;
952 
953 	REQUIRE(pctx != NULL);
954 
955 	if (pctx->seen_eof)
956 		return (ISC_R_SUCCESS);
957 
958 	options |= (ISC_LEXOPT_EOF | ISC_LEXOPT_NOMORE);
959 
960  redo:
961 	pctx->token.type = isc_tokentype_unknown;
962 	result = isc_lex_gettoken(pctx->lexer, options, &pctx->token);
963 	pctx->ungotten = 0;
964 	pctx->line = isc_lex_getsourceline(pctx->lexer);
965 
966 	switch (result) {
967 	case ISC_R_SUCCESS:
968 		if (pctx->token.type == isc_tokentype_eof) {
969 			result = isc_lex_close(pctx->lexer);
970 			INSIST(result == ISC_R_NOMORE ||
971 			       result == ISC_R_SUCCESS);
972 
973 			if (isc_lex_getsourcename(pctx->lexer) != NULL) {
974 				/*
975 				 * Closed an included file, not the main file.
976 				 */
977 				cfg_listelt_t *elt;
978 				elt = ISC_LIST_TAIL(pctx->open_files->
979 						    value.list);
980 				INSIST(elt != NULL);
981 				ISC_LIST_UNLINK(pctx->open_files->
982 						value.list, elt, link);
983 				ISC_LIST_APPEND(pctx->closed_files->
984 						value.list, elt, link);
985 				goto redo;
986 			}
987 			pctx->seen_eof = 1;
988 		}
989 		break;
990 
991 	case ISC_R_NOSPACE:
992 		/* More understandable than "ran out of space". */
993 		cfg_parser_error(pctx, CFG_LOG_NEAR, "token too big");
994 		break;
995 
996 	case ISC_R_IOERROR:
997 		cfg_parser_error(pctx, 0, "%s",
998 				 isc_result_totext(result));
999 		break;
1000 
1001 	default:
1002 		cfg_parser_error(pctx, CFG_LOG_NEAR, "%s",
1003 				 isc_result_totext(result));
1004 		break;
1005 	}
1006 	return (result);
1007 }
1008 
1009 static void
1010 cfg_ungettoken(cfg_parser_t *pctx) {
1011 	REQUIRE(pctx != NULL);
1012 
1013 	if (pctx->seen_eof)
1014 		return;
1015 	isc_lex_ungettoken(pctx->lexer, &pctx->token);
1016 	pctx->ungotten = 1;
1017 }
1018 
1019 static isc_result_t
1020 cfg_peektoken(cfg_parser_t *pctx, int options) {
1021 	isc_result_t result;
1022 
1023 	REQUIRE(pctx != NULL);
1024 
1025 	CHECK(cfg_gettoken(pctx, options));
1026 	cfg_ungettoken(pctx);
1027  cleanup:
1028 	return (result);
1029 }
1030 
1031 /*
1032  * Get a string token, accepting both the quoted and the unquoted form.
1033  * Log an error if the next token is not a string.
1034  */
1035 static isc_result_t
1036 cfg_getstringtoken(cfg_parser_t *pctx) {
1037 	isc_result_t result;
1038 
1039 	result = cfg_gettoken(pctx, CFG_LEXOPT_QSTRING);
1040 	if (result != ISC_R_SUCCESS)
1041 		return (result);
1042 
1043 	if (pctx->token.type != isc_tokentype_string &&
1044 	    pctx->token.type != isc_tokentype_qstring) {
1045 		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected string");
1046 		return (ISC_R_UNEXPECTEDTOKEN);
1047 	}
1048 	return (ISC_R_SUCCESS);
1049 }
1050 
1051 static void
1052 cfg_parser_error(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
1053 	va_list args;
1054 
1055 	REQUIRE(pctx != NULL);
1056 	REQUIRE(fmt != NULL);
1057 
1058 	va_start(args, fmt);
1059 	parser_complain(pctx, 0, flags, fmt, args);
1060 	va_end(args);
1061 	pctx->errors++;
1062 }
1063 
1064 #define MAX_LOG_TOKEN 30 /* How much of a token to quote in log messages. */
1065 
1066 static int
1067 have_current_file(cfg_parser_t *pctx) {
1068 	cfg_listelt_t *elt;
1069 	if (pctx->open_files == NULL)
1070 		return (0);
1071 
1072 	elt = ISC_LIST_TAIL(pctx->open_files->value.list);
1073 	if (elt == NULL)
1074 	      return (0);
1075 
1076 	return (1);
1077 }
1078 
1079 static char *
1080 current_file(cfg_parser_t *pctx) {
1081 	static char none[] = "none";
1082 	cfg_listelt_t *elt;
1083 	cfg_obj_t *fileobj;
1084 
1085 	if (!have_current_file(pctx))
1086 		return (none);
1087 
1088 	elt = ISC_LIST_TAIL(pctx->open_files->value.list);
1089 	if (elt == NULL)	/* shouldn't be possible, but... */
1090 	      return (none);
1091 
1092 	fileobj = elt->obj;
1093 	INSIST(fileobj->type == &cfg_type_qstring);
1094 	return (fileobj->value.string.base);
1095 }
1096 
1097 static void
1098 parser_complain(cfg_parser_t *pctx, int is_warning,
1099 		unsigned int flags, const char *format,
1100 		va_list args)
1101 {
1102 	char tokenbuf[MAX_LOG_TOKEN + 10];
1103 	static char where[PATH_MAX + 100];
1104 	static char message[2048];
1105 	int level = ISC_LOG_ERROR;
1106 	const char *prep = "";
1107 	size_t len;
1108 
1109 	if (is_warning)
1110 		level = ISC_LOG_WARNING;
1111 
1112 	where[0] = '\0';
1113 	if (have_current_file(pctx))
1114 		snprintf(where, sizeof(where), "%s:%u: ",
1115 			 current_file(pctx), pctx->line);
1116 
1117 	len = vsnprintf(message, sizeof(message), format, args);
1118 #define ELIPSIS " ... "
1119 	if (len >= sizeof(message)) {
1120 		message[sizeof(message) - sizeof(ELIPSIS)] = 0;
1121 		strlcat(message, ELIPSIS, sizeof(message));
1122 	}
1123 
1124 	if ((flags & (CFG_LOG_NEAR|CFG_LOG_BEFORE|CFG_LOG_NOPREP)) != 0) {
1125 		isc_region_t r;
1126 
1127 		if (pctx->ungotten)
1128 			(void)cfg_gettoken(pctx, 0);
1129 
1130 		if (pctx->token.type == isc_tokentype_eof) {
1131 			snprintf(tokenbuf, sizeof(tokenbuf), "end of file");
1132 		} else if (pctx->token.type == isc_tokentype_unknown) {
1133 			flags = 0;
1134 			tokenbuf[0] = '\0';
1135 		} else {
1136 			isc_lex_getlasttokentext(pctx->lexer,
1137 						 &pctx->token, &r);
1138 			if (r.length > MAX_LOG_TOKEN)
1139 				snprintf(tokenbuf, sizeof(tokenbuf),
1140 					 "'%.*s...'", MAX_LOG_TOKEN, r.base);
1141 			else
1142 				snprintf(tokenbuf, sizeof(tokenbuf),
1143 					 "'%.*s'", (int)r.length, r.base);
1144 		}
1145 
1146 		/* Choose a preposition. */
1147 		if (flags & CFG_LOG_NEAR)
1148 			prep = " near ";
1149 		else if (flags & CFG_LOG_BEFORE)
1150 			prep = " before ";
1151 		else
1152 			prep = " ";
1153 	} else {
1154 		tokenbuf[0] = '\0';
1155 	}
1156 	isc_log_write(pctx->lctx, CAT, MOD, level,
1157 		      "%s%s%s%s", where, message, prep, tokenbuf);
1158 }
1159 
1160 static isc_result_t
1161 cfg_create_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1162 	cfg_obj_t *obj;
1163 
1164 	REQUIRE(pctx != NULL);
1165 	REQUIRE(type != NULL);
1166 	REQUIRE(ret != NULL && *ret == NULL);
1167 
1168 	obj = malloc(sizeof(cfg_obj_t));
1169 	if (obj == NULL)
1170 		return (ISC_R_NOMEMORY);
1171 	obj->type = type;
1172 	obj->file = current_file(pctx);
1173 	obj->line = pctx->line;
1174 	*ret = obj;
1175 	return (ISC_R_SUCCESS);
1176 }
1177 
1178 static void
1179 map_symtabitem_destroy(char *key, unsigned int type,
1180 		       isc_symvalue_t symval, void *userarg)
1181 {
1182 	cfg_obj_t *obj = symval.as_pointer;
1183 	cfg_parser_t *pctx = (cfg_parser_t *)userarg;
1184 
1185 	UNUSED(key);
1186 	UNUSED(type);
1187 
1188 	cfg_obj_destroy(pctx, &obj);
1189 }
1190 
1191 static isc_result_t
1192 create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1193 	isc_result_t result;
1194 	isc_symtab_t *symtab = NULL;
1195 	cfg_obj_t *obj = NULL;
1196 
1197 	CHECK(cfg_create_obj(pctx, type, &obj));
1198 	CHECK(isc_symtab_create(5, /* XXX */
1199 				map_symtabitem_destroy,
1200 				pctx, 0, &symtab));
1201 	obj->value.map.symtab = symtab;
1202 	obj->value.map.id = NULL;
1203 
1204 	*ret = obj;
1205 	return (ISC_R_SUCCESS);
1206 
1207  cleanup:
1208 	if (obj != NULL)
1209 		free(obj);
1210 	return (result);
1211 }
1212 
1213 static void
1214 free_map(cfg_parser_t *pctx, cfg_obj_t *obj) {
1215 	CLEANUP_OBJ(obj->value.map.id);
1216 	isc_symtab_destroy(&obj->value.map.symtab);
1217 }
1218 
1219 /*
1220  * Destroy 'obj', a configuration object created in 'pctx'.
1221  */
1222 void
1223 cfg_obj_destroy(cfg_parser_t *pctx, cfg_obj_t **objp) {
1224 	cfg_obj_t *obj;
1225 
1226 	REQUIRE(objp != NULL && *objp != NULL);
1227 	REQUIRE(pctx != NULL);
1228 
1229 	obj = *objp;
1230 
1231 	obj->type->rep->free(pctx, obj);
1232 	free(obj);
1233 	*objp = NULL;
1234 }
1235