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