xref: /minix/external/bsd/bind/dist/lib/isccfg/parser.c (revision bb9622b5)
1 /*	$NetBSD: parser.c,v 1.9 2015/07/08 17:29:00 christos Exp $	*/
2 
3 /*
4  * Copyright (C) 2004-2015  Internet Systems Consortium, Inc. ("ISC")
5  * Copyright (C) 2000-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 */
21 
22 /*! \file */
23 
24 #include <config.h>
25 
26 #include <isc/buffer.h>
27 #include <isc/dir.h>
28 #include <isc/formatcheck.h>
29 #include <isc/lex.h>
30 #include <isc/log.h>
31 #include <isc/mem.h>
32 #include <isc/net.h>
33 #include <isc/netaddr.h>
34 #include <isc/netscope.h>
35 #include <isc/print.h>
36 #include <isc/string.h>
37 #include <isc/sockaddr.h>
38 #include <isc/symtab.h>
39 #include <isc/util.h>
40 
41 #include <isccfg/cfg.h>
42 #include <isccfg/grammar.h>
43 #include <isccfg/log.h>
44 
45 /* Shorthand */
46 #define CAT CFG_LOGCATEGORY_CONFIG
47 #define MOD CFG_LOGMODULE_PARSER
48 
49 #define MAP_SYM 1 	/* Unique type for isc_symtab */
50 
51 #define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base)
52 
53 /* Check a return value. */
54 #define CHECK(op) 						\
55 	do { result = (op); 					\
56 		if (result != ISC_R_SUCCESS) goto cleanup; 	\
57 	} while (/*CONSTCOND*/0)
58 
59 /* Clean up a configuration object if non-NULL. */
60 #define CLEANUP_OBJ(obj) \
61 	do { if ((obj) != NULL) cfg_obj_destroy(pctx, &(obj)); } while (/*CONSTCOND*/0)
62 
63 
64 /*
65  * Forward declarations of static functions.
66  */
67 
68 static void
69 free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj);
70 
71 static isc_result_t
72 parse_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
73 
74 static void
75 print_list(cfg_printer_t *pctx, const cfg_obj_t *obj);
76 
77 static void
78 free_list(cfg_parser_t *pctx, cfg_obj_t *obj);
79 
80 static isc_result_t
81 create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp);
82 
83 static isc_result_t
84 create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
85 	      cfg_obj_t **ret);
86 
87 static void
88 free_string(cfg_parser_t *pctx, cfg_obj_t *obj);
89 
90 static isc_result_t
91 create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
92 
93 static void
94 free_map(cfg_parser_t *pctx, cfg_obj_t *obj);
95 
96 static isc_result_t
97 parse_symtab_elt(cfg_parser_t *pctx, const char *name,
98 		 cfg_type_t *elttype, isc_symtab_t *symtab,
99 		 isc_boolean_t callback);
100 
101 static void
102 free_noop(cfg_parser_t *pctx, cfg_obj_t *obj);
103 
104 static isc_result_t
105 cfg_getstringtoken(cfg_parser_t *pctx);
106 
107 static void
108 parser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning,
109 		unsigned int flags, const char *format, va_list args);
110 
111 /*
112  * Data representations.  These correspond to members of the
113  * "value" union in struct cfg_obj (except "void", which does
114  * not need a union member).
115  */
116 
117 cfg_rep_t cfg_rep_uint32 = { "uint32", free_noop };
118 cfg_rep_t cfg_rep_uint64 = { "uint64", free_noop };
119 cfg_rep_t cfg_rep_string = { "string", free_string };
120 cfg_rep_t cfg_rep_boolean = { "boolean", free_noop };
121 cfg_rep_t cfg_rep_map = { "map", free_map };
122 cfg_rep_t cfg_rep_list = { "list", free_list };
123 cfg_rep_t cfg_rep_tuple = { "tuple", free_tuple };
124 cfg_rep_t cfg_rep_sockaddr = { "sockaddr", free_noop };
125 cfg_rep_t cfg_rep_netprefix = { "netprefix", free_noop };
126 cfg_rep_t cfg_rep_void = { "void", free_noop };
127 
128 /*
129  * Configuration type definitions.
130  */
131 
132 /*%
133  * An implicit list.  These are formed by clauses that occur multiple times.
134  */
135 static cfg_type_t cfg_type_implicitlist = {
136 	"implicitlist", NULL, print_list, NULL, &cfg_rep_list, NULL };
137 
138 /* Functions. */
139 
140 void
141 cfg_print_obj(cfg_printer_t *pctx, const cfg_obj_t *obj) {
142 	obj->type->print(pctx, obj);
143 }
144 
145 void
146 cfg_print_chars(cfg_printer_t *pctx, const char *text, int len) {
147 	pctx->f(pctx->closure, text, len);
148 }
149 
150 static void
151 print_open(cfg_printer_t *pctx) {
152 	cfg_print_chars(pctx, "{\n", 2);
153 	pctx->indent++;
154 }
155 
156 static void
157 print_indent(cfg_printer_t *pctx) {
158 	int indent = pctx->indent;
159 	while (indent > 0) {
160 		cfg_print_chars(pctx, "\t", 1);
161 		indent--;
162 	}
163 }
164 
165 static void
166 print_close(cfg_printer_t *pctx) {
167 	pctx->indent--;
168 	print_indent(pctx);
169 	cfg_print_chars(pctx, "}", 1);
170 }
171 
172 isc_result_t
173 cfg_parse_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
174 	isc_result_t result;
175 	INSIST(ret != NULL && *ret == NULL);
176 	result = type->parse(pctx, type, ret);
177 	if (result != ISC_R_SUCCESS)
178 		return (result);
179 	INSIST(*ret != NULL);
180 	return (ISC_R_SUCCESS);
181 }
182 
183 void
184 cfg_print(const cfg_obj_t *obj,
185 	  void (*f)(void *closure, const char *text, int textlen),
186 	  void *closure)
187 {
188 	cfg_printx(obj, 0, f, closure);
189 }
190 
191 void
192 cfg_printx(const cfg_obj_t *obj, unsigned int flags,
193 	     void (*f)(void *closure, const char *text, int textlen),
194 	     void *closure)
195 {
196 	cfg_printer_t pctx;
197 	pctx.f = f;
198 	pctx.closure = closure;
199 	pctx.indent = 0;
200 	pctx.flags = flags;
201 	obj->type->print(&pctx, obj);
202 }
203 
204 /* Tuples. */
205 
206 isc_result_t
207 cfg_create_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
208 	isc_result_t result;
209 	const cfg_tuplefielddef_t *fields = type->of;
210 	const cfg_tuplefielddef_t *f;
211 	cfg_obj_t *obj = NULL;
212 	unsigned int nfields = 0;
213 	int i;
214 
215 	for (f = fields; f->name != NULL; f++)
216 		nfields++;
217 
218 	CHECK(cfg_create_obj(pctx, type, &obj));
219 	obj->value.tuple = isc_mem_get(pctx->mctx,
220 				       nfields * sizeof(cfg_obj_t *));
221 	if (obj->value.tuple == NULL) {
222 		result = ISC_R_NOMEMORY;
223 		goto cleanup;
224 	}
225 	for (f = fields, i = 0; f->name != NULL; f++, i++)
226 		obj->value.tuple[i] = NULL;
227 	*ret = obj;
228 	return (ISC_R_SUCCESS);
229 
230  cleanup:
231 	if (obj != NULL)
232 		isc_mem_put(pctx->mctx, obj, sizeof(*obj));
233 	return (result);
234 }
235 
236 isc_result_t
237 cfg_parse_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
238 {
239 	isc_result_t result;
240 	const cfg_tuplefielddef_t *fields = type->of;
241 	const cfg_tuplefielddef_t *f;
242 	cfg_obj_t *obj = NULL;
243 	unsigned int i;
244 
245 	CHECK(cfg_create_tuple(pctx, type, &obj));
246 	for (f = fields, i = 0; f->name != NULL; f++, i++)
247 		CHECK(cfg_parse_obj(pctx, f->type, &obj->value.tuple[i]));
248 
249 	*ret = obj;
250 	return (ISC_R_SUCCESS);
251 
252  cleanup:
253 	CLEANUP_OBJ(obj);
254 	return (result);
255 }
256 
257 void
258 cfg_print_tuple(cfg_printer_t *pctx, const cfg_obj_t *obj) {
259 	unsigned int i;
260 	const cfg_tuplefielddef_t *fields = obj->type->of;
261 	const cfg_tuplefielddef_t *f;
262 	isc_boolean_t need_space = ISC_FALSE;
263 
264 	for (f = fields, i = 0; f->name != NULL; f++, i++) {
265 		const cfg_obj_t *fieldobj = obj->value.tuple[i];
266 		if (need_space)
267 			cfg_print_chars(pctx, " ", 1);
268 		cfg_print_obj(pctx, fieldobj);
269 		need_space = ISC_TF(fieldobj->type->print != cfg_print_void);
270 	}
271 }
272 
273 void
274 cfg_doc_tuple(cfg_printer_t *pctx, const cfg_type_t *type) {
275 	const cfg_tuplefielddef_t *fields = type->of;
276 	const cfg_tuplefielddef_t *f;
277 	isc_boolean_t need_space = ISC_FALSE;
278 
279 	for (f = fields; f->name != NULL; f++) {
280 		if (need_space)
281 			cfg_print_chars(pctx, " ", 1);
282 		cfg_doc_obj(pctx, f->type);
283 		need_space = ISC_TF(f->type->print != cfg_print_void);
284 	}
285 }
286 
287 static void
288 free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj) {
289 	unsigned int i;
290 	const cfg_tuplefielddef_t *fields = obj->type->of;
291 	const cfg_tuplefielddef_t *f;
292 	unsigned int nfields = 0;
293 
294 	if (obj->value.tuple == NULL)
295 		return;
296 
297 	for (f = fields, i = 0; f->name != NULL; f++, i++) {
298 		CLEANUP_OBJ(obj->value.tuple[i]);
299 		nfields++;
300 	}
301 	isc_mem_put(pctx->mctx, obj->value.tuple,
302 		    nfields * sizeof(cfg_obj_t *));
303 }
304 
305 isc_boolean_t
306 cfg_obj_istuple(const cfg_obj_t *obj) {
307 	REQUIRE(obj != NULL);
308 	return (ISC_TF(obj->type->rep == &cfg_rep_tuple));
309 }
310 
311 const cfg_obj_t *
312 cfg_tuple_get(const cfg_obj_t *tupleobj, const char* name) {
313 	unsigned int i;
314 	const cfg_tuplefielddef_t *fields;
315 	const cfg_tuplefielddef_t *f;
316 
317 	REQUIRE(tupleobj != NULL && tupleobj->type->rep == &cfg_rep_tuple);
318 
319 	fields = tupleobj->type->of;
320 	for (f = fields, i = 0; f->name != NULL; f++, i++) {
321 		if (strcmp(f->name, name) == 0)
322 			return (tupleobj->value.tuple[i]);
323 	}
324 	INSIST(0);
325 	return (NULL);
326 }
327 
328 isc_result_t
329 cfg_parse_special(cfg_parser_t *pctx, int special) {
330 	isc_result_t result;
331 	CHECK(cfg_gettoken(pctx, 0));
332 	if (pctx->token.type == isc_tokentype_special &&
333 	    pctx->token.value.as_char == special)
334 		return (ISC_R_SUCCESS);
335 
336 	cfg_parser_error(pctx, CFG_LOG_NEAR, "'%c' expected", special);
337 	return (ISC_R_UNEXPECTEDTOKEN);
338  cleanup:
339 	return (result);
340 }
341 
342 /*
343  * Parse a required semicolon.  If it is not there, log
344  * an error and increment the error count but continue
345  * parsing.  Since the next token is pushed back,
346  * care must be taken to make sure it is eventually
347  * consumed or an infinite loop may result.
348  */
349 static isc_result_t
350 parse_semicolon(cfg_parser_t *pctx) {
351 	isc_result_t result;
352 	CHECK(cfg_gettoken(pctx, 0));
353 	if (pctx->token.type == isc_tokentype_special &&
354 	    pctx->token.value.as_char == ';')
355 		return (ISC_R_SUCCESS);
356 
357 	cfg_parser_error(pctx, CFG_LOG_BEFORE, "missing ';'");
358 	cfg_ungettoken(pctx);
359  cleanup:
360 	return (result);
361 }
362 
363 /*
364  * Parse EOF, logging and returning an error if not there.
365  */
366 static isc_result_t
367 parse_eof(cfg_parser_t *pctx) {
368 	isc_result_t result;
369 	CHECK(cfg_gettoken(pctx, 0));
370 
371 	if (pctx->token.type == isc_tokentype_eof)
372 		return (ISC_R_SUCCESS);
373 
374 	cfg_parser_error(pctx, CFG_LOG_NEAR, "syntax error");
375 	return (ISC_R_UNEXPECTEDTOKEN);
376  cleanup:
377 	return (result);
378 }
379 
380 /* A list of files, used internally for pctx->files. */
381 
382 static cfg_type_t cfg_type_filelist = {
383 	"filelist", NULL, print_list, NULL, &cfg_rep_list,
384 	&cfg_type_qstring
385 };
386 
387 isc_result_t
388 cfg_parser_create(isc_mem_t *mctx, isc_log_t *lctx, cfg_parser_t **ret) {
389 	isc_result_t result;
390 	cfg_parser_t *pctx;
391 	isc_lexspecials_t specials;
392 
393 	REQUIRE(mctx != NULL);
394 	REQUIRE(ret != NULL && *ret == NULL);
395 
396 	pctx = isc_mem_get(mctx, sizeof(*pctx));
397 	if (pctx == NULL)
398 		return (ISC_R_NOMEMORY);
399 
400 	pctx->mctx = NULL;
401 	isc_mem_attach(mctx, &pctx->mctx);
402 
403 	result = isc_refcount_init(&pctx->references, 1);
404 	if (result != ISC_R_SUCCESS) {
405 		isc_mem_putanddetach(&pctx->mctx, pctx, sizeof(*pctx));
406 		return (result);
407 	}
408 
409 	pctx->lctx = lctx;
410 	pctx->lexer = NULL;
411 	pctx->seen_eof = ISC_FALSE;
412 	pctx->ungotten = ISC_FALSE;
413 	pctx->errors = 0;
414 	pctx->warnings = 0;
415 	pctx->open_files = NULL;
416 	pctx->closed_files = NULL;
417 	pctx->line = 0;
418 	pctx->callback = NULL;
419 	pctx->callbackarg = NULL;
420 	pctx->token.type = isc_tokentype_unknown;
421 	pctx->flags = 0;
422 
423 	memset(specials, 0, sizeof(specials));
424 	specials['{'] = 1;
425 	specials['}'] = 1;
426 	specials[';'] = 1;
427 	specials['/'] = 1;
428 	specials['"'] = 1;
429 	specials['!'] = 1;
430 
431 	CHECK(isc_lex_create(pctx->mctx, 1024, &pctx->lexer));
432 
433 	isc_lex_setspecials(pctx->lexer, specials);
434 	isc_lex_setcomments(pctx->lexer, (ISC_LEXCOMMENT_C |
435 					 ISC_LEXCOMMENT_CPLUSPLUS |
436 					 ISC_LEXCOMMENT_SHELL));
437 
438 	CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->open_files));
439 	CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->closed_files));
440 
441 	*ret = pctx;
442 	return (ISC_R_SUCCESS);
443 
444  cleanup:
445 	if (pctx->lexer != NULL)
446 		isc_lex_destroy(&pctx->lexer);
447 	CLEANUP_OBJ(pctx->open_files);
448 	CLEANUP_OBJ(pctx->closed_files);
449 	isc_mem_putanddetach(&pctx->mctx, pctx, sizeof(*pctx));
450 	return (result);
451 }
452 
453 static isc_result_t
454 parser_openfile(cfg_parser_t *pctx, const char *filename) {
455 	isc_result_t result;
456 	cfg_listelt_t *elt = NULL;
457 	cfg_obj_t *stringobj = NULL;
458 
459 	result = isc_lex_openfile(pctx->lexer, filename);
460 	if (result != ISC_R_SUCCESS) {
461 		cfg_parser_error(pctx, 0, "open: %s: %s",
462 			     filename, isc_result_totext(result));
463 		goto cleanup;
464 	}
465 
466 	CHECK(create_string(pctx, filename, &cfg_type_qstring, &stringobj));
467 	CHECK(create_listelt(pctx, &elt));
468 	elt->obj = stringobj;
469 	ISC_LIST_APPEND(pctx->open_files->value.list, elt, link);
470 
471 	return (ISC_R_SUCCESS);
472  cleanup:
473 	CLEANUP_OBJ(stringobj);
474 	return (result);
475 }
476 
477 void
478 cfg_parser_setcallback(cfg_parser_t *pctx,
479 		       cfg_parsecallback_t callback,
480 		       void *arg)
481 {
482 	pctx->callback = callback;
483 	pctx->callbackarg = arg;
484 }
485 
486 /*
487  * Parse a configuration using a pctx where a lexer has already
488  * been set up with a source.
489  */
490 static isc_result_t
491 parse2(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
492 	isc_result_t result;
493 	cfg_obj_t *obj = NULL;
494 
495 	result = cfg_parse_obj(pctx, type, &obj);
496 
497 	if (pctx->errors != 0) {
498 		/* Errors have been logged. */
499 		if (result == ISC_R_SUCCESS)
500 			result = ISC_R_FAILURE;
501 		goto cleanup;
502 	}
503 
504 	if (result != ISC_R_SUCCESS) {
505 		/* Parsing failed but no errors have been logged. */
506 		cfg_parser_error(pctx, 0, "parsing failed");
507 		goto cleanup;
508 	}
509 
510 	CHECK(parse_eof(pctx));
511 
512 	*ret = obj;
513 	return (ISC_R_SUCCESS);
514 
515  cleanup:
516 	CLEANUP_OBJ(obj);
517 	return (result);
518 }
519 
520 isc_result_t
521 cfg_parse_file(cfg_parser_t *pctx, const char *filename,
522 	       const cfg_type_t *type, cfg_obj_t **ret)
523 {
524 	isc_result_t result;
525 
526 	REQUIRE(filename != NULL);
527 
528 	CHECK(parser_openfile(pctx, filename));
529 	CHECK(parse2(pctx, type, ret));
530  cleanup:
531 	return (result);
532 }
533 
534 
535 isc_result_t
536 cfg_parse_buffer(cfg_parser_t *pctx, isc_buffer_t *buffer,
537 	const cfg_type_t *type, cfg_obj_t **ret)
538 {
539 	isc_result_t result;
540 	REQUIRE(buffer != NULL);
541 	CHECK(isc_lex_openbuffer(pctx->lexer, buffer));
542 	CHECK(parse2(pctx, type, ret));
543  cleanup:
544 	return (result);
545 }
546 
547 void
548 cfg_parser_attach(cfg_parser_t *src, cfg_parser_t **dest) {
549 	REQUIRE(src != NULL);
550 	REQUIRE(dest != NULL && *dest == NULL);
551 	isc_refcount_increment(&src->references, NULL);
552 	*dest = src;
553 }
554 
555 void
556 cfg_parser_destroy(cfg_parser_t **pctxp) {
557 	cfg_parser_t *pctx = *pctxp;
558 	unsigned int refs;
559 
560 	isc_refcount_decrement(&pctx->references, &refs);
561 	if (refs == 0) {
562 		isc_lex_destroy(&pctx->lexer);
563 		/*
564 		 * Cleaning up open_files does not
565 		 * close the files; that was already done
566 		 * by closing the lexer.
567 		 */
568 		CLEANUP_OBJ(pctx->open_files);
569 		CLEANUP_OBJ(pctx->closed_files);
570 		isc_mem_putanddetach(&pctx->mctx, pctx, sizeof(*pctx));
571 	}
572 	*pctxp = NULL;
573 }
574 
575 /*
576  * void
577  */
578 isc_result_t
579 cfg_parse_void(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
580 	UNUSED(type);
581 	return (cfg_create_obj(pctx, &cfg_type_void, ret));
582 }
583 
584 void
585 cfg_print_void(cfg_printer_t *pctx, const cfg_obj_t *obj) {
586 	UNUSED(pctx);
587 	UNUSED(obj);
588 }
589 
590 void
591 cfg_doc_void(cfg_printer_t *pctx, const cfg_type_t *type) {
592 	UNUSED(pctx);
593 	UNUSED(type);
594 }
595 
596 isc_boolean_t
597 cfg_obj_isvoid(const cfg_obj_t *obj) {
598 	REQUIRE(obj != NULL);
599 	return (ISC_TF(obj->type->rep == &cfg_rep_void));
600 }
601 
602 cfg_type_t cfg_type_void = {
603 	"void", cfg_parse_void, cfg_print_void, cfg_doc_void, &cfg_rep_void,
604 	NULL };
605 
606 
607 /*
608  * uint32
609  */
610 isc_result_t
611 cfg_parse_uint32(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
612 	isc_result_t result;
613 	cfg_obj_t *obj = NULL;
614 	UNUSED(type);
615 
616 	CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
617 	if (pctx->token.type != isc_tokentype_number) {
618 		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected number");
619 		return (ISC_R_UNEXPECTEDTOKEN);
620 	}
621 
622 	CHECK(cfg_create_obj(pctx, &cfg_type_uint32, &obj));
623 
624 	obj->value.uint32 = pctx->token.value.as_ulong;
625 	*ret = obj;
626  cleanup:
627 	return (result);
628 }
629 
630 void
631 cfg_print_cstr(cfg_printer_t *pctx, const char *s) {
632 	cfg_print_chars(pctx, s, strlen(s));
633 }
634 
635 void
636 cfg_print_rawuint(cfg_printer_t *pctx, unsigned int u) {
637 	char buf[32];
638 	snprintf(buf, sizeof(buf), "%u", u);
639 	cfg_print_cstr(pctx, buf);
640 }
641 
642 void
643 cfg_print_uint32(cfg_printer_t *pctx, const cfg_obj_t *obj) {
644 	cfg_print_rawuint(pctx, obj->value.uint32);
645 }
646 
647 isc_boolean_t
648 cfg_obj_isuint32(const cfg_obj_t *obj) {
649 	REQUIRE(obj != NULL);
650 	return (ISC_TF(obj->type->rep == &cfg_rep_uint32));
651 }
652 
653 isc_uint32_t
654 cfg_obj_asuint32(const cfg_obj_t *obj) {
655 	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint32);
656 	return (obj->value.uint32);
657 }
658 
659 cfg_type_t cfg_type_uint32 = {
660 	"integer", cfg_parse_uint32, cfg_print_uint32, cfg_doc_terminal,
661 	&cfg_rep_uint32, NULL
662 };
663 
664 
665 /*
666  * uint64
667  */
668 isc_boolean_t
669 cfg_obj_isuint64(const cfg_obj_t *obj) {
670 	REQUIRE(obj != NULL);
671 	return (ISC_TF(obj->type->rep == &cfg_rep_uint64));
672 }
673 
674 isc_uint64_t
675 cfg_obj_asuint64(const cfg_obj_t *obj) {
676 	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint64);
677 	return (obj->value.uint64);
678 }
679 
680 void
681 cfg_print_uint64(cfg_printer_t *pctx, const cfg_obj_t *obj) {
682 	char buf[32];
683 	snprintf(buf, sizeof(buf), "%" ISC_PRINT_QUADFORMAT "u",
684 		 obj->value.uint64);
685 	cfg_print_cstr(pctx, buf);
686 }
687 
688 cfg_type_t cfg_type_uint64 = {
689 	"64_bit_integer", NULL, cfg_print_uint64, cfg_doc_terminal,
690 	&cfg_rep_uint64, NULL
691 };
692 
693 /*
694  * qstring (quoted string), ustring (unquoted string), astring
695  * (any string)
696  */
697 
698 /* Create a string object from a null-terminated C string. */
699 static isc_result_t
700 create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
701 	      cfg_obj_t **ret)
702 {
703 	isc_result_t result;
704 	cfg_obj_t *obj = NULL;
705 	int len;
706 
707 	CHECK(cfg_create_obj(pctx, type, &obj));
708 	len = strlen(contents);
709 	obj->value.string.length = len;
710 	obj->value.string.base = isc_mem_get(pctx->mctx, len + 1);
711 	if (obj->value.string.base == 0) {
712 		isc_mem_put(pctx->mctx, obj, sizeof(*obj));
713 		return (ISC_R_NOMEMORY);
714 	}
715 	memmove(obj->value.string.base, contents, len);
716 	obj->value.string.base[len] = '\0';
717 
718 	*ret = obj;
719  cleanup:
720 	return (result);
721 }
722 
723 isc_result_t
724 cfg_parse_qstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
725 	isc_result_t result;
726 	UNUSED(type);
727 
728 	CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
729 	if (pctx->token.type != isc_tokentype_qstring) {
730 		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected quoted string");
731 		return (ISC_R_UNEXPECTEDTOKEN);
732 	}
733 	return (create_string(pctx,
734 			      TOKEN_STRING(pctx),
735 			      &cfg_type_qstring,
736 			      ret));
737  cleanup:
738 	return (result);
739 }
740 
741 static isc_result_t
742 parse_ustring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
743 	isc_result_t result;
744 	UNUSED(type);
745 
746 	CHECK(cfg_gettoken(pctx, 0));
747 	if (pctx->token.type != isc_tokentype_string) {
748 		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected unquoted string");
749 		return (ISC_R_UNEXPECTEDTOKEN);
750 	}
751 	return (create_string(pctx,
752 			      TOKEN_STRING(pctx),
753 			      &cfg_type_ustring,
754 			      ret));
755  cleanup:
756 	return (result);
757 }
758 
759 isc_result_t
760 cfg_parse_astring(cfg_parser_t *pctx, const cfg_type_t *type,
761 		  cfg_obj_t **ret)
762 {
763 	isc_result_t result;
764 	UNUSED(type);
765 
766 	CHECK(cfg_getstringtoken(pctx));
767 	return (create_string(pctx,
768 			      TOKEN_STRING(pctx),
769 			      &cfg_type_qstring,
770 			      ret));
771  cleanup:
772 	return (result);
773 }
774 
775 isc_result_t
776 cfg_parse_sstring(cfg_parser_t *pctx, const cfg_type_t *type,
777 		  cfg_obj_t **ret)
778 {
779 	isc_result_t result;
780 	UNUSED(type);
781 
782 	CHECK(cfg_getstringtoken(pctx));
783 	return (create_string(pctx,
784 			      TOKEN_STRING(pctx),
785 			      &cfg_type_sstring,
786 			      ret));
787  cleanup:
788 	return (result);
789 }
790 
791 isc_boolean_t
792 cfg_is_enum(const char *s, const char *const *enums) {
793 	const char * const *p;
794 	for (p = enums; *p != NULL; p++) {
795 		if (strcasecmp(*p, s) == 0)
796 			return (ISC_TRUE);
797 	}
798 	return (ISC_FALSE);
799 }
800 
801 static isc_result_t
802 check_enum(cfg_parser_t *pctx, cfg_obj_t *obj, const char *const *enums) {
803 	const char *s = obj->value.string.base;
804 	if (cfg_is_enum(s, enums))
805 		return (ISC_R_SUCCESS);
806 	cfg_parser_error(pctx, 0, "'%s' unexpected", s);
807 	return (ISC_R_UNEXPECTEDTOKEN);
808 }
809 
810 isc_result_t
811 cfg_parse_enum(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
812 	isc_result_t result;
813 	cfg_obj_t *obj = NULL;
814 	CHECK(parse_ustring(pctx, NULL, &obj));
815 	CHECK(check_enum(pctx, obj, type->of));
816 	*ret = obj;
817 	return (ISC_R_SUCCESS);
818  cleanup:
819 	CLEANUP_OBJ(obj);
820 	return (result);
821 }
822 
823 void
824 cfg_doc_enum(cfg_printer_t *pctx, const cfg_type_t *type) {
825 	const char * const *p;
826 	cfg_print_chars(pctx, "( ", 2);
827 	for (p = type->of; *p != NULL; p++) {
828 		cfg_print_cstr(pctx, *p);
829 		if (p[1] != NULL)
830 			cfg_print_chars(pctx, " | ", 3);
831 	}
832 	cfg_print_chars(pctx, " )", 2);
833 }
834 
835 void
836 cfg_print_ustring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
837 	cfg_print_chars(pctx, obj->value.string.base, obj->value.string.length);
838 }
839 
840 static void
841 print_qstring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
842 	cfg_print_chars(pctx, "\"", 1);
843 	cfg_print_ustring(pctx, obj);
844 	cfg_print_chars(pctx, "\"", 1);
845 }
846 
847 static void
848 print_sstring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
849 	cfg_print_chars(pctx, "\"", 1);
850 	if ((pctx->flags & CFG_PRINTER_XKEY) != 0) {
851 		unsigned int len = obj->value.string.length;
852 		while (len-- > 0)
853 			cfg_print_chars(pctx, "?", 1);
854 	} else
855 		cfg_print_ustring(pctx, obj);
856 	cfg_print_chars(pctx, "\"", 1);
857 }
858 
859 static void
860 free_string(cfg_parser_t *pctx, cfg_obj_t *obj) {
861 	isc_mem_put(pctx->mctx, obj->value.string.base,
862 		    obj->value.string.length + 1);
863 }
864 
865 isc_boolean_t
866 cfg_obj_isstring(const cfg_obj_t *obj) {
867 	REQUIRE(obj != NULL);
868 	return (ISC_TF(obj->type->rep == &cfg_rep_string));
869 }
870 
871 const char *
872 cfg_obj_asstring(const cfg_obj_t *obj) {
873 	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_string);
874 	return (obj->value.string.base);
875 }
876 
877 /* Quoted string only */
878 cfg_type_t cfg_type_qstring = {
879 	"quoted_string", cfg_parse_qstring, print_qstring, cfg_doc_terminal,
880 	&cfg_rep_string, NULL
881 };
882 
883 /* Unquoted string only */
884 cfg_type_t cfg_type_ustring = {
885 	"string", parse_ustring, cfg_print_ustring, cfg_doc_terminal,
886 	&cfg_rep_string, NULL
887 };
888 
889 /* Any string (quoted or unquoted); printed with quotes */
890 cfg_type_t cfg_type_astring = {
891 	"string", cfg_parse_astring, print_qstring, cfg_doc_terminal,
892 	&cfg_rep_string, NULL
893 };
894 
895 /*
896  * Any string (quoted or unquoted); printed with quotes.
897  * If CFG_PRINTER_XKEY is set when printing the string will be '?' out.
898  */
899 cfg_type_t cfg_type_sstring = {
900 	"string", cfg_parse_sstring, print_sstring, cfg_doc_terminal,
901 	&cfg_rep_string, NULL
902 };
903 
904 /*
905  * Booleans
906  */
907 
908 isc_boolean_t
909 cfg_obj_isboolean(const cfg_obj_t *obj) {
910 	REQUIRE(obj != NULL);
911 	return (ISC_TF(obj->type->rep == &cfg_rep_boolean));
912 }
913 
914 isc_boolean_t
915 cfg_obj_asboolean(const cfg_obj_t *obj) {
916 	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_boolean);
917 	return (obj->value.boolean);
918 }
919 
920 isc_result_t
921 cfg_parse_boolean(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
922 {
923 	isc_result_t result;
924 	isc_boolean_t value;
925 	cfg_obj_t *obj = NULL;
926 	UNUSED(type);
927 
928 	result = cfg_gettoken(pctx, 0);
929 	if (result != ISC_R_SUCCESS)
930 		return (result);
931 
932 	if (pctx->token.type != isc_tokentype_string)
933 		goto bad_boolean;
934 
935 	if ((strcasecmp(TOKEN_STRING(pctx), "true") == 0) ||
936 	    (strcasecmp(TOKEN_STRING(pctx), "yes") == 0) ||
937 	    (strcmp(TOKEN_STRING(pctx), "1") == 0)) {
938 		value = ISC_TRUE;
939 	} else if ((strcasecmp(TOKEN_STRING(pctx), "false") == 0) ||
940 		   (strcasecmp(TOKEN_STRING(pctx), "no") == 0) ||
941 		   (strcmp(TOKEN_STRING(pctx), "0") == 0)) {
942 		value = ISC_FALSE;
943 	} else {
944 		goto bad_boolean;
945 	}
946 
947 	CHECK(cfg_create_obj(pctx, &cfg_type_boolean, &obj));
948 	obj->value.boolean = value;
949 	*ret = obj;
950 	return (result);
951 
952  bad_boolean:
953 	cfg_parser_error(pctx, CFG_LOG_NEAR, "boolean expected");
954 	return (ISC_R_UNEXPECTEDTOKEN);
955 
956  cleanup:
957 	return (result);
958 }
959 
960 void
961 cfg_print_boolean(cfg_printer_t *pctx, const cfg_obj_t *obj) {
962 	if (obj->value.boolean)
963 		cfg_print_chars(pctx, "yes", 3);
964 	else
965 		cfg_print_chars(pctx, "no", 2);
966 }
967 
968 cfg_type_t cfg_type_boolean = {
969 	"boolean", cfg_parse_boolean, cfg_print_boolean, cfg_doc_terminal,
970 	&cfg_rep_boolean, NULL
971 };
972 
973 /*
974  * Lists.
975  */
976 
977 isc_result_t
978 cfg_create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **obj) {
979 	isc_result_t result;
980 	CHECK(cfg_create_obj(pctx, type, obj));
981 	ISC_LIST_INIT((*obj)->value.list);
982  cleanup:
983 	return (result);
984 }
985 
986 static isc_result_t
987 create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp) {
988 	cfg_listelt_t *elt;
989 	elt = isc_mem_get(pctx->mctx, sizeof(*elt));
990 	if (elt == NULL)
991 		return (ISC_R_NOMEMORY);
992 	elt->obj = NULL;
993 	ISC_LINK_INIT(elt, link);
994 	*eltp = elt;
995 	return (ISC_R_SUCCESS);
996 }
997 
998 static void
999 free_list_elt(cfg_parser_t *pctx, cfg_listelt_t *elt) {
1000 	cfg_obj_destroy(pctx, &elt->obj);
1001 	isc_mem_put(pctx->mctx, elt, sizeof(*elt));
1002 }
1003 
1004 static void
1005 free_list(cfg_parser_t *pctx, cfg_obj_t *obj) {
1006 	cfg_listelt_t *elt, *next;
1007 	for (elt = ISC_LIST_HEAD(obj->value.list);
1008 	     elt != NULL;
1009 	     elt = next)
1010 	{
1011 		next = ISC_LIST_NEXT(elt, link);
1012 		free_list_elt(pctx, elt);
1013 	}
1014 }
1015 
1016 isc_result_t
1017 cfg_parse_listelt(cfg_parser_t *pctx, const cfg_type_t *elttype,
1018 		  cfg_listelt_t **ret)
1019 {
1020 	isc_result_t result;
1021 	cfg_listelt_t *elt = NULL;
1022 	cfg_obj_t *value = NULL;
1023 
1024 	CHECK(create_listelt(pctx, &elt));
1025 
1026 	result = cfg_parse_obj(pctx, elttype, &value);
1027 	if (result != ISC_R_SUCCESS)
1028 		goto cleanup;
1029 
1030 	elt->obj = value;
1031 
1032 	*ret = elt;
1033 	return (ISC_R_SUCCESS);
1034 
1035  cleanup:
1036 	isc_mem_put(pctx->mctx, elt, sizeof(*elt));
1037 	return (result);
1038 }
1039 
1040 /*
1041  * Parse a homogeneous list whose elements are of type 'elttype'
1042  * and where each element is terminated by a semicolon.
1043  */
1044 static isc_result_t
1045 parse_list(cfg_parser_t *pctx, const cfg_type_t *listtype, cfg_obj_t **ret)
1046 {
1047 	cfg_obj_t *listobj = NULL;
1048 	const cfg_type_t *listof = listtype->of;
1049 	isc_result_t result;
1050 	cfg_listelt_t *elt = NULL;
1051 
1052 	CHECK(cfg_create_list(pctx, listtype, &listobj));
1053 
1054 	for (;;) {
1055 		CHECK(cfg_peektoken(pctx, 0));
1056 		if (pctx->token.type == isc_tokentype_special &&
1057 		    pctx->token.value.as_char == /*{*/ '}')
1058 			break;
1059 		CHECK(cfg_parse_listelt(pctx, listof, &elt));
1060 		CHECK(parse_semicolon(pctx));
1061 		ISC_LIST_APPEND(listobj->value.list, elt, link);
1062 		elt = NULL;
1063 	}
1064 	*ret = listobj;
1065 	return (ISC_R_SUCCESS);
1066 
1067  cleanup:
1068 	if (elt != NULL)
1069 		free_list_elt(pctx, elt);
1070 	CLEANUP_OBJ(listobj);
1071 	return (result);
1072 }
1073 
1074 static void
1075 print_list(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1076 	const cfg_list_t *list = &obj->value.list;
1077 	const cfg_listelt_t *elt;
1078 
1079 	for (elt = ISC_LIST_HEAD(*list);
1080 	     elt != NULL;
1081 	     elt = ISC_LIST_NEXT(elt, link)) {
1082 		print_indent(pctx);
1083 		cfg_print_obj(pctx, elt->obj);
1084 		cfg_print_chars(pctx, ";\n", 2);
1085 	}
1086 }
1087 
1088 isc_result_t
1089 cfg_parse_bracketed_list(cfg_parser_t *pctx, const cfg_type_t *type,
1090 		     cfg_obj_t **ret)
1091 {
1092 	isc_result_t result;
1093 	CHECK(cfg_parse_special(pctx, '{'));
1094 	CHECK(parse_list(pctx, type, ret));
1095 	CHECK(cfg_parse_special(pctx, '}'));
1096  cleanup:
1097 	return (result);
1098 }
1099 
1100 void
1101 cfg_print_bracketed_list(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1102 	print_open(pctx);
1103 	print_list(pctx, obj);
1104 	print_close(pctx);
1105 }
1106 
1107 void
1108 cfg_doc_bracketed_list(cfg_printer_t *pctx, const cfg_type_t *type) {
1109 	cfg_print_chars(pctx, "{ ", 2);
1110 	cfg_doc_obj(pctx, type->of);
1111 	cfg_print_chars(pctx, "; ... }", 7);
1112 }
1113 
1114 /*
1115  * Parse a homogeneous list whose elements are of type 'elttype'
1116  * and where elements are separated by space.  The list ends
1117  * before the first semicolon.
1118  */
1119 isc_result_t
1120 cfg_parse_spacelist(cfg_parser_t *pctx, const cfg_type_t *listtype,
1121 		    cfg_obj_t **ret)
1122 {
1123 	cfg_obj_t *listobj = NULL;
1124 	const cfg_type_t *listof = listtype->of;
1125 	isc_result_t result;
1126 
1127 	CHECK(cfg_create_list(pctx, listtype, &listobj));
1128 
1129 	for (;;) {
1130 		cfg_listelt_t *elt = NULL;
1131 
1132 		CHECK(cfg_peektoken(pctx, 0));
1133 		if (pctx->token.type == isc_tokentype_special &&
1134 		    pctx->token.value.as_char == ';')
1135 			break;
1136 		CHECK(cfg_parse_listelt(pctx, listof, &elt));
1137 		ISC_LIST_APPEND(listobj->value.list, elt, link);
1138 	}
1139 	*ret = listobj;
1140 	return (ISC_R_SUCCESS);
1141 
1142  cleanup:
1143 	CLEANUP_OBJ(listobj);
1144 	return (result);
1145 }
1146 
1147 void
1148 cfg_print_spacelist(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1149 	const cfg_list_t *list = &obj->value.list;
1150 	const cfg_listelt_t *elt;
1151 
1152 	for (elt = ISC_LIST_HEAD(*list);
1153 	     elt != NULL;
1154 	     elt = ISC_LIST_NEXT(elt, link)) {
1155 		cfg_print_obj(pctx, elt->obj);
1156 		if (ISC_LIST_NEXT(elt, link) != NULL)
1157 			cfg_print_chars(pctx, " ", 1);
1158 	}
1159 }
1160 
1161 isc_boolean_t
1162 cfg_obj_islist(const cfg_obj_t *obj) {
1163 	REQUIRE(obj != NULL);
1164 	return (ISC_TF(obj->type->rep == &cfg_rep_list));
1165 }
1166 
1167 const cfg_listelt_t *
1168 cfg_list_first(const cfg_obj_t *obj) {
1169 	REQUIRE(obj == NULL || obj->type->rep == &cfg_rep_list);
1170 	if (obj == NULL)
1171 		return (NULL);
1172 	return (ISC_LIST_HEAD(obj->value.list));
1173 }
1174 
1175 const cfg_listelt_t *
1176 cfg_list_next(const cfg_listelt_t *elt) {
1177 	REQUIRE(elt != NULL);
1178 	return (ISC_LIST_NEXT(elt, link));
1179 }
1180 
1181 /*
1182  * Return the length of a list object.  If obj is NULL or is not
1183  * a list, return 0.
1184  */
1185 unsigned int
1186 cfg_list_length(const cfg_obj_t *obj, isc_boolean_t recurse) {
1187 	const cfg_listelt_t *elt;
1188 	unsigned int count = 0;
1189 
1190 	if (obj == NULL || !cfg_obj_islist(obj))
1191 		return (0U);
1192 	for (elt = cfg_list_first(obj);
1193 	     elt != NULL;
1194 	     elt = cfg_list_next(elt)) {
1195 		if (recurse && cfg_obj_islist(elt->obj)) {
1196 			count += cfg_list_length(elt->obj, recurse);
1197 		} else {
1198 			count++;
1199 		}
1200 	}
1201 	return (count);
1202 }
1203 
1204 cfg_obj_t *
1205 cfg_listelt_value(const cfg_listelt_t *elt) {
1206 	REQUIRE(elt != NULL);
1207 	return (elt->obj);
1208 }
1209 
1210 /*
1211  * Maps.
1212  */
1213 
1214 /*
1215  * Parse a map body.  That's something like
1216  *
1217  *   "foo 1; bar { glub; }; zap true; zap false;"
1218  *
1219  * i.e., a sequence of option names followed by values and
1220  * terminated by semicolons.  Used for the top level of
1221  * the named.conf syntax, as well as for the body of the
1222  * options, view, zone, and other statements.
1223  */
1224 isc_result_t
1225 cfg_parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
1226 {
1227 	const cfg_clausedef_t * const *clausesets = type->of;
1228 	isc_result_t result;
1229 	const cfg_clausedef_t * const *clauseset;
1230 	const cfg_clausedef_t *clause;
1231 	cfg_obj_t *value = NULL;
1232 	cfg_obj_t *obj = NULL;
1233 	cfg_obj_t *eltobj = NULL;
1234 	cfg_obj_t *includename = NULL;
1235 	isc_symvalue_t symval;
1236 	cfg_list_t *list = NULL;
1237 
1238 	CHECK(create_map(pctx, type, &obj));
1239 
1240 	obj->value.map.clausesets = clausesets;
1241 
1242 	for (;;) {
1243 		cfg_listelt_t *elt;
1244 
1245 	redo:
1246 		/*
1247 		 * Parse the option name and see if it is known.
1248 		 */
1249 		CHECK(cfg_gettoken(pctx, 0));
1250 
1251 		if (pctx->token.type != isc_tokentype_string) {
1252 			cfg_ungettoken(pctx);
1253 			break;
1254 		}
1255 
1256 		/*
1257 		 * We accept "include" statements wherever a map body
1258 		 * clause can occur.
1259 		 */
1260 		if (strcasecmp(TOKEN_STRING(pctx), "include") == 0) {
1261 			/*
1262 			 * Turn the file name into a temporary configuration
1263 			 * object just so that it is not overwritten by the
1264 			 * semicolon token.
1265 			 */
1266 			CHECK(cfg_parse_obj(pctx, &cfg_type_qstring, &includename));
1267 			CHECK(parse_semicolon(pctx));
1268 			CHECK(parser_openfile(pctx, includename->
1269 					      value.string.base));
1270 			 cfg_obj_destroy(pctx, &includename);
1271 			 goto redo;
1272 		}
1273 
1274 		clause = NULL;
1275 		for (clauseset = clausesets; *clauseset != NULL; clauseset++) {
1276 			for (clause = *clauseset;
1277 			     clause->name != NULL;
1278 			     clause++) {
1279 				if (strcasecmp(TOKEN_STRING(pctx),
1280 					   clause->name) == 0)
1281 					goto done;
1282 			}
1283 		}
1284 	done:
1285 		if (clause == NULL || clause->name == NULL) {
1286 			cfg_parser_error(pctx, CFG_LOG_NOPREP, "unknown option");
1287 			/*
1288 			 * Try to recover by parsing this option as an unknown
1289 			 * option and discarding it.
1290 			 */
1291 			CHECK(cfg_parse_obj(pctx, &cfg_type_unsupported, &eltobj));
1292 			cfg_obj_destroy(pctx, &eltobj);
1293 			CHECK(parse_semicolon(pctx));
1294 			continue;
1295 		}
1296 
1297 		/* Clause is known. */
1298 
1299 		/* Issue warnings if appropriate */
1300 		if ((clause->flags & CFG_CLAUSEFLAG_OBSOLETE) != 0)
1301 			cfg_parser_warning(pctx, 0, "option '%s' is obsolete",
1302 				       clause->name);
1303 		if ((clause->flags & CFG_CLAUSEFLAG_NOTIMP) != 0)
1304 			cfg_parser_warning(pctx, 0, "option '%s' is "
1305 				       "not implemented", clause->name);
1306 		if ((clause->flags & CFG_CLAUSEFLAG_NYI) != 0)
1307 			cfg_parser_warning(pctx, 0, "option '%s' is "
1308 				       "not implemented", clause->name);
1309 
1310 		if ((clause->flags & CFG_CLAUSEFLAG_NOTCONFIGURED) != 0) {
1311 			cfg_parser_warning(pctx, 0, "option '%s' was not "
1312 					   "enabled at compile time",
1313 					   clause->name);
1314 			result = ISC_R_FAILURE;
1315 			goto cleanup;
1316 		}
1317 
1318 		/*
1319 		 * Don't log options with CFG_CLAUSEFLAG_NEWDEFAULT
1320 		 * set here - we need to log the *lack* of such an option,
1321 		 * not its presence.
1322 		 */
1323 
1324 		/* See if the clause already has a value; if not create one. */
1325 		result = isc_symtab_lookup(obj->value.map.symtab,
1326 					   clause->name, 0, &symval);
1327 
1328 		if ((clause->flags & CFG_CLAUSEFLAG_MULTI) != 0) {
1329 			/* Multivalued clause */
1330 			cfg_obj_t *listobj = NULL;
1331 			if (result == ISC_R_NOTFOUND) {
1332 				CHECK(cfg_create_list(pctx,
1333 						  &cfg_type_implicitlist,
1334 						  &listobj));
1335 				symval.as_pointer = listobj;
1336 				result = isc_symtab_define(obj->value.
1337 						   map.symtab,
1338 						   clause->name,
1339 						   1, symval,
1340 						   isc_symexists_reject);
1341 				if (result != ISC_R_SUCCESS) {
1342 					cfg_parser_error(pctx, CFG_LOG_NEAR,
1343 						     "isc_symtab_define(%s) "
1344 						     "failed", clause->name);
1345 					isc_mem_put(pctx->mctx, list,
1346 						    sizeof(cfg_list_t));
1347 					goto cleanup;
1348 				}
1349 			} else {
1350 				INSIST(result == ISC_R_SUCCESS);
1351 				listobj = symval.as_pointer;
1352 			}
1353 
1354 			elt = NULL;
1355 			CHECK(cfg_parse_listelt(pctx, clause->type, &elt));
1356 			CHECK(parse_semicolon(pctx));
1357 
1358 			ISC_LIST_APPEND(listobj->value.list, elt, link);
1359 		} else {
1360 			/* Single-valued clause */
1361 			if (result == ISC_R_NOTFOUND) {
1362 				isc_boolean_t callback =
1363 					ISC_TF((clause->flags &
1364 						CFG_CLAUSEFLAG_CALLBACK) != 0);
1365 				CHECK(parse_symtab_elt(pctx, clause->name,
1366 						       clause->type,
1367 						       obj->value.map.symtab,
1368 						       callback));
1369 				CHECK(parse_semicolon(pctx));
1370 			} else if (result == ISC_R_SUCCESS) {
1371 				cfg_parser_error(pctx, CFG_LOG_NEAR, "'%s' redefined",
1372 					     clause->name);
1373 				result = ISC_R_EXISTS;
1374 				goto cleanup;
1375 			} else {
1376 				cfg_parser_error(pctx, CFG_LOG_NEAR,
1377 					     "isc_symtab_define() failed");
1378 				goto cleanup;
1379 			}
1380 		}
1381 	}
1382 
1383 
1384 	*ret = obj;
1385 	return (ISC_R_SUCCESS);
1386 
1387  cleanup:
1388 	CLEANUP_OBJ(value);
1389 	CLEANUP_OBJ(obj);
1390 	CLEANUP_OBJ(eltobj);
1391 	CLEANUP_OBJ(includename);
1392 	return (result);
1393 }
1394 
1395 static isc_result_t
1396 parse_symtab_elt(cfg_parser_t *pctx, const char *name,
1397 		 cfg_type_t *elttype, isc_symtab_t *symtab,
1398 		 isc_boolean_t callback)
1399 {
1400 	isc_result_t result;
1401 	cfg_obj_t *obj = NULL;
1402 	isc_symvalue_t symval;
1403 
1404 	CHECK(cfg_parse_obj(pctx, elttype, &obj));
1405 
1406 	if (callback && pctx->callback != NULL)
1407 		CHECK(pctx->callback(name, obj, pctx->callbackarg));
1408 
1409 	symval.as_pointer = obj;
1410 	CHECK(isc_symtab_define(symtab, name,
1411 				1, symval,
1412 				isc_symexists_reject));
1413 	return (ISC_R_SUCCESS);
1414 
1415  cleanup:
1416 	CLEANUP_OBJ(obj);
1417 	return (result);
1418 }
1419 
1420 /*
1421  * Parse a map; e.g., "{ foo 1; bar { glub; }; zap true; zap false; }"
1422  */
1423 isc_result_t
1424 cfg_parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1425 	isc_result_t result;
1426 	CHECK(cfg_parse_special(pctx, '{'));
1427 	CHECK(cfg_parse_mapbody(pctx, type, ret));
1428 	CHECK(cfg_parse_special(pctx, '}'));
1429  cleanup:
1430 	return (result);
1431 }
1432 
1433 /*
1434  * Subroutine for cfg_parse_named_map() and cfg_parse_addressed_map().
1435  */
1436 static isc_result_t
1437 parse_any_named_map(cfg_parser_t *pctx, cfg_type_t *nametype, const cfg_type_t *type,
1438 		    cfg_obj_t **ret)
1439 {
1440 	isc_result_t result;
1441 	cfg_obj_t *idobj = NULL;
1442 	cfg_obj_t *mapobj = NULL;
1443 
1444 	CHECK(cfg_parse_obj(pctx, nametype, &idobj));
1445 	CHECK(cfg_parse_map(pctx, type, &mapobj));
1446 	mapobj->value.map.id = idobj;
1447 	idobj = NULL;
1448 	*ret = mapobj;
1449  cleanup:
1450 	CLEANUP_OBJ(idobj);
1451 	return (result);
1452 }
1453 
1454 /*
1455  * Parse a map identified by a string name.  E.g., "name { foo 1; }".
1456  * Used for the "key" and "channel" statements.
1457  */
1458 isc_result_t
1459 cfg_parse_named_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1460 	return (parse_any_named_map(pctx, &cfg_type_astring, type, ret));
1461 }
1462 
1463 /*
1464  * Parse a map identified by a network address.
1465  * Used to be used for the "server" statement.
1466  */
1467 isc_result_t
1468 cfg_parse_addressed_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1469 	return (parse_any_named_map(pctx, &cfg_type_netaddr, type, ret));
1470 }
1471 
1472 /*
1473  * Parse a map identified by a network prefix.
1474  * Used for the "server" statement.
1475  */
1476 isc_result_t
1477 cfg_parse_netprefix_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1478 	return (parse_any_named_map(pctx, &cfg_type_netprefix, type, ret));
1479 }
1480 
1481 void
1482 cfg_print_mapbody(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1483 	isc_result_t result = ISC_R_SUCCESS;
1484 
1485 	const cfg_clausedef_t * const *clauseset;
1486 
1487 	for (clauseset = obj->value.map.clausesets;
1488 	     *clauseset != NULL;
1489 	     clauseset++)
1490 	{
1491 		isc_symvalue_t symval;
1492 		const cfg_clausedef_t *clause;
1493 
1494 		for (clause = *clauseset;
1495 		     clause->name != NULL;
1496 		     clause++) {
1497 			result = isc_symtab_lookup(obj->value.map.symtab,
1498 						   clause->name, 0, &symval);
1499 			if (result == ISC_R_SUCCESS) {
1500 				cfg_obj_t *symobj = symval.as_pointer;
1501 				if (symobj->type == &cfg_type_implicitlist) {
1502 					/* Multivalued. */
1503 					cfg_list_t *list = &symobj->value.list;
1504 					cfg_listelt_t *elt;
1505 					for (elt = ISC_LIST_HEAD(*list);
1506 					     elt != NULL;
1507 					     elt = ISC_LIST_NEXT(elt, link)) {
1508 						print_indent(pctx);
1509 						cfg_print_cstr(pctx, clause->name);
1510 						cfg_print_chars(pctx, " ", 1);
1511 						cfg_print_obj(pctx, elt->obj);
1512 						cfg_print_chars(pctx, ";\n", 2);
1513 					}
1514 				} else {
1515 					/* Single-valued. */
1516 					print_indent(pctx);
1517 					cfg_print_cstr(pctx, clause->name);
1518 					cfg_print_chars(pctx, " ", 1);
1519 					cfg_print_obj(pctx, symobj);
1520 					cfg_print_chars(pctx, ";\n", 2);
1521 				}
1522 			} else if (result == ISC_R_NOTFOUND) {
1523 				; /* do nothing */
1524 			} else {
1525 				INSIST(0);
1526 			}
1527 		}
1528 	}
1529 }
1530 
1531 void
1532 cfg_doc_mapbody(cfg_printer_t *pctx, const cfg_type_t *type) {
1533 	const cfg_clausedef_t * const *clauseset;
1534 	const cfg_clausedef_t *clause;
1535 
1536 	for (clauseset = type->of; *clauseset != NULL; clauseset++) {
1537 		for (clause = *clauseset;
1538 		     clause->name != NULL;
1539 		     clause++) {
1540 			cfg_print_cstr(pctx, clause->name);
1541 			cfg_print_chars(pctx, " ", 1);
1542 			cfg_doc_obj(pctx, clause->type);
1543 			cfg_print_chars(pctx, ";", 1);
1544 			/* XXX print flags here? */
1545 			cfg_print_chars(pctx, "\n\n", 2);
1546 		}
1547 	}
1548 }
1549 
1550 static struct flagtext {
1551 	unsigned int flag;
1552 	const char *text;
1553 } flagtexts[] = {
1554 	{ CFG_CLAUSEFLAG_NOTIMP, "not implemented" },
1555 	{ CFG_CLAUSEFLAG_NYI, "not yet implemented" },
1556 	{ CFG_CLAUSEFLAG_OBSOLETE, "obsolete" },
1557 	{ CFG_CLAUSEFLAG_NEWDEFAULT, "default changed" },
1558 	{ CFG_CLAUSEFLAG_TESTONLY, "test only" },
1559 	{ CFG_CLAUSEFLAG_NOTCONFIGURED, "not configured" },
1560 	{ 0, NULL }
1561 };
1562 
1563 void
1564 cfg_print_map(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1565 	if (obj->value.map.id != NULL) {
1566 		cfg_print_obj(pctx, obj->value.map.id);
1567 		cfg_print_chars(pctx, " ", 1);
1568 	}
1569 	print_open(pctx);
1570 	cfg_print_mapbody(pctx, obj);
1571 	print_close(pctx);
1572 }
1573 
1574 static void
1575 print_clause_flags(cfg_printer_t *pctx, unsigned int flags) {
1576 	struct flagtext *p;
1577 	isc_boolean_t first = ISC_TRUE;
1578 	for (p = flagtexts; p->flag != 0; p++) {
1579 		if ((flags & p->flag) != 0) {
1580 			if (first)
1581 				cfg_print_chars(pctx, " // ", 4);
1582 			else
1583 				cfg_print_chars(pctx, ", ", 2);
1584 			cfg_print_cstr(pctx, p->text);
1585 			first = ISC_FALSE;
1586 		}
1587 	}
1588 }
1589 
1590 void
1591 cfg_doc_map(cfg_printer_t *pctx, const cfg_type_t *type) {
1592 	const cfg_clausedef_t * const *clauseset;
1593 	const cfg_clausedef_t *clause;
1594 
1595 	if (type->parse == cfg_parse_named_map) {
1596 		cfg_doc_obj(pctx, &cfg_type_astring);
1597 		cfg_print_chars(pctx, " ", 1);
1598 	} else if (type->parse == cfg_parse_addressed_map) {
1599 		cfg_doc_obj(pctx, &cfg_type_netaddr);
1600 		cfg_print_chars(pctx, " ", 1);
1601 	} else if (type->parse == cfg_parse_netprefix_map) {
1602 		cfg_doc_obj(pctx, &cfg_type_netprefix);
1603 		cfg_print_chars(pctx, " ", 1);
1604 	}
1605 
1606 	print_open(pctx);
1607 
1608 	for (clauseset = type->of; *clauseset != NULL; clauseset++) {
1609 		for (clause = *clauseset;
1610 		     clause->name != NULL;
1611 		     clause++) {
1612 			print_indent(pctx);
1613 			cfg_print_cstr(pctx, clause->name);
1614 			if (clause->type->print != cfg_print_void)
1615 				cfg_print_chars(pctx, " ", 1);
1616 			cfg_doc_obj(pctx, clause->type);
1617 			cfg_print_chars(pctx, ";", 1);
1618 			print_clause_flags(pctx, clause->flags);
1619 			cfg_print_chars(pctx, "\n", 1);
1620 		}
1621 	}
1622 	print_close(pctx);
1623 }
1624 
1625 isc_boolean_t
1626 cfg_obj_ismap(const cfg_obj_t *obj) {
1627 	REQUIRE(obj != NULL);
1628 	return (ISC_TF(obj->type->rep == &cfg_rep_map));
1629 }
1630 
1631 isc_result_t
1632 cfg_map_get(const cfg_obj_t *mapobj, const char* name, const cfg_obj_t **obj) {
1633 	isc_result_t result;
1634 	isc_symvalue_t val;
1635 	const cfg_map_t *map;
1636 
1637 	REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
1638 	REQUIRE(name != NULL);
1639 	REQUIRE(obj != NULL && *obj == NULL);
1640 
1641 	map = &mapobj->value.map;
1642 
1643 	result = isc_symtab_lookup(map->symtab, name, MAP_SYM, &val);
1644 	if (result != ISC_R_SUCCESS)
1645 		return (result);
1646 	*obj = val.as_pointer;
1647 	return (ISC_R_SUCCESS);
1648 }
1649 
1650 const cfg_obj_t *
1651 cfg_map_getname(const cfg_obj_t *mapobj) {
1652 	REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
1653 	return (mapobj->value.map.id);
1654 }
1655 
1656 unsigned int
1657 cfg_map_count(const cfg_obj_t *mapobj) {
1658 	const cfg_map_t *map;
1659 	REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
1660 	map = &mapobj->value.map;
1661 	return (isc_symtab_count(map->symtab));
1662 }
1663 
1664 /* Parse an arbitrary token, storing its raw text representation. */
1665 static isc_result_t
1666 parse_token(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1667 	cfg_obj_t *obj = NULL;
1668 	isc_result_t result;
1669 	isc_region_t r;
1670 
1671 	UNUSED(type);
1672 
1673 	CHECK(cfg_create_obj(pctx, &cfg_type_token, &obj));
1674 	CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
1675 	if (pctx->token.type == isc_tokentype_eof) {
1676 		cfg_ungettoken(pctx);
1677 		result = ISC_R_EOF;
1678 		goto cleanup;
1679 	}
1680 
1681 	isc_lex_getlasttokentext(pctx->lexer, &pctx->token, &r);
1682 
1683 	obj->value.string.base = isc_mem_get(pctx->mctx, r.length + 1);
1684 	if (obj->value.string.base == NULL) {
1685 		result = ISC_R_NOMEMORY;
1686 		goto cleanup;
1687 	}
1688 	obj->value.string.length = r.length;
1689 	memmove(obj->value.string.base, r.base, r.length);
1690 	obj->value.string.base[r.length] = '\0';
1691 	*ret = obj;
1692 	return (result);
1693 
1694  cleanup:
1695 	if (obj != NULL)
1696 		isc_mem_put(pctx->mctx, obj, sizeof(*obj));
1697 	return (result);
1698 }
1699 
1700 cfg_type_t cfg_type_token = {
1701 	"token", parse_token, cfg_print_ustring, cfg_doc_terminal,
1702 	&cfg_rep_string, NULL
1703 };
1704 
1705 /*
1706  * An unsupported option.  This is just a list of tokens with balanced braces
1707  * ending in a semicolon.
1708  */
1709 
1710 static isc_result_t
1711 parse_unsupported(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1712 	cfg_obj_t *listobj = NULL;
1713 	isc_result_t result;
1714 	int braces = 0;
1715 
1716 	CHECK(cfg_create_list(pctx, type, &listobj));
1717 
1718 	for (;;) {
1719 		cfg_listelt_t *elt = NULL;
1720 
1721 		CHECK(cfg_peektoken(pctx, 0));
1722 		if (pctx->token.type == isc_tokentype_special) {
1723 			if (pctx->token.value.as_char == '{')
1724 				braces++;
1725 			else if (pctx->token.value.as_char == '}')
1726 				braces--;
1727 			else if (pctx->token.value.as_char == ';')
1728 				if (braces == 0)
1729 					break;
1730 		}
1731 		if (pctx->token.type == isc_tokentype_eof || braces < 0) {
1732 			cfg_parser_error(pctx, CFG_LOG_NEAR, "unexpected token");
1733 			result = ISC_R_UNEXPECTEDTOKEN;
1734 			goto cleanup;
1735 		}
1736 
1737 		CHECK(cfg_parse_listelt(pctx, &cfg_type_token, &elt));
1738 		ISC_LIST_APPEND(listobj->value.list, elt, link);
1739 	}
1740 	INSIST(braces == 0);
1741 	*ret = listobj;
1742 	return (ISC_R_SUCCESS);
1743 
1744  cleanup:
1745 	CLEANUP_OBJ(listobj);
1746 	return (result);
1747 }
1748 
1749 cfg_type_t cfg_type_unsupported = {
1750 	"unsupported", parse_unsupported, cfg_print_spacelist, cfg_doc_terminal,
1751 	&cfg_rep_list, NULL
1752 };
1753 
1754 /*
1755  * Try interpreting the current token as a network address.
1756  *
1757  * If CFG_ADDR_WILDOK is set in flags, "*" can be used as a wildcard
1758  * and at least one of CFG_ADDR_V4OK and CFG_ADDR_V6OK must also be set.  The
1759  * "*" is interpreted as the IPv4 wildcard address if CFG_ADDR_V4OK is
1760  * set (including the case where CFG_ADDR_V4OK and CFG_ADDR_V6OK are both set),
1761  * and the IPv6 wildcard address otherwise.
1762  */
1763 static isc_result_t
1764 token_addr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
1765 	char *s;
1766 	struct in_addr in4a;
1767 	struct in6_addr in6a;
1768 
1769 	if (pctx->token.type != isc_tokentype_string)
1770 		return (ISC_R_UNEXPECTEDTOKEN);
1771 
1772 	s = TOKEN_STRING(pctx);
1773 	if ((flags & CFG_ADDR_WILDOK) != 0 && strcmp(s, "*") == 0) {
1774 		if ((flags & CFG_ADDR_V4OK) != 0) {
1775 			isc_netaddr_any(na);
1776 			return (ISC_R_SUCCESS);
1777 		} else if ((flags & CFG_ADDR_V6OK) != 0) {
1778 			isc_netaddr_any6(na);
1779 			return (ISC_R_SUCCESS);
1780 		} else {
1781 			INSIST(0);
1782 		}
1783 	} else {
1784 		if ((flags & (CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK)) != 0) {
1785 			if (inet_pton(AF_INET, s, &in4a) == 1) {
1786 				isc_netaddr_fromin(na, &in4a);
1787 				return (ISC_R_SUCCESS);
1788 			}
1789 		}
1790 		if ((flags & CFG_ADDR_V4PREFIXOK) != 0 &&
1791 		    strlen(s) <= 15U) {
1792 			char buf[64];
1793 			int i;
1794 
1795 			strcpy(buf, s);
1796 			for (i = 0; i < 3; i++) {
1797 				strcat(buf, ".0");
1798 				if (inet_pton(AF_INET, buf, &in4a) == 1) {
1799 					isc_netaddr_fromin(na, &in4a);
1800 					return (ISC_R_SUCCESS);
1801 				}
1802 			}
1803 		}
1804 		if ((flags & CFG_ADDR_V6OK) != 0 &&
1805 		    strlen(s) <= 127U) {
1806 			char buf[128]; /* see lib/bind9/getaddresses.c */
1807 			char *d; /* zone delimiter */
1808 			isc_uint32_t zone = 0; /* scope zone ID */
1809 
1810 			strcpy(buf, s);
1811 			d = strchr(buf, '%');
1812 			if (d != NULL)
1813 				*d = '\0';
1814 
1815 			if (inet_pton(AF_INET6, buf, &in6a) == 1) {
1816 				if (d != NULL) {
1817 #ifdef ISC_PLATFORM_HAVESCOPEID
1818 					isc_result_t result;
1819 
1820 					result = isc_netscope_pton(AF_INET6,
1821 								   d + 1,
1822 								   &in6a,
1823 								   &zone);
1824 					if (result != ISC_R_SUCCESS)
1825 						return (result);
1826 #else
1827 				return (ISC_R_BADADDRESSFORM);
1828 #endif
1829 				}
1830 
1831 				isc_netaddr_fromin6(na, &in6a);
1832 				isc_netaddr_setzone(na, zone);
1833 				return (ISC_R_SUCCESS);
1834 			}
1835 		}
1836 	}
1837 	return (ISC_R_UNEXPECTEDTOKEN);
1838 }
1839 
1840 isc_result_t
1841 cfg_parse_rawaddr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
1842 	isc_result_t result;
1843 	const char *wild = "";
1844 	const char *prefix = "";
1845 
1846 	CHECK(cfg_gettoken(pctx, 0));
1847 	result = token_addr(pctx, flags, na);
1848 	if (result == ISC_R_UNEXPECTEDTOKEN) {
1849 		if ((flags & CFG_ADDR_WILDOK) != 0)
1850 			wild = " or '*'";
1851 		if ((flags & CFG_ADDR_V4PREFIXOK) != 0)
1852 			wild = " or IPv4 prefix";
1853 		if ((flags & CFG_ADDR_MASK) == CFG_ADDR_V4OK)
1854 			cfg_parser_error(pctx, CFG_LOG_NEAR,
1855 					 "expected IPv4 address%s%s",
1856 					 prefix, wild);
1857 		else if ((flags & CFG_ADDR_MASK) == CFG_ADDR_V6OK)
1858 			cfg_parser_error(pctx, CFG_LOG_NEAR,
1859 					 "expected IPv6 address%s%s",
1860 					 prefix, wild);
1861 		else
1862 			cfg_parser_error(pctx, CFG_LOG_NEAR,
1863 					 "expected IP address%s%s",
1864 					 prefix, wild);
1865 	}
1866  cleanup:
1867 	return (result);
1868 }
1869 
1870 isc_boolean_t
1871 cfg_lookingat_netaddr(cfg_parser_t *pctx, unsigned int flags) {
1872 	isc_result_t result;
1873 	isc_netaddr_t na_dummy;
1874 	result = token_addr(pctx, flags, &na_dummy);
1875 	return (ISC_TF(result == ISC_R_SUCCESS));
1876 }
1877 
1878 isc_result_t
1879 cfg_parse_rawport(cfg_parser_t *pctx, unsigned int flags, in_port_t *port) {
1880 	isc_result_t result;
1881 
1882 	CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
1883 
1884 	if ((flags & CFG_ADDR_WILDOK) != 0 &&
1885 	    pctx->token.type == isc_tokentype_string &&
1886 	    strcmp(TOKEN_STRING(pctx), "*") == 0) {
1887 		*port = 0;
1888 		return (ISC_R_SUCCESS);
1889 	}
1890 	if (pctx->token.type != isc_tokentype_number) {
1891 		cfg_parser_error(pctx, CFG_LOG_NEAR,
1892 			     "expected port number or '*'");
1893 		return (ISC_R_UNEXPECTEDTOKEN);
1894 	}
1895 	if (pctx->token.value.as_ulong >= 65536U) {
1896 		cfg_parser_error(pctx, CFG_LOG_NEAR,
1897 			     "port number out of range");
1898 		return (ISC_R_UNEXPECTEDTOKEN);
1899 	}
1900 	*port = (in_port_t)(pctx->token.value.as_ulong);
1901 	return (ISC_R_SUCCESS);
1902  cleanup:
1903 	return (result);
1904 }
1905 
1906 void
1907 cfg_print_rawaddr(cfg_printer_t *pctx, const isc_netaddr_t *na) {
1908 	isc_result_t result;
1909 	char text[128];
1910 	isc_buffer_t buf;
1911 
1912 	isc_buffer_init(&buf, text, sizeof(text));
1913 	result = isc_netaddr_totext(na, &buf);
1914 	RUNTIME_CHECK(result == ISC_R_SUCCESS);
1915 	cfg_print_chars(pctx, isc_buffer_base(&buf), isc_buffer_usedlength(&buf));
1916 }
1917 
1918 isc_result_t
1919 cfg_parse_dscp(cfg_parser_t *pctx, isc_dscp_t *dscp) {
1920 	isc_result_t result;
1921 
1922 	CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
1923 
1924 	if (pctx->token.type != isc_tokentype_number) {
1925 		cfg_parser_error(pctx, CFG_LOG_NEAR,
1926 			     "expected number");
1927 		return (ISC_R_UNEXPECTEDTOKEN);
1928 	}
1929 	if (pctx->token.value.as_ulong > 63U) {
1930 		cfg_parser_error(pctx, CFG_LOG_NEAR,
1931 			     "dscp out of range");
1932 		return (ISC_R_RANGE);
1933 	}
1934 	*dscp = (isc_dscp_t)(pctx->token.value.as_ulong);
1935 	return (ISC_R_SUCCESS);
1936  cleanup:
1937 	return (result);
1938 }
1939 
1940 /* netaddr */
1941 
1942 static unsigned int netaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK;
1943 static unsigned int netaddr4_flags = CFG_ADDR_V4OK;
1944 static unsigned int netaddr4wild_flags = CFG_ADDR_V4OK | CFG_ADDR_WILDOK;
1945 static unsigned int netaddr6_flags = CFG_ADDR_V6OK;
1946 static unsigned int netaddr6wild_flags = CFG_ADDR_V6OK | CFG_ADDR_WILDOK;
1947 
1948 static isc_result_t
1949 parse_netaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1950 	isc_result_t result;
1951 	cfg_obj_t *obj = NULL;
1952 	isc_netaddr_t netaddr;
1953 	unsigned int flags = *(const unsigned int *)type->of;
1954 
1955 	CHECK(cfg_create_obj(pctx, type, &obj));
1956 	CHECK(cfg_parse_rawaddr(pctx, flags, &netaddr));
1957 	isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, 0);
1958 	*ret = obj;
1959 	return (ISC_R_SUCCESS);
1960  cleanup:
1961 	CLEANUP_OBJ(obj);
1962 	return (result);
1963 }
1964 
1965 static void
1966 cfg_doc_netaddr(cfg_printer_t *pctx, const cfg_type_t *type) {
1967 	const unsigned int *flagp = type->of;
1968 	int n = 0;
1969 	if (*flagp != CFG_ADDR_V4OK && *flagp != CFG_ADDR_V6OK)
1970 		cfg_print_chars(pctx, "( ", 2);
1971 	if (*flagp & CFG_ADDR_V4OK) {
1972 		cfg_print_cstr(pctx, "<ipv4_address>");
1973 		n++;
1974 	}
1975 	if (*flagp & CFG_ADDR_V6OK) {
1976 		if (n != 0)
1977 			cfg_print_chars(pctx, " | ", 3);
1978 		cfg_print_cstr(pctx, "<ipv6_address>");
1979 		n++;
1980 	}
1981 	if (*flagp & CFG_ADDR_WILDOK) {
1982 		if (n != 0)
1983 			cfg_print_chars(pctx, " | ", 3);
1984 		cfg_print_chars(pctx, "*", 1);
1985 		n++;
1986 		POST(n);
1987 	}
1988 	if (*flagp != CFG_ADDR_V4OK && *flagp != CFG_ADDR_V6OK)
1989 		cfg_print_chars(pctx, " )", 2);
1990 }
1991 
1992 cfg_type_t cfg_type_netaddr = {
1993 	"netaddr", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
1994 	&cfg_rep_sockaddr, &netaddr_flags
1995 };
1996 
1997 cfg_type_t cfg_type_netaddr4 = {
1998 	"netaddr4", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
1999 	&cfg_rep_sockaddr, &netaddr4_flags
2000 };
2001 
2002 cfg_type_t cfg_type_netaddr4wild = {
2003 	"netaddr4wild", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
2004 	&cfg_rep_sockaddr, &netaddr4wild_flags
2005 };
2006 
2007 cfg_type_t cfg_type_netaddr6 = {
2008 	"netaddr6", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
2009 	&cfg_rep_sockaddr, &netaddr6_flags
2010 };
2011 
2012 cfg_type_t cfg_type_netaddr6wild = {
2013 	"netaddr6wild", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
2014 	&cfg_rep_sockaddr, &netaddr6wild_flags
2015 };
2016 
2017 /* netprefix */
2018 
2019 isc_result_t
2020 cfg_parse_netprefix(cfg_parser_t *pctx, const cfg_type_t *type,
2021 		    cfg_obj_t **ret)
2022 {
2023 	cfg_obj_t *obj = NULL;
2024 	isc_result_t result;
2025 	isc_netaddr_t netaddr;
2026 	unsigned int addrlen = 0, prefixlen;
2027 	UNUSED(type);
2028 
2029 	CHECK(cfg_parse_rawaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK |
2030 				CFG_ADDR_V6OK, &netaddr));
2031 	switch (netaddr.family) {
2032 	case AF_INET:
2033 		addrlen = 32;
2034 		break;
2035 	case AF_INET6:
2036 		addrlen = 128;
2037 		break;
2038 	default:
2039 		INSIST(0);
2040 		break;
2041 	}
2042 	CHECK(cfg_peektoken(pctx, 0));
2043 	if (pctx->token.type == isc_tokentype_special &&
2044 	    pctx->token.value.as_char == '/') {
2045 		CHECK(cfg_gettoken(pctx, 0)); /* read "/" */
2046 		CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
2047 		if (pctx->token.type != isc_tokentype_number) {
2048 			cfg_parser_error(pctx, CFG_LOG_NEAR,
2049 				     "expected prefix length");
2050 			return (ISC_R_UNEXPECTEDTOKEN);
2051 		}
2052 		prefixlen = pctx->token.value.as_ulong;
2053 		if (prefixlen > addrlen) {
2054 			cfg_parser_error(pctx, CFG_LOG_NOPREP,
2055 				     "invalid prefix length");
2056 			return (ISC_R_RANGE);
2057 		}
2058 	} else {
2059 		prefixlen = addrlen;
2060 	}
2061 	CHECK(cfg_create_obj(pctx, &cfg_type_netprefix, &obj));
2062 	obj->value.netprefix.address = netaddr;
2063 	obj->value.netprefix.prefixlen = prefixlen;
2064 	*ret = obj;
2065 	return (ISC_R_SUCCESS);
2066  cleanup:
2067 	cfg_parser_error(pctx, CFG_LOG_NEAR, "expected network prefix");
2068 	return (result);
2069 }
2070 
2071 static void
2072 print_netprefix(cfg_printer_t *pctx, const cfg_obj_t *obj) {
2073 	const cfg_netprefix_t *p = &obj->value.netprefix;
2074 
2075 	cfg_print_rawaddr(pctx, &p->address);
2076 	cfg_print_chars(pctx, "/", 1);
2077 	cfg_print_rawuint(pctx, p->prefixlen);
2078 }
2079 
2080 isc_boolean_t
2081 cfg_obj_isnetprefix(const cfg_obj_t *obj) {
2082 	REQUIRE(obj != NULL);
2083 	return (ISC_TF(obj->type->rep == &cfg_rep_netprefix));
2084 }
2085 
2086 void
2087 cfg_obj_asnetprefix(const cfg_obj_t *obj, isc_netaddr_t *netaddr,
2088 		    unsigned int *prefixlen)
2089 {
2090 	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_netprefix);
2091 	REQUIRE(netaddr != NULL);
2092 	REQUIRE(prefixlen != NULL);
2093 
2094 	*netaddr = obj->value.netprefix.address;
2095 	*prefixlen = obj->value.netprefix.prefixlen;
2096 }
2097 
2098 cfg_type_t cfg_type_netprefix = {
2099 	"netprefix", cfg_parse_netprefix, print_netprefix, cfg_doc_terminal,
2100 	&cfg_rep_netprefix, NULL
2101 };
2102 
2103 static isc_result_t
2104 parse_sockaddrsub(cfg_parser_t *pctx, const cfg_type_t *type,
2105 		  int flags, cfg_obj_t **ret)
2106 {
2107 	isc_result_t result;
2108 	isc_netaddr_t netaddr;
2109 	in_port_t port = 0;
2110 	isc_dscp_t dscp = -1;
2111 	cfg_obj_t *obj = NULL;
2112 	int have_port = 0, have_dscp = 0;
2113 
2114 	CHECK(cfg_create_obj(pctx, type, &obj));
2115 	CHECK(cfg_parse_rawaddr(pctx, flags, &netaddr));
2116 	for (;;) {
2117 		CHECK(cfg_peektoken(pctx, 0));
2118 		if (pctx->token.type == isc_tokentype_string) {
2119 			if (strcasecmp(TOKEN_STRING(pctx), "port") == 0) {
2120 				CHECK(cfg_gettoken(pctx, 0)); /* read "port" */
2121 				CHECK(cfg_parse_rawport(pctx, flags, &port));
2122 				++have_port;
2123 			} else if ((flags & CFG_ADDR_DSCPOK) != 0 &&
2124 				   strcasecmp(TOKEN_STRING(pctx), "dscp") == 0)
2125 			{
2126 				CHECK(cfg_gettoken(pctx, 0)); /* read "dscp" */
2127 				CHECK(cfg_parse_dscp(pctx, &dscp));
2128 				++have_dscp;
2129 			} else
2130 				break;
2131 		} else
2132 			break;
2133 	}
2134 	if (have_port > 1) {
2135 		cfg_parser_error(pctx, 0, "expected at most one port");
2136 		result = ISC_R_UNEXPECTEDTOKEN;
2137 		goto cleanup;
2138 	}
2139 
2140 	if (have_dscp > 1) {
2141 		cfg_parser_error(pctx, 0, "expected at most one dscp");
2142 		result = ISC_R_UNEXPECTEDTOKEN;
2143 		goto cleanup;
2144 	}
2145 	isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, port);
2146 	obj->value.sockaddrdscp.dscp = dscp;
2147 	*ret = obj;
2148 	return (ISC_R_SUCCESS);
2149 
2150  cleanup:
2151 	CLEANUP_OBJ(obj);
2152 	return (result);
2153 }
2154 
2155 static unsigned int sockaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK;
2156 cfg_type_t cfg_type_sockaddr = {
2157 	"sockaddr", cfg_parse_sockaddr, cfg_print_sockaddr, cfg_doc_sockaddr,
2158 	&cfg_rep_sockaddr, &sockaddr_flags
2159 };
2160 
2161 static unsigned int sockaddrdscp_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK |
2162 					 CFG_ADDR_DSCPOK;
2163 cfg_type_t cfg_type_sockaddrdscp = {
2164 	"sockaddr", cfg_parse_sockaddr, cfg_print_sockaddr, cfg_doc_sockaddr,
2165 	&cfg_rep_sockaddr, &sockaddrdscp_flags
2166 };
2167 
2168 isc_result_t
2169 cfg_parse_sockaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2170 	const unsigned int *flagp = type->of;
2171 	return (parse_sockaddrsub(pctx, &cfg_type_sockaddr, *flagp, ret));
2172 }
2173 
2174 void
2175 cfg_print_sockaddr(cfg_printer_t *pctx, const cfg_obj_t *obj) {
2176 	isc_netaddr_t netaddr;
2177 	in_port_t port;
2178 	char buf[ISC_NETADDR_FORMATSIZE];
2179 
2180 	isc_netaddr_fromsockaddr(&netaddr, &obj->value.sockaddr);
2181 	isc_netaddr_format(&netaddr, buf, sizeof(buf));
2182 	cfg_print_cstr(pctx, buf);
2183 	port = isc_sockaddr_getport(&obj->value.sockaddr);
2184 	if (port != 0) {
2185 		cfg_print_chars(pctx, " port ", 6);
2186 		cfg_print_rawuint(pctx, port);
2187 	}
2188 	if (obj->value.sockaddrdscp.dscp != -1) {
2189 		cfg_print_chars(pctx, " dscp ", 6);
2190 		cfg_print_rawuint(pctx, obj->value.sockaddrdscp.dscp);
2191 	}
2192 }
2193 
2194 void
2195 cfg_doc_sockaddr(cfg_printer_t *pctx, const cfg_type_t *type) {
2196 	const unsigned int *flagp = type->of;
2197 	int n = 0;
2198 	cfg_print_chars(pctx, "( ", 2);
2199 	if (*flagp & CFG_ADDR_V4OK) {
2200 		cfg_print_cstr(pctx, "<ipv4_address>");
2201 		n++;
2202 	}
2203 	if (*flagp & CFG_ADDR_V6OK) {
2204 		if (n != 0)
2205 			cfg_print_chars(pctx, " | ", 3);
2206 		cfg_print_cstr(pctx, "<ipv6_address>");
2207 		n++;
2208 	}
2209 	if (*flagp & CFG_ADDR_WILDOK) {
2210 		if (n != 0)
2211 			cfg_print_chars(pctx, " | ", 3);
2212 		cfg_print_chars(pctx, "*", 1);
2213 		n++;
2214 		POST(n);
2215 	}
2216 	cfg_print_chars(pctx, " ) ", 3);
2217 	if (*flagp & CFG_ADDR_WILDOK) {
2218 		cfg_print_cstr(pctx, "[ port ( <integer> | * ) ]");
2219 	} else {
2220 		cfg_print_cstr(pctx, "[ port <integer> ]");
2221 	}
2222 	if ((*flagp & CFG_ADDR_DSCPOK) != 0) {
2223 		cfg_print_cstr(pctx, " [ dscp <integer> ]");
2224 	}
2225 }
2226 
2227 isc_boolean_t
2228 cfg_obj_issockaddr(const cfg_obj_t *obj) {
2229 	REQUIRE(obj != NULL);
2230 	return (ISC_TF(obj->type->rep == &cfg_rep_sockaddr));
2231 }
2232 
2233 const isc_sockaddr_t *
2234 cfg_obj_assockaddr(const cfg_obj_t *obj) {
2235 	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_sockaddr);
2236 	return (&obj->value.sockaddr);
2237 }
2238 
2239 isc_dscp_t
2240 cfg_obj_getdscp(const cfg_obj_t *obj) {
2241 	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_sockaddr);
2242 	return (obj->value.sockaddrdscp.dscp);
2243 }
2244 
2245 isc_result_t
2246 cfg_gettoken(cfg_parser_t *pctx, int options) {
2247 	isc_result_t result;
2248 
2249 	if (pctx->seen_eof)
2250 		return (ISC_R_SUCCESS);
2251 
2252 	options |= (ISC_LEXOPT_EOF | ISC_LEXOPT_NOMORE);
2253 
2254  redo:
2255 	pctx->token.type = isc_tokentype_unknown;
2256 	result = isc_lex_gettoken(pctx->lexer, options, &pctx->token);
2257 	pctx->ungotten = ISC_FALSE;
2258 	pctx->line = isc_lex_getsourceline(pctx->lexer);
2259 
2260 	switch (result) {
2261 	case ISC_R_SUCCESS:
2262 		if (pctx->token.type == isc_tokentype_eof) {
2263 			result = isc_lex_close(pctx->lexer);
2264 			INSIST(result == ISC_R_NOMORE ||
2265 			       result == ISC_R_SUCCESS);
2266 
2267 			if (isc_lex_getsourcename(pctx->lexer) != NULL) {
2268 				/*
2269 				 * Closed an included file, not the main file.
2270 				 */
2271 				cfg_listelt_t *elt;
2272 				elt = ISC_LIST_TAIL(pctx->open_files->
2273 						    value.list);
2274 				INSIST(elt != NULL);
2275 				ISC_LIST_UNLINK(pctx->open_files->
2276 						value.list, elt, link);
2277 				ISC_LIST_APPEND(pctx->closed_files->
2278 						value.list, elt, link);
2279 				goto redo;
2280 			}
2281 			pctx->seen_eof = ISC_TRUE;
2282 		}
2283 		break;
2284 
2285 	case ISC_R_NOSPACE:
2286 		/* More understandable than "ran out of space". */
2287 		cfg_parser_error(pctx, CFG_LOG_NEAR, "token too big");
2288 		break;
2289 
2290 	case ISC_R_IOERROR:
2291 		cfg_parser_error(pctx, 0, "%s",
2292 				 isc_result_totext(result));
2293 		break;
2294 
2295 	default:
2296 		cfg_parser_error(pctx, CFG_LOG_NEAR, "%s",
2297 				 isc_result_totext(result));
2298 		break;
2299 	}
2300 	return (result);
2301 }
2302 
2303 void
2304 cfg_ungettoken(cfg_parser_t *pctx) {
2305 	if (pctx->seen_eof)
2306 		return;
2307 	isc_lex_ungettoken(pctx->lexer, &pctx->token);
2308 	pctx->ungotten = ISC_TRUE;
2309 }
2310 
2311 isc_result_t
2312 cfg_peektoken(cfg_parser_t *pctx, int options) {
2313 	isc_result_t result;
2314 	CHECK(cfg_gettoken(pctx, options));
2315 	cfg_ungettoken(pctx);
2316  cleanup:
2317 	return (result);
2318 }
2319 
2320 /*
2321  * Get a string token, accepting both the quoted and the unquoted form.
2322  * Log an error if the next token is not a string.
2323  */
2324 static isc_result_t
2325 cfg_getstringtoken(cfg_parser_t *pctx) {
2326 	isc_result_t result;
2327 
2328 	result = cfg_gettoken(pctx, CFG_LEXOPT_QSTRING);
2329 	if (result != ISC_R_SUCCESS)
2330 		return (result);
2331 
2332 	if (pctx->token.type != isc_tokentype_string &&
2333 	    pctx->token.type != isc_tokentype_qstring) {
2334 		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected string");
2335 		return (ISC_R_UNEXPECTEDTOKEN);
2336 	}
2337 	return (ISC_R_SUCCESS);
2338 }
2339 
2340 void
2341 cfg_parser_error(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
2342 	va_list args;
2343 	va_start(args, fmt);
2344 	parser_complain(pctx, ISC_FALSE, flags, fmt, args);
2345 	va_end(args);
2346 	pctx->errors++;
2347 }
2348 
2349 void
2350 cfg_parser_warning(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
2351 	va_list args;
2352 	va_start(args, fmt);
2353 	parser_complain(pctx, ISC_TRUE, flags, fmt, args);
2354 	va_end(args);
2355 	pctx->warnings++;
2356 }
2357 
2358 #define MAX_LOG_TOKEN 30 /* How much of a token to quote in log messages. */
2359 
2360 static isc_boolean_t
2361 have_current_file(cfg_parser_t *pctx) {
2362 	cfg_listelt_t *elt;
2363 	if (pctx->open_files == NULL)
2364 		return (ISC_FALSE);
2365 
2366 	elt = ISC_LIST_TAIL(pctx->open_files->value.list);
2367 	if (elt == NULL)
2368 	      return (ISC_FALSE);
2369 
2370 	return (ISC_TRUE);
2371 }
2372 
2373 static char *
2374 current_file(cfg_parser_t *pctx) {
2375 	static char none[] = "none";
2376 	cfg_listelt_t *elt;
2377 	cfg_obj_t *fileobj;
2378 
2379 	if (!have_current_file(pctx))
2380 		return (none);
2381 
2382 	elt = ISC_LIST_TAIL(pctx->open_files->value.list);
2383 	if (elt == NULL)	/* shouldn't be possible, but... */
2384 	      return (none);
2385 
2386 	fileobj = elt->obj;
2387 	INSIST(fileobj->type == &cfg_type_qstring);
2388 	return (fileobj->value.string.base);
2389 }
2390 
2391 static void
2392 parser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning,
2393 		unsigned int flags, const char *format,
2394 		va_list args)
2395 {
2396 	char tokenbuf[MAX_LOG_TOKEN + 10];
2397 	static char where[ISC_DIR_PATHMAX + 100];
2398 	static char message[2048];
2399 	int level = ISC_LOG_ERROR;
2400 	const char *prep = "";
2401 	size_t len;
2402 
2403 	if (is_warning)
2404 		level = ISC_LOG_WARNING;
2405 
2406 	where[0] = '\0';
2407 	if (have_current_file(pctx))
2408 		snprintf(where, sizeof(where), "%s:%u: ",
2409 			 current_file(pctx), pctx->line);
2410 
2411 	len = vsnprintf(message, sizeof(message), format, args);
2412 	if (len >= sizeof(message))
2413 		FATAL_ERROR(__FILE__, __LINE__,
2414 			    "error message would overflow");
2415 
2416 	if ((flags & (CFG_LOG_NEAR|CFG_LOG_BEFORE|CFG_LOG_NOPREP)) != 0) {
2417 		isc_region_t r;
2418 
2419 		if (pctx->ungotten)
2420 			(void)cfg_gettoken(pctx, 0);
2421 
2422 		if (pctx->token.type == isc_tokentype_eof) {
2423 			snprintf(tokenbuf, sizeof(tokenbuf), "end of file");
2424 		} else if (pctx->token.type == isc_tokentype_unknown) {
2425 			flags = 0;
2426 			tokenbuf[0] = '\0';
2427 		} else {
2428 			isc_lex_getlasttokentext(pctx->lexer,
2429 						 &pctx->token, &r);
2430 			if (r.length > MAX_LOG_TOKEN)
2431 				snprintf(tokenbuf, sizeof(tokenbuf),
2432 					 "'%.*s...'", MAX_LOG_TOKEN, r.base);
2433 			else
2434 				snprintf(tokenbuf, sizeof(tokenbuf),
2435 					 "'%.*s'", (int)r.length, r.base);
2436 		}
2437 
2438 		/* Choose a preposition. */
2439 		if (flags & CFG_LOG_NEAR)
2440 			prep = " near ";
2441 		else if (flags & CFG_LOG_BEFORE)
2442 			prep = " before ";
2443 		else
2444 			prep = " ";
2445 	} else {
2446 		tokenbuf[0] = '\0';
2447 	}
2448 	isc_log_write(pctx->lctx, CAT, MOD, level,
2449 		      "%s%s%s%s", where, message, prep, tokenbuf);
2450 }
2451 
2452 void
2453 cfg_obj_log(const cfg_obj_t *obj, isc_log_t *lctx, int level,
2454 	    const char *fmt, ...) {
2455 	va_list ap;
2456 	char msgbuf[2048];
2457 
2458 	if (! isc_log_wouldlog(lctx, level))
2459 		return;
2460 
2461 	va_start(ap, fmt);
2462 
2463 	vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
2464 	isc_log_write(lctx, CAT, MOD, level,
2465 		      "%s:%u: %s",
2466 		      obj->file == NULL ? "<unknown file>" : obj->file,
2467 		      obj->line, msgbuf);
2468 	va_end(ap);
2469 }
2470 
2471 const char *
2472 cfg_obj_file(const cfg_obj_t *obj) {
2473 	return (obj->file);
2474 }
2475 
2476 unsigned int
2477 cfg_obj_line(const cfg_obj_t *obj) {
2478 	return (obj->line);
2479 }
2480 
2481 isc_result_t
2482 cfg_create_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2483 	isc_result_t result;
2484 	cfg_obj_t *obj;
2485 
2486 	obj = isc_mem_get(pctx->mctx, sizeof(cfg_obj_t));
2487 	if (obj == NULL)
2488 		return (ISC_R_NOMEMORY);
2489 	obj->type = type;
2490 	obj->file = current_file(pctx);
2491 	obj->line = pctx->line;
2492 	result = isc_refcount_init(&obj->references, 1);
2493 	if (result != ISC_R_SUCCESS) {
2494 		isc_mem_put(pctx->mctx, obj, sizeof(cfg_obj_t));
2495 		return (result);
2496 	}
2497 	*ret = obj;
2498 	return (ISC_R_SUCCESS);
2499 }
2500 
2501 
2502 static void
2503 map_symtabitem_destroy(char *key, unsigned int type,
2504 		       isc_symvalue_t symval, void *userarg)
2505 {
2506 	cfg_obj_t *obj = symval.as_pointer;
2507 	cfg_parser_t *pctx = (cfg_parser_t *)userarg;
2508 
2509 	UNUSED(key);
2510 	UNUSED(type);
2511 
2512 	cfg_obj_destroy(pctx, &obj);
2513 }
2514 
2515 
2516 static isc_result_t
2517 create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2518 	isc_result_t result;
2519 	isc_symtab_t *symtab = NULL;
2520 	cfg_obj_t *obj = NULL;
2521 
2522 	CHECK(cfg_create_obj(pctx, type, &obj));
2523 	CHECK(isc_symtab_create(pctx->mctx, 5, /* XXX */
2524 				map_symtabitem_destroy,
2525 				pctx, ISC_FALSE, &symtab));
2526 	obj->value.map.symtab = symtab;
2527 	obj->value.map.id = NULL;
2528 
2529 	*ret = obj;
2530 	return (ISC_R_SUCCESS);
2531 
2532  cleanup:
2533 	if (obj != NULL)
2534 		isc_mem_put(pctx->mctx, obj, sizeof(*obj));
2535 	return (result);
2536 }
2537 
2538 static void
2539 free_map(cfg_parser_t *pctx, cfg_obj_t *obj) {
2540 	CLEANUP_OBJ(obj->value.map.id);
2541 	isc_symtab_destroy(&obj->value.map.symtab);
2542 }
2543 
2544 isc_boolean_t
2545 cfg_obj_istype(const cfg_obj_t *obj, const cfg_type_t *type) {
2546 	return (ISC_TF(obj->type == type));
2547 }
2548 
2549 /*
2550  * Destroy 'obj', a configuration object created in 'pctx'.
2551  */
2552 void
2553 cfg_obj_destroy(cfg_parser_t *pctx, cfg_obj_t **objp) {
2554 	cfg_obj_t *obj;
2555 	unsigned int refs;
2556 
2557 	REQUIRE(objp != NULL && *objp != NULL);
2558 	REQUIRE(pctx != NULL);
2559 
2560 	obj = *objp;
2561 
2562 	isc_refcount_decrement(&obj->references, &refs);
2563 	if (refs == 0) {
2564 		obj->type->rep->free(pctx, obj);
2565 		isc_refcount_destroy(&obj->references);
2566 		isc_mem_put(pctx->mctx, obj, sizeof(cfg_obj_t));
2567 	}
2568 	*objp = NULL;
2569 }
2570 
2571 void
2572 cfg_obj_attach(cfg_obj_t *src, cfg_obj_t **dest) {
2573     REQUIRE(src != NULL);
2574     REQUIRE(dest != NULL && *dest == NULL);
2575     isc_refcount_increment(&src->references, NULL);
2576     *dest = src;
2577 }
2578 
2579 static void
2580 free_noop(cfg_parser_t *pctx, cfg_obj_t *obj) {
2581 	UNUSED(pctx);
2582 	UNUSED(obj);
2583 }
2584 
2585 void
2586 cfg_doc_obj(cfg_printer_t *pctx, const cfg_type_t *type) {
2587 	type->doc(pctx, type);
2588 }
2589 
2590 void
2591 cfg_doc_terminal(cfg_printer_t *pctx, const cfg_type_t *type) {
2592 	cfg_print_chars(pctx, "<", 1);
2593 	cfg_print_cstr(pctx, type->name);
2594 	cfg_print_chars(pctx, ">", 1);
2595 }
2596 
2597 void
2598 cfg_print_grammar(const cfg_type_t *type,
2599 	void (*f)(void *closure, const char *text, int textlen),
2600 	void *closure)
2601 {
2602 	cfg_printer_t pctx;
2603 	pctx.f = f;
2604 	pctx.closure = closure;
2605 	pctx.indent = 0;
2606 	pctx.flags = 0;
2607 	cfg_doc_obj(&pctx, type);
2608 }
2609