1135446Strhodes/*
2262706Serwin * Copyright (C) 2004-2014  Internet Systems Consortium, Inc. ("ISC")
3135446Strhodes * Copyright (C) 2000-2003  Internet Software Consortium.
4135446Strhodes *
5193149Sdougb * Permission to use, copy, modify, and/or distribute this software for any
6135446Strhodes * purpose with or without fee is hereby granted, provided that the above
7135446Strhodes * copyright notice and this permission notice appear in all copies.
8135446Strhodes *
9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11135446Strhodes * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15135446Strhodes * PERFORMANCE OF THIS SOFTWARE.
16135446Strhodes */
17135446Strhodes
18234010Sdougb/* $Id$ */
19135446Strhodes
20170222Sdougb/*! \file */
21170222Sdougb
22135446Strhodes#include <config.h>
23135446Strhodes
24135446Strhodes#include <isc/buffer.h>
25135446Strhodes#include <isc/dir.h>
26135446Strhodes#include <isc/formatcheck.h>
27135446Strhodes#include <isc/lex.h>
28135446Strhodes#include <isc/log.h>
29135446Strhodes#include <isc/mem.h>
30135446Strhodes#include <isc/net.h>
31135446Strhodes#include <isc/netaddr.h>
32224092Sdougb#include <isc/netscope.h>
33135446Strhodes#include <isc/print.h>
34135446Strhodes#include <isc/string.h>
35135446Strhodes#include <isc/sockaddr.h>
36224092Sdougb#include <isc/symtab.h>
37135446Strhodes#include <isc/util.h>
38135446Strhodes
39135446Strhodes#include <isccfg/cfg.h>
40135446Strhodes#include <isccfg/grammar.h>
41135446Strhodes#include <isccfg/log.h>
42135446Strhodes
43135446Strhodes/* Shorthand */
44135446Strhodes#define CAT CFG_LOGCATEGORY_CONFIG
45135446Strhodes#define MOD CFG_LOGMODULE_PARSER
46135446Strhodes
47135446Strhodes#define MAP_SYM 1 	/* Unique type for isc_symtab */
48135446Strhodes
49135446Strhodes#define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base)
50135446Strhodes
51135446Strhodes/* Check a return value. */
52135446Strhodes#define CHECK(op) 						\
53193149Sdougb	do { result = (op); 					\
54135446Strhodes		if (result != ISC_R_SUCCESS) goto cleanup; 	\
55135446Strhodes	} while (0)
56135446Strhodes
57135446Strhodes/* Clean up a configuration object if non-NULL. */
58135446Strhodes#define CLEANUP_OBJ(obj) \
59135446Strhodes	do { if ((obj) != NULL) cfg_obj_destroy(pctx, &(obj)); } while (0)
60135446Strhodes
61135446Strhodes
62135446Strhodes/*
63135446Strhodes * Forward declarations of static functions.
64135446Strhodes */
65135446Strhodes
66135446Strhodesstatic void
67135446Strhodesfree_tuple(cfg_parser_t *pctx, cfg_obj_t *obj);
68135446Strhodes
69135446Strhodesstatic isc_result_t
70135446Strhodesparse_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
71135446Strhodes
72135446Strhodesstatic void
73165071Sdougbprint_list(cfg_printer_t *pctx, const cfg_obj_t *obj);
74135446Strhodes
75135446Strhodesstatic void
76135446Strhodesfree_list(cfg_parser_t *pctx, cfg_obj_t *obj);
77135446Strhodes
78135446Strhodesstatic isc_result_t
79135446Strhodescreate_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp);
80135446Strhodes
81135446Strhodesstatic isc_result_t
82135446Strhodescreate_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
83135446Strhodes	      cfg_obj_t **ret);
84135446Strhodes
85135446Strhodesstatic void
86135446Strhodesfree_string(cfg_parser_t *pctx, cfg_obj_t *obj);
87135446Strhodes
88135446Strhodesstatic isc_result_t
89135446Strhodescreate_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
90135446Strhodes
91135446Strhodesstatic void
92135446Strhodesfree_map(cfg_parser_t *pctx, cfg_obj_t *obj);
93135446Strhodes
94135446Strhodesstatic isc_result_t
95135446Strhodesparse_symtab_elt(cfg_parser_t *pctx, const char *name,
96135446Strhodes		 cfg_type_t *elttype, isc_symtab_t *symtab,
97135446Strhodes		 isc_boolean_t callback);
98135446Strhodes
99135446Strhodesstatic void
100135446Strhodesfree_noop(cfg_parser_t *pctx, cfg_obj_t *obj);
101135446Strhodes
102135446Strhodesstatic isc_result_t
103135446Strhodescfg_getstringtoken(cfg_parser_t *pctx);
104135446Strhodes
105135446Strhodesstatic void
106135446Strhodesparser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning,
107135446Strhodes		unsigned int flags, const char *format, va_list args);
108135446Strhodes
109135446Strhodes/*
110135446Strhodes * Data representations.  These correspond to members of the
111135446Strhodes * "value" union in struct cfg_obj (except "void", which does
112135446Strhodes * not need a union member).
113135446Strhodes */
114135446Strhodes
115135446Strhodescfg_rep_t cfg_rep_uint32 = { "uint32", free_noop };
116135446Strhodescfg_rep_t cfg_rep_uint64 = { "uint64", free_noop };
117135446Strhodescfg_rep_t cfg_rep_string = { "string", free_string };
118135446Strhodescfg_rep_t cfg_rep_boolean = { "boolean", free_noop };
119135446Strhodescfg_rep_t cfg_rep_map = { "map", free_map };
120135446Strhodescfg_rep_t cfg_rep_list = { "list", free_list };
121135446Strhodescfg_rep_t cfg_rep_tuple = { "tuple", free_tuple };
122135446Strhodescfg_rep_t cfg_rep_sockaddr = { "sockaddr", free_noop };
123135446Strhodescfg_rep_t cfg_rep_netprefix = { "netprefix", free_noop };
124135446Strhodescfg_rep_t cfg_rep_void = { "void", free_noop };
125135446Strhodes
126135446Strhodes/*
127135446Strhodes * Configuration type definitions.
128135446Strhodes */
129135446Strhodes
130170222Sdougb/*%
131135446Strhodes * An implicit list.  These are formed by clauses that occur multiple times.
132135446Strhodes */
133135446Strhodesstatic cfg_type_t cfg_type_implicitlist = {
134135446Strhodes	"implicitlist", NULL, print_list, NULL, &cfg_rep_list, NULL };
135135446Strhodes
136135446Strhodes/* Functions. */
137135446Strhodes
138135446Strhodesvoid
139165071Sdougbcfg_print_obj(cfg_printer_t *pctx, const cfg_obj_t *obj) {
140135446Strhodes	obj->type->print(pctx, obj);
141135446Strhodes}
142135446Strhodes
143135446Strhodesvoid
144135446Strhodescfg_print_chars(cfg_printer_t *pctx, const char *text, int len) {
145135446Strhodes	pctx->f(pctx->closure, text, len);
146135446Strhodes}
147135446Strhodes
148135446Strhodesstatic void
149135446Strhodesprint_open(cfg_printer_t *pctx) {
150135446Strhodes	cfg_print_chars(pctx, "{\n", 2);
151135446Strhodes	pctx->indent++;
152135446Strhodes}
153135446Strhodes
154135446Strhodesstatic void
155135446Strhodesprint_indent(cfg_printer_t *pctx) {
156135446Strhodes	int indent = pctx->indent;
157135446Strhodes	while (indent > 0) {
158135446Strhodes		cfg_print_chars(pctx, "\t", 1);
159135446Strhodes		indent--;
160135446Strhodes	}
161135446Strhodes}
162135446Strhodes
163135446Strhodesstatic void
164135446Strhodesprint_close(cfg_printer_t *pctx) {
165135446Strhodes	pctx->indent--;
166135446Strhodes	print_indent(pctx);
167135446Strhodes	cfg_print_chars(pctx, "}", 1);
168135446Strhodes}
169135446Strhodes
170135446Strhodesisc_result_t
171135446Strhodescfg_parse_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
172135446Strhodes	isc_result_t result;
173135446Strhodes	INSIST(ret != NULL && *ret == NULL);
174135446Strhodes	result = type->parse(pctx, type, ret);
175135446Strhodes	if (result != ISC_R_SUCCESS)
176135446Strhodes		return (result);
177135446Strhodes	INSIST(*ret != NULL);
178135446Strhodes	return (ISC_R_SUCCESS);
179135446Strhodes}
180135446Strhodes
181135446Strhodesvoid
182165071Sdougbcfg_print(const cfg_obj_t *obj,
183135446Strhodes	  void (*f)(void *closure, const char *text, int textlen),
184135446Strhodes	  void *closure)
185135446Strhodes{
186262706Serwin	cfg_printx(obj, 0, f, closure);
187262706Serwin}
188262706Serwin
189262706Serwinvoid
190262706Serwincfg_printx(const cfg_obj_t *obj, unsigned int flags,
191262706Serwin	     void (*f)(void *closure, const char *text, int textlen),
192262706Serwin	     void *closure)
193262706Serwin{
194135446Strhodes	cfg_printer_t pctx;
195135446Strhodes	pctx.f = f;
196135446Strhodes	pctx.closure = closure;
197135446Strhodes	pctx.indent = 0;
198262706Serwin	pctx.flags = flags;
199135446Strhodes	obj->type->print(&pctx, obj);
200135446Strhodes}
201135446Strhodes
202135446Strhodes/* Tuples. */
203193149Sdougb
204135446Strhodesisc_result_t
205135446Strhodescfg_create_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
206135446Strhodes	isc_result_t result;
207135446Strhodes	const cfg_tuplefielddef_t *fields = type->of;
208135446Strhodes	const cfg_tuplefielddef_t *f;
209135446Strhodes	cfg_obj_t *obj = NULL;
210135446Strhodes	unsigned int nfields = 0;
211135446Strhodes	int i;
212135446Strhodes
213135446Strhodes	for (f = fields; f->name != NULL; f++)
214135446Strhodes		nfields++;
215135446Strhodes
216135446Strhodes	CHECK(cfg_create_obj(pctx, type, &obj));
217135446Strhodes	obj->value.tuple = isc_mem_get(pctx->mctx,
218135446Strhodes				       nfields * sizeof(cfg_obj_t *));
219135446Strhodes	if (obj->value.tuple == NULL) {
220135446Strhodes		result = ISC_R_NOMEMORY;
221135446Strhodes		goto cleanup;
222135446Strhodes	}
223135446Strhodes	for (f = fields, i = 0; f->name != NULL; f++, i++)
224135446Strhodes		obj->value.tuple[i] = NULL;
225135446Strhodes	*ret = obj;
226135446Strhodes	return (ISC_R_SUCCESS);
227135446Strhodes
228135446Strhodes cleanup:
229135446Strhodes	if (obj != NULL)
230135446Strhodes		isc_mem_put(pctx->mctx, obj, sizeof(*obj));
231135446Strhodes	return (result);
232135446Strhodes}
233135446Strhodes
234135446Strhodesisc_result_t
235135446Strhodescfg_parse_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
236135446Strhodes{
237135446Strhodes	isc_result_t result;
238135446Strhodes	const cfg_tuplefielddef_t *fields = type->of;
239135446Strhodes	const cfg_tuplefielddef_t *f;
240135446Strhodes	cfg_obj_t *obj = NULL;
241135446Strhodes	unsigned int i;
242135446Strhodes
243135446Strhodes	CHECK(cfg_create_tuple(pctx, type, &obj));
244135446Strhodes	for (f = fields, i = 0; f->name != NULL; f++, i++)
245135446Strhodes		CHECK(cfg_parse_obj(pctx, f->type, &obj->value.tuple[i]));
246135446Strhodes
247135446Strhodes	*ret = obj;
248135446Strhodes	return (ISC_R_SUCCESS);
249135446Strhodes
250135446Strhodes cleanup:
251135446Strhodes	CLEANUP_OBJ(obj);
252135446Strhodes	return (result);
253135446Strhodes}
254135446Strhodes
255135446Strhodesvoid
256165071Sdougbcfg_print_tuple(cfg_printer_t *pctx, const cfg_obj_t *obj) {
257135446Strhodes	unsigned int i;
258135446Strhodes	const cfg_tuplefielddef_t *fields = obj->type->of;
259135446Strhodes	const cfg_tuplefielddef_t *f;
260135446Strhodes	isc_boolean_t need_space = ISC_FALSE;
261135446Strhodes
262135446Strhodes	for (f = fields, i = 0; f->name != NULL; f++, i++) {
263165071Sdougb		const cfg_obj_t *fieldobj = obj->value.tuple[i];
264135446Strhodes		if (need_space)
265135446Strhodes			cfg_print_chars(pctx, " ", 1);
266135446Strhodes		cfg_print_obj(pctx, fieldobj);
267135446Strhodes		need_space = ISC_TF(fieldobj->type->print != cfg_print_void);
268135446Strhodes	}
269135446Strhodes}
270135446Strhodes
271135446Strhodesvoid
272135446Strhodescfg_doc_tuple(cfg_printer_t *pctx, const cfg_type_t *type) {
273135446Strhodes	const cfg_tuplefielddef_t *fields = type->of;
274135446Strhodes	const cfg_tuplefielddef_t *f;
275135446Strhodes	isc_boolean_t need_space = ISC_FALSE;
276135446Strhodes
277135446Strhodes	for (f = fields; f->name != NULL; f++) {
278135446Strhodes		if (need_space)
279135446Strhodes			cfg_print_chars(pctx, " ", 1);
280135446Strhodes		cfg_doc_obj(pctx, f->type);
281135446Strhodes		need_space = ISC_TF(f->type->print != cfg_print_void);
282135446Strhodes	}
283135446Strhodes}
284135446Strhodes
285135446Strhodesstatic void
286135446Strhodesfree_tuple(cfg_parser_t *pctx, cfg_obj_t *obj) {
287135446Strhodes	unsigned int i;
288135446Strhodes	const cfg_tuplefielddef_t *fields = obj->type->of;
289135446Strhodes	const cfg_tuplefielddef_t *f;
290135446Strhodes	unsigned int nfields = 0;
291135446Strhodes
292135446Strhodes	if (obj->value.tuple == NULL)
293135446Strhodes		return;
294135446Strhodes
295135446Strhodes	for (f = fields, i = 0; f->name != NULL; f++, i++) {
296135446Strhodes		CLEANUP_OBJ(obj->value.tuple[i]);
297135446Strhodes		nfields++;
298135446Strhodes	}
299135446Strhodes	isc_mem_put(pctx->mctx, obj->value.tuple,
300135446Strhodes		    nfields * sizeof(cfg_obj_t *));
301135446Strhodes}
302135446Strhodes
303135446Strhodesisc_boolean_t
304165071Sdougbcfg_obj_istuple(const cfg_obj_t *obj) {
305135446Strhodes	REQUIRE(obj != NULL);
306135446Strhodes	return (ISC_TF(obj->type->rep == &cfg_rep_tuple));
307135446Strhodes}
308135446Strhodes
309165071Sdougbconst cfg_obj_t *
310165071Sdougbcfg_tuple_get(const cfg_obj_t *tupleobj, const char* name) {
311135446Strhodes	unsigned int i;
312135446Strhodes	const cfg_tuplefielddef_t *fields;
313135446Strhodes	const cfg_tuplefielddef_t *f;
314193149Sdougb
315135446Strhodes	REQUIRE(tupleobj != NULL && tupleobj->type->rep == &cfg_rep_tuple);
316135446Strhodes
317135446Strhodes	fields = tupleobj->type->of;
318135446Strhodes	for (f = fields, i = 0; f->name != NULL; f++, i++) {
319135446Strhodes		if (strcmp(f->name, name) == 0)
320135446Strhodes			return (tupleobj->value.tuple[i]);
321135446Strhodes	}
322135446Strhodes	INSIST(0);
323135446Strhodes	return (NULL);
324135446Strhodes}
325135446Strhodes
326135446Strhodesisc_result_t
327135446Strhodescfg_parse_special(cfg_parser_t *pctx, int special) {
328193149Sdougb	isc_result_t result;
329135446Strhodes	CHECK(cfg_gettoken(pctx, 0));
330135446Strhodes	if (pctx->token.type == isc_tokentype_special &&
331135446Strhodes	    pctx->token.value.as_char == special)
332135446Strhodes		return (ISC_R_SUCCESS);
333135446Strhodes
334135446Strhodes	cfg_parser_error(pctx, CFG_LOG_NEAR, "'%c' expected", special);
335135446Strhodes	return (ISC_R_UNEXPECTEDTOKEN);
336135446Strhodes cleanup:
337135446Strhodes	return (result);
338135446Strhodes}
339135446Strhodes
340135446Strhodes/*
341135446Strhodes * Parse a required semicolon.  If it is not there, log
342135446Strhodes * an error and increment the error count but continue
343135446Strhodes * parsing.  Since the next token is pushed back,
344135446Strhodes * care must be taken to make sure it is eventually
345135446Strhodes * consumed or an infinite loop may result.
346135446Strhodes */
347135446Strhodesstatic isc_result_t
348135446Strhodesparse_semicolon(cfg_parser_t *pctx) {
349193149Sdougb	isc_result_t result;
350135446Strhodes	CHECK(cfg_gettoken(pctx, 0));
351135446Strhodes	if (pctx->token.type == isc_tokentype_special &&
352135446Strhodes	    pctx->token.value.as_char == ';')
353135446Strhodes		return (ISC_R_SUCCESS);
354135446Strhodes
355135446Strhodes	cfg_parser_error(pctx, CFG_LOG_BEFORE, "missing ';'");
356135446Strhodes	cfg_ungettoken(pctx);
357135446Strhodes cleanup:
358135446Strhodes	return (result);
359135446Strhodes}
360135446Strhodes
361135446Strhodes/*
362135446Strhodes * Parse EOF, logging and returning an error if not there.
363135446Strhodes */
364135446Strhodesstatic isc_result_t
365135446Strhodesparse_eof(cfg_parser_t *pctx) {
366193149Sdougb	isc_result_t result;
367135446Strhodes	CHECK(cfg_gettoken(pctx, 0));
368135446Strhodes
369135446Strhodes	if (pctx->token.type == isc_tokentype_eof)
370135446Strhodes		return (ISC_R_SUCCESS);
371135446Strhodes
372135446Strhodes	cfg_parser_error(pctx, CFG_LOG_NEAR, "syntax error");
373135446Strhodes	return (ISC_R_UNEXPECTEDTOKEN);
374135446Strhodes cleanup:
375135446Strhodes	return (result);
376135446Strhodes}
377135446Strhodes
378135446Strhodes/* A list of files, used internally for pctx->files. */
379135446Strhodes
380135446Strhodesstatic cfg_type_t cfg_type_filelist = {
381135446Strhodes	"filelist", NULL, print_list, NULL, &cfg_rep_list,
382135446Strhodes	&cfg_type_qstring
383135446Strhodes};
384135446Strhodes
385135446Strhodesisc_result_t
386135446Strhodescfg_parser_create(isc_mem_t *mctx, isc_log_t *lctx, cfg_parser_t **ret) {
387135446Strhodes	isc_result_t result;
388135446Strhodes	cfg_parser_t *pctx;
389135446Strhodes	isc_lexspecials_t specials;
390135446Strhodes
391135446Strhodes	REQUIRE(mctx != NULL);
392135446Strhodes	REQUIRE(ret != NULL && *ret == NULL);
393135446Strhodes
394135446Strhodes	pctx = isc_mem_get(mctx, sizeof(*pctx));
395135446Strhodes	if (pctx == NULL)
396135446Strhodes		return (ISC_R_NOMEMORY);
397135446Strhodes
398254402Serwin	pctx->mctx = NULL;
399254402Serwin	isc_mem_attach(mctx, &pctx->mctx);
400254402Serwin
401224092Sdougb	result = isc_refcount_init(&pctx->references, 1);
402224092Sdougb	if (result != ISC_R_SUCCESS) {
403254402Serwin		isc_mem_putanddetach(&pctx->mctx, pctx, sizeof(*pctx));
404224092Sdougb		return (result);
405224092Sdougb	}
406224092Sdougb
407135446Strhodes	pctx->lctx = lctx;
408135446Strhodes	pctx->lexer = NULL;
409135446Strhodes	pctx->seen_eof = ISC_FALSE;
410135446Strhodes	pctx->ungotten = ISC_FALSE;
411135446Strhodes	pctx->errors = 0;
412135446Strhodes	pctx->warnings = 0;
413135446Strhodes	pctx->open_files = NULL;
414135446Strhodes	pctx->closed_files = NULL;
415135446Strhodes	pctx->line = 0;
416135446Strhodes	pctx->callback = NULL;
417135446Strhodes	pctx->callbackarg = NULL;
418135446Strhodes	pctx->token.type = isc_tokentype_unknown;
419224092Sdougb	pctx->flags = 0;
420135446Strhodes
421135446Strhodes	memset(specials, 0, sizeof(specials));
422135446Strhodes	specials['{'] = 1;
423135446Strhodes	specials['}'] = 1;
424135446Strhodes	specials[';'] = 1;
425135446Strhodes	specials['/'] = 1;
426135446Strhodes	specials['"'] = 1;
427135446Strhodes	specials['!'] = 1;
428135446Strhodes
429135446Strhodes	CHECK(isc_lex_create(pctx->mctx, 1024, &pctx->lexer));
430135446Strhodes
431135446Strhodes	isc_lex_setspecials(pctx->lexer, specials);
432135446Strhodes	isc_lex_setcomments(pctx->lexer, (ISC_LEXCOMMENT_C |
433135446Strhodes					 ISC_LEXCOMMENT_CPLUSPLUS |
434135446Strhodes					 ISC_LEXCOMMENT_SHELL));
435135446Strhodes
436135446Strhodes	CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->open_files));
437135446Strhodes	CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->closed_files));
438135446Strhodes
439135446Strhodes	*ret = pctx;
440135446Strhodes	return (ISC_R_SUCCESS);
441135446Strhodes
442135446Strhodes cleanup:
443135446Strhodes	if (pctx->lexer != NULL)
444135446Strhodes		isc_lex_destroy(&pctx->lexer);
445135446Strhodes	CLEANUP_OBJ(pctx->open_files);
446135446Strhodes	CLEANUP_OBJ(pctx->closed_files);
447254402Serwin	isc_mem_putanddetach(&pctx->mctx, pctx, sizeof(*pctx));
448135446Strhodes	return (result);
449135446Strhodes}
450135446Strhodes
451135446Strhodesstatic isc_result_t
452135446Strhodesparser_openfile(cfg_parser_t *pctx, const char *filename) {
453135446Strhodes	isc_result_t result;
454135446Strhodes	cfg_listelt_t *elt = NULL;
455135446Strhodes	cfg_obj_t *stringobj = NULL;
456135446Strhodes
457135446Strhodes	result = isc_lex_openfile(pctx->lexer, filename);
458135446Strhodes	if (result != ISC_R_SUCCESS) {
459135446Strhodes		cfg_parser_error(pctx, 0, "open: %s: %s",
460135446Strhodes			     filename, isc_result_totext(result));
461135446Strhodes		goto cleanup;
462135446Strhodes	}
463135446Strhodes
464135446Strhodes	CHECK(create_string(pctx, filename, &cfg_type_qstring, &stringobj));
465135446Strhodes	CHECK(create_listelt(pctx, &elt));
466135446Strhodes	elt->obj = stringobj;
467135446Strhodes	ISC_LIST_APPEND(pctx->open_files->value.list, elt, link);
468135446Strhodes
469135446Strhodes	return (ISC_R_SUCCESS);
470135446Strhodes cleanup:
471135446Strhodes	CLEANUP_OBJ(stringobj);
472135446Strhodes	return (result);
473135446Strhodes}
474135446Strhodes
475135446Strhodesvoid
476135446Strhodescfg_parser_setcallback(cfg_parser_t *pctx,
477135446Strhodes		       cfg_parsecallback_t callback,
478135446Strhodes		       void *arg)
479135446Strhodes{
480135446Strhodes	pctx->callback = callback;
481135446Strhodes	pctx->callbackarg = arg;
482135446Strhodes}
483135446Strhodes
484135446Strhodes/*
485135446Strhodes * Parse a configuration using a pctx where a lexer has already
486135446Strhodes * been set up with a source.
487135446Strhodes */
488135446Strhodesstatic isc_result_t
489135446Strhodesparse2(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
490135446Strhodes	isc_result_t result;
491135446Strhodes	cfg_obj_t *obj = NULL;
492135446Strhodes
493135446Strhodes	result = cfg_parse_obj(pctx, type, &obj);
494135446Strhodes
495135446Strhodes	if (pctx->errors != 0) {
496135446Strhodes		/* Errors have been logged. */
497135446Strhodes		if (result == ISC_R_SUCCESS)
498135446Strhodes			result = ISC_R_FAILURE;
499135446Strhodes		goto cleanup;
500135446Strhodes	}
501135446Strhodes
502135446Strhodes	if (result != ISC_R_SUCCESS) {
503135446Strhodes		/* Parsing failed but no errors have been logged. */
504135446Strhodes		cfg_parser_error(pctx, 0, "parsing failed");
505135446Strhodes		goto cleanup;
506135446Strhodes	}
507135446Strhodes
508135446Strhodes	CHECK(parse_eof(pctx));
509135446Strhodes
510135446Strhodes	*ret = obj;
511135446Strhodes	return (ISC_R_SUCCESS);
512135446Strhodes
513135446Strhodes cleanup:
514135446Strhodes	CLEANUP_OBJ(obj);
515135446Strhodes	return (result);
516135446Strhodes}
517135446Strhodes
518135446Strhodesisc_result_t
519135446Strhodescfg_parse_file(cfg_parser_t *pctx, const char *filename,
520135446Strhodes	       const cfg_type_t *type, cfg_obj_t **ret)
521135446Strhodes{
522135446Strhodes	isc_result_t result;
523135446Strhodes
524135446Strhodes	REQUIRE(filename != NULL);
525135446Strhodes
526135446Strhodes	CHECK(parser_openfile(pctx, filename));
527135446Strhodes	CHECK(parse2(pctx, type, ret));
528135446Strhodes cleanup:
529135446Strhodes	return (result);
530135446Strhodes}
531135446Strhodes
532135446Strhodes
533135446Strhodesisc_result_t
534135446Strhodescfg_parse_buffer(cfg_parser_t *pctx, isc_buffer_t *buffer,
535135446Strhodes	const cfg_type_t *type, cfg_obj_t **ret)
536135446Strhodes{
537135446Strhodes	isc_result_t result;
538135446Strhodes	REQUIRE(buffer != NULL);
539193149Sdougb	CHECK(isc_lex_openbuffer(pctx->lexer, buffer));
540135446Strhodes	CHECK(parse2(pctx, type, ret));
541135446Strhodes cleanup:
542135446Strhodes	return (result);
543135446Strhodes}
544135446Strhodes
545135446Strhodesvoid
546224092Sdougbcfg_parser_attach(cfg_parser_t *src, cfg_parser_t **dest) {
547224092Sdougb	REQUIRE(src != NULL);
548224092Sdougb	REQUIRE(dest != NULL && *dest == NULL);
549224092Sdougb	isc_refcount_increment(&src->references, NULL);
550224092Sdougb	*dest = src;
551224092Sdougb}
552224092Sdougb
553224092Sdougbvoid
554135446Strhodescfg_parser_destroy(cfg_parser_t **pctxp) {
555135446Strhodes	cfg_parser_t *pctx = *pctxp;
556224092Sdougb	unsigned int refs;
557224092Sdougb
558224092Sdougb	isc_refcount_decrement(&pctx->references, &refs);
559224092Sdougb	if (refs == 0) {
560224092Sdougb		isc_lex_destroy(&pctx->lexer);
561224092Sdougb		/*
562224092Sdougb		 * Cleaning up open_files does not
563224092Sdougb		 * close the files; that was already done
564224092Sdougb		 * by closing the lexer.
565224092Sdougb		 */
566224092Sdougb		CLEANUP_OBJ(pctx->open_files);
567224092Sdougb		CLEANUP_OBJ(pctx->closed_files);
568254402Serwin		isc_mem_putanddetach(&pctx->mctx, pctx, sizeof(*pctx));
569224092Sdougb	}
570135446Strhodes	*pctxp = NULL;
571135446Strhodes}
572135446Strhodes
573135446Strhodes/*
574135446Strhodes * void
575135446Strhodes */
576135446Strhodesisc_result_t
577135446Strhodescfg_parse_void(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
578135446Strhodes	UNUSED(type);
579135446Strhodes	return (cfg_create_obj(pctx, &cfg_type_void, ret));
580135446Strhodes}
581135446Strhodes
582135446Strhodesvoid
583165071Sdougbcfg_print_void(cfg_printer_t *pctx, const cfg_obj_t *obj) {
584135446Strhodes	UNUSED(pctx);
585135446Strhodes	UNUSED(obj);
586135446Strhodes}
587135446Strhodes
588135446Strhodesvoid
589135446Strhodescfg_doc_void(cfg_printer_t *pctx, const cfg_type_t *type) {
590135446Strhodes	UNUSED(pctx);
591135446Strhodes	UNUSED(type);
592135446Strhodes}
593135446Strhodes
594135446Strhodesisc_boolean_t
595165071Sdougbcfg_obj_isvoid(const cfg_obj_t *obj) {
596135446Strhodes	REQUIRE(obj != NULL);
597135446Strhodes	return (ISC_TF(obj->type->rep == &cfg_rep_void));
598135446Strhodes}
599135446Strhodes
600135446Strhodescfg_type_t cfg_type_void = {
601135446Strhodes	"void", cfg_parse_void, cfg_print_void, cfg_doc_void, &cfg_rep_void,
602135446Strhodes	NULL };
603135446Strhodes
604135446Strhodes
605135446Strhodes/*
606135446Strhodes * uint32
607135446Strhodes */
608135446Strhodesisc_result_t
609135446Strhodescfg_parse_uint32(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
610193149Sdougb	isc_result_t result;
611135446Strhodes	cfg_obj_t *obj = NULL;
612135446Strhodes	UNUSED(type);
613135446Strhodes
614135446Strhodes	CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
615135446Strhodes	if (pctx->token.type != isc_tokentype_number) {
616135446Strhodes		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected number");
617135446Strhodes		return (ISC_R_UNEXPECTEDTOKEN);
618135446Strhodes	}
619135446Strhodes
620135446Strhodes	CHECK(cfg_create_obj(pctx, &cfg_type_uint32, &obj));
621135446Strhodes
622135446Strhodes	obj->value.uint32 = pctx->token.value.as_ulong;
623135446Strhodes	*ret = obj;
624135446Strhodes cleanup:
625135446Strhodes	return (result);
626135446Strhodes}
627135446Strhodes
628135446Strhodesvoid
629135446Strhodescfg_print_cstr(cfg_printer_t *pctx, const char *s) {
630135446Strhodes	cfg_print_chars(pctx, s, strlen(s));
631135446Strhodes}
632135446Strhodes
633135446Strhodesvoid
634135446Strhodescfg_print_rawuint(cfg_printer_t *pctx, unsigned int u) {
635135446Strhodes	char buf[32];
636135446Strhodes	snprintf(buf, sizeof(buf), "%u", u);
637135446Strhodes	cfg_print_cstr(pctx, buf);
638135446Strhodes}
639135446Strhodes
640135446Strhodesvoid
641165071Sdougbcfg_print_uint32(cfg_printer_t *pctx, const cfg_obj_t *obj) {
642135446Strhodes	cfg_print_rawuint(pctx, obj->value.uint32);
643135446Strhodes}
644135446Strhodes
645135446Strhodesisc_boolean_t
646165071Sdougbcfg_obj_isuint32(const cfg_obj_t *obj) {
647135446Strhodes	REQUIRE(obj != NULL);
648135446Strhodes	return (ISC_TF(obj->type->rep == &cfg_rep_uint32));
649135446Strhodes}
650135446Strhodes
651135446Strhodesisc_uint32_t
652165071Sdougbcfg_obj_asuint32(const cfg_obj_t *obj) {
653135446Strhodes	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint32);
654135446Strhodes	return (obj->value.uint32);
655135446Strhodes}
656135446Strhodes
657135446Strhodescfg_type_t cfg_type_uint32 = {
658135446Strhodes	"integer", cfg_parse_uint32, cfg_print_uint32, cfg_doc_terminal,
659135446Strhodes	&cfg_rep_uint32, NULL
660135446Strhodes};
661135446Strhodes
662135446Strhodes
663135446Strhodes/*
664135446Strhodes * uint64
665135446Strhodes */
666135446Strhodesisc_boolean_t
667165071Sdougbcfg_obj_isuint64(const cfg_obj_t *obj) {
668135446Strhodes	REQUIRE(obj != NULL);
669135446Strhodes	return (ISC_TF(obj->type->rep == &cfg_rep_uint64));
670135446Strhodes}
671135446Strhodes
672135446Strhodesisc_uint64_t
673165071Sdougbcfg_obj_asuint64(const cfg_obj_t *obj) {
674135446Strhodes	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint64);
675135446Strhodes	return (obj->value.uint64);
676135446Strhodes}
677135446Strhodes
678135446Strhodesvoid
679165071Sdougbcfg_print_uint64(cfg_printer_t *pctx, const cfg_obj_t *obj) {
680135446Strhodes	char buf[32];
681135446Strhodes	snprintf(buf, sizeof(buf), "%" ISC_PRINT_QUADFORMAT "u",
682135446Strhodes		 obj->value.uint64);
683135446Strhodes	cfg_print_cstr(pctx, buf);
684135446Strhodes}
685135446Strhodes
686135446Strhodescfg_type_t cfg_type_uint64 = {
687135446Strhodes	"64_bit_integer", NULL, cfg_print_uint64, cfg_doc_terminal,
688135446Strhodes	&cfg_rep_uint64, NULL
689135446Strhodes};
690135446Strhodes
691135446Strhodes/*
692135446Strhodes * qstring (quoted string), ustring (unquoted string), astring
693135446Strhodes * (any string)
694135446Strhodes */
695135446Strhodes
696135446Strhodes/* Create a string object from a null-terminated C string. */
697135446Strhodesstatic isc_result_t
698135446Strhodescreate_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
699135446Strhodes	      cfg_obj_t **ret)
700135446Strhodes{
701135446Strhodes	isc_result_t result;
702135446Strhodes	cfg_obj_t *obj = NULL;
703135446Strhodes	int len;
704135446Strhodes
705135446Strhodes	CHECK(cfg_create_obj(pctx, type, &obj));
706135446Strhodes	len = strlen(contents);
707135446Strhodes	obj->value.string.length = len;
708135446Strhodes	obj->value.string.base = isc_mem_get(pctx->mctx, len + 1);
709135446Strhodes	if (obj->value.string.base == 0) {
710135446Strhodes		isc_mem_put(pctx->mctx, obj, sizeof(*obj));
711135446Strhodes		return (ISC_R_NOMEMORY);
712135446Strhodes	}
713262706Serwin	memmove(obj->value.string.base, contents, len);
714135446Strhodes	obj->value.string.base[len] = '\0';
715135446Strhodes
716135446Strhodes	*ret = obj;
717135446Strhodes cleanup:
718135446Strhodes	return (result);
719135446Strhodes}
720135446Strhodes
721135446Strhodesisc_result_t
722135446Strhodescfg_parse_qstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
723193149Sdougb	isc_result_t result;
724135446Strhodes	UNUSED(type);
725135446Strhodes
726135446Strhodes	CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
727135446Strhodes	if (pctx->token.type != isc_tokentype_qstring) {
728135446Strhodes		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected quoted string");
729135446Strhodes		return (ISC_R_UNEXPECTEDTOKEN);
730135446Strhodes	}
731135446Strhodes	return (create_string(pctx,
732135446Strhodes			      TOKEN_STRING(pctx),
733135446Strhodes			      &cfg_type_qstring,
734135446Strhodes			      ret));
735135446Strhodes cleanup:
736135446Strhodes	return (result);
737135446Strhodes}
738135446Strhodes
739135446Strhodesstatic isc_result_t
740135446Strhodesparse_ustring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
741193149Sdougb	isc_result_t result;
742135446Strhodes	UNUSED(type);
743135446Strhodes
744135446Strhodes	CHECK(cfg_gettoken(pctx, 0));
745135446Strhodes	if (pctx->token.type != isc_tokentype_string) {
746135446Strhodes		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected unquoted string");
747135446Strhodes		return (ISC_R_UNEXPECTEDTOKEN);
748135446Strhodes	}
749135446Strhodes	return (create_string(pctx,
750135446Strhodes			      TOKEN_STRING(pctx),
751135446Strhodes			      &cfg_type_ustring,
752135446Strhodes			      ret));
753135446Strhodes cleanup:
754135446Strhodes	return (result);
755135446Strhodes}
756135446Strhodes
757135446Strhodesisc_result_t
758165071Sdougbcfg_parse_astring(cfg_parser_t *pctx, const cfg_type_t *type,
759165071Sdougb		  cfg_obj_t **ret)
760165071Sdougb{
761193149Sdougb	isc_result_t result;
762135446Strhodes	UNUSED(type);
763135446Strhodes
764135446Strhodes	CHECK(cfg_getstringtoken(pctx));
765135446Strhodes	return (create_string(pctx,
766135446Strhodes			      TOKEN_STRING(pctx),
767135446Strhodes			      &cfg_type_qstring,
768135446Strhodes			      ret));
769135446Strhodes cleanup:
770135446Strhodes	return (result);
771135446Strhodes}
772135446Strhodes
773262706Serwinisc_result_t
774262706Serwincfg_parse_sstring(cfg_parser_t *pctx, const cfg_type_t *type,
775262706Serwin		  cfg_obj_t **ret)
776262706Serwin{
777262706Serwin	isc_result_t result;
778262706Serwin	UNUSED(type);
779262706Serwin
780262706Serwin	CHECK(cfg_getstringtoken(pctx));
781262706Serwin	return (create_string(pctx,
782262706Serwin			      TOKEN_STRING(pctx),
783262706Serwin			      &cfg_type_sstring,
784262706Serwin			      ret));
785262706Serwin cleanup:
786262706Serwin	return (result);
787262706Serwin}
788262706Serwin
789135446Strhodesisc_boolean_t
790135446Strhodescfg_is_enum(const char *s, const char *const *enums) {
791135446Strhodes	const char * const *p;
792135446Strhodes	for (p = enums; *p != NULL; p++) {
793135446Strhodes		if (strcasecmp(*p, s) == 0)
794135446Strhodes			return (ISC_TRUE);
795135446Strhodes	}
796135446Strhodes	return (ISC_FALSE);
797135446Strhodes}
798135446Strhodes
799135446Strhodesstatic isc_result_t
800135446Strhodescheck_enum(cfg_parser_t *pctx, cfg_obj_t *obj, const char *const *enums) {
801135446Strhodes	const char *s = obj->value.string.base;
802135446Strhodes	if (cfg_is_enum(s, enums))
803135446Strhodes		return (ISC_R_SUCCESS);
804135446Strhodes	cfg_parser_error(pctx, 0, "'%s' unexpected", s);
805135446Strhodes	return (ISC_R_UNEXPECTEDTOKEN);
806135446Strhodes}
807135446Strhodes
808135446Strhodesisc_result_t
809135446Strhodescfg_parse_enum(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
810193149Sdougb	isc_result_t result;
811135446Strhodes	cfg_obj_t *obj = NULL;
812135446Strhodes	CHECK(parse_ustring(pctx, NULL, &obj));
813135446Strhodes	CHECK(check_enum(pctx, obj, type->of));
814135446Strhodes	*ret = obj;
815135446Strhodes	return (ISC_R_SUCCESS);
816135446Strhodes cleanup:
817193149Sdougb	CLEANUP_OBJ(obj);
818135446Strhodes	return (result);
819135446Strhodes}
820135446Strhodes
821135446Strhodesvoid
822135446Strhodescfg_doc_enum(cfg_printer_t *pctx, const cfg_type_t *type) {
823135446Strhodes	const char * const *p;
824135446Strhodes	cfg_print_chars(pctx, "( ", 2);
825135446Strhodes	for (p = type->of; *p != NULL; p++) {
826135446Strhodes		cfg_print_cstr(pctx, *p);
827135446Strhodes		if (p[1] != NULL)
828135446Strhodes			cfg_print_chars(pctx, " | ", 3);
829135446Strhodes	}
830135446Strhodes	cfg_print_chars(pctx, " )", 2);
831135446Strhodes}
832135446Strhodes
833135446Strhodesvoid
834165071Sdougbcfg_print_ustring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
835135446Strhodes	cfg_print_chars(pctx, obj->value.string.base, obj->value.string.length);
836135446Strhodes}
837135446Strhodes
838135446Strhodesstatic void
839165071Sdougbprint_qstring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
840135446Strhodes	cfg_print_chars(pctx, "\"", 1);
841135446Strhodes	cfg_print_ustring(pctx, obj);
842135446Strhodes	cfg_print_chars(pctx, "\"", 1);
843135446Strhodes}
844135446Strhodes
845135446Strhodesstatic void
846262706Serwinprint_sstring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
847262706Serwin	cfg_print_chars(pctx, "\"", 1);
848262706Serwin	if ((pctx->flags & CFG_PRINTER_XKEY) != 0) {
849262706Serwin		unsigned int len = obj->value.string.length;
850262706Serwin		while (len-- > 0)
851262706Serwin			cfg_print_chars(pctx, "?", 1);
852262706Serwin	} else
853262706Serwin		cfg_print_ustring(pctx, obj);
854262706Serwin	cfg_print_chars(pctx, "\"", 1);
855262706Serwin}
856262706Serwin
857262706Serwinstatic void
858135446Strhodesfree_string(cfg_parser_t *pctx, cfg_obj_t *obj) {
859135446Strhodes	isc_mem_put(pctx->mctx, obj->value.string.base,
860135446Strhodes		    obj->value.string.length + 1);
861135446Strhodes}
862135446Strhodes
863135446Strhodesisc_boolean_t
864165071Sdougbcfg_obj_isstring(const cfg_obj_t *obj) {
865135446Strhodes	REQUIRE(obj != NULL);
866135446Strhodes	return (ISC_TF(obj->type->rep == &cfg_rep_string));
867135446Strhodes}
868135446Strhodes
869165071Sdougbconst char *
870165071Sdougbcfg_obj_asstring(const cfg_obj_t *obj) {
871135446Strhodes	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_string);
872135446Strhodes	return (obj->value.string.base);
873135446Strhodes}
874135446Strhodes
875135446Strhodes/* Quoted string only */
876135446Strhodescfg_type_t cfg_type_qstring = {
877135446Strhodes	"quoted_string", cfg_parse_qstring, print_qstring, cfg_doc_terminal,
878135446Strhodes	&cfg_rep_string, NULL
879135446Strhodes};
880135446Strhodes
881135446Strhodes/* Unquoted string only */
882135446Strhodescfg_type_t cfg_type_ustring = {
883135446Strhodes	"string", parse_ustring, cfg_print_ustring, cfg_doc_terminal,
884135446Strhodes	&cfg_rep_string, NULL
885135446Strhodes};
886135446Strhodes
887135446Strhodes/* Any string (quoted or unquoted); printed with quotes */
888135446Strhodescfg_type_t cfg_type_astring = {
889135446Strhodes	"string", cfg_parse_astring, print_qstring, cfg_doc_terminal,
890135446Strhodes	&cfg_rep_string, NULL
891135446Strhodes};
892135446Strhodes
893135446Strhodes/*
894262706Serwin * Any string (quoted or unquoted); printed with quotes.
895262706Serwin * If CFG_PRINTER_XKEY is set when printing the string will be '?' out.
896262706Serwin */
897262706Serwincfg_type_t cfg_type_sstring = {
898262706Serwin	"string", cfg_parse_sstring, print_sstring, cfg_doc_terminal,
899262706Serwin	&cfg_rep_string, NULL
900262706Serwin};
901262706Serwin
902262706Serwin/*
903135446Strhodes * Booleans
904135446Strhodes */
905135446Strhodes
906135446Strhodesisc_boolean_t
907165071Sdougbcfg_obj_isboolean(const cfg_obj_t *obj) {
908135446Strhodes	REQUIRE(obj != NULL);
909135446Strhodes	return (ISC_TF(obj->type->rep == &cfg_rep_boolean));
910135446Strhodes}
911135446Strhodes
912135446Strhodesisc_boolean_t
913165071Sdougbcfg_obj_asboolean(const cfg_obj_t *obj) {
914135446Strhodes	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_boolean);
915135446Strhodes	return (obj->value.boolean);
916135446Strhodes}
917135446Strhodes
918224092Sdougbisc_result_t
919224092Sdougbcfg_parse_boolean(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
920135446Strhodes{
921193149Sdougb	isc_result_t result;
922135446Strhodes	isc_boolean_t value;
923135446Strhodes	cfg_obj_t *obj = NULL;
924135446Strhodes	UNUSED(type);
925135446Strhodes
926135446Strhodes	result = cfg_gettoken(pctx, 0);
927135446Strhodes	if (result != ISC_R_SUCCESS)
928135446Strhodes		return (result);
929135446Strhodes
930135446Strhodes	if (pctx->token.type != isc_tokentype_string)
931135446Strhodes		goto bad_boolean;
932135446Strhodes
933135446Strhodes	if ((strcasecmp(TOKEN_STRING(pctx), "true") == 0) ||
934135446Strhodes	    (strcasecmp(TOKEN_STRING(pctx), "yes") == 0) ||
935135446Strhodes	    (strcmp(TOKEN_STRING(pctx), "1") == 0)) {
936135446Strhodes		value = ISC_TRUE;
937135446Strhodes	} else if ((strcasecmp(TOKEN_STRING(pctx), "false") == 0) ||
938135446Strhodes		   (strcasecmp(TOKEN_STRING(pctx), "no") == 0) ||
939135446Strhodes		   (strcmp(TOKEN_STRING(pctx), "0") == 0)) {
940135446Strhodes		value = ISC_FALSE;
941135446Strhodes	} else {
942135446Strhodes		goto bad_boolean;
943135446Strhodes	}
944135446Strhodes
945135446Strhodes	CHECK(cfg_create_obj(pctx, &cfg_type_boolean, &obj));
946135446Strhodes	obj->value.boolean = value;
947135446Strhodes	*ret = obj;
948135446Strhodes	return (result);
949135446Strhodes
950135446Strhodes bad_boolean:
951135446Strhodes	cfg_parser_error(pctx, CFG_LOG_NEAR, "boolean expected");
952135446Strhodes	return (ISC_R_UNEXPECTEDTOKEN);
953135446Strhodes
954135446Strhodes cleanup:
955135446Strhodes	return (result);
956135446Strhodes}
957135446Strhodes
958224092Sdougbvoid
959224092Sdougbcfg_print_boolean(cfg_printer_t *pctx, const cfg_obj_t *obj) {
960135446Strhodes	if (obj->value.boolean)
961135446Strhodes		cfg_print_chars(pctx, "yes", 3);
962135446Strhodes	else
963135446Strhodes		cfg_print_chars(pctx, "no", 2);
964135446Strhodes}
965135446Strhodes
966135446Strhodescfg_type_t cfg_type_boolean = {
967224092Sdougb	"boolean", cfg_parse_boolean, cfg_print_boolean, cfg_doc_terminal,
968135446Strhodes	&cfg_rep_boolean, NULL
969135446Strhodes};
970135446Strhodes
971135446Strhodes/*
972135446Strhodes * Lists.
973135446Strhodes */
974135446Strhodes
975135446Strhodesisc_result_t
976135446Strhodescfg_create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **obj) {
977135446Strhodes	isc_result_t result;
978135446Strhodes	CHECK(cfg_create_obj(pctx, type, obj));
979135446Strhodes	ISC_LIST_INIT((*obj)->value.list);
980135446Strhodes cleanup:
981135446Strhodes	return (result);
982135446Strhodes}
983135446Strhodes
984135446Strhodesstatic isc_result_t
985135446Strhodescreate_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp) {
986135446Strhodes	cfg_listelt_t *elt;
987135446Strhodes	elt = isc_mem_get(pctx->mctx, sizeof(*elt));
988135446Strhodes	if (elt == NULL)
989135446Strhodes		return (ISC_R_NOMEMORY);
990135446Strhodes	elt->obj = NULL;
991135446Strhodes	ISC_LINK_INIT(elt, link);
992135446Strhodes	*eltp = elt;
993135446Strhodes	return (ISC_R_SUCCESS);
994135446Strhodes}
995135446Strhodes
996135446Strhodesstatic void
997135446Strhodesfree_list_elt(cfg_parser_t *pctx, cfg_listelt_t *elt) {
998135446Strhodes	cfg_obj_destroy(pctx, &elt->obj);
999135446Strhodes	isc_mem_put(pctx->mctx, elt, sizeof(*elt));
1000135446Strhodes}
1001135446Strhodes
1002135446Strhodesstatic void
1003135446Strhodesfree_list(cfg_parser_t *pctx, cfg_obj_t *obj) {
1004135446Strhodes	cfg_listelt_t *elt, *next;
1005135446Strhodes	for (elt = ISC_LIST_HEAD(obj->value.list);
1006135446Strhodes	     elt != NULL;
1007135446Strhodes	     elt = next)
1008135446Strhodes	{
1009135446Strhodes		next = ISC_LIST_NEXT(elt, link);
1010135446Strhodes		free_list_elt(pctx, elt);
1011135446Strhodes	}
1012135446Strhodes}
1013135446Strhodes
1014135446Strhodesisc_result_t
1015135446Strhodescfg_parse_listelt(cfg_parser_t *pctx, const cfg_type_t *elttype,
1016135446Strhodes		  cfg_listelt_t **ret)
1017135446Strhodes{
1018135446Strhodes	isc_result_t result;
1019135446Strhodes	cfg_listelt_t *elt = NULL;
1020135446Strhodes	cfg_obj_t *value = NULL;
1021135446Strhodes
1022135446Strhodes	CHECK(create_listelt(pctx, &elt));
1023135446Strhodes
1024135446Strhodes	result = cfg_parse_obj(pctx, elttype, &value);
1025135446Strhodes	if (result != ISC_R_SUCCESS)
1026135446Strhodes		goto cleanup;
1027135446Strhodes
1028135446Strhodes	elt->obj = value;
1029135446Strhodes
1030135446Strhodes	*ret = elt;
1031135446Strhodes	return (ISC_R_SUCCESS);
1032135446Strhodes
1033135446Strhodes cleanup:
1034135446Strhodes	isc_mem_put(pctx->mctx, elt, sizeof(*elt));
1035135446Strhodes	return (result);
1036135446Strhodes}
1037135446Strhodes
1038135446Strhodes/*
1039135446Strhodes * Parse a homogeneous list whose elements are of type 'elttype'
1040135446Strhodes * and where each element is terminated by a semicolon.
1041135446Strhodes */
1042135446Strhodesstatic isc_result_t
1043135446Strhodesparse_list(cfg_parser_t *pctx, const cfg_type_t *listtype, cfg_obj_t **ret)
1044135446Strhodes{
1045135446Strhodes	cfg_obj_t *listobj = NULL;
1046135446Strhodes	const cfg_type_t *listof = listtype->of;
1047135446Strhodes	isc_result_t result;
1048135446Strhodes	cfg_listelt_t *elt = NULL;
1049135446Strhodes
1050135446Strhodes	CHECK(cfg_create_list(pctx, listtype, &listobj));
1051135446Strhodes
1052135446Strhodes	for (;;) {
1053135446Strhodes		CHECK(cfg_peektoken(pctx, 0));
1054135446Strhodes		if (pctx->token.type == isc_tokentype_special &&
1055135446Strhodes		    pctx->token.value.as_char == /*{*/ '}')
1056135446Strhodes			break;
1057135446Strhodes		CHECK(cfg_parse_listelt(pctx, listof, &elt));
1058135446Strhodes		CHECK(parse_semicolon(pctx));
1059135446Strhodes		ISC_LIST_APPEND(listobj->value.list, elt, link);
1060135446Strhodes		elt = NULL;
1061135446Strhodes	}
1062135446Strhodes	*ret = listobj;
1063135446Strhodes	return (ISC_R_SUCCESS);
1064135446Strhodes
1065135446Strhodes cleanup:
1066135446Strhodes	if (elt != NULL)
1067135446Strhodes		free_list_elt(pctx, elt);
1068135446Strhodes	CLEANUP_OBJ(listobj);
1069135446Strhodes	return (result);
1070135446Strhodes}
1071135446Strhodes
1072135446Strhodesstatic void
1073165071Sdougbprint_list(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1074165071Sdougb	const cfg_list_t *list = &obj->value.list;
1075165071Sdougb	const cfg_listelt_t *elt;
1076135446Strhodes
1077135446Strhodes	for (elt = ISC_LIST_HEAD(*list);
1078135446Strhodes	     elt != NULL;
1079135446Strhodes	     elt = ISC_LIST_NEXT(elt, link)) {
1080135446Strhodes		print_indent(pctx);
1081135446Strhodes		cfg_print_obj(pctx, elt->obj);
1082135446Strhodes		cfg_print_chars(pctx, ";\n", 2);
1083135446Strhodes	}
1084135446Strhodes}
1085135446Strhodes
1086135446Strhodesisc_result_t
1087135446Strhodescfg_parse_bracketed_list(cfg_parser_t *pctx, const cfg_type_t *type,
1088135446Strhodes		     cfg_obj_t **ret)
1089135446Strhodes{
1090135446Strhodes	isc_result_t result;
1091135446Strhodes	CHECK(cfg_parse_special(pctx, '{'));
1092135446Strhodes	CHECK(parse_list(pctx, type, ret));
1093135446Strhodes	CHECK(cfg_parse_special(pctx, '}'));
1094135446Strhodes cleanup:
1095135446Strhodes	return (result);
1096135446Strhodes}
1097135446Strhodes
1098135446Strhodesvoid
1099165071Sdougbcfg_print_bracketed_list(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1100135446Strhodes	print_open(pctx);
1101135446Strhodes	print_list(pctx, obj);
1102135446Strhodes	print_close(pctx);
1103135446Strhodes}
1104135446Strhodes
1105135446Strhodesvoid
1106135446Strhodescfg_doc_bracketed_list(cfg_printer_t *pctx, const cfg_type_t *type) {
1107135446Strhodes	cfg_print_chars(pctx, "{ ", 2);
1108135446Strhodes	cfg_doc_obj(pctx, type->of);
1109135446Strhodes	cfg_print_chars(pctx, "; ... }", 7);
1110135446Strhodes}
1111135446Strhodes
1112135446Strhodes/*
1113135446Strhodes * Parse a homogeneous list whose elements are of type 'elttype'
1114135446Strhodes * and where elements are separated by space.  The list ends
1115135446Strhodes * before the first semicolon.
1116135446Strhodes */
1117135446Strhodesisc_result_t
1118135446Strhodescfg_parse_spacelist(cfg_parser_t *pctx, const cfg_type_t *listtype,
1119135446Strhodes		    cfg_obj_t **ret)
1120135446Strhodes{
1121135446Strhodes	cfg_obj_t *listobj = NULL;
1122135446Strhodes	const cfg_type_t *listof = listtype->of;
1123135446Strhodes	isc_result_t result;
1124135446Strhodes
1125135446Strhodes	CHECK(cfg_create_list(pctx, listtype, &listobj));
1126135446Strhodes
1127135446Strhodes	for (;;) {
1128135446Strhodes		cfg_listelt_t *elt = NULL;
1129135446Strhodes
1130135446Strhodes		CHECK(cfg_peektoken(pctx, 0));
1131135446Strhodes		if (pctx->token.type == isc_tokentype_special &&
1132135446Strhodes		    pctx->token.value.as_char == ';')
1133135446Strhodes			break;
1134135446Strhodes		CHECK(cfg_parse_listelt(pctx, listof, &elt));
1135135446Strhodes		ISC_LIST_APPEND(listobj->value.list, elt, link);
1136135446Strhodes	}
1137135446Strhodes	*ret = listobj;
1138135446Strhodes	return (ISC_R_SUCCESS);
1139135446Strhodes
1140135446Strhodes cleanup:
1141135446Strhodes	CLEANUP_OBJ(listobj);
1142135446Strhodes	return (result);
1143135446Strhodes}
1144135446Strhodes
1145135446Strhodesvoid
1146165071Sdougbcfg_print_spacelist(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1147165071Sdougb	const cfg_list_t *list = &obj->value.list;
1148165071Sdougb	const cfg_listelt_t *elt;
1149135446Strhodes
1150135446Strhodes	for (elt = ISC_LIST_HEAD(*list);
1151135446Strhodes	     elt != NULL;
1152135446Strhodes	     elt = ISC_LIST_NEXT(elt, link)) {
1153135446Strhodes		cfg_print_obj(pctx, elt->obj);
1154135446Strhodes		if (ISC_LIST_NEXT(elt, link) != NULL)
1155135446Strhodes			cfg_print_chars(pctx, " ", 1);
1156135446Strhodes	}
1157135446Strhodes}
1158135446Strhodes
1159135446Strhodesisc_boolean_t
1160165071Sdougbcfg_obj_islist(const cfg_obj_t *obj) {
1161135446Strhodes	REQUIRE(obj != NULL);
1162135446Strhodes	return (ISC_TF(obj->type->rep == &cfg_rep_list));
1163135446Strhodes}
1164135446Strhodes
1165165071Sdougbconst cfg_listelt_t *
1166165071Sdougbcfg_list_first(const cfg_obj_t *obj) {
1167135446Strhodes	REQUIRE(obj == NULL || obj->type->rep == &cfg_rep_list);
1168135446Strhodes	if (obj == NULL)
1169135446Strhodes		return (NULL);
1170135446Strhodes	return (ISC_LIST_HEAD(obj->value.list));
1171135446Strhodes}
1172135446Strhodes
1173165071Sdougbconst cfg_listelt_t *
1174165071Sdougbcfg_list_next(const cfg_listelt_t *elt) {
1175135446Strhodes	REQUIRE(elt != NULL);
1176135446Strhodes	return (ISC_LIST_NEXT(elt, link));
1177135446Strhodes}
1178135446Strhodes
1179193149Sdougb/*
1180193149Sdougb * Return the length of a list object.  If obj is NULL or is not
1181193149Sdougb * a list, return 0.
1182193149Sdougb */
1183193149Sdougbunsigned int
1184193149Sdougbcfg_list_length(const cfg_obj_t *obj, isc_boolean_t recurse) {
1185193149Sdougb	const cfg_listelt_t *elt;
1186193149Sdougb	unsigned int count = 0;
1187193149Sdougb
1188193149Sdougb	if (obj == NULL || !cfg_obj_islist(obj))
1189193149Sdougb		return (0U);
1190193149Sdougb	for (elt = cfg_list_first(obj);
1191193149Sdougb	     elt != NULL;
1192193149Sdougb	     elt = cfg_list_next(elt)) {
1193193149Sdougb		if (recurse && cfg_obj_islist(elt->obj)) {
1194193149Sdougb			count += cfg_list_length(elt->obj, recurse);
1195193149Sdougb		} else {
1196193149Sdougb			count++;
1197193149Sdougb		}
1198193149Sdougb	}
1199193149Sdougb	return (count);
1200193149Sdougb}
1201193149Sdougb
1202224092Sdougbcfg_obj_t *
1203165071Sdougbcfg_listelt_value(const cfg_listelt_t *elt) {
1204135446Strhodes	REQUIRE(elt != NULL);
1205135446Strhodes	return (elt->obj);
1206135446Strhodes}
1207135446Strhodes
1208135446Strhodes/*
1209135446Strhodes * Maps.
1210135446Strhodes */
1211135446Strhodes
1212135446Strhodes/*
1213135446Strhodes * Parse a map body.  That's something like
1214135446Strhodes *
1215135446Strhodes *   "foo 1; bar { glub; }; zap true; zap false;"
1216135446Strhodes *
1217135446Strhodes * i.e., a sequence of option names followed by values and
1218135446Strhodes * terminated by semicolons.  Used for the top level of
1219135446Strhodes * the named.conf syntax, as well as for the body of the
1220135446Strhodes * options, view, zone, and other statements.
1221135446Strhodes */
1222135446Strhodesisc_result_t
1223135446Strhodescfg_parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
1224135446Strhodes{
1225135446Strhodes	const cfg_clausedef_t * const *clausesets = type->of;
1226135446Strhodes	isc_result_t result;
1227135446Strhodes	const cfg_clausedef_t * const *clauseset;
1228135446Strhodes	const cfg_clausedef_t *clause;
1229135446Strhodes	cfg_obj_t *value = NULL;
1230135446Strhodes	cfg_obj_t *obj = NULL;
1231135446Strhodes	cfg_obj_t *eltobj = NULL;
1232135446Strhodes	cfg_obj_t *includename = NULL;
1233135446Strhodes	isc_symvalue_t symval;
1234135446Strhodes	cfg_list_t *list = NULL;
1235135446Strhodes
1236135446Strhodes	CHECK(create_map(pctx, type, &obj));
1237135446Strhodes
1238135446Strhodes	obj->value.map.clausesets = clausesets;
1239135446Strhodes
1240135446Strhodes	for (;;) {
1241135446Strhodes		cfg_listelt_t *elt;
1242135446Strhodes
1243135446Strhodes	redo:
1244135446Strhodes		/*
1245135446Strhodes		 * Parse the option name and see if it is known.
1246135446Strhodes		 */
1247135446Strhodes		CHECK(cfg_gettoken(pctx, 0));
1248135446Strhodes
1249135446Strhodes		if (pctx->token.type != isc_tokentype_string) {
1250135446Strhodes			cfg_ungettoken(pctx);
1251135446Strhodes			break;
1252135446Strhodes		}
1253135446Strhodes
1254135446Strhodes		/*
1255135446Strhodes		 * We accept "include" statements wherever a map body
1256135446Strhodes		 * clause can occur.
1257135446Strhodes		 */
1258135446Strhodes		if (strcasecmp(TOKEN_STRING(pctx), "include") == 0) {
1259135446Strhodes			/*
1260135446Strhodes			 * Turn the file name into a temporary configuration
1261135446Strhodes			 * object just so that it is not overwritten by the
1262135446Strhodes			 * semicolon token.
1263135446Strhodes			 */
1264135446Strhodes			CHECK(cfg_parse_obj(pctx, &cfg_type_qstring, &includename));
1265135446Strhodes			CHECK(parse_semicolon(pctx));
1266135446Strhodes			CHECK(parser_openfile(pctx, includename->
1267135446Strhodes					      value.string.base));
1268135446Strhodes			 cfg_obj_destroy(pctx, &includename);
1269135446Strhodes			 goto redo;
1270135446Strhodes		}
1271135446Strhodes
1272135446Strhodes		clause = NULL;
1273135446Strhodes		for (clauseset = clausesets; *clauseset != NULL; clauseset++) {
1274135446Strhodes			for (clause = *clauseset;
1275135446Strhodes			     clause->name != NULL;
1276135446Strhodes			     clause++) {
1277135446Strhodes				if (strcasecmp(TOKEN_STRING(pctx),
1278135446Strhodes					   clause->name) == 0)
1279135446Strhodes					goto done;
1280135446Strhodes			}
1281135446Strhodes		}
1282135446Strhodes	done:
1283135446Strhodes		if (clause == NULL || clause->name == NULL) {
1284135446Strhodes			cfg_parser_error(pctx, CFG_LOG_NOPREP, "unknown option");
1285135446Strhodes			/*
1286135446Strhodes			 * Try to recover by parsing this option as an unknown
1287135446Strhodes			 * option and discarding it.
1288135446Strhodes			 */
1289135446Strhodes			CHECK(cfg_parse_obj(pctx, &cfg_type_unsupported, &eltobj));
1290135446Strhodes			cfg_obj_destroy(pctx, &eltobj);
1291135446Strhodes			CHECK(parse_semicolon(pctx));
1292135446Strhodes			continue;
1293135446Strhodes		}
1294135446Strhodes
1295135446Strhodes		/* Clause is known. */
1296135446Strhodes
1297135446Strhodes		/* Issue warnings if appropriate */
1298135446Strhodes		if ((clause->flags & CFG_CLAUSEFLAG_OBSOLETE) != 0)
1299135446Strhodes			cfg_parser_warning(pctx, 0, "option '%s' is obsolete",
1300135446Strhodes				       clause->name);
1301135446Strhodes		if ((clause->flags & CFG_CLAUSEFLAG_NOTIMP) != 0)
1302135446Strhodes			cfg_parser_warning(pctx, 0, "option '%s' is "
1303135446Strhodes				       "not implemented", clause->name);
1304135446Strhodes		if ((clause->flags & CFG_CLAUSEFLAG_NYI) != 0)
1305135446Strhodes			cfg_parser_warning(pctx, 0, "option '%s' is "
1306135446Strhodes				       "not implemented", clause->name);
1307224092Sdougb
1308224092Sdougb		if ((clause->flags & CFG_CLAUSEFLAG_NOTCONFIGURED) != 0) {
1309224092Sdougb			cfg_parser_warning(pctx, 0, "option '%s' is not "
1310224092Sdougb					   "configured", clause->name);
1311224092Sdougb			result = ISC_R_FAILURE;
1312224092Sdougb			goto cleanup;
1313224092Sdougb		}
1314224092Sdougb
1315135446Strhodes		/*
1316135446Strhodes		 * Don't log options with CFG_CLAUSEFLAG_NEWDEFAULT
1317135446Strhodes		 * set here - we need to log the *lack* of such an option,
1318135446Strhodes		 * not its presence.
1319135446Strhodes		 */
1320135446Strhodes
1321135446Strhodes		/* See if the clause already has a value; if not create one. */
1322135446Strhodes		result = isc_symtab_lookup(obj->value.map.symtab,
1323135446Strhodes					   clause->name, 0, &symval);
1324135446Strhodes
1325135446Strhodes		if ((clause->flags & CFG_CLAUSEFLAG_MULTI) != 0) {
1326135446Strhodes			/* Multivalued clause */
1327135446Strhodes			cfg_obj_t *listobj = NULL;
1328135446Strhodes			if (result == ISC_R_NOTFOUND) {
1329135446Strhodes				CHECK(cfg_create_list(pctx,
1330135446Strhodes						  &cfg_type_implicitlist,
1331135446Strhodes						  &listobj));
1332135446Strhodes				symval.as_pointer = listobj;
1333135446Strhodes				result = isc_symtab_define(obj->value.
1334135446Strhodes						   map.symtab,
1335135446Strhodes						   clause->name,
1336135446Strhodes						   1, symval,
1337135446Strhodes						   isc_symexists_reject);
1338135446Strhodes				if (result != ISC_R_SUCCESS) {
1339135446Strhodes					cfg_parser_error(pctx, CFG_LOG_NEAR,
1340135446Strhodes						     "isc_symtab_define(%s) "
1341135446Strhodes						     "failed", clause->name);
1342135446Strhodes					isc_mem_put(pctx->mctx, list,
1343135446Strhodes						    sizeof(cfg_list_t));
1344135446Strhodes					goto cleanup;
1345135446Strhodes				}
1346135446Strhodes			} else {
1347135446Strhodes				INSIST(result == ISC_R_SUCCESS);
1348135446Strhodes				listobj = symval.as_pointer;
1349135446Strhodes			}
1350135446Strhodes
1351135446Strhodes			elt = NULL;
1352135446Strhodes			CHECK(cfg_parse_listelt(pctx, clause->type, &elt));
1353135446Strhodes			CHECK(parse_semicolon(pctx));
1354135446Strhodes
1355135446Strhodes			ISC_LIST_APPEND(listobj->value.list, elt, link);
1356135446Strhodes		} else {
1357135446Strhodes			/* Single-valued clause */
1358135446Strhodes			if (result == ISC_R_NOTFOUND) {
1359135446Strhodes				isc_boolean_t callback =
1360135446Strhodes					ISC_TF((clause->flags &
1361135446Strhodes						CFG_CLAUSEFLAG_CALLBACK) != 0);
1362135446Strhodes				CHECK(parse_symtab_elt(pctx, clause->name,
1363135446Strhodes						       clause->type,
1364135446Strhodes						       obj->value.map.symtab,
1365135446Strhodes						       callback));
1366135446Strhodes				CHECK(parse_semicolon(pctx));
1367135446Strhodes			} else if (result == ISC_R_SUCCESS) {
1368135446Strhodes				cfg_parser_error(pctx, CFG_LOG_NEAR, "'%s' redefined",
1369135446Strhodes					     clause->name);
1370135446Strhodes				result = ISC_R_EXISTS;
1371135446Strhodes				goto cleanup;
1372135446Strhodes			} else {
1373135446Strhodes				cfg_parser_error(pctx, CFG_LOG_NEAR,
1374135446Strhodes					     "isc_symtab_define() failed");
1375135446Strhodes				goto cleanup;
1376135446Strhodes			}
1377135446Strhodes		}
1378135446Strhodes	}
1379135446Strhodes
1380135446Strhodes
1381135446Strhodes	*ret = obj;
1382135446Strhodes	return (ISC_R_SUCCESS);
1383135446Strhodes
1384135446Strhodes cleanup:
1385135446Strhodes	CLEANUP_OBJ(value);
1386135446Strhodes	CLEANUP_OBJ(obj);
1387135446Strhodes	CLEANUP_OBJ(eltobj);
1388135446Strhodes	CLEANUP_OBJ(includename);
1389135446Strhodes	return (result);
1390135446Strhodes}
1391135446Strhodes
1392135446Strhodesstatic isc_result_t
1393135446Strhodesparse_symtab_elt(cfg_parser_t *pctx, const char *name,
1394135446Strhodes		 cfg_type_t *elttype, isc_symtab_t *symtab,
1395135446Strhodes		 isc_boolean_t callback)
1396135446Strhodes{
1397135446Strhodes	isc_result_t result;
1398135446Strhodes	cfg_obj_t *obj = NULL;
1399135446Strhodes	isc_symvalue_t symval;
1400135446Strhodes
1401135446Strhodes	CHECK(cfg_parse_obj(pctx, elttype, &obj));
1402135446Strhodes
1403135446Strhodes	if (callback && pctx->callback != NULL)
1404135446Strhodes		CHECK(pctx->callback(name, obj, pctx->callbackarg));
1405193149Sdougb
1406135446Strhodes	symval.as_pointer = obj;
1407135446Strhodes	CHECK(isc_symtab_define(symtab, name,
1408135446Strhodes				1, symval,
1409135446Strhodes				isc_symexists_reject));
1410135446Strhodes	return (ISC_R_SUCCESS);
1411135446Strhodes
1412135446Strhodes cleanup:
1413135446Strhodes	CLEANUP_OBJ(obj);
1414135446Strhodes	return (result);
1415135446Strhodes}
1416135446Strhodes
1417135446Strhodes/*
1418135446Strhodes * Parse a map; e.g., "{ foo 1; bar { glub; }; zap true; zap false; }"
1419135446Strhodes */
1420135446Strhodesisc_result_t
1421135446Strhodescfg_parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1422135446Strhodes	isc_result_t result;
1423135446Strhodes	CHECK(cfg_parse_special(pctx, '{'));
1424135446Strhodes	CHECK(cfg_parse_mapbody(pctx, type, ret));
1425135446Strhodes	CHECK(cfg_parse_special(pctx, '}'));
1426135446Strhodes cleanup:
1427135446Strhodes	return (result);
1428135446Strhodes}
1429135446Strhodes
1430135446Strhodes/*
1431135446Strhodes * Subroutine for cfg_parse_named_map() and cfg_parse_addressed_map().
1432135446Strhodes */
1433135446Strhodesstatic isc_result_t
1434135446Strhodesparse_any_named_map(cfg_parser_t *pctx, cfg_type_t *nametype, const cfg_type_t *type,
1435135446Strhodes		    cfg_obj_t **ret)
1436135446Strhodes{
1437135446Strhodes	isc_result_t result;
1438135446Strhodes	cfg_obj_t *idobj = NULL;
1439135446Strhodes	cfg_obj_t *mapobj = NULL;
1440135446Strhodes
1441135446Strhodes	CHECK(cfg_parse_obj(pctx, nametype, &idobj));
1442135446Strhodes	CHECK(cfg_parse_map(pctx, type, &mapobj));
1443135446Strhodes	mapobj->value.map.id = idobj;
1444135446Strhodes	idobj = NULL;
1445135446Strhodes	*ret = mapobj;
1446135446Strhodes cleanup:
1447135446Strhodes	CLEANUP_OBJ(idobj);
1448135446Strhodes	return (result);
1449135446Strhodes}
1450135446Strhodes
1451135446Strhodes/*
1452193149Sdougb * Parse a map identified by a string name.  E.g., "name { foo 1; }".
1453135446Strhodes * Used for the "key" and "channel" statements.
1454135446Strhodes */
1455135446Strhodesisc_result_t
1456135446Strhodescfg_parse_named_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1457135446Strhodes	return (parse_any_named_map(pctx, &cfg_type_astring, type, ret));
1458135446Strhodes}
1459135446Strhodes
1460135446Strhodes/*
1461135446Strhodes * Parse a map identified by a network address.
1462170222Sdougb * Used to be used for the "server" statement.
1463135446Strhodes */
1464135446Strhodesisc_result_t
1465135446Strhodescfg_parse_addressed_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1466135446Strhodes	return (parse_any_named_map(pctx, &cfg_type_netaddr, type, ret));
1467135446Strhodes}
1468135446Strhodes
1469170222Sdougb/*
1470170222Sdougb * Parse a map identified by a network prefix.
1471170222Sdougb * Used for the "server" statement.
1472170222Sdougb */
1473170222Sdougbisc_result_t
1474170222Sdougbcfg_parse_netprefix_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1475170222Sdougb	return (parse_any_named_map(pctx, &cfg_type_netprefix, type, ret));
1476170222Sdougb}
1477170222Sdougb
1478135446Strhodesvoid
1479165071Sdougbcfg_print_mapbody(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1480135446Strhodes	isc_result_t result = ISC_R_SUCCESS;
1481135446Strhodes
1482135446Strhodes	const cfg_clausedef_t * const *clauseset;
1483135446Strhodes
1484135446Strhodes	for (clauseset = obj->value.map.clausesets;
1485135446Strhodes	     *clauseset != NULL;
1486135446Strhodes	     clauseset++)
1487135446Strhodes	{
1488135446Strhodes		isc_symvalue_t symval;
1489135446Strhodes		const cfg_clausedef_t *clause;
1490135446Strhodes
1491135446Strhodes		for (clause = *clauseset;
1492135446Strhodes		     clause->name != NULL;
1493135446Strhodes		     clause++) {
1494135446Strhodes			result = isc_symtab_lookup(obj->value.map.symtab,
1495135446Strhodes						   clause->name, 0, &symval);
1496135446Strhodes			if (result == ISC_R_SUCCESS) {
1497135446Strhodes				cfg_obj_t *obj = symval.as_pointer;
1498135446Strhodes				if (obj->type == &cfg_type_implicitlist) {
1499135446Strhodes					/* Multivalued. */
1500135446Strhodes					cfg_list_t *list = &obj->value.list;
1501135446Strhodes					cfg_listelt_t *elt;
1502135446Strhodes					for (elt = ISC_LIST_HEAD(*list);
1503135446Strhodes					     elt != NULL;
1504135446Strhodes					     elt = ISC_LIST_NEXT(elt, link)) {
1505135446Strhodes						print_indent(pctx);
1506135446Strhodes						cfg_print_cstr(pctx, clause->name);
1507135446Strhodes						cfg_print_chars(pctx, " ", 1);
1508135446Strhodes						cfg_print_obj(pctx, elt->obj);
1509135446Strhodes						cfg_print_chars(pctx, ";\n", 2);
1510135446Strhodes					}
1511135446Strhodes				} else {
1512135446Strhodes					/* Single-valued. */
1513135446Strhodes					print_indent(pctx);
1514135446Strhodes					cfg_print_cstr(pctx, clause->name);
1515135446Strhodes					cfg_print_chars(pctx, " ", 1);
1516135446Strhodes					cfg_print_obj(pctx, obj);
1517135446Strhodes					cfg_print_chars(pctx, ";\n", 2);
1518135446Strhodes				}
1519135446Strhodes			} else if (result == ISC_R_NOTFOUND) {
1520135446Strhodes				; /* do nothing */
1521135446Strhodes			} else {
1522135446Strhodes				INSIST(0);
1523135446Strhodes			}
1524135446Strhodes		}
1525135446Strhodes	}
1526135446Strhodes}
1527135446Strhodes
1528135446Strhodesvoid
1529135446Strhodescfg_doc_mapbody(cfg_printer_t *pctx, const cfg_type_t *type) {
1530135446Strhodes	const cfg_clausedef_t * const *clauseset;
1531135446Strhodes	const cfg_clausedef_t *clause;
1532193149Sdougb
1533135446Strhodes	for (clauseset = type->of; *clauseset != NULL; clauseset++) {
1534135446Strhodes		for (clause = *clauseset;
1535135446Strhodes		     clause->name != NULL;
1536135446Strhodes		     clause++) {
1537135446Strhodes			cfg_print_cstr(pctx, clause->name);
1538135446Strhodes			cfg_print_chars(pctx, " ", 1);
1539135446Strhodes			cfg_doc_obj(pctx, clause->type);
1540135446Strhodes			cfg_print_chars(pctx, ";", 1);
1541135446Strhodes			/* XXX print flags here? */
1542135446Strhodes			cfg_print_chars(pctx, "\n\n", 2);
1543135446Strhodes		}
1544135446Strhodes	}
1545135446Strhodes}
1546135446Strhodes
1547135446Strhodesstatic struct flagtext {
1548135446Strhodes	unsigned int flag;
1549135446Strhodes	const char *text;
1550135446Strhodes} flagtexts[] = {
1551135446Strhodes	{ CFG_CLAUSEFLAG_NOTIMP, "not implemented" },
1552135446Strhodes	{ CFG_CLAUSEFLAG_NYI, "not yet implemented" },
1553135446Strhodes	{ CFG_CLAUSEFLAG_OBSOLETE, "obsolete" },
1554135446Strhodes	{ CFG_CLAUSEFLAG_NEWDEFAULT, "default changed" },
1555193149Sdougb	{ CFG_CLAUSEFLAG_TESTONLY, "test only" },
1556224092Sdougb	{ CFG_CLAUSEFLAG_NOTCONFIGURED, "not configured" },
1557135446Strhodes	{ 0, NULL }
1558135446Strhodes};
1559135446Strhodes
1560135446Strhodesvoid
1561165071Sdougbcfg_print_map(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1562135446Strhodes	if (obj->value.map.id != NULL) {
1563135446Strhodes		cfg_print_obj(pctx, obj->value.map.id);
1564135446Strhodes		cfg_print_chars(pctx, " ", 1);
1565135446Strhodes	}
1566135446Strhodes	print_open(pctx);
1567135446Strhodes	cfg_print_mapbody(pctx, obj);
1568135446Strhodes	print_close(pctx);
1569135446Strhodes}
1570135446Strhodes
1571135446Strhodesstatic void
1572135446Strhodesprint_clause_flags(cfg_printer_t *pctx, unsigned int flags) {
1573135446Strhodes	struct flagtext *p;
1574135446Strhodes	isc_boolean_t first = ISC_TRUE;
1575135446Strhodes	for (p = flagtexts; p->flag != 0; p++) {
1576135446Strhodes		if ((flags & p->flag) != 0) {
1577135446Strhodes			if (first)
1578135446Strhodes				cfg_print_chars(pctx, " // ", 4);
1579135446Strhodes			else
1580135446Strhodes				cfg_print_chars(pctx, ", ", 2);
1581135446Strhodes			cfg_print_cstr(pctx, p->text);
1582135446Strhodes			first = ISC_FALSE;
1583135446Strhodes		}
1584135446Strhodes	}
1585135446Strhodes}
1586135446Strhodes
1587135446Strhodesvoid
1588135446Strhodescfg_doc_map(cfg_printer_t *pctx, const cfg_type_t *type) {
1589135446Strhodes	const cfg_clausedef_t * const *clauseset;
1590135446Strhodes	const cfg_clausedef_t *clause;
1591193149Sdougb
1592135446Strhodes	if (type->parse == cfg_parse_named_map) {
1593135446Strhodes		cfg_doc_obj(pctx, &cfg_type_astring);
1594135446Strhodes		cfg_print_chars(pctx, " ", 1);
1595135446Strhodes	} else if (type->parse == cfg_parse_addressed_map) {
1596135446Strhodes		cfg_doc_obj(pctx, &cfg_type_netaddr);
1597135446Strhodes		cfg_print_chars(pctx, " ", 1);
1598170222Sdougb	} else if (type->parse == cfg_parse_netprefix_map) {
1599170222Sdougb		cfg_doc_obj(pctx, &cfg_type_netprefix);
1600170222Sdougb		cfg_print_chars(pctx, " ", 1);
1601135446Strhodes	}
1602193149Sdougb
1603135446Strhodes	print_open(pctx);
1604193149Sdougb
1605135446Strhodes	for (clauseset = type->of; *clauseset != NULL; clauseset++) {
1606135446Strhodes		for (clause = *clauseset;
1607135446Strhodes		     clause->name != NULL;
1608135446Strhodes		     clause++) {
1609135446Strhodes			print_indent(pctx);
1610135446Strhodes			cfg_print_cstr(pctx, clause->name);
1611135446Strhodes			if (clause->type->print != cfg_print_void)
1612135446Strhodes				cfg_print_chars(pctx, " ", 1);
1613135446Strhodes			cfg_doc_obj(pctx, clause->type);
1614135446Strhodes			cfg_print_chars(pctx, ";", 1);
1615135446Strhodes			print_clause_flags(pctx, clause->flags);
1616135446Strhodes			cfg_print_chars(pctx, "\n", 1);
1617135446Strhodes		}
1618135446Strhodes	}
1619135446Strhodes	print_close(pctx);
1620135446Strhodes}
1621135446Strhodes
1622135446Strhodesisc_boolean_t
1623165071Sdougbcfg_obj_ismap(const cfg_obj_t *obj) {
1624135446Strhodes	REQUIRE(obj != NULL);
1625135446Strhodes	return (ISC_TF(obj->type->rep == &cfg_rep_map));
1626135446Strhodes}
1627135446Strhodes
1628135446Strhodesisc_result_t
1629165071Sdougbcfg_map_get(const cfg_obj_t *mapobj, const char* name, const cfg_obj_t **obj) {
1630135446Strhodes	isc_result_t result;
1631135446Strhodes	isc_symvalue_t val;
1632165071Sdougb	const cfg_map_t *map;
1633193149Sdougb
1634135446Strhodes	REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
1635135446Strhodes	REQUIRE(name != NULL);
1636135446Strhodes	REQUIRE(obj != NULL && *obj == NULL);
1637135446Strhodes
1638135446Strhodes	map = &mapobj->value.map;
1639193149Sdougb
1640135446Strhodes	result = isc_symtab_lookup(map->symtab, name, MAP_SYM, &val);
1641135446Strhodes	if (result != ISC_R_SUCCESS)
1642135446Strhodes		return (result);
1643135446Strhodes	*obj = val.as_pointer;
1644135446Strhodes	return (ISC_R_SUCCESS);
1645135446Strhodes}
1646135446Strhodes
1647165071Sdougbconst cfg_obj_t *
1648165071Sdougbcfg_map_getname(const cfg_obj_t *mapobj) {
1649135446Strhodes	REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
1650135446Strhodes	return (mapobj->value.map.id);
1651135446Strhodes}
1652135446Strhodes
1653135446Strhodes
1654135446Strhodes/* Parse an arbitrary token, storing its raw text representation. */
1655135446Strhodesstatic isc_result_t
1656135446Strhodesparse_token(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1657135446Strhodes	cfg_obj_t *obj = NULL;
1658193149Sdougb	isc_result_t result;
1659135446Strhodes	isc_region_t r;
1660135446Strhodes
1661135446Strhodes	UNUSED(type);
1662135446Strhodes
1663135446Strhodes	CHECK(cfg_create_obj(pctx, &cfg_type_token, &obj));
1664135446Strhodes	CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
1665135446Strhodes	if (pctx->token.type == isc_tokentype_eof) {
1666135446Strhodes		cfg_ungettoken(pctx);
1667135446Strhodes		result = ISC_R_EOF;
1668135446Strhodes		goto cleanup;
1669135446Strhodes	}
1670135446Strhodes
1671135446Strhodes	isc_lex_getlasttokentext(pctx->lexer, &pctx->token, &r);
1672135446Strhodes
1673135446Strhodes	obj->value.string.base = isc_mem_get(pctx->mctx, r.length + 1);
1674165071Sdougb	if (obj->value.string.base == NULL) {
1675165071Sdougb		result = ISC_R_NOMEMORY;
1676165071Sdougb		goto cleanup;
1677165071Sdougb	}
1678135446Strhodes	obj->value.string.length = r.length;
1679262706Serwin	memmove(obj->value.string.base, r.base, r.length);
1680135446Strhodes	obj->value.string.base[r.length] = '\0';
1681135446Strhodes	*ret = obj;
1682165071Sdougb	return (result);
1683135446Strhodes
1684135446Strhodes cleanup:
1685165071Sdougb	if (obj != NULL)
1686165071Sdougb		isc_mem_put(pctx->mctx, obj, sizeof(*obj));
1687135446Strhodes	return (result);
1688135446Strhodes}
1689135446Strhodes
1690135446Strhodescfg_type_t cfg_type_token = {
1691135446Strhodes	"token", parse_token, cfg_print_ustring, cfg_doc_terminal,
1692135446Strhodes	&cfg_rep_string, NULL
1693135446Strhodes};
1694135446Strhodes
1695135446Strhodes/*
1696135446Strhodes * An unsupported option.  This is just a list of tokens with balanced braces
1697135446Strhodes * ending in a semicolon.
1698135446Strhodes */
1699135446Strhodes
1700135446Strhodesstatic isc_result_t
1701135446Strhodesparse_unsupported(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1702135446Strhodes	cfg_obj_t *listobj = NULL;
1703135446Strhodes	isc_result_t result;
1704135446Strhodes	int braces = 0;
1705135446Strhodes
1706135446Strhodes	CHECK(cfg_create_list(pctx, type, &listobj));
1707135446Strhodes
1708135446Strhodes	for (;;) {
1709135446Strhodes		cfg_listelt_t *elt = NULL;
1710135446Strhodes
1711135446Strhodes		CHECK(cfg_peektoken(pctx, 0));
1712135446Strhodes		if (pctx->token.type == isc_tokentype_special) {
1713135446Strhodes			if (pctx->token.value.as_char == '{')
1714135446Strhodes				braces++;
1715135446Strhodes			else if (pctx->token.value.as_char == '}')
1716135446Strhodes				braces--;
1717135446Strhodes			else if (pctx->token.value.as_char == ';')
1718135446Strhodes				if (braces == 0)
1719135446Strhodes					break;
1720135446Strhodes		}
1721135446Strhodes		if (pctx->token.type == isc_tokentype_eof || braces < 0) {
1722135446Strhodes			cfg_parser_error(pctx, CFG_LOG_NEAR, "unexpected token");
1723135446Strhodes			result = ISC_R_UNEXPECTEDTOKEN;
1724135446Strhodes			goto cleanup;
1725135446Strhodes		}
1726135446Strhodes
1727135446Strhodes		CHECK(cfg_parse_listelt(pctx, &cfg_type_token, &elt));
1728135446Strhodes		ISC_LIST_APPEND(listobj->value.list, elt, link);
1729135446Strhodes	}
1730135446Strhodes	INSIST(braces == 0);
1731135446Strhodes	*ret = listobj;
1732135446Strhodes	return (ISC_R_SUCCESS);
1733135446Strhodes
1734135446Strhodes cleanup:
1735135446Strhodes	CLEANUP_OBJ(listobj);
1736135446Strhodes	return (result);
1737135446Strhodes}
1738135446Strhodes
1739135446Strhodescfg_type_t cfg_type_unsupported = {
1740135446Strhodes	"unsupported", parse_unsupported, cfg_print_spacelist, cfg_doc_terminal,
1741135446Strhodes	&cfg_rep_list, NULL
1742135446Strhodes};
1743135446Strhodes
1744135446Strhodes/*
1745135446Strhodes * Try interpreting the current token as a network address.
1746135446Strhodes *
1747135446Strhodes * If CFG_ADDR_WILDOK is set in flags, "*" can be used as a wildcard
1748135446Strhodes * and at least one of CFG_ADDR_V4OK and CFG_ADDR_V6OK must also be set.  The
1749193149Sdougb * "*" is interpreted as the IPv4 wildcard address if CFG_ADDR_V4OK is
1750135446Strhodes * set (including the case where CFG_ADDR_V4OK and CFG_ADDR_V6OK are both set),
1751135446Strhodes * and the IPv6 wildcard address otherwise.
1752135446Strhodes */
1753135446Strhodesstatic isc_result_t
1754135446Strhodestoken_addr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
1755135446Strhodes	char *s;
1756135446Strhodes	struct in_addr in4a;
1757135446Strhodes	struct in6_addr in6a;
1758135446Strhodes
1759135446Strhodes	if (pctx->token.type != isc_tokentype_string)
1760135446Strhodes		return (ISC_R_UNEXPECTEDTOKEN);
1761135446Strhodes
1762135446Strhodes	s = TOKEN_STRING(pctx);
1763135446Strhodes	if ((flags & CFG_ADDR_WILDOK) != 0 && strcmp(s, "*") == 0) {
1764135446Strhodes		if ((flags & CFG_ADDR_V4OK) != 0) {
1765135446Strhodes			isc_netaddr_any(na);
1766135446Strhodes			return (ISC_R_SUCCESS);
1767135446Strhodes		} else if ((flags & CFG_ADDR_V6OK) != 0) {
1768135446Strhodes			isc_netaddr_any6(na);
1769135446Strhodes			return (ISC_R_SUCCESS);
1770135446Strhodes		} else {
1771135446Strhodes			INSIST(0);
1772135446Strhodes		}
1773135446Strhodes	} else {
1774135446Strhodes		if ((flags & (CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK)) != 0) {
1775135446Strhodes			if (inet_pton(AF_INET, s, &in4a) == 1) {
1776135446Strhodes				isc_netaddr_fromin(na, &in4a);
1777135446Strhodes				return (ISC_R_SUCCESS);
1778135446Strhodes			}
1779135446Strhodes		}
1780135446Strhodes		if ((flags & CFG_ADDR_V4PREFIXOK) != 0 &&
1781135446Strhodes		    strlen(s) <= 15U) {
1782135446Strhodes			char buf[64];
1783135446Strhodes			int i;
1784135446Strhodes
1785135446Strhodes			strcpy(buf, s);
1786135446Strhodes			for (i = 0; i < 3; i++) {
1787135446Strhodes				strcat(buf, ".0");
1788135446Strhodes				if (inet_pton(AF_INET, buf, &in4a) == 1) {
1789135446Strhodes					isc_netaddr_fromin(na, &in4a);
1790135446Strhodes					return (ISC_R_SUCCESS);
1791135446Strhodes				}
1792135446Strhodes			}
1793135446Strhodes		}
1794135446Strhodes		if ((flags & CFG_ADDR_V6OK) != 0 &&
1795135446Strhodes		    strlen(s) <= 127U) {
1796135446Strhodes			char buf[128]; /* see lib/bind9/getaddresses.c */
1797135446Strhodes			char *d; /* zone delimiter */
1798135446Strhodes			isc_uint32_t zone = 0; /* scope zone ID */
1799135446Strhodes
1800135446Strhodes			strcpy(buf, s);
1801135446Strhodes			d = strchr(buf, '%');
1802135446Strhodes			if (d != NULL)
1803135446Strhodes				*d = '\0';
1804135446Strhodes
1805135446Strhodes			if (inet_pton(AF_INET6, buf, &in6a) == 1) {
1806135446Strhodes				if (d != NULL) {
1807135446Strhodes#ifdef ISC_PLATFORM_HAVESCOPEID
1808135446Strhodes					isc_result_t result;
1809135446Strhodes
1810135446Strhodes					result = isc_netscope_pton(AF_INET6,
1811135446Strhodes								   d + 1,
1812135446Strhodes								   &in6a,
1813135446Strhodes								   &zone);
1814135446Strhodes					if (result != ISC_R_SUCCESS)
1815135446Strhodes						return (result);
1816135446Strhodes#else
1817135446Strhodes				return (ISC_R_BADADDRESSFORM);
1818135446Strhodes#endif
1819135446Strhodes				}
1820135446Strhodes
1821135446Strhodes				isc_netaddr_fromin6(na, &in6a);
1822135446Strhodes				isc_netaddr_setzone(na, zone);
1823135446Strhodes				return (ISC_R_SUCCESS);
1824135446Strhodes			}
1825135446Strhodes		}
1826135446Strhodes	}
1827135446Strhodes	return (ISC_R_UNEXPECTEDTOKEN);
1828135446Strhodes}
1829135446Strhodes
1830135446Strhodesisc_result_t
1831135446Strhodescfg_parse_rawaddr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
1832135446Strhodes	isc_result_t result;
1833170222Sdougb	const char *wild = "";
1834170222Sdougb	const char *prefix = "";
1835170222Sdougb
1836135446Strhodes	CHECK(cfg_gettoken(pctx, 0));
1837135446Strhodes	result = token_addr(pctx, flags, na);
1838170222Sdougb	if (result == ISC_R_UNEXPECTEDTOKEN) {
1839170222Sdougb		if ((flags & CFG_ADDR_WILDOK) != 0)
1840170222Sdougb			wild = " or '*'";
1841170222Sdougb		if ((flags & CFG_ADDR_V4PREFIXOK) != 0)
1842170222Sdougb			wild = " or IPv4 prefix";
1843170222Sdougb		if ((flags & CFG_ADDR_MASK) == CFG_ADDR_V4OK)
1844170222Sdougb			cfg_parser_error(pctx, CFG_LOG_NEAR,
1845170222Sdougb					 "expected IPv4 address%s%s",
1846170222Sdougb					 prefix, wild);
1847170222Sdougb		else if ((flags & CFG_ADDR_MASK) == CFG_ADDR_V6OK)
1848170222Sdougb			cfg_parser_error(pctx, CFG_LOG_NEAR,
1849170222Sdougb					 "expected IPv6 address%s%s",
1850170222Sdougb					 prefix, wild);
1851170222Sdougb		else
1852170222Sdougb			cfg_parser_error(pctx, CFG_LOG_NEAR,
1853170222Sdougb					 "expected IP address%s%s",
1854170222Sdougb					 prefix, wild);
1855170222Sdougb	}
1856135446Strhodes cleanup:
1857135446Strhodes	return (result);
1858135446Strhodes}
1859135446Strhodes
1860135446Strhodesisc_boolean_t
1861135446Strhodescfg_lookingat_netaddr(cfg_parser_t *pctx, unsigned int flags) {
1862135446Strhodes	isc_result_t result;
1863135446Strhodes	isc_netaddr_t na_dummy;
1864135446Strhodes	result = token_addr(pctx, flags, &na_dummy);
1865135446Strhodes	return (ISC_TF(result == ISC_R_SUCCESS));
1866135446Strhodes}
1867135446Strhodes
1868135446Strhodesisc_result_t
1869135446Strhodescfg_parse_rawport(cfg_parser_t *pctx, unsigned int flags, in_port_t *port) {
1870135446Strhodes	isc_result_t result;
1871135446Strhodes
1872135446Strhodes	CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
1873135446Strhodes
1874135446Strhodes	if ((flags & CFG_ADDR_WILDOK) != 0 &&
1875135446Strhodes	    pctx->token.type == isc_tokentype_string &&
1876135446Strhodes	    strcmp(TOKEN_STRING(pctx), "*") == 0) {
1877135446Strhodes		*port = 0;
1878135446Strhodes		return (ISC_R_SUCCESS);
1879135446Strhodes	}
1880135446Strhodes	if (pctx->token.type != isc_tokentype_number) {
1881135446Strhodes		cfg_parser_error(pctx, CFG_LOG_NEAR,
1882135446Strhodes			     "expected port number or '*'");
1883135446Strhodes		return (ISC_R_UNEXPECTEDTOKEN);
1884135446Strhodes	}
1885135446Strhodes	if (pctx->token.value.as_ulong >= 65536U) {
1886135446Strhodes		cfg_parser_error(pctx, CFG_LOG_NEAR,
1887135446Strhodes			     "port number out of range");
1888135446Strhodes		return (ISC_R_UNEXPECTEDTOKEN);
1889135446Strhodes	}
1890135446Strhodes	*port = (in_port_t)(pctx->token.value.as_ulong);
1891135446Strhodes	return (ISC_R_SUCCESS);
1892135446Strhodes cleanup:
1893135446Strhodes	return (result);
1894135446Strhodes}
1895135446Strhodes
1896135446Strhodesvoid
1897165071Sdougbcfg_print_rawaddr(cfg_printer_t *pctx, const isc_netaddr_t *na) {
1898135446Strhodes	isc_result_t result;
1899135446Strhodes	char text[128];
1900135446Strhodes	isc_buffer_t buf;
1901135446Strhodes
1902135446Strhodes	isc_buffer_init(&buf, text, sizeof(text));
1903135446Strhodes	result = isc_netaddr_totext(na, &buf);
1904135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
1905135446Strhodes	cfg_print_chars(pctx, isc_buffer_base(&buf), isc_buffer_usedlength(&buf));
1906135446Strhodes}
1907135446Strhodes
1908135446Strhodes/* netaddr */
1909135446Strhodes
1910170222Sdougbstatic unsigned int netaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK;
1911170222Sdougbstatic unsigned int netaddr4_flags = CFG_ADDR_V4OK;
1912170222Sdougbstatic unsigned int netaddr4wild_flags = CFG_ADDR_V4OK | CFG_ADDR_WILDOK;
1913170222Sdougbstatic unsigned int netaddr6_flags = CFG_ADDR_V6OK;
1914170222Sdougbstatic unsigned int netaddr6wild_flags = CFG_ADDR_V6OK | CFG_ADDR_WILDOK;
1915170222Sdougb
1916135446Strhodesstatic isc_result_t
1917135446Strhodesparse_netaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1918135446Strhodes	isc_result_t result;
1919135446Strhodes	cfg_obj_t *obj = NULL;
1920135446Strhodes	isc_netaddr_t netaddr;
1921170222Sdougb	unsigned int flags = *(const unsigned int *)type->of;
1922170222Sdougb
1923135446Strhodes	CHECK(cfg_create_obj(pctx, type, &obj));
1924170222Sdougb	CHECK(cfg_parse_rawaddr(pctx, flags, &netaddr));
1925135446Strhodes	isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, 0);
1926135446Strhodes	*ret = obj;
1927135446Strhodes	return (ISC_R_SUCCESS);
1928135446Strhodes cleanup:
1929135446Strhodes	CLEANUP_OBJ(obj);
1930135446Strhodes	return (result);
1931135446Strhodes}
1932135446Strhodes
1933170222Sdougbstatic void
1934170222Sdougbcfg_doc_netaddr(cfg_printer_t *pctx, const cfg_type_t *type) {
1935170222Sdougb	const unsigned int *flagp = type->of;
1936170222Sdougb	int n = 0;
1937170222Sdougb	if (*flagp != CFG_ADDR_V4OK && *flagp != CFG_ADDR_V6OK)
1938170222Sdougb		cfg_print_chars(pctx, "( ", 2);
1939170222Sdougb	if (*flagp & CFG_ADDR_V4OK) {
1940170222Sdougb		cfg_print_cstr(pctx, "<ipv4_address>");
1941170222Sdougb		n++;
1942170222Sdougb	}
1943170222Sdougb	if (*flagp & CFG_ADDR_V6OK) {
1944170222Sdougb		if (n != 0)
1945170222Sdougb			cfg_print_chars(pctx, " | ", 3);
1946170222Sdougb		cfg_print_cstr(pctx, "<ipv6_address>");
1947193149Sdougb		n++;
1948170222Sdougb	}
1949170222Sdougb	if (*flagp & CFG_ADDR_WILDOK) {
1950170222Sdougb		if (n != 0)
1951170222Sdougb			cfg_print_chars(pctx, " | ", 3);
1952170222Sdougb		cfg_print_chars(pctx, "*", 1);
1953170222Sdougb		n++;
1954225361Sdougb		POST(n);
1955170222Sdougb	}
1956170222Sdougb	if (*flagp != CFG_ADDR_V4OK && *flagp != CFG_ADDR_V6OK)
1957170222Sdougb		cfg_print_chars(pctx, " )", 2);
1958170222Sdougb}
1959170222Sdougb
1960135446Strhodescfg_type_t cfg_type_netaddr = {
1961170222Sdougb	"netaddr", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
1962170222Sdougb	&cfg_rep_sockaddr, &netaddr_flags
1963135446Strhodes};
1964135446Strhodes
1965170222Sdougbcfg_type_t cfg_type_netaddr4 = {
1966170222Sdougb	"netaddr4", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
1967170222Sdougb	&cfg_rep_sockaddr, &netaddr4_flags
1968170222Sdougb};
1969170222Sdougb
1970170222Sdougbcfg_type_t cfg_type_netaddr4wild = {
1971170222Sdougb	"netaddr4wild", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
1972170222Sdougb	&cfg_rep_sockaddr, &netaddr4wild_flags
1973170222Sdougb};
1974170222Sdougb
1975170222Sdougbcfg_type_t cfg_type_netaddr6 = {
1976170222Sdougb	"netaddr6", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
1977170222Sdougb	&cfg_rep_sockaddr, &netaddr6_flags
1978170222Sdougb};
1979170222Sdougb
1980170222Sdougbcfg_type_t cfg_type_netaddr6wild = {
1981170222Sdougb	"netaddr6wild", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
1982170222Sdougb	&cfg_rep_sockaddr, &netaddr6wild_flags
1983170222Sdougb};
1984170222Sdougb
1985135446Strhodes/* netprefix */
1986135446Strhodes
1987135446Strhodesisc_result_t
1988135446Strhodescfg_parse_netprefix(cfg_parser_t *pctx, const cfg_type_t *type,
1989135446Strhodes		    cfg_obj_t **ret)
1990135446Strhodes{
1991135446Strhodes	cfg_obj_t *obj = NULL;
1992135446Strhodes	isc_result_t result;
1993135446Strhodes	isc_netaddr_t netaddr;
1994225361Sdougb	unsigned int addrlen = 0, prefixlen;
1995135446Strhodes	UNUSED(type);
1996135446Strhodes
1997135446Strhodes	CHECK(cfg_parse_rawaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK |
1998135446Strhodes				CFG_ADDR_V6OK, &netaddr));
1999135446Strhodes	switch (netaddr.family) {
2000135446Strhodes	case AF_INET:
2001135446Strhodes		addrlen = 32;
2002135446Strhodes		break;
2003135446Strhodes	case AF_INET6:
2004135446Strhodes		addrlen = 128;
2005135446Strhodes		break;
2006135446Strhodes	default:
2007135446Strhodes		INSIST(0);
2008135446Strhodes		break;
2009135446Strhodes	}
2010135446Strhodes	CHECK(cfg_peektoken(pctx, 0));
2011135446Strhodes	if (pctx->token.type == isc_tokentype_special &&
2012135446Strhodes	    pctx->token.value.as_char == '/') {
2013135446Strhodes		CHECK(cfg_gettoken(pctx, 0)); /* read "/" */
2014135446Strhodes		CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
2015135446Strhodes		if (pctx->token.type != isc_tokentype_number) {
2016135446Strhodes			cfg_parser_error(pctx, CFG_LOG_NEAR,
2017135446Strhodes				     "expected prefix length");
2018135446Strhodes			return (ISC_R_UNEXPECTEDTOKEN);
2019135446Strhodes		}
2020135446Strhodes		prefixlen = pctx->token.value.as_ulong;
2021135446Strhodes		if (prefixlen > addrlen) {
2022135446Strhodes			cfg_parser_error(pctx, CFG_LOG_NOPREP,
2023135446Strhodes				     "invalid prefix length");
2024135446Strhodes			return (ISC_R_RANGE);
2025135446Strhodes		}
2026135446Strhodes	} else {
2027135446Strhodes		prefixlen = addrlen;
2028135446Strhodes	}
2029135446Strhodes	CHECK(cfg_create_obj(pctx, &cfg_type_netprefix, &obj));
2030135446Strhodes	obj->value.netprefix.address = netaddr;
2031135446Strhodes	obj->value.netprefix.prefixlen = prefixlen;
2032135446Strhodes	*ret = obj;
2033135446Strhodes	return (ISC_R_SUCCESS);
2034135446Strhodes cleanup:
2035135446Strhodes	cfg_parser_error(pctx, CFG_LOG_NEAR, "expected network prefix");
2036135446Strhodes	return (result);
2037135446Strhodes}
2038135446Strhodes
2039135446Strhodesstatic void
2040165071Sdougbprint_netprefix(cfg_printer_t *pctx, const cfg_obj_t *obj) {
2041165071Sdougb	const cfg_netprefix_t *p = &obj->value.netprefix;
2042165071Sdougb
2043135446Strhodes	cfg_print_rawaddr(pctx, &p->address);
2044135446Strhodes	cfg_print_chars(pctx, "/", 1);
2045135446Strhodes	cfg_print_rawuint(pctx, p->prefixlen);
2046135446Strhodes}
2047135446Strhodes
2048135446Strhodesisc_boolean_t
2049165071Sdougbcfg_obj_isnetprefix(const cfg_obj_t *obj) {
2050135446Strhodes	REQUIRE(obj != NULL);
2051135446Strhodes	return (ISC_TF(obj->type->rep == &cfg_rep_netprefix));
2052135446Strhodes}
2053135446Strhodes
2054135446Strhodesvoid
2055165071Sdougbcfg_obj_asnetprefix(const cfg_obj_t *obj, isc_netaddr_t *netaddr,
2056225361Sdougb		    unsigned int *prefixlen)
2057225361Sdougb{
2058135446Strhodes	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_netprefix);
2059225361Sdougb	REQUIRE(netaddr != NULL);
2060225361Sdougb	REQUIRE(prefixlen != NULL);
2061225361Sdougb
2062135446Strhodes	*netaddr = obj->value.netprefix.address;
2063135446Strhodes	*prefixlen = obj->value.netprefix.prefixlen;
2064135446Strhodes}
2065135446Strhodes
2066135446Strhodescfg_type_t cfg_type_netprefix = {
2067135446Strhodes	"netprefix", cfg_parse_netprefix, print_netprefix, cfg_doc_terminal,
2068135446Strhodes	&cfg_rep_netprefix, NULL
2069135446Strhodes};
2070135446Strhodes
2071135446Strhodesstatic isc_result_t
2072135446Strhodesparse_sockaddrsub(cfg_parser_t *pctx, const cfg_type_t *type,
2073135446Strhodes		  int flags, cfg_obj_t **ret)
2074135446Strhodes{
2075135446Strhodes	isc_result_t result;
2076135446Strhodes	isc_netaddr_t netaddr;
2077135446Strhodes	in_port_t port = 0;
2078135446Strhodes	cfg_obj_t *obj = NULL;
2079135446Strhodes
2080135446Strhodes	CHECK(cfg_create_obj(pctx, type, &obj));
2081135446Strhodes	CHECK(cfg_parse_rawaddr(pctx, flags, &netaddr));
2082135446Strhodes	CHECK(cfg_peektoken(pctx, 0));
2083135446Strhodes	if (pctx->token.type == isc_tokentype_string &&
2084135446Strhodes	    strcasecmp(TOKEN_STRING(pctx), "port") == 0) {
2085135446Strhodes		CHECK(cfg_gettoken(pctx, 0)); /* read "port" */
2086135446Strhodes		CHECK(cfg_parse_rawport(pctx, flags, &port));
2087135446Strhodes	}
2088135446Strhodes	isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, port);
2089135446Strhodes	*ret = obj;
2090135446Strhodes	return (ISC_R_SUCCESS);
2091135446Strhodes
2092135446Strhodes cleanup:
2093135446Strhodes	CLEANUP_OBJ(obj);
2094135446Strhodes	return (result);
2095135446Strhodes}
2096135446Strhodes
2097135446Strhodesstatic unsigned int sockaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK;
2098135446Strhodescfg_type_t cfg_type_sockaddr = {
2099135446Strhodes	"sockaddr", cfg_parse_sockaddr, cfg_print_sockaddr, cfg_doc_sockaddr,
2100135446Strhodes	&cfg_rep_sockaddr, &sockaddr_flags
2101135446Strhodes};
2102135446Strhodes
2103135446Strhodesisc_result_t
2104135446Strhodescfg_parse_sockaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2105135446Strhodes	const unsigned int *flagp = type->of;
2106135446Strhodes	return (parse_sockaddrsub(pctx, &cfg_type_sockaddr, *flagp, ret));
2107135446Strhodes}
2108135446Strhodes
2109135446Strhodesvoid
2110165071Sdougbcfg_print_sockaddr(cfg_printer_t *pctx, const cfg_obj_t *obj) {
2111135446Strhodes	isc_netaddr_t netaddr;
2112135446Strhodes	in_port_t port;
2113135446Strhodes	char buf[ISC_NETADDR_FORMATSIZE];
2114135446Strhodes
2115135446Strhodes	isc_netaddr_fromsockaddr(&netaddr, &obj->value.sockaddr);
2116135446Strhodes	isc_netaddr_format(&netaddr, buf, sizeof(buf));
2117135446Strhodes	cfg_print_cstr(pctx, buf);
2118135446Strhodes	port = isc_sockaddr_getport(&obj->value.sockaddr);
2119135446Strhodes	if (port != 0) {
2120135446Strhodes		cfg_print_chars(pctx, " port ", 6);
2121135446Strhodes		cfg_print_rawuint(pctx, port);
2122135446Strhodes	}
2123135446Strhodes}
2124135446Strhodes
2125135446Strhodesvoid
2126135446Strhodescfg_doc_sockaddr(cfg_printer_t *pctx, const cfg_type_t *type) {
2127135446Strhodes	const unsigned int *flagp = type->of;
2128135446Strhodes	int n = 0;
2129135446Strhodes	cfg_print_chars(pctx, "( ", 2);
2130135446Strhodes	if (*flagp & CFG_ADDR_V4OK) {
2131135446Strhodes		cfg_print_cstr(pctx, "<ipv4_address>");
2132135446Strhodes		n++;
2133135446Strhodes	}
2134135446Strhodes	if (*flagp & CFG_ADDR_V6OK) {
2135135446Strhodes		if (n != 0)
2136135446Strhodes			cfg_print_chars(pctx, " | ", 3);
2137135446Strhodes		cfg_print_cstr(pctx, "<ipv6_address>");
2138193149Sdougb		n++;
2139135446Strhodes	}
2140135446Strhodes	if (*flagp & CFG_ADDR_WILDOK) {
2141135446Strhodes		if (n != 0)
2142135446Strhodes			cfg_print_chars(pctx, " | ", 3);
2143135446Strhodes		cfg_print_chars(pctx, "*", 1);
2144135446Strhodes		n++;
2145225361Sdougb		POST(n);
2146135446Strhodes	}
2147135446Strhodes	cfg_print_chars(pctx, " ) ", 3);
2148135446Strhodes	if (*flagp & CFG_ADDR_WILDOK) {
2149135446Strhodes		cfg_print_cstr(pctx, "[ port ( <integer> | * ) ]");
2150135446Strhodes	} else {
2151135446Strhodes		cfg_print_cstr(pctx, "[ port <integer> ]");
2152135446Strhodes	}
2153135446Strhodes}
2154135446Strhodes
2155135446Strhodesisc_boolean_t
2156165071Sdougbcfg_obj_issockaddr(const cfg_obj_t *obj) {
2157135446Strhodes	REQUIRE(obj != NULL);
2158135446Strhodes	return (ISC_TF(obj->type->rep == &cfg_rep_sockaddr));
2159135446Strhodes}
2160135446Strhodes
2161165071Sdougbconst isc_sockaddr_t *
2162165071Sdougbcfg_obj_assockaddr(const cfg_obj_t *obj) {
2163135446Strhodes	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_sockaddr);
2164135446Strhodes	return (&obj->value.sockaddr);
2165135446Strhodes}
2166135446Strhodes
2167135446Strhodesisc_result_t
2168135446Strhodescfg_gettoken(cfg_parser_t *pctx, int options) {
2169135446Strhodes	isc_result_t result;
2170135446Strhodes
2171135446Strhodes	if (pctx->seen_eof)
2172135446Strhodes		return (ISC_R_SUCCESS);
2173135446Strhodes
2174135446Strhodes	options |= (ISC_LEXOPT_EOF | ISC_LEXOPT_NOMORE);
2175135446Strhodes
2176135446Strhodes redo:
2177135446Strhodes	pctx->token.type = isc_tokentype_unknown;
2178135446Strhodes	result = isc_lex_gettoken(pctx->lexer, options, &pctx->token);
2179135446Strhodes	pctx->ungotten = ISC_FALSE;
2180135446Strhodes	pctx->line = isc_lex_getsourceline(pctx->lexer);
2181135446Strhodes
2182135446Strhodes	switch (result) {
2183135446Strhodes	case ISC_R_SUCCESS:
2184135446Strhodes		if (pctx->token.type == isc_tokentype_eof) {
2185135446Strhodes			result = isc_lex_close(pctx->lexer);
2186135446Strhodes			INSIST(result == ISC_R_NOMORE ||
2187135446Strhodes			       result == ISC_R_SUCCESS);
2188135446Strhodes
2189135446Strhodes			if (isc_lex_getsourcename(pctx->lexer) != NULL) {
2190135446Strhodes				/*
2191135446Strhodes				 * Closed an included file, not the main file.
2192135446Strhodes				 */
2193135446Strhodes				cfg_listelt_t *elt;
2194135446Strhodes				elt = ISC_LIST_TAIL(pctx->open_files->
2195135446Strhodes						    value.list);
2196135446Strhodes				INSIST(elt != NULL);
2197135446Strhodes				ISC_LIST_UNLINK(pctx->open_files->
2198135446Strhodes						value.list, elt, link);
2199135446Strhodes				ISC_LIST_APPEND(pctx->closed_files->
2200135446Strhodes						value.list, elt, link);
2201135446Strhodes				goto redo;
2202135446Strhodes			}
2203135446Strhodes			pctx->seen_eof = ISC_TRUE;
2204135446Strhodes		}
2205135446Strhodes		break;
2206135446Strhodes
2207135446Strhodes	case ISC_R_NOSPACE:
2208135446Strhodes		/* More understandable than "ran out of space". */
2209135446Strhodes		cfg_parser_error(pctx, CFG_LOG_NEAR, "token too big");
2210135446Strhodes		break;
2211135446Strhodes
2212135446Strhodes	case ISC_R_IOERROR:
2213135446Strhodes		cfg_parser_error(pctx, 0, "%s",
2214135446Strhodes				 isc_result_totext(result));
2215135446Strhodes		break;
2216135446Strhodes
2217135446Strhodes	default:
2218135446Strhodes		cfg_parser_error(pctx, CFG_LOG_NEAR, "%s",
2219135446Strhodes				 isc_result_totext(result));
2220135446Strhodes		break;
2221135446Strhodes	}
2222135446Strhodes	return (result);
2223135446Strhodes}
2224135446Strhodes
2225135446Strhodesvoid
2226135446Strhodescfg_ungettoken(cfg_parser_t *pctx) {
2227135446Strhodes	if (pctx->seen_eof)
2228135446Strhodes		return;
2229135446Strhodes	isc_lex_ungettoken(pctx->lexer, &pctx->token);
2230135446Strhodes	pctx->ungotten = ISC_TRUE;
2231135446Strhodes}
2232135446Strhodes
2233135446Strhodesisc_result_t
2234135446Strhodescfg_peektoken(cfg_parser_t *pctx, int options) {
2235135446Strhodes	isc_result_t result;
2236135446Strhodes	CHECK(cfg_gettoken(pctx, options));
2237135446Strhodes	cfg_ungettoken(pctx);
2238135446Strhodes cleanup:
2239135446Strhodes	return (result);
2240135446Strhodes}
2241135446Strhodes
2242135446Strhodes/*
2243135446Strhodes * Get a string token, accepting both the quoted and the unquoted form.
2244135446Strhodes * Log an error if the next token is not a string.
2245135446Strhodes */
2246135446Strhodesstatic isc_result_t
2247135446Strhodescfg_getstringtoken(cfg_parser_t *pctx) {
2248135446Strhodes	isc_result_t result;
2249135446Strhodes
2250135446Strhodes	result = cfg_gettoken(pctx, CFG_LEXOPT_QSTRING);
2251135446Strhodes	if (result != ISC_R_SUCCESS)
2252135446Strhodes		return (result);
2253135446Strhodes
2254135446Strhodes	if (pctx->token.type != isc_tokentype_string &&
2255135446Strhodes	    pctx->token.type != isc_tokentype_qstring) {
2256135446Strhodes		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected string");
2257135446Strhodes		return (ISC_R_UNEXPECTEDTOKEN);
2258135446Strhodes	}
2259135446Strhodes	return (ISC_R_SUCCESS);
2260135446Strhodes}
2261135446Strhodes
2262135446Strhodesvoid
2263135446Strhodescfg_parser_error(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
2264135446Strhodes	va_list args;
2265135446Strhodes	va_start(args, fmt);
2266135446Strhodes	parser_complain(pctx, ISC_FALSE, flags, fmt, args);
2267135446Strhodes	va_end(args);
2268135446Strhodes	pctx->errors++;
2269135446Strhodes}
2270135446Strhodes
2271135446Strhodesvoid
2272135446Strhodescfg_parser_warning(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
2273135446Strhodes	va_list args;
2274135446Strhodes	va_start(args, fmt);
2275135446Strhodes	parser_complain(pctx, ISC_TRUE, flags, fmt, args);
2276135446Strhodes	va_end(args);
2277135446Strhodes	pctx->warnings++;
2278135446Strhodes}
2279135446Strhodes
2280135446Strhodes#define MAX_LOG_TOKEN 30 /* How much of a token to quote in log messages. */
2281135446Strhodes
2282236374Sdougbstatic isc_boolean_t
2283236374Sdougbhave_current_file(cfg_parser_t *pctx) {
2284236374Sdougb	cfg_listelt_t *elt;
2285236374Sdougb	if (pctx->open_files == NULL)
2286236374Sdougb		return (ISC_FALSE);
2287236374Sdougb
2288236374Sdougb	elt = ISC_LIST_TAIL(pctx->open_files->value.list);
2289236374Sdougb	if (elt == NULL)
2290236374Sdougb	      return (ISC_FALSE);
2291236374Sdougb
2292236374Sdougb	return (ISC_TRUE);
2293236374Sdougb}
2294236374Sdougb
2295135446Strhodesstatic char *
2296135446Strhodescurrent_file(cfg_parser_t *pctx) {
2297135446Strhodes	static char none[] = "none";
2298135446Strhodes	cfg_listelt_t *elt;
2299135446Strhodes	cfg_obj_t *fileobj;
2300135446Strhodes
2301236374Sdougb	if (!have_current_file(pctx))
2302135446Strhodes		return (none);
2303236374Sdougb
2304135446Strhodes	elt = ISC_LIST_TAIL(pctx->open_files->value.list);
2305236374Sdougb	if (elt == NULL)	/* shouldn't be possible, but... */
2306135446Strhodes	      return (none);
2307135446Strhodes
2308135446Strhodes	fileobj = elt->obj;
2309135446Strhodes	INSIST(fileobj->type == &cfg_type_qstring);
2310135446Strhodes	return (fileobj->value.string.base);
2311135446Strhodes}
2312135446Strhodes
2313135446Strhodesstatic void
2314135446Strhodesparser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning,
2315135446Strhodes		unsigned int flags, const char *format,
2316135446Strhodes		va_list args)
2317135446Strhodes{
2318135446Strhodes	char tokenbuf[MAX_LOG_TOKEN + 10];
2319135446Strhodes	static char where[ISC_DIR_PATHMAX + 100];
2320135446Strhodes	static char message[2048];
2321135446Strhodes	int level = ISC_LOG_ERROR;
2322135446Strhodes	const char *prep = "";
2323135446Strhodes	size_t len;
2324135446Strhodes
2325135446Strhodes	if (is_warning)
2326135446Strhodes		level = ISC_LOG_WARNING;
2327135446Strhodes
2328236374Sdougb	where[0] = '\0';
2329236374Sdougb	if (have_current_file(pctx))
2330236374Sdougb		snprintf(where, sizeof(where), "%s:%u: ",
2331236374Sdougb			 current_file(pctx), pctx->line);
2332135446Strhodes
2333135446Strhodes	len = vsnprintf(message, sizeof(message), format, args);
2334135446Strhodes	if (len >= sizeof(message))
2335135446Strhodes		FATAL_ERROR(__FILE__, __LINE__,
2336135446Strhodes			    "error message would overflow");
2337135446Strhodes
2338135446Strhodes	if ((flags & (CFG_LOG_NEAR|CFG_LOG_BEFORE|CFG_LOG_NOPREP)) != 0) {
2339135446Strhodes		isc_region_t r;
2340135446Strhodes
2341135446Strhodes		if (pctx->ungotten)
2342135446Strhodes			(void)cfg_gettoken(pctx, 0);
2343135446Strhodes
2344135446Strhodes		if (pctx->token.type == isc_tokentype_eof) {
2345135446Strhodes			snprintf(tokenbuf, sizeof(tokenbuf), "end of file");
2346135446Strhodes		} else if (pctx->token.type == isc_tokentype_unknown) {
2347135446Strhodes			flags = 0;
2348135446Strhodes			tokenbuf[0] = '\0';
2349135446Strhodes		} else {
2350135446Strhodes			isc_lex_getlasttokentext(pctx->lexer,
2351135446Strhodes						 &pctx->token, &r);
2352135446Strhodes			if (r.length > MAX_LOG_TOKEN)
2353135446Strhodes				snprintf(tokenbuf, sizeof(tokenbuf),
2354135446Strhodes					 "'%.*s...'", MAX_LOG_TOKEN, r.base);
2355135446Strhodes			else
2356135446Strhodes				snprintf(tokenbuf, sizeof(tokenbuf),
2357135446Strhodes					 "'%.*s'", (int)r.length, r.base);
2358135446Strhodes		}
2359135446Strhodes
2360135446Strhodes		/* Choose a preposition. */
2361135446Strhodes		if (flags & CFG_LOG_NEAR)
2362135446Strhodes			prep = " near ";
2363135446Strhodes		else if (flags & CFG_LOG_BEFORE)
2364135446Strhodes			prep = " before ";
2365135446Strhodes		else
2366135446Strhodes			prep = " ";
2367135446Strhodes	} else {
2368135446Strhodes		tokenbuf[0] = '\0';
2369135446Strhodes	}
2370135446Strhodes	isc_log_write(pctx->lctx, CAT, MOD, level,
2371135446Strhodes		      "%s%s%s%s", where, message, prep, tokenbuf);
2372135446Strhodes}
2373135446Strhodes
2374135446Strhodesvoid
2375165071Sdougbcfg_obj_log(const cfg_obj_t *obj, isc_log_t *lctx, int level,
2376165071Sdougb	    const char *fmt, ...) {
2377135446Strhodes	va_list ap;
2378135446Strhodes	char msgbuf[2048];
2379135446Strhodes
2380135446Strhodes	if (! isc_log_wouldlog(lctx, level))
2381135446Strhodes		return;
2382135446Strhodes
2383135446Strhodes	va_start(ap, fmt);
2384135446Strhodes
2385135446Strhodes	vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
2386135446Strhodes	isc_log_write(lctx, CAT, MOD, level,
2387135446Strhodes		      "%s:%u: %s",
2388135446Strhodes		      obj->file == NULL ? "<unknown file>" : obj->file,
2389135446Strhodes		      obj->line, msgbuf);
2390135446Strhodes	va_end(ap);
2391135446Strhodes}
2392135446Strhodes
2393135446Strhodesconst char *
2394165071Sdougbcfg_obj_file(const cfg_obj_t *obj) {
2395135446Strhodes	return (obj->file);
2396135446Strhodes}
2397135446Strhodes
2398135446Strhodesunsigned int
2399165071Sdougbcfg_obj_line(const cfg_obj_t *obj) {
2400135446Strhodes	return (obj->line);
2401135446Strhodes}
2402135446Strhodes
2403135446Strhodesisc_result_t
2404135446Strhodescfg_create_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2405224092Sdougb	isc_result_t result;
2406135446Strhodes	cfg_obj_t *obj;
2407135446Strhodes
2408135446Strhodes	obj = isc_mem_get(pctx->mctx, sizeof(cfg_obj_t));
2409135446Strhodes	if (obj == NULL)
2410135446Strhodes		return (ISC_R_NOMEMORY);
2411135446Strhodes	obj->type = type;
2412135446Strhodes	obj->file = current_file(pctx);
2413135446Strhodes	obj->line = pctx->line;
2414224092Sdougb	result = isc_refcount_init(&obj->references, 1);
2415224092Sdougb	if (result != ISC_R_SUCCESS) {
2416224092Sdougb		isc_mem_put(pctx->mctx, obj, sizeof(cfg_obj_t));
2417224092Sdougb		return (result);
2418224092Sdougb	}
2419135446Strhodes	*ret = obj;
2420135446Strhodes	return (ISC_R_SUCCESS);
2421135446Strhodes}
2422135446Strhodes
2423224092Sdougb
2424135446Strhodesstatic void
2425135446Strhodesmap_symtabitem_destroy(char *key, unsigned int type,
2426135446Strhodes		       isc_symvalue_t symval, void *userarg)
2427135446Strhodes{
2428135446Strhodes	cfg_obj_t *obj = symval.as_pointer;
2429135446Strhodes	cfg_parser_t *pctx = (cfg_parser_t *)userarg;
2430135446Strhodes
2431135446Strhodes	UNUSED(key);
2432135446Strhodes	UNUSED(type);
2433135446Strhodes
2434135446Strhodes	cfg_obj_destroy(pctx, &obj);
2435135446Strhodes}
2436135446Strhodes
2437135446Strhodes
2438135446Strhodesstatic isc_result_t
2439135446Strhodescreate_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2440135446Strhodes	isc_result_t result;
2441135446Strhodes	isc_symtab_t *symtab = NULL;
2442135446Strhodes	cfg_obj_t *obj = NULL;
2443135446Strhodes
2444135446Strhodes	CHECK(cfg_create_obj(pctx, type, &obj));
2445135446Strhodes	CHECK(isc_symtab_create(pctx->mctx, 5, /* XXX */
2446135446Strhodes				map_symtabitem_destroy,
2447135446Strhodes				pctx, ISC_FALSE, &symtab));
2448135446Strhodes	obj->value.map.symtab = symtab;
2449135446Strhodes	obj->value.map.id = NULL;
2450135446Strhodes
2451135446Strhodes	*ret = obj;
2452135446Strhodes	return (ISC_R_SUCCESS);
2453135446Strhodes
2454135446Strhodes cleanup:
2455135446Strhodes	if (obj != NULL)
2456135446Strhodes		isc_mem_put(pctx->mctx, obj, sizeof(*obj));
2457135446Strhodes	return (result);
2458135446Strhodes}
2459135446Strhodes
2460135446Strhodesstatic void
2461135446Strhodesfree_map(cfg_parser_t *pctx, cfg_obj_t *obj) {
2462135446Strhodes	CLEANUP_OBJ(obj->value.map.id);
2463135446Strhodes	isc_symtab_destroy(&obj->value.map.symtab);
2464135446Strhodes}
2465135446Strhodes
2466135446Strhodesisc_boolean_t
2467165071Sdougbcfg_obj_istype(const cfg_obj_t *obj, const cfg_type_t *type) {
2468135446Strhodes	return (ISC_TF(obj->type == type));
2469135446Strhodes}
2470135446Strhodes
2471135446Strhodes/*
2472135446Strhodes * Destroy 'obj', a configuration object created in 'pctx'.
2473135446Strhodes */
2474135446Strhodesvoid
2475135446Strhodescfg_obj_destroy(cfg_parser_t *pctx, cfg_obj_t **objp) {
2476254402Serwin	cfg_obj_t *obj;
2477224092Sdougb	unsigned int refs;
2478224092Sdougb
2479254402Serwin	REQUIRE(objp != NULL && *objp != NULL);
2480254402Serwin	REQUIRE(pctx != NULL);
2481254402Serwin
2482254402Serwin	obj = *objp;
2483254402Serwin
2484224092Sdougb	isc_refcount_decrement(&obj->references, &refs);
2485224092Sdougb	if (refs == 0) {
2486224092Sdougb		obj->type->rep->free(pctx, obj);
2487224092Sdougb		isc_refcount_destroy(&obj->references);
2488224092Sdougb		isc_mem_put(pctx->mctx, obj, sizeof(cfg_obj_t));
2489224092Sdougb	}
2490135446Strhodes	*objp = NULL;
2491135446Strhodes}
2492135446Strhodes
2493224092Sdougbvoid
2494224092Sdougbcfg_obj_attach(cfg_obj_t *src, cfg_obj_t **dest) {
2495224092Sdougb    REQUIRE(src != NULL);
2496224092Sdougb    REQUIRE(dest != NULL && *dest == NULL);
2497224092Sdougb    isc_refcount_increment(&src->references, NULL);
2498224092Sdougb    *dest = src;
2499224092Sdougb}
2500224092Sdougb
2501135446Strhodesstatic void
2502135446Strhodesfree_noop(cfg_parser_t *pctx, cfg_obj_t *obj) {
2503135446Strhodes	UNUSED(pctx);
2504135446Strhodes	UNUSED(obj);
2505135446Strhodes}
2506135446Strhodes
2507135446Strhodesvoid
2508135446Strhodescfg_doc_obj(cfg_printer_t *pctx, const cfg_type_t *type) {
2509135446Strhodes	type->doc(pctx, type);
2510135446Strhodes}
2511135446Strhodes
2512135446Strhodesvoid
2513135446Strhodescfg_doc_terminal(cfg_printer_t *pctx, const cfg_type_t *type) {
2514135446Strhodes	cfg_print_chars(pctx, "<", 1);
2515135446Strhodes	cfg_print_cstr(pctx, type->name);
2516135446Strhodes	cfg_print_chars(pctx, ">", 1);
2517135446Strhodes}
2518135446Strhodes
2519135446Strhodesvoid
2520135446Strhodescfg_print_grammar(const cfg_type_t *type,
2521135446Strhodes	void (*f)(void *closure, const char *text, int textlen),
2522135446Strhodes	void *closure)
2523135446Strhodes{
2524135446Strhodes	cfg_printer_t pctx;
2525135446Strhodes	pctx.f = f;
2526135446Strhodes	pctx.closure = closure;
2527135446Strhodes	pctx.indent = 0;
2528262706Serwin	pctx.flags = 0;
2529135446Strhodes	cfg_doc_obj(&pctx, type);
2530135446Strhodes}
2531